# HG changeset patch # User Stepan777 # Date 1340557022 -14400 # Node ID fd707afbc3a288ba59bfbf8762b9865616c6e5ea # Parent 000e4543f2045fd1406b363bf35f78b13787bf0d 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/ diff -r 000e4543f204 -r fd707afbc3a2 QTfrontend/game.cpp --- 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); } diff -r 000e4543f204 -r fd707afbc3a2 QTfrontend/gameuiconfig.cpp --- 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(); } diff -r 000e4543f204 -r fd707afbc3a2 QTfrontend/hwform.cpp --- 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); } } diff -r 000e4543f204 -r fd707afbc3a2 QTfrontend/net/recorder.cpp --- 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 #include +//#include #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; diff -r 000e4543f204 -r fd707afbc3a2 QTfrontend/net/recorder.h --- 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 diff -r 000e4543f204 -r fd707afbc3a2 QTfrontend/ui/page/pagevideos.cpp --- 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 #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 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")); +} diff -r 000e4543f204 -r fd707afbc3a2 QTfrontend/ui/page/pagevideos.h --- 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 -#include +#include #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 diff -r 000e4543f204 -r fd707afbc3a2 QTfrontend/util/libav_iteraction.cpp --- 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 #include #include +#include #include "libav_iteraction.h" #include "HWApplication.h" @@ -42,7 +43,7 @@ QString shortName; QString longName; bool isRecomended; - // QString extension; + QString extension; QVector 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; +} diff -r 000e4543f204 -r fd707afbc3a2 QTfrontend/util/libav_iteraction.h --- 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 diff -r 000e4543f204 -r fd707afbc3a2 hedgewars/avwrapper.c --- 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 #include +#include #include #include #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) diff -r 000e4543f204 -r fd707afbc3a2 hedgewars/uVariables.pas --- 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; diff -r 000e4543f204 -r fd707afbc3a2 hedgewars/uVideoRec.pas --- 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;