hedgewars/uVideoRec.pas
changeset 7392 bc3306c59a08
parent 7386 e82a076df09b
child 7538 2d6e69b392cb
equal deleted inserted replaced
7390:27bfd8bbde7e 7392:bc3306c59a08
    37 interface
    37 interface
    38 
    38 
    39 var flagPrerecording: boolean = false;
    39 var flagPrerecording: boolean = false;
    40 
    40 
    41 function BeginVideoRecording: Boolean;
    41 function BeginVideoRecording: Boolean;
    42 function LoadNextCameraPosition: LongInt;
    42 function LoadNextCameraPosition(var newRealTicks, newGameTicks: LongInt): Boolean;
    43 procedure EncodeFrame;
    43 procedure EncodeFrame;
    44 procedure StopVideoRecording;
    44 procedure StopVideoRecording;
    45 
    45 
    46 procedure BeginPreRecording;
    46 procedure BeginPreRecording;
    47 procedure StopPreRecording;
    47 procedure StopPreRecording;
    86     RGB_Buffer: PByte;
    86     RGB_Buffer: PByte;
    87     cameraFile: File of TFrame;
    87     cameraFile: File of TFrame;
    88     audioFile: File;
    88     audioFile: File;
    89     numPixels: LongWord;
    89     numPixels: LongWord;
    90     startTime, numFrames, curTime, progress, maxProgress: LongWord;
    90     startTime, numFrames, curTime, progress, maxProgress: LongWord;
    91     cameraFilePath, soundFilePath: shortstring;
    91     soundFilePath: shortstring;
    92     thumbnailSaved : Boolean;
    92     thumbnailSaved : Boolean;
    93 
    93 
    94 function BeginVideoRecording: Boolean;
    94 function BeginVideoRecording: Boolean;
    95 var filename, desc: shortstring;
    95 var filename, desc: shortstring;
    96 begin
    96 begin
    97     AddFileLog('BeginVideoRecording');
    97     AddFileLog('BeginVideoRecording');
    98 
    98 
    99 {$IOCHECKS OFF}
    99 {$IOCHECKS OFF}
   100     // open file with prerecorded camera positions
   100     // open file with prerecorded camera positions
   101     cameraFilePath:= UserPathPrefix + '/VideoTemp/' + RecPrefix + '.txtin';
   101     filename:= UserPathPrefix + '/VideoTemp/' + RecPrefix + '.txtin';
   102     Assign(cameraFile, cameraFilePath);
   102     Assign(cameraFile, filename);
   103     Reset(cameraFile);
   103     Reset(cameraFile);
   104     maxProgress:= FileSize(cameraFile);
   104     maxProgress:= FileSize(cameraFile);
   105     if IOResult <> 0 then
   105     if IOResult <> 0 then
   106     begin
   106     begin
   107         AddFileLog('Error: Could not read from ' + cameraFilePath);
   107         AddFileLog('Error: Could not read from ' + filename);
   108         exit(false);
   108         exit(false);
   109     end;
   109     end;
   110 {$IOCHECKS ON}
   110 {$IOCHECKS ON}
   111 
   111 
   112     // store some description in output file
   112     // store some description in output file
   161     FreeMem(YCbCr_Planes[1], numPixels div 4);
   161     FreeMem(YCbCr_Planes[1], numPixels div 4);
   162     FreeMem(YCbCr_Planes[2], numPixels div 4);
   162     FreeMem(YCbCr_Planes[2], numPixels div 4);
   163     FreeMem(RGB_Buffer, 4*numPixels);
   163     FreeMem(RGB_Buffer, 4*numPixels);
   164     Close(cameraFile);
   164     Close(cameraFile);
   165     AVWrapper_Close();
   165     AVWrapper_Close();
   166     DeleteFile(cameraFilePath);
   166     Erase(cameraFile);
   167     DeleteFile(soundFilePath);
   167     DeleteFile(soundFilePath);
   168     SendIPC(_S'v'); // inform frontend that we finished
   168     SendIPC(_S'v'); // inform frontend that we finished
   169 end;
   169 end;
   170 
   170 
   171 function pixel(x, y, color: LongInt): LongInt;
   171 function pixel(x, y, color: LongInt): LongInt;
   205     SDLNet_Write16(progress*10000 div maxProgress, @s[2]);
   205     SDLNet_Write16(progress*10000 div maxProgress, @s[2]);
   206     SendIPC(s);
   206     SendIPC(s);
   207     inc(numFrames);
   207     inc(numFrames);
   208 end;
   208 end;
   209 
   209 
   210 // returns new game ticks
   210 function LoadNextCameraPosition(var newRealTicks, newGameTicks: LongInt): Boolean;
   211 function LoadNextCameraPosition: LongInt;
       
   212 var frame: TFrame;
   211 var frame: TFrame;
   213 begin
   212 begin
   214     LoadNextCameraPosition:= GameTicks;
       
   215     // we need to skip or duplicate frames to match target framerate
   213     // we need to skip or duplicate frames to match target framerate
   216     while Int64(curTime)*cVideoFramerateNum <= Int64(numFrames)*cVideoFramerateDen*1000 do
   214     while Int64(curTime)*cVideoFramerateNum <= Int64(numFrames)*cVideoFramerateDen*1000 do
   217     begin
   215     begin
   218     {$IOCHECKS OFF}
   216     {$IOCHECKS OFF}
   219         if eof(cameraFile) then
   217         if eof(cameraFile) then
   220             exit(-1);
   218             exit(false);
   221         BlockRead(cameraFile, frame, 1);
   219         BlockRead(cameraFile, frame, 1);
   222     {$IOCHECKS ON}
   220     {$IOCHECKS ON}
   223         curTime:= frame.realTicks;
   221         curTime:= frame.realTicks;
   224         WorldDx:= frame.CamX;
   222         WorldDx:= frame.CamX;
   225         WorldDy:= frame.CamY + cScreenHeight div 2;
   223         WorldDy:= frame.CamY + cScreenHeight div 2;
   226         zoom:= frame.zoom*cScreenWidth;
   224         zoom:= frame.zoom*cScreenWidth;
   227         ZoomValue:= zoom;
   225         ZoomValue:= zoom;
   228         inc(progress);
   226         inc(progress);
   229         LoadNextCameraPosition:= frame.gameTicks;
   227         newRealTicks:= frame.realTicks;
   230     end;
   228         newGameTicks:= frame.gameTicks;
       
   229     end;
       
   230     LoadNextCameraPosition:= true;
   231 end;
   231 end;
   232 
   232 
   233 // Callback which records sound.
   233 // Callback which records sound.
   234 // This procedure may be called from different thread.
   234 // This procedure may be called from different thread.
   235 procedure RecordPostMix(udata: pointer; stream: PByte; len: LongInt); cdecl;
   235 procedure RecordPostMix(udata: pointer; stream: PByte; len: LongInt); cdecl;
   249     k:= max(max(cScreenWidth, cScreenHeight) div 400, 1); // here 400 is minimum size of thumbnail
   249     k:= max(max(cScreenWidth, cScreenHeight) div 400, 1); // here 400 is minimum size of thumbnail
   250     MakeScreenshot(thumbpath, k);
   250     MakeScreenshot(thumbpath, k);
   251     thumbnailSaved:= true;
   251     thumbnailSaved:= true;
   252 end;
   252 end;
   253 
   253 
       
   254 // copy file (free pascal doesn't have copy file function)
       
   255 procedure CopyFile(src, dest: shortstring);
       
   256 var inF, outF: file;
       
   257     buffer: array[0..1023] of byte;
       
   258     result: LongInt;
       
   259 begin
       
   260 {$IOCHECKS OFF}
       
   261     result:= 0; // avoid compiler hint
       
   262 
       
   263     Assign(inF, src);
       
   264     Reset(inF, 1);
       
   265     if IOResult <> 0 then
       
   266     begin
       
   267         AddFileLog('Error: Could not read from ' + src);
       
   268         exit;
       
   269     end;
       
   270 
       
   271     Assign(outF, dest);
       
   272     Rewrite(outF, 1);
       
   273     if IOResult <> 0 then
       
   274     begin
       
   275         AddFileLog('Error: Could not write to ' + dest);
       
   276         exit;
       
   277     end;
       
   278 
       
   279     repeat
       
   280         BlockRead(inF, buffer, 1024, result);
       
   281         BlockWrite(outF, buffer, result);
       
   282     until result < 1024;
       
   283 {$IOCHECKS ON}
       
   284 end;
       
   285 
   254 procedure BeginPreRecording;
   286 procedure BeginPreRecording;
   255 var format: word;
   287 var format: word;
   256     filename: shortstring;
   288     filename: shortstring;
   257     frequency, channels: LongInt;
   289     frequency, channels: LongInt;
   258 begin
   290 begin
   259     AddFileLog('BeginPreRecording');
   291     AddFileLog('BeginPreRecording');
   260 
   292 
   261     thumbnailSaved:= false;
   293     thumbnailSaved:= false;
   262     RecPrefix:= 'hw-' + FormatDateTime('YYYY-MM-DD_HH-mm-ss-z', Now());
   294     RecPrefix:= 'hw-' + FormatDateTime('YYYY-MM-DD_HH-mm-ss-z', Now());
       
   295 
       
   296     // If this video is recorded from demo executed directly (without frontend)
       
   297     // then we need to copy demo so that frontend will be able to find it later.
       
   298     if recordFileName <> '' then
       
   299     begin
       
   300         if GameType <> gmtDemo then // this is save and game demo is not recording, abort
       
   301             exit;
       
   302         CopyFile(recordFileName, UserPathPrefix + '/VideoTemp/' + RecPrefix + '.hwd');
       
   303     end;
   263 
   304 
   264     Mix_QuerySpec(@frequency, @format, @channels);
   305     Mix_QuerySpec(@frequency, @format, @channels);
   265     AddFileLog('sound: frequency = ' + IntToStr(frequency) + ', format = ' + IntToStr(format) + ', channels = ' + IntToStr(channels));
   306     AddFileLog('sound: frequency = ' + IntToStr(frequency) + ', format = ' + IntToStr(format) + ', channels = ' + IntToStr(channels));
   266     if format <> $8010 then
   307     if format <> $8010 then
   267     begin
   308     begin