Mercurial > vba-linux
comparison src/common/nesvideos-piece.cpp @ 1:f9f4f1b99eed
importing src directory
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 03 Mar 2012 10:31:27 -0600 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
0:8ced16adf2e1 | 1:f9f4f1b99eed |
---|---|
1 #include <cmath> | |
2 #include <cstdio> | |
3 #include <string> | |
4 #include <vector> | |
5 | |
6 /* Note: This module assumes everyone uses RGB15 as display depth */ | |
7 | |
8 static std::string VIDEO_CMD = | |
9 "mencoder - -o test0.avi" | |
10 " -noskip -mc 0" | |
11 " -ovc lavc" | |
12 " -oac mp3lame" | |
13 " -lameopts preset=256:aq=2:mode=3" | |
14 " -lavcopts vcodec=ffv1:context=0:format=BGR32:coder=0:vstrict=-1" | |
15 " >& mencoder.log"; | |
16 | |
17 static void FlushWrite(FILE* fp, const unsigned char*buf, unsigned length); | |
18 | |
19 #define BGR24 (0x42475218) // BGR24 fourcc | |
20 #define BGR16 (0x42475210) // BGR16 fourcc | |
21 #define BGR15 (0x4247520F) // BGR15 fourcc | |
22 | |
23 static FILE* (*openFunc) (const char*, const char*) = NULL; | |
24 static int (*closeFunc) (FILE*) = NULL; | |
25 | |
26 #if (defined(WIN32) || defined(win32)) // capital is standard, but check for either | |
27 #include <cstdlib> | |
28 #define popen _popen; | |
29 #define pclose _pclose; | |
30 #endif | |
31 | |
32 #define u32(n) (n)&255,((n)>>8)&255,((n)>>16)&255,((n)>>24)&255 | |
33 #define u16(n) (n)&255,((n)>>8)&255 | |
34 #define s4(s) s[0],s[1],s[2],s[3] | |
35 | |
36 static const unsigned FPS_SCALE = (0x1000000); | |
37 | |
38 // general-purpose A/V sync debugging, ignored unless explicitly enabled with NESVideoEnableDebugging | |
39 static void (*debugVideoMessageFunc)(const char *msg) = NULL; | |
40 static void (*debugAudioMessageFunc)(const char *msg) = NULL; | |
41 // logo adds 1 "frame" to audio, so offset that (A/V frames shouldn't necessarily match up depending on the rates, but should at least make them start out matching in case they do) | |
42 static unsigned audioFramesWritten=0, videoFramesWritten=1; | |
43 static double audioSecondsWritten=0, videoSecondsWritten=0; | |
44 | |
45 | |
46 static class AVI | |
47 { | |
48 FILE* avifp; | |
49 | |
50 bool KnowVideo; | |
51 unsigned width; | |
52 unsigned height; | |
53 unsigned fps_scaled; | |
54 std::vector<unsigned char> VideoBuffer; | |
55 | |
56 bool KnowAudio; | |
57 unsigned rate; | |
58 unsigned chans; | |
59 unsigned bits; | |
60 std::vector<unsigned char> AudioBuffer; | |
61 | |
62 public: | |
63 AVI() : | |
64 avifp(NULL), | |
65 KnowVideo(false), | |
66 KnowAudio(false) | |
67 { | |
68 } | |
69 ~AVI() | |
70 { | |
71 if(avifp) closeFunc(avifp); | |
72 } | |
73 | |
74 void Audio(unsigned r,unsigned b,unsigned c, | |
75 const unsigned char*d, unsigned nsamples) | |
76 { | |
77 if(!KnowAudio) | |
78 { | |
79 rate = r; | |
80 chans = c; | |
81 bits = b; | |
82 KnowAudio = true; | |
83 CheckFlushing(); | |
84 } | |
85 unsigned bytes = nsamples*chans*(bits/8); | |
86 | |
87 if(debugAudioMessageFunc) | |
88 { | |
89 audioFramesWritten++; | |
90 audioSecondsWritten += (double)nsamples / (double)rate; // += bytes times seconds per byte | |
91 char temp [64]; | |
92 sprintf(temp, "A: %.2lf s, %d f", audioSecondsWritten, audioFramesWritten); | |
93 debugAudioMessageFunc(temp); | |
94 } | |
95 | |
96 if(KnowVideo) | |
97 SendAudioFrame(d, bytes); | |
98 else | |
99 { | |
100 AudioBuffer.insert(AudioBuffer.end(), d, d+bytes); | |
101 fprintf(stderr, "Buffering %u bytes of audio\n", bytes); | |
102 } | |
103 } | |
104 void Video(unsigned w,unsigned h,unsigned f, const unsigned char*d) | |
105 { | |
106 if(!KnowVideo) | |
107 { | |
108 width=w; | |
109 height=h; | |
110 fps_scaled=f; | |
111 KnowVideo = true; | |
112 CheckFlushing(); | |
113 } | |
114 | |
115 unsigned bytes = width*height*2; | |
116 | |
117 //std::vector<unsigned char> tmp(bytes, 'k'); | |
118 //d = &tmp[0]; | |
119 | |
120 if(debugVideoMessageFunc) | |
121 { | |
122 videoFramesWritten++; | |
123 videoSecondsWritten += (double)FPS_SCALE / (double)fps_scaled; // += seconds per frame | |
124 char temp [64]; | |
125 sprintf(temp, "V: %.2lf s, %d f", videoSecondsWritten, videoFramesWritten); | |
126 debugVideoMessageFunc(temp); | |
127 } | |
128 | |
129 if(KnowAudio) | |
130 SendVideoFrame(d, bytes); | |
131 else | |
132 { | |
133 VideoBuffer.insert(VideoBuffer.end(), d, d+bytes); | |
134 fprintf(stderr, "Buffering %u bytes of video\n", bytes); | |
135 } | |
136 } | |
137 | |
138 private: | |
139 void CheckFlushing() | |
140 { | |
141 //AudioBuffer.clear(); | |
142 //VideoBuffer.clear(); | |
143 | |
144 if(KnowAudio && KnowVideo) | |
145 { | |
146 unsigned last_offs; | |
147 | |
148 // Flush Audio | |
149 | |
150 last_offs = 0; | |
151 while(last_offs < AudioBuffer.size()) | |
152 { | |
153 unsigned bytes = rate / (fps_scaled / FPS_SCALE); | |
154 bytes *= chans*(bits/8); | |
155 | |
156 unsigned remain = AudioBuffer.size() - last_offs; | |
157 if(bytes > remain) bytes = remain; | |
158 if(!bytes) break; | |
159 | |
160 unsigned begin = last_offs; | |
161 last_offs += bytes; | |
162 SendAudioFrame(&AudioBuffer[begin], bytes); | |
163 } | |
164 AudioBuffer.erase(AudioBuffer.begin(), AudioBuffer.begin()+last_offs); | |
165 | |
166 // Flush Video | |
167 | |
168 last_offs = 0; | |
169 while(last_offs < VideoBuffer.size()) | |
170 { | |
171 unsigned bytes = width*height*2; | |
172 unsigned remain = VideoBuffer.size() - last_offs; | |
173 if(bytes > remain) bytes = remain; | |
174 if(!bytes)break; | |
175 | |
176 unsigned begin = last_offs; | |
177 last_offs += bytes; | |
178 SendVideoFrame(&VideoBuffer[begin], bytes); | |
179 } | |
180 VideoBuffer.erase(VideoBuffer.begin(), VideoBuffer.begin()+last_offs); | |
181 } | |
182 } | |
183 | |
184 void SendVideoFrame(const unsigned char* vidbuf, unsigned framesize) | |
185 { | |
186 CheckBegin(); | |
187 | |
188 //fprintf(stderr, "Writing 00dc of %u bytes\n", framesize); | |
189 | |
190 const unsigned char header[] = { s4("00dc"), u32(framesize) }; | |
191 FlushWrite(avifp, header, sizeof(header)); | |
192 FlushWrite(avifp, vidbuf, framesize); | |
193 } | |
194 | |
195 void SendAudioFrame(const unsigned char* audbuf, unsigned framesize) | |
196 { | |
197 CheckBegin(); | |
198 | |
199 //fprintf(stderr, "Writing 01wb of %u bytes\n", framesize); | |
200 | |
201 const unsigned char header[] = { s4("01wb"), u32(framesize) }; | |
202 FlushWrite(avifp, header, sizeof(header)); | |
203 FlushWrite(avifp, audbuf, framesize); | |
204 } | |
205 | |
206 void CheckBegin() | |
207 { | |
208 if(avifp) return; | |
209 | |
210 if(!openFunc) openFunc = popen; // default | |
211 if(!closeFunc) closeFunc = pclose; // default | |
212 | |
213 avifp = openFunc(VIDEO_CMD.c_str(), "wb"); | |
214 if(!avifp) return; | |
215 | |
216 const unsigned fourcc = BGR16; | |
217 const unsigned framesize = width*height*2; | |
218 | |
219 const unsigned aud_rate = rate; | |
220 const unsigned aud_chans = chans; | |
221 const unsigned aud_bits = bits; | |
222 | |
223 const unsigned nframes = 0; //unknown | |
224 const unsigned scale = FPS_SCALE; | |
225 const unsigned scaled_fps = fps_scaled; | |
226 | |
227 const unsigned SIZE_strh_vids = 4 + 4*2 + 2*2 + 8*4 + 2*4; | |
228 const unsigned SIZE_strf_vids = 4*3 + 2*2 + 4*6; | |
229 const unsigned SIZE_strl_vids = 4+ 4+(4+SIZE_strh_vids) + 4+(4+SIZE_strf_vids); | |
230 | |
231 const unsigned SIZE_strh_auds = 4 + 4*3 + 2*2 + 4*8 + 2*4; | |
232 const unsigned SIZE_strf_auds = 2*2 + 4*2 + 2*3; | |
233 const unsigned SIZE_strl_auds = 4+ 4+(4+SIZE_strh_auds) + 4+(4+SIZE_strf_auds); | |
234 | |
235 const unsigned SIZE_avih = 4*12; | |
236 const unsigned SIZE_hdrl = 4+4+ (4+SIZE_avih) + 4 + (4+SIZE_strl_vids) + 4 + (4+SIZE_strl_auds); | |
237 const unsigned SIZE_movi = 4 + nframes*(4+4+framesize); | |
238 const unsigned SIZE_avi = 4+4+ (4+SIZE_hdrl) + 4 + (4+SIZE_movi); | |
239 | |
240 const unsigned char AVIheader[] = | |
241 { | |
242 s4("RIFF"), | |
243 u32(SIZE_avi), | |
244 s4("AVI "), | |
245 | |
246 // HEADER | |
247 | |
248 s4("LIST"), | |
249 u32(SIZE_hdrl), | |
250 s4("hdrl"), | |
251 | |
252 s4("avih"), | |
253 u32(SIZE_avih), | |
254 u32(0), | |
255 u32(0), | |
256 u32(0), | |
257 u32(0), | |
258 u32(nframes), | |
259 u32(0), | |
260 u32(2), // two streams | |
261 u32(0), | |
262 u32(0), | |
263 u32(0), | |
264 u32(0), | |
265 u32(0), | |
266 | |
267 // VIDEO HEADER | |
268 | |
269 s4("LIST"), | |
270 u32(SIZE_strl_vids), | |
271 s4("strl"), | |
272 | |
273 s4("strh"), | |
274 u32(SIZE_strh_vids), | |
275 s4("vids"), | |
276 u32(0), | |
277 u32(0), | |
278 u16(0), | |
279 u16(0), | |
280 u32(0), | |
281 u32(scale), | |
282 u32(scaled_fps), | |
283 u32(0), | |
284 u32(0), | |
285 u32(0), | |
286 u32(0), | |
287 u32(0), | |
288 u16(0), | |
289 u16(0), | |
290 u16(0), | |
291 u16(0), | |
292 | |
293 s4("strf"), | |
294 u32(SIZE_strf_vids), | |
295 u32(0), | |
296 u32(width), | |
297 u32(height), | |
298 u16(0), | |
299 u16(0), | |
300 u32(fourcc), | |
301 u32(0), | |
302 u32(0), | |
303 u32(0), | |
304 u32(0), | |
305 u32(0), | |
306 | |
307 // AUDIO HEADER | |
308 | |
309 s4("LIST"), | |
310 u32(SIZE_strl_auds), | |
311 s4("strl"), | |
312 | |
313 s4("strh"), | |
314 u32(SIZE_strh_auds), | |
315 s4("auds"), | |
316 u32(0), //fourcc | |
317 u32(0), //handler | |
318 u32(0), //flags | |
319 u16(0), //prio | |
320 u16(0), //lang | |
321 u32(0), //init frames | |
322 u32(1), //scale | |
323 u32(aud_rate), | |
324 u32(0), //start | |
325 u32(0), //rate*length | |
326 u32(1048576), //suggested bufsize | |
327 u32(0), //quality | |
328 u32(aud_chans * (aud_bits / 8)), //sample size | |
329 u16(0), //frame size | |
330 u16(0), | |
331 u16(0), | |
332 u16(0), | |
333 | |
334 s4("strf"), | |
335 u32(SIZE_strf_auds), | |
336 u16(1), // pcm format | |
337 u16(aud_chans), | |
338 u32(aud_rate), | |
339 u32(aud_rate * aud_chans * (aud_bits/8)), // samples per second | |
340 u16(aud_chans * (aud_bits/8)), //block align | |
341 u16(aud_bits), //bits | |
342 u16(0), //cbSize | |
343 | |
344 // MOVIE | |
345 | |
346 s4("LIST"), | |
347 u32(SIZE_movi), | |
348 s4("movi") | |
349 }; | |
350 | |
351 FlushWrite(avifp, AVIheader, sizeof(AVIheader)); | |
352 } | |
353 } AVI; | |
354 | |
355 extern "C" | |
356 { | |
357 int LoggingEnabled = 0; /* 0=no, 1=yes, 2=recording! */ | |
358 | |
359 const char* NESVideoGetVideoCmd() | |
360 { | |
361 return VIDEO_CMD.c_str(); | |
362 } | |
363 void NESVideoSetVideoCmd(const char *cmd) | |
364 { | |
365 VIDEO_CMD = cmd; | |
366 } | |
367 void NESVideoEnableDebugging( void videoMessageFunc(const char *msg), void audioMessageFunc(const char *msg) ) | |
368 { | |
369 debugVideoMessageFunc = videoMessageFunc; | |
370 debugAudioMessageFunc = audioMessageFunc; | |
371 } | |
372 void NESVideoSetFileFuncs( FILE* open(const char *,const char *), int close(FILE*) ) | |
373 { | |
374 openFunc = open; | |
375 closeFunc = close; | |
376 } | |
377 | |
378 void NESVideoLoggingVideo | |
379 (const void*data, unsigned width,unsigned height, | |
380 unsigned fps_scaled | |
381 ) | |
382 { | |
383 if(LoggingEnabled < 2) return; | |
384 | |
385 unsigned LogoFrames = fps_scaled >> 24; | |
386 | |
387 static bool First = true; | |
388 if(First) | |
389 { | |
390 First=false; | |
391 /* Bisqwit's logo addition routine. */ | |
392 /* If you don't have his files, this function does nothing | |
393 * and it does not matter at all. | |
394 */ | |
395 | |
396 const char *background = | |
397 width==320 ? "logo320_240" | |
398 : width==160 ? "logo160_144" | |
399 : width==240 ? "logo240_160" | |
400 : height>224 ? "logo256_240" | |
401 : "logo256_224"; | |
402 | |
403 /* Note: This should be 1 second long. */ | |
404 for(unsigned frame = 0; frame < LogoFrames; ++frame) | |
405 { | |
406 char Buf[4096]; | |
407 sprintf(Buf, "/shares/home/bisqwit/povray/nesvlogo/%s_f%u.tga", | |
408 background, frame); | |
409 | |
410 FILE*fp = fopen(Buf, "rb"); | |
411 if(!fp) // write blackness when missing frames to keep the intro 1 second long: | |
412 { | |
413 unsigned bytes = width*height*2; | |
414 unsigned char* buf = (unsigned char*)malloc(bytes); | |
415 if(buf) | |
416 { | |
417 memset(buf,0,bytes); | |
418 AVI.Video(width,height,fps_scaled, buf); | |
419 if(debugVideoMessageFunc) videoFramesWritten--; | |
420 free(buf); | |
421 } | |
422 } | |
423 else // write 1 frame of the logo: | |
424 { | |
425 int idlen = fgetc(fp); | |
426 /* Silently ignore all other header data. | |
427 * These files are assumed to be uncompressed BGR24 tga files with Y swapped. | |
428 * Even their geometry is assumed to match perfectly. | |
429 */ | |
430 fseek(fp, 1+1+2+2+1+ /*org*/2+2+ /*geo*/2+2+ 1+1+idlen, SEEK_CUR); | |
431 | |
432 bool yflip=true; | |
433 std::vector<unsigned char> data(width*height*3); | |
434 for(unsigned y=height; y-->0; ) | |
435 fread(&data[y*width*3], 1, width*3, fp); | |
436 fclose(fp); | |
437 | |
438 std::vector<unsigned short> result(width*height); | |
439 for(unsigned pos=0, max=result.size(); pos<max; ++pos) | |
440 { | |
441 unsigned usepos = pos; | |
442 if(yflip) | |
443 { | |
444 unsigned y = pos/width; | |
445 usepos = (usepos%width) + (height-y-1)*width; | |
446 } | |
447 | |
448 unsigned B = data[usepos*3+0]; | |
449 unsigned G = data[usepos*3+1]; | |
450 unsigned R = data[usepos*3+2]; | |
451 result[pos] = ((B*31/255)<<0) | |
452 | ((G*63/255)<<5) | |
453 | ((R*31/255)<<11); | |
454 } | |
455 AVI.Video(width,height,fps_scaled, (const unsigned char*)&result[0]); | |
456 if(debugVideoMessageFunc) videoFramesWritten--; | |
457 } | |
458 } | |
459 } | |
460 AVI.Video(width,height,fps_scaled, (const unsigned char*) data); | |
461 } | |
462 | |
463 void NESVideoLoggingAudio | |
464 (const void*data, | |
465 unsigned rate, unsigned bits, unsigned chans, | |
466 unsigned nsamples) | |
467 { | |
468 if(LoggingEnabled < 2) return; | |
469 | |
470 static bool First = true; | |
471 if(First) | |
472 { | |
473 First=false; | |
474 | |
475 const unsigned n = rate; // assumes 1 second of logo to write silence for | |
476 if(n > 0) | |
477 { | |
478 unsigned bytes = n*chans*(bits/8); | |
479 unsigned char* buf = (unsigned char*)malloc(bytes); | |
480 if(buf) | |
481 { | |
482 memset(buf,0,bytes); | |
483 AVI.Audio(rate,bits,chans, buf, n); | |
484 free(buf); | |
485 } | |
486 } | |
487 } | |
488 | |
489 AVI.Audio(rate,bits,chans, (const unsigned char*) data, nsamples); | |
490 } | |
491 } /* extern "C" */ | |
492 | |
493 | |
494 | |
495 static void FlushWrite(FILE* fp, const unsigned char*buf, unsigned length) | |
496 { | |
497 /// unsigned failures = 0; | |
498 /// const static int FAILURE_THRESH = 8092; // don't want to loop infinitely if we keep failing to make progress - actually maybe you would want this, so the checking is disabled | |
499 while(length > 0 /*&& failures < FAILURE_THRESH*/) | |
500 { | |
501 unsigned written = fwrite(buf, 1, length, fp); | |
502 /// if(written == 0) | |
503 /// failures++; | |
504 /// else | |
505 /// { | |
506 length -= written; | |
507 buf += written; | |
508 /// failures = 0; | |
509 /// } | |
510 } | |
511 /// if(failures >= FAILURE_THRESH) | |
512 /// { | |
513 /// fprintf(stderr, "FlushWrite() failed to write %d bytes %d times - giving up.", length, failures); | |
514 /// LoggingEnabled = 0; | |
515 /// } | |
516 } | |
517 | |
518 // for the UB tech | |
519 #undef BGR24 | |
520 #undef BGR16 | |
521 #undef BGR15 | |
522 | |
523 #undef u32 | |
524 #undef u16 | |
525 #undef s4 |