Jump to content
Visual Boy Advance-M
spacy51

new video recording system using ffmpeg + named pipes

Recommended Posts

I managed to write some very ugly code to test wether it is possible for VBA-M to encode with with an ffmpeg executable.

 

The answer is: YES, it is possible

 

I have to create a named pipe with the windows API and write the raw rgb24 image data to it while playing. ffmpeg.exe can then be started and the named pipe can be set as the input file.

 

The command line for ffmpeg looked as follows:

ffmpeg -r 60 -pix_fmt rgb24 -s 240x160 -f rawvideo -i \\.\pipe\vbam -f mp4 -b 1000000 -y vbam.mp4

 

This approach has many good points about it:

- All encoding functionality resides in ffmpeg.exe (VBA-M.exe will not get bloated)

- ffmpeg supports almost every video format/codec you can imagine.

- No intermediate file necessary for encoding in any format

 

 

Here's a sample video file:

http://www.mediafire.com/file/i5j1znbjwnl/vbam.mp4

currently no sound implemented.

Share this post


Link to post
Share on other sites

Hm, I am not sure wether I should record at 60 Hz or at 59.737 Hz. GBATEK states that 59.737 Hz is the time needed to refresh every display line once. Does VBA-M emulate it that way? How long is one frame of audio?

Share this post


Link to post
Share on other sites

Hm, AVIWrite seems to be fine when using it with ffdshow.

Well, if anyone is interested in perfecting this, here's the code:

 

HANDLE hPipe

 

// create named pipe

hPipe = CreateNamedPipe(

_T( "\\\\.\\pipe\\vbam" ),

PIPE_ACCESS_OUTBOUND,

PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, // PIPE_NOWAIT needed ?

1,

240 * 160 * 3,

240 * 160 * 3,

0,

NULL );

ASSERT( hPipe != INVALID_HANDLE_VALUE );

 

BOOL retConnect = ConnectNamedPipe( hPipe, NULL );

ASSERT( retConnect != 0 );

 

inline void cpyImg32raw( unsigned char *dst, unsigned char *src, unsigned int srcPitch, unsigned short width, unsigned short height )

{

unsigned char r, g, b;

unsigned short x = width;

const unsigned int garbage = srcPitch - ( width * 4 );

 

while( height-- ) {

while( x-- ) {

r = *src++;

g = *src++;

b = *src++;

src++; // unused 8 bit

*dst++ = b;

*dst++ = g;

*dst++ = r;

}

x = width;

src += garbage;

}

}

 

 

// prepare source image

unsigned char *bmp;

unsigned short srcPitch = theApp.sizeX * ( systemColorDepth >> 3 ) + 4;

switch( systemColorDepth )

{

// case 16:

// bmp = new unsigned char[ theApp.sizeX * theApp.sizeY * 2 ];

// cpyImg16bmp( bmp, pix + srcPitch, srcPitch, theApp.sizeX, theApp.sizeY );

// break;

case 32:

// use 24 bit colors to reduce video size

bmp = new unsigned char[ theApp.sizeX * theApp.sizeY * 3 ];

cpyImg32raw bmp, pix + srcPitch, srcPitch, theApp.sizeX, theApp.sizeY );

break;

}

 

 

// send source image through pipe

DWORD written = 0;

BOOL ret = WriteFile( hPipe, bmp, theApp.sizeX * theApp.sizeY * 3, &written, NULL );

DWORD err = GetLastError();

ASSERT( ret == TRUE );

 

 

delete bmp;

 

 

FlushFileBuffers( hPipe );

DisconnectNamedPipe( hPipe );

CloseHandle( hPipe );

 

 

ffmpeg.exe -r 59.7275 -pix_fmt bgr24 -s 240x160 -f rawvideo -i \\.\pipe\vbam -f mp4 -y vbam.mp4

Share this post


Link to post
Share on other sites

Nach, any thoughts?

 

Yes, several actually.

 

I believe a method along these lines is the best way to encode.

1) It eliminates any platform specific video encoding libraries.

2) It eliminates needing to write any code to interface with those libraries which can get messy quickly.

3) It's easy to make cross platform.

4) It allows a user to easily set his own encoding options, and save them as a preset.

5) Programs can be swapped at any time to use whichever works best (MEncoder, FFMPEG, or anything else).

6) We get free support of any of the newest encoding methods without any reprogramming done on our part.

7) The code is rather easy, and the programmer doesn't need to learn any API beyond standard C if done correctly.

 

However, I don't necessarily agree with using a named piped (FIFO file). How to create a named pipe changes per OS, and some OSs have whacked out restrictions on named pipes like only on particular file systems or other nonsense.

 

Most of these command line operated encoding programs don't just works with files, but would accept input via stdin. So I prefer launching them via popen() which is standard C, and writing to them in a normal method, which gives maximum portability.

 

That's what I did in ZSNES. However I wrote specialized versions of popen() for Windows and Linux which returns a null pointer if it could not launch the executable.

 

The method should be user configurable to specify which program to use, how many passes to run, which commands to pass the program for each pass, and commands at the end to clean up the binary (such as running mkvmerge, or hexedit into an AVI new timecodes).

 

ZSNES begin of dump code: https://zsnes.bountysource.com/svn/!source/5224/trunk/src/zmovie.c#1984

 

Begin Dump: https://zsnes.bountysource.com/svn/!source/5224/trunk/src/zmovie.c#2263

 

Write Frame: https://zsnes.bountysource.com/svn/!source/5224/trunk/src/zmovie.c#236

 

Finish Dump: https://zsnes.bountysource.com/svn/!source/5224/trunk/src/zmovie.c#2203

 

Embed logo in video prior to dumping the video (see http://tasvideos.org for use of this) https://zsnes.bountysource.com/svn/!source/5224/trunk/src/zmovie.c#2173

 

Modified popen() for Windows: https://zsnes.bountysource.com/svn/!source/5224/trunk/src/win/safelib.c

 

Modified popen() for Linux (and other UNIXs): https://zsnes.bountysource.com/svn/!source/5224/trunk/src/linux/safelib.c#262

 

Creating user config file from developer's end: https://zsnes.bountysource.com/svn/!source/5224/trunk/src/md.psr

What user sees when editing config file: http://nach.pastebin.com/m3b752734

Custom config for FLV: http://ia340931.us.archive.org/0/items/Zsnes_zmv2flv_cfg_file/zmovie.cfg

 

Some guy who tweaked his own settings: http://www.mortenblog.net/2009/02/23/recording-snes-game-videoes-with-zsnes-and-mencoder/

 

Since I want to include encoding like this in other projects, I was thinking of making a completely emulator independent C++ class which handled dumping like this, with support for user settings, begin dump, write frames, finish up dump.

 

Hm, I am not sure wether I should record at 60 Hz or at 59.737 Hz. GBATEK states that 59.737 Hz is the time needed to refresh every display line once. Does VBA-M emulate it that way? How long is one frame of audio?

 

You should write it at whatever speed VBA-M goes at. In the case of audio, if need be, resample it as I do in ZSNES.

Share this post


Link to post
Share on other sites

Suggestion.

 

Before we go about fkin with other parts of the emulator that don't need to be fked with just yet. Maybe addressing the issues we already have would be good.

 

The DirectInput bug is positively a show stopper for players wanting to use their gamepads, especially the thumbsticks, as these can make fighting games perform smoother.

Share this post


Link to post
Share on other sites
Since I want to include encoding like this in other projects, I was thinking of making a completely emulator independent C++ class which handled dumping like this, with support for user settings, begin dump, write frames, finish up dump.

 

Awesome idea. I think this will be the best. For both our sakes. :heh:

 

The DirectInput bug is positively a show stopper for players wanting to use their gamepads, especially the thumbsticks, as these can make fighting games perform smoother.

 

Right, get me into a full blown VBA-M coding mood and then I'll see.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×