summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Aaron Giles <aaron@aarongiles.com>2008-05-13 03:02:11 +0000
committer Aaron Giles <aaron@aarongiles.com>2008-05-13 03:02:11 +0000
commit86dd599aa8887e656f50d70e3e6318d6c05360c1 (patch)
treeaad3806efbc62954adfcf22473aecb5131edbc22
parent5c918815e4b16c623a313e983f38d8febebf1526 (diff)
Added uncompressed AVI recording. Extended aviio to be able
to write RGB bitmaps. Unfortunately, the only option is fully uncompressed, which means the resulting AVIs are *HUGE* and may not play correctly in realtime due to high data rate. The intention is that these uncompressed AVIs are post-processed by other utilities to compress the video and produce a realtime playable result. Added new command-line option -aviwrite which works just like -mngwrite, except it produces AVIs and streams sound to them. Updated documentation accordingly. Shift+F12 still produces MNGs for now, though this might change in the future. Modified fileio.c to retain the full pathname to the file so that it can be queried while the file is open.
-rw-r--r--docs/config.txt6
-rw-r--r--src/emu/emuopts.c1
-rw-r--r--src/emu/emuopts.h1
-rw-r--r--src/emu/fileio.c33
-rw-r--r--src/emu/fileio.h3
-rw-r--r--src/emu/sound.c1
-rw-r--r--src/emu/ui.c6
-rw-r--r--src/emu/video.c236
-rw-r--r--src/emu/video.h12
-rw-r--r--src/lib/util/aviio.c116
-rw-r--r--src/lib/util/aviio.h3
11 files changed, 354 insertions, 64 deletions
diff --git a/docs/config.txt b/docs/config.txt
index dedb7502bb1..b71b46a3ea9 100644
--- a/docs/config.txt
+++ b/docs/config.txt
@@ -461,6 +461,12 @@ Core state/playback options
for that, and reassemble the audio/video using offline tools. The
default is NULL (no recording).
+-aviwrite <filename>
+
+ Stream video and sound data to the given <filename> in AVI format,
+ producing an animation of the game session complete with sound. The
+ default is NULL (no recording).
+
-wavwrite <filename>
Writes the final mixer output to the given <filename> in WAV format,
diff --git a/src/emu/emuopts.c b/src/emu/emuopts.c
index 21a39282d57..1564d851a56 100644
--- a/src/emu/emuopts.c
+++ b/src/emu/emuopts.c
@@ -65,6 +65,7 @@ const options_entry mame_core_options[] =
{ "playback;pb", NULL, 0, "playback an input file" },
{ "record;rec", NULL, 0, "record an input file" },
{ "mngwrite", NULL, 0, "optional filename to write a MNG movie of the current session" },
+ { "aviwrite", NULL, 0, "optional filename to write an AVI movie of the current session" },
{ "wavwrite", NULL, 0, "optional filename to write a WAV file of the current session" },
/* performance options */
diff --git a/src/emu/emuopts.h b/src/emu/emuopts.h
index 11af383f003..44d49880050 100644
--- a/src/emu/emuopts.h
+++ b/src/emu/emuopts.h
@@ -64,6 +64,7 @@
#define OPTION_PLAYBACK "playback"
#define OPTION_RECORD "record"
#define OPTION_MNGWRITE "mngwrite"
+#define OPTION_AVIWRITE "aviwrite"
#define OPTION_WAVWRITE "wavwrite"
/* core performance options */
diff --git a/src/emu/fileio.c b/src/emu/fileio.c
index 1566bab96d3..3c07d7f867a 100644
--- a/src/emu/fileio.c
+++ b/src/emu/fileio.c
@@ -44,6 +44,7 @@ struct _mame_file
#ifdef DEBUG_COOKIE
UINT32 debug_cookie; /* sanity checking for debugging */
#endif
+ astring * filename; /* full filename */
core_file * file; /* core file pointer */
UINT32 openflags; /* flags we used for the open */
char hash[HASH_BUF_SIZE]; /* hash data for the file */
@@ -214,7 +215,6 @@ static file_error fopen_internal(core_options *opts, const char *searchpath, con
{
file_error filerr = FILERR_NOT_FOUND;
path_iterator iterator;
- astring *fullname;
/* can either have a hash or open for write, but not both */
if ((openflags & OPEN_FLAG_HAS_CRC) && (openflags & OPEN_FLAG_WRITE))
@@ -240,28 +240,27 @@ static file_error fopen_internal(core_options *opts, const char *searchpath, con
path_iterator_init(&iterator, opts, searchpath);
/* loop over paths */
- fullname = astring_alloc();
- while (path_iterator_get_next(&iterator, fullname))
+ (*file)->filename = astring_alloc();
+ while (path_iterator_get_next(&iterator, (*file)->filename))
{
/* compute the full pathname */
- if (astring_len(fullname) > 0)
- astring_catc(fullname, PATH_SEPARATOR);
- astring_catc(fullname, filename);
+ if (astring_len((*file)->filename) > 0)
+ astring_catc((*file)->filename, PATH_SEPARATOR);
+ astring_catc((*file)->filename, filename);
/* attempt to open the file directly */
- filerr = core_fopen(astring_c(fullname), openflags, &(*file)->file);
+ filerr = core_fopen(astring_c((*file)->filename), openflags, &(*file)->file);
if (filerr == FILERR_NONE)
break;
/* if we're opening for read-only we have other options */
if ((openflags & (OPEN_FLAG_READ | OPEN_FLAG_WRITE)) == OPEN_FLAG_READ)
{
- filerr = fopen_attempt_zipped(fullname, crc, openflags, *file);
+ filerr = fopen_attempt_zipped((*file)->filename, crc, openflags, *file);
if (filerr == FILERR_NONE)
break;
}
}
- astring_free(fullname);
/* handle errors and return */
if (filerr != FILERR_NONE)
@@ -379,6 +378,8 @@ void mame_fclose(mame_file *file)
core_fclose(file->file);
if (file->zipdata != NULL)
free(file->zipdata);
+ if (file->filename != NULL)
+ astring_free(file->filename);
free(file);
}
@@ -702,6 +703,20 @@ core_file *mame_core_file(mame_file *file)
/*-------------------------------------------------
+ mame_file_full_name - return the full filename
+ for a given mame_file
+-------------------------------------------------*/
+
+const char *mame_file_full_name(mame_file *file)
+{
+ /* return a pointer to the string if we have one; otherwise, return NULL */
+ if (file->filename != NULL)
+ return astring_c(file->filename);
+ return NULL;
+}
+
+
+/*-------------------------------------------------
mame_fhash - returns the hash for a file
-------------------------------------------------*/
diff --git a/src/emu/fileio.h b/src/emu/fileio.h
index 3272ee2ede4..2d79f46a8a7 100644
--- a/src/emu/fileio.h
+++ b/src/emu/fileio.h
@@ -163,6 +163,9 @@ void mame_closepath(mame_path *path);
/* return the core_file underneath the mame_file */
core_file *mame_core_file(mame_file *file);
+/* return the full filename for a given mame_file */
+const char *mame_file_full_name(mame_file *file);
+
/* return a hash string for the file with the given functions */
const char *mame_fhash(mame_file *file, UINT32 functions);
diff --git a/src/emu/sound.c b/src/emu/sound.c
index 7deabf91b7c..d57a8d82bab 100644
--- a/src/emu/sound.c
+++ b/src/emu/sound.c
@@ -753,6 +753,7 @@ static TIMER_CALLBACK( sound_update )
if (finalmix_offset > 0)
{
osd_update_audio_stream(machine, finalmix, finalmix_offset / 2);
+ video_avi_add_sound(machine, finalmix, finalmix_offset / 2);
if (wavfile != NULL)
wav_add_data_16(wavfile, finalmix, finalmix_offset);
}
diff --git a/src/emu/ui.c b/src/emu/ui.c
index 07644dfc311..901ee29e8fe 100644
--- a/src/emu/ui.c
+++ b/src/emu/ui.c
@@ -1314,14 +1314,14 @@ static UINT32 handler_ingame(running_machine *machine, UINT32 state)
/* toggle movie recording */
if (input_ui_pressed(IPT_UI_RECORD_MOVIE))
{
- if (!video_is_movie_active(machine->primary_screen))
+ if (!video_mng_is_movie_active(machine->primary_screen))
{
- video_movie_begin_recording(machine->primary_screen, NULL);
+ video_mng_begin_recording(machine->primary_screen, NULL);
popmessage("REC START");
}
else
{
- video_movie_end_recording(machine->primary_screen);
+ video_mng_end_recording(machine->primary_screen);
popmessage("REC STOP");
}
}
diff --git a/src/emu/video.c b/src/emu/video.c
index 9aea274d024..09b0c83ffb0 100644
--- a/src/emu/video.c
+++ b/src/emu/video.c
@@ -15,6 +15,7 @@
#include "debugger.h"
#include "rendutil.h"
#include "ui.h"
+#include "aviio.h"
#include "snap.lh"
@@ -79,7 +80,8 @@ struct _screen_state
vblank_state_changed_func vblank_callback[MAX_VBLANK_CALLBACKS]; /* the array of callbacks */
/* movie recording */
- mame_file * movie_file; /* handle to the open movie file */
+ mame_file * mng_file; /* handle to the open movie file */
+ avi_file * avi_file; /* handle to the open movie file */
UINT32 movie_frame; /* current movie frame number */
};
@@ -188,7 +190,8 @@ static void create_snapshot_bitmap(const device_config *screen);
static file_error mame_fopen_next(running_machine *machine, const char *pathoption, const char *extension, mame_file **file);
/* movie recording */
-static void movie_record_frame(const device_config *screen);
+static void video_mng_record_frame(const device_config *screen);
+static void video_avi_record_frame(const device_config *screen);
/* software rendering */
static void rgb888_draw_primitives(const render_primitive *primlist, void *dstdata, UINT32 width, UINT32 height, UINT32 pitch);
@@ -345,8 +348,12 @@ void video_init(running_machine *machine)
/* start recording movie if specified */
filename = options_get_string(mame_options(), OPTION_MNGWRITE);
- if ((filename[0] != 0) && (machine->primary_screen != NULL))
- video_movie_begin_recording(machine->primary_screen, filename);
+ if (filename[0] != 0 && machine->primary_screen != NULL)
+ video_mng_begin_recording(machine->primary_screen, filename);
+
+ filename = options_get_string(mame_options(), OPTION_AVIWRITE);
+ if (filename[0] != 0 && machine->primary_screen != NULL)
+ video_avi_begin_recording(machine->primary_screen, filename);
/* if no screens, create a periodic timer to drive updates */
if (machine->primary_screen == NULL)
@@ -371,7 +378,10 @@ static void video_exit(running_machine *machine)
/* stop recording any movie */
if (machine->primary_screen != NULL)
- video_movie_end_recording(machine->primary_screen);
+ {
+ video_mng_end_recording(machine->primary_screen);
+ video_avi_end_recording(machine->primary_screen);
+ }
/* free all the graphics elements */
for (i = 0; i < MAX_GFX_ELEMENTS; i++)
@@ -696,7 +706,7 @@ void video_screen_configure(const device_config *screen, int width, int height,
/* if there has been no VBLANK time specified in the MACHINE_DRIVER, compute it now
from the visible area, otherwise just used the supplied value */
- if ((config->vblank == 0) && !config->oldstyle_vblank_supplied)
+ if (config->vblank == 0 && !config->oldstyle_vblank_supplied)
state->vblank_period = state->scantime * (height - (visarea->max_y + 1 - visarea->min_y));
else
state->vblank_period = config->vblank;
@@ -833,7 +843,7 @@ void video_screen_update_now(const device_config *screen)
we only update up to the previous scanline.
This minimizes the number of pixels that might be drawn
incorrectly until we support a pixel level granularity */
- if ((current_hpos < (state->width / 2)) && (current_vpos > 0))
+ if (current_hpos < (state->width / 2) && current_vpos > 0)
current_vpos = current_vpos - 1;
video_screen_update_partial(screen, current_vpos);
@@ -1157,10 +1167,10 @@ static DEVICE_START( video_screen )
assert(config->height > 0);
assert(config->refresh > 0);
assert(config->visarea.min_x >= 0);
- assert((config->visarea.max_x < config->width) || (config->type == SCREEN_TYPE_VECTOR));
+ assert(config->visarea.max_x < config->width || config->type == SCREEN_TYPE_VECTOR);
assert(config->visarea.max_x > config->visarea.min_x);
assert(config->visarea.min_y >= 0);
- assert((config->visarea.max_y < config->height) || (config->type == SCREEN_TYPE_VECTOR));
+ assert(config->visarea.max_y < config->height || config->type == SCREEN_TYPE_VECTOR);
assert(config->visarea.max_y > config->visarea.min_y);
/* allocate the VBLANK timers */
@@ -1297,7 +1307,7 @@ static TIMER_CALLBACK( vblank_begin_callback )
(*state->vblank_callback[i])(screen, TRUE);
/* if this is the primary screen and we need to update now */
- if ((screen == machine->primary_screen) && !(machine->config->video_attributes & VIDEO_UPDATE_AFTER_VBLANK))
+ if (screen == machine->primary_screen && !(machine->config->video_attributes & VIDEO_UPDATE_AFTER_VBLANK))
video_frame_update(machine, FALSE);
/* reset the VBLANK start timer for the next frame */
@@ -1327,7 +1337,7 @@ static TIMER_CALLBACK( vblank_end_callback )
(*state->vblank_callback[i])(screen, FALSE);
/* if this is the primary screen and we need to update now */
- if ((screen == machine->primary_screen) && (machine->config->video_attributes & VIDEO_UPDATE_AFTER_VBLANK))
+ if (screen == machine->primary_screen && (machine->config->video_attributes & VIDEO_UPDATE_AFTER_VBLANK))
video_frame_update(machine, FALSE);
/* increment the frame number counter */
@@ -1445,7 +1455,7 @@ void video_frame_update(running_machine *machine, int debug)
if (phase == MAME_PHASE_RUNNING)
{
/* reset partial updates if we're paused or if the debugger is active */
- if ((machine->primary_screen != NULL) && (mame_is_paused(machine) || debug || mame_debug_is_active()))
+ if (machine->primary_screen != NULL && (mame_is_paused(machine) || debug || mame_debug_is_active()))
{
void *param = (void *)machine->primary_screen;
scanline0_callback(machine, param, 0);
@@ -1508,7 +1518,10 @@ static int finish_screen_updates(running_machine *machine)
/* update our movie recording state */
if (!mame_is_paused(machine))
- movie_record_frame(screen);
+ {
+ video_mng_record_frame(screen);
+ video_avi_record_frame(screen);
+ }
}
/* reset the screen changed flags */
@@ -2158,7 +2171,7 @@ void video_save_active_screen_snapshots(running_machine *machine)
/*-------------------------------------------------
creare_snapshot_bitmap - creates a
bitmap containing the screenshot for the
- given screen number
+ given screen
-------------------------------------------------*/
static void create_snapshot_bitmap(const device_config *screen)
@@ -2177,7 +2190,7 @@ static void create_snapshot_bitmap(const device_config *screen)
render_target_set_bounds(global.snap_target, width, height, 0);
/* if we don't have a bitmap, or if it's not the right size, allocate a new one */
- if ((global.snap_bitmap == NULL) || (width != global.snap_bitmap->width) || (height != global.snap_bitmap->height))
+ if (global.snap_bitmap == NULL || width != global.snap_bitmap->width || height != global.snap_bitmap->height)
{
if (global.snap_bitmap != NULL)
bitmap_free(global.snap_bitmap);
@@ -2233,72 +2246,72 @@ static file_error mame_fopen_next(running_machine *machine, const char *pathopti
***************************************************************************/
/*-------------------------------------------------
- video_is_movie_active - return true if a movie
- is currently being recorded
+ video_mng_is_movie_active - return true if a
+ MNG movie is currently being recorded
-------------------------------------------------*/
-int video_is_movie_active(const device_config *screen)
+int video_mng_is_movie_active(const device_config *screen)
{
screen_state *state = get_safe_token(screen);
- return (state->movie_file != NULL);
+ return (state->mng_file != NULL);
}
/*-------------------------------------------------
- video_movie_begin_recording - begin recording
+ video_mng_begin_recording - begin recording
of a MNG movie
-------------------------------------------------*/
-void video_movie_begin_recording(const device_config *screen, const char *name)
+void video_mng_begin_recording(const device_config *screen, const char *name)
{
screen_state *state = get_safe_token(screen);
file_error filerr;
/* close any existing movie file */
- if (state->movie_file != NULL)
- video_movie_end_recording(screen);
+ if (state->mng_file != NULL)
+ video_mng_end_recording(screen);
/* create a new movie file and start recording */
if (name != NULL)
- filerr = mame_fopen(SEARCHPATH_MOVIE, name, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, &state->movie_file);
+ filerr = mame_fopen(SEARCHPATH_MOVIE, name, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, &state->mng_file);
else
- filerr = mame_fopen_next(screen->machine, SEARCHPATH_MOVIE, "mng", &state->movie_file);
+ filerr = mame_fopen_next(screen->machine, SEARCHPATH_MOVIE, "mng", &state->mng_file);
state->movie_frame = 0;
}
/*-------------------------------------------------
- video_movie_end_recording - stop recording of
+ video_mng_end_recording - stop recording of
a MNG movie
-------------------------------------------------*/
-void video_movie_end_recording(const device_config *screen)
+void video_mng_end_recording(const device_config *screen)
{
screen_state *state = get_safe_token(screen);
/* close the file if it exists */
- if (state->movie_file != NULL)
+ if (state->mng_file != NULL)
{
- mng_capture_stop(mame_core_file(state->movie_file));
- mame_fclose(state->movie_file);
- state->movie_file = NULL;
+ mng_capture_stop(mame_core_file(state->mng_file));
+ mame_fclose(state->mng_file);
+ state->mng_file = NULL;
state->movie_frame = 0;
}
}
/*-------------------------------------------------
- movie_record_frame - record a frame of a
+ video_mng_record_frame - record a frame of a
movie
-------------------------------------------------*/
-static void movie_record_frame(const device_config *screen)
+static void video_mng_record_frame(const device_config *screen)
{
screen_state *state = get_safe_token(screen);
const rgb_t *palette;
/* only record if we have a file */
- if (state->movie_file != NULL)
+ if (state->mng_file != NULL)
{
png_info pnginfo = { 0 };
png_error error;
@@ -2320,30 +2333,171 @@ static void movie_record_frame(const device_config *screen)
png_add_text(&pnginfo, "System", text);
/* start the capture */
- error = mng_capture_start(mame_core_file(state->movie_file), global.snap_bitmap, ATTOSECONDS_TO_HZ(state->frame_period));
+ error = mng_capture_start(mame_core_file(state->mng_file), global.snap_bitmap, ATTOSECONDS_TO_HZ(state->frame_period));
if (error != PNGERR_NONE)
{
png_free(&pnginfo);
- video_movie_end_recording(screen);
+ video_mng_end_recording(screen);
return;
}
}
/* write the next frame */
palette = (screen->machine->palette != NULL) ? palette_entry_list_adjusted(screen->machine->palette) : NULL;
- error = mng_capture_frame(mame_core_file(state->movie_file), &pnginfo, global.snap_bitmap, screen->machine->config->total_colors, palette);
+ error = mng_capture_frame(mame_core_file(state->mng_file), &pnginfo, global.snap_bitmap, screen->machine->config->total_colors, palette);
png_free(&pnginfo);
if (error != PNGERR_NONE)
- {
- video_movie_end_recording(screen);
- return;
- }
+ video_mng_end_recording(screen);
+
+ profiler_mark(PROFILER_END);
+ }
+}
+
+
+
+/***************************************************************************
+ AVI MOVIE RECORDING
+***************************************************************************/
+
+/*-------------------------------------------------
+ video_avi_begin_recording - begin recording
+ of an AVI movie
+-------------------------------------------------*/
+
+void video_avi_begin_recording(const device_config *screen, const char *name)
+{
+ screen_state *state = get_safe_token(screen);
+ avi_movie_info info;
+ mame_file *tempfile;
+ file_error filerr;
+ avi_error avierr;
+
+ /* close any existing movie file */
+ if (state->avi_file != NULL)
+ video_avi_end_recording(screen);
+
+ /* create a snapshot bitmap so we know what the target size is */
+ create_snapshot_bitmap(screen);
+
+ /* build up information about this new movie */
+ info.video_format = 0;
+ info.video_timescale = 1000 * ATTOSECONDS_TO_HZ(state->frame_period);
+ info.video_sampletime = 1000;
+ info.video_numsamples = 0;
+ info.video_width = global.snap_bitmap->width;
+ info.video_height = global.snap_bitmap->height;
+ info.video_depth = 24;
+
+ info.audio_format = 0;
+ info.audio_timescale = 1;
+ info.audio_sampletime = screen->machine->sample_rate;
+ info.audio_numsamples = 0;
+ info.audio_channels = 2;
+ info.audio_samplebits = 16;
+ info.audio_samplerate = screen->machine->sample_rate;
+
+ /* create a new temporary movie file */
+ if (name != NULL)
+ filerr = mame_fopen(SEARCHPATH_MOVIE, name, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, &tempfile);
+ else
+ filerr = mame_fopen_next(screen->machine, SEARCHPATH_MOVIE, "avi", &tempfile);
+ state->movie_frame = 0;
+
+ /* if we succeeded, make a copy of the name and create the real file over top */
+ if (filerr == FILERR_NONE)
+ {
+ astring *fullname = astring_dupc(mame_file_full_name(tempfile));
+ mame_fclose(tempfile);
+
+ /* create the file and free the string */
+ avierr = avi_create(astring_c(fullname), &info, &state->avi_file);
+ astring_free(fullname);
+ }
+}
+
+
+/*-------------------------------------------------
+ video_avi_end_recording - stop recording of
+ a avi movie
+-------------------------------------------------*/
+
+void video_avi_end_recording(const device_config *screen)
+{
+ screen_state *state = get_safe_token(screen);
+
+ /* close the file if it exists */
+ if (state->avi_file != NULL)
+ {
+ avi_close(state->avi_file);
+ state->avi_file = NULL;
+ state->movie_frame = 0;
+ }
+}
+
+
+/*-------------------------------------------------
+ video_avi_record_frame - record a frame of a
+ movie
+-------------------------------------------------*/
+
+static void video_avi_record_frame(const device_config *screen)
+{
+ screen_state *state = get_safe_token(screen);
+
+ /* only record if we have a file */
+ if (state->avi_file != NULL)
+ {
+ avi_error avierr;
+
+ profiler_mark(PROFILER_MOVIE_REC);
+
+ /* create the bitmap */
+ create_snapshot_bitmap(screen);
+
+ /* write the next frame */
+ avierr = avi_append_video_frame_rgb32(state->avi_file, global.snap_bitmap);
+ if (avierr != AVIERR_NONE)
+ video_avi_end_recording(screen);
profiler_mark(PROFILER_END);
}
}
+/*-------------------------------------------------
+ video_avi_add_sound - add sound to an AVI
+ recording
+-------------------------------------------------*/
+
+void video_avi_add_sound(running_machine *machine, const INT16 *sound, int numsamples)
+{
+ const device_config *screen;
+
+ /* loop over screens that might be recording */
+ for (screen = video_screen_first(machine->config); screen != NULL; screen = video_screen_next(screen))
+ {
+ screen_state *state = get_safe_token(screen);
+
+ /* only record if we have a file */
+ if (state->avi_file != NULL)
+ {
+ avi_error avierr;
+
+ profiler_mark(PROFILER_MOVIE_REC);
+
+ /* write the next frame */
+ avierr = avi_append_sound_samples(state->avi_file, 0, sound + 0, numsamples, 1);
+ if (avierr == AVIERR_NONE)
+ avierr = avi_append_sound_samples(state->avi_file, 1, sound + 1, numsamples, 1);
+ if (avierr != AVIERR_NONE)
+ video_avi_end_recording(screen);
+
+ profiler_mark(PROFILER_END);
+ }
+ }
+}
+
+
/***************************************************************************
SOFTWARE RENDERING
diff --git a/src/emu/video.h b/src/emu/video.h
index 3ebcbf4053d..68a7bf55fd3 100644
--- a/src/emu/video.h
+++ b/src/emu/video.h
@@ -263,9 +263,13 @@ void video_save_active_screen_snapshots(running_machine *machine);
/* ----- movie recording ----- */
-int video_is_movie_active(const device_config *screen);
-void video_movie_begin_recording(const device_config *screen, const char *name);
-void video_movie_end_recording(const device_config *screen);
-
+int video_mng_is_movie_active(const device_config *screen);
+void video_mng_begin_recording(const device_config *screen, const char *name);
+void video_mng_end_recording(const device_config *screen);
+
+int video_avi_is_movie_active(const device_config *screen);
+void video_avi_begin_recording(const device_config *screen, const char *name);
+void video_avi_end_recording(const device_config *screen);
+void video_avi_add_sound(running_machine *machine, const INT16 *sound, int numsamples);
#endif /* __VIDEO_H__ */
diff --git a/src/lib/util/aviio.c b/src/lib/util/aviio.c
index c4edafd9b3c..80484524f5c 100644
--- a/src/lib/util/aviio.c
+++ b/src/lib/util/aviio.c
@@ -52,11 +52,6 @@
#define HANDLER_DIB AVI_FOURCC('D','I','B',' ')
#define HANDLER_HFYU AVI_FOURCC('h','f','y','u')
-#define FORMAT_UYVY AVI_FOURCC('U','Y','V','Y')
-#define FORMAT_VYUY AVI_FOURCC('V','Y','U','Y')
-#define FORMAT_YUY2 AVI_FOURCC('Y','U','Y','2')
-#define FORMAT_HFYU AVI_FOURCC('H','F','Y','U')
-
/* main AVI header files */
#define AVIF_HASINDEX 0x00000010
#define AVIF_MUSTUSEINDEX 0x00000020
@@ -224,6 +219,9 @@ static avi_error soundbuf_initialize(avi_file *file);
static avi_error soundbuf_write_chunk(avi_file *file, UINT32 framenum);
static avi_error soundbuf_flush(avi_file *file, int only_flush_full);
+/* RGB helpers */
+static avi_error rgb32_compress_to_rgb(avi_stream *stream, const bitmap_t *bitmap, UINT8 *data, UINT32 numbytes);
+
/* YUY helpers */
static avi_error yuv_decompress_to_yuy16(avi_stream *stream, const UINT8 *data, UINT32 numbytes, bitmap_t *bitmap);
static avi_error yuy16_compress_to_yuy(avi_stream *stream, const bitmap_t *bitmap, UINT8 *data, UINT32 numbytes);
@@ -928,6 +926,10 @@ avi_error avi_append_video_frame_yuy16(avi_file *file, const bitmap_t *bitmap)
avi_error avierr;
UINT32 maxlength;
+ /* validate our ability to handle the data */
+ if (stream->format != FORMAT_UYVY && stream->format != FORMAT_VYUY && stream->format != FORMAT_YUY2 && stream->format != FORMAT_HFYU)
+ return AVIERR_UNSUPPORTED_VIDEO_FORMAT;
+
/* double check bitmap format */
if (bitmap->format != BITMAP_FORMAT_YUY16)
return AVIERR_INVALID_BITMAP;
@@ -960,11 +962,61 @@ avi_error avi_append_video_frame_yuy16(avi_file *file, const bitmap_t *bitmap)
/*-------------------------------------------------
+ avi_append_video_frame_rgb32 - append a frame
+ of video in RGB32 format
+-------------------------------------------------*/
+
+avi_error avi_append_video_frame_rgb32(avi_file *file, const bitmap_t *bitmap)
+{
+ avi_stream *stream = get_video_stream(file);
+ avi_error avierr;
+ UINT32 maxlength;
+
+ /* validate our ability to handle the data */
+ if (stream->format != 0)
+ return AVIERR_UNSUPPORTED_VIDEO_FORMAT;
+
+ /* depth must be 24 */
+ if (stream->depth != 24)
+ return AVIERR_UNSUPPORTED_VIDEO_FORMAT;
+
+ /* double check bitmap format */
+ if (bitmap->format != BITMAP_FORMAT_RGB32)
+ return AVIERR_INVALID_BITMAP;
+
+ /* write out any sound data first */
+ avierr = soundbuf_write_chunk(file, stream->chunks);
+ if (avierr != AVIERR_NONE)
+ return avierr;
+
+ /* make sure we have enough room */
+ maxlength = 3 * stream->width * stream->height;
+ avierr = expand_tempbuffer(file, maxlength);
+ if (avierr != AVIERR_NONE)
+ return avierr;
+
+ /* copy the RGB data to the destination */
+ avierr = rgb32_compress_to_rgb(stream, bitmap, file->tempbuffer, maxlength);
+ if (avierr != AVIERR_NONE)
+ return avierr;
+
+ /* set the info for this new chunk */
+ avierr = set_stream_chunk_info(stream, stream->chunks, file->writeoffs, maxlength + 8);
+ if (avierr != AVIERR_NONE)
+ return avierr;
+ stream->samples = file->info.video_numsamples = stream->chunks;
+
+ /* write the data */
+ return chunk_write(file, get_chunkid_for_stream(file, stream), file->tempbuffer, maxlength);
+}
+
+
+/*-------------------------------------------------
avi_append_sound_samples - append sound
samples
-------------------------------------------------*/
-avi_error avi_append_sound_samples(avi_file *file, int channel, const INT16 *samples, UINT32 numsamples)
+avi_error avi_append_sound_samples(avi_file *file, int channel, const INT16 *samples, UINT32 numsamples, UINT32 sampleskip)
{
UINT32 sampoffset = file->soundbuf_chansamples[channel];
UINT32 sampnum;
@@ -977,6 +1029,7 @@ avi_error avi_append_sound_samples(avi_file *file, int channel, const INT16 *sam
for (sampnum = 0; sampnum < numsamples; sampnum++)
{
INT16 data = *samples++;
+ samples += sampleskip;
data = LITTLE_ENDIANIZE_INT16(data);
file->soundbuf[sampoffset++ * file->info.audio_channels + channel] = data;
}
@@ -2240,6 +2293,57 @@ static avi_error soundbuf_flush(avi_file *file, int only_flush_full)
/*-------------------------------------------------
+ rgb32_compress_to_rgb - "compress" an RGB32
+ bitmap to an RGB encoded frame
+-------------------------------------------------*/
+
+static avi_error rgb32_compress_to_rgb(avi_stream *stream, const bitmap_t *bitmap, UINT8 *data, UINT32 numbytes)
+{
+ int height = MIN(stream->height, bitmap->height);
+ int width = MIN(stream->width, bitmap->width);
+ UINT8 *dataend = data + numbytes;
+ int x, y;
+
+ /* compressed video */
+ for (y = 0; y < height; y++)
+ {
+ const UINT32 *source = (UINT32 *)bitmap->base + y * bitmap->rowpixels;
+ UINT8 *dest = data + (stream->height - 1 - y) * stream->width * 3;
+
+ for (x = 0; x < width && dest < dataend; x++)
+ {
+ UINT32 pix = *source++;
+ *dest++ = RGB_BLUE(pix);
+ *dest++ = RGB_GREEN(pix);
+ *dest++ = RGB_RED(pix);
+ }
+
+ /* fill in any blank space on the right */
+ for ( ; x < stream->width && dest < dataend; x++)
+ {
+ *dest++ = 0;
+ *dest++ = 0;
+ *dest++ = 0;
+ }
+ }
+
+ /* fill in any blank space on the bottom */
+ for ( ; y < stream->height; y++)
+ {
+ UINT8 *dest = data + (stream->height - 1 - y) * stream->width * 3;
+ for (x = 0; x < stream->width && dest < dataend; x++)
+ {
+ *dest++ = 0;
+ *dest++ = 0;
+ *dest++ = 0;
+ }
+ }
+
+ return AVIERR_NONE;
+}
+
+
+/*-------------------------------------------------
yuv_decompress_to_yuy16 - decompress a YUV
encoded frame to a YUY16 bitmap
-------------------------------------------------*/
diff --git a/src/lib/util/aviio.h b/src/lib/util/aviio.h
index 37e949e6961..9c9e938893e 100644
--- a/src/lib/util/aviio.h
+++ b/src/lib/util/aviio.h
@@ -118,6 +118,7 @@ avi_error avi_read_video_frame_yuy16(avi_file *file, UINT32 framenum, bitmap_t *
avi_error avi_read_sound_samples(avi_file *file, int channel, UINT32 firstsample, UINT32 numsamples, INT16 *output);
avi_error avi_append_video_frame_yuy16(avi_file *file, const bitmap_t *bitmap);
-avi_error avi_append_sound_samples(avi_file *file, int channel, const INT16 *samples, UINT32 numsamples);
+avi_error avi_append_video_frame_rgb32(avi_file *file, const bitmap_t *bitmap);
+avi_error avi_append_sound_samples(avi_file *file, int channel, const INT16 *samples, UINT32 numsamples, UINT32 sampleskip);
#endif