summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-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