mirror of
https://github.com/holub/mame
synced 2025-04-22 16:31:49 +03:00
Refactoring of AVI/MNG recording code (#6537)
* Initial refactor of AVI/MNG movie recording, consolidation of copy and paste code, hiding of AVI/MNG behind interfaces * Extracted recording specific code out of src/emu/video.cpp and put into src/emu/recording.cpp * Took the opportunity to move slightly more logic out of video.cpp into recording.cpp * Bug fix * Consolidated frame counting logic
This commit is contained in:
parent
5f8108b3ec
commit
7610231242
@ -177,6 +177,8 @@ files {
|
||||
MAME_DIR .. "src/emu/profiler.h",
|
||||
MAME_DIR .. "src/emu/output.cpp",
|
||||
MAME_DIR .. "src/emu/output.h",
|
||||
MAME_DIR .. "src/emu/recording.cpp",
|
||||
MAME_DIR .. "src/emu/recording.h",
|
||||
MAME_DIR .. "src/emu/render.cpp",
|
||||
MAME_DIR .. "src/emu/render.h",
|
||||
MAME_DIR .. "src/emu/rendfont.cpp",
|
||||
|
@ -273,11 +273,11 @@ void running_machine::start()
|
||||
// start recording movie if specified
|
||||
const char *filename = options().mng_write();
|
||||
if (filename[0] != 0)
|
||||
m_video->begin_recording(filename, video_manager::MF_MNG);
|
||||
m_video->begin_recording(filename, movie_recording::format::MNG);
|
||||
|
||||
filename = options().avi_write();
|
||||
if (filename[0] != 0)
|
||||
m_video->begin_recording(filename, video_manager::MF_AVI);
|
||||
m_video->begin_recording(filename, movie_recording::format::AVI);
|
||||
|
||||
// if we're coming in with a savegame request, process it now
|
||||
const char *savegame = options().state();
|
||||
|
292
src/emu/recording.cpp
Normal file
292
src/emu/recording.cpp
Normal file
@ -0,0 +1,292 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Aaron Giles
|
||||
/***************************************************************************
|
||||
|
||||
recording.cpp
|
||||
|
||||
Core MAME video routines.
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#include "emu.h"
|
||||
#include "screen.h"
|
||||
#include "aviio.h"
|
||||
#include "png.h"
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
class avi_movie_recording : public movie_recording
|
||||
{
|
||||
public:
|
||||
avi_movie_recording(screen_device *screen)
|
||||
: movie_recording(screen)
|
||||
{
|
||||
}
|
||||
|
||||
bool initialize(running_machine &machine, std::unique_ptr<emu_file> &&file, int32_t width, int32_t height);
|
||||
virtual bool add_sound_to_recording(const s16 *sound, int numsamples) override;
|
||||
|
||||
protected:
|
||||
virtual bool append_single_video_frame(bitmap_rgb32 &bitmap, const rgb_t *palette, int palette_entries) override;
|
||||
|
||||
private:
|
||||
avi_file::ptr m_avi_file; // handle to the open movie file
|
||||
};
|
||||
|
||||
|
||||
class mng_movie_recording : public movie_recording
|
||||
{
|
||||
public:
|
||||
mng_movie_recording(screen_device *screen, std::map<std::string, std::string> &&info_fields);
|
||||
~mng_movie_recording();
|
||||
|
||||
bool initialize(std::unique_ptr<emu_file> &&file, bitmap_t &snap_bitmap);
|
||||
virtual bool add_sound_to_recording(const s16 *sound, int numsamples) override;
|
||||
|
||||
protected:
|
||||
virtual bool append_single_video_frame(bitmap_rgb32 &bitmap, const rgb_t *palette, int palette_entries) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<emu_file> m_mng_file; // handle to the open movie file
|
||||
std::map<std::string, std::string> m_info_fields;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// MOVIE RECORDING
|
||||
//**************************************************************************
|
||||
|
||||
//-------------------------------------------------
|
||||
// movie_recording - constructor
|
||||
//-------------------------------------------------
|
||||
|
||||
movie_recording::movie_recording(screen_device *screen)
|
||||
: m_screen(screen)
|
||||
, m_frame(0)
|
||||
{
|
||||
m_frame_period = m_screen ? m_screen->frame_period() : attotime::from_hz(screen_device::DEFAULT_FRAME_RATE);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// movie_recording - destructor
|
||||
//-------------------------------------------------
|
||||
|
||||
movie_recording::~movie_recording()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// movie_recording::append_video_frame
|
||||
//-------------------------------------------------
|
||||
|
||||
bool movie_recording::append_video_frame(bitmap_rgb32 &bitmap, attotime curtime)
|
||||
{
|
||||
// identify the palette
|
||||
bool has_palette = screen() && screen()->has_palette();
|
||||
const rgb_t *palette = has_palette ? screen()->palette().palette()->entry_list_adjusted() : nullptr;
|
||||
int palette_entries = has_palette ? screen()->palette().entries() : 0;
|
||||
|
||||
// keep appending frames until we're at curtime
|
||||
while (next_frame_time() <= curtime)
|
||||
{
|
||||
// append this bitmap as a single frame
|
||||
if (!append_single_video_frame(bitmap, palette, palette_entries))
|
||||
return false;
|
||||
m_frame++;
|
||||
|
||||
// advance time
|
||||
set_next_frame_time(next_frame_time() + frame_period());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// movie_recording::create - creates a new recording
|
||||
// for the specified format
|
||||
//-------------------------------------------------
|
||||
|
||||
movie_recording::ptr movie_recording::create(running_machine &machine, screen_device *screen, format fmt, std::unique_ptr<emu_file> &&file, bitmap_rgb32 &snap_bitmap)
|
||||
{
|
||||
movie_recording::ptr result;
|
||||
switch (fmt)
|
||||
{
|
||||
case movie_recording::format::AVI:
|
||||
{
|
||||
auto avi_recording = std::make_unique<avi_movie_recording>(screen);
|
||||
if (avi_recording->initialize(machine, std::move(file), snap_bitmap.width(), snap_bitmap.height()))
|
||||
result = std::move(avi_recording);
|
||||
}
|
||||
break;
|
||||
|
||||
case movie_recording::format::MNG:
|
||||
{
|
||||
std::map<std::string, std::string> info_fields;
|
||||
info_fields["Software"] = std::string(emulator_info::get_appname()).append(" ").append(emulator_info::get_build_version());
|
||||
info_fields["System"] = std::string(machine.system().manufacturer).append(" ").append(machine.system().type.fullname());
|
||||
|
||||
auto mng_recording = std::make_unique<mng_movie_recording>(screen, std::move(info_fields));
|
||||
if (mng_recording->initialize(std::move(file), snap_bitmap))
|
||||
result = std::move(mng_recording);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw false;
|
||||
}
|
||||
|
||||
// if we successfully create a recording, set the current time and return it
|
||||
if (result)
|
||||
result->set_next_frame_time(machine.time());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// movie_recording::format_file_extension
|
||||
//-------------------------------------------------
|
||||
|
||||
const char *movie_recording::format_file_extension(movie_recording::format fmt)
|
||||
{
|
||||
switch (fmt)
|
||||
{
|
||||
case format::AVI: return "avi";
|
||||
case format::MNG: return "mng";
|
||||
default: throw false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// avi_movie_recording::initialize
|
||||
//-------------------------------------------------
|
||||
|
||||
bool avi_movie_recording::initialize(running_machine &machine, std::unique_ptr<emu_file> &&file, int32_t width, int32_t height)
|
||||
{
|
||||
// we only use the file we're passed to get the full path
|
||||
std::string fullpath = file->fullpath();
|
||||
file.reset();
|
||||
|
||||
// build up information about this new movie
|
||||
avi_file::movie_info info;
|
||||
info.video_format = 0;
|
||||
info.video_timescale = 1000 * frame_period().as_hz();
|
||||
info.video_sampletime = 1000;
|
||||
info.video_numsamples = 0;
|
||||
info.video_width = width;
|
||||
info.video_height = height;
|
||||
info.video_depth = 24;
|
||||
|
||||
info.audio_format = 0;
|
||||
info.audio_timescale = machine.sample_rate();
|
||||
info.audio_sampletime = 1;
|
||||
info.audio_numsamples = 0;
|
||||
info.audio_channels = 2;
|
||||
info.audio_samplebits = 16;
|
||||
info.audio_samplerate = machine.sample_rate();
|
||||
|
||||
// create the file
|
||||
avi_file::error avierr = avi_file::create(fullpath, info, m_avi_file);
|
||||
if (avierr != avi_file::error::NONE)
|
||||
osd_printf_error("Error creating AVI: %s\n", avi_file::error_string(avierr));
|
||||
return avierr == avi_file::error::NONE;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// avi_movie_recording::append_single_video_frame
|
||||
//-------------------------------------------------
|
||||
|
||||
bool avi_movie_recording::append_single_video_frame(bitmap_rgb32 &bitmap, const rgb_t *palette, int palette_entries)
|
||||
{
|
||||
avi_file::error avierr = m_avi_file->append_video_frame(bitmap);
|
||||
return avierr == avi_file::error::NONE;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// avi_movie_recording::append_video_frame
|
||||
//-------------------------------------------------
|
||||
|
||||
bool avi_movie_recording::add_sound_to_recording(const s16 *sound, int numsamples)
|
||||
{
|
||||
g_profiler.start(PROFILER_MOVIE_REC);
|
||||
|
||||
// write the next frame
|
||||
avi_file::error avierr = m_avi_file->append_sound_samples(0, sound + 0, numsamples, 1);
|
||||
if (avierr == avi_file::error::NONE)
|
||||
avierr = m_avi_file->append_sound_samples(1, sound + 1, numsamples, 1);
|
||||
|
||||
g_profiler.stop();
|
||||
return avierr == avi_file::error::NONE;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// mng_movie_recording - constructor
|
||||
//-------------------------------------------------
|
||||
|
||||
mng_movie_recording::mng_movie_recording(screen_device *screen, std::map<std::string, std::string> &&info_fields)
|
||||
: movie_recording(screen)
|
||||
, m_info_fields(std::move(info_fields))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// mng_movie_recording - destructor
|
||||
//-------------------------------------------------
|
||||
|
||||
mng_movie_recording::~mng_movie_recording()
|
||||
{
|
||||
if (m_mng_file)
|
||||
mng_capture_stop(*m_mng_file);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// mng_movie_recording::initialize
|
||||
//-------------------------------------------------
|
||||
|
||||
bool mng_movie_recording::initialize(std::unique_ptr<emu_file> &&file, bitmap_t &snap_bitmap)
|
||||
{
|
||||
m_mng_file = std::move(file);
|
||||
png_error pngerr = mng_capture_start(*m_mng_file, snap_bitmap, frame_period().as_hz());
|
||||
if (pngerr != PNGERR_NONE)
|
||||
osd_printf_error("Error capturing MNG, png_error=%d\n", pngerr);
|
||||
return pngerr == PNGERR_NONE;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// mng_movie_recording::append_single_video_frame
|
||||
//-------------------------------------------------
|
||||
|
||||
bool mng_movie_recording::append_single_video_frame(bitmap_rgb32 &bitmap, const rgb_t *palette, int palette_entries)
|
||||
{
|
||||
// set up the text fields in the movie info
|
||||
png_info pnginfo;
|
||||
if (current_frame() == 0)
|
||||
{
|
||||
for (auto &ent : m_info_fields)
|
||||
pnginfo.add_text(ent.first.c_str(), ent.second.c_str());
|
||||
}
|
||||
|
||||
png_error error = mng_capture_frame(*m_mng_file, pnginfo, bitmap, palette_entries, palette);
|
||||
return error == png_error::PNGERR_NONE;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// mng_movie_recording::add_sound_to_recording
|
||||
//-------------------------------------------------
|
||||
|
||||
bool mng_movie_recording::add_sound_to_recording(const s16 *sound, int numsamples)
|
||||
{
|
||||
// not supported; do nothing
|
||||
return true;
|
||||
}
|
85
src/emu/recording.h
Normal file
85
src/emu/recording.h
Normal file
@ -0,0 +1,85 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Aaron Giles
|
||||
/***************************************************************************
|
||||
|
||||
recording.h
|
||||
|
||||
MAME AVI/MNG video recording routines.
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef __EMU_H__
|
||||
#error Dont include this file directly; include emu.h instead.
|
||||
#endif
|
||||
|
||||
#ifndef MAME_EMU_RECORDING_H
|
||||
#define MAME_EMU_RECORDING_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "attotime.h"
|
||||
#include "palette.h"
|
||||
|
||||
class screen_device;
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// TYPE DEFINITIONS
|
||||
//**************************************************************************
|
||||
|
||||
// ======================> movie_recording
|
||||
|
||||
class movie_recording
|
||||
{
|
||||
public:
|
||||
// movie format options
|
||||
enum class format
|
||||
{
|
||||
MNG,
|
||||
AVI
|
||||
};
|
||||
|
||||
typedef std::unique_ptr<movie_recording> ptr;
|
||||
|
||||
// dtor
|
||||
virtual ~movie_recording();
|
||||
|
||||
// accessors
|
||||
screen_device *screen() { return m_screen; }
|
||||
attotime frame_period() { return m_frame_period; }
|
||||
void set_next_frame_time(attotime time) { m_next_frame_time = time; }
|
||||
attotime next_frame_time() const { return m_next_frame_time; }
|
||||
|
||||
// methods
|
||||
bool append_video_frame(bitmap_rgb32 &bitmap, attotime curtime);
|
||||
|
||||
// virtuals
|
||||
virtual bool add_sound_to_recording(const s16 *sound, int numsamples) = 0;
|
||||
|
||||
// statics
|
||||
static movie_recording::ptr create(running_machine &machine, screen_device *screen, format fmt, std::unique_ptr<emu_file> &&file, bitmap_rgb32 &snap_bitmap);
|
||||
static const char *format_file_extension(format fmt);
|
||||
|
||||
protected:
|
||||
// ctor
|
||||
movie_recording(screen_device *screen);
|
||||
movie_recording(const movie_recording &) = delete;
|
||||
movie_recording(movie_recording &&) = delete;
|
||||
|
||||
// virtuals
|
||||
virtual bool append_single_video_frame(bitmap_rgb32 &bitmap, const rgb_t *palette, int palette_entries) = 0;
|
||||
|
||||
// accessors
|
||||
int current_frame() const { return m_frame; }
|
||||
|
||||
private:
|
||||
screen_device * m_screen; // screen associated with this movie (can be nullptr)
|
||||
attotime m_frame_period; // time of frame period
|
||||
attotime m_next_frame_time; // time of next frame
|
||||
int m_frame; // current movie frame number
|
||||
};
|
||||
|
||||
|
||||
#endif // MAME_EMU_RECORDING_H
|
@ -16,7 +16,6 @@
|
||||
#include "rendersw.hxx"
|
||||
#include "output.h"
|
||||
|
||||
#include "aviio.h"
|
||||
#include "png.h"
|
||||
#include "xmlfile.h"
|
||||
|
||||
@ -412,144 +411,47 @@ std::string &video_manager::timecode_total_text(std::string &str)
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// begin_recording_mng - begin recording a MNG
|
||||
// begin_recording_screen - begin recording a
|
||||
// movie for a specific screen
|
||||
//-------------------------------------------------
|
||||
|
||||
void video_manager::begin_recording_mng(const char *name, uint32_t index, screen_device *screen)
|
||||
void video_manager::begin_recording_screen(const std::string &filename, uint32_t index, screen_device *screen, movie_recording::format format)
|
||||
{
|
||||
// stop any existing recording
|
||||
end_recording_mng(index);
|
||||
// determine the file extension
|
||||
const char *extension = movie_recording::format_file_extension(format);
|
||||
|
||||
mng_info_t &info = m_mngs[index];
|
||||
// create the emu_file
|
||||
bool is_absolute_path = !filename.empty() && osd_is_absolute_path(filename);
|
||||
std::unique_ptr<emu_file> movie_file = std::make_unique<emu_file>(
|
||||
is_absolute_path ? "" : machine().options().snapshot_directory(),
|
||||
OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
|
||||
|
||||
// reset the state
|
||||
info.m_mng_frame = 0;
|
||||
info.m_mng_next_frame_time = machine().time();
|
||||
|
||||
// create a new movie file and start recording
|
||||
info.m_mng_file = std::make_unique<emu_file>(machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
|
||||
osd_file::error filerr;
|
||||
if (name != nullptr)
|
||||
// and open the actual file
|
||||
osd_file::error filerr = filename.empty()
|
||||
? open_next(*movie_file, extension)
|
||||
: movie_file->open(filename);
|
||||
if (filerr != osd_file::error::NONE)
|
||||
{
|
||||
std::string full_name(name);
|
||||
|
||||
if (index > 0)
|
||||
{
|
||||
char name_buf[256] = { 0 };
|
||||
snprintf(name_buf, 256, "%s%d", name, index);
|
||||
full_name = name_buf;
|
||||
}
|
||||
|
||||
filerr = info.m_mng_file->open(full_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
filerr = open_next(*info.m_mng_file, "mng");
|
||||
osd_printf_error("Error creating movie, osd_file::error=%d\n", int(filerr));
|
||||
return;
|
||||
}
|
||||
|
||||
if (filerr == osd_file::error::NONE)
|
||||
{
|
||||
// start the capture
|
||||
int rate = int(screen ? screen->frame_period().as_hz() : screen_device::DEFAULT_FRAME_RATE);
|
||||
png_error pngerr = mng_capture_start(*info.m_mng_file, m_snap_bitmap, rate);
|
||||
if (pngerr != PNGERR_NONE)
|
||||
{
|
||||
osd_printf_error("Error capturing MNG, png_error=%d\n", pngerr);
|
||||
return end_recording_mng(index);
|
||||
}
|
||||
// we have a file; try to create the recording
|
||||
std::unique_ptr<movie_recording> recording = movie_recording::create(machine(), screen, format, std::move(movie_file), m_snap_bitmap);
|
||||
|
||||
// compute the frame time
|
||||
info.m_mng_frame_period = attotime::from_hz(rate);
|
||||
}
|
||||
else
|
||||
{
|
||||
osd_printf_error("Error creating MNG, osd_file::error=%d\n", int(filerr));
|
||||
info.m_mng_file.reset();
|
||||
}
|
||||
// if successful push it onto the list
|
||||
if (recording)
|
||||
m_movie_recordings.push_back(std::move(recording));
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// begin_recording_avi - begin recording an AVI
|
||||
//-------------------------------------------------
|
||||
|
||||
void video_manager::begin_recording_avi(const char *name, uint32_t index, screen_device *screen)
|
||||
{
|
||||
// stop any existing recording
|
||||
end_recording_avi(index);
|
||||
|
||||
avi_info_t &avi_info = m_avis[index];
|
||||
|
||||
// reset the state
|
||||
avi_info.m_avi_frame = 0;
|
||||
avi_info.m_avi_next_frame_time = machine().time();
|
||||
|
||||
// build up information about this new movie
|
||||
avi_file::movie_info info;
|
||||
info.video_format = 0;
|
||||
info.video_timescale = 1000 * (screen ? screen->frame_period().as_hz() : screen_device::DEFAULT_FRAME_RATE);
|
||||
info.video_sampletime = 1000;
|
||||
info.video_numsamples = 0;
|
||||
info.video_width = m_snap_bitmap.width();
|
||||
info.video_height = m_snap_bitmap.height();
|
||||
info.video_depth = 24;
|
||||
|
||||
info.audio_format = 0;
|
||||
info.audio_timescale = machine().sample_rate();
|
||||
info.audio_sampletime = 1;
|
||||
info.audio_numsamples = 0;
|
||||
info.audio_channels = 2;
|
||||
info.audio_samplebits = 16;
|
||||
info.audio_samplerate = machine().sample_rate();
|
||||
|
||||
// create a new temporary movie file
|
||||
osd_file::error filerr;
|
||||
std::string fullpath;
|
||||
{
|
||||
emu_file tempfile(machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
|
||||
if (name != nullptr)
|
||||
{
|
||||
std::string full_name(name);
|
||||
|
||||
if (index > 0)
|
||||
{
|
||||
char name_buf[256] = { 0 };
|
||||
snprintf(name_buf, 256, "%s%d", name, index);
|
||||
full_name = name_buf;
|
||||
}
|
||||
|
||||
filerr = tempfile.open(full_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
filerr = open_next(tempfile, "avi");
|
||||
}
|
||||
|
||||
// if we succeeded, make a copy of the name and create the real file over top
|
||||
if (filerr == osd_file::error::NONE)
|
||||
fullpath = tempfile.fullpath();
|
||||
}
|
||||
|
||||
if (filerr == osd_file::error::NONE)
|
||||
{
|
||||
// compute the frame time
|
||||
avi_info.m_avi_frame_period = attotime::from_seconds(1000) / info.video_timescale;
|
||||
|
||||
// create the file and free the string
|
||||
avi_file::error avierr = avi_file::create(fullpath, info, avi_info.m_avi_file);
|
||||
if (avierr != avi_file::error::NONE)
|
||||
{
|
||||
osd_printf_error("Error creating AVI: %s\n", avi_file::error_string(avierr));
|
||||
return end_recording_avi(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// begin_recording - begin recording of a movie
|
||||
//-------------------------------------------------
|
||||
|
||||
void video_manager::begin_recording(const char *name, movie_format format)
|
||||
void video_manager::begin_recording(const char *name, movie_recording::format format)
|
||||
{
|
||||
// create a snapshot bitmap so we know what the target size is
|
||||
screen_device_iterator iterator = screen_device_iterator(machine().root_device());
|
||||
@ -563,84 +465,33 @@ void video_manager::begin_recording(const char *name, movie_format format)
|
||||
count = 1;
|
||||
}
|
||||
|
||||
switch (format)
|
||||
// clear out existing recordings
|
||||
m_movie_recordings.clear();
|
||||
|
||||
if (m_snap_native)
|
||||
{
|
||||
case MF_AVI:
|
||||
if (m_avis.empty())
|
||||
m_avis.resize(count);
|
||||
if (m_snap_native)
|
||||
{
|
||||
for (uint32_t index = 0; index < count; index++, iter++)
|
||||
{
|
||||
create_snapshot_bitmap(iter.current());
|
||||
begin_recording_avi(name, index, iter.current());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
create_snapshot_bitmap(nullptr);
|
||||
begin_recording_avi(name, 0, iter.current());
|
||||
}
|
||||
break;
|
||||
for (uint32_t index = 0; index < count; index++, iter++)
|
||||
{
|
||||
create_snapshot_bitmap(iter.current());
|
||||
|
||||
case MF_MNG:
|
||||
if (m_mngs.empty())
|
||||
m_mngs.resize(count);
|
||||
if (m_snap_native)
|
||||
{
|
||||
for (uint32_t index = 0; index < count; index++, iter++)
|
||||
{
|
||||
create_snapshot_bitmap(iter.current());
|
||||
begin_recording_mng(name, index, iter.current());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
create_snapshot_bitmap(nullptr);
|
||||
begin_recording_mng(name, 0, iter.current());
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
osd_printf_error("Unknown movie format: %d\n", format);
|
||||
break;
|
||||
std::string tempname;
|
||||
if (name)
|
||||
tempname = index > 0 ? name : util::string_format("%s%d", name, index);
|
||||
begin_recording_screen(
|
||||
tempname,
|
||||
index,
|
||||
iter.current(),
|
||||
format);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
create_snapshot_bitmap(nullptr);
|
||||
begin_recording_screen(name ? name : "", 0, iter.current(), format);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// end_recording_avi - stop recording an AVI movie
|
||||
//--------------------------------------------------
|
||||
|
||||
void video_manager::end_recording_avi(uint32_t index)
|
||||
{
|
||||
avi_info_t &info = m_avis[index];
|
||||
if (info.m_avi_file)
|
||||
{
|
||||
info.m_avi_file.reset();
|
||||
|
||||
// reset the state
|
||||
info.m_avi_frame = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// end_recording_mng - stop recording a MNG movie
|
||||
//--------------------------------------------------
|
||||
|
||||
void video_manager::end_recording_mng(uint32_t index)
|
||||
{
|
||||
mng_info_t &info = m_mngs[index];
|
||||
if (info.m_mng_file != nullptr)
|
||||
{
|
||||
mng_capture_stop(*info.m_mng_file);
|
||||
info.m_mng_file.reset();
|
||||
|
||||
// reset the state
|
||||
info.m_mng_frame = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// add_sound_to_recording - add sound to a movie
|
||||
// recording
|
||||
@ -648,37 +499,10 @@ void video_manager::end_recording_mng(uint32_t index)
|
||||
|
||||
void video_manager::add_sound_to_recording(const s16 *sound, int numsamples)
|
||||
{
|
||||
for (uint32_t index = 0; index < m_avis.size(); index++)
|
||||
{
|
||||
add_sound_to_avi_recording(sound, numsamples, index);
|
||||
if (!m_snap_native)
|
||||
break;
|
||||
}
|
||||
for (auto &recording : m_movie_recordings)
|
||||
recording->add_sound_to_recording(sound, numsamples);
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// add_sound_to_avi_recording - add sound to an
|
||||
// AVI recording for a given screen
|
||||
//-------------------------------------------------
|
||||
|
||||
void video_manager::add_sound_to_avi_recording(const s16 *sound, int numsamples, uint32_t index)
|
||||
{
|
||||
avi_info_t &info = m_avis[index];
|
||||
// only record if we have a file
|
||||
if (info.m_avi_file != nullptr)
|
||||
{
|
||||
g_profiler.start(PROFILER_MOVIE_REC);
|
||||
|
||||
// write the next frame
|
||||
avi_file::error avierr = info.m_avi_file->append_sound_samples(0, sound + 0, numsamples, 1);
|
||||
if (avierr == avi_file::error::NONE)
|
||||
avierr = info.m_avi_file->append_sound_samples(1, sound + 1, numsamples, 1);
|
||||
if (avierr != avi_file::error::NONE)
|
||||
end_recording_avi(index);
|
||||
|
||||
g_profiler.stop();
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// video_exit - close down the video system
|
||||
@ -687,17 +511,7 @@ void video_manager::add_sound_to_avi_recording(const s16 *sound, int numsamples,
|
||||
void video_manager::exit()
|
||||
{
|
||||
// stop recording any movie
|
||||
for (uint32_t index = 0; index < (std::max)(m_mngs.size(), m_avis.size()); index++)
|
||||
{
|
||||
if (index < m_avis.size())
|
||||
end_recording_avi(index);
|
||||
|
||||
if (index < m_mngs.size())
|
||||
end_recording_mng(index);
|
||||
|
||||
if (!m_snap_native)
|
||||
break;
|
||||
}
|
||||
m_movie_recordings.clear();
|
||||
|
||||
// free the snapshot target
|
||||
machine().render().target_free(m_snap_target);
|
||||
@ -733,17 +547,8 @@ void video_manager::screenless_update_callback(void *ptr, int param)
|
||||
|
||||
void video_manager::postload()
|
||||
{
|
||||
for (uint32_t index = 0; index < (std::max)(m_mngs.size(), m_avis.size()); index++)
|
||||
{
|
||||
if (index < m_avis.size())
|
||||
m_avis[index].m_avi_next_frame_time = machine().time();
|
||||
|
||||
if (index < m_mngs.size())
|
||||
m_mngs[index].m_mng_next_frame_time = machine().time();
|
||||
|
||||
if (!m_snap_native)
|
||||
break;
|
||||
}
|
||||
for (const auto &x : m_movie_recordings)
|
||||
x->set_next_frame_time(machine().time());
|
||||
}
|
||||
|
||||
|
||||
@ -754,21 +559,7 @@ void video_manager::postload()
|
||||
|
||||
bool video_manager::is_recording() const
|
||||
{
|
||||
for (mng_info_t const &mng : m_mngs)
|
||||
{
|
||||
if (mng.m_mng_file)
|
||||
return true;
|
||||
else if (!m_snap_native)
|
||||
break;
|
||||
}
|
||||
for (avi_info_t const &avi : m_avis)
|
||||
{
|
||||
if (avi.m_avi_file)
|
||||
return true;
|
||||
else if (!m_snap_native)
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
return !m_movie_recordings.empty();
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
@ -1464,79 +1255,22 @@ void video_manager::record_frame()
|
||||
g_profiler.start(PROFILER_MOVIE_REC);
|
||||
attotime curtime = machine().time();
|
||||
|
||||
screen_device_iterator device_iterator = screen_device_iterator(machine().root_device());
|
||||
screen_device_iterator::auto_iterator iter = device_iterator.begin();
|
||||
|
||||
for (uint32_t index = 0; index < (std::max)(m_mngs.size(), m_avis.size()); index++, iter++)
|
||||
bool error = false;
|
||||
for (auto &recording : m_movie_recordings)
|
||||
{
|
||||
// create the bitmap
|
||||
create_snapshot_bitmap(iter.current());
|
||||
create_snapshot_bitmap(recording->screen());
|
||||
|
||||
// handle an AVI recording
|
||||
if ((index < m_avis.size()) && m_avis[index].m_avi_file)
|
||||
{
|
||||
avi_info_t &avi_info = m_avis[index];
|
||||
|
||||
// loop until we hit the right time
|
||||
while (avi_info.m_avi_next_frame_time <= curtime)
|
||||
{
|
||||
// write the next frame
|
||||
avi_file::error avierr = avi_info.m_avi_file->append_video_frame(m_snap_bitmap);
|
||||
if (avierr != avi_file::error::NONE)
|
||||
{
|
||||
g_profiler.stop(); // FIXME: double exit if this happens?
|
||||
end_recording_avi(index);
|
||||
break;
|
||||
}
|
||||
|
||||
// advance time
|
||||
avi_info.m_avi_next_frame_time += avi_info.m_avi_frame_period;
|
||||
avi_info.m_avi_frame++;
|
||||
}
|
||||
}
|
||||
|
||||
// handle a MNG recording
|
||||
if ((index < m_mngs.size()) && m_mngs[index].m_mng_file)
|
||||
{
|
||||
mng_info_t &mng_info = m_mngs[index];
|
||||
|
||||
// loop until we hit the right time
|
||||
while (mng_info.m_mng_next_frame_time <= curtime)
|
||||
{
|
||||
// set up the text fields in the movie info
|
||||
png_info pnginfo;
|
||||
if (mng_info.m_mng_frame == 0)
|
||||
{
|
||||
std::string text1 = std::string(emulator_info::get_appname()).append(" ").append(emulator_info::get_build_version());
|
||||
std::string text2 = std::string(machine().system().manufacturer).append(" ").append(machine().system().type.fullname());
|
||||
pnginfo.add_text("Software", text1.c_str());
|
||||
pnginfo.add_text("System", text2.c_str());
|
||||
}
|
||||
|
||||
// write the next frame
|
||||
screen_device *screen = iter.current();
|
||||
const rgb_t *palette = (screen != nullptr && screen->has_palette()) ? screen->palette().palette()->entry_list_adjusted() : nullptr;
|
||||
int entries = (screen != nullptr && screen->has_palette()) ? screen->palette().entries() : 0;
|
||||
png_error error = mng_capture_frame(*mng_info.m_mng_file, pnginfo, m_snap_bitmap, entries, palette);
|
||||
if (error != PNGERR_NONE)
|
||||
{
|
||||
g_profiler.stop(); // FIXME: double exit if this happens?
|
||||
end_recording_mng(index);
|
||||
break;
|
||||
}
|
||||
|
||||
// advance time
|
||||
mng_info.m_mng_next_frame_time += mng_info.m_mng_frame_period;
|
||||
mng_info.m_mng_frame++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_snap_native)
|
||||
// and append the frame
|
||||
if (!recording->append_video_frame(m_snap_bitmap, curtime))
|
||||
{
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (error)
|
||||
end_recording();
|
||||
g_profiler.stop();
|
||||
}
|
||||
|
||||
@ -1554,52 +1288,21 @@ void video_manager::toggle_throttle()
|
||||
// toggle_record_movie
|
||||
//-------------------------------------------------
|
||||
|
||||
void video_manager::toggle_record_movie(movie_format format)
|
||||
void video_manager::toggle_record_movie(movie_recording::format format)
|
||||
{
|
||||
if (!is_recording())
|
||||
{
|
||||
begin_recording(nullptr, format);
|
||||
machine().popmessage("REC START (%s)", format == MF_MNG ? "MNG" : "AVI");
|
||||
machine().popmessage("REC START (%s)", format == movie_recording::format::MNG ? "MNG" : "AVI");
|
||||
}
|
||||
else
|
||||
{
|
||||
end_recording(format);
|
||||
machine().popmessage("REC STOP (%s)", format == MF_MNG ? "MNG" : "AVI");
|
||||
end_recording();
|
||||
machine().popmessage("REC STOP");
|
||||
}
|
||||
}
|
||||
|
||||
void video_manager::end_recording(movie_format format)
|
||||
void video_manager::end_recording()
|
||||
{
|
||||
screen_device_iterator device_iterator = screen_device_iterator(machine().root_device());
|
||||
screen_device_iterator::auto_iterator iter = device_iterator.begin();
|
||||
const uint32_t count = (uint32_t)device_iterator.count();
|
||||
|
||||
switch (format)
|
||||
{
|
||||
case MF_AVI:
|
||||
for (uint32_t index = 0; index < count; index++, iter++)
|
||||
{
|
||||
end_recording_avi(index);
|
||||
if (!m_snap_native)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MF_MNG:
|
||||
for (uint32_t index = 0; index < count; index++, iter++)
|
||||
{
|
||||
end_recording_mng(index);
|
||||
if (!m_snap_native)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
osd_printf_error("Unknown movie format: %d\n", format);
|
||||
break;
|
||||
}
|
||||
m_movie_recordings.clear();
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
#ifndef MAME_EMU_VIDEO_H
|
||||
#define MAME_EMU_VIDEO_H
|
||||
|
||||
#include "aviio.h"
|
||||
#include "recording.h"
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
@ -40,13 +40,6 @@ class video_manager
|
||||
friend class screen_device;
|
||||
|
||||
public:
|
||||
// movie format options
|
||||
enum movie_format
|
||||
{
|
||||
MF_MNG,
|
||||
MF_AVI
|
||||
};
|
||||
|
||||
// construction/destruction
|
||||
video_manager(running_machine &machine);
|
||||
|
||||
@ -69,9 +62,7 @@ public:
|
||||
|
||||
// misc
|
||||
void toggle_throttle();
|
||||
void toggle_record_movie(movie_format format);
|
||||
void toggle_record_mng() { toggle_record_movie(MF_MNG); }
|
||||
void toggle_record_avi() { toggle_record_movie(MF_AVI); }
|
||||
void toggle_record_movie(movie_recording::format format);
|
||||
osd_file::error open_next(emu_file &file, const char *extension, uint32_t index = 0);
|
||||
void compute_snapshot_size(s32 &width, s32 &height);
|
||||
void pixels(u32 *buffer);
|
||||
@ -90,14 +81,9 @@ public:
|
||||
void save_input_timecode();
|
||||
|
||||
// movies
|
||||
void begin_recording(const char *name, movie_format format);
|
||||
void begin_recording_mng(const char *name, uint32_t index, screen_device *screen);
|
||||
void begin_recording_avi(const char *name, uint32_t index, screen_device *screen);
|
||||
void end_recording(movie_format format);
|
||||
void end_recording_mng(uint32_t index);
|
||||
void end_recording_avi(uint32_t index);
|
||||
void begin_recording(const char *name, movie_recording::format format);
|
||||
void end_recording();
|
||||
void add_sound_to_recording(const s16 *sound, int numsamples);
|
||||
void add_sound_to_avi_recording(const s16 *sound, int numsamples, uint32_t index);
|
||||
|
||||
void set_timecode_enabled(bool value) { m_timecode_enabled = value; }
|
||||
bool get_timecode_enabled() { return m_timecode_enabled; }
|
||||
@ -132,6 +118,9 @@ private:
|
||||
void create_snapshot_bitmap(screen_device *screen);
|
||||
void record_frame();
|
||||
|
||||
// movies
|
||||
void begin_recording_screen(const std::string &filename, uint32_t index, screen_device *screen, movie_recording::format format);
|
||||
|
||||
// internal state
|
||||
running_machine & m_machine; // reference to our machine
|
||||
|
||||
@ -180,38 +169,8 @@ private:
|
||||
s32 m_snap_width; // width of snapshots (0 == auto)
|
||||
s32 m_snap_height; // height of snapshots (0 == auto)
|
||||
|
||||
// movie recording - MNG
|
||||
class mng_info_t
|
||||
{
|
||||
public:
|
||||
mng_info_t()
|
||||
: m_mng_frame_period(attotime::zero)
|
||||
, m_mng_next_frame_time(attotime::zero)
|
||||
, m_mng_frame(0) { }
|
||||
|
||||
std::unique_ptr<emu_file> m_mng_file; // handle to the open movie file
|
||||
attotime m_mng_frame_period; // period of a single movie frame
|
||||
attotime m_mng_next_frame_time; // time of next frame
|
||||
u32 m_mng_frame; // current movie frame number
|
||||
};
|
||||
std::vector<mng_info_t> m_mngs;
|
||||
|
||||
// movie recording - AVI
|
||||
class avi_info_t
|
||||
{
|
||||
public:
|
||||
avi_info_t()
|
||||
: m_avi_file(nullptr)
|
||||
, m_avi_frame_period(attotime::zero)
|
||||
, m_avi_next_frame_time(attotime::zero)
|
||||
, m_avi_frame(0) { }
|
||||
|
||||
avi_file::ptr m_avi_file; // handle to the open movie file
|
||||
attotime m_avi_frame_period; // period of a single movie frame
|
||||
attotime m_avi_next_frame_time; // time of next frame
|
||||
u32 m_avi_frame; // current movie frame number
|
||||
};
|
||||
std::vector<avi_info_t> m_avis;
|
||||
// movie recordings
|
||||
std::vector<movie_recording::ptr> m_movie_recordings;
|
||||
|
||||
static const bool s_skiptable[FRAMESKIP_LEVELS][FRAMESKIP_LEVELS];
|
||||
|
||||
|
@ -2129,16 +2129,16 @@ void lua_engine::initialize()
|
||||
std::string fn = filename;
|
||||
strreplace(fn, "/", PATH_SEPARATOR);
|
||||
strreplace(fn, "%g", machine().basename());
|
||||
vm.begin_recording(fn.c_str(), video_manager::MF_AVI);
|
||||
vm.begin_recording(fn.c_str(), movie_recording::format::AVI);
|
||||
},
|
||||
[](video_manager &vm) { vm.begin_recording(nullptr, video_manager::MF_AVI); }));
|
||||
[](video_manager &vm) { vm.begin_recording(nullptr, movie_recording::format::AVI); }));
|
||||
video_type.set("end_recording", [this](video_manager &vm) {
|
||||
if(!vm.is_recording())
|
||||
{
|
||||
machine().logerror("[luaengine] No active recording to stop\n");
|
||||
return;
|
||||
}
|
||||
vm.end_recording(video_manager::MF_AVI);
|
||||
vm.end_recording();
|
||||
});
|
||||
video_type.set("snapshot", &video_manager::save_active_screen_snapshots);
|
||||
video_type.set("is_recording", &video_manager::is_recording);
|
||||
|
@ -1208,11 +1208,11 @@ uint32_t mame_ui_manager::handler_ingame(render_container &container)
|
||||
|
||||
// toggle MNG recording
|
||||
if (machine().ui_input().pressed(IPT_UI_RECORD_MNG))
|
||||
machine().video().toggle_record_mng();
|
||||
machine().video().toggle_record_movie(movie_recording::format::MNG);
|
||||
|
||||
// toggle MNG recording
|
||||
if (machine().ui_input().pressed(IPT_UI_RECORD_AVI))
|
||||
machine().video().toggle_record_avi();
|
||||
machine().video().toggle_record_movie(movie_recording::format::AVI);
|
||||
|
||||
// toggle profiler display
|
||||
if (machine().ui_input().pressed(IPT_UI_SHOW_PROFILER))
|
||||
|
Loading…
Reference in New Issue
Block a user