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/
This commit is contained in:
parent
caf13d2da4
commit
f292f7babe
17 changed files with 1149 additions and 1551 deletions
8
Makefile
8
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
|
||||
|
|
|
@ -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<const std::string&> avi_dump("dump-avi", "Start AVI capture",
|
||||
"Syntax: dump-avi <prefix>\nStart AVI capture to <prefix>.\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;
|
||||
|
422
avi/cscd.cpp
422
avi/cscd.cpp
|
@ -7,6 +7,7 @@
|
|||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#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<char> 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<char> 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<char> 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<char> 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<char> 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<char> 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<char> 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<char> 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<char> 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<char*>(&magic) == 2);
|
||||
switch(sf) {
|
||||
case avi_cscd_dumper::SNDFMT_SILENCE:
|
||||
return 32768;
|
||||
case avi_cscd_dumper::SNDFMT_SIGNED_8:
|
||||
return static_cast<unsigned short>((static_cast<short>(*addr) << 8)) + 32768;
|
||||
case avi_cscd_dumper::SNDFMT_UNSIGNED_8:
|
||||
return static_cast<unsigned short>(static_cast<unsigned char>(*addr)) << 8;
|
||||
case avi_cscd_dumper::SNDFMT_SIGNED_16BE:
|
||||
a = static_cast<unsigned char>(addr[0]);
|
||||
b = static_cast<unsigned char>(addr[1]);
|
||||
return a * 256 + b + 32768;
|
||||
case avi_cscd_dumper::SNDFMT_SIGNED_16NE:
|
||||
a = static_cast<unsigned char>(addr[0]);
|
||||
b = static_cast<unsigned char>(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<unsigned char>(addr[0]);
|
||||
b = static_cast<unsigned char>(addr[1]);
|
||||
return b * 256 + a + 32768;
|
||||
case avi_cscd_dumper::SNDFMT_UNSIGNED_16BE:
|
||||
a = static_cast<unsigned char>(addr[0]);
|
||||
b = static_cast<unsigned char>(addr[1]);
|
||||
return a * 256 + b;
|
||||
case avi_cscd_dumper::SNDFMT_UNSIGNED_16NE:
|
||||
a = static_cast<unsigned char>(addr[0]);
|
||||
b = static_cast<unsigned char>(addr[1]);
|
||||
if(little_endian)
|
||||
return b * 256 + a;
|
||||
else
|
||||
return a * 256 + b;
|
||||
case avi_cscd_dumper::SNDFMT_UNSIGNED_16LE:
|
||||
a = static_cast<unsigned char>(addr[0]);
|
||||
b = static_cast<unsigned char>(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<char*>(&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<char> 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<const char*>(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<unsigned short>(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<const char*>(laudio);
|
||||
const char* r = reinterpret_cast<const char*>(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<unsigned short>(laudio[i]) + 32768;
|
||||
unsigned short rs = static_cast<unsigned short>(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<unsigned char>& data, bool keyframe, unsigned level,
|
||||
unsigned mode)
|
||||
size_t avi_cscd_dumper::emit_frame(const std::vector<unsigned char>& 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<unsigned char>& 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<unsigned char>& 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<Uint64>(gp_sampling_rate) * f.fps_d % f.fps_n;
|
||||
unsigned long ret = static_cast<Uint64>(gp_sampling_rate) * f.fps_d / f.fps_n;
|
||||
if(static_cast<Uint64>(frame_period_counter) * critical % f.fps_n < critical)
|
||||
unsigned long critical = static_cast<uint64_t>(gp_sampling_rate) * f.fps_d % f.fps_n;
|
||||
unsigned long ret = static_cast<uint64_t>(gp_sampling_rate) * f.fps_d / f.fps_n;
|
||||
if(static_cast<uint64_t>(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()
|
||||
|
|
100
avi/cscd.hpp
100
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 <boost/thread.hpp>
|
||||
#include <boost/thread/locks.hpp>
|
||||
|
||||
#define ACTUALLY_USE_THREADS
|
||||
typedef boost::thread thread_class;
|
||||
typedef boost::condition_variable cv_class;
|
||||
typedef boost::mutex mutex_class;
|
||||
typedef boost::unique_lock<boost::mutex> umutex_class;
|
||||
|
||||
#else
|
||||
#ifdef STD_THREADS
|
||||
#ifdef NATIVE_THREADS
|
||||
#include <thread>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
#define ACTUALLY_USE_THREADS
|
||||
typedef std::thread thread_class;
|
||||
typedef std::condition_variable cv_class;
|
||||
typedef std::mutex mutex_class;
|
||||
typedef std::unique_lock<std::mutex> umutex_class;
|
||||
|
||||
#else
|
||||
|
||||
struct thread_class
|
||||
{
|
||||
template<typename T, typename args>
|
||||
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 <boost/thread.hpp>
|
||||
#include <boost/thread/locks.hpp>
|
||||
typedef boost::thread thread_class;
|
||||
typedef boost::condition_variable cv_class;
|
||||
typedef boost::mutex mutex_class;
|
||||
typedef boost::unique_lock<boost::mutex> umutex_class;
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
|
@ -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<unsigned char> 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<buffered_frame> frame_buffer;
|
||||
|
||||
//Fills compression_output with frame/sound packet and returns the total size.
|
||||
size_t emit_frame(const std::vector<unsigned char>& data, bool keyframe, unsigned level, unsigned mode);
|
||||
size_t emit_frame(const std::vector<unsigned char>& 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);
|
||||
|
|
602
avi/jmd.cpp
602
avi/jmd.cpp
|
@ -1,240 +1,382 @@
|
|||
#include "jmd.hpp"
|
||||
#include <iostream>
|
||||
#include <zlib.h>
|
||||
#include <stdexcept>
|
||||
#include "core/advdumper.hpp"
|
||||
#include "core/dispatch.hpp"
|
||||
#include "core/settings.hpp"
|
||||
#include "library/serialization.hpp"
|
||||
|
||||
#include <iomanip>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
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 <sstream>
|
||||
#include <fstream>
|
||||
#include <deque>
|
||||
#include <zlib.h>
|
||||
#define INBUF_PIXELS 4096
|
||||
#define OUTBUF_ADVANCE 4096
|
||||
|
||||
std::vector<char> jmd_dumper::compress_frame(uint32_t* memory, uint32_t width, uint32_t height)
|
||||
|
||||
namespace
|
||||
{
|
||||
std::vector<char> 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<size_t>(width) * height;
|
||||
bool input_clear = true;
|
||||
bool flushed = false;
|
||||
size_t bsize = 0;
|
||||
uint32_t _magic = 0x18100800;
|
||||
const uint8_t* magic = reinterpret_cast<const uint8_t*>(&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<uint8_t*>(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<uint8_t*>(&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<uint32_t, uint32_t> soundrate;
|
||||
struct frame_buffer
|
||||
{
|
||||
uint64_t ts;
|
||||
std::vector<char> data;
|
||||
};
|
||||
struct sample_buffer
|
||||
{
|
||||
uint64_t ts;
|
||||
short l;
|
||||
short r;
|
||||
};
|
||||
|
||||
std::deque<frame_buffer> frames;
|
||||
std::deque<sample_buffer> samples;
|
||||
|
||||
std::vector<char> compress_frame(uint32_t* memory, uint32_t width, uint32_t height)
|
||||
{
|
||||
std::vector<char> 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<size_t>(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<uint8_t*>(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<uint8_t*>(&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<std::string> list_submodes() throw(std::bad_alloc)
|
||||
{
|
||||
std::set<std::string> 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()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
46
avi/jmd.hpp
46
avi/jmd.hpp
|
@ -1,46 +0,0 @@
|
|||
#ifndef _jmd__hpp__included__
|
||||
#define _jmd__hpp__included__
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <deque>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
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<char> data;
|
||||
};
|
||||
struct sample_buffer
|
||||
{
|
||||
uint64_t ts;
|
||||
short l;
|
||||
short r;
|
||||
};
|
||||
|
||||
std::deque<frame_buffer> frames;
|
||||
std::deque<sample_buffer> samples;
|
||||
|
||||
std::vector<char> 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
|
153
avi/raw.cpp
Normal file
153
avi/raw.cpp
Normal file
|
@ -0,0 +1,153 @@
|
|||
#include "core/advdumper.hpp"
|
||||
#include "core/dispatch.hpp"
|
||||
#include "library/serialization.hpp"
|
||||
|
||||
#include <iomanip>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <zlib.h>
|
||||
|
||||
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<unsigned char*>(&magic))[2];
|
||||
unsigned g = (reinterpret_cast<unsigned char*>(&magic))[1];
|
||||
unsigned b = (reinterpret_cast<unsigned char*>(&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<char*>(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<std::string> list_submodes() throw(std::bad_alloc)
|
||||
{
|
||||
std::set<std::string> 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()
|
||||
{
|
||||
}
|
||||
}
|
282
avi/sdmp.cpp
282
avi/sdmp.cpp
|
@ -1,101 +1,205 @@
|
|||
#include "lsnes.hpp"
|
||||
#include <snes/snes.hpp>
|
||||
#include "core/advdumper.hpp"
|
||||
#include "core/dispatch.hpp"
|
||||
#include "library/serialization.hpp"
|
||||
|
||||
#include "sdmp.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <zlib.h>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
|
||||
#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<char*>(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<char*>(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<char*>(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<char*>(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<char*>(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<std::string> list_submodes() throw(std::bad_alloc)
|
||||
{
|
||||
std::set<std::string> 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<unsigned short>(left) >> 8;
|
||||
pkt[2] = static_cast<unsigned short>(left);
|
||||
pkt[3] = static_cast<unsigned short>(right) >> 8;
|
||||
pkt[4] = static_cast<unsigned short>(right);
|
||||
out.write(reinterpret_cast<char*>(pkt), 5);
|
||||
if(!out)
|
||||
throw std::runtime_error("Failed to write sample");
|
||||
ssize += 5;
|
||||
}
|
||||
|
||||
void sdump_dumper::end()
|
||||
{
|
||||
if(sdump_iopen)
|
||||
out.close();
|
||||
}
|
||||
|
|
30
avi/sdmp.hpp
30
avi/sdmp.hpp
|
@ -1,30 +0,0 @@
|
|||
#ifndef _sdmp__hpp__included__
|
||||
#define _sdmp__hpp__included__
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <cstdint>
|
||||
|
||||
#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
|
|
@ -5,6 +5,8 @@
|
|||
#include <set>
|
||||
#include <stdexcept>
|
||||
|
||||
#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
|
||||
|
|
61
include/library/serialization.hpp
Normal file
61
include/library/serialization.hpp
Normal file
|
@ -0,0 +1,61 @@
|
|||
#ifndef _library__serialization__hpp__included__
|
||||
#define _library__serialization__hpp__included__
|
||||
|
||||
template<typename T1, typename T2, size_t ssize, bool be>
|
||||
void _write_common(unsigned char* target, T1 value)
|
||||
{
|
||||
for(size_t i = 0; i < ssize; i++)
|
||||
if(be)
|
||||
target[i] = static_cast<T2>(value) >> 8 * (ssize - i - 1);
|
||||
else
|
||||
target[i] = static_cast<T2>(value) >> 8 * i;
|
||||
}
|
||||
|
||||
template<typename T1, typename T2, size_t ssize, bool be>
|
||||
T1 _read_common(unsigned char* source)
|
||||
{
|
||||
T2 value = 0;
|
||||
for(size_t i = 0; i < ssize; i++)
|
||||
if(be)
|
||||
value |= static_cast<T2>(source[i]) << 8 * (ssize - i - 1);
|
||||
else
|
||||
value |= static_cast<T2>(source[i]) << 8 * i;
|
||||
return static_cast<T1>(value);
|
||||
}
|
||||
|
||||
#define write8sbe(t, v) _write_common< int8_t, uint8_t, 1, true>(reinterpret_cast<unsigned char*>(t), (v));
|
||||
#define write8sle(t, v) _write_common< int8_t, uint8_t, 1, false>(reinterpret_cast<unsigned char*>(t), (v));
|
||||
#define write8ube(t, v) _write_common< uint8_t, uint8_t, 1, true>(reinterpret_cast<unsigned char*>(t), (v));
|
||||
#define write8ule(t, v) _write_common< uint8_t, uint8_t, 1, false>(reinterpret_cast<unsigned char*>(t), (v));
|
||||
#define write16sbe(t, v) _write_common< int16_t, uint16_t, 2, true>(reinterpret_cast<unsigned char*>(t), (v));
|
||||
#define write16sle(t, v) _write_common< int16_t, uint16_t, 2, false>(reinterpret_cast<unsigned char*>(t), (v));
|
||||
#define write16ube(t, v) _write_common<uint16_t, uint16_t, 2, true>(reinterpret_cast<unsigned char*>(t), (v));
|
||||
#define write16ule(t, v) _write_common<uint16_t, uint16_t, 2, false>(reinterpret_cast<unsigned char*>(t), (v));
|
||||
#define write32sbe(t, v) _write_common< int32_t, uint32_t, 4, true>(reinterpret_cast<unsigned char*>(t), (v));
|
||||
#define write32sle(t, v) _write_common< int32_t, uint32_t, 4, false>(reinterpret_cast<unsigned char*>(t), (v));
|
||||
#define write32ube(t, v) _write_common<uint32_t, uint32_t, 4, true>(reinterpret_cast<unsigned char*>(t), (v));
|
||||
#define write32ule(t, v) _write_common<uint32_t, uint32_t, 4, false>(reinterpret_cast<unsigned char*>(t), (v));
|
||||
#define write64sbe(t, v) _write_common< int64_t, uint64_t, 8, true>(reinterpret_cast<unsigned char*>(t), (v));
|
||||
#define write64sle(t, v) _write_common< int64_t, uint64_t, 8, false>(reinterpret_cast<unsigned char*>(t), (v));
|
||||
#define write64ube(t, v) _write_common<uint64_t, uint64_t, 8, true>(reinterpret_cast<unsigned char*>(t), (v));
|
||||
#define write64ule(t, v) _write_common<uint64_t, uint64_t, 8, false>(reinterpret_cast<unsigned char*>(t), (v));
|
||||
#define read8sbe(t) _read_common< int8_t, uint8_t, 1, true>(reinterpret_cast<unsigned char*>(t));
|
||||
#define read8sle(t) _read_common< int8_t, uint8_t, 1, false>(reinterpret_cast<unsigned char*>(t));
|
||||
#define read8ube(t) _read_common< uint8_t, uint8_t, 1, true>(reinterpret_cast<unsigned char*>(t));
|
||||
#define read8ule(t) _read_common< uint8_t, uint8_t, 1, false>(reinterpret_cast<unsigned char*>(t));
|
||||
#define read16sbe(t) _read_common< int16_t, uint16_t, 2, true>(reinterpret_cast<unsigned char*>(t));
|
||||
#define read16sle(t) _read_common< int16_t, uint16_t, 2, false>(reinterpret_cast<unsigned char*>(t));
|
||||
#define read16ube(t) _read_common<uint16_t, uint16_t, 2, true>(reinterpret_cast<unsigned char*>(t));
|
||||
#define read16ule(t) _read_common<uint16_t, uint16_t, 2, false>(reinterpret_cast<unsigned char*>(t));
|
||||
#define read32sbe(t) _read_common< int32_t, uint32_t, 4, true>(reinterpret_cast<unsigned char*>(t));
|
||||
#define read32sle(t) _read_common< int32_t, uint32_t, 4, false>(reinterpret_cast<unsigned char*>(t));
|
||||
#define read32ube(t) _read_common<uint32_t, uint32_t, 4, true>(reinterpret_cast<unsigned char*>(t));
|
||||
#define read32ule(t) _read_common<uint32_t, uint32_t, 4, false>(reinterpret_cast<unsigned char*>(t));
|
||||
#define read64sbe(t) _read_common< int64_t, uint64_t, 8, true>(reinterpret_cast<unsigned char*>(t));
|
||||
#define read64sle(t) _read_common< int64_t, uint64_t, 8, false>(reinterpret_cast<unsigned char*>(t));
|
||||
#define read64ube(t) _read_common<uint64_t, uint64_t, 8, true>(reinterpret_cast<unsigned char*>(t));
|
||||
#define read64ule(t) _read_common<uint64_t, uint64_t, 8, false>(reinterpret_cast<unsigned char*>(t));
|
||||
|
||||
|
||||
|
||||
#endif
|
135
manual.lyx
135
manual.lyx
|
@ -1060,93 +1060,96 @@ Following commands control video dumping:
|
|||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
dump-avi <prefix>
|
||||
start-dump <dumper> [<mode>] <prefix/filename>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Dump AVI video to prefix <prefix> Notes:
|
||||
Start dumping using dumper <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 <dumper>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
End current AVI video dump (closing the emulator also closes the dump).
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
dump-jmd <file>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Dump JMD video to file <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 <prefix>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Dump SDMP to <prefix>, splitting at 2GB.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
dump-sdmp-ss <file>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Dump SDMP to <file>, 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 <prefix>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Dump raw audio (to <prefix>.audio, at 32040.5Hz) and video (to <prefix>.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 <dumper>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsection
|
||||
|
|
52
manual.txt
52
manual.txt
|
@ -488,53 +488,49 @@ Run <script> as if commands were entered on the command line.
|
|||
|
||||
Following commands control video dumping:
|
||||
|
||||
6.5.1 dump-avi <prefix>
|
||||
6.5.1 start-dump <dumper> [<mode>] <prefix/filename>
|
||||
|
||||
Dump AVI video to prefix <prefix> Notes:
|
||||
Start dumping using dumper <dumper>. If mode is present or not
|
||||
and if prefix or filename is present depends on the dumper and
|
||||
dumper mode.
|
||||
|
||||
• The codec is Camstudio Codec in gzip mode.
|
||||
The following dumpers are available:
|
||||
|
||||
• Encoder and muxer are internal, available on all platforms.
|
||||
• INTERNAL-AVI-CSCD: Internal CSCD in .avi dumper.
|
||||
|
||||
• Audio enable/disable and framerate has no effect.
|
||||
– Does not take mode.
|
||||
|
||||
• The audio dumped to .avi is low-quality version. The
|
||||
high-quality version is dumped to .sox file.
|
||||
– Takes a prefix.
|
||||
|
||||
6.5.2 end-avi
|
||||
• INTERNAL-JMD: Internal .jmd dumper.
|
||||
|
||||
End current AVI video dump (closing the emulator also closes the
|
||||
dump).
|
||||
– Does not take mode.
|
||||
|
||||
6.5.3 dump-jmd <file>
|
||||
– Takes a filename.
|
||||
|
||||
Dump JMD video to file <file>.
|
||||
• INTERNAL-RAW: Internal RAW dumper.
|
||||
|
||||
6.5.4 end-jmd
|
||||
– Does not take mode.
|
||||
|
||||
End the current JMD dump in progress.
|
||||
– Takes a prefix.
|
||||
|
||||
6.5.5 dump-sdmp <prefix>
|
||||
– Sound is big-endian signed 16-bit, usually at 32040.5Hz.
|
||||
|
||||
Dump SDMP to <prefix>, splitting at 2GB.
|
||||
– Video is always upscaled to double resolution (512x448 / 512
|
||||
x 478).
|
||||
|
||||
6.5.6 dump-sdmp-ss <file>
|
||||
– Video framerate is usually 322445/6448 fps for PAL and
|
||||
10738636/178683 fps for NTSC.
|
||||
|
||||
Dump SDMP to <file>, no splitting
|
||||
• INTERNAL-SDMP: Internal SDMP dumper.
|
||||
|
||||
6.5.7 end-sdmp
|
||||
– Mode 'ms': Multi-segment. Takes prefix.
|
||||
|
||||
End the current SDMP dump in progress.
|
||||
– Mode 'ss': Single-segment. Takes filename.
|
||||
|
||||
6.5.8 dump-raw <prefix>
|
||||
6.5.2 end-dump <dumper>
|
||||
|
||||
Dump raw audio (to <prefix>.audio, at 32040.5Hz) and video (to
|
||||
<prefix>.video>, at nominal framerate and 512x448 / 512x478
|
||||
size).
|
||||
|
||||
6.5.9 end-raw
|
||||
|
||||
End the current RAW dump in progress.
|
||||
End dumping using <dumper>
|
||||
|
||||
6.6 Memory manipulation
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#include "core/advdumper.hpp"
|
||||
#include "core/command.hpp"
|
||||
#include "core/dispatch.hpp"
|
||||
#include "core/globalwrap.hpp"
|
||||
#include "core/lua.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
@ -8,6 +10,42 @@
|
|||
namespace
|
||||
{
|
||||
globalwrap<std::map<std::string, adv_dumper*>> dumpers;
|
||||
|
||||
adv_dumper& find_by_name(const std::string& dname)
|
||||
{
|
||||
auto i = adv_dumper::get_dumper_set();
|
||||
for(auto j : i)
|
||||
if(j->id() == dname)
|
||||
return *j;
|
||||
throw std::runtime_error("Unknown dumper");
|
||||
}
|
||||
|
||||
function_ptr_command<tokensplitter&> start_dump("start-dump", "Start dumping",
|
||||
"Syntax: start-dump <dumper> <prefix/filename>\nSyntax: start-dump <dumper> <mode> <prefix/filename>\n"
|
||||
"Start dumping using <dumper> in mode <mode> to <prefix/filename>\n",
|
||||
[](tokensplitter& t) throw(std::bad_alloc, std::runtime_error) {
|
||||
adv_dumper& d = find_by_name(t);
|
||||
auto modes = d.list_submodes();
|
||||
std::string mode;
|
||||
if(!modes.empty()) {
|
||||
mode = std::string(t);
|
||||
if(!modes.count(mode))
|
||||
throw std::runtime_error("Bad mode for dumper");
|
||||
}
|
||||
std::string target = t.tail();
|
||||
if(target == "")
|
||||
throw std::runtime_error("Command syntax error");
|
||||
d.start(mode, target);
|
||||
});
|
||||
|
||||
function_ptr_command<tokensplitter&> end_dump("end-dump", "End dumping",
|
||||
"Syntax: end-dump <dumper>\nEnd dumping using dumper <dumper>\n",
|
||||
[](tokensplitter& t) throw(std::bad_alloc, std::runtime_error) {
|
||||
adv_dumper& d = find_by_name(t);
|
||||
if(t.tail() != "")
|
||||
throw std::runtime_error("Command syntax error");
|
||||
d.end();
|
||||
});
|
||||
}
|
||||
|
||||
const std::string& adv_dumper::id() throw()
|
||||
|
@ -34,3 +72,27 @@ adv_dumper::adv_dumper(const std::string& id) throw(std::bad_alloc)
|
|||
d_id = id;
|
||||
dumpers()[d_id] = this;
|
||||
}
|
||||
|
||||
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)())
|
||||
{
|
||||
struct lua_render_context lrc;
|
||||
render_queue rq;
|
||||
lrc.left_gap = lgap;
|
||||
lrc.right_gap = rgap;
|
||||
lrc.bottom_gap = bgap;
|
||||
lrc.top_gap = tgap;
|
||||
lrc.queue = &rq;
|
||||
lrc.width = source.width;
|
||||
lrc.height = source.height;
|
||||
lua_callback_do_video(&lrc);
|
||||
if(fn)
|
||||
fn();
|
||||
target.set_palette(roffset, goffset, boffset);
|
||||
target.reallocate(lrc.left_gap + source.width * hscl + lrc.right_gap, lrc.top_gap +
|
||||
source.height * vscl + lrc.bottom_gap, false);
|
||||
target.set_origin(lrc.left_gap, lrc.top_gap);
|
||||
target.copy_from(source, hscl, vscl);
|
||||
rq.run(target);
|
||||
}
|
||||
|
|
|
@ -1,235 +0,0 @@
|
|||
#include "jmd.hpp"
|
||||
|
||||
#include "core/advdumper.hpp"
|
||||
#include "core/command.hpp"
|
||||
#include "core/dispatch.hpp"
|
||||
#include "core/lua.hpp"
|
||||
#include "core/misc.hpp"
|
||||
#include "core/settings.hpp"
|
||||
|
||||
#include <iomanip>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <zlib.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
numeric_setting clevel("jmd-compression", 0, 9, 7);
|
||||
|
||||
class jmd_avsnoop : public information_dispatch
|
||||
{
|
||||
public:
|
||||
jmd_avsnoop(const std::string& filename, unsigned level) throw(std::bad_alloc)
|
||||
: information_dispatch("dump-jmd")
|
||||
{
|
||||
enable_send_sound();
|
||||
vid_dumper = new jmd_dumper(filename, level);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
~jmd_avsnoop() throw()
|
||||
{
|
||||
delete vid_dumper;
|
||||
}
|
||||
|
||||
void on_frame(struct lcscreen& _frame, uint32_t fps_n, uint32_t fps_d)
|
||||
{
|
||||
struct lua_render_context lrc;
|
||||
render_queue rq;
|
||||
lrc.left_gap = 0;
|
||||
lrc.right_gap = 0;
|
||||
lrc.bottom_gap = 0;
|
||||
lrc.top_gap = 0;
|
||||
lrc.queue = &rq;
|
||||
lrc.width = _frame.width;
|
||||
lrc.height = _frame.height;
|
||||
lua_callback_do_video(&lrc);
|
||||
dscr.set_palette(0, 8, 16);
|
||||
dscr.reallocate(lrc.left_gap + _frame.width + lrc.right_gap, lrc.top_gap + _frame.height +
|
||||
lrc.bottom_gap, false);
|
||||
dscr.set_origin(lrc.left_gap, lrc.top_gap);
|
||||
dscr.copy_from(_frame, 1, 1);
|
||||
rq.run(dscr);
|
||||
|
||||
vid_dumper->video(get_next_video_ts(fps_n, fps_d), dscr.memory, dscr.width, dscr.height);
|
||||
have_dumped_frame = true;
|
||||
}
|
||||
|
||||
void on_sample(short l, short r)
|
||||
{
|
||||
uint64_t ts = get_next_audio_ts();
|
||||
if(have_dumped_frame)
|
||||
vid_dumper->audio(ts, l, r);
|
||||
}
|
||||
|
||||
void on_dump_end()
|
||||
{
|
||||
vid_dumper->end(maxtc);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
vid_dumper->gameinfo(gi.gamename, authstr, 1000000000ULL * gi.length, gi.get_rerecords());
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
jmd_dumper* vid_dumper;
|
||||
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<uint32_t, uint32_t> soundrate;
|
||||
};
|
||||
|
||||
jmd_avsnoop* vid_dumper;
|
||||
|
||||
void startdump(std::string prefix)
|
||||
{
|
||||
if(prefix == "")
|
||||
throw std::runtime_error("Expected filename");
|
||||
if(vid_dumper)
|
||||
throw std::runtime_error("JMD dumping already in progress");
|
||||
unsigned long level2 = (unsigned long)level2;
|
||||
try {
|
||||
vid_dumper = new jmd_avsnoop(prefix, level2);
|
||||
} 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 " << level2 << std::endl;
|
||||
information_dispatch::do_dumper_update();
|
||||
}
|
||||
|
||||
void enddump()
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
function_ptr_command<const std::string&> jmd_dump("dump-jmd", "Start JMD capture",
|
||||
"Syntax: dump-jmd <file>\nStart JMD capture to <file>.\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-jmd", "End JMD capture",
|
||||
"Syntax: end-jmd\nEnd a JMD capture.\n",
|
||||
[]() throw(std::bad_alloc, std::runtime_error) {
|
||||
enddump();
|
||||
});
|
||||
|
||||
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<std::string> list_submodes() throw(std::bad_alloc)
|
||||
{
|
||||
std::set<std::string> 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& targetname) throw(std::bad_alloc,
|
||||
std::runtime_error)
|
||||
{
|
||||
startdump(targetname);
|
||||
}
|
||||
|
||||
void end() throw()
|
||||
{
|
||||
enddump();
|
||||
}
|
||||
} adv;
|
||||
|
||||
adv_jmd_dumper::~adv_jmd_dumper() throw()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -1,193 +0,0 @@
|
|||
#include "core/advdumper.hpp"
|
||||
#include "core/command.hpp"
|
||||
#include "core/dispatch.hpp"
|
||||
#include "core/lua.hpp"
|
||||
#include "core/misc.hpp"
|
||||
#include "core/settings.hpp"
|
||||
|
||||
#include <iomanip>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <zlib.h>
|
||||
|
||||
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;
|
||||
struct lua_render_context lrc;
|
||||
render_queue rq;
|
||||
lrc.left_gap = 0;
|
||||
lrc.right_gap = 0;
|
||||
lrc.bottom_gap = 0;
|
||||
lrc.top_gap = 0;
|
||||
lrc.queue = &rq;
|
||||
lrc.width = _frame.width;
|
||||
lrc.height = _frame.height;
|
||||
lua_callback_do_video(&lrc);
|
||||
dscr.set_palette(16, 8, 0);
|
||||
uint32_t hscl = (_frame.width < 400) ? 2 : 1;
|
||||
uint32_t vscl = (_frame.height < 400) ? 2 : 1;
|
||||
dscr.reallocate(lrc.left_gap + _frame.width * hscl + lrc.right_gap, lrc.top_gap +
|
||||
_frame.height * vscl + lrc.bottom_gap, false);
|
||||
dscr.set_origin(lrc.left_gap, lrc.top_gap);
|
||||
dscr.copy_from(_frame, hscl, vscl);
|
||||
rq.run(dscr);
|
||||
for(size_t i = 0; i < dscr.height; i++)
|
||||
video->write(reinterpret_cast<char*>(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];
|
||||
buffer[0] = static_cast<unsigned short>(l);
|
||||
buffer[1] = static_cast<unsigned short>(l) >> 8;
|
||||
buffer[2] = static_cast<unsigned short>(r);
|
||||
buffer[3] = static_cast<unsigned short>(r) >> 8;
|
||||
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;
|
||||
|
||||
void startdump(std::string prefix)
|
||||
{
|
||||
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 enddump()
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
function_ptr_command<const std::string&> raw_dump("dump-raw", "Start RAW capture",
|
||||
"Syntax: dump-raw <prefix>\nStart RAW capture to <prefix>.video and <prefix>.audio.\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_raw("end-raw", "End RAW capture",
|
||||
"Syntax: end-raw\nEnd a RAW capture.\n",
|
||||
[]() throw(std::bad_alloc, std::runtime_error) {
|
||||
enddump();
|
||||
});
|
||||
|
||||
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<std::string> list_submodes() throw(std::bad_alloc)
|
||||
{
|
||||
std::set<std::string> 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& targetname) throw(std::bad_alloc,
|
||||
std::runtime_error)
|
||||
{
|
||||
startdump(targetname);
|
||||
}
|
||||
|
||||
void end() throw()
|
||||
{
|
||||
enddump();
|
||||
}
|
||||
} adv;
|
||||
|
||||
adv_raw_dumper::~adv_raw_dumper() throw()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -1,169 +0,0 @@
|
|||
#include "sdmp.hpp"
|
||||
#include "core/advdumper.hpp"
|
||||
#include "core/command.hpp"
|
||||
#include "core/dispatch.hpp"
|
||||
#include "core/lua.hpp"
|
||||
#include "core/misc.hpp"
|
||||
#include "core/settings.hpp"
|
||||
|
||||
#include <iomanip>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <zlib.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
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();
|
||||
dumper = new sdump_dumper(prefix, ssflag);
|
||||
}
|
||||
|
||||
~sdmp_avsnoop() throw()
|
||||
{
|
||||
delete dumper;
|
||||
}
|
||||
|
||||
void on_raw_frame(const uint32_t* raw, bool hires, bool interlaced, bool overscan, unsigned region)
|
||||
{
|
||||
unsigned flags = 0;
|
||||
dumper->frame(raw, (hires ? SDUMP_FLAG_HIRES : 0) | (interlaced ? SDUMP_FLAG_INTERLACED : 0) |
|
||||
(overscan ? SDUMP_FLAG_OVERSCAN : 0) | (region == VIDEO_REGION_PAL ? SDUMP_FLAG_PAL :
|
||||
0));
|
||||
}
|
||||
|
||||
void on_sample(short l, short r)
|
||||
{
|
||||
dumper->sample(l, r);
|
||||
}
|
||||
|
||||
void on_dump_end()
|
||||
{
|
||||
dumper->end();
|
||||
}
|
||||
|
||||
bool get_dumper_flag() throw()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
private:
|
||||
sdump_dumper* dumper;
|
||||
};
|
||||
|
||||
sdmp_avsnoop* vid_dumper;
|
||||
|
||||
void startdump(bool ss, const std::string& prefix)
|
||||
{
|
||||
if(prefix == "") {
|
||||
if(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, 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(ss)
|
||||
messages << "Dumping SDMP (SS) to " << prefix << std::endl;
|
||||
else
|
||||
messages << "Dumping SDMP to " << prefix << std::endl;
|
||||
information_dispatch::do_dumper_update();
|
||||
}
|
||||
|
||||
void enddump()
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
function_ptr_command<const std::string&> sdmp_dump("dump-sdmp", "Start sdmp capture",
|
||||
"Syntax: dump-sdmp <prefix>\nStart SDMP capture to <prefix>\n",
|
||||
[](const std::string& prefix) throw(std::bad_alloc, std::runtime_error) {
|
||||
startdump(false, prefix);
|
||||
});
|
||||
|
||||
function_ptr_command<const std::string&> sdmp_dumpss("dump-sdmpss", "Start SS sdmp capture",
|
||||
"Syntax: dump-sdmpss <file>\nStart SS SDMP capture to <file>\n",
|
||||
[](const std::string& prefix) throw(std::bad_alloc, std::runtime_error) {
|
||||
startdump(true, prefix);
|
||||
});
|
||||
|
||||
function_ptr_command<> end_avi("end-sdmp", "End SDMP capture",
|
||||
"Syntax: end-sdmp\nEnd a SDMP capture.\n",
|
||||
[]() throw(std::bad_alloc, std::runtime_error) {
|
||||
enddump();
|
||||
});
|
||||
|
||||
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<std::string> list_submodes() throw(std::bad_alloc)
|
||||
{
|
||||
std::set<std::string> 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& targetname) throw(std::bad_alloc,
|
||||
std::runtime_error)
|
||||
{
|
||||
startdump((mode == "ss"), targetname);
|
||||
}
|
||||
|
||||
void end() throw()
|
||||
{
|
||||
enddump();
|
||||
}
|
||||
} adv;
|
||||
|
||||
adv_sdmp_dumper::~adv_sdmp_dumper() throw()
|
||||
{
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue