diff options
Diffstat (limited to 'src/emu/recording.cpp')
-rw-r--r-- | src/emu/recording.cpp | 309 |
1 files changed, 309 insertions, 0 deletions
diff --git a/src/emu/recording.cpp b/src/emu/recording.cpp new file mode 100644 index 00000000000..91bc0e58bf7 --- /dev/null +++ b/src/emu/recording.cpp @@ -0,0 +1,309 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + recording.cpp + + MAME AVI/MNG video recording routines. + +***************************************************************************/ + +#include "emu.h" + +#include "fileio.h" +#include "main.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_period(attotime::zero) + , m_next_frame_time(attotime::zero) + , m_frame(0) +{ +} + + +//------------------------------------------------- +// 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()); + result->set_channel_count(machine.sound().outputs_count()); + } + 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 = 0x3fff'fffc; // multiple of 60, for screenless machines + info.video_sampletime = screen() ? screen()->frame_period().as_ticks(info.video_timescale) : 0x0111'1111; + 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 = machine.sound().outputs_count(); + info.audio_samplebits = 16; + info.audio_samplerate = machine.sample_rate(); + + m_channels = info.audio_channels; + + // compute the frame time + set_frame_period(attotime::from_ticks(info.video_sampletime, info.video_timescale)); + + // 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) +{ + auto profile = g_profiler.start(PROFILER_MOVIE_REC); + + // write the next frame + avi_file::error avierr = avi_file::error::NONE; + for (int channel = 0; channel != m_channels && avierr == avi_file::error::NONE; channel ++) + avierr = m_avi_file->append_sound_samples(channel, sound + channel, numsamples, m_channels-1); + + 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) + util::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) +{ + // compute the frame time (MNG rate is an unsigned integer) + attotime period = screen() ? screen()->frame_period() : attotime::from_hz(screen_device::DEFAULT_FRAME_RATE); + u32 rate = u32(period.as_hz()); + set_frame_period(attotime::from_hz(rate)); + + m_mng_file = std::move(file); + std::error_condition const pngerr = util::mng_capture_start(*m_mng_file, snap_bitmap, rate); + if (pngerr) + osd_printf_error("Error capturing MNG (%s:%d %s)\n", pngerr.category().name(), pngerr.value(), pngerr.message()); + return !pngerr; +} + + +//------------------------------------------------- +// 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 + util::png_info pnginfo; + if (current_frame() == 0) + { + for (auto &ent : m_info_fields) + pnginfo.add_text(ent.first, ent.second); + } + + std::error_condition const error = util::mng_capture_frame(*m_mng_file, pnginfo, bitmap, palette_entries, palette); + return !error; +} + + +//------------------------------------------------- +// 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; +} |