// license:BSD-3-Clause // copyright-holders:Ryan Holtz //============================================================ // // aviwrite.cpp - AVI screenshot writer class // //============================================================ #include "emu.h" #include "aviwrite.h" #include "modules/lib/osdobj_common.h" #include "fileio.h" #include "screen.h" avi_write::avi_write(running_machine& machine, uint32_t width, uint32_t height) : m_machine(machine) , m_recording(false) , m_width(width) , m_height(height) , m_output_file(nullptr) , m_frame(0) { } avi_write::~avi_write() { if (m_recording) { stop(); } } void avi_write::record(std::string_view name) { if (m_recording) { end_avi_recording(); } begin_avi_recording(name); } void avi_write::stop() { osd_printf_info("Stopping AVI recording after %d frames.\n", m_frame); end_avi_recording(); } void avi_write::begin_avi_recording(std::string_view name) { // stop any existing recording end_avi_recording(); // reset the state m_frame = 0; m_next_frame_time = m_machine.time(); const screen_device *primary_screen = screen_device_enumerator(m_machine.root_device()).first(); // build up information about this new movie avi_file::movie_info info; info.video_format = 0; info.video_timescale = 1000 * (primary_screen ? ATTOSECONDS_TO_HZ(primary_screen->frame_period().m_attoseconds) : screen_device::DEFAULT_FRAME_RATE); info.video_sampletime = 1000; info.video_numsamples = 0; info.video_width = m_width; info.video_height = m_height; info.video_depth = 24; info.audio_format = 0; info.audio_timescale = m_machine.sample_rate().value(); info.audio_sampletime = 1; info.audio_numsamples = 0; info.audio_channels = 2; info.audio_samplebits = 16; info.audio_samplerate = m_machine.sample_rate().value(); // compute the frame time m_frame_period = attotime::from_seconds(1000) / info.video_timescale; // create a new temporary movie file emu_file tempfile(m_machine.options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); const std::error_condition filerr = (name.empty() || name == OSDOPTVAL_AUTO) ? m_machine.video().open_next(tempfile, "avi") : tempfile.open(name); // if we succeeded, make a copy of the name and create the real file over top if (!filerr) { const std::string fullpath = tempfile.fullpath(); tempfile.close(); // create the file and free the string avi_file::error avierr = avi_file::create(fullpath, info, m_output_file); if (avierr != avi_file::error::NONE) { osd_printf_error("Error creating AVI: %s\n", avi_file::error_string(avierr)); } else { m_recording = true; } } } void avi_write::end_avi_recording() { m_recording = false; m_output_file.reset(); m_frame = 0; } void avi_write::video_frame(bitmap_rgb32& snap) { // get the current time attotime curtime = m_machine.time(); // loop until we hit the right time while (m_next_frame_time <= curtime) { // handle an AVI recording // write the next frame avi_file::error avierr = m_output_file->append_video_frame(snap); if (avierr != avi_file::error::NONE) { osd_printf_error("Error while logging AVI video frame: %s\n", avi_file::error_string(avierr)); osd_printf_error("Stopping AVI recording.\n"); end_avi_recording(); return; } // advance time m_next_frame_time += m_frame_period; m_frame++; } } void avi_write::audio_frame(const int16_t *buffer, int samples_this_frame) { // only record if we have a file if (m_output_file != nullptr) { // write the next frame avi_file::error avierr = m_output_file->append_sound_samples(0, buffer + 0, samples_this_frame, 1); if (avierr == avi_file::error::NONE) avierr = m_output_file->append_sound_samples(1, buffer + 1, samples_this_frame, 1); if (avierr != avi_file::error::NONE) { osd_printf_error("Error while logging AVI audio frame: %s\n", avi_file::error_string(avierr)); osd_printf_error("Stopping AVI recording.\n"); end_avi_recording(); } } }