rework saving of camera positions so there is no need to know framerate during prerecording.
authorStepan777 <stepik-777@mail.ru>
Mon, 09 Jul 2012 17:03:57 +0400
changeset 7376 48b79b3ca592
parent 7373 d5ec4e4eb2d5
child 7379 aa29a2f16cc7
rework saving of camera positions so there is no need to know framerate during prerecording. support x264 on older versions of ffmpeg/libav. remove some unuseful options.
QTfrontend/game.cpp
QTfrontend/hwform.cpp
QTfrontend/net/recorder.cpp
QTfrontend/net/recorder.h
QTfrontend/util/libav_iteraction.cpp
hedgewars/ArgParsers.inc
hedgewars/avwrapper.c
hedgewars/uConsts.pas
hedgewars/uVariables.pas
hedgewars/uVideoRec.pas
--- a/QTfrontend/game.cpp	Mon Jul 09 16:42:13 2012 +0400
+++ b/QTfrontend/game.cpp	Mon Jul 09 17:03:57 2012 +0400
@@ -335,8 +335,6 @@
     arguments << QString::number(config->translateQuality());
     arguments << QString::number(config->stereoMode());
     arguments << tr("en.txt");
-    arguments << QString::number(config->rec_Framerate()); // framerate num
-    arguments << "1";  // framerate den
 
     return arguments;
 }
--- a/QTfrontend/hwform.cpp	Mon Jul 09 16:42:13 2012 +0400
+++ b/QTfrontend/hwform.cpp	Mon Jul 09 17:03:57 2012 +0400
@@ -1438,8 +1438,7 @@
         videosDir.rename(prefix + ".txtout", prefix + ".txtin"); // rename this file to not open it twice
         HWRecorder* pRecorder = new HWRecorder(config, prefix);
         ui.pageVideos->addRecorder(pRecorder);
-        int numFrames = QFileInfo(videosDir.absoluteFilePath(prefix + ".txtin")).size()/16;
-        pRecorder->EncodeVideo(record, numFrames);
+        pRecorder->EncodeVideo(record);
     }
 }
 
--- a/QTfrontend/net/recorder.cpp	Mon Jul 09 16:42:13 2012 +0400
+++ b/QTfrontend/net/recorder.cpp	Mon Jul 09 17:03:57 2012 +0400
@@ -59,7 +59,7 @@
             SendIPC("!");
             break;
         case 'p':
-            emit onProgress(float(++curFrame)/numFrames);
+            emit onProgress((quint8(msg.at(2))*256.0 + quint8(msg.at(3)))*0.0001);
             break;
         case 'v':
             finished = true;
@@ -68,11 +68,8 @@
     }
 }
 
-void HWRecorder::EncodeVideo(const QByteArray & record, int numFrames)
+void HWRecorder::EncodeVideo(const QByteArray & record)
 {
-    this->numFrames = numFrames;
-    curFrame = 0;
-
     toSendBuf = record;
     toSendBuf.replace(QByteArray("\x02TD"), QByteArray("\x02TV"));
     toSendBuf.replace(QByteArray("\x02TL"), QByteArray("\x02TV"));
@@ -104,15 +101,13 @@
     arguments << QString::number(config->translateQuality());
     arguments << QString::number(config->stereoMode());
     arguments << HWGame::tr("en.txt");
-    arguments << QString::number(config->rec_Framerate()); // framerate num
-    arguments << "1";  // framerate den
+    arguments << QString::number(config->rec_Framerate()); // framerate numerator
+    arguments << "1";  // framerate denominator
     arguments << prefix;
     arguments << config->AVFormat();
     arguments << config->videoCodec();
     arguments << "5"; // video quality
-    arguments << "medium";
     arguments << (config->recordAudio()? config->audioCodec() : "no");
-    arguments << "5"; // audio quality
 
     return arguments;
 }
--- a/QTfrontend/net/recorder.h	Mon Jul 09 16:42:13 2012 +0400
+++ b/QTfrontend/net/recorder.h	Mon Jul 09 17:03:57 2012 +0400
@@ -34,7 +34,7 @@
         HWRecorder(GameUIConfig * config, const QString & prefix);
         virtual ~HWRecorder();
 
-        void EncodeVideo(const QByteArray & record, int numFrames);
+        void EncodeVideo(const QByteArray & record);
 
         VideoItem * item; // used by pagevideos
         QString name;
@@ -51,8 +51,6 @@
         void encodingFinished(bool success);
 
     private:
-        int curFrame;
-        int numFrames;
         bool finished;
         GameUIConfig * config;
 };
--- a/QTfrontend/util/libav_iteraction.cpp	Mon Jul 09 16:42:13 2012 +0400
+++ b/QTfrontend/util/libav_iteraction.cpp	Mon Jul 09 17:03:57 2012 +0400
@@ -84,9 +84,6 @@
 #endif
             continue;
 
-        if (pCodec->capabilities & CODEC_CAP_EXPERIMENTAL)
-            continue;
-
         if (pCodec->type != AVMEDIA_TYPE_VIDEO && pCodec->type != AVMEDIA_TYPE_AUDIO)
             continue;
 
@@ -98,14 +95,6 @@
         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)
@@ -170,7 +159,7 @@
             codec.isRecomended = true;
 
         // FIXME: remove next line
-       // codec.longName += QString(" (%1)").arg(codec.shortName);
+        codec.longName += QString(" (%1)").arg(codec.shortName);
     }
 
     // get list of all formats
@@ -204,7 +193,7 @@
         format.longName = QString("%1 (*.%2)").arg(pFormat->long_name).arg(ext);
 
         // FIXME: remove next line
-       // format.longName += QString(" (%1)").arg(format.shortName);
+        format.longName += QString(" (%1)").arg(format.shortName);
 
         format.isRecomended = strcmp(pFormat->name, "mp4") == 0 || strcmp(pFormat->name, "avi") == 0;
 
--- a/hedgewars/ArgParsers.inc	Mon Jul 09 16:42:13 2012 +0400
+++ b/hedgewars/ArgParsers.inc	Mon Jul 09 17:03:57 2012 +0400
@@ -61,10 +61,6 @@
     else 
         cStereoMode:= TStereoMode(max(0, min(ord(high(TStereoMode)), tmp-6)));
     cLocaleFName:= ParamStr(17);
-{$IFDEF USE_VIDEO_RECORDING}
-    cVideoFramerateNum:= StrToInt(ParamStr(18));
-    cVideoFramerateDen:= StrToInt(ParamStr(19));
-{$ENDIF}
 end;
 
 {$IFDEF USE_VIDEO_RECORDING}
@@ -72,13 +68,13 @@
 begin
     internalStartGameWithParameters();
     GameType:= gmtRecord;
+    cVideoFramerateNum:= StrToInt(ParamStr(18));
+    cVideoFramerateDen:= StrToInt(ParamStr(19));
     RecPrefix:= ParamStr(20);
     cAVFormat:= ParamStr(21);
     cVideoCodec:= ParamStr(22);
     cVideoQuality:= StrToInt(ParamStr(23));
-    cVideoPreset:= ParamStr(24);
-    cAudioCodec:= ParamStr(25);
-    cAudioQuality:= StrToInt(ParamStr(26));
+    cAudioCodec:= ParamStr(24);
 end;
 {$ENDIF}
 
--- a/hedgewars/avwrapper.c	Mon Jul 09 16:42:13 2012 +0400
+++ b/hedgewars/avwrapper.c	Mon Jul 09 17:03:57 2012 +0400
@@ -23,9 +23,8 @@
 
 static int g_Width, g_Height;
 static uint32_t g_Frequency, g_Channels;
-static int g_VQuality, g_AQuality;
+static int g_VQuality;
 static AVRational g_Framerate;
-static const char* g_pPreset;
 
 static FILE* g_pSoundFile;
 static int16_t* g_pSamples;
@@ -110,13 +109,11 @@
     g_pAudio->channels = g_Channels;
 
     // set quality
-    if (g_AQuality > 100)
-        g_pAudio->bit_rate = g_AQuality;
-    else
-    {
-        g_pAudio->flags |= CODEC_FLAG_QSCALE;
-        g_pAudio->global_quality = g_AQuality*FF_QP2LAMBDA;
-    }
+    g_pAudio->bit_rate = 160000;
+
+    // for codecs that support variable bitrate use it, it should be better
+    g_pAudio->flags |= CODEC_FLAG_QSCALE;
+    g_pAudio->global_quality = 1*FF_QP2LAMBDA;
 
     // some formats want stream headers to be separate
     if (g_pFormat->flags & AVFMT_GLOBALHEADER)
@@ -240,11 +237,42 @@
     if (g_pFormat->flags & AVFMT_GLOBALHEADER)
         g_pVideo->flags |= CODEC_FLAG_GLOBAL_HEADER;
 
+#if LIBAVCODEC_VERSION_MAJOR < 54
+    // for some versions of ffmpeg x264 options must be set explicitly
+    if (strcmp(g_pVCodec->name, "libx264") == 0)
+    {
+        g_pVideo->coder_type = FF_CODER_TYPE_AC;
+        g_pVideo->flags |= CODEC_FLAG_LOOP_FILTER;
+        g_pVideo->crf = 23;
+        g_pVideo->thread_count = 3;
+        g_pVideo->me_cmp = FF_CMP_CHROMA;
+        g_pVideo->partitions = X264_PART_I8X8 | X264_PART_I4X4 | X264_PART_P8X8 | X264_PART_B8X8;
+        g_pVideo->me_method = ME_HEX;
+        g_pVideo->me_subpel_quality = 7;
+        g_pVideo->me_range = 16;
+        g_pVideo->gop_size = 250;
+        g_pVideo->keyint_min = 25;
+        g_pVideo->scenechange_threshold = 40;
+        g_pVideo->i_quant_factor = 0.71;
+        g_pVideo->b_frame_strategy = 1;
+        g_pVideo->qcompress = 0.6;
+        g_pVideo->qmin = 10;
+        g_pVideo->qmax = 51;
+        g_pVideo->max_qdiff = 4;
+        g_pVideo->max_b_frames = 3;
+        g_pVideo->refs = 3;
+        g_pVideo->directpred = 1;
+        g_pVideo->trellis = 1;
+        g_pVideo->flags2 = CODEC_FLAG2_BPYRAMID | CODEC_FLAG2_MIXED_REFS | CODEC_FLAG2_WPRED | CODEC_FLAG2_8X8DCT | CODEC_FLAG2_FASTPSKIP;
+        g_pVideo->weighted_p_pred = 2;
+    }
+#endif
+
     // open the codec
 #if LIBAVCODEC_VERSION_MAJOR >= 53
     AVDictionary* pDict = NULL;
     if (strcmp(g_pVCodec->name, "libx264") == 0)
-        av_dict_set(&pDict, "preset", g_pPreset, 0);
+        av_dict_set(&pDict, "preset", "medium", 0);
 
     if (avcodec_open2(g_pVideo, g_pVCodec, &pDict) < 0)
 #else
@@ -348,10 +376,9 @@
          const char* pFormatName,
          const char* pVCodecName,
          const char* pACodecName,
-         const char* pVPreset,
          int Width, int Height,
          int FramerateNum, int FramerateDen,
-         int VQuality, int AQuality)
+         int VQuality)
 {    
     AddFileLogRaw = pAddFileLogRaw;
     av_log_set_callback( &LogCallback );
@@ -361,8 +388,6 @@
     g_Framerate.num = FramerateNum;
     g_Framerate.den = FramerateDen;
     g_VQuality = VQuality;
-    g_AQuality = AQuality;
-    g_pPreset = pVPreset;
 
     // initialize libav and register all codecs and formats
     av_register_all();
--- a/hedgewars/uConsts.pas	Mon Jul 09 16:42:13 2012 +0400
+++ b/hedgewars/uConsts.pas	Mon Jul 09 17:03:57 2012 +0400
@@ -27,12 +27,8 @@
 
 const
     sfMax = 1000;
-{$IFNDEF USE_VIDEO_RECORDING}
     cDefaultParamNum = 17;
-{$ELSE}
-    cDefaultParamNum = 19;
     cVideorecParamNum = cDefaultParamNum + 7;
-{$ENDIF}
 
     // message constants
     errmsgCreateSurface   = 'Error creating SDL surface';
--- a/hedgewars/uVariables.pas	Mon Jul 09 16:42:13 2012 +0400
+++ b/hedgewars/uVariables.pas	Mon Jul 09 17:03:57 2012 +0400
@@ -56,12 +56,10 @@
     RecPrefix      : shortstring;
     cAVFormat       : shortstring;
     cVideoCodec     : shortstring;
-    cVideoFramerateNum : LongInt = 25;
-    cVideoFramerateDen : LongInt = 1;
+    cVideoFramerateNum : LongInt;
+    cVideoFramerateDen : LongInt;
     cVideoQuality      : LongInt;
-    cVideoPreset    : shortstring;
     cAudioCodec     : shortstring;
-    cAudioQuality   : LongInt;
 {$ENDIF}
 //////////////////////////
     cMapName        : shortstring = '';
--- a/hedgewars/uVideoRec.pas	Mon Jul 09 16:42:13 2012 +0400
+++ b/hedgewars/uVideoRec.pas	Mon Jul 09 17:03:57 2012 +0400
@@ -62,21 +62,22 @@
 {$IFDEF WIN32}
 procedure AVWrapper_Init(
               AddLog: TAddFileLogRaw;
-              filename, desc, soundFile, format, vcodec, acodec, preset: PChar;
-              width, height, framerateNum, framerateDen, vquality, aquality: LongInt); cdecl; external AVWrapperLibName;
+              filename, desc, soundFile, format, vcodec, acodec: PChar;
+              width, height, framerateNum, framerateDen, vquality: 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, desc, soundFile, format, vcodec, acodec, preset: PChar;
-              width, height, framerateNum, framerateDen, vquality, aquality: LongInt); cdecl; external;
+              filename, desc, soundFile, format, vcodec, acodec: PChar;
+              width, height, framerateNum, framerateDen, vquality: LongInt); cdecl; external;
 procedure AVWrapper_Close; cdecl; external;
 procedure AVWrapper_WriteFrame( pY, pCb, pCr: PByte ); cdecl; external;
 {$ENDIF}
 
 type TFrame = record
-                  ticks: LongWord;
+                  realTicks: LongWord;
+                  gameTicks: LongWord;
                   CamX, CamY: LongInt;
                   zoom: single;
               end;
@@ -86,7 +87,7 @@
     cameraFile: File of TFrame;
     audioFile: File;
     numPixels: LongWord;
-    startTime, numFrames: LongWord;
+    startTime, numFrames, curTime, progress, maxProgress: LongWord;
     cameraFilePath, soundFilePath: shortstring;
     thumbnailSaved : Boolean;
 
@@ -95,13 +96,12 @@
 begin
     AddFileLog('BeginVideoRecording');
 
-    numPixels:= cScreenWidth*cScreenHeight;
-
 {$IOCHECKS OFF}
     // open file with prerecorded camera positions
     cameraFilePath:= UserPathPrefix + '/VideoTemp/' + RecPrefix + '.txtin';
     Assign(cameraFile, cameraFilePath);
     Reset(cameraFile);
+    maxProgress:= FileSize(cameraFile);
     if IOResult <> 0 then
     begin
         AddFileLog('Error: Could not read from ' + cameraFilePath);
@@ -109,6 +109,7 @@
     end;
 {$IOCHECKS ON}
 
+    // store some description in output file
     desc:= '';
     if UserNick <> '' then
         desc+= 'Player: ' + UserNick + #10;
@@ -126,10 +127,10 @@
     cAVFormat+= #0;
     cAudioCodec+= #0;
     cVideoCodec+= #0;
-    cVideoPreset+= #0;
-    AVWrapper_Init(@AddFileLogRaw, @filename[1], @desc[1], @soundFilePath[1], @cAVFormat[1], @cVideoCodec[1], @cAudioCodec[1], @cVideoPreset[1],
-                   cScreenWidth, cScreenHeight, cVideoFramerateNum, cVideoFramerateDen, cAudioQuality, cVideoQuality);
+    AVWrapper_Init(@AddFileLogRaw, @filename[1], @desc[1], @soundFilePath[1], @cAVFormat[1], @cVideoCodec[1], @cAudioCodec[1],
+                   cScreenWidth, cScreenHeight, cVideoFramerateNum, cVideoFramerateDen, cVideoQuality);
 
+    numPixels:= cScreenWidth*cScreenHeight;
     YCbCr_Planes[0]:= GetMem(numPixels);
     YCbCr_Planes[1]:= GetMem(numPixels div 4);
     YCbCr_Planes[2]:= GetMem(numPixels div 4);
@@ -147,6 +148,9 @@
         exit(false);
     end;
 
+    curTime:= 0;
+    numFrames:= 0;
+    progress:= 0;
     BeginVideoRecording:= true;
 end;
 
@@ -161,7 +165,7 @@
     AVWrapper_Close();
     DeleteFile(cameraFilePath);
     DeleteFile(soundFilePath);
-    SendIPC(_S'v');
+    SendIPC(_S'v'); // inform frontend that we finished
 end;
 
 function pixel(x, y, color: LongInt): LongInt;
@@ -171,6 +175,7 @@
 
 procedure EncodeFrame;
 var x, y, r, g, b: LongInt;
+    s: shortstring;
 begin
     // read pixels from OpenGL
     glReadPixels(0, 0, cScreenWidth, cScreenHeight, GL_RGBA, GL_UNSIGNED_BYTE, RGB_Buffer);
@@ -194,24 +199,35 @@
 
     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');
+    // inform frontend that we have encoded new frame
+    s[0]:= #3;
+    s[1]:= 'p'; // p for progress
+    SDLNet_Write16(progress*10000 div maxProgress, @s[2]);
+    SendIPC(s);
+    inc(numFrames);
 end;
 
 // returns new game ticks
 function LoadNextCameraPosition: LongInt;
 var frame: TFrame;
 begin
-{$IOCHECKS OFF}
-    if eof(cameraFile) then
-        exit(-1);
-    BlockRead(cameraFile, frame, 1);
-{$IOCHECKS ON}
-    WorldDx:= frame.CamX;
-    WorldDy:= frame.CamY + cScreenHeight div 2;
-    zoom:= frame.zoom*cScreenWidth;
-    ZoomValue:= zoom;
-    LoadNextCameraPosition:= frame.ticks;
+    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);
+        BlockRead(cameraFile, frame, 1);
+    {$IOCHECKS ON}
+        curTime:= frame.realTicks;
+        WorldDx:= frame.CamX;
+        WorldDy:= frame.CamY + cScreenHeight div 2;
+        zoom:= frame.zoom*cScreenWidth;
+        ZoomValue:= zoom;
+        inc(progress);
+        LoadNextCameraPosition:= frame.gameTicks;
+    end;
 end;
 
 // Callback which records sound.
@@ -247,9 +263,8 @@
 begin
     AddFileLog('BeginPreRecording');
 
-    numFrames:= 0;
     thumbnailSaved:= false;
-    RecPrefix:= FormatDateTime('YYYY-MM-DD_HH-mm-ss', Now());
+    RecPrefix:= FormatDateTime('YYYY-MM-DD_HH-mm-ss', Now()) + inttostr(GameTicks);
 
     Mix_QuerySpec(@frequency, @format, @channels);
     AddFileLog('sound: frequency = ' + IntToStr(frequency) + ', format = ' + IntToStr(format) + ', channels = ' + IntToStr(channels));
@@ -310,22 +325,17 @@
 end;
 
 procedure SaveCameraPosition;
-var curTime: LongInt;
-    frame: TFrame;
+var frame: TFrame;
 begin
     if (not thumbnailSaved) and (ScreenFade = sfNone) then
         SaveThumbnail();
 
-    curTime:= SDL_GetTicks();
-    while Int64(curTime - startTime)*cVideoFramerateNum > Int64(numFrames)*cVideoFramerateDen*1000 do
-    begin
-        frame.ticks:= GameTicks;
-        frame.CamX:= WorldDx;
-        frame.CamY:= WorldDy - cScreenHeight div 2;
-        frame.zoom:= zoom/cScreenWidth;
-        BlockWrite(cameraFile, frame, 1);
-        inc(numFrames);
-    end;
+    frame.realTicks:= SDL_GetTicks() - startTime;
+    frame.gameTicks:= GameTicks;
+    frame.CamX:= WorldDx;
+    frame.CamY:= WorldDy - cScreenHeight div 2;
+    frame.zoom:= zoom/cScreenWidth;
+    BlockWrite(cameraFile, frame, 1);
 end;
 
 procedure freeModule;