# HG changeset patch # User Stepan777 # 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;