67 zoom: single; |
67 zoom: single; |
68 end; |
68 end; |
69 |
69 |
70 var RGB_Buffer: PByte; |
70 var RGB_Buffer: PByte; |
71 cameraFile: File; |
71 cameraFile: File; |
|
72 cameraFileName: shortstring; |
72 audioFile: File; |
73 audioFile: File; |
73 numPixels: LongWord; |
74 numPixels: LongWord; |
74 startTime, numFrames, curTime, progress, maxProgress: LongWord; |
75 startTime, numFrames, curTime, progress, maxProgress: LongWord; |
75 soundFilePath: shortstring; |
76 soundFilePath: shortstring; |
76 thumbnailSaved: boolean; |
77 thumbnailSaved: boolean; |
77 recordAudio: boolean; |
78 recordAudio: boolean; |
78 |
79 |
79 function BeginVideoRecording: Boolean; |
80 function BeginVideoRecording: Boolean; |
80 var filename, desc: shortstring; |
81 var filename, desc: shortstring; |
|
82 filenameA, descA, soundFilePathA, cAVFormatA, cVideoCodecA, cAudioCodecA: ansistring; |
81 begin |
83 begin |
82 AddFileLog('BeginVideoRecording'); |
84 AddFileLog('BeginVideoRecording'); |
83 |
85 |
84 {$IOCHECKS OFF} |
86 {$IOCHECKS OFF} |
85 // open file with prerecorded camera positions |
87 // open file with prerecorded camera positions |
86 filename:= shortstring(UserPathPrefix) + '/VideoTemp/' + shortstring(RecPrefix) + '.txtin'; |
88 cameraFileName:= shortstring(UserPathPrefix) + '/VideoTemp/' + shortstring(RecPrefix) + '.txtin'; |
87 Assign(cameraFile, filename); |
89 Assign(cameraFile, cameraFileName); |
88 Reset(cameraFile, SizeOf(TFrame)); |
90 Reset(cameraFile, SizeOf(TFrame)); |
89 maxProgress:= FileSize(cameraFile); |
91 maxProgress:= FileSize(cameraFile); |
90 if IOResult <> 0 then |
92 if IOResult <> 0 then |
91 begin |
93 begin |
92 AddFileLog('Error: Could not read from ' + filename); |
94 AddFileLog('Error: Could not read from ' + cameraFileName); |
93 exit(false); |
95 exit(false); |
94 end; |
96 end; |
95 {$IOCHECKS ON} |
97 {$IOCHECKS ON} |
96 |
98 |
97 { Store some description in output file. |
99 { Store some description in output file. |
119 if recordAudio then |
121 if recordAudio then |
120 soundFilePath:= shortstring(UserPathPrefix) + '/VideoTemp/' + shortstring(RecPrefix) + '.sw' |
122 soundFilePath:= shortstring(UserPathPrefix) + '/VideoTemp/' + shortstring(RecPrefix) + '.sw' |
121 else |
123 else |
122 soundFilePath:= ''; |
124 soundFilePath:= ''; |
123 |
125 |
|
126 filenameA:= ansistring(filename); |
|
127 descA:= ansistring(desc); |
|
128 soundFilePathA:= ansistring(soundFilePath); |
|
129 cAVFormatA:= ansistring(cAVFormat); |
|
130 cVideoCodecA:= ansistring(cVideoCodec); |
|
131 cAudioCodecA:= ansistring(cAudioCodec); |
124 if checkFails(AVWrapper_Init(@AddFileLogRaw |
132 if checkFails(AVWrapper_Init(@AddFileLogRaw |
125 , PChar(ansistring(filename)) |
133 , PChar(filenameA) |
126 , PChar(ansistring(desc)) |
134 , PChar(descA) |
127 , PChar(ansistring(soundFilePath)) |
135 , PChar(soundFilePathA) |
128 , PChar(ansistring(cAVFormat)) |
136 , PChar(cAVFormatA) |
129 , PChar(ansistring(cVideoCodec)) |
137 , PChar(cVideoCodecA) |
130 , PChar(ansistring(cAudioCodec)) |
138 , PChar(cAudioCodecA) |
131 , cScreenWidth, cScreenHeight, cVideoFramerateNum, cVideoFramerateDen, cVideoQuality) >= 0, |
139 , cScreenWidth, cScreenHeight, cVideoFramerateNum, cVideoFramerateDen, cVideoQuality) >= 0, |
132 'AVWrapper_Init failed', |
140 'AVWrapper_Init failed', |
133 true) then exit(false); |
141 true) then exit(false); |
134 |
142 |
135 numPixels:= cScreenWidth*cScreenHeight; |
143 numPixels:= cScreenWidth*cScreenHeight; |
156 begin |
164 begin |
157 AddFileLog('AVWrapper_Close() has failed.'); |
165 AddFileLog('AVWrapper_Close() has failed.'); |
158 halt(HaltVideoRec); |
166 halt(HaltVideoRec); |
159 end; |
167 end; |
160 {$IOCHECKS OFF} |
168 {$IOCHECKS OFF} |
161 // Provoke IOResult to be set |
169 if FileExists(cameraFileName) then |
162 FileSize(cameraFile); |
170 DeleteFile(cameraFileName) |
163 if IOResult = 0 then |
|
164 Erase(cameraFile) |
|
165 else |
171 else |
166 AddFileLog('Warning: Tried to delete the cameraFile but it was already deleted'); |
172 AddFileLog('Warning: Tried to delete the cameraFile but it was already deleted'); |
167 {$IOCHECKS ON} |
173 {$IOCHECKS ON} |
168 if recordAudio and FileExists(soundFilePath) then |
174 if recordAudio and FileExists(soundFilePath) then |
169 DeleteFile(soundFilePath); |
175 DeleteFile(soundFilePath); |
190 inc(numFrames); |
196 inc(numFrames); |
191 end; |
197 end; |
192 |
198 |
193 function LoadNextCameraPosition(var newRealTicks, newGameTicks: LongInt): Boolean; |
199 function LoadNextCameraPosition(var newRealTicks, newGameTicks: LongInt): Boolean; |
194 var frame: TFrame = (realTicks: 0; gameTicks: 0; CamX: 0; CamY: 0; zoom: 0); |
200 var frame: TFrame = (realTicks: 0; gameTicks: 0; CamX: 0; CamY: 0; zoom: 0); |
|
201 res: LongInt; |
195 begin |
202 begin |
196 // we need to skip or duplicate frames to match target framerate |
203 // we need to skip or duplicate frames to match target framerate |
197 while Int64(curTime)*cVideoFramerateNum <= Int64(numFrames)*cVideoFramerateDen*1000 do |
204 while Int64(curTime)*cVideoFramerateNum <= Int64(numFrames)*cVideoFramerateDen*1000 do |
198 begin |
205 begin |
|
206 res:= 0; |
199 {$IOCHECKS OFF} |
207 {$IOCHECKS OFF} |
200 if eof(cameraFile) then |
208 if eof(cameraFile) then |
201 exit(false); |
209 exit(false); |
202 BlockRead(cameraFile, frame, 1); |
210 BlockRead(cameraFile, frame, 1, res); |
203 {$IOCHECKS ON} |
211 {$IOCHECKS ON} |
204 curTime:= frame.realTicks; |
212 curTime:= frame.realTicks; |
205 WorldDx:= frame.CamX; |
213 WorldDx:= frame.CamX; |
206 WorldDy:= frame.CamY + cScreenHeight div 2; |
214 WorldDy:= frame.CamY + cScreenHeight div 2; |
207 zoom:= frame.zoom*cScreenWidth; |
215 zoom:= frame.zoom*cScreenWidth; |
214 end; |
222 end; |
215 |
223 |
216 // Callback which records sound. |
224 // Callback which records sound. |
217 // This procedure may be called from different thread. |
225 // This procedure may be called from different thread. |
218 procedure RecordPostMix(udata: pointer; stream: PByte; len: LongInt); cdecl; |
226 procedure RecordPostMix(udata: pointer; stream: PByte; len: LongInt); cdecl; |
219 begin |
227 var result: LongInt; |
|
228 begin |
|
229 result:= 0; // avoid warning |
220 udata:= udata; // avoid warning |
230 udata:= udata; // avoid warning |
221 {$IOCHECKS OFF} |
231 {$IOCHECKS OFF} |
222 BlockWrite(audioFile, stream^, len); |
232 BlockWrite(audioFile, stream^, len, result); |
223 {$IOCHECKS ON} |
233 {$IOCHECKS ON} |
224 end; |
234 end; |
225 |
235 |
226 procedure SaveThumbnail; |
236 procedure SaveThumbnail; |
227 var thumbpath: shortstring; |
237 var thumbpath: shortstring; |
236 |
246 |
237 // copy file (free pascal doesn't have copy file function) |
247 // copy file (free pascal doesn't have copy file function) |
238 procedure CopyFile(src, dest: shortstring); |
248 procedure CopyFile(src, dest: shortstring); |
239 var inF, outF: file; |
249 var inF, outF: file; |
240 buffer: array[0..1023] of byte; |
250 buffer: array[0..1023] of byte; |
241 result: LongInt; |
251 result, result2: LongInt; |
242 i: integer; |
252 i: integer; |
243 begin |
253 begin |
244 {$IOCHECKS OFF} |
254 {$IOCHECKS OFF} |
245 result:= 0; // avoid compiler hint and warning |
255 result:= 0; // avoid compiler hint and warning |
|
256 result2:= 0; // avoid compiler hint and warning |
246 for i:= 0 to 1023 do |
257 for i:= 0 to 1023 do |
247 buffer[i]:= 0; |
258 buffer[i]:= 0; |
248 |
259 |
249 Assign(inF, src); |
260 Assign(inF, src); |
250 Reset(inF, 1); |
261 Reset(inF, 1); |
262 exit; |
273 exit; |
263 end; |
274 end; |
264 |
275 |
265 repeat |
276 repeat |
266 BlockRead(inF, buffer, 1024, result); |
277 BlockRead(inF, buffer, 1024, result); |
267 BlockWrite(outF, buffer, result); |
278 BlockWrite(outF, buffer, result, result2); |
268 until result < 1024; |
279 until result < 1024; |
269 {$IOCHECKS ON} |
280 {$IOCHECKS ON} |
270 end; |
281 end; |
271 |
282 |
272 procedure BeginPreRecording; |
283 procedure BeginPreRecording; |
273 var format: word; |
284 var format: word; |
274 filename: shortstring; |
285 filename: shortstring; |
275 frequency, channels: LongInt; |
286 frequency, channels: LongInt; |
276 begin |
287 result: LongInt; |
|
288 begin |
|
289 result:= 0; |
277 AddFileLog('BeginPreRecording'); |
290 AddFileLog('BeginPreRecording'); |
278 |
291 |
279 thumbnailSaved:= false; |
292 thumbnailSaved:= false; |
280 RecPrefix:= 'hw-' + FormatDateTime('YYYY-MM-DD_HH-mm-ss-z', Now()); |
293 RecPrefix:= 'hw-' + FormatDateTime('YYYY-MM-DD_HH-mm-ss-z', TDateTime(Now())); |
281 |
294 |
282 // If this video is recorded from demo executed directly (without frontend) |
295 // If this video is recorded from demo executed directly (without frontend) |
283 // then we need to copy demo so that frontend will be able to find it later. |
296 // then we need to copy demo so that frontend will be able to find it later. |
284 if recordFileName <> '' then |
297 if recordFileName <> '' then |
285 begin |
298 begin |
322 end; |
335 end; |
323 |
336 |
324 if cIsSoundEnabled then |
337 if cIsSoundEnabled then |
325 begin |
338 begin |
326 // save audio parameters in sound file |
339 // save audio parameters in sound file |
327 BlockWrite(audioFile, frequency, 4); |
340 BlockWrite(audioFile, frequency, 4, result); |
328 BlockWrite(audioFile, channels, 4); |
341 BlockWrite(audioFile, channels, 4, result); |
329 {$IOCHECKS ON} |
342 {$IOCHECKS ON} |
330 |
343 |
331 // register callback for actual audio recording |
344 // register callback for actual audio recording |
332 Mix_SetPostMix(@RecordPostMix, nil); |
345 Mix_SetPostMix(@RecordPostMix, nil); |
333 end; |
346 end; |
358 SaveThumbnail(); |
371 SaveThumbnail(); |
359 end; |
372 end; |
360 |
373 |
361 procedure SaveCameraPosition; |
374 procedure SaveCameraPosition; |
362 var frame: TFrame; |
375 var frame: TFrame; |
363 begin |
376 result: LongInt; |
|
377 begin |
|
378 result:= 0; |
364 if (not thumbnailSaved) and (ScreenFade = sfNone) then |
379 if (not thumbnailSaved) and (ScreenFade = sfNone) then |
365 SaveThumbnail(); |
380 SaveThumbnail(); |
366 |
381 |
367 frame.realTicks:= SDL_GetTicks() - startTime; |
382 frame.realTicks:= SDL_GetTicks() - startTime; |
368 frame.gameTicks:= GameTicks; |
383 frame.gameTicks:= GameTicks; |
369 frame.CamX:= WorldDx; |
384 frame.CamX:= WorldDx; |
370 frame.CamY:= WorldDy - cScreenHeight div 2; |
385 frame.CamY:= WorldDy - cScreenHeight div 2; |
371 frame.zoom:= zoom/cScreenWidth; |
386 frame.zoom:= zoom/cScreenWidth; |
372 BlockWrite(cameraFile, frame, 1); |
387 BlockWrite(cameraFile, frame, 1, result); |
373 end; |
388 end; |
374 |
389 |
375 procedure initModule; |
390 procedure initModule; |
376 begin |
391 begin |
377 // we need to make sure these variables are initialized before the main loop |
392 // we need to make sure these variables are initialized before the main loop |