10
#include <unistd.h> // mknod, unlink, write
12
#include <sys/stat.h> // S_IFIFO
13
#include <fcntl.h> // fcntl
14
#include <sys/poll.h> // poll
15
#include <stdlib.h> // setenv
16
#include <string.h> // strrchr
17
#include <sys/file.h> // flock
25
#ifdef HAVE_X264 // don't worry, you really don't need it
31
/* Note: This module assumes everyone uses BGR16 as display depth */
33
//#define LOGO_LENGTH_HEADER (1.2)
34
//#define LOGO_LENGTH_OVERLAP (10.0-LOGO_LENGTH_HEADER)
35
//#define LOGO_LENGTH_HEADER (1.1)
36
//#define LOGO_LENGTH_OVERLAP (3.95-LOGO_LENGTH_HEADER)
37
//#define LOGO_LENGTH_OVERLAP (3-LOGO_LENGTH_HEADER)
38
//#define LOGO_LENGTH_HEADER (1.5)
39
#define LOGO_LENGTH_OVERLAP (0)
40
#define LOGO_LENGTH_HEADER (0)
42
static std::string VIDEO_CMD = "";
44
-rawvideo on:fps=60:format=0x42475220:w=256:h=224:size=$[1024*224]
45
-audiofile "+AUDIO_FN+"
47
static std::string AUDIO_FN = "s.log";
49
static bool Terminate=false;
50
static unsigned videonumber = 0;
54
static pthread_mutex_t APIlock = PTHREAD_MUTEX_INITIALIZER;
57
pthread_mutex_lock(&APIlock);
58
//fprintf(stderr, "audio start\n"); fflush(stderr);
61
//fprintf(stderr, "audio end\n"); fflush(stderr);
62
pthread_mutex_unlock(&APIlock); }
66
static unsigned NonblockWrite(FILE* fp, const unsigned char*buf, unsigned length)
69
int result = write(fileno(fp), buf, length);
70
if(result == -1 && errno==EAGAIN)
74
if(result == -1 && errno==EINTR) goto Retry;
83
static int WaitUntilOneIsWritable(FILE*f1, FILE*f2)
85
struct pollfd po[2] = { {fileno(f1),POLLOUT,0}, {fileno(f2),POLLOUT,0} };
87
return ((po[0].revents & POLLOUT) ? 1 : 0)
88
| ((po[1].revents & POLLOUT) ? 2 : 0);
91
#define BGR32 0x42475220 // BGR32 fourcc
92
#define BGR24 0x42475218 // BGR24 fourcc
93
#define BGR16 0x42475210 // BGR16 fourcc
94
#define BGR15 0x4247520F // BGR15 fourcc
95
#define I420 0x30323449 // I420 fourcc
96
#define YUY2 0x32595559 // YUY2 fourcc
98
static unsigned USE_FOURCC = BGR16;
99
static unsigned INPUT_BPP = 16;
101
#define u32(n) (n)&255,((n)>>8)&255,((n)>>16)&255,((n)>>24)&255
102
#define u16(n) (n)&255,((n)>>8)&255
103
#define s4(s) s[0],s[1],s[2],s[3]
105
static const unsigned FPS_SCALE = 0x1000000;
107
static struct Construct
112
getcwd(Buf,sizeof(Buf));
113
Buf[sizeof(Buf)-1]=0;
114
AUDIO_FN = Buf + std::string("/") + AUDIO_FN;
125
(unsigned r,unsigned b,unsigned c,
126
const unsigned char*d, unsigned nsamples) = 0;
129
(unsigned w,unsigned h,unsigned f, const unsigned char*d) = 0;
131
virtual void SaveState(const std::string&) { }
132
virtual void LoadState(const std::string&) { }
135
class NormalAVI: public AVI
143
unsigned vid_fps_scaled;
144
std::list<std::vector<unsigned char> > VideoBuffer;
151
std::list<std::vector<unsigned char> > AudioBuffer;
158
KnowVideo(false), VidBufSize(0),
159
KnowAudio(false), AudBufSize(0)
164
while(VidBufSize && AudBufSize)
168
if(audfp) fclose(audfp);
169
if(vidfp) pclose(vidfp);
170
unlink(AUDIO_FN.c_str());
174
(unsigned r,unsigned b,unsigned c,
175
const unsigned char*d, unsigned nsamples)
177
if(Terminate) return;
187
unsigned bytes = nsamples * aud_chans * (aud_bits / 8);
190
if(KnowVideo && AudioBuffer.empty())
192
//fprintf(stderr, "Writing %u of %s from %p to %p\t", bytes, "aud", (void*)d, (void*)audfp);
193
wrote = NonblockWrite(audfp, d, bytes);
194
//fprintf(stderr, "Wrote %u\n", wrote);
198
unsigned remain = bytes-wrote;
199
//fprintf(stderr, "Buffering %u of %s (%p..%p)\n", remain, "aud", d+wrote, d+bytes);
200
AudioBuffer.push_back(std::vector<unsigned char>(d+wrote, d+bytes));
201
AudBufSize += remain;
207
(unsigned w,unsigned h,unsigned f, const unsigned char*d)
209
if(Terminate) return;
219
unsigned bpp = INPUT_BPP; if(bpp == 15 || bpp == 17) bpp = 16;
220
unsigned bytes = vid_width * vid_height * bpp / 8;
222
//std::vector<unsigned char> tmp(bytes, 'k');
226
if(KnowAudio && VideoBuffer.empty())
229
//fprintf(stderr, "Writing %u of %s from %p to %p\t", bytes, "vid", (void*)d, (void*)vidfp);
230
wrote = NonblockWrite(vidfp, d, bytes);
231
//fprintf(stderr, "Wrote %u\n", wrote);
236
unsigned remain = bytes-wrote;
237
//fprintf(stderr, "Buffering %u of %s (%p..%p)\n", remain, "vid", d+wrote, d+bytes);
239
VideoBuffer.push_back(std::vector<unsigned char>(d+wrote, d+bytes));
240
VidBufSize += remain;
246
/* fp is passed as a reference because it may be NULL
247
* prior to calling, and this function changes it. */
248
template<typename BufType>
249
void FlushBufferSome(BufType& List, unsigned& Size, FILE*& fp, const char* what)
254
if(List.empty() || Terminate) return;
256
typename BufType::iterator i = List.begin();
257
std::vector<unsigned char>& buf = *i;
265
unsigned bytes = buf.size();
268
//fprintf(stderr, "Writing %u of %s from %p to %p\t", bytes, what, (void*)&buf[0], (void*)fp);
270
unsigned ate = NonblockWrite(fp, &buf[0], bytes);
272
//fprintf(stderr, "Wrote %u\n", ate);
274
buf.erase(buf.begin(), buf.begin()+ate);
286
//AudioBuffer.clear();
287
//VideoBuffer.clear();
289
if(KnowAudio && KnowVideo && !Terminate)
291
if(!AudioBuffer.empty() && !VideoBuffer.empty())
294
/* vidfp = &1, audfp = &2 */
295
int attempt = WaitUntilOneIsWritable(vidfp, audfp);
297
if(attempt <= 0) break; /* Some kind of error can cause this */
300
if(attempt&1) FlushBufferSome(VideoBuffer, VidBufSize, vidfp, "vid");
303
if(attempt&2) FlushBufferSome(AudioBuffer, AudBufSize, audfp, "aud");
304
} while (!AudioBuffer.empty() && !VideoBuffer.empty());
308
FlushBufferSome(VideoBuffer, VidBufSize, vidfp, "vid");
309
FlushBufferSome(AudioBuffer, AudBufSize, audfp, "aud");
312
fprintf(stderr, "Buffer Sizes: Audio %u(%u) video %u(%u)\n",
313
(unsigned)AudioBuffer.size(), AudBufSize,
314
(unsigned)VideoBuffer.size(), VidBufSize);
318
std::string GetMEncoderRawvideoParam() const
321
unsigned bpp = INPUT_BPP; if(bpp == 15 || bpp == 17) bpp = 16;
322
sprintf(Buf, "fps=%g:format=0x%04X:w=%u:h=%u:size=%u",
323
vid_fps_scaled / (double)FPS_SCALE,
327
vid_width*vid_height * bpp/8);
330
std::string GetMEncoderRawaudioParam() const
333
sprintf(Buf, "channels=%u:rate=%u:samplesize=%u:bitrate=%u",
337
aud_rate*aud_chans*(aud_bits/8) );
340
std::string GetMEncoderCommand() const
342
std::string mandatory = "-audiofile " + AUDIO_FN
343
+ " -audio-demuxer rawaudio"
344
+ " -demuxer rawvideo"
345
+ " -rawvideo " + GetMEncoderRawvideoParam()
346
+ " -rawaudio " + GetMEncoderRawaudioParam()
348
std::string cmd = VIDEO_CMD;
350
std::string::size_type p = cmd.find("NESV""SETTINGS");
352
cmd = cmd.replace(p, 4+8, mandatory);
354
fprintf(stderr, "Warning: NESVSETTINGS not found in videocmd\n");
356
char videonumstr[64];
357
sprintf(videonumstr, "%u", videonumber);
361
p = cmd.find("VIDEO""NUMBER");
362
if(p == cmd.npos) break;
363
cmd = cmd.replace(p, 5+6, videonumstr);
366
fprintf(stderr, "Launch: %s\n", cmd.c_str()); fflush(stderr);
375
unlink(AUDIO_FN.c_str());
376
mknod(AUDIO_FN.c_str(), S_IFIFO|0666, 0);
381
/* Note: popen does not accept b/t in mode param */
382
setenv("LD_PRELOAD", "", 1);
383
vidfp = popen(GetMEncoderCommand().c_str(), "w");
386
perror("Launch failed");
390
fcntl(fileno(vidfp), F_SETFL, O_WRONLY | O_NONBLOCK);
397
audfp = fopen(AUDIO_FN.c_str(), "wb");
401
perror(AUDIO_FN.c_str());
402
if(errno == ESTALE) goto Retry;
406
fcntl(fileno(audfp), F_SETFL, O_WRONLY | O_NONBLOCK);
412
class RerecordingAVI: public AVI
414
std::map<std::string, std::pair<off_t, off_t> > FrameStates;
415
size_t aud_framesize;
416
size_t vid_framesize;
438
LockF(FILE* f) : fp(f) { flock(fileno(fp), LOCK_EX); }
439
~LockF() { flock(fileno(fp), LOCK_UN); }
442
LockF& operator=(const LockF&);
447
RerecordingAVI(long FrameNumber)
457
virtual ~RerecordingAVI()
461
off_t vidpos = ftello(vidfp);
462
off_t audpos = ftello(audfp);
465
(long long)vidpos, (long long)audpos);
467
if(vidfp) fclose(vidfp);
468
if(audfp) fclose(audfp);
469
if(eventfp) fclose(eventfp);
470
if(statefp) fclose(statefp);
472
if(x264) x264_encoder_close(x264);
477
(unsigned aud_rate,unsigned aud_bits,unsigned aud_chans,
478
const unsigned char*data, unsigned nsamples)
480
size_t bytes = nsamples * aud_chans * (aud_bits / 8);
481
size_t framesize = aud_rate * aud_chans * (aud_bits / 8);
483
if(framesize != aud_framesize)
485
aud_framesize = framesize;
487
fprintf(eventfp, "AudFrameSize %lu\n", (unsigned long)aud_framesize);
492
fwrite(data, 1, bytes, audfp);
496
(unsigned vid_width,unsigned vid_height,
497
unsigned vid_fps_scaled, const unsigned char*data)
499
unsigned bpp = INPUT_BPP; if(bpp == 15 || bpp == 17) bpp = 16;
500
size_t bytes = vid_width * vid_height * bpp / 8;
501
size_t framesize = bytes;
503
if(framesize != vid_framesize)
505
vid_framesize = framesize;
507
fprintf(eventfp, "VidFrameSize %lu\n", (unsigned long)vid_framesize);
514
if(bpp == 12) /* For I420, we use a local X264 encoder */
518
x264_param_default(¶m);
519
x264_param_parse(¶m, "psnr", "no");
520
x264_param_parse(¶m, "ssim", "no");
521
param.i_width = vid_width;
522
param.i_height = vid_height;
523
param.i_csp = X264_CSP_I420;
524
//param.i_scenecut_threshold = -1;
525
//param.b_bframe_adaptive = 0;
526
//param.rc.i_rc_method = X264_RC_CRF;
527
//param.rc.i_qp_constant = 0;
528
x264_param_parse(¶m, "me", "dia");
529
x264_param_parse(¶m, "crf", "6");
530
x264_param_parse(¶m, "frameref", "8");
531
param.i_frame_reference = 1;
532
param.analyse.i_subpel_refine = 1;
533
param.analyse.i_me_method = X264_ME_DIA;
535
param.analyse.inter = 0;
536
param.analyse.b_transform_8x8 = 0;
537
param.analyse.b_weighted_bipred = 0;
538
param.analyse.i_trellis = 0;
540
//param.b_repeat_headers = 1; // guess this might be needed
542
param.i_fps_num = vid_fps_scaled;
543
param.i_fps_den = 1 << 24;
545
x264 = x264_encoder_open(¶m);
548
fprintf(stderr, "x264_encoder_open failed.\n");
553
const size_t npixels = vid_width * vid_height;
555
pic.i_type = forcekey ? X264_TYPE_IDR : X264_TYPE_AUTO;
558
pic.img.i_csp = X264_CSP_I420;
560
pic.img.i_stride[0] = vid_width;
561
pic.img.i_stride[1] = vid_width / 2;
562
pic.img.i_stride[2] = vid_width / 2;
563
pic.img.plane[0] = const_cast<uint8_t*>(data) + npixels*0/4;
564
pic.img.plane[1] = const_cast<uint8_t*>(data) + npixels*4/4;
565
pic.img.plane[2] = const_cast<uint8_t*>(data) + npixels*5/4;
567
x264_nal_t* nal; int i_nal;
568
x264_picture_t pic_out;
569
if(x264_encoder_encode(x264, &nal, &i_nal, &pic, &pic_out) < 0)
571
fprintf(stderr, "x264_encoder_encode failed\n");
575
for(int i=0; i<i_nal; ++i) i_size += nal[i].i_payload * 2 + 4;
576
std::vector<unsigned char> muxbuf(i_size);
578
for(int i=0; i<i_nal; ++i)
580
int room_required = nal[i].i_payload * 3/2 + 4;
581
if(muxbuf.size() < i_size + room_required)
582
muxbuf.resize(i_size + room_required);
584
int i_data = muxbuf.size() - i_size;
585
i_size += x264_nal_encode(&muxbuf[i_size], &i_data, 1, &nal[i]);
588
fwrite(&muxbuf[0], 1, i_size, vidfp);
594
fwrite(data, 1, bytes, vidfp);
600
off_t vidpos = ftello(vidfp);
601
off_t audpos = ftello(audfp);
604
(long long)vidpos, (long long)audpos);
610
virtual void SaveState(const std::string& slot)
614
off_t vidpos = ftello(vidfp);
615
off_t audpos = ftello(audfp);
618
"%llX %llX Save %s\n",
619
(long long)vidpos, (long long)audpos, slot.c_str());
622
FrameStates[slot] = std::make_pair(vidpos, audpos);
628
virtual void LoadState(const std::string& slot)
632
const std::pair<off_t, off_t>& old = FrameStates[slot];
633
off_t vidpos = ftello(vidfp);
634
off_t audpos = ftello(audfp);
636
"%llX %llX Load %llX %llX %s\n",
637
(long long)vidpos, (long long)audpos,
638
(long long)old.first,
639
(long long)old.second,
649
std::string vidfn = VIDEO_CMD + ".vid";
650
std::string audfn = VIDEO_CMD + ".aud";
651
std::string eventfn = VIDEO_CMD + ".log";
652
std::string statefn = VIDEO_CMD + ".state";
653
vidfp = fopen(vidfn.c_str(), "ab+");
654
audfp = fopen(audfn.c_str(), "ab+");
655
eventfp = fopen(eventfn.c_str(), "ab+");
656
statefp = fopen2(statefn.c_str(), "rb+", "wb+");
661
off_t vidpos = ftello(vidfp);
662
off_t audpos = ftello(audfp);
665
(long long)vidpos, (long long)audpos);
668
static FILE* fopen2(const char* fn, const char* mode1, const char* mode2)
670
FILE* result = fopen(fn, mode1);
671
if(!result) result = fopen(fn, mode2);
681
while(fgets(Buf, sizeof(Buf), statefp))
683
if(*Buf == '-') break;
685
long long vidpos, audpos;
686
strtok(Buf, "\r"); strtok(Buf, "\n");
687
sscanf(Buf, "%llX %llX %4095s", &vidpos, &audpos, slotname);
688
FrameStates[slotname] = std::pair<off_t,off_t> (vidpos, audpos);
696
for(std::map<std::string, std::pair<off_t, off_t> >::const_iterator
697
i = FrameStates.begin(); i != FrameStates.end(); ++i)
699
fprintf(statefp, "%llX %llX %s\n",
700
(long long) i->second.first,
701
(long long) i->second.second,
704
fprintf(statefp, "-\n");
718
bool SentVideo = false;
719
bool SentAudio = false;
724
#include "quantize.h"
725
#include "rgbtorgb.h"
727
static bool RerecordingMode = false;
728
static long CurrentFrameNumber = 0;
732
int LoggingEnabled = 0; /* 0=no, 1=yes, 2=recording! */
734
const char* NESVideoGetVideoCmd()
736
return VIDEO_CMD.c_str();
738
void NESVideoSetVideoCmd(const char *cmd)
747
void NESVideoSetRerecordingMode(long FrameNumber)
749
//const int LogoFramesOverlap = (int)( (LOGO_LENGTH_OVERLAP * fps_scaled) / (1 << 24) );
750
RerecordingMode = true;
751
CurrentFrameNumber = FrameNumber;
753
LogoInfo::SentVideo = FrameNumber > 0;
754
LogoInfo::SentAudio = FrameNumber > 0;
755
LogoInfo::OverlapSent = FrameNumber;
759
static class AVI& GetAVIptr()
765
fprintf(stderr, "Beginning rerecording project at frame %ld\n", CurrentFrameNumber);
766
AVI = new RerecordingAVI(CurrentFrameNumber);
770
fprintf(stderr, "Starting new AVI (num %u)\n", videonumber);
777
void NESVideoRerecordingSave(const char* slot)
779
GetAVIptr().SaveState(slot);
782
void NESVideoRerecordingLoad(const char* slot)
784
GetAVIptr().LoadState(slot);
787
void NESVideoNextAVI()
795
fprintf(stderr, "Closing AVI (next will be started)\n");
803
static void Overlay32With32(unsigned char* target, const unsigned char* source, int alpha)
805
target[0] += ((int)(source[0] - target[0])) * alpha / 255;
806
target[1] += ((int)(source[1] - target[1])) * alpha / 255;
807
target[2] += ((int)(source[2] - target[2])) * alpha / 255;
810
static void OverlayLogoFrom(const char* fn, std::vector<unsigned char>& data)
812
FILE*fp = fopen(fn, "rb");
814
if(!fp) return; /* Silently ignore missing frames */
816
gdImagePtr im = gdImageCreateFromPng(fp);
817
if(!gdImageTrueColor(im))
819
fprintf(stderr, "'%s': Only true color images are supported\n", fn);
824
unsigned new_width = gdImageSX(im);
825
unsigned new_height= gdImageSY(im);
827
if(new_width != LogoInfo::width
828
|| new_height != LogoInfo::height)
830
if(new_height < LogoInfo::height || new_height > LogoInfo::height+20)
831
fprintf(stderr, "'%s': ERROR, expected %dx%d, got %dx%d\n", fn,
832
LogoInfo::width, LogoInfo::height,
833
new_width, new_height);
836
for(unsigned y=0; y<LogoInfo::height; ++y)
838
unsigned char pixbuf[4] = {0,0,0,0};
839
for(unsigned x = 0; x < LogoInfo::width; ++x)
841
int color = gdImageTrueColorPixel(im, x,y);
842
int alpha = 255-gdTrueColorGetAlpha(color)*256/128;
843
pixbuf[2] = gdTrueColorGetRed(color);
844
pixbuf[1] = gdTrueColorGetGreen(color);
845
pixbuf[0] = gdTrueColorGetBlue(color);
846
Overlay32With32(&data[(y*LogoInfo::width+x)*3], pixbuf, alpha);
855
static const std::string GetLogoFileName(unsigned frameno)
857
std::string avdir = "/home/you/yourlogo/";
860
sprintf(AvName, "logo_%d_%d_f%03u.png",
865
std::string want = avdir + AvName;
866
int ac = access(want.c_str(), R_OK);
869
/* No correct avatar file? Check if there's an approximate match. */
870
static std::map<int, std::vector<std::string> > files;
871
if(files.empty()) /* Cache the list of logo files. */
873
static const char GlobPat[] = "logo_*_*_f*.png";
875
globdata.gl_offs = 0;
876
fprintf(stderr, "Loading list of usable logo animation files in %s...\n", avdir.c_str());
877
int globres = glob( (avdir + GlobPat).c_str(), GLOB_NOSORT, NULL, &globdata);
880
for(size_t n=0; n<globdata.gl_pathc; ++n)
882
const char* fn = globdata.gl_pathv[n];
883
const char* slash = strrchr(fn, '/');
884
if(slash) fn = slash+1;
886
int gotw=0, goth=0, gotf=0;
887
sscanf(fn, "logo_%d_%d_f%d", &gotw,&goth,&gotf);
888
files[gotf].push_back(fn);
894
std::map<int, std::vector<std::string> >::const_iterator
895
i = files.find(frameno);
901
const std::vector<std::string>& fnames = i->second;
902
for(size_t b=fnames.size(), a=0; a<b; ++a)
904
unsigned gotw=0, goth=0;
905
sscanf(fnames[a].c_str(), "logo_%u_%u", &gotw,&goth);
906
if(gotw < LogoInfo::width || goth < LogoInfo::height) continue;
908
int dist = std::max(gotw - LogoInfo::width,
909
goth - LogoInfo::height);
911
if(bestdist == -1 || dist < bestdist)
912
{ bestdist = dist; best = fnames[a]; }
915
if(bestdist >= 0) want = avdir + best;
921
static const std::vector<unsigned char> NVConvert24To16Frame
922
(const std::vector<unsigned char>& logodata)
924
std::vector<unsigned char> result(LogoInfo::width * LogoInfo::height * 2);
925
Convert24To16Frame(&logodata[0], &result[0], LogoInfo::width * LogoInfo::height, LogoInfo::width);
928
static const std::vector<unsigned char> NVConvert24To15Frame
929
(const std::vector<unsigned char>& logodata)
931
std::vector<unsigned char> result(LogoInfo::width * LogoInfo::height * 2);
932
Convert24To15Frame(&logodata[0], &result[0], LogoInfo::width * LogoInfo::height, LogoInfo::width);
936
static const std::vector<unsigned char> NVConvert24To_I420Frame
937
(const std::vector<unsigned char>& logodata)
939
std::vector<unsigned char> result(LogoInfo::width * LogoInfo::height * 3 / 2);
940
Convert24To_I420Frame(&logodata[0], &result[0], LogoInfo::width * LogoInfo::height, LogoInfo::width);
944
static const std::vector<unsigned char> NVConvert24To_YUY2Frame
945
(const std::vector<unsigned char>& logodata)
947
std::vector<unsigned char> result(LogoInfo::width * LogoInfo::height * 3 / 2);
948
Convert24To_YUY2Frame(&logodata[0], &result[0], LogoInfo::width * LogoInfo::height, LogoInfo::width);
952
static const std::vector<unsigned char> NVConvert16To24Frame
953
(const void* data, unsigned npixels)
955
std::vector<unsigned char> logodata(npixels*3); /* filled with black. */
956
Convert16To24Frame(data, &logodata[0], npixels);
960
static const std::vector<unsigned char> NVConvert15To24Frame
961
(const void* data, unsigned npixels)
963
std::vector<unsigned char> logodata(npixels*3); /* filled with black. */
964
Convert15To24Frame(data, &logodata[0], npixels);
968
static const std::vector<unsigned char> NVConvert_I420To24Frame
969
(const void* data, unsigned npixels)
971
std::vector<unsigned char> logodata(npixels*3); /* filled with black. */
972
Convert_I420To24Frame(data, &logodata[0], npixels, LogoInfo::width);
976
static const std::vector<unsigned char> NVConvert_YUY2To24Frame
977
(const void* data, unsigned npixels)
979
std::vector<unsigned char> logodata(npixels*3); /* filled with black. */
980
Convert_YUY2To24Frame(data, &logodata[0], npixels, LogoInfo::width);
984
static void SubstituteWithBlackIfNeeded(const void*& data)
986
/* If the first frames of the animation consist of a
987
* single color (such as gray for NES), replace them
988
* with black to avoid ugly backgrounds on logo animations
991
static bool Deviate = false;
992
static short* Replacement = 0;
993
static unsigned wid=0, hei=0;
996
if(Replacement) { delete[] Replacement; Replacement=0; }
1000
unsigned dim = LogoInfo::width * LogoInfo::height;
1001
const short* p = (const short*)data;
1002
for(unsigned a=0; a<dim; ++a)
1009
if(Replacement && (wid != LogoInfo::width || hei != LogoInfo::height))
1011
delete[] Replacement;
1015
wid = LogoInfo::width;
1016
hei = LogoInfo::height;
1020
Replacement = new short[dim];
1021
for(unsigned a=0; a<dim; ++a) Replacement[a]=0x0000;
1023
data = (void*)Replacement;
1027
void NESVideoLoggingVideo
1028
(const void*data, unsigned width,unsigned height,
1029
unsigned fps_scaled,
1033
if(LoggingEnabled < 2) return;
1035
++CurrentFrameNumber;
1037
#ifdef THREAD_SAFETY
1041
if(bpp == 32) /* Convert 32 to 24 */
1045
static std::vector<unsigned char> VideoBuf;
1046
VideoBuf.resize(width*height * 3);
1048
Convert32To24Frame(data, &VideoBuf[0], width*height);
1049
data = (void*)&VideoBuf[0];
1052
if(bpp) INPUT_BPP = bpp;
1056
case 32: USE_FOURCC = BGR32; break;
1057
case 24: USE_FOURCC = BGR24; break;
1058
case 16: USE_FOURCC = BGR16; break;
1059
case 15: USE_FOURCC = BGR15; break;
1060
case 12: USE_FOURCC = I420; break;
1061
case 17: USE_FOURCC = YUY2; break;
1063
//USE_FOURCC = BGR24; // FIXME TEMPORARY
1066
const int LogoFramesHeader = (int)( (LOGO_LENGTH_HEADER * fps_scaled) / (1 << 24) );
1067
const int LogoFramesOverlap = (int)( (LOGO_LENGTH_OVERLAP * fps_scaled) / (1 << 24) );
1069
LogoInfo::width = width;
1070
LogoInfo::height = height;
1072
if(INPUT_BPP == 16 || INPUT_BPP == 15)
1074
SubstituteWithBlackIfNeeded(data);
1076
else if(INPUT_BPP != 24 && INPUT_BPP != 12 && INPUT_BPP != 17)
1078
fprintf(stderr, "NESVIDEOS_PIECE only supports 16 and 24 bpp, you gave %u bpp\n",
1083
if(!LogoInfo::SentVideo)
1085
/* Send animation frames that do not involve source video? */
1086
LogoInfo::SentVideo=true;
1088
if(LogoFramesHeader > 0)
1090
for(int frame = 0; frame < LogoFramesHeader; ++frame)
1092
std::vector<unsigned char> logodata(width*height*3); /* filled with black. */
1094
std::string fn = GetLogoFileName(frame);
1095
/*fprintf(stderr, "wid=%d(%d), hei=%d(%d),fn=%s\n",
1096
width, LogoInfo::width,
1097
height, LogoInfo::height,
1099
OverlayLogoFrom(fn.c_str(), logodata);
1101
//INPUT_BPP = 24; USE_FOURCC = BGR24; // FIXME TEMPORARY
1105
std::vector<unsigned char> result = NVConvert24To16Frame(logodata);
1106
GetAVIptr().Video(width,height,fps_scaled, &result[0]);
1108
else if(INPUT_BPP == 15)
1110
std::vector<unsigned char> result = NVConvert24To15Frame(logodata);
1111
GetAVIptr().Video(width,height,fps_scaled, &result[0]);
1113
else if(INPUT_BPP == 12)
1115
std::vector<unsigned char> result = NVConvert24To_I420Frame(logodata);
1116
GetAVIptr().Video(width,height,fps_scaled, &result[0]);
1118
else if(INPUT_BPP == 17)
1120
std::vector<unsigned char> result = NVConvert24To_YUY2Frame(logodata);
1121
GetAVIptr().Video(width,height,fps_scaled, &result[0]);
1125
GetAVIptr().Video(width,height,fps_scaled, &logodata[0]);
1131
if(LogoInfo::OverlapSent < LogoFramesOverlap)
1133
/* Send animation frames that mix source and animation? */
1135
std::string fn = GetLogoFileName(LogoInfo::OverlapSent + LogoFramesHeader);
1137
fprintf(stderr, "wid=%d(%d), hei=%d(%d),fn=%s\n",
1138
width, LogoInfo::width,
1139
height, LogoInfo::height,
1142
std::vector<unsigned char> logodata;
1145
logodata = NVConvert16To24Frame(data, width*height);
1147
else if(INPUT_BPP == 15)
1149
logodata = NVConvert15To24Frame(data, width*height);
1151
else if(INPUT_BPP == 17)
1153
logodata = NVConvert_YUY2To24Frame(data, width*height);
1155
else if(INPUT_BPP == 12)
1157
logodata = NVConvert_I420To24Frame(data, width*height);
1161
logodata.resize(width*height*3); /* filled with black. */
1162
memcpy(&logodata[0], data, width*height*3);
1165
OverlayLogoFrom(fn.c_str(), logodata);
1167
// INPUT_BPP = 24; USE_FOURCC = BGR24; // FIXME TEMPORARY
1171
std::vector<unsigned char> result = NVConvert24To16Frame(logodata);
1172
GetAVIptr().Video(width,height,fps_scaled, &result[0]);
1174
else if(INPUT_BPP == 15)
1176
std::vector<unsigned char> result = NVConvert24To15Frame(logodata);
1177
GetAVIptr().Video(width,height,fps_scaled, &result[0]);
1179
else if(INPUT_BPP == 12)
1181
std::vector<unsigned char> result = NVConvert24To_I420Frame(logodata);
1182
GetAVIptr().Video(width,height,fps_scaled, &result[0]);
1184
else if(INPUT_BPP == 17)
1186
std::vector<unsigned char> result = NVConvert24To_YUY2Frame(logodata);
1187
GetAVIptr().Video(width,height,fps_scaled, &result[0]);
1191
GetAVIptr().Video(width,height,fps_scaled, &logodata[0]);
1194
++LogoInfo::OverlapSent;
1199
GetAVIptr().Video(width,height,fps_scaled, (const unsigned char*) data);
1202
void NESVideoLoggingAudio
1204
unsigned rate, unsigned bits, unsigned chans,
1207
if(LoggingEnabled < 2) return;
1209
++CurrentFrameNumber;
1211
#ifdef THREAD_SAFETY
1215
if(!LogoInfo::SentAudio && LOGO_LENGTH_HEADER > 0)
1217
LogoInfo::SentAudio=true;
1219
double HdrLength = LOGO_LENGTH_HEADER; // N64 workaround
1221
const long n = (long)(rate * HdrLength)/*
1225
unsigned bytes = n*chans*(bits/8);
1226
unsigned char* buf = (unsigned char*)malloc(bytes);
1229
memset(buf,0,bytes);
1230
GetAVIptr().Audio(rate,bits,chans, buf, n);
1237
fprintf(stderr, "Writing %u samples (%u bits, %u chans, %u rate)\n",
1238
nsamples, bits, chans, rate);*/
1241
static FILE*fp = fopen("audiodump.wav", "wb");
1242
fwrite(data, 1, nsamples*(bits/8)*chans, fp);
1245
GetAVIptr().Audio(rate,bits,chans, (const unsigned char*) data, nsamples);