# HG changeset patch # User Stepan777 # Date 1342183160 -14400 # Node ID bc3306c59a0892991c61ef429ddf65db52c33b59 # Parent 27bfd8bbde7ece626ea904c7b85eaa5e0d236d7c Correctly distinguish between game and real ticks while recording video, so that paused game and speed-up demo are recorded better. Also support recording videos from demos executed directly (without frontend), such videos will be automatically encoded on frontend startup. diff -r 27bfd8bbde7e -r bc3306c59a08 QTfrontend/hwform.cpp --- a/QTfrontend/hwform.cpp Fri Jul 13 16:35:42 2012 +0400 +++ b/QTfrontend/hwform.cpp Fri Jul 13 16:39:20 2012 +0400 @@ -142,7 +142,7 @@ config = new GameUIConfig(this, cfgdir->absolutePath() + "/hedgewars.ini"); - ui.pageVideos->config = config; + ui.pageVideos->init(config); #ifdef __APPLE__ panel = new M3Panel; @@ -1428,18 +1428,7 @@ } } - // encode videos - QDir videosDir(cfgdir->absolutePath() + "/VideoTemp/"); - QStringList files = videosDir.entryList(QStringList("*.txtout"), QDir::Files); - foreach (const QString & str, files) - { - 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, prefix); - ui.pageVideos->addRecorder(pRecorder); - pRecorder->EncodeVideo(record); - } + ui.pageVideos->startEncoding(record); } void HWForm::startTraining(const QString & scriptName) diff -r 27bfd8bbde7e -r bc3306c59a08 QTfrontend/ui/page/pagevideos.cpp --- a/QTfrontend/ui/page/pagevideos.cpp Fri Jul 13 16:35:42 2012 +0400 +++ b/QTfrontend/ui/page/pagevideos.cpp Fri Jul 13 16:39:20 2012 +0400 @@ -301,13 +301,7 @@ 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), config(0) @@ -317,6 +311,19 @@ initPage(); } +void PageVideos::init(GameUIConfig * config) +{ + this->config = config; + + 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); + + startEncoding(); // this is for videos recorded from demos which were executed directly (without frontend) +} + // user changed file format, we need to update list of codecs void PageVideos::changeAVFormat(int index) { @@ -844,3 +851,32 @@ } return list; } + +void PageVideos::startEncoding(const QByteArray & record) +{ + QDir videoTempDir(cfgdir->absolutePath() + "/VideoTemp/"); + QStringList files = videoTempDir.entryList(QStringList("*.txtout"), QDir::Files); + foreach (const QString & str, files) + { + QString prefix = str; + prefix.chop(7); // remove ".txtout" + videoTempDir.rename(prefix + ".txtout", prefix + ".txtin"); // rename this file to not open it twice + + HWRecorder* pRecorder = new HWRecorder(config, prefix); + + if (!record.isEmpty()) + pRecorder->EncodeVideo(record); + else + { + // this is for videos recorded from demos which were executed directly (without frontend) + QFile demofile(videoTempDir.absoluteFilePath(prefix + ".hwd")); + if (!demofile.open(QIODevice::ReadOnly)) + continue; + QByteArray demo = demofile.readAll(); + if (demo.isEmpty()) + continue; + pRecorder->EncodeVideo(demo); + } + addRecorder(pRecorder); + } +} diff -r 27bfd8bbde7e -r bc3306c59a08 QTfrontend/ui/page/pagevideos.h --- a/QTfrontend/ui/page/pagevideos.h Fri Jul 13 16:35:42 2012 +0400 +++ b/QTfrontend/ui/page/pagevideos.h Fri Jul 13 16:39:20 2012 +0400 @@ -41,7 +41,6 @@ QCheckBox *checkUseGameRes; QCheckBox *checkRecordAudio; - GameUIConfig * config; QString format() { return comboAVFormats->itemData(comboAVFormats->currentIndex()).toString(); } @@ -57,6 +56,8 @@ void addRecorder(HWRecorder* pRecorder); bool tryQuit(HWForm *form); QString getVideosInProgress(); // get multi-line string with list of videos in progress + void startEncoding(const QByteArray & record = QByteArray()); + void init(GameUIConfig * config); private: // virtuals from AbstractPage @@ -76,6 +77,8 @@ void clearTemp(); void clearThumbnail(); + GameUIConfig * config; + // options group QComboBox *comboAVFormats; QComboBox *comboVideoCodecs; diff -r 27bfd8bbde7e -r bc3306c59a08 hedgewars/hwengine.pas --- a/hedgewars/hwengine.pas Fri Jul 13 16:35:42 2012 +0400 +++ b/hedgewars/hwengine.pas Fri Jul 13 16:39:20 2012 +0400 @@ -82,15 +82,15 @@ end; gsConfirm, gsGame: begin + DrawWorld(Lag); DoGameTick(Lag); ProcessVisualGears(Lag); - DrawWorld(Lag); end; gsChat: begin + DrawWorld(Lag); DoGameTick(Lag); ProcessVisualGears(Lag); - DrawWorld(Lag); end; gsExit: begin @@ -273,27 +273,32 @@ {$IFDEF USE_VIDEO_RECORDING} procedure RecorderMainLoop; -var CurrTime, PrevTime: LongInt; +var oldGameTicks, oldRealTicks, newGameTicks, newRealTicks: LongInt; begin if not BeginVideoRecording() then exit; DoTimer(0); // gsLandGen -> gsStart DoTimer(0); // gsStart -> gsGame - CurrTime:= LoadNextCameraPosition(); + if not LoadNextCameraPosition(newRealTicks, newGameTicks) then + exit; fastScrolling:= true; - DoTimer(CurrTime); + DoGameTick(newGameTicks); fastScrolling:= false; - while true do + oldRealTicks:= 0; + oldGameTicks:= newGameTicks; + + while LoadNextCameraPosition(newRealTicks, newGameTicks) do begin + IPCCheckSock(); + DoGameTick(newGameTicks - oldGameTicks); + if GameState = gsExit then + break; + ProcessVisualGears(newRealTicks - oldRealTicks); + DrawWorld(newRealTicks - oldRealTicks); EncodeFrame(); - PrevTime:= CurrTime; - CurrTime:= LoadNextCameraPosition(); - if CurrTime = -1 then - break; - if DoTimer(CurrTime - PrevTime) then - break; - IPCCheckSock(); + oldRealTicks:= newRealTicks; + oldGameTicks:= newGameTicks; end; StopVideoRecording(); end; diff -r 27bfd8bbde7e -r bc3306c59a08 hedgewars/uVideoRec.pas --- a/hedgewars/uVideoRec.pas Fri Jul 13 16:35:42 2012 +0400 +++ b/hedgewars/uVideoRec.pas Fri Jul 13 16:39:20 2012 +0400 @@ -39,7 +39,7 @@ var flagPrerecording: boolean = false; function BeginVideoRecording: Boolean; -function LoadNextCameraPosition: LongInt; +function LoadNextCameraPosition(var newRealTicks, newGameTicks: LongInt): Boolean; procedure EncodeFrame; procedure StopVideoRecording; @@ -88,7 +88,7 @@ audioFile: File; numPixels: LongWord; startTime, numFrames, curTime, progress, maxProgress: LongWord; - cameraFilePath, soundFilePath: shortstring; + soundFilePath: shortstring; thumbnailSaved : Boolean; function BeginVideoRecording: Boolean; @@ -98,13 +98,13 @@ {$IOCHECKS OFF} // open file with prerecorded camera positions - cameraFilePath:= UserPathPrefix + '/VideoTemp/' + RecPrefix + '.txtin'; - Assign(cameraFile, cameraFilePath); + filename:= UserPathPrefix + '/VideoTemp/' + RecPrefix + '.txtin'; + Assign(cameraFile, filename); Reset(cameraFile); maxProgress:= FileSize(cameraFile); if IOResult <> 0 then begin - AddFileLog('Error: Could not read from ' + cameraFilePath); + AddFileLog('Error: Could not read from ' + filename); exit(false); end; {$IOCHECKS ON} @@ -163,7 +163,7 @@ FreeMem(RGB_Buffer, 4*numPixels); Close(cameraFile); AVWrapper_Close(); - DeleteFile(cameraFilePath); + Erase(cameraFile); DeleteFile(soundFilePath); SendIPC(_S'v'); // inform frontend that we finished end; @@ -207,17 +207,15 @@ inc(numFrames); end; -// returns new game ticks -function LoadNextCameraPosition: LongInt; +function LoadNextCameraPosition(var newRealTicks, newGameTicks: LongInt): Boolean; var frame: TFrame; begin - LoadNextCameraPosition:= GameTicks; // we need to skip or duplicate frames to match target framerate while Int64(curTime)*cVideoFramerateNum <= Int64(numFrames)*cVideoFramerateDen*1000 do begin {$IOCHECKS OFF} if eof(cameraFile) then - exit(-1); + exit(false); BlockRead(cameraFile, frame, 1); {$IOCHECKS ON} curTime:= frame.realTicks; @@ -226,8 +224,10 @@ zoom:= frame.zoom*cScreenWidth; ZoomValue:= zoom; inc(progress); - LoadNextCameraPosition:= frame.gameTicks; + newRealTicks:= frame.realTicks; + newGameTicks:= frame.gameTicks; end; + LoadNextCameraPosition:= true; end; // Callback which records sound. @@ -251,6 +251,38 @@ thumbnailSaved:= true; end; +// copy file (free pascal doesn't have copy file function) +procedure CopyFile(src, dest: shortstring); +var inF, outF: file; + buffer: array[0..1023] of byte; + result: LongInt; +begin +{$IOCHECKS OFF} + result:= 0; // avoid compiler hint + + Assign(inF, src); + Reset(inF, 1); + if IOResult <> 0 then + begin + AddFileLog('Error: Could not read from ' + src); + exit; + end; + + Assign(outF, dest); + Rewrite(outF, 1); + if IOResult <> 0 then + begin + AddFileLog('Error: Could not write to ' + dest); + exit; + end; + + repeat + BlockRead(inF, buffer, 1024, result); + BlockWrite(outF, buffer, result); + until result < 1024; +{$IOCHECKS ON} +end; + procedure BeginPreRecording; var format: word; filename: shortstring; @@ -261,6 +293,15 @@ thumbnailSaved:= false; RecPrefix:= 'hw-' + FormatDateTime('YYYY-MM-DD_HH-mm-ss-z', Now()); + // If this video is recorded from demo executed directly (without frontend) + // then we need to copy demo so that frontend will be able to find it later. + if recordFileName <> '' then + begin + if GameType <> gmtDemo then // this is save and game demo is not recording, abort + exit; + CopyFile(recordFileName, UserPathPrefix + '/VideoTemp/' + RecPrefix + '.hwd'); + end; + Mix_QuerySpec(@frequency, @format, @channels); AddFileLog('sound: frequency = ' + IntToStr(frequency) + ', format = ' + IntToStr(format) + ', channels = ' + IntToStr(channels)); if format <> $8010 then diff -r 27bfd8bbde7e -r bc3306c59a08 hedgewars/uWorld.pas --- a/hedgewars/uWorld.pas Fri Jul 13 16:35:42 2012 +0400 +++ b/hedgewars/uWorld.pas Fri Jul 13 16:39:20 2012 +0400 @@ -1392,7 +1392,7 @@ end; // Lag alert -if isInLag and (GameType <> gmtRecord) then +if isInLag then DrawSprite(sprLag, 32 - (cScreenWidth shr 1), 32, (RealTicks shr 7) mod 12); // Wind bar