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