# HG changeset patch # User Stepan777 # Date 1339424130 -14400 # Node ID baa69bd025d918242bb7963490a21481c93d9bd2 # Parent f484455dd05511b4ed5177b3e998e218307a2938 1. Implement new page in frontend with options for video recording. 2. Store temoprary files in different directory diff -r f484455dd055 -r baa69bd025d9 QTfrontend/CMakeLists.txt --- a/QTfrontend/CMakeLists.txt Mon Jun 11 17:56:10 2012 +0400 +++ b/QTfrontend/CMakeLists.txt Mon Jun 11 18:15:30 2012 +0400 @@ -154,6 +154,9 @@ endif() endif() +IF (WIN32) + link_directories(${CMAKE_SOURCE_DIR}/misc/winutils/lib) +ENDIF() add_executable(hedgewars WIN32 ${hwfr_src} @@ -165,6 +168,9 @@ set(HW_LINK_LIBS quazip + avformat + avcodec + avutil ${QT_LIBRARIES} ${SDL_LIBRARY} ${SDLMIXER_LIBRARY} diff -r f484455dd055 -r baa69bd025d9 QTfrontend/game.cpp --- a/QTfrontend/game.cpp Mon Jun 11 17:56:10 2012 +0400 +++ b/QTfrontend/game.cpp Mon Jun 11 18:15:30 2012 +0400 @@ -333,7 +333,7 @@ arguments << QString::number(config->translateQuality()); arguments << QString::number(config->stereoMode()); arguments << tr("en.txt"); - arguments << "30"; // framerate num + arguments << QString::number(config->rec_Framerate()); // framerate num arguments << "1"; // framerate den return arguments; diff -r f484455dd055 -r baa69bd025d9 QTfrontend/gameuiconfig.cpp --- a/QTfrontend/gameuiconfig.cpp Mon Jun 11 17:56:10 2012 +0400 +++ b/QTfrontend/gameuiconfig.cpp Mon Jun 11 18:15:30 2012 +0400 @@ -26,6 +26,7 @@ #include "gameuiconfig.h" #include "hwform.h" #include "pageoptions.h" +#include "pagevideos.h" #include "pagenetserver.h" #include "hwconsts.h" #include "fpsedit.h" @@ -42,6 +43,7 @@ resizeToConfigValues(); reloadValues(); + reloadVideosValues(); } void GameUIConfig::reloadValues(void) @@ -110,6 +112,29 @@ else if (depth > 16) depth = 32; } +void GameUIConfig::reloadVideosValues(void) +{ + Form->ui.pageVideos->framerateBox->setValue(value("videorec/fps",25).toUInt()); + bool useGameRes = value("videorec/usegameres",true).toBool(); + if (useGameRes) + { + QRect res = vid_Resolution(); + Form->ui.pageVideos->widthEdit->setText(QString::number(res.width())); + Form->ui.pageVideos->heightEdit->setText(QString::number(res.height())); + } + else + { + 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()); + if (!Form->ui.pageVideos->tryCodecs(value("videorec/format","no").toString(), + value("videorec/videocodec","no").toString(), + value("videorec/audiocodec","no").toString())) + Form->ui.pageVideos->setDefaultCodecs(); +} + QStringList GameUIConfig::GetTeamsList() { QDir teamdir; @@ -182,6 +207,22 @@ #ifdef SPARKLE_ENABLED setValue("misc/autoUpdate", isAutoUpdateEnabled()); #endif + + Form->gameSettings->sync(); +} + +void GameUIConfig::SaveVideosOptions() +{ + QRect res = rec_Resolution(); + setValue("videorec/format", AVFormat()); + setValue("videorec/videocodec", videoCodec()); + setValue("videorec/audiocodec", audioCodec()); + 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/audio", recordAudio()); + Form->gameSettings->sync(); } @@ -380,3 +421,37 @@ { return Form->ui.pageOptions->volumeBox->value() * 128 / 100; } + +QString GameUIConfig::AVFormat() +{ + return Form->ui.pageVideos->getFormat(); +} + +QString GameUIConfig::videoCodec() +{ + return Form->ui.pageVideos->getVideoCodec(); +} + +QString GameUIConfig::audioCodec() +{ + return Form->ui.pageVideos->getAudioCodec(); +} + +QRect GameUIConfig::rec_Resolution() +{ + if (Form->ui.pageVideos->CBUseGameRes->isChecked()) + return vid_Resolution(); + QRect res(0,0,0,0); + res.setWidth(Form->ui.pageVideos->widthEdit->text().toUInt()); + res.setHeight(Form->ui.pageVideos->heightEdit->text().toUInt()); +} + +int GameUIConfig::rec_Framerate() +{ + return Form->ui.pageVideos->framerateBox->value(); +} + +bool GameUIConfig::recordAudio() +{ + return Form->ui.pageVideos->CBRecordAudio->isChecked(); +} diff -r f484455dd055 -r baa69bd025d9 QTfrontend/gameuiconfig.h --- a/QTfrontend/gameuiconfig.h Mon Jun 11 17:56:10 2012 +0400 +++ b/QTfrontend/gameuiconfig.h Mon Jun 11 18:15:30 2012 +0400 @@ -59,18 +59,27 @@ void resizeToConfigValues(); quint32 stereoMode() const; + QString AVFormat(); + QString videoCodec(); + QString audioCodec(); + QRect rec_Resolution(); + int rec_Framerate(); + bool recordAudio(); + #ifdef __APPLE__ #ifdef SPARKLE_ENABLED bool isAutoUpdateEnabled(); #endif #endif - void reloadValues(void); + void reloadValues(); + void reloadVideosValues(); signals: void frontendFullscreen(bool value); public slots: void SaveOptions(); + void SaveVideosOptions(); void updNetNick(); private: bool netPasswordIsValid(); diff -r f484455dd055 -r baa69bd025d9 QTfrontend/hwform.cpp --- a/QTfrontend/hwform.cpp Mon Jun 11 17:56:10 2012 +0400 +++ b/QTfrontend/hwform.cpp Mon Jun 11 18:15:30 2012 +0400 @@ -76,6 +76,7 @@ #include "pagegamestats.h" #include "pageplayrecord.h" #include "pagedata.h" +#include "pagevideos.h" #include "hwconsts.h" #include "newnetclient.h" #include "gamecfgwidget.h" @@ -141,6 +142,8 @@ config = new GameUIConfig(this, cfgdir->absolutePath() + "/hedgewars.ini"); + ui.pageVideos->config = config; + #ifdef __APPLE__ panel = new M3Panel; @@ -199,6 +202,9 @@ connect(ui.pageNetGame, SIGNAL(DLCClicked()), pageSwitchMapper, SLOT(map())); pageSwitchMapper->setMapping(ui.pageNetGame, ID_PAGE_DATADOWNLOAD); + connect(ui.pageMain->BtnVideos, SIGNAL(clicked()), pageSwitchMapper, SLOT(map())); + pageSwitchMapper->setMapping(ui.pageMain->BtnVideos, ID_PAGE_VIDEOS); + //connect(ui.pageMain->BtnExit, SIGNAL(pressed()), this, SLOT(btnExitPressed())); //connect(ui.pageMain->BtnExit, SIGNAL(clicked()), this, SLOT(btnExitClicked())); @@ -289,6 +295,7 @@ connect(ui.pageConnecting, SIGNAL(cancelConnection()), this, SLOT(GoBack())); + connect(ui.pageVideos, SIGNAL(goBack()), config, SLOT(SaveVideosOptions())); ammoSchemeModel = new AmmoSchemeModel(this, cfgdir->absolutePath() + "/schemes.ini"); ui.pageScheme->setModel(ammoSchemeModel); @@ -604,6 +611,11 @@ config->reloadValues(); } + if (id == ID_PAGE_VIDEOS ) + { + config->reloadVideosValues(); + } + // load and save ignore/friends lists if (lastid == ID_PAGE_NETGAME) // leaving a room ui.pageNetGame->pChatWidget->saveLists(ui.pageOptions->editNetNick->text()); @@ -1409,15 +1421,16 @@ } } - QDir videosDir(cfgdir->absolutePath() + "/Videos/"); + // encode videos + QDir videosDir(cfgdir->absolutePath() + "/VideoTemp/"); QStringList files = videosDir.entryList(QStringList("*.txtout"), QDir::Files); - for (QStringList::iterator str = files.begin(); str != files.end(); str++) + foreach (const QString & str, files) { - str->chop(7); // remove ".txtout" - // need to rename this file to not open it twice - videosDir.rename(*str + ".txtout", *str + ".txtin"); + 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, *str); + pRecorder->EncodeVideo(record, prefix); } } @@ -1459,6 +1472,7 @@ xfire_free(); #endif config->SaveOptions(); + config->SaveVideosOptions(); event->accept(); } diff -r f484455dd055 -r baa69bd025d9 QTfrontend/hwform.h --- a/QTfrontend/hwform.h Mon Jun 11 17:56:10 2012 +0400 +++ b/QTfrontend/hwform.h Mon Jun 11 18:15:30 2012 +0400 @@ -175,6 +175,7 @@ ID_PAGE_DRAWMAP , ID_PAGE_DATADOWNLOAD , ID_PAGE_FEEDBACK , + ID_PAGE_VIDEOS, MAX_PAGE }; QPointer game; diff -r f484455dd055 -r baa69bd025d9 QTfrontend/main.cpp --- a/QTfrontend/main.cpp Mon Jun 11 17:56:10 2012 +0400 +++ b/QTfrontend/main.cpp Mon Jun 11 18:15:30 2012 +0400 @@ -200,6 +200,7 @@ checkForDir(cfgdir->absolutePath() + "/Teams"); checkForDir(cfgdir->absolutePath() + "/Logs"); checkForDir(cfgdir->absolutePath() + "/Videos"); + checkForDir(cfgdir->absolutePath() + "/VideoTemp"); } datadir->cd(bindir->absolutePath()); diff -r f484455dd055 -r baa69bd025d9 QTfrontend/net/recorder.cpp --- a/QTfrontend/net/recorder.cpp Mon Jun 11 17:56:10 2012 +0400 +++ b/QTfrontend/net/recorder.cpp Mon Jun 11 18:15:30 2012 +0400 @@ -37,7 +37,7 @@ void HWRecorder::onClientDisconnect() { } - + void HWRecorder::onClientRead() { quint8 msglen; @@ -69,7 +69,7 @@ QStringList HWRecorder::getArguments() { QStringList arguments; - QRect resolution = config->vid_Resolution(); + QRect resolution = config->rec_Resolution(); arguments << cfgdir->absolutePath(); arguments << QString::number(resolution.width()); arguments << QString::number(resolution.height()); @@ -87,14 +87,17 @@ arguments << QString::number(config->translateQuality()); arguments << QString::number(config->stereoMode()); arguments << HWGame::tr("en.txt"); - arguments << "30"; // framerate num + arguments << QString::number(config->rec_Framerate()); // framerate num arguments << "1"; // framerate den arguments << prefix; - arguments << "mp4"; - arguments << "mpeg4"; // arguments << "libx264"; + arguments << config->AVFormat(); + arguments << config->videoCodec(); arguments << "5"; // video quality arguments << "medium"; - arguments << "libmp3lame"; + if (config->recordAudio()) + arguments << config->audioCodec(); + else + arguments << "no"; arguments << "5"; // audio quality return arguments; diff -r f484455dd055 -r baa69bd025d9 QTfrontend/ui/page/pagemain.cpp --- a/QTfrontend/ui/page/pagemain.cpp Mon Jun 11 17:56:10 2012 +0400 +++ b/QTfrontend/ui/page/pagemain.cpp Mon Jun 11 18:15:30 2012 +0400 @@ -85,9 +85,13 @@ bottomLayout->setStretch(0,1); btnBack->setWhatsThis(tr("Exit game")); - - BtnSetup = addButton(":/res/Settings.png", bottomLayout, 1, true); + + BtnVideos = addButton(":/res/Record.png", bottomLayout, 1, true); + BtnVideos->setWhatsThis(tr("Manage videos recorded from game")); + + BtnSetup = addButton(":/res/Settings.png", bottomLayout, 2, true); BtnSetup->setWhatsThis(tr("Edit game preferences")); + return bottomLayout; } diff -r f484455dd055 -r baa69bd025d9 QTfrontend/ui/page/pagemain.h --- a/QTfrontend/ui/page/pagemain.h Mon Jun 11 17:56:10 2012 +0400 +++ b/QTfrontend/ui/page/pagemain.h Mon Jun 11 18:15:30 2012 +0400 @@ -34,6 +34,7 @@ QPushButton * BtnFeedback; QPushButton * BtnInfo; QPushButton * BtnDataDownload; + QPushButton * BtnVideos; QLabel * mainNote; private: diff -r f484455dd055 -r baa69bd025d9 QTfrontend/ui/page/pagevideos.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/QTfrontend/ui/page/pagevideos.cpp Mon Jun 11 18:15:30 2012 +0400 @@ -0,0 +1,262 @@ +/* + * Hedgewars, a free turn based strategy game + * Copyright (c) 2004-2012 Andrey Korotaev + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pagevideos.h" +#include "igbox.h" +#include "libav_iteraction.h" +#include "gameuiconfig.h" + +QLayout * PageVideos::bodyLayoutDefinition() +{ + QGridLayout * pageLayout = new QGridLayout(); + pageLayout->setColumnStretch(0, 1); + pageLayout->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); + + // Label for format + QLabel *labelFormat = new QLabel(groupRec); + labelFormat->setText(QLabel::tr("Format")); + RecLayout->addWidget(labelFormat, 0, 0); + + // List of supported formats + CBAVFormats = new QComboBox(groupRec); + RecLayout->addWidget(CBAVFormats, 0, 1, 1, 4); + LibavIteraction::instance().FillFormats(CBAVFormats); + + QFrame * hr = new QFrame(groupRec); + hr->setFrameStyle(QFrame::HLine); + hr->setLineWidth(3); + hr->setFixedHeight(10); + RecLayout->addWidget(hr, 1, 0, 1, 5); + + // Label for audio codec + QLabel *labelACodec = new QLabel(groupRec); + labelACodec->setText(QLabel::tr("Audio codec")); + RecLayout->addWidget(labelACodec, 2, 0); + + // List of supported audio codecs + CBAudioCodecs = new QComboBox(groupRec); + RecLayout->addWidget(CBAudioCodecs, 2, 1, 1, 3); + + // record audio + CBRecordAudio = new QCheckBox(groupRec); + CBRecordAudio->setText(QCheckBox::tr("Record audio")); + RecLayout->addWidget(CBRecordAudio, 2, 4); + + hr = new QFrame(groupRec); + hr->setFrameStyle(QFrame::HLine); + hr->setLineWidth(3); + hr->setFixedHeight(10); + RecLayout->addWidget(hr, 3, 0, 1, 5); + + // Label for video codec + QLabel *labelVCodec = new QLabel(groupRec); + labelVCodec->setText(QLabel::tr("Video codec")); + RecLayout->addWidget(labelVCodec, 4, 0); + + // List of supported video codecs + CBVideoCodecs = new QComboBox(groupRec); + RecLayout->addWidget(CBVideoCodecs, 4, 1, 1, 4); + + // Label for resolution + QLabel *labelRes = new QLabel(groupRec); + labelRes->setText(QLabel::tr("Resolution")); + RecLayout->addWidget(labelRes, 5, 0); + + // width + widthEdit = new QLineEdit(groupRec); + widthEdit->setValidator(new QIntValidator(this)); + RecLayout->addWidget(widthEdit, 5, 1); + + // x + QLabel *labelX = new QLabel(groupRec); + labelX->setText("X"); + RecLayout->addWidget(labelX, 5, 2); + + // height + heightEdit = new QLineEdit(groupRec); + heightEdit->setValidator(new QIntValidator(groupRec)); + RecLayout->addWidget(heightEdit, 5, 3); + + // use game res + CBUseGameRes = new QCheckBox(groupRec); + CBUseGameRes->setText(QCheckBox::tr("Use game resolution")); + RecLayout->addWidget(CBUseGameRes, 5, 4); + + // Label for framerate + QLabel *labelFramerate = new QLabel(groupRec); + labelFramerate->setText(QLabel::tr("Framerate")); + RecLayout->addWidget(labelFramerate, 6, 0); + + // framerate + framerateBox = new QSpinBox(groupRec); + framerateBox->setRange(1, 200); + framerateBox->setSingleStep(1); + RecLayout->addWidget(framerateBox, 6, 1); + + BtnDefaults = new QPushButton(groupRec); + BtnDefaults->setText(QPushButton::tr("Set default options")); + RecLayout->addWidget(BtnDefaults, 7, 0, 1, 5); + + pageLayout->addWidget(groupRec, 0, 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")); + + QStringList columns; + columns << tr("Name") << tr("Lenght") << tr("..."); + + filesTable = new QTableWidget(groupTable); + filesTable->setColumnCount(3); + filesTable->setHorizontalHeaderLabels(columns); + QVBoxLayout *box = new QVBoxLayout(groupTable); + box->addWidget(filesTable); + + pageLayout->addWidget(groupTable, 0, 1, 2, 1); + } + + return pageLayout; +} + +QLayout * PageVideos::footerLayoutDefinition() +{ + return NULL; +} + +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())); +} + +PageVideos::PageVideos(QWidget* parent) : AbstractPage(parent) +{ + initPage(); +} + +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) + { + CBRecordAudio->setChecked(false); + CBRecordAudio->setEnabled(false); + } + else + CBRecordAudio->setEnabled(true); + int iVCodec = CBVideoCodecs->findData(prevVCodec); + if (iVCodec != -1) + CBVideoCodecs->setCurrentIndex(iVCodec); + int iACodec = CBAudioCodecs->findData(prevACodec); + if (iACodec != -1) + CBAudioCodecs->setCurrentIndex(iACodec); +} + +void PageVideos::changeUseGameRes(int state) +{ + if (state && config) + { + QRect resolution = config->vid_Resolution(); + widthEdit->setText(QString::number(resolution.width())); + heightEdit->setText(QString::number(resolution.height())); + } + widthEdit->setEnabled(!state); + heightEdit->setEnabled(!state); +} + +void PageVideos::changeRecordAudio(int state) +{ + CBAudioCodecs->setEnabled(!!state); +} + +void PageVideos::setDefaultCodecs() +{ + if (tryCodecs("mp4", "libx264", "libmp3lame")) + return; + if (tryCodecs("mp4", "libx264", "ac3_fixed")) + return; + if (tryCodecs("avi", "libxvid", "libmp3lame")) + return; + if (tryCodecs("avi", "libxvid", "ac3_fixed")) + return; + if (tryCodecs("avi", "mpeg4", "libmp3lame")) + return; + if (tryCodecs("avi", "mpeg4", "ac3_fixed")) + return; + + // this shouldn't happen, just in case + if (tryCodecs("ogg", "libtheora", "libvorbis")) + return; + tryCodecs("ogg", "libtheora", "flac"); +} + +void PageVideos::setDefaultOptions() +{ + framerateBox->setValue(25); + CBRecordAudio->setChecked(true); + CBUseGameRes->setChecked(true); + setDefaultCodecs(); +} + +bool PageVideos::tryCodecs(const QString & format, const QString & vcodec, const QString & acodec) +{ + int iFormat = CBAVFormats->findData(format); + if (iFormat == -1) + return false; + CBAVFormats->setCurrentIndex(iFormat); + + int iVCodec = CBVideoCodecs->findData(vcodec); + if (iVCodec == -1) + return false; + CBVideoCodecs->setCurrentIndex(iVCodec); + + int iACodec = CBAudioCodecs->findData(acodec); + if (iACodec == -1 && CBRecordAudio->isChecked()) + return false; + if (iACodec != -1) + CBAudioCodecs->setCurrentIndex(iACodec); + + return true; +} diff -r f484455dd055 -r baa69bd025d9 QTfrontend/ui/page/pagevideos.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/QTfrontend/ui/page/pagevideos.h Mon Jun 11 18:15:30 2012 +0400 @@ -0,0 +1,76 @@ +/* + * Hedgewars, a free turn based strategy game + * Copyright (c) 2004-2012 Andrey Korotaev + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + + +#ifndef PAGE_VIDEOS_H +#define PAGE_VIDEOS_H + +#include +#include +#include "AbstractPage.h" + +class GameUIConfig; + +class PageVideos : public AbstractPage +{ + Q_OBJECT + + public: + PageVideos(QWidget* parent = 0); + + QComboBox *CBAVFormats; + QComboBox *CBVideoCodecs; + QComboBox *CBAudioCodecs; + QSpinBox *framerateBox; + QLineEdit *widthEdit; + QLineEdit *heightEdit; + QCheckBox *CBUseGameRes; + QCheckBox *CBRecordAudio; + + QString getFormat() + { return CBAVFormats->itemData(CBAVFormats->currentIndex()).toString(); } + + QString getVideoCodec() + { return CBVideoCodecs->itemData(CBVideoCodecs->currentIndex()).toString(); } + + QString getAudioCodec() + { return CBAudioCodecs->itemData(CBAudioCodecs->currentIndex()).toString(); } + + void setDefaultCodecs(); + bool tryCodecs(const QString & format, const QString & vcodec, const QString & acodec); + + GameUIConfig * config; + + signals: + + private: + QLayout * bodyLayoutDefinition(); + QLayout * footerLayoutDefinition(); + void connectSignals(); + + QPushButton *BtnDefaults; + QTableWidget *filesTable; + + private slots: + void changeAVFormat(int index); + void changeUseGameRes(int state); + void changeRecordAudio(int state); + void setDefaultOptions(); +}; + +#endif // PAGE_VIDEOS_H diff -r f484455dd055 -r baa69bd025d9 QTfrontend/ui_hwform.cpp --- a/QTfrontend/ui_hwform.cpp Mon Jun 11 17:56:10 2012 +0400 +++ b/QTfrontend/ui_hwform.cpp Mon Jun 11 18:15:30 2012 +0400 @@ -46,6 +46,7 @@ #include "pagegamestats.h" #include "pageplayrecord.h" #include "pagedata.h" +#include "pagevideos.h" #include "hwconsts.h" void Ui_HWForm::setupUi(HWForm *HWForm) @@ -145,4 +146,7 @@ pageFeedback = new PageFeedback(); Pages->addWidget(pageFeedback); + + pageVideos = new PageVideos(); + Pages->addWidget(pageVideos); } diff -r f484455dd055 -r baa69bd025d9 QTfrontend/ui_hwform.h --- a/QTfrontend/ui_hwform.h Mon Jun 11 17:56:10 2012 +0400 +++ b/QTfrontend/ui_hwform.h Mon Jun 11 18:15:30 2012 +0400 @@ -43,6 +43,7 @@ class PageAdmin; class PageNetType; class PageDrawMap; +class PageVideos; class QStackedLayout; class QFont; class QWidget; @@ -78,6 +79,7 @@ PageNetType *pageNetType; PageCampaign *pageCampaign; PageDrawMap *pageDrawMap; + PageVideos *pageVideos; QStackedLayout *Pages; QFont *font14; diff -r f484455dd055 -r baa69bd025d9 QTfrontend/util/libav_iteraction.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/QTfrontend/util/libav_iteraction.cpp Mon Jun 11 18:15:30 2012 +0400 @@ -0,0 +1,269 @@ +/* + * Hedgewars, a free turn based strategy game + * Copyright (c) 2004-2012 Andrey Korotaev + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#define __STDC_CONSTANT_MACROS +extern "C" +{ +#include "libavformat/avformat.h" +} +#include +#include +#include + +#include "libav_iteraction.h" +#include "HWApplication.h" + +struct Codec +{ + CodecID id; + bool isAudio; + QString shortName; // used for identification + QString longName; // used for displaying to user + bool isRecomended; +}; + +struct Format +{ + QString shortName; + QString longName; + bool isRecomended; + // QString extension; + QVector codecs; +}; + +QList codecs; +QMap formats; + +LibavIteraction & LibavIteraction::instance() +{ + static LibavIteraction instance; + return instance; +} + +bool FormatQueryCodec(AVOutputFormat *ofmt, enum CodecID codec_id) +{ +#if LIBAVFORMAT_VERSION_MAJOR >= 54 + return avformat_query_codec(ofmt, codec_id, FF_COMPLIANCE_NORMAL) == 1; +#else + if (ofmt->codec_tag) + return !!av_codec_get_tag(ofmt->codec_tag, codec_id); + return codec_id == ofmt->video_codec || codec_id == ofmt->audio_codec; +#endif +} + +LibavIteraction::LibavIteraction() +{ + // initialize libav and register all codecs and formats + av_register_all(); + + // get list of all codecs + AVCodec* pCodec = NULL; + while (pCodec = av_codec_next(pCodec)) + { +#if LIBAVCODEC_VERSION_MAJOR >= 54 + if (!av_codec_is_encoder(pCodec)) +#else + if (!pCodec->encode) +#endif + continue; + + if (pCodec->capabilities & CODEC_CAP_EXPERIMENTAL) + continue; + + if (pCodec->type != AVMEDIA_TYPE_VIDEO && pCodec->type != AVMEDIA_TYPE_AUDIO) + continue; + + // this encoders seems to be buggy + 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) + continue; + if (strcmp(pCodec->name, "libxvid") == 0) + continue; +#endif + + if (pCodec->type == AVMEDIA_TYPE_VIDEO) + { + if (pCodec->supported_framerates != NULL) + continue; + + // check if codec supports yuv 4:2:0 format + if (!pCodec->pix_fmts) + continue; + bool yuv420Supported = false; + for (const PixelFormat* pfmt = pCodec->pix_fmts; *pfmt != -1; pfmt++) + if (*pfmt == PIX_FMT_YUV420P) + { + yuv420Supported = true; + break; + } + if (!yuv420Supported) + continue; + } + if (pCodec->type == AVMEDIA_TYPE_AUDIO) + { + // check if codec supports signed 16-bit format + if (!pCodec->sample_fmts) + continue; + bool s16Supported = false; + for (const AVSampleFormat* pfmt = pCodec->sample_fmts; *pfmt != -1; pfmt++) + if (*pfmt == AV_SAMPLE_FMT_S16/* || *pfmt == AV_SAMPLE_FMT_FLT*/) + { + s16Supported = true; + break; + } + if (!s16Supported) + continue; + } + // add codec to list of codecs + codecs.push_back(Codec()); + Codec & codec = codecs.back(); + codec.id = pCodec->id; + codec.isAudio = pCodec->type == AVMEDIA_TYPE_AUDIO; + codec.shortName = pCodec->name; + codec.longName = pCodec->long_name; + + codec.isRecomended = false; + if (strcmp(pCodec->name, "libx264") == 0) + { + codec.longName = "H.264/MPEG-4 Part 10 AVC (x264)"; + codec.isRecomended = true; + } + else if (strcmp(pCodec->name, "libxvid") == 0) + { + codec.longName = "MPEG-4 Part 2 (Xvid)"; + codec.isRecomended = true; + } + else if (strcmp(pCodec->name, "libmp3lame") == 0) + { + codec.longName = "MP3 (MPEG audio layer 3) (LAME)"; + codec.isRecomended = true; + } + else + codec.longName = pCodec->long_name; + + if (strcmp(pCodec->name, "mpeg4") == 0 || strcmp(pCodec->name, "ac3_fixed") == 0) + codec.isRecomended = true; + + // FIXME: remove next line + codec.longName += QString(" (%1)").arg(codec.shortName); + } + + // get list of all formats + AVOutputFormat* pFormat = NULL; + while (pFormat = av_oformat_next(pFormat)) + { + if (!pFormat->extensions) + continue; + + // skip some strange formats to not confuse users + if (strstr(pFormat->long_name, "raw")) + continue; + + Format format; + bool hasVideoCodec = false; + for (QList::iterator codec = codecs.begin(); codec != codecs.end(); ++codec) + { + if (!FormatQueryCodec(pFormat, codec->id)) + continue; + format.codecs.push_back(&*codec); + if (!codec->isAudio) + hasVideoCodec = true; + } + if (!hasVideoCodec) + continue; + format.shortName = pFormat->name; + + QString ext(pFormat->extensions); + ext.truncate(strcspn(pFormat->extensions, ",")); + format.longName = QString("%1 (*.%2)").arg(pFormat->long_name).arg(ext); + + // FIXME: remove next line + format.longName += QString(" (%1)").arg(format.shortName); + + format.isRecomended = strcmp(pFormat->name, "mp4") == 0 || strcmp(pFormat->name, "avi") == 0; + + formats[pFormat->name] = format; + } +} + +void LibavIteraction::FillFormats(QComboBox * pFormats) +{ + // first insert recomended formats + foreach(const Format & format, formats) + if (format.isRecomended) + pFormats->addItem(format.longName, format.shortName); + + // remember where to place separator between recomended and other formats + int sep = pFormats->count(); + + // insert remaining formats + foreach(const Format & format, formats) + if (!format.isRecomended) + pFormats->addItem(format.longName, format.shortName); + + // insert separator if necessary + if (sep != 0 && sep != pFormats->count()) + pFormats->insertSeparator(sep); +} + +void LibavIteraction::FillCodecs(const QVariant & fmt, QComboBox * pVCodecs, QComboBox * pACodecs) +{ + Format & format = formats[fmt.toString()]; + + // first insert recomended codecs + foreach(Codec * codec, format.codecs) + { + if (codec->isRecomended) + { + if (codec->isAudio) + pACodecs->addItem(codec->longName, codec->shortName); + else + pVCodecs->addItem(codec->longName, codec->shortName); + } + } + + // remember where to place separators between recomended and other codecs + int vsep = pVCodecs->count(); + int asep = pACodecs->count(); + + // insert remaining codecs + foreach(Codec * codec, format.codecs) + { + if (!codec->isRecomended) + { + if (codec->isAudio) + pACodecs->addItem(codec->longName, codec->shortName); + else + pVCodecs->addItem(codec->longName, codec->shortName); + } + } + + // insert separators if necessary + if (vsep != 0 && vsep != pVCodecs->count()) + pVCodecs->insertSeparator(vsep); + if (asep != 0 && asep != pACodecs->count()) + pACodecs->insertSeparator(asep); +} diff -r f484455dd055 -r baa69bd025d9 QTfrontend/util/libav_iteraction.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/QTfrontend/util/libav_iteraction.h Mon Jun 11 18:15:30 2012 +0400 @@ -0,0 +1,41 @@ +/* + * Hedgewars, a free turn based strategy game + * Copyright (c) 2004-2012 Andrey Korotaev + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#ifndef LIBAV_ITERACTION +#define LIBAV_ITERACTION + +#include + +/** + * @brief Class for interacting with ffmpeg/libav libraries + * + * @see singleton pattern + */ +class LibavIteraction +{ + LibavIteraction(); + +public: + + static LibavIteraction & instance(); + + void FillFormats(QComboBox * pFormats); + void FillCodecs(const QVariant & format, QComboBox * pVCodecs, QComboBox * pACodecs); +}; + +#endif // LIBAV_ITERACTION diff -r f484455dd055 -r baa69bd025d9 hedgewars/ArgParsers.inc --- a/hedgewars/ArgParsers.inc Mon Jun 11 17:56:10 2012 +0400 +++ b/hedgewars/ArgParsers.inc Mon Jun 11 18:15:30 2012 +0400 @@ -78,7 +78,6 @@ cVideoQuality:= StrToInt(ParamStr(23)); cVideoPreset:= ParamStr(24); cAudioCodec:= ParamStr(25); - // cRecordAudio:= cAudioCodec <> 'no'; cAudioQuality:= StrToInt(ParamStr(26)); end; {$ENDIF} diff -r f484455dd055 -r baa69bd025d9 hedgewars/avwrapper.c --- a/hedgewars/avwrapper.c Mon Jun 11 17:56:10 2012 +0400 +++ b/hedgewars/avwrapper.c Mon Jun 11 18:15:30 2012 +0400 @@ -26,6 +26,8 @@ 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 @@ -82,7 +84,7 @@ static void AddAudioStream() { -#if LIBAVCODEC_VERSION_MAJOR >= 54 +#if LIBAVFORMAT_VERSION_MAJOR >= 54 g_pAStream = avformat_new_stream(g_pContainer, g_pACodec); #else g_pAStream = av_new_stream(g_pContainer, 1); @@ -183,7 +185,7 @@ // add a video output stream static void AddVideoStream() { -#if LIBAVCODEC_VERSION_MAJOR >= 54 +#if LIBAVFORMAT_VERSION_MAJOR >= 54 g_pVStream = avformat_new_stream(g_pContainer, g_pVCodec); #else g_pVStream = av_new_stream(g_pContainer, 0); @@ -192,7 +194,7 @@ FatalError("Could not allocate video stream"); g_pVideo = g_pVStream->codec; - + avcodec_get_context_defaults3(g_pVideo, g_pVCodec); g_pVideo->codec_id = g_pVCodec->id; @@ -321,6 +323,7 @@ void AVWrapper_Init( void (*pAddFileLogRaw)(const char*), const char* pFilename, + const char* pFinalFilename, const char* pSoundFile, const char* pFormatName, const char* pVCodecName, @@ -360,10 +363,12 @@ g_pContainer->oformat = g_pFormat; // append extesnion to filename - snprintf(g_pContainer->filename, sizeof(g_pContainer->filename), - "%s.%*s", - pFilename, - strcspn(g_pFormat->extensions, ","), g_pFormat->extensions); + 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); @@ -423,6 +428,9 @@ // 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 f484455dd055 -r baa69bd025d9 hedgewars/hwengine.pas --- a/hedgewars/hwengine.pas Mon Jun 11 17:56:10 2012 +0400 +++ b/hedgewars/hwengine.pas Mon Jun 11 18:15:30 2012 +0400 @@ -415,10 +415,12 @@ isDeveloperMode:= false; TryDo(InitStepsFlags = cifAllInited, 'Some parameters not set (flags = ' + inttostr(InitStepsFlags) + ')', true); ParseCommand('rotmask', true); - + +{$IFDEF USE_VIDEO_RECORDING} if GameType = gmtRecord then RecorderMainLoop() else +{$ENDIF} MainLoop(); // clean up all the memory allocated diff -r f484455dd055 -r baa69bd025d9 hedgewars/options.inc --- a/hedgewars/options.inc Mon Jun 11 17:56:10 2012 +0400 +++ b/hedgewars/options.inc Mon Jun 11 18:15:30 2012 +0400 @@ -50,6 +50,7 @@ {$DEFINE USE_TOUCH_INTERFACE} {$ELSE} {$DEFINE USE_AM_NUMCOLUMN} + {$DEFINE USE_VIDEO_RECORDING} {$ENDIF} @@ -63,7 +64,6 @@ {$ENDIF} {$ENDIF} - {$DEFINE USE_VIDEO_RECORDING} {$IFDEF PAS2C} {$DEFINE NOCONSOLE} diff -r f484455dd055 -r baa69bd025d9 hedgewars/uStore.pas --- a/hedgewars/uStore.pas Mon Jun 11 17:56:10 2012 +0400 +++ b/hedgewars/uStore.pas Mon Jun 11 18:15:30 2012 +0400 @@ -40,7 +40,9 @@ procedure ShowWeaponTooltip(x, y: LongInt); procedure FreeWeaponTooltip; procedure MakeCrossHairs; +{$IFDEF USE_VIDEO_RECORDING} procedure InitOffscreenOpenGL; +{$ENDIF} procedure WarpMouse(x, y: Word); inline; procedure SwapBuffers; inline; @@ -1022,6 +1024,7 @@ WeaponTooltipTex:= nil end; +{$IFDEF USE_VIDEO_RECORDING} procedure InitOffscreenOpenGL; var ArgCount: LongInt; PrgName: pchar; @@ -1034,6 +1037,7 @@ glutHideWindow(); SetupOpenGL(); end; +{$ENDIF} procedure chFullScr(var s: shortstring); var flags: Longword = 0; diff -r f484455dd055 -r baa69bd025d9 hedgewars/uVideoRec.pas --- a/hedgewars/uVideoRec.pas Mon Jun 11 17:56:10 2012 +0400 +++ b/hedgewars/uVideoRec.pas Mon Jun 11 18:15:30 2012 +0400 @@ -55,14 +55,14 @@ {$IFDEF WIN32} procedure AVWrapper_Init( AddLog: TAddFileLogRaw; - filename, soundFile, format, vcodec, acodec, preset: PChar; + filename, finalFilename, soundFile, format, vcodec, acodec, preset: PChar; width, height, framerateNum, framerateDen, frequency, channels, 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, soundFile, format, vcodec, acodec, preset: PChar; + filename, finalFilename, soundFile, format, vcodec, acodec, preset: PChar; width, height, framerateNum, framerateDen, frequency, channels, vquality, aquality: LongInt); cdecl; external; procedure AVWrapper_Close; cdecl; external; procedure AVWrapper_WriteFrame( pY, pCb, pCr: PByte ); cdecl; external; @@ -75,15 +75,15 @@ cameraFile: TextFile; audioFile: File; - + numPixels: LongInt; firstTick, nframes: Int64; - + cameraFilePath, soundFilePath: shortstring; function BeginVideoRecording: Boolean; -var filename: shortstring; +var filename, finalFilename: shortstring; begin AddFileLog('BeginVideoRecording'); @@ -91,7 +91,7 @@ {$IOCHECKS OFF} // open file with prerecorded camera positions - cameraFilePath:= UserPathPrefix + '/Videos/' + cRecPrefix + '.txtin'; + cameraFilePath:= UserPathPrefix + '/VideoTemp/' + cRecPrefix + '.txtin'; Assign(cameraFile, cameraFilePath); Reset(cameraFile); if IOResult <> 0 then @@ -103,13 +103,14 @@ ReadLn(cameraFile, frequency, channels); {$IOCHECKS ON} - filename:= UserPathPrefix + '/Videos/' + cRecPrefix + #0; - soundFilePath:= UserPathPrefix + '/Videos/' + cRecPrefix + '.hwsound' + #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], @soundFilePath[1], @cAVFormat[1], @cVideoCodec[1], @cAudioCodec[1], @cVideoPreset[1], + 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); YCbCr_Planes[0]:= GetMem(numPixels); @@ -176,6 +177,7 @@ AVWrapper_WriteFrame(YCbCr_Planes[0], YCbCr_Planes[1], YCbCr_Planes[2]); end; +// returns new game ticks function LoadNextCameraPosition: LongInt; var NextTime: LongInt; NextZoom: LongInt; @@ -186,19 +188,18 @@ exit(-1); ReadLn(cameraFile, NextTime, NextWorldDx, NextWorldDy, NextZoom); {$IOCHECKS ON} - if NextTime = 0 then - exit(-1); WorldDx:= NextWorldDx; - WorldDy:= NextWorldDy; - zoom:= NextZoom/10000; - ZoomValue:= NextZoom/10000; + WorldDy:= NextWorldDy + cScreenHeight div 2; + zoom:= NextZoom/10000*cScreenWidth; + ZoomValue:= zoom; LoadNextCameraPosition:= NextTime; end; -// this procedure may be called from different thread +// Callback which records sound. +// This procedure may be called from different thread. procedure RecordPostMix(udata: pointer; stream: PByte; len: LongInt); cdecl; begin - udata:= udata; + udata:= udata; // avoid warning {$IOCHECKS OFF} BlockWrite(audioFile, stream^, len); {$IOCHECKS ON} @@ -209,7 +210,7 @@ filePrefix, filename: shortstring; begin AddFileLog('BeginPreRecording'); - + nframes:= 0; firstTick:= SDL_GetTicks(); @@ -224,7 +225,8 @@ end; {$IOCHECKS OFF} - filename:= UserPathPrefix + '/Videos/' + filePrefix + '.hwsound'; + // create sound file + filename:= UserPathPrefix + '/VideoTemp/' + filePrefix + '.sw'; Assign(audioFile, filename); Rewrite(audioFile, 1); if IOResult <> 0 then @@ -233,7 +235,8 @@ exit; end; - filename:= UserPathPrefix + '/Videos/' + filePrefix + '.txtout'; + // create file with camera positions + filename:= UserPathPrefix + '/VideoTemp/' + filePrefix + '.txtout'; Assign(cameraFile, filename); Rewrite(cameraFile); if IOResult <> 0 then @@ -269,7 +272,7 @@ Ticks:= SDL_GetTicks(); while (Ticks - firstTick)*cVideoFramerateNum > nframes*cVideoFramerateDen*1000 do begin - WriteLn(cameraFile, inttostr(GameTicks) + ' ' + inttostr(WorldDx) + ' ' + inttostr(WorldDy) + ' ' + inttostr(Round(zoom*10000))); + WriteLn(cameraFile, inttostr(GameTicks) + ' ' + inttostr(WorldDx) + ' ' + inttostr(WorldDy - cScreenHeight div 2) + ' ' + inttostr(Round(zoom*10000/cScreenWidth))); inc(nframes); end; end; diff -r f484455dd055 -r baa69bd025d9 project_files/hedgewars.pro --- a/project_files/hedgewars.pro Mon Jun 11 17:56:10 2012 +0400 +++ b/project_files/hedgewars.pro Mon Jun 11 18:15:30 2012 +0400 @@ -105,7 +105,9 @@ ../QTfrontend/ui/widget/colorwidget.h \ ../QTfrontend/model/HatModel.h \ ../QTfrontend/model/GameStyleModel.h \ - ../QTfrontend/recorder.h + ../QTfrontend/util/libav_iteraction.h \ + ../QTfrontend/ui/page/pagevideos.h \ + ../QTfrontend/net/recorder.h SOURCES += ../QTfrontend/model/ammoSchemeModel.cpp \ ../QTfrontend/model/MapModel.cpp \ @@ -188,7 +190,9 @@ ../QTfrontend/ui/widget/colorwidget.cpp \ ../QTfrontend/model/HatModel.cpp \ ../QTfrontend/model/GameStyleModel.cpp \ - ../QTfrontend/recorder.cpp + ../QTfrontend/util/libav_iteraction.cpp \ + ../QTfrontend/ui/page/pagevideos.cpp \ + ../QTfrontend/net/recorder.cpp win32 { SOURCES += ../QTfrontend/xfire.cpp