Mercurial > vba-linux
diff 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 |
line wrap: on
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/common/nesvideos-piece.cpp Sat Mar 03 10:31:27 2012 -0600 1.3 @@ -0,0 +1,525 @@ 1.4 +#include <cmath> 1.5 +#include <cstdio> 1.6 +#include <string> 1.7 +#include <vector> 1.8 + 1.9 +/* Note: This module assumes everyone uses RGB15 as display depth */ 1.10 + 1.11 +static std::string VIDEO_CMD = 1.12 + "mencoder - -o test0.avi" 1.13 + " -noskip -mc 0" 1.14 + " -ovc lavc" 1.15 + " -oac mp3lame" 1.16 + " -lameopts preset=256:aq=2:mode=3" 1.17 + " -lavcopts vcodec=ffv1:context=0:format=BGR32:coder=0:vstrict=-1" 1.18 + " >& mencoder.log"; 1.19 + 1.20 +static void FlushWrite(FILE* fp, const unsigned char*buf, unsigned length); 1.21 + 1.22 +#define BGR24 (0x42475218) // BGR24 fourcc 1.23 +#define BGR16 (0x42475210) // BGR16 fourcc 1.24 +#define BGR15 (0x4247520F) // BGR15 fourcc 1.25 + 1.26 +static FILE* (*openFunc) (const char*, const char*) = NULL; 1.27 +static int (*closeFunc) (FILE*) = NULL; 1.28 + 1.29 +#if (defined(WIN32) || defined(win32)) // capital is standard, but check for either 1.30 + #include <cstdlib> 1.31 + #define popen _popen; 1.32 + #define pclose _pclose; 1.33 +#endif 1.34 + 1.35 +#define u32(n) (n)&255,((n)>>8)&255,((n)>>16)&255,((n)>>24)&255 1.36 +#define u16(n) (n)&255,((n)>>8)&255 1.37 +#define s4(s) s[0],s[1],s[2],s[3] 1.38 + 1.39 +static const unsigned FPS_SCALE = (0x1000000); 1.40 + 1.41 +// general-purpose A/V sync debugging, ignored unless explicitly enabled with NESVideoEnableDebugging 1.42 +static void (*debugVideoMessageFunc)(const char *msg) = NULL; 1.43 +static void (*debugAudioMessageFunc)(const char *msg) = NULL; 1.44 +// 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) 1.45 +static unsigned audioFramesWritten=0, videoFramesWritten=1; 1.46 +static double audioSecondsWritten=0, videoSecondsWritten=0; 1.47 + 1.48 + 1.49 +static class AVI 1.50 +{ 1.51 + FILE* avifp; 1.52 + 1.53 + bool KnowVideo; 1.54 + unsigned width; 1.55 + unsigned height; 1.56 + unsigned fps_scaled; 1.57 + std::vector<unsigned char> VideoBuffer; 1.58 + 1.59 + bool KnowAudio; 1.60 + unsigned rate; 1.61 + unsigned chans; 1.62 + unsigned bits; 1.63 + std::vector<unsigned char> AudioBuffer; 1.64 + 1.65 +public: 1.66 + AVI() : 1.67 + avifp(NULL), 1.68 + KnowVideo(false), 1.69 + KnowAudio(false) 1.70 + { 1.71 + } 1.72 + ~AVI() 1.73 + { 1.74 + if(avifp) closeFunc(avifp); 1.75 + } 1.76 + 1.77 + void Audio(unsigned r,unsigned b,unsigned c, 1.78 + const unsigned char*d, unsigned nsamples) 1.79 + { 1.80 + if(!KnowAudio) 1.81 + { 1.82 + rate = r; 1.83 + chans = c; 1.84 + bits = b; 1.85 + KnowAudio = true; 1.86 + CheckFlushing(); 1.87 + } 1.88 + unsigned bytes = nsamples*chans*(bits/8); 1.89 + 1.90 + if(debugAudioMessageFunc) 1.91 + { 1.92 + audioFramesWritten++; 1.93 + audioSecondsWritten += (double)nsamples / (double)rate; // += bytes times seconds per byte 1.94 + char temp [64]; 1.95 + sprintf(temp, "A: %.2lf s, %d f", audioSecondsWritten, audioFramesWritten); 1.96 + debugAudioMessageFunc(temp); 1.97 + } 1.98 + 1.99 + if(KnowVideo) 1.100 + SendAudioFrame(d, bytes); 1.101 + else 1.102 + { 1.103 + AudioBuffer.insert(AudioBuffer.end(), d, d+bytes); 1.104 + fprintf(stderr, "Buffering %u bytes of audio\n", bytes); 1.105 + } 1.106 + } 1.107 + void Video(unsigned w,unsigned h,unsigned f, const unsigned char*d) 1.108 + { 1.109 + if(!KnowVideo) 1.110 + { 1.111 + width=w; 1.112 + height=h; 1.113 + fps_scaled=f; 1.114 + KnowVideo = true; 1.115 + CheckFlushing(); 1.116 + } 1.117 + 1.118 + unsigned bytes = width*height*2; 1.119 + 1.120 + //std::vector<unsigned char> tmp(bytes, 'k'); 1.121 + //d = &tmp[0]; 1.122 + 1.123 + if(debugVideoMessageFunc) 1.124 + { 1.125 + videoFramesWritten++; 1.126 + videoSecondsWritten += (double)FPS_SCALE / (double)fps_scaled; // += seconds per frame 1.127 + char temp [64]; 1.128 + sprintf(temp, "V: %.2lf s, %d f", videoSecondsWritten, videoFramesWritten); 1.129 + debugVideoMessageFunc(temp); 1.130 + } 1.131 + 1.132 + if(KnowAudio) 1.133 + SendVideoFrame(d, bytes); 1.134 + else 1.135 + { 1.136 + VideoBuffer.insert(VideoBuffer.end(), d, d+bytes); 1.137 + fprintf(stderr, "Buffering %u bytes of video\n", bytes); 1.138 + } 1.139 + } 1.140 + 1.141 +private: 1.142 + void CheckFlushing() 1.143 + { 1.144 + //AudioBuffer.clear(); 1.145 + //VideoBuffer.clear(); 1.146 + 1.147 + if(KnowAudio && KnowVideo) 1.148 + { 1.149 + unsigned last_offs; 1.150 + 1.151 + // Flush Audio 1.152 + 1.153 + last_offs = 0; 1.154 + while(last_offs < AudioBuffer.size()) 1.155 + { 1.156 + unsigned bytes = rate / (fps_scaled / FPS_SCALE); 1.157 + bytes *= chans*(bits/8); 1.158 + 1.159 + unsigned remain = AudioBuffer.size() - last_offs; 1.160 + if(bytes > remain) bytes = remain; 1.161 + if(!bytes) break; 1.162 + 1.163 + unsigned begin = last_offs; 1.164 + last_offs += bytes; 1.165 + SendAudioFrame(&AudioBuffer[begin], bytes); 1.166 + } 1.167 + AudioBuffer.erase(AudioBuffer.begin(), AudioBuffer.begin()+last_offs); 1.168 + 1.169 + // Flush Video 1.170 + 1.171 + last_offs = 0; 1.172 + while(last_offs < VideoBuffer.size()) 1.173 + { 1.174 + unsigned bytes = width*height*2; 1.175 + unsigned remain = VideoBuffer.size() - last_offs; 1.176 + if(bytes > remain) bytes = remain; 1.177 + if(!bytes)break; 1.178 + 1.179 + unsigned begin = last_offs; 1.180 + last_offs += bytes; 1.181 + SendVideoFrame(&VideoBuffer[begin], bytes); 1.182 + } 1.183 + VideoBuffer.erase(VideoBuffer.begin(), VideoBuffer.begin()+last_offs); 1.184 + } 1.185 + } 1.186 + 1.187 + void SendVideoFrame(const unsigned char* vidbuf, unsigned framesize) 1.188 + { 1.189 + CheckBegin(); 1.190 + 1.191 + //fprintf(stderr, "Writing 00dc of %u bytes\n", framesize); 1.192 + 1.193 + const unsigned char header[] = { s4("00dc"), u32(framesize) }; 1.194 + FlushWrite(avifp, header, sizeof(header)); 1.195 + FlushWrite(avifp, vidbuf, framesize); 1.196 + } 1.197 + 1.198 + void SendAudioFrame(const unsigned char* audbuf, unsigned framesize) 1.199 + { 1.200 + CheckBegin(); 1.201 + 1.202 + //fprintf(stderr, "Writing 01wb of %u bytes\n", framesize); 1.203 + 1.204 + const unsigned char header[] = { s4("01wb"), u32(framesize) }; 1.205 + FlushWrite(avifp, header, sizeof(header)); 1.206 + FlushWrite(avifp, audbuf, framesize); 1.207 + } 1.208 + 1.209 + void CheckBegin() 1.210 + { 1.211 + if(avifp) return; 1.212 + 1.213 + if(!openFunc) openFunc = popen; // default 1.214 + if(!closeFunc) closeFunc = pclose; // default 1.215 + 1.216 + avifp = openFunc(VIDEO_CMD.c_str(), "wb"); 1.217 + if(!avifp) return; 1.218 + 1.219 + const unsigned fourcc = BGR16; 1.220 + const unsigned framesize = width*height*2; 1.221 + 1.222 + const unsigned aud_rate = rate; 1.223 + const unsigned aud_chans = chans; 1.224 + const unsigned aud_bits = bits; 1.225 + 1.226 + const unsigned nframes = 0; //unknown 1.227 + const unsigned scale = FPS_SCALE; 1.228 + const unsigned scaled_fps = fps_scaled; 1.229 + 1.230 + const unsigned SIZE_strh_vids = 4 + 4*2 + 2*2 + 8*4 + 2*4; 1.231 + const unsigned SIZE_strf_vids = 4*3 + 2*2 + 4*6; 1.232 + const unsigned SIZE_strl_vids = 4+ 4+(4+SIZE_strh_vids) + 4+(4+SIZE_strf_vids); 1.233 + 1.234 + const unsigned SIZE_strh_auds = 4 + 4*3 + 2*2 + 4*8 + 2*4; 1.235 + const unsigned SIZE_strf_auds = 2*2 + 4*2 + 2*3; 1.236 + const unsigned SIZE_strl_auds = 4+ 4+(4+SIZE_strh_auds) + 4+(4+SIZE_strf_auds); 1.237 + 1.238 + const unsigned SIZE_avih = 4*12; 1.239 + const unsigned SIZE_hdrl = 4+4+ (4+SIZE_avih) + 4 + (4+SIZE_strl_vids) + 4 + (4+SIZE_strl_auds); 1.240 + const unsigned SIZE_movi = 4 + nframes*(4+4+framesize); 1.241 + const unsigned SIZE_avi = 4+4+ (4+SIZE_hdrl) + 4 + (4+SIZE_movi); 1.242 + 1.243 + const unsigned char AVIheader[] = 1.244 + { 1.245 + s4("RIFF"), 1.246 + u32(SIZE_avi), 1.247 + s4("AVI "), 1.248 + 1.249 + // HEADER 1.250 + 1.251 + s4("LIST"), 1.252 + u32(SIZE_hdrl), 1.253 + s4("hdrl"), 1.254 + 1.255 + s4("avih"), 1.256 + u32(SIZE_avih), 1.257 + u32(0), 1.258 + u32(0), 1.259 + u32(0), 1.260 + u32(0), 1.261 + u32(nframes), 1.262 + u32(0), 1.263 + u32(2), // two streams 1.264 + u32(0), 1.265 + u32(0), 1.266 + u32(0), 1.267 + u32(0), 1.268 + u32(0), 1.269 + 1.270 + // VIDEO HEADER 1.271 + 1.272 + s4("LIST"), 1.273 + u32(SIZE_strl_vids), 1.274 + s4("strl"), 1.275 + 1.276 + s4("strh"), 1.277 + u32(SIZE_strh_vids), 1.278 + s4("vids"), 1.279 + u32(0), 1.280 + u32(0), 1.281 + u16(0), 1.282 + u16(0), 1.283 + u32(0), 1.284 + u32(scale), 1.285 + u32(scaled_fps), 1.286 + u32(0), 1.287 + u32(0), 1.288 + u32(0), 1.289 + u32(0), 1.290 + u32(0), 1.291 + u16(0), 1.292 + u16(0), 1.293 + u16(0), 1.294 + u16(0), 1.295 + 1.296 + s4("strf"), 1.297 + u32(SIZE_strf_vids), 1.298 + u32(0), 1.299 + u32(width), 1.300 + u32(height), 1.301 + u16(0), 1.302 + u16(0), 1.303 + u32(fourcc), 1.304 + u32(0), 1.305 + u32(0), 1.306 + u32(0), 1.307 + u32(0), 1.308 + u32(0), 1.309 + 1.310 + // AUDIO HEADER 1.311 + 1.312 + s4("LIST"), 1.313 + u32(SIZE_strl_auds), 1.314 + s4("strl"), 1.315 + 1.316 + s4("strh"), 1.317 + u32(SIZE_strh_auds), 1.318 + s4("auds"), 1.319 + u32(0), //fourcc 1.320 + u32(0), //handler 1.321 + u32(0), //flags 1.322 + u16(0), //prio 1.323 + u16(0), //lang 1.324 + u32(0), //init frames 1.325 + u32(1), //scale 1.326 + u32(aud_rate), 1.327 + u32(0), //start 1.328 + u32(0), //rate*length 1.329 + u32(1048576), //suggested bufsize 1.330 + u32(0), //quality 1.331 + u32(aud_chans * (aud_bits / 8)), //sample size 1.332 + u16(0), //frame size 1.333 + u16(0), 1.334 + u16(0), 1.335 + u16(0), 1.336 + 1.337 + s4("strf"), 1.338 + u32(SIZE_strf_auds), 1.339 + u16(1), // pcm format 1.340 + u16(aud_chans), 1.341 + u32(aud_rate), 1.342 + u32(aud_rate * aud_chans * (aud_bits/8)), // samples per second 1.343 + u16(aud_chans * (aud_bits/8)), //block align 1.344 + u16(aud_bits), //bits 1.345 + u16(0), //cbSize 1.346 + 1.347 + // MOVIE 1.348 + 1.349 + s4("LIST"), 1.350 + u32(SIZE_movi), 1.351 + s4("movi") 1.352 + }; 1.353 + 1.354 + FlushWrite(avifp, AVIheader, sizeof(AVIheader)); 1.355 + } 1.356 +} AVI; 1.357 + 1.358 +extern "C" 1.359 +{ 1.360 + int LoggingEnabled = 0; /* 0=no, 1=yes, 2=recording! */ 1.361 + 1.362 + const char* NESVideoGetVideoCmd() 1.363 + { 1.364 + return VIDEO_CMD.c_str(); 1.365 + } 1.366 + void NESVideoSetVideoCmd(const char *cmd) 1.367 + { 1.368 + VIDEO_CMD = cmd; 1.369 + } 1.370 + void NESVideoEnableDebugging( void videoMessageFunc(const char *msg), void audioMessageFunc(const char *msg) ) 1.371 + { 1.372 + debugVideoMessageFunc = videoMessageFunc; 1.373 + debugAudioMessageFunc = audioMessageFunc; 1.374 + } 1.375 + void NESVideoSetFileFuncs( FILE* open(const char *,const char *), int close(FILE*) ) 1.376 + { 1.377 + openFunc = open; 1.378 + closeFunc = close; 1.379 + } 1.380 + 1.381 + void NESVideoLoggingVideo 1.382 + (const void*data, unsigned width,unsigned height, 1.383 + unsigned fps_scaled 1.384 + ) 1.385 + { 1.386 + if(LoggingEnabled < 2) return; 1.387 + 1.388 + unsigned LogoFrames = fps_scaled >> 24; 1.389 + 1.390 + static bool First = true; 1.391 + if(First) 1.392 + { 1.393 + First=false; 1.394 + /* Bisqwit's logo addition routine. */ 1.395 + /* If you don't have his files, this function does nothing 1.396 + * and it does not matter at all. 1.397 + */ 1.398 + 1.399 + const char *background = 1.400 + width==320 ? "logo320_240" 1.401 + : width==160 ? "logo160_144" 1.402 + : width==240 ? "logo240_160" 1.403 + : height>224 ? "logo256_240" 1.404 + : "logo256_224"; 1.405 + 1.406 + /* Note: This should be 1 second long. */ 1.407 + for(unsigned frame = 0; frame < LogoFrames; ++frame) 1.408 + { 1.409 + char Buf[4096]; 1.410 + sprintf(Buf, "/shares/home/bisqwit/povray/nesvlogo/%s_f%u.tga", 1.411 + background, frame); 1.412 + 1.413 + FILE*fp = fopen(Buf, "rb"); 1.414 + if(!fp) // write blackness when missing frames to keep the intro 1 second long: 1.415 + { 1.416 + unsigned bytes = width*height*2; 1.417 + unsigned char* buf = (unsigned char*)malloc(bytes); 1.418 + if(buf) 1.419 + { 1.420 + memset(buf,0,bytes); 1.421 + AVI.Video(width,height,fps_scaled, buf); 1.422 + if(debugVideoMessageFunc) videoFramesWritten--; 1.423 + free(buf); 1.424 + } 1.425 + } 1.426 + else // write 1 frame of the logo: 1.427 + { 1.428 + int idlen = fgetc(fp); 1.429 + /* Silently ignore all other header data. 1.430 + * These files are assumed to be uncompressed BGR24 tga files with Y swapped. 1.431 + * Even their geometry is assumed to match perfectly. 1.432 + */ 1.433 + fseek(fp, 1+1+2+2+1+ /*org*/2+2+ /*geo*/2+2+ 1+1+idlen, SEEK_CUR); 1.434 + 1.435 + bool yflip=true; 1.436 + std::vector<unsigned char> data(width*height*3); 1.437 + for(unsigned y=height; y-->0; ) 1.438 + fread(&data[y*width*3], 1, width*3, fp); 1.439 + fclose(fp); 1.440 + 1.441 + std::vector<unsigned short> result(width*height); 1.442 + for(unsigned pos=0, max=result.size(); pos<max; ++pos) 1.443 + { 1.444 + unsigned usepos = pos; 1.445 + if(yflip) 1.446 + { 1.447 + unsigned y = pos/width; 1.448 + usepos = (usepos%width) + (height-y-1)*width; 1.449 + } 1.450 + 1.451 + unsigned B = data[usepos*3+0]; 1.452 + unsigned G = data[usepos*3+1]; 1.453 + unsigned R = data[usepos*3+2]; 1.454 + result[pos] = ((B*31/255)<<0) 1.455 + | ((G*63/255)<<5) 1.456 + | ((R*31/255)<<11); 1.457 + } 1.458 + AVI.Video(width,height,fps_scaled, (const unsigned char*)&result[0]); 1.459 + if(debugVideoMessageFunc) videoFramesWritten--; 1.460 + } 1.461 + } 1.462 + } 1.463 + AVI.Video(width,height,fps_scaled, (const unsigned char*) data); 1.464 + } 1.465 + 1.466 + void NESVideoLoggingAudio 1.467 + (const void*data, 1.468 + unsigned rate, unsigned bits, unsigned chans, 1.469 + unsigned nsamples) 1.470 + { 1.471 + if(LoggingEnabled < 2) return; 1.472 + 1.473 + static bool First = true; 1.474 + if(First) 1.475 + { 1.476 + First=false; 1.477 + 1.478 + const unsigned n = rate; // assumes 1 second of logo to write silence for 1.479 + if(n > 0) 1.480 + { 1.481 + unsigned bytes = n*chans*(bits/8); 1.482 + unsigned char* buf = (unsigned char*)malloc(bytes); 1.483 + if(buf) 1.484 + { 1.485 + memset(buf,0,bytes); 1.486 + AVI.Audio(rate,bits,chans, buf, n); 1.487 + free(buf); 1.488 + } 1.489 + } 1.490 + } 1.491 + 1.492 + AVI.Audio(rate,bits,chans, (const unsigned char*) data, nsamples); 1.493 + } 1.494 +} /* extern "C" */ 1.495 + 1.496 + 1.497 + 1.498 +static void FlushWrite(FILE* fp, const unsigned char*buf, unsigned length) 1.499 +{ 1.500 +/// unsigned failures = 0; 1.501 +/// 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 1.502 + while(length > 0 /*&& failures < FAILURE_THRESH*/) 1.503 + { 1.504 + unsigned written = fwrite(buf, 1, length, fp); 1.505 +/// if(written == 0) 1.506 +/// failures++; 1.507 +/// else 1.508 +/// { 1.509 + length -= written; 1.510 + buf += written; 1.511 +/// failures = 0; 1.512 +/// } 1.513 + } 1.514 +/// if(failures >= FAILURE_THRESH) 1.515 +/// { 1.516 +/// fprintf(stderr, "FlushWrite() failed to write %d bytes %d times - giving up.", length, failures); 1.517 +/// LoggingEnabled = 0; 1.518 +/// } 1.519 +} 1.520 + 1.521 +// for the UB tech 1.522 +#undef BGR24 1.523 +#undef BGR16 1.524 +#undef BGR15 1.525 + 1.526 +#undef u32 1.527 +#undef u16 1.528 +#undef s4