pagevideos is now much better that before:
1. Display list of video files.
2. For each file in progress display progress bar.
3. Description for each file (size, duration etc).
4. It is possible to remove and rename files.
5. Video file can be launched in external media player.
6. ...
also fixed some bugs
http://postimage.org/image/hk87cuqm9/
--- a/QTfrontend/game.cpp Sun Jun 24 20:31:26 2012 +0400
+++ b/QTfrontend/game.cpp Sun Jun 24 20:57:02 2012 +0400
@@ -65,6 +65,8 @@
emit HaveRecord(rtSave, demo);
else if (gameState == gsFinished)
emit HaveRecord(rtDemo, demo);
+ else
+ emit HaveRecord(rtNeither, demo);
}
SetGameState(gsStopped);
}
--- a/QTfrontend/gameuiconfig.cpp Sun Jun 24 20:31:26 2012 +0400
+++ b/QTfrontend/gameuiconfig.cpp Sun Jun 24 20:57:02 2012 +0400
@@ -127,8 +127,8 @@
Form->ui.pageVideos->widthEdit->setText(value("videorec/width","800").toString());
Form->ui.pageVideos->heightEdit->setText(value("videorec/height","600").toString());
}
- Form->ui.pageVideos->CBUseGameRes->setChecked(useGameRes);
- Form->ui.pageVideos->CBRecordAudio->setChecked(value("videorec/audio",true).toBool());
+ Form->ui.pageVideos->checkUseGameRes->setChecked(useGameRes);
+ Form->ui.pageVideos->checkRecordAudio->setChecked(value("videorec/audio",true).toBool());
if (!Form->ui.pageVideos->tryCodecs(value("videorec/format","no").toString(),
value("videorec/videocodec","no").toString(),
value("videorec/audiocodec","no").toString()))
@@ -220,7 +220,7 @@
setValue("videorec/fps", rec_Framerate());
setValue("videorec/width", res.width());
setValue("videorec/height", res.height());
- setValue("videorec/usegameres", Form->ui.pageVideos->CBUseGameRes->isChecked());
+ setValue("videorec/usegameres", Form->ui.pageVideos->checkUseGameRes->isChecked());
setValue("videorec/audio", recordAudio());
Form->gameSettings->sync();
@@ -424,22 +424,22 @@
QString GameUIConfig::AVFormat()
{
- return Form->ui.pageVideos->getFormat();
+ return Form->ui.pageVideos->format();
}
QString GameUIConfig::videoCodec()
{
- return Form->ui.pageVideos->getVideoCodec();
+ return Form->ui.pageVideos->videoCodec();
}
QString GameUIConfig::audioCodec()
{
- return Form->ui.pageVideos->getAudioCodec();
+ return Form->ui.pageVideos->audioCodec();
}
QRect GameUIConfig::rec_Resolution()
{
- if (Form->ui.pageVideos->CBUseGameRes->isChecked())
+ if (Form->ui.pageVideos->checkUseGameRes->isChecked())
return vid_Resolution();
QRect res(0,0,0,0);
res.setWidth(Form->ui.pageVideos->widthEdit->text().toUInt());
@@ -454,5 +454,5 @@
bool GameUIConfig::recordAudio()
{
- return Form->ui.pageVideos->CBRecordAudio->isChecked();
+ return Form->ui.pageVideos->checkRecordAudio->isChecked();
}
--- a/QTfrontend/hwform.cpp Sun Jun 24 20:31:26 2012 +0400
+++ b/QTfrontend/hwform.cpp Sun Jun 24 20:57:02 2012 +0400
@@ -1429,8 +1429,10 @@
QString prefix = str;
prefix.chop(7); // remove ".txtout"
videosDir.rename(prefix + ".txtout", prefix + ".txtin"); // rename this file to not open it twice
- HWRecorder* pRecorder = new HWRecorder(config);
- pRecorder->EncodeVideo(record, prefix);
+ HWRecorder* pRecorder = new HWRecorder(config, prefix);
+ ui.pageVideos->addRecorder(pRecorder);
+ int numFrames = QFileInfo(videosDir.absoluteFilePath(prefix + ".txtin")).size()/16;
+ pRecorder->EncodeVideo(record, numFrames);
}
}
--- a/QTfrontend/net/recorder.cpp Sun Jun 24 20:31:26 2012 +0400
+++ b/QTfrontend/net/recorder.cpp Sun Jun 24 20:57:02 2012 +0400
@@ -18,20 +18,26 @@
#include <QString>
#include <QByteArray>
+//#include <QMessageBox>
#include "recorder.h"
#include "gameuiconfig.h"
#include "hwconsts.h"
#include "game.h"
+#include "libav_iteraction.h"
-HWRecorder::HWRecorder(GameUIConfig * config) :
+HWRecorder::HWRecorder(GameUIConfig * config, const QString &prefix) :
TCPBase(false)
{
this->config = config;
+ this->prefix = prefix;
+ finished = false;
+ name = prefix + "." + LibavIteraction::instance().getExtension(config->AVFormat());
}
HWRecorder::~HWRecorder()
{
+ emit encodingFinished(finished);
}
void HWRecorder::onClientDisconnect()
@@ -47,14 +53,25 @@
{
QByteArray msg = readbuffer.left(msglen + 1);
readbuffer.remove(0, msglen + 1);
- if (msg.at(1) == '?')
+ switch (msg.at(1))
+ {
+ case '?':
SendIPC("!");
+ break;
+ case 'p':
+ emit onProgress(float(++curFrame)/numFrames);
+ break;
+ case 'v':
+ finished = true;
+ break;
+ }
}
}
-void HWRecorder::EncodeVideo( const QByteArray & record, const QString & prefix )
+void HWRecorder::EncodeVideo(const QByteArray & record, int numFrames)
{
- this->prefix = prefix;
+ this->numFrames = numFrames;
+ curFrame = 0;
toSendBuf = record;
toSendBuf.replace(QByteArray("\x02TD"), QByteArray("\x02TV"));
@@ -73,7 +90,7 @@
arguments << cfgdir->absolutePath();
arguments << QString::number(resolution.width());
arguments << QString::number(resolution.height());
- arguments << QString::number(config->bitDepth()); // bpp
+ arguments << "32"; // bpp
arguments << QString("%1").arg(ipc_port);
arguments << "0"; // fullscreen
arguments << "0"; // sound
@@ -94,10 +111,7 @@
arguments << config->videoCodec();
arguments << "5"; // video quality
arguments << "medium";
- if (config->recordAudio())
- arguments << config->audioCodec();
- else
- arguments << "no";
+ arguments << (config->recordAudio()? config->audioCodec() : "no");
arguments << "5"; // audio quality
return arguments;
--- a/QTfrontend/net/recorder.h Sun Jun 24 20:31:26 2012 +0400
+++ b/QTfrontend/net/recorder.h Sun Jun 24 20:57:02 2012 +0400
@@ -25,28 +25,36 @@
#include "tcpBase.h"
class GameUIConfig;
+class VideoItem;
class HWRecorder : public TCPBase
{
Q_OBJECT
public:
- HWRecorder(GameUIConfig * config);
+ HWRecorder(GameUIConfig * config, const QString & prefix);
virtual ~HWRecorder();
- void EncodeVideo(const QByteArray & record, const QString & prefix);
+ void EncodeVideo(const QByteArray & record, int numFrames);
+
+ VideoItem * item; // used by pagevideos
+ QString name;
+ QString prefix;
protected:
+ // virtuals from TCPBase
virtual QStringList getArguments();
virtual void onClientRead();
virtual void onClientDisconnect();
signals:
-
- public slots:
+ void onProgress(float progress); // 0 < progress < 1
+ void encodingFinished(bool success);
private:
+ int curFrame;
+ int numFrames;
+ bool finished;
GameUIConfig * config;
- QString prefix;
};
#endif // RECORDER_H
--- a/QTfrontend/ui/page/pagevideos.cpp Sun Jun 24 20:31:26 2012 +0400
+++ b/QTfrontend/ui/page/pagevideos.cpp Sun Jun 24 20:57:02 2012 +0400
@@ -25,133 +25,229 @@
#include <QLineEdit>
#include <QSpinBox>
#include <QTableWidget>
+#include <QDir>
+#include <QProgressBar>
+#include <QStringList>
+#include <QDesktopServices>
+#include <QUrl>
+#include <QList>
+#include <QMessageBox>
+#include <QHeaderView>
+#include <QKeyEvent>
+#include <QVBoxLayout>
+#include <QFileSystemWatcher>
+#include "hwconsts.h"
#include "pagevideos.h"
#include "igbox.h"
#include "libav_iteraction.h"
#include "gameuiconfig.h"
+#include "recorder.h"
+
+// columns in table with list of video files
+enum VideosColumns
+{
+ vcName,
+ vcSize,
+ vcProgress,
+
+ vcNumColumns,
+};
+
+class VideoItem : public QTableWidgetItem
+{
+ // note: QTableWidgetItem is not Q_OBJECT
+
+ public:
+ VideoItem(const QString& name);
+ ~VideoItem();
+
+ QString name;
+ QString desc; // description
+ HWRecorder * pRecorder; // non NULL if file is being encoded
+ bool seen; // used when updating directory
+ float lastSizeUpdate;
+
+ bool ready()
+ { return !pRecorder; }
+
+ QString path()
+ { return cfgdir->absoluteFilePath("Videos/" + name); }
+};
+
+VideoItem::VideoItem(const QString& name)
+ : QTableWidgetItem(name, UserType)
+{
+ this->name = name;
+ pRecorder = NULL;
+ lastSizeUpdate = 0;
+}
+
+VideoItem::~VideoItem()
+{}
QLayout * PageVideos::bodyLayoutDefinition()
{
- QGridLayout * pageLayout = new QGridLayout();
- pageLayout->setColumnStretch(0, 1);
- pageLayout->setColumnStretch(1, 1);
+ QGridLayout * pPageLayout = new QGridLayout();
+ pPageLayout->setColumnStretch(0, 1);
+ pPageLayout->setColumnStretch(1, 1);
{
- IconedGroupBox* groupRec = new IconedGroupBox(this);
- groupRec->setIcon(QIcon(":/res/graphicsicon.png"));
- groupRec->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
- groupRec->setTitle(QGroupBox::tr("Video recording options"));
- QGridLayout * RecLayout = new QGridLayout(groupRec);
+ IconedGroupBox* pOptionsGroup = new IconedGroupBox(this);
+ pOptionsGroup->setIcon(QIcon(":/res/graphicsicon.png"));
+ pOptionsGroup->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ pOptionsGroup->setTitle(QGroupBox::tr("Video recording options"));
+ QGridLayout * pOptLayout = new QGridLayout(pOptionsGroup);
- // Label for format
- QLabel *labelFormat = new QLabel(groupRec);
+ // label for format
+ QLabel *labelFormat = new QLabel(pOptionsGroup);
labelFormat->setText(QLabel::tr("Format"));
- RecLayout->addWidget(labelFormat, 0, 0);
+ pOptLayout->addWidget(labelFormat, 0, 0);
- // List of supported formats
- CBAVFormats = new QComboBox(groupRec);
- RecLayout->addWidget(CBAVFormats, 0, 1, 1, 4);
- LibavIteraction::instance().FillFormats(CBAVFormats);
+ // list of supported formats
+ comboAVFormats = new QComboBox(pOptionsGroup);
+ pOptLayout->addWidget(comboAVFormats, 0, 1, 1, 4);
+ LibavIteraction::instance().fillFormats(comboAVFormats);
- QFrame * hr = new QFrame(groupRec);
+ // separator
+ QFrame * hr = new QFrame(pOptionsGroup);
hr->setFrameStyle(QFrame::HLine);
hr->setLineWidth(3);
hr->setFixedHeight(10);
- RecLayout->addWidget(hr, 1, 0, 1, 5);
+ pOptLayout->addWidget(hr, 1, 0, 1, 5);
- // Label for audio codec
- QLabel *labelACodec = new QLabel(groupRec);
+ // label for audio codec
+ QLabel *labelACodec = new QLabel(pOptionsGroup);
labelACodec->setText(QLabel::tr("Audio codec"));
- RecLayout->addWidget(labelACodec, 2, 0);
+ pOptLayout->addWidget(labelACodec, 2, 0);
- // List of supported audio codecs
- CBAudioCodecs = new QComboBox(groupRec);
- RecLayout->addWidget(CBAudioCodecs, 2, 1, 1, 3);
+ // list of supported audio codecs
+ comboAudioCodecs = new QComboBox(pOptionsGroup);
+ pOptLayout->addWidget(comboAudioCodecs, 2, 1, 1, 3);
- // record audio
- CBRecordAudio = new QCheckBox(groupRec);
- CBRecordAudio->setText(QCheckBox::tr("Record audio"));
- RecLayout->addWidget(CBRecordAudio, 2, 4);
+ // checkbox 'record audio'
+ checkRecordAudio = new QCheckBox(pOptionsGroup);
+ checkRecordAudio->setText(QCheckBox::tr("Record audio"));
+ pOptLayout->addWidget(checkRecordAudio, 2, 4);
- hr = new QFrame(groupRec);
+ // separator
+ hr = new QFrame(pOptionsGroup);
hr->setFrameStyle(QFrame::HLine);
hr->setLineWidth(3);
hr->setFixedHeight(10);
- RecLayout->addWidget(hr, 3, 0, 1, 5);
+ pOptLayout->addWidget(hr, 3, 0, 1, 5);
- // Label for video codec
- QLabel *labelVCodec = new QLabel(groupRec);
+ // label for video codec
+ QLabel *labelVCodec = new QLabel(pOptionsGroup);
labelVCodec->setText(QLabel::tr("Video codec"));
- RecLayout->addWidget(labelVCodec, 4, 0);
+ pOptLayout->addWidget(labelVCodec, 4, 0);
- // List of supported video codecs
- CBVideoCodecs = new QComboBox(groupRec);
- RecLayout->addWidget(CBVideoCodecs, 4, 1, 1, 4);
+ // list of supported video codecs
+ comboVideoCodecs = new QComboBox(pOptionsGroup);
+ pOptLayout->addWidget(comboVideoCodecs, 4, 1, 1, 4);
- // Label for resolution
- QLabel *labelRes = new QLabel(groupRec);
+ // label for resolution
+ QLabel *labelRes = new QLabel(pOptionsGroup);
labelRes->setText(QLabel::tr("Resolution"));
- RecLayout->addWidget(labelRes, 5, 0);
+ pOptLayout->addWidget(labelRes, 5, 0);
// width
- widthEdit = new QLineEdit(groupRec);
+ widthEdit = new QLineEdit(pOptionsGroup);
widthEdit->setValidator(new QIntValidator(this));
- RecLayout->addWidget(widthEdit, 5, 1);
+ pOptLayout->addWidget(widthEdit, 5, 1);
// x
- QLabel *labelX = new QLabel(groupRec);
+ QLabel *labelX = new QLabel(pOptionsGroup);
labelX->setText("X");
- RecLayout->addWidget(labelX, 5, 2);
+ pOptLayout->addWidget(labelX, 5, 2);
// height
- heightEdit = new QLineEdit(groupRec);
- heightEdit->setValidator(new QIntValidator(groupRec));
- RecLayout->addWidget(heightEdit, 5, 3);
+ heightEdit = new QLineEdit(pOptionsGroup);
+ heightEdit->setValidator(new QIntValidator(pOptionsGroup));
+ pOptLayout->addWidget(heightEdit, 5, 3);
- // use game res
- CBUseGameRes = new QCheckBox(groupRec);
- CBUseGameRes->setText(QCheckBox::tr("Use game resolution"));
- RecLayout->addWidget(CBUseGameRes, 5, 4);
+ // checkbox 'use game resolution'
+ checkUseGameRes = new QCheckBox(pOptionsGroup);
+ checkUseGameRes->setText(QCheckBox::tr("Use game resolution"));
+ pOptLayout->addWidget(checkUseGameRes, 5, 4);
- // Label for framerate
- QLabel *labelFramerate = new QLabel(groupRec);
+ // label for framerate
+ QLabel *labelFramerate = new QLabel(pOptionsGroup);
labelFramerate->setText(QLabel::tr("Framerate"));
- RecLayout->addWidget(labelFramerate, 6, 0);
+ pOptLayout->addWidget(labelFramerate, 6, 0);
// framerate
- framerateBox = new QSpinBox(groupRec);
+ framerateBox = new QSpinBox(pOptionsGroup);
framerateBox->setRange(1, 200);
framerateBox->setSingleStep(1);
- RecLayout->addWidget(framerateBox, 6, 1);
+ pOptLayout->addWidget(framerateBox, 6, 1);
- BtnDefaults = new QPushButton(groupRec);
- BtnDefaults->setText(QPushButton::tr("Set default options"));
- RecLayout->addWidget(BtnDefaults, 7, 0, 1, 5);
+ // button 'set default options'
+ btnDefaults = new QPushButton(pOptionsGroup);
+ btnDefaults->setText(QPushButton::tr("Set default options"));
+ pOptLayout->addWidget(btnDefaults, 7, 0, 1, 5);
- pageLayout->addWidget(groupRec, 0, 0);
+ pPageLayout->addWidget(pOptionsGroup, 1, 0);
}
{
- IconedGroupBox* groupTable = new IconedGroupBox(this);
- //groupRec->setContentTopPadding(0);
- groupTable->setIcon(QIcon(":/res/graphicsicon.png"));
- groupTable->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
- groupTable->setTitle(QGroupBox::tr("Videos"));
+ IconedGroupBox* pTableGroup = new IconedGroupBox(this);
+ pTableGroup->setIcon(QIcon(":/res/graphicsicon.png"));
+ pTableGroup->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ pTableGroup->setTitle(QGroupBox::tr("Videos"));
QStringList columns;
- columns << tr("Name") << tr("Lenght") << tr("...");
+ columns << tr("Name");
+ columns << tr("Size");
+ columns << tr("...");
- filesTable = new QTableWidget(groupTable);
- filesTable->setColumnCount(3);
+ filesTable = new QTableWidget(pTableGroup);
+ filesTable->setColumnCount(vcNumColumns);
filesTable->setHorizontalHeaderLabels(columns);
- QVBoxLayout *box = new QVBoxLayout(groupTable);
+ filesTable->setSelectionBehavior(QAbstractItemView::SelectRows);
+ filesTable->setEditTriggers(QAbstractItemView::SelectedClicked);
+ filesTable->verticalHeader()->hide();
+
+ QHeaderView * header = filesTable->horizontalHeader();
+ int length = header->length(); // FIXME
+ // header->setResizeMode(QHeaderView::ResizeToContents);
+ header->resizeSection(vcName, length/2);
+ header->resizeSection(vcSize, length/4);
+ header->resizeSection(vcProgress, length/4);
+
+ btnOpenDir = new QPushButton(QPushButton::tr("Open videos directory"), pTableGroup);
+
+ QVBoxLayout *box = new QVBoxLayout(pTableGroup);
box->addWidget(filesTable);
+ box->addWidget(btnOpenDir);
- pageLayout->addWidget(groupTable, 0, 1, 2, 1);
+ pPageLayout->addWidget(pTableGroup, 0, 1, 2, 1);
}
- return pageLayout;
+ {
+ IconedGroupBox* pDescGroup = new IconedGroupBox(this);
+ pDescGroup->setIcon(QIcon(":/res/graphicsicon.png"));
+ pDescGroup->setTitle(QGroupBox::tr("Description"));
+ QGridLayout* pDescLayout = new QGridLayout(pDescGroup);
+
+ labelThumbnail = new QLabel(pDescGroup);
+ pDescLayout->addWidget(labelThumbnail, 0, 0);
+
+ // label with file description
+ labelDesc = new QLabel(pDescGroup);
+ labelDesc->setAlignment(Qt::AlignLeft | Qt::AlignTop);
+ pDescLayout->addWidget(labelDesc, 0, 1);
+
+ // buttons: play and delete
+ btnPlay = new QPushButton(QPushButton::tr("Play"), pDescGroup);
+ pDescLayout->addWidget(btnPlay, 1, 0);
+ btnDelete = new QPushButton(QPushButton::tr("Delete"), pDescGroup);
+ pDescLayout->addWidget(btnDelete, 1, 1);
+
+ pPageLayout->addWidget(pDescGroup, 0, 0);
+ }
+
+ return pPageLayout;
}
QLayout * PageVideos::footerLayoutDefinition()
@@ -161,43 +257,69 @@
void PageVideos::connectSignals()
{
- connect(CBUseGameRes, SIGNAL(stateChanged(int)), this, SLOT(changeUseGameRes(int)));
- connect(CBRecordAudio, SIGNAL(stateChanged(int)), this, SLOT(changeRecordAudio(int)));
- connect(CBAVFormats, SIGNAL(currentIndexChanged(int)), this, SLOT(changeAVFormat(int)));
- connect(BtnDefaults, SIGNAL(clicked()), this, SLOT(setDefaultOptions()));
+ connect(checkUseGameRes, SIGNAL(stateChanged(int)), this, SLOT(changeUseGameRes(int)));
+ connect(checkRecordAudio, SIGNAL(stateChanged(int)), this, SLOT(changeRecordAudio(int)));
+ connect(comboAVFormats, SIGNAL(currentIndexChanged(int)), this, SLOT(changeAVFormat(int)));
+ connect(btnDefaults, SIGNAL(clicked()), this, SLOT(setDefaultOptions()));
+ connect(filesTable, SIGNAL(cellDoubleClicked(int, int)), this, SLOT(cellDoubleClicked(int, int)));
+ connect(filesTable, SIGNAL(cellChanged(int,int)), this, SLOT(cellChanged(int, int)));
+ connect(filesTable, SIGNAL(currentCellChanged(int,int,int,int)), this, SLOT(currentCellChanged(int,int,int,int)));
+ connect(btnPlay, SIGNAL(clicked()), this, SLOT(playSelectedFile()));
+ connect(btnDelete, SIGNAL(clicked()), this, SLOT(deleteSelectedFiles()));
+ connect(btnOpenDir, SIGNAL(clicked()), this, SLOT(openVideosDirectory()));
+
+ QString path = cfgdir->absolutePath() + "/Videos";
+ QFileSystemWatcher * pWatcher = new QFileSystemWatcher(this);
+ pWatcher->addPath(path);
+ connect(pWatcher, SIGNAL(directoryChanged(const QString &)), this, SLOT(updateFileList(const QString &)));
+ updateFileList(path);
}
-PageVideos::PageVideos(QWidget* parent) : AbstractPage(parent)
+PageVideos::PageVideos(QWidget* parent) : AbstractPage(parent),
+ config(0)
{
+ nameChangedFromCode = false;
initPage();
}
+// user changed file format, we need to update list of codecs
void PageVideos::changeAVFormat(int index)
{
- QString prevVCodec = getVideoCodec();
- QString prevACodec = getAudioCodec();
- CBVideoCodecs->clear();
- CBAudioCodecs->clear();
- LibavIteraction::instance().FillCodecs(CBAVFormats->itemData(index), CBVideoCodecs, CBAudioCodecs);
- if (CBAudioCodecs->count() == 0)
+ // remember selected codecs
+ QString prevVCodec = videoCodec();
+ QString prevACodec = audioCodec();
+
+ // clear lists of codecs
+ comboVideoCodecs->clear();
+ comboAudioCodecs->clear();
+
+ // get list of codecs for specified format
+ LibavIteraction::instance().fillCodecs(comboAVFormats->itemData(index).toString(), comboVideoCodecs, comboAudioCodecs);
+
+ // disable audio if there is no audio codec
+ if (comboAudioCodecs->count() == 0)
{
- CBRecordAudio->setChecked(false);
- CBRecordAudio->setEnabled(false);
+ checkRecordAudio->setChecked(false);
+ checkRecordAudio->setEnabled(false);
}
else
- CBRecordAudio->setEnabled(true);
- int iVCodec = CBVideoCodecs->findData(prevVCodec);
+ checkRecordAudio->setEnabled(true);
+
+ // restore selected codecs if possible
+ int iVCodec = comboVideoCodecs->findData(prevVCodec);
if (iVCodec != -1)
- CBVideoCodecs->setCurrentIndex(iVCodec);
- int iACodec = CBAudioCodecs->findData(prevACodec);
+ comboVideoCodecs->setCurrentIndex(iVCodec);
+ int iACodec = comboAudioCodecs->findData(prevACodec);
if (iACodec != -1)
- CBAudioCodecs->setCurrentIndex(iACodec);
+ comboAudioCodecs->setCurrentIndex(iACodec);
}
+// user switched checkbox 'use game resolution'
void PageVideos::changeUseGameRes(int state)
{
if (state && config)
{
+ // set resolution to game resolution
QRect resolution = config->vid_Resolution();
widthEdit->setText(QString::number(resolution.width()));
heightEdit->setText(QString::number(resolution.height()));
@@ -206,25 +328,36 @@
heightEdit->setEnabled(!state);
}
+// user switched checkbox 'record audio'
void PageVideos::changeRecordAudio(int state)
{
- CBAudioCodecs->setEnabled(!!state);
+ comboAudioCodecs->setEnabled(!!state);
}
void PageVideos::setDefaultCodecs()
{
if (tryCodecs("mp4", "libx264", "libmp3lame"))
return;
- if (tryCodecs("mp4", "libx264", "ac3_fixed"))
+ if (tryCodecs("mp4", "libx264", "libfaac"))
+ return;
+ if (tryCodecs("mp4", "libx264", "libvo_aacenc"))
+ return;
+ if (tryCodecs("mp4", "libx264", "aac"))
+ return;
+ if (tryCodecs("mp4", "libx264", "mp2"))
return;
if (tryCodecs("avi", "libxvid", "libmp3lame"))
return;
if (tryCodecs("avi", "libxvid", "ac3_fixed"))
return;
+ if (tryCodecs("avi", "libxvid", "mp2"))
+ return;
if (tryCodecs("avi", "mpeg4", "libmp3lame"))
return;
if (tryCodecs("avi", "mpeg4", "ac3_fixed"))
return;
+ if (tryCodecs("avi", "mpeg4", "mp2"))
+ return;
// this shouldn't happen, just in case
if (tryCodecs("ogg", "libtheora", "libvorbis"))
@@ -235,28 +368,328 @@
void PageVideos::setDefaultOptions()
{
framerateBox->setValue(25);
- CBRecordAudio->setChecked(true);
- CBUseGameRes->setChecked(true);
+ checkRecordAudio->setChecked(true);
+ checkUseGameRes->setChecked(true);
setDefaultCodecs();
}
bool PageVideos::tryCodecs(const QString & format, const QString & vcodec, const QString & acodec)
{
- int iFormat = CBAVFormats->findData(format);
+ // first we should change format
+ int iFormat = comboAVFormats->findData(format);
if (iFormat == -1)
return false;
- CBAVFormats->setCurrentIndex(iFormat);
+ comboAVFormats->setCurrentIndex(iFormat);
- int iVCodec = CBVideoCodecs->findData(vcodec);
+ // try to find video codec
+ int iVCodec = comboVideoCodecs->findData(vcodec);
if (iVCodec == -1)
return false;
- CBVideoCodecs->setCurrentIndex(iVCodec);
+ comboVideoCodecs->setCurrentIndex(iVCodec);
- int iACodec = CBAudioCodecs->findData(acodec);
- if (iACodec == -1 && CBRecordAudio->isChecked())
+ // try to find audio codec
+ int iACodec = comboAudioCodecs->findData(acodec);
+ if (iACodec == -1 && checkRecordAudio->isChecked())
return false;
if (iACodec != -1)
- CBAudioCodecs->setCurrentIndex(iACodec);
+ comboAudioCodecs->setCurrentIndex(iACodec);
return true;
}
+
+// get file size as string
+QString FileSizeStr(const QString & path)
+{
+ quint64 size = QFileInfo(path).size();
+
+ quint64 KiB = 1024;
+ quint64 MiB = 1024*KiB;
+ quint64 GiB = 1024*MiB;
+ QString sizeStr;
+ if (size >= GiB)
+ return QString("%1 GiB").arg(QString::number(float(size)/GiB, 'f', 2));
+ if (size >= MiB)
+ return QString("%1 MiB").arg(QString::number(float(size)/MiB, 'f', 2));
+ if (size >= KiB)
+ return QString("%1 KiB").arg(QString::number(float(size)/KiB, 'f', 2));
+ return PageVideos::tr("%1 bytes").arg(QString::number(size));
+}
+
+// set file size in file list in specified row
+void PageVideos::updateSize(int row)
+{
+ VideoItem * item = nameItem(row);
+ QString path = item->ready()? item->path() : cfgdir->absoluteFilePath("VideoTemp/" + item->pRecorder->name);
+ filesTable->item(row, vcSize)->setText(FileSizeStr(path));
+}
+
+void PageVideos::updateFileList(const QString & path)
+{
+ // mark all files as non seen
+ int numRows = filesTable->rowCount();
+ for (int i = 0; i < numRows; i++)
+ nameItem(i)->seen = false;
+
+ QStringList files = QDir(path).entryList(QDir::Files);
+ foreach (const QString & name, files)
+ {
+ int row = -1;
+ foreach (QTableWidgetItem * item, filesTable->findItems(name, Qt::MatchExactly))
+ {
+ if (item->type() != QTableWidgetItem::UserType || !((VideoItem*)item)->ready())
+ continue;
+ row = item->row();
+ break;
+ }
+ if (row == -1)
+ row = appendRow(name);
+ VideoItem * item = nameItem(row);
+ item->seen = true;
+ item->desc = "";
+ updateSize(row);
+ }
+
+ // remove all non seen files
+ for (int i = 0; i < filesTable->rowCount();)
+ {
+ VideoItem * item = nameItem(i);
+ if (item->ready() && !item->seen)
+ filesTable->removeRow(i);
+ else
+ i++;
+ }
+}
+
+void PageVideos::addRecorder(HWRecorder* pRecorder)
+{
+ int row = appendRow(pRecorder->name);
+ VideoItem * item = nameItem(row);
+ item->pRecorder = pRecorder;
+ pRecorder->item = item;
+
+ // add progress bar
+ QProgressBar * progressBar = new QProgressBar(filesTable);
+ progressBar->setMinimum(0);
+ progressBar->setMaximum(10000);
+ progressBar->setValue(0);
+ connect(pRecorder, SIGNAL(onProgress(float)), this, SLOT(updateProgress(float)));
+ connect(pRecorder, SIGNAL(encodingFinished(bool)), this, SLOT(encodingFinished(bool)));
+ filesTable->setCellWidget(row, vcProgress, progressBar);
+}
+
+void PageVideos::updateProgress(float value)
+{
+ HWRecorder * pRecorder = (HWRecorder*)sender();
+ VideoItem * item = (VideoItem*)pRecorder->item;
+ int row = filesTable->row(item);
+
+ // update file size every percent
+ if (value - item->lastSizeUpdate > 0.01)
+ {
+ updateSize(row);
+ item->lastSizeUpdate = value;
+ }
+
+ // update progress bar
+ QProgressBar * progressBar = (QProgressBar*)filesTable->cellWidget(row, vcProgress);
+ progressBar->setValue(value*10000);
+ progressBar->setFormat(QString("%1%").arg(value*100, 0, 'f', 2));
+}
+
+void PageVideos::encodingFinished(bool success)
+{
+ HWRecorder * pRecorder = (HWRecorder*)sender();
+ VideoItem * item = (VideoItem*)pRecorder->item;
+ int row = filesTable->row(item);
+
+ if (success)
+ {
+ // move file to destination
+ success = cfgdir->rename("VideoTemp/" + pRecorder->name, "Videos/" + item->name);
+ if (!success)
+ {
+ // unable to rename for some reason (maybe user entered incorrect name);
+ // try to use temp name instead.
+ success = cfgdir->rename("VideoTemp/" + pRecorder->name, "Videos/" + pRecorder->name);
+ if (success)
+ setName(item, pRecorder->name);
+ }
+ }
+
+ if (!success)
+ {
+ filesTable->removeRow(row);
+ return;
+ }
+
+ filesTable->setCellWidget(row, vcProgress, NULL); // remove progress bar
+ item->pRecorder = NULL;
+ updateSize(row);
+ updateDescription();
+}
+
+void PageVideos::cellDoubleClicked(int row, int column)
+{
+ play(row);
+}
+
+void PageVideos::cellChanged(int row, int column)
+{
+ // user can only edit name
+ if (column != vcName || nameChangedFromCode)
+ return;
+
+ // user has edited filename, so we should rename the file
+ VideoItem * item = nameItem(row);
+ QString oldName = item->name;
+ QString newName = item->text();
+ item->name = newName;
+ if (item->ready())
+ {
+ if(!cfgdir->rename("Videos/" + oldName, "Videos/" + newName))
+ {
+ // unable to rename for some reason (maybe user entered incorrect name),
+ // therefore restore old name in cell
+ setName(item, oldName);
+ }
+ }
+}
+
+void PageVideos::setName(VideoItem * item, const QString & newName)
+{
+ nameChangedFromCode = true;
+ item->setText(newName);
+ nameChangedFromCode = false;
+ item->name = newName;
+}
+
+int PageVideos::appendRow(const QString & name)
+{
+ int row = filesTable->rowCount();
+ filesTable->setRowCount(row+1);
+
+ // add 'name' item
+ QTableWidgetItem * item = new VideoItem(name);
+ item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
+ nameChangedFromCode = true;
+ filesTable->setItem(row, vcName, item);
+ nameChangedFromCode = false;
+
+ // add 'size' item
+ item = new QTableWidgetItem();
+ item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
+ item->setTextAlignment(Qt::AlignRight);
+ filesTable->setItem(row, vcSize, item);
+
+ // add 'progress' item
+ item = new QTableWidgetItem();
+ item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
+ filesTable->setItem(row, vcProgress, item);
+
+ return row;
+}
+
+VideoItem* PageVideos::nameItem(int row)
+{
+ return (VideoItem*)filesTable->item(row, vcName);
+}
+
+void PageVideos::updateDescription()
+{
+ VideoItem * item = nameItem(filesTable->currentRow());
+ QString desc = "";
+ if (item)
+ {
+ QString t = item->name;
+ desc += item->name + "\n";
+ // t.replace(".mp4", ".bmp");
+ // QMessageBox::information(this, "1", cfgdir->absoluteFilePath("Screenshots/" + t));
+ // m_pic.load(cfgdir->absoluteFilePath("Screenshots/" + t));
+ // m_pic = m_pic.scaledToWidth(400);
+ // m_thumbnail.setPixmap(m_pic);
+
+ if (item->ready())
+ {
+ QString path = item->path();
+ desc += tr("\nSize: ") + FileSizeStr(path) + "\n";
+ if (item->desc == "")
+ item->desc = LibavIteraction::instance().getFileInfo(path);
+ desc += item->desc;
+ }
+ else
+ desc += tr("(in progress...)");
+ }
+ labelDesc->setText(desc);
+}
+
+// user selected another cell, so we should change description
+void PageVideos::currentCellChanged(int row, int column, int previousRow, int previousColumn)
+{
+ updateDescription();
+}
+
+// open video file in external media player
+void PageVideos::play(int row)
+{
+ VideoItem * item = nameItem(row);
+ if (item->ready())
+ QDesktopServices::openUrl(QUrl("file:///" + item->path()));
+}
+
+void PageVideos::playSelectedFile()
+{
+ int index = filesTable->currentRow();
+ if (index != -1)
+ play(index);
+}
+
+void PageVideos::deleteSelectedFiles()
+{
+ QList<QTableWidgetItem*> items = filesTable->selectedItems();
+ int num = items.size() / vcNumColumns;
+ if (num == 0)
+ return;
+
+ // ask user if (s)he is serious
+ if (QMessageBox::question(this,
+ tr("Are you sure?"),
+ tr("Do you really want do remove %1 file(s)?").arg(num),
+ QMessageBox::Yes | QMessageBox::No)
+ != QMessageBox::Yes)
+ return;
+
+ // remove
+ foreach (QTableWidgetItem * witem, items)
+ {
+ if (witem->type() != QTableWidgetItem::UserType)
+ continue;
+ VideoItem * item = (VideoItem*)witem;
+ if (!item->ready())
+ item->pRecorder->deleteLater();
+ else
+ cfgdir->remove("Videos/" + item->name);
+ }
+}
+
+void PageVideos::keyPressEvent(QKeyEvent * pEvent)
+{
+ if (filesTable->hasFocus())
+ {
+ if (pEvent->key() == Qt::Key_Delete)
+ {
+ deleteSelectedFiles();
+ return;
+ }
+ if (pEvent->key() == Qt::Key_Enter) // doesn't work
+ {
+ playSelectedFile();
+ return;
+ }
+ }
+ AbstractPage::keyPressEvent(pEvent);
+}
+
+void PageVideos::openVideosDirectory()
+{
+ QDesktopServices::openUrl(QUrl("file:///"+cfgdir->absolutePath() + "/Videos"));
+}
--- a/QTfrontend/ui/page/pagevideos.h Sun Jun 24 20:31:26 2012 +0400
+++ b/QTfrontend/ui/page/pagevideos.h Sun Jun 24 20:57:02 2012 +0400
@@ -20,11 +20,12 @@
#ifndef PAGE_VIDEOS_H
#define PAGE_VIDEOS_H
-#include <QPushButton>
-#include <QTableWidget>
+#include <QPixmap>
#include "AbstractPage.h"
class GameUIConfig;
+class HWRecorder;
+class VideoItem;
class PageVideos : public AbstractPage
{
@@ -33,44 +34,77 @@
public:
PageVideos(QWidget* parent = 0);
- QComboBox *CBAVFormats;
- QComboBox *CBVideoCodecs;
- QComboBox *CBAudioCodecs;
QSpinBox *framerateBox;
QLineEdit *widthEdit;
QLineEdit *heightEdit;
- QCheckBox *CBUseGameRes;
- QCheckBox *CBRecordAudio;
+ QCheckBox *checkUseGameRes;
+ QCheckBox *checkRecordAudio;
- QString getFormat()
- { return CBAVFormats->itemData(CBAVFormats->currentIndex()).toString(); }
+ GameUIConfig * config;
- QString getVideoCodec()
- { return CBVideoCodecs->itemData(CBVideoCodecs->currentIndex()).toString(); }
+ QString format()
+ { return comboAVFormats->itemData(comboAVFormats->currentIndex()).toString(); }
- QString getAudioCodec()
- { return CBAudioCodecs->itemData(CBAudioCodecs->currentIndex()).toString(); }
+ QString videoCodec()
+ { return comboVideoCodecs->itemData(comboVideoCodecs->currentIndex()).toString(); }
+
+ QString audioCodec()
+ { return comboAudioCodecs->itemData(comboAudioCodecs->currentIndex()).toString(); }
void setDefaultCodecs();
bool tryCodecs(const QString & format, const QString & vcodec, const QString & acodec);
-
- GameUIConfig * config;
-
- signals:
+ void addRecorder(HWRecorder* pRecorder);
private:
+ // virtuals from AbstractPage
QLayout * bodyLayoutDefinition();
QLayout * footerLayoutDefinition();
void connectSignals();
- QPushButton *BtnDefaults;
+ // virtual from QWidget
+ void keyPressEvent(QKeyEvent * pEvent);
+
+ void setName(VideoItem * item, const QString & newName);
+ void updateSize(int row);
+ int appendRow(const QString & name);
+ VideoItem* nameItem(int row);
+ void play(int row);
+ void updateDescription();
+
+ // options group
+ QComboBox *comboAVFormats;
+ QComboBox *comboVideoCodecs;
+ QComboBox *comboAudioCodecs;
+ QPushButton *btnDefaults;
+
+ // file list group
QTableWidget *filesTable;
+ QPushButton *btnOpenDir;
+
+ // description group
+ QPushButton *btnPlay, *btnDelete;
+ QLabel *labelDesc;
+ QLabel *labelThumbnail;
+ QPixmap picThumbnail;
+
+ // this flag is used to distinguish if cell was changed from code or by user
+ // (in signal cellChanged)
+ bool nameChangedFromCode;
private slots:
void changeAVFormat(int index);
void changeUseGameRes(int state);
void changeRecordAudio(int state);
void setDefaultOptions();
+ void encodingFinished(bool success);
+ void updateProgress(float value);
+ void cellDoubleClicked(int row, int column);
+ void cellChanged(int row, int column);
+ void currentCellChanged(int row, int column, int previousRow, int previousColumn);
+ void playSelectedFile();
+ void deleteSelectedFiles();
+ void openVideosDirectory();
+ void updateFileList(const QString & path);
};
#endif // PAGE_VIDEOS_H
--- a/QTfrontend/util/libav_iteraction.cpp Sun Jun 24 20:31:26 2012 +0400
+++ b/QTfrontend/util/libav_iteraction.cpp Sun Jun 24 20:57:02 2012 +0400
@@ -24,6 +24,7 @@
#include <QVector>
#include <QList>
#include <QMessageBox>
+#include <QComboBox>
#include "libav_iteraction.h"
#include "HWApplication.h"
@@ -42,7 +43,7 @@
QString shortName;
QString longName;
bool isRecomended;
- // QString extension;
+ QString extension;
QVector<Codec*> codecs;
};
@@ -55,6 +56,7 @@
return instance;
}
+// test if given format supports given codec
bool FormatQueryCodec(AVOutputFormat *ofmt, enum CodecID codec_id)
{
#if LIBAVFORMAT_VERSION_MAJOR >= 54
@@ -89,13 +91,13 @@
continue;
// this encoders seems to be buggy
- if (strcmp(pCodec->name, "rv10") == 0 ||
- strcmp(pCodec->name, "rv20") == 0)
+ if (strcmp(pCodec->name, "rv10") == 0 || strcmp(pCodec->name, "rv20") == 0)
continue;
// doesn't support stereo sound
if (strcmp(pCodec->name, "real_144") == 0)
continue;
+
#if LIBAVCODEC_VERSION_MAJOR < 54
// FIXME: theese doesn't work for some reason
if (strcmp(pCodec->name, "libx264") == 0)
@@ -194,10 +196,11 @@
}
if (!hasVideoCodec)
continue;
- format.shortName = pFormat->name;
QString ext(pFormat->extensions);
ext.truncate(strcspn(pFormat->extensions, ","));
+ format.extension = ext;
+ format.shortName = pFormat->name;
format.longName = QString("%1 (*.%2)").arg(pFormat->long_name).arg(ext);
// FIXME: remove next line
@@ -209,7 +212,7 @@
}
}
-void LibavIteraction::FillFormats(QComboBox * pFormats)
+void LibavIteraction::fillFormats(QComboBox * pFormats)
{
// first insert recomended formats
foreach(const Format & format, formats)
@@ -229,9 +232,9 @@
pFormats->insertSeparator(sep);
}
-void LibavIteraction::FillCodecs(const QVariant & fmt, QComboBox * pVCodecs, QComboBox * pACodecs)
+void LibavIteraction::fillCodecs(const QString & fmt, QComboBox * pVCodecs, QComboBox * pACodecs)
{
- Format & format = formats[fmt.toString()];
+ Format & format = formats[fmt];
// first insert recomended codecs
foreach(Codec * codec, format.codecs)
@@ -267,3 +270,62 @@
if (asep != 0 && asep != pACodecs->count())
pACodecs->insertSeparator(asep);
}
+
+QString LibavIteraction::getExtension(const QString & format)
+{
+ return formats[format].extension;
+}
+
+QString tr(QString a)
+{
+ return a;
+}
+
+// get information abaout file (duration, resolution etc) in multiline string
+QString LibavIteraction::getFileInfo(const QString & filepath)
+{
+ AVFormatContext* pContext = NULL;
+ QByteArray utf8path = filepath.toUtf8();
+ if (avformat_open_input(&pContext, utf8path.data(), NULL, NULL) < 0)
+ return "";
+ if (avformat_find_stream_info(pContext, NULL) < 0)
+ return "";
+
+ int s = float(pContext->duration)/AV_TIME_BASE;
+ QString desc = QString(tr("Duration: %1m %2s\n")).arg(s/60).arg(s%60);
+ for (int i = 0; i < pContext->nb_streams; i++)
+ {
+ AVStream* pStream = pContext->streams[i];
+ if (!pStream)
+ continue;
+ AVCodecContext* pCodec = pContext->streams[i]->codec;
+ if (!pCodec)
+ continue;
+
+ if (pCodec->codec_type == AVMEDIA_TYPE_VIDEO)
+ {
+ desc += QString(tr("Video: %1x%2, ")).arg(pCodec->width).arg(pCodec->height);
+ if (pStream->avg_frame_rate.den)
+ {
+ float fps = float(pStream->avg_frame_rate.num)/pStream->avg_frame_rate.den;
+ desc += QString(tr("%1 fps, ")).arg(fps, 0, 'f', 2);
+ }
+ }
+ else if (pCodec->codec_type == AVMEDIA_TYPE_AUDIO)
+ desc += tr("Audio: ");
+ else
+ continue;
+ AVCodec* pDecoder = avcodec_find_decoder(pCodec->codec_id);
+ desc += pDecoder? pDecoder->name : "unknown";
+ desc += "\n";
+ }
+ AVDictionaryEntry* pComment = av_dict_get(pContext->metadata, "comment", NULL, 0);
+ if (pComment)
+ desc += QString("\n") + pComment->value;
+#if LIBAVCODEC_VERSION_MAJOR < 54
+ av_close_input_file(pContext);
+#else
+ avformat_close_input(&pContext);
+#endif
+ return desc;
+}
--- a/QTfrontend/util/libav_iteraction.h Sun Jun 24 20:31:26 2012 +0400
+++ b/QTfrontend/util/libav_iteraction.h Sun Jun 24 20:57:02 2012 +0400
@@ -34,8 +34,16 @@
static LibavIteraction & instance();
- void FillFormats(QComboBox * pFormats);
- void FillCodecs(const QVariant & format, QComboBox * pVCodecs, QComboBox * pACodecs);
+ // fill combo box with known file formats
+ void fillFormats(QComboBox * pFormats);
+
+ // fill combo boxes with known codecs for given formats
+ void fillCodecs(const QString & format, QComboBox * pVCodecs, QComboBox * pACodecs);
+
+ QString getExtension(const QString & format);
+
+ // get information about file (duration, resolution etc) in multiline string
+ QString getFileInfo(const QString & filepath);
};
#endif // LIBAV_ITERACTION
--- a/hedgewars/avwrapper.c Sun Jun 24 20:31:26 2012 +0400
+++ b/hedgewars/avwrapper.c Sun Jun 24 20:57:02 2012 +0400
@@ -1,6 +1,7 @@
#include <stdlib.h>
#include <stdio.h>
+#include <stdint.h>
#include <string.h>
#include <stdarg.h>
#include "libavformat/avformat.h"
@@ -17,7 +18,7 @@
static AVCodecContext* g_pVideo;
static int g_Width, g_Height;
-static int g_Frequency, g_Channels;
+static uint32_t g_Frequency, g_Channels;
static int g_VQuality, g_AQuality;
static AVRational g_Framerate;
static const char* g_pPreset;
@@ -26,8 +27,6 @@
static int16_t* g_pSamples;
static int g_NumSamples;
-static char g_Filename[1024];
-
/*
Initially I wrote code for latest ffmpeg, but on Linux (Ubuntu)
only older version is available from repository. That's why you see here
@@ -90,7 +89,10 @@
g_pAStream = av_new_stream(g_pContainer, 1);
#endif
if(!g_pAStream)
- FatalError("Could not allocate audio stream");
+ {
+ Log("Could not allocate audio stream");
+ return;
+ }
g_pAStream->id = 1;
g_pAudio = g_pAStream->codec;
@@ -118,7 +120,10 @@
// open it
if (avcodec_open2(g_pAudio, g_pACodec, NULL) < 0)
- FatalError("Could not open audio codec %s", g_pACodec->long_name);
+ {
+ Log("Could not open audio codec %s", g_pACodec->long_name);
+ return;
+ }
#if LIBAVCODEC_VERSION_MAJOR >= 54
if (g_pACodec->capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE)
@@ -131,7 +136,10 @@
g_pSamples = (int16_t*)av_malloc(g_NumSamples*g_Channels*sizeof(int16_t));
g_pAFrame = avcodec_alloc_frame();
if (!g_pAFrame)
- FatalError("Could not allocate frame");
+ {
+ Log("Could not allocate frame");
+ return;
+ }
}
// returns non-zero if there is more sound
@@ -242,7 +250,7 @@
g_pVFrame->linesize[3] = 0;
}
-static int WriteFrame( AVFrame* pFrame )
+static int WriteFrame(AVFrame* pFrame)
{
double AudioTime, VideoTime;
@@ -323,7 +331,7 @@
void AVWrapper_Init(
void (*pAddFileLogRaw)(const char*),
const char* pFilename,
- const char* pFinalFilename,
+ const char* pDesc,
const char* pSoundFile,
const char* pFormatName,
const char* pVCodecName,
@@ -331,18 +339,15 @@
const char* pVPreset,
int Width, int Height,
int FramerateNum, int FramerateDen,
- int Frequency, int Channels,
int VQuality, int AQuality)
{
AddFileLogRaw = pAddFileLogRaw;
av_log_set_callback( &LogCallback );
- g_Width = Width;
- g_Height = Height;
+ g_Width = Width & ~1; // make even (dimensions should be even)
+ g_Height = Height & ~1; // make even
g_Framerate.num = FramerateNum;
g_Framerate.den = FramerateDen;
- g_Frequency = Frequency;
- g_Channels = Channels;
g_VQuality = VQuality;
g_AQuality = AQuality;
g_pPreset = pVPreset;
@@ -362,13 +367,15 @@
g_pContainer->oformat = g_pFormat;
+ // store description of file
+ av_dict_set(&g_pContainer->metadata, "comment", pDesc, 0);
+
// append extesnion to filename
char ext[16];
strncpy(ext, g_pFormat->extensions, 16);
ext[15] = 0;
ext[strcspn(ext,",")] = 0;
snprintf(g_pContainer->filename, sizeof(g_pContainer->filename), "%s.%s", pFilename, ext);
- snprintf(g_Filename, sizeof(g_Filename), "%s.%s", pFinalFilename, ext);
// find codecs
g_pVCodec = avcodec_find_encoder_by_name(pVCodecName);
@@ -384,20 +391,23 @@
Log("Video codec \"%s\" was not found; video will be ignored.\n", pVCodecName);
if (g_pACodec)
- AddAudioStream();
+ {
+ g_pSoundFile = fopen(pSoundFile, "rb");
+ if (g_pSoundFile)
+ {
+ fread(&g_Frequency, 4, 1, g_pSoundFile);
+ fread(&g_Channels, 4, 1, g_pSoundFile);
+ AddAudioStream();
+ }
+ else
+ Log("Could not open %s", pSoundFile);
+ }
else
Log("Audio codec \"%s\" was not found; audio will be ignored.\n", pACodecName);
if (!g_pAStream && !g_pVStream)
FatalError("No video, no audio, aborting...");
- if (g_pAStream)
- {
- g_pSoundFile = fopen(pSoundFile, "rb");
- if (!g_pSoundFile)
- FatalError("Could not open %s", pSoundFile);
- }
-
// write format info to log
av_dump_format(g_pContainer, 0, g_pContainer->filename, 1);
@@ -428,9 +438,6 @@
// close the output file
if (!(g_pFormat->flags & AVFMT_NOFILE))
avio_close(g_pContainer->pb);
-
- // move file to destination
- rename(g_pContainer->filename, g_Filename);
// free everything
if (g_pVStream)
--- a/hedgewars/uVariables.pas Sun Jun 24 20:31:26 2012 +0400
+++ b/hedgewars/uVariables.pas Sun Jun 24 20:57:02 2012 +0400
@@ -56,8 +56,8 @@
cRecPrefix : shortstring;
cAVFormat : shortstring;
cVideoCodec : shortstring;
- cVideoFramerateNum : Int64;
- cVideoFramerateDen : Int64;
+ cVideoFramerateNum : LongInt = 25;
+ cVideoFramerateDen : LongInt = 1;
cVideoQuality : LongInt;
cVideoPreset : shortstring;
cAudioCodec : shortstring;
--- a/hedgewars/uVideoRec.pas Sun Jun 24 20:31:26 2012 +0400
+++ b/hedgewars/uVideoRec.pas Sun Jun 24 20:57:02 2012 +0400
@@ -44,7 +44,7 @@
implementation
-uses uVariables, uUtils, GLunit, SDLh, SysUtils;
+uses uVariables, uUtils, GLunit, SDLh, SysUtils, uIO;
{$IFDEF WIN32}
const AVWrapperLibName = 'libavwrapper.dll';
@@ -55,35 +55,35 @@
{$IFDEF WIN32}
procedure AVWrapper_Init(
AddLog: TAddFileLogRaw;
- filename, finalFilename, soundFile, format, vcodec, acodec, preset: PChar;
- width, height, framerateNum, framerateDen, frequency, channels, vquality, aquality: LongInt); cdecl; external AVWrapperLibName;
+ filename, desc, soundFile, format, vcodec, acodec, preset: PChar;
+ width, height, framerateNum, framerateDen, vquality, aquality: LongInt); cdecl; external AVWrapperLibName;
procedure AVWrapper_Close; cdecl; external AVWrapperLibName;
procedure AVWrapper_WriteFrame( pY, pCb, pCr: PByte ); cdecl; external AVWrapperLibName;
{$ELSE}
procedure AVWrapper_Init(
AddLog: TAddFileLogRaw;
- filename, finalFilename, soundFile, format, vcodec, acodec, preset: PChar;
- width, height, framerateNum, framerateDen, frequency, channels, vquality, aquality: LongInt); cdecl; external;
+ filename, desc, soundFile, format, vcodec, acodec, preset: PChar;
+ width, height, framerateNum, framerateDen, vquality, aquality: LongInt); cdecl; external;
procedure AVWrapper_Close; cdecl; external;
procedure AVWrapper_WriteFrame( pY, pCb, pCr: PByte ); cdecl; external;
{$ENDIF}
+type TFrame = record
+ ticks: LongWord;
+ CamX, CamY: LongInt;
+ zoom: single;
+ end;
+
var YCbCr_Planes: array[0..2] of PByte;
RGB_Buffer: PByte;
-
- frequency, channels: LongInt;
-
- cameraFile: TextFile;
+ cameraFile: File of TFrame;
audioFile: File;
-
- numPixels: LongInt;
-
- firstTick, nframes: Int64;
-
+ numPixels: LongWord;
+ startTime, numFrames: LongWord;
cameraFilePath, soundFilePath: shortstring;
function BeginVideoRecording: Boolean;
-var filename, finalFilename: shortstring;
+var filename, desc: shortstring;
begin
AddFileLog('BeginVideoRecording');
@@ -99,19 +99,27 @@
AddFileLog('Error: Could not read from ' + cameraFilePath);
exit(false);
end;
-
- ReadLn(cameraFile, frequency, channels);
{$IOCHECKS ON}
+ desc:= '';
+ if UserNick <> '' then
+ desc+= 'Player: ' + UserNick + #10;
+ if recordFileName <> '' then
+ desc+= 'Record: ' + recordFileName + #10;
+ if cMapName <> '' then
+ desc+= 'Map: ' + cMapName + #10;
+ if Theme <> '' then
+ desc+= 'Theme: ' + Theme + #10;
+ desc+= #0;
+
filename:= UserPathPrefix + '/VideoTemp/' + cRecPrefix + #0;
- finalFilename:= UserPathPrefix + '/Videos/' + cRecPrefix + #0;
soundFilePath:= UserPathPrefix + '/VideoTemp/' + cRecPrefix + '.sw' + #0;
cAVFormat+= #0;
cAudioCodec+= #0;
cVideoCodec+= #0;
cVideoPreset+= #0;
- AVWrapper_Init(@AddFileLogRaw, @filename[1], @finalFilename[1], @soundFilePath[1], @cAVFormat[1], @cVideoCodec[1], @cAudioCodec[1], @cVideoPreset[1],
- cScreenWidth, cScreenHeight, cVideoFramerateNum, cVideoFramerateDen, frequency, channels, cAudioQuality, cVideoQuality);
+ AVWrapper_Init(@AddFileLogRaw, @filename[1], @desc[1], @soundFilePath[1], @cAVFormat[1], @cVideoCodec[1], @cAudioCodec[1], @cVideoPreset[1],
+ cScreenWidth, cScreenHeight, cVideoFramerateNum, cVideoFramerateDen, cAudioQuality, cVideoQuality);
YCbCr_Planes[0]:= GetMem(numPixels);
YCbCr_Planes[1]:= GetMem(numPixels div 4);
@@ -144,6 +152,7 @@
AVWrapper_Close();
DeleteFile(cameraFilePath);
DeleteFile(soundFilePath);
+ SendIPC(_S'v');
end;
function pixel(x, y, color: LongInt): LongInt;
@@ -175,24 +184,25 @@
end;
AVWrapper_WriteFrame(YCbCr_Planes[0], YCbCr_Planes[1], YCbCr_Planes[2]);
+
+ // inform frontend that we have encoded new frame (p for progress)
+ SendIPC(_S'p');
end;
// returns new game ticks
function LoadNextCameraPosition: LongInt;
-var NextTime: LongInt;
- NextZoom: LongInt;
- NextWorldDx, NextWorldDy: LongInt;
+var frame: TFrame;
begin
{$IOCHECKS OFF}
if eof(cameraFile) then
exit(-1);
- ReadLn(cameraFile, NextTime, NextWorldDx, NextWorldDy, NextZoom);
+ BlockRead(cameraFile, frame, 1);
{$IOCHECKS ON}
- WorldDx:= NextWorldDx;
- WorldDy:= NextWorldDy + cScreenHeight div 2;
- zoom:= NextZoom/10000*cScreenWidth;
+ WorldDx:= frame.CamX;
+ WorldDy:= frame.CamY + cScreenHeight div 2;
+ zoom:= frame.zoom*cScreenWidth;
ZoomValue:= zoom;
- LoadNextCameraPosition:= NextTime;
+ LoadNextCameraPosition:= frame.ticks;
end;
// Callback which records sound.
@@ -208,15 +218,17 @@
procedure BeginPreRecording;
var format: word;
filePrefix, filename: shortstring;
+ frequency, channels: LongInt;
begin
AddFileLog('BeginPreRecording');
- nframes:= 0;
- firstTick:= SDL_GetTicks();
+ numFrames:= 0;
+ startTime:= SDL_GetTicks();
filePrefix:= FormatDateTime('YYYY-MM-DD_HH-mm-ss', Now());
Mix_QuerySpec(@frequency, @format, @channels);
+ AddFileLog('sound: frequency = ' + IntToStr(frequency) + ', format = ' + IntToStr(format) + ', channels = ' + IntToStr(channels));
if format <> $8010 then
begin
// TODO: support any audio format
@@ -244,8 +256,10 @@
AddFileLog('Error: Could not write to ' + filename);
exit;
end;
+
+ BlockWrite(audioFile, frequency, 4);
+ BlockWrite(audioFile, channels, 4);
{$IOCHECKS ON}
- WriteLn(cameraFile, inttostr(frequency) + ' ' + inttostr(channels));
// register callback for actual audio recording
Mix_SetPostMix(@RecordPostMix, nil);
@@ -267,13 +281,18 @@
end;
procedure SaveCameraPosition;
-var Ticks: LongInt;
+var curTime: LongInt;
+ frame: TFrame;
begin
- Ticks:= SDL_GetTicks();
- while (Ticks - firstTick)*cVideoFramerateNum > nframes*cVideoFramerateDen*1000 do
+ curTime:= SDL_GetTicks();
+ while Int64(curTime - startTime)*cVideoFramerateNum > Int64(numFrames)*cVideoFramerateDen*1000 do
begin
- WriteLn(cameraFile, inttostr(GameTicks) + ' ' + inttostr(WorldDx) + ' ' + inttostr(WorldDy - cScreenHeight div 2) + ' ' + inttostr(Round(zoom*10000/cScreenWidth)));
- inc(nframes);
+ frame.ticks:= GameTicks;
+ frame.CamX:= WorldDx;
+ frame.CamY:= WorldDy - cScreenHeight div 2;
+ frame.zoom:= zoom/cScreenWidth;
+ BlockWrite(cameraFile, frame, 1);
+ inc(numFrames);
end;
end;