# HG changeset patch
# User Stepan777 <stepik-777@mail.ru>
# Date 1341839037 -14400
# Node ID 48b79b3ca592c710d0918ab749544a3a4683ac53
# Parent  d5ec4e4eb2d550a489b3f6f8eb949616b412bbfa
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.

diff -r d5ec4e4eb2d5 -r 48b79b3ca592 QTfrontend/game.cpp
--- 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;
 }
diff -r d5ec4e4eb2d5 -r 48b79b3ca592 QTfrontend/hwform.cpp
--- 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);
     }
 }
 
diff -r d5ec4e4eb2d5 -r 48b79b3ca592 QTfrontend/net/recorder.cpp
--- 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;
 }
diff -r d5ec4e4eb2d5 -r 48b79b3ca592 QTfrontend/net/recorder.h
--- 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;
 };
diff -r d5ec4e4eb2d5 -r 48b79b3ca592 QTfrontend/util/libav_iteraction.cpp
--- 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;
 
diff -r d5ec4e4eb2d5 -r 48b79b3ca592 hedgewars/ArgParsers.inc
--- 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}
 
diff -r d5ec4e4eb2d5 -r 48b79b3ca592 hedgewars/avwrapper.c
--- 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();
diff -r d5ec4e4eb2d5 -r 48b79b3ca592 hedgewars/uConsts.pas
--- 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';
diff -r d5ec4e4eb2d5 -r 48b79b3ca592 hedgewars/uVariables.pas
--- 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 = '';
diff -r d5ec4e4eb2d5 -r 48b79b3ca592 hedgewars/uVideoRec.pas
--- 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;