Skip to content

Commit

Permalink
-Added movie recording feature using ffmpeg piping
Browse files Browse the repository at this point in the history
  • Loading branch information
uze6666 committed Sep 25, 2015
1 parent e0d7f59 commit 135c2bb
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 18 deletions.
68 changes: 57 additions & 11 deletions tools/uzem/avr8.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@ static u8 encode_delta(int d)
u32 hsync_more_col;
u32 hsync_less_col;

FILE* avconv_video = NULL;
FILE* avconv_audio = NULL;

void avr8::spi_calculateClock(){
// calculate the number of cycles before the write completes
u16 spiClockDivider;
Expand Down Expand Up @@ -250,6 +253,10 @@ void avr8::write_io(u8 addr,u8 value)
SDL_LockAudio();
audioRing.push(value);
SDL_UnlockAudio();

//Send audio byte to ffmpeg
if(recordMovie && avconv_audio) fwrite(&value, 1, 1, avconv_audio);

}
}
else if (addr == ports::PORTD)
Expand All @@ -275,9 +282,11 @@ void avr8::write_io(u8 addr,u8 value)
if(scanline_count >= 0){

current_cycle = left_edge;

current_scanline = (u32*)((u8*)screen->pixels + scanline_count * 2 * screen->pitch + inset);
/*



/*
if(hsyncHelp){
if(prev_scanline!=NULL && elapsedCycles > HSYNC_PERIOD){
Expand Down Expand Up @@ -323,7 +332,9 @@ void avr8::write_io(u8 addr,u8 value)
{
if (SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen);
SDL_Flip(screen);
//SDL_framerateDelay(&fpsmanager);

//Send video frame to ffmpeg
if (recordMovie && avconv_video) fwrite(screen->pixels, 640*480*4, 1, avconv_video);

SDL_Event event;
while (singleStep? SDL_WaitEvent(&event) : SDL_PollEvent(&event))
Expand Down Expand Up @@ -360,6 +371,9 @@ void avr8::write_io(u8 addr,u8 value)
buttons[0]=captureData[capturePtr]+(captureData[capturePtr+1]<<8);
capturePtr+=2;
captureSize-=2;
}else if(captureMode==CAPTURE_READ && captureSize==0){
printf("Playback reached end of capture file.\n");
shutdown(0);
}


Expand Down Expand Up @@ -1627,16 +1641,16 @@ bool avr8::init_gui()
init_joysticks();

if (fullscreen)
screen = SDL_SetVideoMode(800,600,32,sdl_flags | SDL_FULLSCREEN);
screen = SDL_SetVideoMode(640,480,32,sdl_flags | SDL_FULLSCREEN);
else
screen = SDL_SetVideoMode(630,448,32,sdl_flags);
screen = SDL_SetVideoMode(640,480,32,sdl_flags);
if (!screen)
{
fprintf(stderr, "Unable to set 630x448x32 video mode.\n");
fprintf(stderr, "Unable to set 640x480x32 video mode.\n");
return false;
}
else if (fullscreen) // Center in fullscreen
inset = ((600-448)/2) * screen->pitch + 4 * ((800-630)/2);
//else if (fullscreen) // Center in fullscreen
inset = ((480-448)/2) * screen->pitch + 4 * ((640-630)/2);

if (SDL_MUSTLOCK(screen) && SDL_LockSurface(screen) < 0)
return false;
Expand Down Expand Up @@ -1690,8 +1704,20 @@ bool avr8::init_gui()
hsync_more_col=SDL_MapRGB(screen->format, 255,0, 0); //red
hsync_less_col=SDL_MapRGB(screen->format, 255,255, 0); //yellow

SDL_initFramerate(&fpsmanager);
SDL_setFramerate(&fpsmanager, 60);
if (recordMovie){

if (avconv_video == NULL) avconv_video = popen("ffmpeg -y -f rawvideo -s 640x480 -pix_fmt bgra -r 60 -i - -vf scale=-1:720 -sws_flags neighbor -an -b:v 1000k uzemtemp.mp4" , "w");
if (avconv_video == NULL){
fprintf(stderr, "Unable to init ffmpeg.\n");
return false;
}

avconv_audio = popen("ffmpeg -y -f u8 -ar 15734 -ac 1 -i - -acodec libmp3lame -ar 44.1k uzemtemp.mp3", "w");
if(avconv_audio == NULL){
fprintf(stderr, "Unable to init ffmpeg.\n");
return false;
}
}

//Set window icon
SDL_Surface *slogo;
Expand Down Expand Up @@ -2547,10 +2573,30 @@ void avr8::shutdown(int errcode){
}
}

if(captureMode==CAPTURE_WRITE && captureFile!=NULL){
if(captureFile!=NULL){
fclose(captureFile);
}

//movie recording
if(recordMovie){
if (avconv_video) pclose(avconv_video);
if (avconv_audio) pclose(avconv_audio);

char mux[1024];
strcpy(mux,"ffmpeg -y -i uzemtemp.mp4 -i uzemtemp.mp3 -vcodec copy -acodec copy -f mp4 ");
strcat(mux,romName);
strcat(mux,".mp4");

FILE* avconv_mux = popen(mux,"r");
if (avconv_mux) {
pclose(avconv_mux);
unlink("uzemtemp.mp4");
unlink("uzemtemp.mp3");
}else{
printf("Error with ffmpeg multiplexer.");
}
}

if (joystickFile) {
FILE* f = fopen(joystickFile,"wb");

Expand Down
9 changes: 7 additions & 2 deletions tools/uzem/avr8.h
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,8 @@ struct avr8
{
avr8() :
/*Core*/
pc(0), cycleCounter(0), watchdogTimer(0), prevPortB(0), prevWDR(0), eepromFile("eeprom.bin"),enableGdb(false),newTCCR1B(0),elapsedCyclesSleep(0),hsyncHelp(false),
pc(0), cycleCounter(0), watchdogTimer(0), prevPortB(0), prevWDR(0), eepromFile("eeprom.bin"),enableGdb(false),
newTCCR1B(0),elapsedCyclesSleep(0),hsyncHelp(false),recordMovie(false),

/*Video*/
fullscreen(false),inset(0),
Expand All @@ -291,7 +292,8 @@ struct avr8
joystickFile(0),pad_mode(SNES_PAD), new_input_mode(false),

/*GDB*/
singleStep(0), nextSingleStep(0), gdbBreakpointFound(false),gdbInvalidOpcode(false),gdbPort(1284),state(CPU_STOPPED),gdb(0),
singleStep(0), nextSingleStep(0), gdbBreakpointFound(false),gdbInvalidOpcode(false),gdbPort(1284),
state(CPU_STOPPED),gdb(0),

/*Uzekeyboard*/
uzeKbState(0),uzeKbEnabled(false),
Expand All @@ -311,6 +313,7 @@ struct avr8
memset(sram, 0, sizeof(sram));
memset(eeprom, 0, sizeof(eeprom));
memset(progmem,0,progSize);
memset(romName,0,sizeof(romName));
}

/*Core*/
Expand All @@ -331,6 +334,8 @@ struct avr8
int randomSeed;
const char* eepromFile;
bool hsyncHelp;
bool recordMovie;
char romName[256];

struct
{
Expand Down
36 changes: 31 additions & 5 deletions tools/uzem/uzem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ static const struct option longopts[] ={
{ "mouse" , no_argument , NULL, 'm' },
{ "2p" , no_argument , NULL, '2' },
{ "img" , required_argument, NULL, 'g' },
{ "mbr" , no_argument , NULL, 'r' },
{ "record" , no_argument , NULL, 'r' },
{ "eeprom" , required_argument, NULL, 'e' },
{ "pgm" , required_argument, NULL, 'p' },
{ "boot" , no_argument, NULL, 'b' },
Expand Down Expand Up @@ -81,6 +81,7 @@ void showHelp(char* programName){
printerr("\t--capture -c Captures controllers data to file.\n");
printerr("\t--loadcap -l Load and replays controllers data from file.\n");
printerr("\t--synchelp -z Displays and logs information to help troubleshooting HSYNC timing issues.\n");
printerr("\t--record -r Record a movie in mp4/720p(60fps) format. (ffmpeg executable must be in the same directory as uzem or system path)\n");
}

int ends_with(const char* name, const char* extension, size_t length)
Expand Down Expand Up @@ -146,7 +147,7 @@ int main(int argc,char **argv)
uzebox.pad_mode = avr8::SNES_PAD2;
break;
case 'r':
//TODO: implement MBR emulation option
uzebox.recordMovie=true;
break;
case 's':
uzebox.SDpath = optarg;
Expand Down Expand Up @@ -245,10 +246,35 @@ int main(int argc,char **argv)
}
}

//get rom name without extension
char *pfile = heximage + strlen(heximage);
for (;; pfile--)
{
if ((*pfile == '\\') || (*pfile == '/') || pfile==heximage)
{
if(pfile!=heximage)pfile++; //skip the slash character

for(int i=0;i<256;i++){
if(*pfile=='.') break;
uzebox.romName[i]=*pfile;
pfile++;
}
break;
}
}

//get rom name without extension to build
//the capture file name

//build the capture file name
int len=strlen(uzebox.romName);
char capfname[len+4];
strcpy(capfname,uzebox.romName);
capfname[len+0]='.';
capfname[len+1]='c';
capfname[len+2]='a';
capfname[len+3]='p';
capfname[len+4]=0;

/*
char capfname[256];
char *pfile = heximage + strlen(heximage);
for (;; pfile--)
Expand All @@ -272,7 +298,7 @@ int main(int argc,char **argv)
break;
}
}

*/

if(uzebox.captureMode==CAPTURE_READ)
{
Expand Down

0 comments on commit 135c2bb

Please sign in to comment.