From f292f7babe3562d09521e3f4ab16a19ed7d7e7d0 Mon Sep 17 00:00:00 2001 From: Ilari Liusvaara Date: Sat, 4 Feb 2012 16:57:41 +0200 Subject: [PATCH] Do some small fixups with dumpers - Merge simpler dumpers (JMD, RAW and SDMP) with control code. - Remove unused code from AVI(CSCD) dumper. - Remove non-threaded dumper support from AVI(CSCD) dumper. - Use advanced dumpers API to start/stop dumpers from command line. - Move dumping-related code to avi/ --- Makefile | 8 +- {src/core => avi}/avidump-control.cpp | 128 ++---- avi/cscd.cpp | 422 ++++++------------ avi/cscd.hpp | 100 +---- avi/jmd.cpp | 602 ++++++++++++++++---------- avi/jmd.hpp | 46 -- avi/raw.cpp | 153 +++++++ avi/sdmp.cpp | 282 ++++++++---- avi/sdmp.hpp | 30 -- include/core/advdumper.hpp | 22 + include/library/serialization.hpp | 61 +++ manual.lyx | 135 +++--- manual.txt | 52 +-- src/core/advdumper.cpp | 62 +++ src/core/jmd-control.cpp | 235 ---------- src/core/raw-control.cpp | 193 --------- src/core/sdmp-control.cpp | 169 -------- 17 files changed, 1149 insertions(+), 1551 deletions(-) rename {src/core => avi}/avidump-control.cpp (63%) delete mode 100644 avi/jmd.hpp create mode 100644 avi/raw.cpp delete mode 100644 avi/sdmp.hpp create mode 100644 include/library/serialization.hpp delete mode 100644 src/core/jmd-control.cpp delete mode 100644 src/core/raw-control.cpp delete mode 100644 src/core/sdmp-control.cpp diff --git a/Makefile b/Makefile index 247bdb03..a16699e3 100644 --- a/Makefile +++ b/Makefile @@ -43,7 +43,7 @@ endif #Threads ifdef THREADS ifeq ($(THREADS), YES) -CFLAGS += -DSTD_THREADS +CFLAGS += -DNATIVE_THREADS else ifeq ($(THREADS), BOOST) CFLAGS += -DBOOST_THREADS @@ -53,11 +53,7 @@ else LDFLAGS += -lboost_thread-mt endif else -ifeq ($(THREADS), NO) -CFLAGS += -DNO_THREADS -else -$(error "Bad value for THREADS (expected YES, BOOST, BOOSTNOMT or NO)") -endif +$(error "Bad value for THREADS (expected YES or BOOST)") endif endif endif diff --git a/src/core/avidump-control.cpp b/avi/avidump-control.cpp similarity index 63% rename from src/core/avidump-control.cpp rename to avi/avidump-control.cpp index 3c81e776..ed7d561d 100644 --- a/src/core/avidump-control.cpp +++ b/avi/avidump-control.cpp @@ -2,7 +2,6 @@ #include "sox.hpp" #include "core/advdumper.hpp" -#include "core/command.hpp" #include "core/dispatch.hpp" #include "core/lua.hpp" #include "core/misc.hpp" @@ -57,6 +56,8 @@ namespace numeric_setting max_frames_per_segment("avi-maxframes", 0, 999999999, 0); numeric_setting soundrate_setting("avi-soundrate", 0, 2, 0); + void waitfn(); + class avi_avsnoop : public information_dispatch { public: @@ -69,11 +70,9 @@ namespace avi_cscd_dumper::segment_parameters sp; soundrate = get_sound_rate(); gp.sampling_rate = get_rate(soundrate.first, soundrate.second, soundrate_setting); - gp.channel_count = 2; - gp.audio_16bit = true; sp.fps_n = 60; sp.fps_d = 1; - sp.dataformat = avi_cscd_dumper::PIXFMT_RGB15_NE; + sp.dataformat = avi_cscd_dumper::PIXFMT_XRGB; sp.width = 256; sp.height = 224; sp.default_stride = true; @@ -103,19 +102,8 @@ namespace hscl = 2; if(dump_large && _frame.height < 400) vscl = 2; + render_video_hud(dscr, _frame, hscl, vscl, 16, 8, 0, dlb, dtb, drb, dbb, waitfn); - struct lua_render_context lrc; - render_queue rq; - lrc.left_gap = dlb; - lrc.right_gap = drb; - lrc.bottom_gap = dbb; - lrc.top_gap = dtb; - lrc.queue = &rq; - lrc.width = _frame.width * hscl; - lrc.height = _frame.height * vscl; - lua_callback_do_video(&lrc); - - vid_dumper->wait_frame_processing(); avi_cscd_dumper::segment_parameters sp; sp.fps_n = fps_n; sp.fps_d = fps_d; @@ -124,22 +112,16 @@ namespace sp.dataformat = avi_cscd_dumper::PIXFMT_XRGB; else sp.dataformat = avi_cscd_dumper::PIXFMT_BGRX; - sp.width = lrc.left_gap + hscl * _frame.width + lrc.right_gap; - sp.height = lrc.top_gap + vscl * _frame.height + lrc.bottom_gap; + sp.width = dscr.width; + sp.height = dscr.height; sp.default_stride = true; sp.stride = 1024; sp.keyframe_distance = _parameters.keyframe_interval; sp.deflate_level = _parameters.compression_level; sp.max_segment_frames = _parameters.max_frames_per_segment; vid_dumper->set_segment_parameters(sp); - dscr.reallocate(lrc.left_gap + hscl * _frame.width + lrc.right_gap, lrc.top_gap + vscl * - _frame.height + lrc.bottom_gap, false); - dscr.set_origin(lrc.left_gap, lrc.top_gap); - dscr.copy_from(_frame, hscl, vscl); - rq.run(dscr); vid_dumper->video(dscr.memory); have_dumped_frame = true; - //vid_dumper->wait_frame_processing(); } void on_sample(short l, short r) @@ -147,7 +129,7 @@ namespace dcounter += soundrate.first; while(dcounter < soundrate.second * audio_record_rate + soundrate.first) { if(have_dumped_frame) - vid_dumper->audio(&l, &r, 1, avi_cscd_dumper::SNDFMT_SIGNED_16NE); + vid_dumper->audio(&l, &r, 1); dcounter += soundrate.first; } dcounter -= (soundrate.second * audio_record_rate + soundrate.first); @@ -166,8 +148,9 @@ namespace { return true; } - private: avi_cscd_dumper* vid_dumper; + private: + sox_dumper* soxdumper; screen dscr; unsigned dcounter; @@ -179,62 +162,11 @@ namespace avi_avsnoop* vid_dumper; - void startdump(const std::string& prefix) + void waitfn() { - if(prefix == "") - throw std::runtime_error("Expected prefix"); - if(vid_dumper) - throw std::runtime_error("AVI(CSCD) dumping already in progress"); - unsigned long level2 = (unsigned long)clevel; - struct avi_info parameters; - parameters.compression_level = (level2 > 9) ? (level2 - 9) : level2; - parameters.audio_sampling_rate = 32000; - parameters.keyframe_interval = (level2 > 9) ? 300 : 1; - parameters.max_frames_per_segment = max_frames_per_segment; - try { - vid_dumper = new avi_avsnoop(prefix, parameters); - } catch(std::bad_alloc& e) { - throw; - } catch(std::exception& e) { - std::ostringstream x; - x << "Error starting AVI(CSCD) dump: " << e.what(); - throw std::runtime_error(x.str()); - } - messages << "Dumping AVI(CSCD) to " << prefix << " at level " << level2 << std::endl; - information_dispatch::do_dumper_update(); + vid_dumper->vid_dumper->wait_frame_processing(); } - void enddump() - { - if(!vid_dumper) - throw std::runtime_error("No AVI(CSCD) video dump in progress"); - try { - vid_dumper->on_dump_end(); - messages << "AVI(CSCD) Dump finished" << std::endl; - } catch(std::bad_alloc& e) { - throw; - } catch(std::exception& e) { - messages << "Error ending AVI(CSCD) dump: " << e.what() << std::endl; - } - delete vid_dumper; - vid_dumper = NULL; - information_dispatch::do_dumper_update(); - } - - function_ptr_command avi_dump("dump-avi", "Start AVI capture", - "Syntax: dump-avi \nStart AVI capture to .\n", - [](const std::string& args) throw(std::bad_alloc, std::runtime_error) { - tokensplitter t(args); - std::string prefix = t.tail(); - startdump(prefix); - }); - - function_ptr_command<> end_avi("end-avi", "End AVI capture", - "Syntax: end-avi\nEnd a AVI capture.\n", - []() throw(std::bad_alloc, std::runtime_error) { - enddump(); - }); - class adv_avi_dumper : public adv_dumper { public: @@ -266,15 +198,47 @@ namespace return (vid_dumper != NULL); } - void start(const std::string& mode, const std::string& targetname) throw(std::bad_alloc, + void start(const std::string& mode, const std::string& prefix) throw(std::bad_alloc, std::runtime_error) { - startdump(targetname); + if(prefix == "") + throw std::runtime_error("Expected prefix"); + if(vid_dumper) + throw std::runtime_error("AVI(CSCD) dumping already in progress"); + unsigned long level2 = (unsigned long)clevel; + struct avi_info parameters; + parameters.compression_level = (level2 > 9) ? (level2 - 9) : level2; + parameters.audio_sampling_rate = 32000; + parameters.keyframe_interval = (level2 > 9) ? 300 : 1; + parameters.max_frames_per_segment = max_frames_per_segment; + try { + vid_dumper = new avi_avsnoop(prefix, parameters); + } catch(std::bad_alloc& e) { + throw; + } catch(std::exception& e) { + std::ostringstream x; + x << "Error starting AVI(CSCD) dump: " << e.what(); + throw std::runtime_error(x.str()); + } + messages << "Dumping AVI(CSCD) to " << prefix << " at level " << level2 << std::endl; + information_dispatch::do_dumper_update(); } void end() throw() { - enddump(); + if(!vid_dumper) + throw std::runtime_error("No AVI(CSCD) video dump in progress"); + try { + vid_dumper->on_dump_end(); + messages << "AVI(CSCD) Dump finished" << std::endl; + } catch(std::bad_alloc& e) { + throw; + } catch(std::exception& e) { + messages << "Error ending AVI(CSCD) dump: " << e.what() << std::endl; + } + delete vid_dumper; + vid_dumper = NULL; + information_dispatch::do_dumper_update(); } } adv; diff --git a/avi/cscd.cpp b/avi/cscd.cpp index 3963c944..f3a775a0 100644 --- a/avi/cscd.cpp +++ b/avi/cscd.cpp @@ -7,6 +7,7 @@ #include #include #include +#include "library/serialization.hpp" #define AVI_CUTOFF_SIZE 2100000000 @@ -26,25 +27,6 @@ namespace } }; - void write8(char* out, unsigned char x) - { - out[0] = x; - } - - void write16(char* out, unsigned x) - { - out[0] = x; - out[1] = x >> 8; - } - - void write32(char* out, unsigned long x) - { - out[0] = x; - out[1] = x >> 8; - out[2] = x >> 16; - out[3] = x >> 24; - } - struct stream_format_base { virtual ~stream_format_base(); @@ -88,19 +70,19 @@ namespace { std::vector buf; buf.resize(size()); - write32(&buf[0], 0x66727473UL); //Type - write32(&buf[4], size() - 8); //Size. - write32(&buf[8], 40); //BITMAPINFOHEADER size. - write32(&buf[12], width); - write32(&buf[16], height); - write16(&buf[20], planes); - write16(&buf[22], bit_count); - write32(&buf[24], compression); - write32(&buf[28], size_image); - write32(&buf[32], resolution_x); - write32(&buf[36], resolution_y); - write32(&buf[40], clr_used); - write32(&buf[44], clr_important); + write32ule(&buf[0], 0x66727473UL); //Type + write32ule(&buf[4], size() - 8); //Size. + write32ule(&buf[8], 40); //BITMAPINFOHEADER size. + write32ule(&buf[12], width); + write32ule(&buf[16], height); + write16ule(&buf[20], planes); + write16ule(&buf[22], bit_count); + write32ule(&buf[24], compression); + write32ule(&buf[28], size_image); + write32ule(&buf[32], resolution_x); + write32ule(&buf[36], resolution_y); + write32ule(&buf[40], clr_used); + write32ule(&buf[44], clr_important); out.write(&buf[0], buf.size()); if(!out) throw std::runtime_error("Can't write strf (video)"); @@ -131,16 +113,16 @@ namespace { std::vector buf; buf.resize(size()); - write32(&buf[0], 0x66727473UL); //Type - write32(&buf[4], size() - 8); //Size. - write16(&buf[8], format_tag); - write16(&buf[10], channels); - write32(&buf[12], samples_per_second); - write32(&buf[16], average_bytes_per_second); - write16(&buf[20], block_align); - write16(&buf[22], bits_per_sample); - write16(&buf[24], 0); //No extension data. - write16(&buf[26], 0); //Pad + write32ule(&buf[0], 0x66727473UL); //Type + write32ule(&buf[4], size() - 8); //Size. + write16ule(&buf[8], format_tag); + write16ule(&buf[10], channels); + write32ule(&buf[12], samples_per_second); + write32ule(&buf[16], average_bytes_per_second); + write16ule(&buf[20], block_align); + write16ule(&buf[22], bits_per_sample); + write16ule(&buf[24], 0); //No extension data. + write16ule(&buf[26], 0); //Pad out.write(&buf[0], buf.size()); if(!out) throw std::runtime_error("Can't write strf (audio)"); @@ -177,25 +159,25 @@ namespace { std::vector buf; buf.resize(size()); - write32(&buf[0], 0x68727473UL); //Type - write32(&buf[4], size() - 8); //Size. - write32(&buf[8], format.type()); - write32(&buf[12], handler); - write32(&buf[16], flags); - write16(&buf[20], priority); - write16(&buf[22], language); - write32(&buf[24], initial_frames); - write32(&buf[28], format.scale()); - write32(&buf[32], format.rate()); - write32(&buf[36], start); - write32(&buf[40], length); - write32(&buf[44], suggested_buffer_size); - write32(&buf[48], quality); - write32(&buf[52], format.sample_size()); - write32(&buf[56], format.rect_left()); - write32(&buf[60], format.rect_top()); - write32(&buf[64], format.rect_right()); - write32(&buf[68], format.rect_bottom()); + write32ule(&buf[0], 0x68727473UL); //Type + write32ule(&buf[4], size() - 8); //Size. + write32ule(&buf[8], format.type()); + write32ule(&buf[12], handler); + write32ule(&buf[16], flags); + write16ule(&buf[20], priority); + write16ule(&buf[22], language); + write32ule(&buf[24], initial_frames); + write32ule(&buf[28], format.scale()); + write32ule(&buf[32], format.rate()); + write32ule(&buf[36], start); + write32ule(&buf[40], length); + write32ule(&buf[44], suggested_buffer_size); + write32ule(&buf[48], quality); + write32ule(&buf[52], format.sample_size()); + write32ule(&buf[56], format.rect_left()); + write32ule(&buf[60], format.rect_top()); + write32ule(&buf[64], format.rect_right()); + write32ule(&buf[68], format.rect_bottom()); out.write(&buf[0], buf.size()); if(!out) throw std::runtime_error("Can't write strh"); @@ -212,9 +194,9 @@ namespace { std::vector buf; buf.resize(12); - write32(&buf[0], 0x5453494CUL); //List. - write32(&buf[4], size() - 8); - write32(&buf[8], 0x6c727473UL); //Type. + write32ule(&buf[0], 0x5453494CUL); //List. + write32ule(&buf[4], size() - 8); + write32ule(&buf[8], 0x6c727473UL); //Type. out.write(&buf[0], buf.size()); if(!out) throw std::runtime_error("Can't write strl"); @@ -237,22 +219,22 @@ namespace { std::vector buf; buf.resize(size()); - write32(&buf[0], 0x68697661); //Type. - write32(&buf[4], size() - 8); - write32(&buf[8], microsec_per_frame); - write32(&buf[12], max_bytes_per_sec); - write32(&buf[16], padding_granularity); - write32(&buf[20], flags); - write32(&buf[24], videotrack.strh.length); - write32(&buf[28], initial_frames); - write32(&buf[32], tracks); - write32(&buf[36], suggested_buffer_size); - write32(&buf[40], videotrack.strf.width); - write32(&buf[44], videotrack.strf.height); - write32(&buf[48], 0); - write32(&buf[52], 0); - write32(&buf[56], 0); - write32(&buf[60], 0); + write32ule(&buf[0], 0x68697661); //Type. + write32ule(&buf[4], size() - 8); + write32ule(&buf[8], microsec_per_frame); + write32ule(&buf[12], max_bytes_per_sec); + write32ule(&buf[16], padding_granularity); + write32ule(&buf[20], flags); + write32ule(&buf[24], videotrack.strh.length); + write32ule(&buf[28], initial_frames); + write32ule(&buf[32], tracks); + write32ule(&buf[36], suggested_buffer_size); + write32ule(&buf[40], videotrack.strf.width); + write32ule(&buf[44], videotrack.strf.height); + write32ule(&buf[48], 0); + write32ule(&buf[52], 0); + write32ule(&buf[56], 0); + write32ule(&buf[60], 0); out.write(&buf[0], buf.size()); if(!out) throw std::runtime_error("Can't write avih"); @@ -269,9 +251,9 @@ namespace { std::vector buf; buf.resize(12); - write32(&buf[0], 0x5453494CUL); //List. - write32(&buf[4], size() - 8); - write32(&buf[8], 0x6c726468UL); //Type. + write32ule(&buf[0], 0x5453494CUL); //List. + write32ule(&buf[4], size() - 8); + write32ule(&buf[8], 0x6c726468UL); //Type. out.write(&buf[0], buf.size()); if(!out) throw std::runtime_error("Can't write hdrl"); @@ -301,9 +283,9 @@ namespace { std::vector buf; buf.resize(12); - write32(&buf[0], 0x5453494CUL); //List. - write32(&buf[4], size() - 8); - write32(&buf[8], 0x69766f6d); //Type. + write32ule(&buf[0], 0x5453494CUL); //List. + write32ule(&buf[4], size() - 8); + write32ule(&buf[8], 0x69766f6d); //Type. out.write(&buf[0], buf.size()); out.seekp(payload_size, std::ios_base::cur); if(!out) @@ -331,10 +313,10 @@ namespace { std::vector buf; buf.resize(16); - write32(&buf[0], chunk_type); - write32(&buf[4], flags); - write32(&buf[8], offset); - write32(&buf[12], length); + write32ule(&buf[0], chunk_type); + write32ule(&buf[4], flags); + write32ule(&buf[8], offset); + write32ule(&buf[12], length); out.write(&buf[0], buf.size()); if(!out) throw std::runtime_error("Can't write index entry"); @@ -359,8 +341,8 @@ namespace { std::vector buf; buf.resize(8); - write32(&buf[0], 0x31786469UL); //Type. - write32(&buf[4], size() - 8); + write32ule(&buf[0], 0x31786469UL); //Type. + write32ule(&buf[4], size() - 8); out.write(&buf[0], buf.size()); if(!out) throw std::runtime_error("Can't write idx1"); @@ -369,139 +351,35 @@ namespace } }; - size_t bpp_for_pixtype(enum avi_cscd_dumper::pixelformat pf) - { - switch(pf) { - case avi_cscd_dumper::PIXFMT_RGB15_BE: - case avi_cscd_dumper::PIXFMT_RGB15_LE: - case avi_cscd_dumper::PIXFMT_RGB15_NE: - return 2; - case avi_cscd_dumper::PIXFMT_RGB24: - case avi_cscd_dumper::PIXFMT_BGR24: - return 3; - case avi_cscd_dumper::PIXFMT_RGBX: - case avi_cscd_dumper::PIXFMT_BGRX: - case avi_cscd_dumper::PIXFMT_XRGB: - case avi_cscd_dumper::PIXFMT_XBGR: - return 4; - default: - return 0; - } - } - - size_t bps_for_sndtype(enum avi_cscd_dumper::soundformat sf) - { - switch(sf) { - case avi_cscd_dumper::SNDFMT_SILENCE: - return 0; - case avi_cscd_dumper::SNDFMT_SIGNED_8: - case avi_cscd_dumper::SNDFMT_UNSIGNED_8: - return 1; - case avi_cscd_dumper::SNDFMT_SIGNED_16BE: - case avi_cscd_dumper::SNDFMT_SIGNED_16NE: - case avi_cscd_dumper::SNDFMT_SIGNED_16LE: - case avi_cscd_dumper::SNDFMT_UNSIGNED_16BE: - case avi_cscd_dumper::SNDFMT_UNSIGNED_16NE: - case avi_cscd_dumper::SNDFMT_UNSIGNED_16LE: - return 2; - }; - return 0; - } - - inline unsigned short convert_audio_sample(const char* addr, enum avi_cscd_dumper::soundformat sf) - { - unsigned short a; - unsigned short b; - unsigned short magic = 258; - bool little_endian = (*reinterpret_cast(&magic) == 2); - switch(sf) { - case avi_cscd_dumper::SNDFMT_SILENCE: - return 32768; - case avi_cscd_dumper::SNDFMT_SIGNED_8: - return static_cast((static_cast(*addr) << 8)) + 32768; - case avi_cscd_dumper::SNDFMT_UNSIGNED_8: - return static_cast(static_cast(*addr)) << 8; - case avi_cscd_dumper::SNDFMT_SIGNED_16BE: - a = static_cast(addr[0]); - b = static_cast(addr[1]); - return a * 256 + b + 32768; - case avi_cscd_dumper::SNDFMT_SIGNED_16NE: - a = static_cast(addr[0]); - b = static_cast(addr[1]); - if(little_endian) - return b * 256 + a + 32768; - else - return a * 256 + b + 32768; - case avi_cscd_dumper::SNDFMT_SIGNED_16LE: - a = static_cast(addr[0]); - b = static_cast(addr[1]); - return b * 256 + a + 32768; - case avi_cscd_dumper::SNDFMT_UNSIGNED_16BE: - a = static_cast(addr[0]); - b = static_cast(addr[1]); - return a * 256 + b; - case avi_cscd_dumper::SNDFMT_UNSIGNED_16NE: - a = static_cast(addr[0]); - b = static_cast(addr[1]); - if(little_endian) - return b * 256 + a; - else - return a * 256 + b; - case avi_cscd_dumper::SNDFMT_UNSIGNED_16LE: - a = static_cast(addr[0]); - b = static_cast(addr[1]); - return a * 256 + b; - }; - return 32768; - } - void copy_row(unsigned char* target, const unsigned char* src, unsigned width, enum avi_cscd_dumper::pixelformat pf) { unsigned ewidth = (width + 3) >> 2 << 2; - size_t sbpp = bpp_for_pixtype(pf); - size_t dbpp = (sbpp == 2) ? 2 : 3; - unsigned short magic = 258; - bool little_endian = (*reinterpret_cast(&magic) == 2); for(unsigned i = 0; i < width; i++) { switch(pf) { - case avi_cscd_dumper::PIXFMT_RGB15_BE: - target[dbpp * i + 0] = src[sbpp * i + 1]; - target[dbpp * i + 1] = src[sbpp * i + 0]; - break; - case avi_cscd_dumper::PIXFMT_RGB15_NE: - target[dbpp * i + 0] = src[sbpp * i + (little_endian ? 0 : 1)]; - target[dbpp * i + 1] = src[sbpp * i + (little_endian ? 1 : 0)]; - break; - case avi_cscd_dumper::PIXFMT_RGB15_LE: - target[dbpp * i + 0] = src[sbpp * i + 0]; - target[dbpp * i + 1] = src[sbpp * i + 1]; - break; - case avi_cscd_dumper::PIXFMT_BGR24: case avi_cscd_dumper::PIXFMT_BGRX: - target[dbpp * i + 0] = src[sbpp * i + 0]; - target[dbpp * i + 1] = src[sbpp * i + 1]; - target[dbpp * i + 2] = src[sbpp * i + 2]; + target[3 * i + 0] = src[4 * i + 0]; + target[3 * i + 1] = src[4 * i + 1]; + target[3 * i + 2] = src[4 * i + 2]; break; - case avi_cscd_dumper::PIXFMT_RGB24: case avi_cscd_dumper::PIXFMT_RGBX: - target[dbpp * i + 0] = src[sbpp * i + 2]; - target[dbpp * i + 1] = src[sbpp * i + 1]; - target[dbpp * i + 2] = src[sbpp * i + 0]; + target[3 * i + 0] = src[4 * i + 2]; + target[3 * i + 1] = src[4 * i + 1]; + target[3 * i + 2] = src[4 * i + 0]; break; case avi_cscd_dumper::PIXFMT_XRGB: - target[dbpp * i + 0] = src[sbpp * i + 3]; - target[dbpp * i + 1] = src[sbpp * i + 2]; - target[dbpp * i + 2] = src[sbpp * i + 1]; + target[3 * i + 0] = src[4 * i + 3]; + target[3 * i + 1] = src[4 * i + 2]; + target[3 * i + 2] = src[4 * i + 1]; break; case avi_cscd_dumper::PIXFMT_XBGR: - target[dbpp * i + 0] = src[sbpp * i + 1]; - target[dbpp * i + 1] = src[sbpp * i + 2]; - target[dbpp * i + 2] = src[sbpp * i + 3]; + target[3 * i + 0] = src[4 * i + 1]; + target[3 * i + 1] = src[4 * i + 2]; + target[3 * i + 2] = src[4 * i + 3]; break; } } - memset(target + dbpp * width, 0, dbpp * (ewidth - width)); + memset(target + 3 * width, 0, 3 * (ewidth - width)); } } @@ -516,9 +394,9 @@ struct avi_file_structure { std::vector buf; buf.resize(12); - write32(&buf[0], 0x46464952UL); //RIFF. - write32(&buf[4], size() - 8); - write32(&buf[8], 0x20495641UL); //Type. + write32ule(&buf[0], 0x46464952UL); //RIFF. + write32ule(&buf[4], size() - 8); + write32ule(&buf[8], 0x20495641UL); //Type. out.write(&buf[0], buf.size()); if(!out) throw std::runtime_error("Can't write AVI header"); @@ -550,9 +428,9 @@ struct avi_file_structure namespace { void fill_avi_structure(struct avi_file_structure* avis, unsigned width, unsigned height, unsigned long fps_n, - unsigned long fps_d, int mode, unsigned channels, unsigned long sampling_rate, bool bits16) + unsigned long fps_d, unsigned long sampling_rate) { - avis->hdrl.avih.microsec_per_frame = (Uint64)1000000 * fps_d / fps_n; + avis->hdrl.avih.microsec_per_frame = (uint64_t)1000000 * fps_d / fps_n; avis->hdrl.avih.max_bytes_per_sec = 1000000; avis->hdrl.avih.padding_granularity = 0; avis->hdrl.avih.flags = 2064; @@ -569,9 +447,9 @@ namespace avis->hdrl.videotrack.strf.width = width; avis->hdrl.videotrack.strf.height = height; avis->hdrl.videotrack.strf.planes = 1; - avis->hdrl.videotrack.strf.bit_count = (mode + 1) << 3; + avis->hdrl.videotrack.strf.bit_count = 24; avis->hdrl.videotrack.strf.compression = 0x44435343; - avis->hdrl.videotrack.strf.size_image = (1UL * (mode + 1) * width * height); + avis->hdrl.videotrack.strf.size_image = (3UL * width * height); avis->hdrl.videotrack.strf.resolution_x = 4000; avis->hdrl.videotrack.strf.resolution_y = 4000; avis->hdrl.videotrack.strf.clr_used = 0; @@ -587,12 +465,12 @@ namespace avis->hdrl.audiotrack.strh.suggested_buffer_size = 1000000; avis->hdrl.audiotrack.strh.quality = 9999; avis->hdrl.audiotrack.strf.format_tag = 1; - avis->hdrl.audiotrack.strf.channels = channels; + avis->hdrl.audiotrack.strf.channels = 2; avis->hdrl.audiotrack.strf.samples_per_second = sampling_rate; - avis->hdrl.audiotrack.strf.average_bytes_per_second = sampling_rate * channels * (bits16 ? 2 : 1); - avis->hdrl.audiotrack.strf.block_align = channels * (bits16 ? 2 : 1); - avis->hdrl.audiotrack.strf.bits_per_sample = (bits16 ? 16 : 8); - avis->hdrl.audiotrack.strf.blocksize = channels * (bits16 ? 2 : 1); + avis->hdrl.audiotrack.strf.average_bytes_per_second = sampling_rate * 4; + avis->hdrl.audiotrack.strf.block_align = 4; + avis->hdrl.audiotrack.strf.bits_per_sample = 16; + avis->hdrl.audiotrack.strf.blocksize = 4; } } @@ -602,13 +480,11 @@ avi_cscd_dumper::avi_cscd_dumper(const std::string& prefix, const avi_cscd_dumpe dump_prefix = prefix; if(!global.sampling_rate || global.sampling_rate >= 0xFFFFFFFFUL) throw std::runtime_error("Sound sampling rate invalid"); - if(!global.channel_count || global.channel_count >= 0xFFFFU) - throw std::runtime_error("Sound channel count invalid"); if(!segment.fps_n || segment.fps_n >= 0xFFFFFFFFUL) throw std::runtime_error("FPS numerator invalid"); if(!segment.fps_d || segment.fps_d >= 0xFFFFFFFFUL) throw std::runtime_error("FPS denominator invalid"); - if(!bpp_for_pixtype(segment.dataformat)) + if(segment.dataformat < PIXFMT_RGBX || segment.dataformat > PIXFMT_XBGR) throw std::runtime_error("Pixel format invalid"); if(!segment.width || segment.width > 0xFFFCU) throw std::runtime_error("Width invalid"); @@ -617,8 +493,6 @@ avi_cscd_dumper::avi_cscd_dumper(const std::string& prefix, const avi_cscd_dumpe if(segment.deflate_level > 9) throw std::runtime_error("Invalid deflate level"); gp_sampling_rate = global.sampling_rate; - gp_channel_count = global.channel_count; - gp_audio_16bit = global.audio_16bit; sp_fps_n = segment.fps_n; sp_fps_d = segment.fps_d; sp_dataformat = segment.dataformat; @@ -626,7 +500,7 @@ avi_cscd_dumper::avi_cscd_dumper(const std::string& prefix, const avi_cscd_dumpe sp_height = segment.height; sp_max_segment_frames = segment.max_segment_frames; if(segment.default_stride) - sp_stride = bpp_for_pixtype(segment.dataformat) * segment.width; + sp_stride = 4 * segment.width; else sp_stride = segment.stride; sp_keyframe_distance = segment.keyframe_distance; @@ -687,7 +561,7 @@ void avi_cscd_dumper::set_segment_parameters(const avi_cscd_dumper::segment_para throw std::runtime_error("FPS numerator invalid"); if(!segment.fps_d || segment.fps_d >= 0xFFFFFFFFUL) throw std::runtime_error("FPS denominator invalid"); - if(segment.dataformat < PIXFMT_RGB15_LE || segment.dataformat > PIXFMT_XBGR) + if(segment.dataformat < PIXFMT_RGBX || segment.dataformat > PIXFMT_XBGR) throw std::runtime_error("Pixel format invalid"); if(!segment.width || segment.width > 0xFFFCU) throw std::runtime_error("Width invalid"); @@ -697,7 +571,7 @@ void avi_cscd_dumper::set_segment_parameters(const avi_cscd_dumper::segment_para throw std::runtime_error("Invalid deflate level"); //Switch all parameters that can't be incompatible. if(segment.default_stride) - sp_stride = bpp_for_pixtype(segment.dataformat) * segment.width; + sp_stride = 4 * segment.width; else sp_stride = segment.stride; sp_keyframe_distance = segment.keyframe_distance; @@ -713,10 +587,6 @@ void avi_cscd_dumper::set_segment_parameters(const avi_cscd_dumper::segment_para incompatible = true; if(((sp_height + 3) >> 2) != ((segment.height + 3) >> 2)) incompatible = true; - if(bpp_for_pixtype(sp_dataformat) == 2 && bpp_for_pixtype(segment.dataformat) != 2) - incompatible = true; - if(bpp_for_pixtype(sp_dataformat) != 2 && bpp_for_pixtype(segment.dataformat) == 2) - incompatible = true; if(incompatible) { spn_dataformat = segment.dataformat; @@ -735,23 +605,19 @@ void avi_cscd_dumper::set_segment_parameters(const avi_cscd_dumper::segment_para } } -void avi_cscd_dumper::audio(const void* audio, size_t samples, enum avi_cscd_dumper::soundformat format) - throw(std::bad_alloc, std::runtime_error) +void avi_cscd_dumper::audio(const short* audio, size_t samples) throw(std::bad_alloc, std::runtime_error) { if(exception_error_present) throw std::runtime_error(exception_error); - const char* s = reinterpret_cast(audio); - size_t stride = bps_for_sndtype(format); - size_t mstride = gp_channel_count * stride; //std::cerr << "Locking lock." << std::endl; frame_mutex.lock(); //std::cerr << "Locked lock." << std::endl; for(size_t i = 0; i < samples; i++) { - for(size_t j = 0; j < gp_channel_count; j++) { - unsigned short as = convert_audio_sample(s + mstride * i + stride * j, format); - while(buffered_sound_samples * gp_channel_count + j >= sound_buffer.size()) + for(size_t j = 0; j < 2; j++) { + unsigned short as = static_cast(audio[2 * i + j]) + 32768; + while(buffered_sound_samples * 2 + j >= sound_buffer.size()) sound_buffer.resize(sound_buffer.size() + 128); - sound_buffer[buffered_sound_samples * gp_channel_count + j] = as; + sound_buffer[buffered_sound_samples * 2 + j] = as; } buffered_sound_samples++; } @@ -759,23 +625,18 @@ void avi_cscd_dumper::audio(const void* audio, size_t samples, enum avi_cscd_dum request_flush_buffers(false); } -void avi_cscd_dumper::audio(const void* laudio, const void* raudio, size_t samples, - enum avi_cscd_dumper::soundformat format) throw(std::bad_alloc, std::runtime_error) +void avi_cscd_dumper::audio(const short* laudio, const short* raudio, size_t samples) throw(std::bad_alloc, + std::runtime_error) { if(exception_error_present) throw std::runtime_error(exception_error); - if(gp_channel_count != 2) - throw std::runtime_error("Split-stereo audio only allowed for stereo output"); - const char* l = reinterpret_cast(laudio); - const char* r = reinterpret_cast(raudio); - size_t stride = bps_for_sndtype(format); //std::cerr << "Locking lock." << std::endl; frame_mutex.lock(); //std::cerr << "Locked lock." << std::endl; for(size_t i = 0; i < samples; i++) { - unsigned short ls = convert_audio_sample(l + stride * i, format); - unsigned short rs = convert_audio_sample(r + stride * i, format); - while(buffered_sound_samples * gp_channel_count >= sound_buffer.size()) + unsigned short ls = static_cast(laudio[i]) + 32768; + unsigned short rs = static_cast(raudio[i]) + 32768; + while(buffered_sound_samples * 2 >= sound_buffer.size()) sound_buffer.resize(sound_buffer.size() + 128); sound_buffer[buffered_sound_samples * 2 + 0] = ls; sound_buffer[buffered_sound_samples * 2 + 1] = rs; @@ -798,9 +659,6 @@ void avi_cscd_dumper::video(const void* framedata) throw(std::bad_alloc, std::ru frame_cond.notify_all(); //std::cerr << "Requesting processing of frame" << std::endl; frame_mutex.unlock(); -#ifndef ACTUALLY_USE_THREADS - _video(framedata); -#endif } void avi_cscd_dumper::_video(const void* framedata) @@ -817,7 +675,6 @@ void avi_cscd_dumper::_video(const void* framedata) switch_segments_on_next_frame = false; } frame.compression_level = sp_deflate_level; - frame.mode = (bpp_for_pixtype(sp_dataformat) == 2) ? 1 : 2; frame.fps_d = sp_fps_d; frame.fps_n = sp_fps_n; frame.width = sp_width; @@ -825,8 +682,8 @@ void avi_cscd_dumper::_video(const void* framedata) frame.keyframe = (++frames_since_last_keyframe >= sp_keyframe_distance); if(frame.keyframe) frames_since_last_keyframe = 0; - size_t stride = ((bpp_for_pixtype(sp_dataformat) == 2) ? 2 : 3) * ((sp_width + 3) >> 2 << 2); - size_t srcstride = (bpp_for_pixtype(sp_dataformat)) * sp_width; + size_t stride = 3 * ((sp_width + 3) >> 2 << 2); + size_t srcstride = 4 * sp_width; frame.data.resize(stride * ((sp_height + 3) >> 2 << 2)); if(framedata == NULL) memset(&frame.data[0], 0, frame.data.size()); @@ -863,8 +720,7 @@ void avi_cscd_dumper::end() throw(std::bad_alloc, std::runtime_error) end_segment(); } -size_t avi_cscd_dumper::emit_frame(const std::vector& data, bool keyframe, unsigned level, - unsigned mode) +size_t avi_cscd_dumper::emit_frame(const std::vector& data, bool keyframe, unsigned level) { size_t nsize = data.size(); if(previous_frame.size() != nsize) { @@ -890,45 +746,35 @@ size_t avi_cscd_dumper::emit_frame(const std::vector& data, bool compression_output[1] = '0'; compression_output[2] = 'd'; compression_output[3] = 'b'; //strictly speaking, this is wrong, but FCEUX does this when dumping. - compression_output[4] = (l + 2); - compression_output[5] = (l + 2) >> 8; - compression_output[6] = (l + 2) >> 16; - compression_output[7] = (l + 2) >> 24; + write32ule(&compression_output[4], l + 2); compression_output[8] = (keyframe ? 0x3 : 0x2) | (level << 4); - compression_output[9] = mode << 2; + compression_output[9] = 8; return l + 10; } size_t avi_cscd_dumper::emit_sound(size_t samples) { - size_t packetsize = 8 + samples * gp_channel_count * (gp_audio_16bit ? 2 : 1); - size_t towrite = samples * gp_channel_count; + size_t packetsize = 8 + samples * 4; + size_t towrite = samples * 2; if(packetsize + 3 > compression_output.size()) compression_output.resize(packetsize + 3); compression_output[0] = '0'; compression_output[1] = '1'; compression_output[2] = 'w'; compression_output[3] = 'b'; - compression_output[4] = (packetsize - 8); - compression_output[5] = (packetsize - 8) >> 8; - compression_output[6] = (packetsize - 8) >> 16; - compression_output[7] = (packetsize - 8) >> 24; + write32ule(&compression_output[4], packetsize - 8); size_t itr = 0; umutex_class _frame_mutex(frame_mutex); for(size_t i = 0; i < towrite; i++) { unsigned short sample = 0; - if(itr < buffered_sound_samples * gp_channel_count) + if(itr < buffered_sound_samples * 2) sample = sound_buffer[itr++]; - if(gp_audio_16bit) { - compression_output[8 + 2 * i + 1] = (sample + 32768) >> 8; - compression_output[8 + 2 * i + 0] = (sample + 32768); - } else - compression_output[8 + i] = (sample + 32768) >> 8; + write16ule(&compression_output[8 + 2 * i], sample + 32768); } - if(itr < buffered_sound_samples * gp_channel_count) { - memmove(&sound_buffer[0], &sound_buffer[itr], sizeof(unsigned short) * (buffered_sound_samples * - gp_channel_count - itr)); - buffered_sound_samples -= itr / gp_channel_count; + if(itr < buffered_sound_samples * 2) { + memmove(&sound_buffer[0], &sound_buffer[itr], sizeof(unsigned short) * (buffered_sound_samples * 2 + - itr)); + buffered_sound_samples -= itr / 2; } else buffered_sound_samples = 0; while(packetsize & 3) @@ -947,7 +793,7 @@ void avi_cscd_dumper::start_segment(unsigned major_seg, unsigned minor_seg) throw std::runtime_error("Can't open AVI file"); avifile_structure = new avi_file_structure; fill_avi_structure(avifile_structure, (f.width + 3) >> 2 << 2, (f.height + 3) >> 2 << 2, f.fps_n, - f.fps_d, f.mode, gp_channel_count, gp_sampling_rate, gp_audio_16bit); + f.fps_d, gp_sampling_rate); avifile_structure->start_data(avifile); frame_period_counter = 0; } @@ -998,13 +844,12 @@ void avi_cscd_dumper::write_frame_av(size_t samples) std::vector& data = f.data; bool keyframe = f.keyframe; unsigned level = f.compression_level; - unsigned mode = f.mode; bool force_break = f.forcebreak; size_t size; bool tmp = restart_segment_if_needed(force_break); keyframe = keyframe || tmp; - size = emit_frame(data, keyframe, level, mode); + size = emit_frame(data, keyframe, level); emit_frame_stream(size, keyframe); size = emit_sound(samples); emit_sound_stream(size, samples); @@ -1035,9 +880,9 @@ size_t avi_cscd_dumper::samples_for_next_frame() //The average number of samples per frame needs to be: //samplerate * fps_d / fps_n. struct buffered_frame& f = *frame_buffer.begin(); - unsigned long critical = static_cast(gp_sampling_rate) * f.fps_d % f.fps_n; - unsigned long ret = static_cast(gp_sampling_rate) * f.fps_d / f.fps_n; - if(static_cast(frame_period_counter) * critical % f.fps_n < critical) + unsigned long critical = static_cast(gp_sampling_rate) * f.fps_d % f.fps_n; + unsigned long ret = static_cast(gp_sampling_rate) * f.fps_d / f.fps_n; + if(static_cast(frame_period_counter) * critical % f.fps_n < critical) ret++; return ret; } @@ -1068,9 +913,6 @@ void avi_cscd_dumper::request_flush_buffers(bool forced) frame_cond.notify_all(); //std::cerr << "Requesting buffer flush (" << flush_requested_forced << ")" << std::endl; frame_mutex.unlock(); -#ifndef ACTUALLY_USE_THREADS - flush_buffers(forced); -#endif } bool avi_cscd_dumper::is_frame_processing() throw() diff --git a/avi/cscd.hpp b/avi/cscd.hpp index 1c1cc2ee..4bd56a3a 100644 --- a/avi/cscd.hpp +++ b/avi/cscd.hpp @@ -1,68 +1,30 @@ #ifndef _output_cscd__hpp__included__ #define _output_cscd__hpp__included__ -#if defined(__linux__) && !defined(BOOST_THREADS) && !defined(NO_THREADS) -#define STD_THREADS 1 +#if defined(__linux__) && !defined(BOOST_THREADS) +#define NATIVE_THREADS 1 #endif -#ifdef BOOST_THREADS -#include -#include - -#define ACTUALLY_USE_THREADS -typedef boost::thread thread_class; -typedef boost::condition_variable cv_class; -typedef boost::mutex mutex_class; -typedef boost::unique_lock umutex_class; - -#else -#ifdef STD_THREADS +#ifdef NATIVE_THREADS #include #include #include - -#define ACTUALLY_USE_THREADS typedef std::thread thread_class; typedef std::condition_variable cv_class; typedef std::mutex mutex_class; typedef std::unique_lock umutex_class; - #else - -struct thread_class -{ - template - thread_class(T obj, args a) {} - void join() {} -}; - -typedef struct mutex_class -{ - void lock() {} - void unlock() {} -} umutex_class; - -struct cv_class -{ - void wait(umutex_class& m) {} - void notify_all() {} -}; - -#endif -#endif - -#ifdef __GNUC__ -typedef unsigned long long Uint64; -typedef unsigned long long Sint64; -#elif defined(_MSC_VER) -typedef unsigned __int64 Uint64; -typedef signed __int64 Sint64; -#else -#error "Please define 64bit types." +#include +#include +typedef boost::thread thread_class; +typedef boost::condition_variable cv_class; +typedef boost::mutex mutex_class; +typedef boost::unique_lock umutex_class; #endif +#include #include #include #include @@ -86,14 +48,6 @@ public: * Sound sampling rate. */ unsigned long sampling_rate; -/** - * Sound channel count. - */ - unsigned channel_count; -/** - * 16-bit audio flag. - */ - bool audio_16bit; }; /** @@ -101,33 +55,12 @@ public: */ enum pixelformat { - PIXFMT_RGB15_LE, /* Little-endian 15-bit RGB. */ - PIXFMT_RGB15_BE, /* Big-endian 15-bit RGB. */ - PIXFMT_RGB15_NE, /* Native-endian 15-bit RGB. */ - PIXFMT_RGB24, /* 24-bit RGB, RGB order. */ - PIXFMT_BGR24, /* 24-bit RGB, BGR order. */ PIXFMT_RGBX, /* 32-bit RGB, RGBx order. */ PIXFMT_BGRX, /* 32-bit RGB, BGRx order. */ PIXFMT_XRGB, /* 32-bit RGB, BGRx order. */ PIXFMT_XBGR /* 32-bit RGB, xBGR order. */ }; -/** - * Sound formats. - */ - enum soundformat - { - SNDFMT_SILENCE, /* No data, silence. */ - SNDFMT_SIGNED_8, /* 8-bit signed. */ - SNDFMT_UNSIGNED_8, /* 8-bit unsigned. */ - SNDFMT_SIGNED_16LE, /* 16-bit signed little-endian. */ - SNDFMT_UNSIGNED_16LE, /* 16-bit unsigned little-endian. */ - SNDFMT_SIGNED_16BE, /* 16-bit signed big-endian. */ - SNDFMT_UNSIGNED_16BE, /* 16-bit unsigned big-endian. */ - SNDFMT_SIGNED_16NE, /* 16-bit signed native-endian. */ - SNDFMT_UNSIGNED_16NE /* 16-bit unsigned native-endian. */ - }; - /** * AVI per-segment parameters */ @@ -242,12 +175,10 @@ public: * * Parameter audio: Audio, first to last channel, first to last sample order. * Parameter samples: Number of samples to add. Note: Number of samples, not number of channels*samples. - * Parameter format: Sound sample format. * Throws std::bad_alloc: Not enough memory. * Throws std::runtime_error: Can't write sound data. */ - void audio(const void* audio, size_t samples, enum soundformat format) throw(std::bad_alloc, - std::runtime_error); + void audio(const short* audio, size_t samples) throw(std::bad_alloc, std::runtime_error); /** * Dump audio (stereo). @@ -255,12 +186,10 @@ public: * Parameter laudio: Audio for left channel. * Parameter raudio: Audio for right channel. * Parameter samples: Number of samples to add. - * Parameter format: Sound sample format. * Throws std::bad_alloc: Not enough memory. * Throws std::runtime_error: Can't write sound data or not stereo sound. */ - void audio(const void* laudio, const void* raudio, size_t samples, enum soundformat format) - throw(std::bad_alloc, std::runtime_error); + void audio(const short* laudio, const short* raudio, size_t samples) throw(std::bad_alloc, std::runtime_error); /** * Signal end of dump. @@ -279,7 +208,6 @@ private: std::vector data; bool keyframe; unsigned compression_level; - unsigned mode; //1 => 15 bit, 2 => 24 bit, 3 => 32 bit. bool forcebreak; unsigned long fps_n; unsigned long fps_d; @@ -290,8 +218,6 @@ private: //Global parameters. std::string dump_prefix; unsigned long gp_sampling_rate; - unsigned gp_channel_count; - bool gp_audio_16bit; //Current segment parameters. unsigned long sp_fps_n; unsigned long sp_fps_d; @@ -328,7 +254,7 @@ private: std::list frame_buffer; //Fills compression_output with frame/sound packet and returns the total size. - size_t emit_frame(const std::vector& data, bool keyframe, unsigned level, unsigned mode); + size_t emit_frame(const std::vector& data, bool keyframe, unsigned level); size_t emit_sound(size_t samples); //Write a frame/sound packet in compression_output into stream. void emit_frame_stream(size_t size, bool keyframe); diff --git a/avi/jmd.cpp b/avi/jmd.cpp index b6e05293..de80527b 100644 --- a/avi/jmd.cpp +++ b/avi/jmd.cpp @@ -1,240 +1,382 @@ -#include "jmd.hpp" -#include -#include -#include +#include "core/advdumper.hpp" +#include "core/dispatch.hpp" +#include "core/settings.hpp" +#include "library/serialization.hpp" + +#include +#include #include - -namespace -{ - void write32(char* x, uint64_t v) - { - x[0] = (v >> 24); - x[1] = (v >> 16); - x[2] = (v >> 8); - x[3] = v; - } -} - -void jmd_dumper::video(uint64_t ts, uint32_t* memory, uint32_t width, uint32_t height) -{ - frame_buffer f; - f.ts = ts; - size_t fsize = 0; - //We'll compress the frame here. - f.data = compress_frame(memory, width, height); - frames.push_back(f); - flush_buffers(false); -} - -void jmd_dumper::audio(uint64_t ts, short l, short r) -{ - sample_buffer s; - s.ts = ts; - s.l = l; - s.r = r; - samples.push_back(s); - flush_buffers(false); -} - -jmd_dumper::jmd_dumper(const std::string& filename, unsigned level) -{ - clevel = level; - jmd.open(filename.c_str(), std::ios::out | std::ios::binary); - if(!jmd) - throw std::runtime_error("Can't open output JMD file."); - last_written_ts = 0; - //Write the segment tables. - //Stream #0 is video. - //Stream #1 is PCM audio. - //Stream #2 is Gameinfo. - //Stream #3 is Dummy. - char header[] = { - /* Magic */ - -1, -1, 0x4A, 0x50, 0x43, 0x52, 0x52, 0x4D, 0x55, 0x4C, 0x54, 0x49, 0x44, 0x55, 0x4D, 0x50, - /* Channel count. */ - 0x00, 0x04, - /* Video channel header. */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 'v', 'i', - /* Audio channel header. */ - 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 'a', 'u', - /* Gameinfo channel header. */ - 0x00, 0x02, 0x00, 0x05, 0x00, 0x02, 'g', 'i', - /* Dummy channel header. */ - 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 'd', 'u' - }; - jmd.write(header, sizeof(header)); - if(!jmd) - throw std::runtime_error("Can't write JMD header and segment table"); -} - -jmd_dumper::~jmd_dumper() -{ - try { - end(last_written_ts); - } catch(...) { - } -} - -void jmd_dumper::end(uint64_t ts) -{ - flush_buffers(true); - if(last_written_ts > ts) { - jmd.close(); - return; - } - char dummypacket[8] = {0x00, 0x03}; - write32(dummypacket + 2, ts - last_written_ts); - last_written_ts = ts; - jmd.write(dummypacket, sizeof(dummypacket)); - if(!jmd) - throw std::runtime_error("Can't write JMD ending dummy packet"); - jmd.close(); -} - -void jmd_dumper::gameinfo(const std::string& gamename, const std::string& authors, uint64_t gametime, - uint64_t rerecords) -{ - //FIXME: Implement this. -} - -void jmd_dumper::flush_buffers(bool force) -{ - while(!frames.empty() || !samples.empty()) { - if(frames.empty() || samples.empty()) { - if(!force) - return; - else if(!frames.empty()) { - frame_buffer& f = frames.front(); - flush_frame(f); - frames.pop_front(); - } else if(!samples.empty()) { - sample_buffer& s = samples.front(); - flush_sample(s); - samples.pop_front(); - } - continue; - } - frame_buffer& f = frames.front(); - sample_buffer& s = samples.front(); - if(f.ts <= s.ts) { - flush_frame(f); - frames.pop_front(); - } else { - flush_sample(s); - samples.pop_front(); - } - } -} - -void jmd_dumper::flush_frame(frame_buffer& f) -{ - char videopacketh[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; - write32(videopacketh + 2, f.ts - last_written_ts); - last_written_ts = f.ts; - unsigned lneed = 0; - if(f.data.size() >= (1ULL << 63)) - videopacketh[7 + lneed++] = 0x80 | ((f.data.size() >> 63) & 0x7F); - if(f.data.size() >= (1ULL << 56)) - videopacketh[7 + lneed++] = 0x80 | ((f.data.size() >> 56) & 0x7F); - if(f.data.size() >= (1ULL << 49)) - videopacketh[7 + lneed++] = 0x80 | ((f.data.size() >> 49) & 0x7F); - if(f.data.size() >= (1ULL << 42)) - videopacketh[7 + lneed++] = 0x80 | ((f.data.size() >> 42) & 0x7F); - if(f.data.size() >= (1ULL << 35)) - videopacketh[7 + lneed++] = 0x80 | ((f.data.size() >> 35) & 0x7F); - if(f.data.size() >= (1ULL << 28)) - videopacketh[7 + lneed++] = 0x80 | ((f.data.size() >> 28) & 0x7F); - if(f.data.size() >= (1ULL << 21)) - videopacketh[7 + lneed++] = 0x80 | ((f.data.size() >> 21) & 0x7F); - if(f.data.size() >= (1ULL << 14)) - videopacketh[7 + lneed++] = 0x80 | ((f.data.size() >> 14) & 0x7F); - if(f.data.size() >= (1ULL << 7)) - videopacketh[7 + lneed++] = 0x80 | ((f.data.size() >> 7) & 0x7F); - videopacketh[7 + lneed++] = (f.data.size() & 0x7F); - - jmd.write(videopacketh, 7 + lneed); - if(!jmd) - throw std::runtime_error("Can't write JMD video packet header"); - if(f.data.size() > 0) - jmd.write(&f.data[0], f.data.size()); - if(!jmd) - throw std::runtime_error("Can't write JMD video packet body"); -} - -void jmd_dumper::flush_sample(sample_buffer& s) -{ - char soundpacket[12] = {0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04}; - write32(soundpacket + 2, s.ts - last_written_ts); - last_written_ts = s.ts; - soundpacket[8] = (s.l >> 8) & 0xFF; - soundpacket[9] = s.l & 0xFF; - soundpacket[10] = (s.r >> 8) & 0xFF; - soundpacket[11] = s.r & 0xFF; - jmd.write(soundpacket, sizeof(soundpacket)); - if(!jmd) - throw std::runtime_error("Can't write JMD sound packet"); -} - +#include +#include +#include +#include #define INBUF_PIXELS 4096 #define OUTBUF_ADVANCE 4096 -std::vector jmd_dumper::compress_frame(uint32_t* memory, uint32_t width, uint32_t height) + +namespace { - std::vector ret; - z_stream stream; - memset(&stream, 0, sizeof(stream)); - if(deflateInit(&stream, clevel) != Z_OK) - throw std::runtime_error("Can't initialize zlib stream"); + numeric_setting clevel("jmd-compression", 0, 9, 7); - size_t usize = 4; - ret.resize(4); - ret[0] = (width >> 8); - ret[1] = width; - ret[2] = (height >> 8); - ret[3] = height; - uint8_t input_buffer[4 * INBUF_PIXELS]; - size_t ptr = 0; - size_t pixels = static_cast(width) * height; - bool input_clear = true; - bool flushed = false; - size_t bsize = 0; - uint32_t _magic = 0x18100800; - const uint8_t* magic = reinterpret_cast(&magic); - while(1) { - if(input_clear) { - size_t pixel = ptr; - for(unsigned i = 0; i < INBUF_PIXELS && pixel < pixels; i++, pixel++) { - input_buffer[4 * i + 0] = memory[pixel]; - input_buffer[4 * i + 1] = memory[pixel] >> 8; - input_buffer[4 * i + 2] = memory[pixel] >> 16; - input_buffer[4 * i + 3] = 0; + class jmd_avsnoop : public information_dispatch + { + public: + jmd_avsnoop(const std::string& filename) throw(std::bad_alloc) + : information_dispatch("dump-jmd") + { + enable_send_sound(); + complevel = clevel; + jmd.open(filename.c_str(), std::ios::out | std::ios::binary); + if(!jmd) + throw std::runtime_error("Can't open output JMD file."); + last_written_ts = 0; + //Write the segment tables. + //Stream #0 is video. + //Stream #1 is PCM audio. + //Stream #2 is Gameinfo. + //Stream #3 is Dummy. + char header[] = { + /* Magic */ + -1, -1, 0x4A, 0x50, 0x43, 0x52, 0x52, 0x4D, 0x55, 0x4C, 0x54, 0x49, 0x44, 0x55, 0x4D, + 0x50, + /* Channel count. */ + 0x00, 0x04, + /* Video channel header. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 'v', 'i', + /* Audio channel header. */ + 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 'a', 'u', + /* Gameinfo channel header. */ + 0x00, 0x02, 0x00, 0x05, 0x00, 0x02, 'g', 'i', + /* Dummy channel header. */ + 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 'd', 'u' + }; + jmd.write(header, sizeof(header)); + if(!jmd) + throw std::runtime_error("Can't write JMD header and segment table"); + have_dumped_frame = false; + audio_w = 0; + audio_n = 0; + video_w = 0; + video_n = 0; + maxtc = 0; + soundrate = get_sound_rate(); + try { + on_gameinfo(get_gameinfo()); + } catch(std::exception& e) { + messages << "Can't write gameinfo: " << e.what() << std::endl; } - bsize = pixel - ptr; - ptr = pixel; - input_clear = false; - //Now the input data to compress is in input_buffer, bsize elements. - stream.next_in = reinterpret_cast(input_buffer); - stream.avail_in = 4 * bsize; } - if(!stream.avail_out) { - if(flushed) - usize += (OUTBUF_ADVANCE - stream.avail_out); - flushed = true; - ret.resize(usize + OUTBUF_ADVANCE); - stream.next_out = reinterpret_cast(&ret[usize]); - stream.avail_out = OUTBUF_ADVANCE; - } - int r = deflate(&stream, (ptr == pixels) ? Z_FINISH : 0); - if(r == Z_STREAM_END) - break; - if(r != Z_OK) - throw std::runtime_error("Can't deflate data"); - if(!stream.avail_in) - input_clear = true; - } - usize += (OUTBUF_ADVANCE - stream.avail_out); - deflateEnd(&stream); - ret.resize(usize); - return ret; + ~jmd_avsnoop() throw() + { + try { + on_dump_end(); + } catch(...) { + } + } + + void on_frame(struct lcscreen& _frame, uint32_t fps_n, uint32_t fps_d) + { + render_video_hud(dscr, _frame, 1, 1, 0, 8, 16, 0, 0, 0, 0, NULL); + frame_buffer f; + f.ts = get_next_video_ts(fps_n, fps_d); + size_t fsize = 0; + //We'll compress the frame here. + f.data = compress_frame(dscr.memory, dscr.width, dscr.height); + frames.push_back(f); + flush_buffers(false); + have_dumped_frame = true; + } + + void on_sample(short l, short r) + { + uint64_t ts = get_next_audio_ts(); + if(have_dumped_frame) { + sample_buffer s; + s.ts = ts; + s.l = l; + s.r = r; + samples.push_back(s); + flush_buffers(false); + } + } + + void on_dump_end() + { + flush_buffers(true); + if(last_written_ts > maxtc) { + jmd.close(); + return; + } + char dummypacket[8] = {0x00, 0x03}; + write32ube(dummypacket + 2, maxtc - last_written_ts); + last_written_ts = maxtc; + jmd.write(dummypacket, sizeof(dummypacket)); + if(!jmd) + throw std::runtime_error("Can't write JMD ending dummy packet"); + jmd.close(); + } + + void on_gameinfo(const struct gameinfo_struct& gi) + { + std::string authstr; + for(size_t i = 0; i < gi.get_author_count(); i++) { + if(i != 0) + authstr = authstr + ", "; + authstr = authstr + gi.get_author_short(i); + } + //TODO: Implement + } + + bool get_dumper_flag() throw() + { + return true; + } + private: + uint64_t get_next_video_ts(uint32_t fps_n, uint32_t fps_d) + { + uint64_t ret = video_w; + video_w += (1000000000ULL * fps_d) / fps_n; + video_n += (1000000000ULL * fps_d) % fps_n; + if(video_n >= fps_n) { + video_n -= fps_n; + video_w++; + } + maxtc = (ret > maxtc) ? ret : maxtc; + return ret; + } + + uint64_t get_next_audio_ts() + { + uint64_t ret = audio_w; + audio_w += (1000000000ULL * soundrate.second) / soundrate.first; + audio_n += (1000000000ULL * soundrate.second) % soundrate.first; + if(audio_n >= soundrate.first) { + audio_n -= soundrate.first; + audio_w++; + } + maxtc = (ret > maxtc) ? ret : maxtc; + return ret; + } + + screen dscr; + unsigned dcounter; + bool have_dumped_frame; + uint64_t audio_w; + uint64_t audio_n; + uint64_t video_w; + uint64_t video_n; + uint64_t maxtc; + std::pair soundrate; + struct frame_buffer + { + uint64_t ts; + std::vector data; + }; + struct sample_buffer + { + uint64_t ts; + short l; + short r; + }; + + std::deque frames; + std::deque samples; + + std::vector compress_frame(uint32_t* memory, uint32_t width, uint32_t height) + { + std::vector ret; + z_stream stream; + memset(&stream, 0, sizeof(stream)); + if(deflateInit(&stream, complevel) != Z_OK) + throw std::runtime_error("Can't initialize zlib stream"); + + size_t usize = 4; + ret.resize(4); + write16ube(&ret[0], width); + write16ube(&ret[2], height); + uint8_t input_buffer[4 * INBUF_PIXELS]; + size_t ptr = 0; + size_t pixels = static_cast(width) * height; + bool input_clear = true; + bool flushed = false; + size_t bsize = 0; + while(1) { + if(input_clear) { + size_t pixel = ptr; + for(unsigned i = 0; i < INBUF_PIXELS && pixel < pixels; i++, pixel++) + write32ule(input_buffer + (4 * i), memory[pixel]); + bsize = pixel - ptr; + ptr = pixel; + input_clear = false; + //Now the input data to compress is in input_buffer, bsize elements. + stream.next_in = reinterpret_cast(input_buffer); + stream.avail_in = 4 * bsize; + } + if(!stream.avail_out) { + if(flushed) + usize += (OUTBUF_ADVANCE - stream.avail_out); + flushed = true; + ret.resize(usize + OUTBUF_ADVANCE); + stream.next_out = reinterpret_cast(&ret[usize]); + stream.avail_out = OUTBUF_ADVANCE; + } + int r = deflate(&stream, (ptr == pixels) ? Z_FINISH : 0); + if(r == Z_STREAM_END) + break; + if(r != Z_OK) + throw std::runtime_error("Can't deflate data"); + if(!stream.avail_in) + input_clear = true; + } + usize += (OUTBUF_ADVANCE - stream.avail_out); + deflateEnd(&stream); + + ret.resize(usize); + return ret; + } + + void flush_buffers(bool force) + { + while(!frames.empty() || !samples.empty()) { + if(frames.empty() || samples.empty()) { + if(!force) + return; + else if(!frames.empty()) { + frame_buffer& f = frames.front(); + flush_frame(f); + frames.pop_front(); + } else if(!samples.empty()) { + sample_buffer& s = samples.front(); + flush_sample(s); + samples.pop_front(); + } + continue; + } + frame_buffer& f = frames.front(); + sample_buffer& s = samples.front(); + if(f.ts <= s.ts) { + flush_frame(f); + frames.pop_front(); + } else { + flush_sample(s); + samples.pop_front(); + } + } + } + + void flush_frame(frame_buffer& f) + { + //Channel 0, minor 1. + char videopacketh[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; + write32ube(videopacketh + 2, f.ts - last_written_ts); + last_written_ts = f.ts; + unsigned lneed = 0; + uint64_t datasize = f.data.size(); //Possibly upcast to avoid warnings. + for(unsigned shift = 63; shift > 0; shift -= 7) + if(datasize >= (1ULL << shift)) + videopacketh[7 + lneed++] = 0x80 | ((datasize >> shift) & 0x7F); + videopacketh[7 + lneed++] = (datasize & 0x7F); + + jmd.write(videopacketh, 7 + lneed); + if(!jmd) + throw std::runtime_error("Can't write JMD video packet header"); + if(datasize > 0) + jmd.write(&f.data[0], datasize); + if(!jmd) + throw std::runtime_error("Can't write JMD video packet body"); + } + + void flush_sample(sample_buffer& s) + { + //Channel 1, minor 1, payload 4. + char soundpacket[12] = {0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04}; + write32ube(soundpacket + 2, s.ts - last_written_ts); + last_written_ts = s.ts; + write16sbe(soundpacket + 8, s.l); + write16sbe(soundpacket + 10, s.r); + jmd.write(soundpacket, sizeof(soundpacket)); + if(!jmd) + throw std::runtime_error("Can't write JMD sound packet"); + } + + std::ofstream jmd; + uint64_t last_written_ts; + unsigned complevel; + }; + + jmd_avsnoop* vid_dumper; + + class adv_jmd_dumper : public adv_dumper + { + public: + adv_jmd_dumper() : adv_dumper("INTERNAL-JMD") {information_dispatch::do_dumper_update(); } + ~adv_jmd_dumper() throw(); + std::set list_submodes() throw(std::bad_alloc) + { + std::set x; + return x; + } + + bool wants_prefix(const std::string& mode) throw() + { + return false; + } + + std::string name() throw(std::bad_alloc) + { + return "JMD"; + } + + std::string modename(const std::string& mode) throw(std::bad_alloc) + { + return ""; + } + + bool busy() + { + return (vid_dumper != NULL); + } + + void start(const std::string& mode, const std::string& prefix) throw(std::bad_alloc, + std::runtime_error) + { + if(prefix == "") + throw std::runtime_error("Expected filename"); + if(vid_dumper) + throw std::runtime_error("JMD dumping already in progress"); + try { + vid_dumper = new jmd_avsnoop(prefix); + } catch(std::bad_alloc& e) { + throw; + } catch(std::exception& e) { + std::ostringstream x; + x << "Error starting JMD dump: " << e.what(); + throw std::runtime_error(x.str()); + } + messages << "Dumping to " << prefix << " at level " << clevel << std::endl; + information_dispatch::do_dumper_update(); + } + + void end() throw() + { + if(!vid_dumper) + throw std::runtime_error("No JMD video dump in progress"); + try { + vid_dumper->on_dump_end(); + messages << "JMD Dump finished" << std::endl; + } catch(std::bad_alloc& e) { + throw; + } catch(std::exception& e) { + messages << "Error ending JMD dump: " << e.what() << std::endl; + } + delete vid_dumper; + vid_dumper = NULL; + information_dispatch::do_dumper_update(); + } + } adv; + + adv_jmd_dumper::~adv_jmd_dumper() throw() + { + } } diff --git a/avi/jmd.hpp b/avi/jmd.hpp deleted file mode 100644 index 77d9351a..00000000 --- a/avi/jmd.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef _jmd__hpp__included__ -#define _jmd__hpp__included__ - -#include -#include -#include -#include -#include -#include - -class jmd_dumper -{ -public: - jmd_dumper(const std::string& filename, unsigned level); - ~jmd_dumper(); - void video(uint64_t ts, uint32_t* memory, uint32_t width, uint32_t height); - void audio(uint64_t ts, short l, short r); - void gameinfo(const std::string& gamename, const std::string& authors, uint64_t gametime, uint64_t rerecords); - void end(uint64_t ts); -private: - struct frame_buffer - { - uint64_t ts; - std::vector data; - }; - struct sample_buffer - { - uint64_t ts; - short l; - short r; - }; - - std::deque frames; - std::deque samples; - - std::vector compress_frame(uint32_t* memory, uint32_t width, uint32_t height); - void flush_buffers(bool force); - void flush_frame(frame_buffer& f); - void flush_sample(sample_buffer& s); - - std::ofstream jmd; - uint64_t last_written_ts; - unsigned clevel; -}; - -#endif diff --git a/avi/raw.cpp b/avi/raw.cpp new file mode 100644 index 00000000..1310ea02 --- /dev/null +++ b/avi/raw.cpp @@ -0,0 +1,153 @@ +#include "core/advdumper.hpp" +#include "core/dispatch.hpp" +#include "library/serialization.hpp" + +#include +#include +#include +#include +#include +#include + +namespace +{ + class raw_avsnoop : public information_dispatch + { + public: + raw_avsnoop(const std::string& prefix) throw(std::bad_alloc) + : information_dispatch("dump-raw") + { + enable_send_sound(); + video = new std::ofstream(prefix + ".video", std::ios::out | std::ios::binary); + audio = new std::ofstream(prefix + ".audio", std::ios::out | std::ios::binary); + if(!*video || !*audio) + throw std::runtime_error("Can't open output files"); + have_dumped_frame = false; + } + + ~raw_avsnoop() throw() + { + delete video; + delete audio; + } + + void on_frame(struct lcscreen& _frame, uint32_t fps_n, uint32_t fps_d) + { + if(!video) + return; + unsigned magic = 0x18100800U; + unsigned r = (reinterpret_cast(&magic))[2]; + unsigned g = (reinterpret_cast(&magic))[1]; + unsigned b = (reinterpret_cast(&magic))[0]; + uint32_t hscl = (_frame.width < 400) ? 2 : 1; + uint32_t vscl = (_frame.height < 400) ? 2 : 1; + render_video_hud(dscr, _frame, hscl, vscl, r, g, b, 0, 0, 0, 0, NULL); + for(size_t i = 0; i < dscr.height; i++) + video->write(reinterpret_cast(dscr.rowptr(i)), 4 * dscr.width); + have_dumped_frame = true; + } + + void on_sample(short l, short r) + { + if(have_dumped_frame && audio) { + char buffer[4]; + write16sbe(buffer + 0, l); + write16sbe(buffer + 2, r); + audio->write(buffer, 4); + } + } + + void on_dump_end() + { + delete video; + delete audio; + video = NULL; + audio = NULL; + } + + bool get_dumper_flag() throw() + { + return true; + } + private: + std::ofstream* audio; + std::ofstream* video; + bool have_dumped_frame; + struct screen dscr; + }; + + raw_avsnoop* vid_dumper; + + class adv_raw_dumper : public adv_dumper + { + public: + adv_raw_dumper() : adv_dumper("INTERNAL-RAW") {information_dispatch::do_dumper_update(); } + ~adv_raw_dumper() throw(); + std::set list_submodes() throw(std::bad_alloc) + { + std::set x; + return x; + } + + bool wants_prefix(const std::string& mode) throw() + { + return true; + } + + std::string name() throw(std::bad_alloc) + { + return "RAW"; + } + + std::string modename(const std::string& mode) throw(std::bad_alloc) + { + return ""; + } + + bool busy() + { + return (vid_dumper != NULL); + } + + void start(const std::string& mode, const std::string& prefix) throw(std::bad_alloc, + std::runtime_error) + { + if(prefix == "") + throw std::runtime_error("Expected prefix"); + if(vid_dumper) + throw std::runtime_error("RAW dumping already in progress"); + try { + vid_dumper = new raw_avsnoop(prefix); + } catch(std::bad_alloc& e) { + throw; + } catch(std::exception& e) { + std::ostringstream x; + x << "Error starting RAW dump: " << e.what(); + throw std::runtime_error(x.str()); + } + messages << "Dumping to " << prefix << std::endl; + information_dispatch::do_dumper_update(); + } + + void end() throw() + { + if(!vid_dumper) + throw std::runtime_error("No RAW video dump in progress"); + try { + vid_dumper->on_dump_end(); + messages << "RAW Dump finished" << std::endl; + } catch(std::bad_alloc& e) { + throw; + } catch(std::exception& e) { + messages << "Error ending RAW dump: " << e.what() << std::endl; + } + delete vid_dumper; + vid_dumper = NULL; + information_dispatch::do_dumper_update(); + } + } adv; + + adv_raw_dumper::~adv_raw_dumper() throw() + { + } +} diff --git a/avi/sdmp.cpp b/avi/sdmp.cpp index 3e8e0c30..1ae5c33c 100644 --- a/avi/sdmp.cpp +++ b/avi/sdmp.cpp @@ -1,101 +1,205 @@ #include "lsnes.hpp" #include +#include "core/advdumper.hpp" +#include "core/dispatch.hpp" +#include "library/serialization.hpp" -#include "sdmp.hpp" - -#include #include +#include +#include +#include +#include +#include +#include #include #define CUTOFF 2100000000 +#define SDUMP_FLAG_HIRES 1 +#define SDUMP_FLAG_INTERLACED 2 +#define SDUMP_FLAG_OVERSCAN 4 +#define SDUMP_FLAG_PAL 8 -sdump_dumper::sdump_dumper(const std::string& prefix, bool ss) +namespace { - oprefix = prefix; - sdump_ss = ss; - ssize = 0; - next_seq = 0; - sdump_iopen = false; -} - -sdump_dumper::~sdump_dumper() throw() -{ - try { - end(); - } catch(...) { - } -} - -void sdump_dumper::frame(const uint32_t* buffer, unsigned flags) -{ - flags &= 0xF; - unsigned char tbuffer[2049]; - if(!sdump_iopen || (ssize > CUTOFF && !sdump_ss)) { - std::cerr << "Starting new segment" << std::endl; - if(sdump_iopen) - out.close(); - std::ostringstream str; - if(sdump_ss) - str << oprefix; - else - str << oprefix << "_" << std::setw(4) << std::setfill('0') << (next_seq++) << ".sdmp"; - std::string str2 = str.str(); - out.open(str2.c_str(), std::ios::out | std::ios::binary); - if(!out) - throw std::runtime_error("Failed to open '" + str2 + "'"); - sdump_iopen = true; - tbuffer[0] = 'S'; - tbuffer[1] = 'D'; - tbuffer[2] = 'M'; - tbuffer[3] = 'P'; - uint32_t apufreq = SNES::system.apu_frequency(); - uint32_t cpufreq = SNES::system.cpu_frequency(); - tbuffer[4] = cpufreq >> 24; - tbuffer[5] = cpufreq >> 16; - tbuffer[6] = cpufreq >> 8; - tbuffer[7] = cpufreq; - tbuffer[8] = apufreq >> 24; - tbuffer[9] = apufreq >> 16; - tbuffer[10] = apufreq >> 8; - tbuffer[11] = apufreq; - out.write(reinterpret_cast(tbuffer), 12); - if(!out) - throw std::runtime_error("Failed to write header to '" + str2 + "'"); - ssize = 12; - } - tbuffer[0] = flags; - for(unsigned i = 0; i < 512; i++) { - for(unsigned j = 0; j < 512; j++) { - tbuffer[4 * j + 1] = buffer[512 * i + j] >> 24; - tbuffer[4 * j + 2] = buffer[512 * i + j] >> 16; - tbuffer[4 * j + 3] = buffer[512 * i + j] >> 8; - tbuffer[4 * j + 4] = buffer[512 * i + j]; + class sdmp_avsnoop : public information_dispatch + { + public: + sdmp_avsnoop(const std::string& prefix, bool ssflag) throw(std::bad_alloc) + : information_dispatch("dump-sdmp") + { + enable_send_sound(); + oprefix = prefix; + sdump_ss = ssflag; + ssize = 0; + next_seq = 0; + sdump_iopen = false; } - out.write(reinterpret_cast(tbuffer + (i ? 1 : 0)), i ? 2048 : 2049); + + ~sdmp_avsnoop() throw() + { + try { + if(sdump_iopen) + out.close(); + } catch(...) { + } + } + + void on_raw_frame(const uint32_t* raw, bool hires, bool interlaced, bool overscan, unsigned region) + { + unsigned flags = 0; + flags |= (hires ? SDUMP_FLAG_HIRES : 0); + flags |= (interlaced ? SDUMP_FLAG_INTERLACED : 0); + flags |= (overscan ? SDUMP_FLAG_OVERSCAN : 0); + flags |= (region == VIDEO_REGION_PAL ? SDUMP_FLAG_PAL : 0); + unsigned char tbuffer[2049]; + if(!sdump_iopen || (ssize > CUTOFF && !sdump_ss)) { + std::cerr << "Starting new segment" << std::endl; + if(sdump_iopen) + out.close(); + std::ostringstream str; + if(sdump_ss) + str << oprefix; + else + str << oprefix << "_" << std::setw(4) << std::setfill('0') << (next_seq++) + << ".sdmp"; + std::string str2 = str.str(); + out.open(str2.c_str(), std::ios::out | std::ios::binary); + if(!out) + throw std::runtime_error("Failed to open '" + str2 + "'"); + sdump_iopen = true; + write32ube(tbuffer, 0x53444D50U); + write32ube(tbuffer + 4, SNES::system.cpu_frequency()); + write32ube(tbuffer + 8, SNES::system.apu_frequency()); + out.write(reinterpret_cast(tbuffer), 12); + if(!out) + throw std::runtime_error("Failed to write header to '" + str2 + "'"); + ssize = 12; + } + tbuffer[0] = flags; + for(unsigned i = 0; i < 512; i++) { + for(unsigned j = 0; j < 512; j++) + write32ube(tbuffer + (4 * j + 1), raw[512 * i + j]); + out.write(reinterpret_cast(tbuffer + (i ? 1 : 0)), i ? 2048 : 2049); + } + if(!out) + throw std::runtime_error("Failed to write frame"); + ssize += 1048577; + } + + void on_sample(short l, short r) + { + if(!sdump_iopen) + return; + unsigned char pkt[5]; + pkt[0] = 16; + write16sbe(pkt + 1, l); + write16sbe(pkt + 3, r); + out.write(reinterpret_cast(pkt), 5); + if(!out) + throw std::runtime_error("Failed to write sample"); + ssize += 5; + } + + void on_dump_end() + { + if(sdump_iopen) + out.close(); + } + + bool get_dumper_flag() throw() + { + return true; + } + private: + std::string oprefix; + bool sdump_ss; + uint64_t ssize; + uint64_t next_seq; + bool sdump_iopen; + std::ofstream out; + }; + + sdmp_avsnoop* vid_dumper; + + class adv_sdmp_dumper : public adv_dumper + { + public: + adv_sdmp_dumper() : adv_dumper("INTERNAL-SDMP") { information_dispatch::do_dumper_update(); } + ~adv_sdmp_dumper() throw(); + std::set list_submodes() throw(std::bad_alloc) + { + std::set x; + x.insert("ss"); + x.insert("ms"); + return x; + } + + bool wants_prefix(const std::string& mode) throw() + { + return (mode != "ss"); + } + + std::string name() throw(std::bad_alloc) + { + return "SDMP"; + } + + std::string modename(const std::string& mode) throw(std::bad_alloc) + { + return (mode == "ss" ? "Single-Segment" : "Multi-Segment"); + } + + bool busy() + { + return (vid_dumper != NULL); + } + + void start(const std::string& mode, const std::string& prefix) throw(std::bad_alloc, + std::runtime_error) + { + if(prefix == "") { + if(mode == "ss") + throw std::runtime_error("Expected filename"); + else + throw std::runtime_error("Expected prefix"); + } + if(vid_dumper) + throw std::runtime_error("SDMP Dump already in progress"); + try { + vid_dumper = new sdmp_avsnoop(prefix, mode == "ss"); + } catch(std::bad_alloc& e) { + throw; + } catch(std::exception& e) { + std::ostringstream x; + x << "Error starting SDMP dump: " << e.what(); + throw std::runtime_error(x.str()); + } + if(mode == "ss") + messages << "Dumping SDMP (SS) to " << prefix << std::endl; + else + messages << "Dumping SDMP to " << prefix << std::endl; + information_dispatch::do_dumper_update(); + } + + void end() throw() + { + if(!vid_dumper) + throw std::runtime_error("No SDMP video dump in progress"); + try { + vid_dumper->on_dump_end(); + messages << "SDMP Dump finished" << std::endl; + } catch(std::bad_alloc& e) { + throw; + } catch(std::exception& e) { + messages << "Error ending SDMP dump: " << e.what() << std::endl; + } + delete vid_dumper; + vid_dumper = NULL; + information_dispatch::do_dumper_update(); + } + } adv; + + adv_sdmp_dumper::~adv_sdmp_dumper() throw() + { } - if(!out) - throw std::runtime_error("Failed to write frame"); - ssize += 1048577; -} - -void sdump_dumper::sample(short left, short right) -{ - if(!sdump_iopen) - return; - unsigned char pkt[5]; - pkt[0] = 16; - pkt[1] = static_cast(left) >> 8; - pkt[2] = static_cast(left); - pkt[3] = static_cast(right) >> 8; - pkt[4] = static_cast(right); - out.write(reinterpret_cast(pkt), 5); - if(!out) - throw std::runtime_error("Failed to write sample"); - ssize += 5; -} - -void sdump_dumper::end() -{ - if(sdump_iopen) - out.close(); } diff --git a/avi/sdmp.hpp b/avi/sdmp.hpp deleted file mode 100644 index 85907efe..00000000 --- a/avi/sdmp.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef _sdmp__hpp__included__ -#define _sdmp__hpp__included__ - -#include -#include -#include - -#define SDUMP_FLAG_HIRES 1 -#define SDUMP_FLAG_INTERLACED 2 -#define SDUMP_FLAG_OVERSCAN 4 -#define SDUMP_FLAG_PAL 8 - -class sdump_dumper -{ -public: - sdump_dumper(const std::string& prefix, bool ssflag); - ~sdump_dumper() throw(); - void frame(const uint32_t* rawdata, unsigned flags); - void sample(short l, short r); - void end(); -private: - std::string oprefix; - bool sdump_ss; - uint64_t ssize; - uint64_t next_seq; - bool sdump_iopen; - std::ofstream out; -}; - -#endif \ No newline at end of file diff --git a/include/core/advdumper.hpp b/include/core/advdumper.hpp index ce7ecdcd..00cfa41e 100644 --- a/include/core/advdumper.hpp +++ b/include/core/advdumper.hpp @@ -5,6 +5,8 @@ #include #include +#include "core/render.hpp" + class adv_dumper { public: @@ -84,4 +86,24 @@ private: std::string d_id; }; +/** + * Render Lua HUD on video. + * + * Parameter target: The target screen to render on. + * Parameter source: The source screen to read. + * Parameter hscl: The horizontal scale factor. + * Parameter vscl: The vertical scale factor. + * Parameter roffset: Red offset. + * Parameter goffset: Green offset. + * Parameter boffset: Blue offset. + * Parameter lgap: Left gap. + * Parameter tgap: Top gap. + * Parameter rgap: Right gap + * Parameter bgap: Bottom gap. + * Parameter fn: Function to call between running lua hooks and actually rendering. + */ +void render_video_hud(struct screen& target, struct lcscreen& source, uint32_t hscl, uint32_t vscl, + uint32_t roffset, uint32_t goffset, uint32_t boffset, uint32_t lgap, uint32_t tgap, uint32_t rgap, + uint32_t bgap, void(*fn)()); + #endif diff --git a/include/library/serialization.hpp b/include/library/serialization.hpp new file mode 100644 index 00000000..4385b7e0 --- /dev/null +++ b/include/library/serialization.hpp @@ -0,0 +1,61 @@ +#ifndef _library__serialization__hpp__included__ +#define _library__serialization__hpp__included__ + +template +void _write_common(unsigned char* target, T1 value) +{ + for(size_t i = 0; i < ssize; i++) + if(be) + target[i] = static_cast(value) >> 8 * (ssize - i - 1); + else + target[i] = static_cast(value) >> 8 * i; +} + +template +T1 _read_common(unsigned char* source) +{ + T2 value = 0; + for(size_t i = 0; i < ssize; i++) + if(be) + value |= static_cast(source[i]) << 8 * (ssize - i - 1); + else + value |= static_cast(source[i]) << 8 * i; + return static_cast(value); +} + +#define write8sbe(t, v) _write_common< int8_t, uint8_t, 1, true>(reinterpret_cast(t), (v)); +#define write8sle(t, v) _write_common< int8_t, uint8_t, 1, false>(reinterpret_cast(t), (v)); +#define write8ube(t, v) _write_common< uint8_t, uint8_t, 1, true>(reinterpret_cast(t), (v)); +#define write8ule(t, v) _write_common< uint8_t, uint8_t, 1, false>(reinterpret_cast(t), (v)); +#define write16sbe(t, v) _write_common< int16_t, uint16_t, 2, true>(reinterpret_cast(t), (v)); +#define write16sle(t, v) _write_common< int16_t, uint16_t, 2, false>(reinterpret_cast(t), (v)); +#define write16ube(t, v) _write_common(reinterpret_cast(t), (v)); +#define write16ule(t, v) _write_common(reinterpret_cast(t), (v)); +#define write32sbe(t, v) _write_common< int32_t, uint32_t, 4, true>(reinterpret_cast(t), (v)); +#define write32sle(t, v) _write_common< int32_t, uint32_t, 4, false>(reinterpret_cast(t), (v)); +#define write32ube(t, v) _write_common(reinterpret_cast(t), (v)); +#define write32ule(t, v) _write_common(reinterpret_cast(t), (v)); +#define write64sbe(t, v) _write_common< int64_t, uint64_t, 8, true>(reinterpret_cast(t), (v)); +#define write64sle(t, v) _write_common< int64_t, uint64_t, 8, false>(reinterpret_cast(t), (v)); +#define write64ube(t, v) _write_common(reinterpret_cast(t), (v)); +#define write64ule(t, v) _write_common(reinterpret_cast(t), (v)); +#define read8sbe(t) _read_common< int8_t, uint8_t, 1, true>(reinterpret_cast(t)); +#define read8sle(t) _read_common< int8_t, uint8_t, 1, false>(reinterpret_cast(t)); +#define read8ube(t) _read_common< uint8_t, uint8_t, 1, true>(reinterpret_cast(t)); +#define read8ule(t) _read_common< uint8_t, uint8_t, 1, false>(reinterpret_cast(t)); +#define read16sbe(t) _read_common< int16_t, uint16_t, 2, true>(reinterpret_cast(t)); +#define read16sle(t) _read_common< int16_t, uint16_t, 2, false>(reinterpret_cast(t)); +#define read16ube(t) _read_common(reinterpret_cast(t)); +#define read16ule(t) _read_common(reinterpret_cast(t)); +#define read32sbe(t) _read_common< int32_t, uint32_t, 4, true>(reinterpret_cast(t)); +#define read32sle(t) _read_common< int32_t, uint32_t, 4, false>(reinterpret_cast(t)); +#define read32ube(t) _read_common(reinterpret_cast(t)); +#define read32ule(t) _read_common(reinterpret_cast(t)); +#define read64sbe(t) _read_common< int64_t, uint64_t, 8, true>(reinterpret_cast(t)); +#define read64sle(t) _read_common< int64_t, uint64_t, 8, false>(reinterpret_cast(t)); +#define read64ube(t) _read_common(reinterpret_cast(t)); +#define read64ule(t) _read_common(reinterpret_cast(t)); + + + +#endif \ No newline at end of file diff --git a/manual.lyx b/manual.lyx index 5223fd6b..1b912f10 100644 --- a/manual.lyx +++ b/manual.lyx @@ -1060,93 +1060,96 @@ Following commands control video dumping: \end_layout \begin_layout Subsubsection -dump-avi +start-dump [] \end_layout \begin_layout Standard -Dump AVI video to prefix Notes: +Start dumping using dumper . + If mode is present or not and if prefix or filename is present depends + on the dumper and dumper mode. +\end_layout + +\begin_layout Standard +The following dumpers are available: \end_layout \begin_layout Itemize -The codec is Camstudio Codec in gzip mode. +INTERNAL-AVI-CSCD: Internal CSCD in .avi dumper. +\end_layout + +\begin_deeper +\begin_layout Itemize +Does not take mode. \end_layout \begin_layout Itemize -Encoder and muxer are internal, available on all platforms. +Takes a prefix. +\end_layout + +\end_deeper +\begin_layout Itemize +INTERNAL-JMD: Internal .jmd dumper. +\end_layout + +\begin_deeper +\begin_layout Itemize +Does not take mode. \end_layout \begin_layout Itemize -Audio enable/disable and framerate has no effect. +Takes a filename. +\end_layout + +\end_deeper +\begin_layout Itemize +INTERNAL-RAW: Internal RAW dumper. +\end_layout + +\begin_deeper +\begin_layout Itemize +Does not take mode. \end_layout \begin_layout Itemize -The audio dumped to .avi is low-quality version. - The high-quality version is dumped to .sox file. +Takes a prefix. \end_layout +\begin_layout Itemize +Sound is big-endian signed 16-bit, usually at 32040.5Hz. +\end_layout + +\begin_layout Itemize +Video is always upscaled to double resolution (512x448 / 512 x 478). +\end_layout + +\begin_layout Itemize +Video framerate is usually 322445/6448 fps for PAL and 10738636/178683 fps + for NTSC. +\end_layout + +\end_deeper +\begin_layout Itemize +INTERNAL-SDMP: Internal SDMP dumper. +\end_layout + +\begin_deeper +\begin_layout Itemize +Mode 'ms': Multi-segment. + Takes prefix. +\end_layout + +\begin_layout Itemize +Mode 'ss': Single-segment. + Takes filename. +\end_layout + +\end_deeper \begin_layout Subsubsection -end-avi +end-dump \end_layout \begin_layout Standard -End current AVI video dump (closing the emulator also closes the dump). -\end_layout - -\begin_layout Subsubsection -dump-jmd -\end_layout - -\begin_layout Standard -Dump JMD video to file . -\end_layout - -\begin_layout Subsubsection -end-jmd -\end_layout - -\begin_layout Standard -End the current JMD dump in progress. -\end_layout - -\begin_layout Subsubsection -dump-sdmp -\end_layout - -\begin_layout Standard -Dump SDMP to , splitting at 2GB. -\end_layout - -\begin_layout Subsubsection -dump-sdmp-ss -\end_layout - -\begin_layout Standard -Dump SDMP to , no splitting -\end_layout - -\begin_layout Subsubsection -end-sdmp -\end_layout - -\begin_layout Standard -End the current SDMP dump in progress. -\end_layout - -\begin_layout Subsubsection -dump-raw -\end_layout - -\begin_layout Standard -Dump raw audio (to .audio, at 32040.5Hz) and video (to .video>, - at nominal framerate and 512x448 / 512x478 size). -\end_layout - -\begin_layout Subsubsection -end-raw -\end_layout - -\begin_layout Standard -End the current RAW dump in progress. +End dumping using \end_layout \begin_layout Subsection diff --git a/manual.txt b/manual.txt index e97cc82f..db33a488 100644 --- a/manual.txt +++ b/manual.txt @@ -488,53 +488,49 @@ Run