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.
--- 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;