hedgewars/uVideoRec.pas
changeset 7280 fd707afbc3a2
parent 7235 baa69bd025d9
child 7286 068adc6948e3
equal deleted inserted replaced
7278:000e4543f204 7280:fd707afbc3a2
    42 
    42 
    43 procedure freeModule;
    43 procedure freeModule;
    44 
    44 
    45 implementation
    45 implementation
    46 
    46 
    47 uses uVariables, uUtils, GLunit, SDLh, SysUtils;
    47 uses uVariables, uUtils, GLunit, SDLh, SysUtils, uIO;
    48 
    48 
    49 {$IFDEF WIN32}
    49 {$IFDEF WIN32}
    50 const AVWrapperLibName = 'libavwrapper.dll';
    50 const AVWrapperLibName = 'libavwrapper.dll';
    51 {$ENDIF}
    51 {$ENDIF}
    52 
    52 
    53 type TAddFileLogRaw = procedure (s: pchar); cdecl;
    53 type TAddFileLogRaw = procedure (s: pchar); cdecl;
    54 
    54 
    55 {$IFDEF WIN32}
    55 {$IFDEF WIN32}
    56 procedure AVWrapper_Init(
    56 procedure AVWrapper_Init(
    57               AddLog: TAddFileLogRaw;
    57               AddLog: TAddFileLogRaw;
    58               filename, finalFilename, soundFile, format, vcodec, acodec, preset: PChar;
    58               filename, desc, soundFile, format, vcodec, acodec, preset: PChar;
    59               width, height, framerateNum, framerateDen, frequency, channels, vquality, aquality: LongInt); cdecl; external AVWrapperLibName;
    59               width, height, framerateNum, framerateDen, vquality, aquality: LongInt); cdecl; external AVWrapperLibName;
    60 procedure AVWrapper_Close; cdecl; external AVWrapperLibName;
    60 procedure AVWrapper_Close; cdecl; external AVWrapperLibName;
    61 procedure AVWrapper_WriteFrame( pY, pCb, pCr: PByte ); cdecl; external AVWrapperLibName;
    61 procedure AVWrapper_WriteFrame( pY, pCb, pCr: PByte ); cdecl; external AVWrapperLibName;
    62 {$ELSE}
    62 {$ELSE}
    63 procedure AVWrapper_Init(
    63 procedure AVWrapper_Init(
    64               AddLog: TAddFileLogRaw;
    64               AddLog: TAddFileLogRaw;
    65               filename, finalFilename, soundFile, format, vcodec, acodec, preset: PChar;
    65               filename, desc, soundFile, format, vcodec, acodec, preset: PChar;
    66               width, height, framerateNum, framerateDen, frequency, channels, vquality, aquality: LongInt); cdecl; external;
    66               width, height, framerateNum, framerateDen, vquality, aquality: LongInt); cdecl; external;
    67 procedure AVWrapper_Close; cdecl; external;
    67 procedure AVWrapper_Close; cdecl; external;
    68 procedure AVWrapper_WriteFrame( pY, pCb, pCr: PByte ); cdecl; external;
    68 procedure AVWrapper_WriteFrame( pY, pCb, pCr: PByte ); cdecl; external;
    69 {$ENDIF}
    69 {$ENDIF}
    70 
    70 
       
    71 type TFrame = record
       
    72                   ticks: LongWord;
       
    73                   CamX, CamY: LongInt;
       
    74                   zoom: single;
       
    75               end;
       
    76 
    71 var YCbCr_Planes: array[0..2] of PByte;
    77 var YCbCr_Planes: array[0..2] of PByte;
    72     RGB_Buffer: PByte;
    78     RGB_Buffer: PByte;
    73 
    79     cameraFile: File of TFrame;
    74     frequency, channels: LongInt;
       
    75 
       
    76     cameraFile: TextFile;
       
    77     audioFile: File;
    80     audioFile: File;
    78 
    81     numPixels: LongWord;
    79     numPixels: LongInt;
    82     startTime, numFrames: LongWord;
    80 
       
    81     firstTick, nframes: Int64;
       
    82 
       
    83     cameraFilePath, soundFilePath: shortstring;
    83     cameraFilePath, soundFilePath: shortstring;
    84 
    84 
    85 function BeginVideoRecording: Boolean;
    85 function BeginVideoRecording: Boolean;
    86 var filename, finalFilename: shortstring;
    86 var filename, desc: shortstring;
    87 begin
    87 begin
    88     AddFileLog('BeginVideoRecording');
    88     AddFileLog('BeginVideoRecording');
    89 
    89 
    90     numPixels:= cScreenWidth*cScreenHeight;
    90     numPixels:= cScreenWidth*cScreenHeight;
    91 
    91 
    97     if IOResult <> 0 then
    97     if IOResult <> 0 then
    98     begin
    98     begin
    99         AddFileLog('Error: Could not read from ' + cameraFilePath);
    99         AddFileLog('Error: Could not read from ' + cameraFilePath);
   100         exit(false);
   100         exit(false);
   101     end;
   101     end;
   102 
       
   103     ReadLn(cameraFile, frequency, channels);
       
   104 {$IOCHECKS ON}
   102 {$IOCHECKS ON}
   105 
   103 
       
   104     desc:= '';
       
   105     if UserNick <> '' then
       
   106         desc+= 'Player: ' + UserNick + #10;
       
   107     if recordFileName <> '' then
       
   108         desc+= 'Record: ' + recordFileName + #10;
       
   109     if cMapName <> '' then
       
   110         desc+= 'Map: ' + cMapName + #10;
       
   111     if Theme <> '' then
       
   112         desc+= 'Theme: ' + Theme + #10;
       
   113     desc+= #0;
       
   114 
   106     filename:= UserPathPrefix + '/VideoTemp/' + cRecPrefix + #0;
   115     filename:= UserPathPrefix + '/VideoTemp/' + cRecPrefix + #0;
   107     finalFilename:= UserPathPrefix + '/Videos/' + cRecPrefix + #0;
       
   108     soundFilePath:= UserPathPrefix + '/VideoTemp/' + cRecPrefix + '.sw' + #0;
   116     soundFilePath:= UserPathPrefix + '/VideoTemp/' + cRecPrefix + '.sw' + #0;
   109     cAVFormat+= #0;
   117     cAVFormat+= #0;
   110     cAudioCodec+= #0;
   118     cAudioCodec+= #0;
   111     cVideoCodec+= #0;
   119     cVideoCodec+= #0;
   112     cVideoPreset+= #0;
   120     cVideoPreset+= #0;
   113     AVWrapper_Init(@AddFileLogRaw, @filename[1], @finalFilename[1], @soundFilePath[1], @cAVFormat[1], @cVideoCodec[1], @cAudioCodec[1], @cVideoPreset[1],
   121     AVWrapper_Init(@AddFileLogRaw, @filename[1], @desc[1], @soundFilePath[1], @cAVFormat[1], @cVideoCodec[1], @cAudioCodec[1], @cVideoPreset[1],
   114                    cScreenWidth, cScreenHeight, cVideoFramerateNum, cVideoFramerateDen, frequency, channels, cAudioQuality, cVideoQuality);
   122                    cScreenWidth, cScreenHeight, cVideoFramerateNum, cVideoFramerateDen, cAudioQuality, cVideoQuality);
   115 
   123 
   116     YCbCr_Planes[0]:= GetMem(numPixels);
   124     YCbCr_Planes[0]:= GetMem(numPixels);
   117     YCbCr_Planes[1]:= GetMem(numPixels div 4);
   125     YCbCr_Planes[1]:= GetMem(numPixels div 4);
   118     YCbCr_Planes[2]:= GetMem(numPixels div 4);
   126     YCbCr_Planes[2]:= GetMem(numPixels div 4);
   119 
   127 
   142     FreeMem(RGB_Buffer, 4*numPixels);
   150     FreeMem(RGB_Buffer, 4*numPixels);
   143     Close(cameraFile);
   151     Close(cameraFile);
   144     AVWrapper_Close();
   152     AVWrapper_Close();
   145     DeleteFile(cameraFilePath);
   153     DeleteFile(cameraFilePath);
   146     DeleteFile(soundFilePath);
   154     DeleteFile(soundFilePath);
       
   155     SendIPC(_S'v');
   147 end;
   156 end;
   148 
   157 
   149 function pixel(x, y, color: LongInt): LongInt;
   158 function pixel(x, y, color: LongInt): LongInt;
   150 begin
   159 begin
   151     pixel:= RGB_Buffer[(cScreenHeight-y-1)*cScreenWidth*4 + x*4 + color];
   160     pixel:= RGB_Buffer[(cScreenHeight-y-1)*cScreenWidth*4 + x*4 + color];
   173             YCbCr_Planes[1][y*(cScreenWidth div 2) + x]:= Byte(128 + ((-2428*r - 4768*g + 7196*b) shr 16));
   182             YCbCr_Planes[1][y*(cScreenWidth div 2) + x]:= Byte(128 + ((-2428*r - 4768*g + 7196*b) shr 16));
   174             YCbCr_Planes[2][y*(cScreenWidth div 2) + x]:= Byte(128 + (( 7196*r - 6026*g - 1170*b) shr 16));
   183             YCbCr_Planes[2][y*(cScreenWidth div 2) + x]:= Byte(128 + (( 7196*r - 6026*g - 1170*b) shr 16));
   175         end;
   184         end;
   176 
   185 
   177     AVWrapper_WriteFrame(YCbCr_Planes[0], YCbCr_Planes[1], YCbCr_Planes[2]);
   186     AVWrapper_WriteFrame(YCbCr_Planes[0], YCbCr_Planes[1], YCbCr_Planes[2]);
       
   187 
       
   188     // inform frontend that we have encoded new frame (p for progress)
       
   189     SendIPC(_S'p');
   178 end;
   190 end;
   179 
   191 
   180 // returns new game ticks
   192 // returns new game ticks
   181 function LoadNextCameraPosition: LongInt;
   193 function LoadNextCameraPosition: LongInt;
   182 var NextTime: LongInt;
   194 var frame: TFrame;
   183     NextZoom: LongInt;
       
   184     NextWorldDx, NextWorldDy: LongInt;
       
   185 begin
   195 begin
   186 {$IOCHECKS OFF}
   196 {$IOCHECKS OFF}
   187     if eof(cameraFile) then
   197     if eof(cameraFile) then
   188         exit(-1);
   198         exit(-1);
   189     ReadLn(cameraFile, NextTime, NextWorldDx, NextWorldDy, NextZoom);
   199     BlockRead(cameraFile, frame, 1);
   190 {$IOCHECKS ON}
   200 {$IOCHECKS ON}
   191     WorldDx:= NextWorldDx;
   201     WorldDx:= frame.CamX;
   192     WorldDy:= NextWorldDy + cScreenHeight div 2;
   202     WorldDy:= frame.CamY + cScreenHeight div 2;
   193     zoom:= NextZoom/10000*cScreenWidth;
   203     zoom:= frame.zoom*cScreenWidth;
   194     ZoomValue:= zoom;
   204     ZoomValue:= zoom;
   195     LoadNextCameraPosition:= NextTime;
   205     LoadNextCameraPosition:= frame.ticks;
   196 end;
   206 end;
   197 
   207 
   198 // Callback which records sound.
   208 // Callback which records sound.
   199 // This procedure may be called from different thread.
   209 // This procedure may be called from different thread.
   200 procedure RecordPostMix(udata: pointer; stream: PByte; len: LongInt); cdecl;
   210 procedure RecordPostMix(udata: pointer; stream: PByte; len: LongInt); cdecl;
   206 end;
   216 end;
   207 
   217 
   208 procedure BeginPreRecording;
   218 procedure BeginPreRecording;
   209 var format: word;
   219 var format: word;
   210     filePrefix, filename: shortstring;
   220     filePrefix, filename: shortstring;
       
   221     frequency, channels: LongInt;
   211 begin
   222 begin
   212     AddFileLog('BeginPreRecording');
   223     AddFileLog('BeginPreRecording');
   213 
   224 
   214     nframes:= 0;
   225     numFrames:= 0;
   215     firstTick:= SDL_GetTicks();
   226     startTime:= SDL_GetTicks();
   216 
   227 
   217     filePrefix:= FormatDateTime('YYYY-MM-DD_HH-mm-ss', Now());
   228     filePrefix:= FormatDateTime('YYYY-MM-DD_HH-mm-ss', Now());
   218 
   229 
   219     Mix_QuerySpec(@frequency, @format, @channels);
   230     Mix_QuerySpec(@frequency, @format, @channels);
       
   231     AddFileLog('sound: frequency = ' + IntToStr(frequency) + ', format = ' + IntToStr(format) + ', channels = ' + IntToStr(channels));
   220     if format <> $8010 then
   232     if format <> $8010 then
   221     begin
   233     begin
   222         // TODO: support any audio format
   234         // TODO: support any audio format
   223         AddFileLog('Error: Unexpected audio format ' + IntToStr(format));
   235         AddFileLog('Error: Unexpected audio format ' + IntToStr(format));
   224         exit;
   236         exit;
   242     if IOResult <> 0 then
   254     if IOResult <> 0 then
   243     begin
   255     begin
   244         AddFileLog('Error: Could not write to ' + filename);
   256         AddFileLog('Error: Could not write to ' + filename);
   245         exit;
   257         exit;
   246     end;
   258     end;
       
   259 
       
   260     BlockWrite(audioFile, frequency, 4);
       
   261     BlockWrite(audioFile, channels, 4);
   247 {$IOCHECKS ON}
   262 {$IOCHECKS ON}
   248     WriteLn(cameraFile, inttostr(frequency) + ' ' + inttostr(channels));
       
   249 
   263 
   250     // register callback for actual audio recording
   264     // register callback for actual audio recording
   251     Mix_SetPostMix(@RecordPostMix, nil);
   265     Mix_SetPostMix(@RecordPostMix, nil);
   252 
   266 
   253     flagPrerecording:= true;
   267     flagPrerecording:= true;
   265     Mix_SetPostMix(nil, nil);
   279     Mix_SetPostMix(nil, nil);
   266     SDL_UnlockAudio();
   280     SDL_UnlockAudio();
   267 end;
   281 end;
   268 
   282 
   269 procedure SaveCameraPosition;
   283 procedure SaveCameraPosition;
   270 var Ticks: LongInt;
   284 var curTime: LongInt;
   271 begin
   285     frame: TFrame;
   272     Ticks:= SDL_GetTicks();
   286 begin
   273     while (Ticks - firstTick)*cVideoFramerateNum > nframes*cVideoFramerateDen*1000 do
   287     curTime:= SDL_GetTicks();
   274     begin
   288     while Int64(curTime - startTime)*cVideoFramerateNum > Int64(numFrames)*cVideoFramerateDen*1000 do
   275         WriteLn(cameraFile, inttostr(GameTicks) + ' ' + inttostr(WorldDx) + ' ' + inttostr(WorldDy - cScreenHeight div 2) + ' ' + inttostr(Round(zoom*10000/cScreenWidth)));
   289     begin
   276         inc(nframes);
   290         frame.ticks:= GameTicks;
       
   291         frame.CamX:= WorldDx;
       
   292         frame.CamY:= WorldDy - cScreenHeight div 2;
       
   293         frame.zoom:= zoom/cScreenWidth;
       
   294         BlockWrite(cameraFile, frame, 1);
       
   295         inc(numFrames);
   277     end;
   296     end;
   278 end;
   297 end;
   279 
   298 
   280 procedure freeModule;
   299 procedure freeModule;
   281 begin
   300 begin