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