lsnes/src/core/jmd-control.cpp

176 lines
4.4 KiB
C++
Raw Normal View History

2011-10-12 03:29:52 +03:00
#include "jmd.hpp"
2011-11-06 14:41:41 +02:00
#include "core/command.hpp"
#include "core/dispatch.hpp"
2011-11-06 14:41:41 +02:00
#include "core/lua.hpp"
#include "core/misc.hpp"
#include "core/settings.hpp"
2011-10-12 03:29:52 +03:00
#include <iomanip>
#include <cassert>
#include <cstring>
#include <sstream>
#include <zlib.h>
namespace
{
2012-01-06 17:28:01 +02:00
numeric_setting clevel("jmd-compression", 0, 9, 7);
class jmd_avsnoop : public information_dispatch
2011-10-12 03:29:52 +03:00
{
public:
jmd_avsnoop(const std::string& filename, unsigned level) throw(std::bad_alloc)
: information_dispatch("dump-jmd")
2011-10-12 03:29:52 +03:00
{
2012-01-06 17:28:01 +02:00
enable_send_sound();
2011-10-12 03:29:52 +03:00
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;
}
2011-10-12 03:29:52 +03:00
}
~jmd_avsnoop() throw()
{
delete vid_dumper;
}
void on_frame(struct lcscreen& _frame, uint32_t fps_n, uint32_t fps_d)
2011-10-12 03:29:52 +03:00
{
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);
2011-10-12 03:29:52 +03:00
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);
2011-10-12 03:29:52 +03:00
dscr.copy_from(_frame, 1, 1);
rq.run(dscr);
2011-11-03 17:57:46 +02:00
2011-10-12 03:29:52 +03:00
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)
2011-10-12 03:29:52 +03:00
{
uint64_t ts = get_next_audio_ts();
if(have_dumped_frame)
vid_dumper->audio(ts, l, r);
}
void on_dump_end()
2011-10-12 03:29:52 +03:00
{
vid_dumper->end(maxtc);
}
void on_gameinfo(const struct gameinfo_struct& gi)
2011-10-12 03:29:52 +03:00
{
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());
2011-10-12 03:29:52 +03:00
}
bool get_dumper_flag() throw()
{
return true;
}
2011-10-12 03:29:52 +03:00
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;
2011-10-12 03:29:52 +03:00
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;
2011-10-12 03:29:52 +03:00
};
jmd_avsnoop* vid_dumper;
function_ptr_command<const std::string&> jmd_dump("dump-jmd", "Start JMD capture",
2012-01-06 17:28:01 +02:00
"Syntax: dump-jmd <file>\nStart JMD capture to <file>.\n",
2011-10-12 03:29:52 +03:00
[](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
tokensplitter t(args);
std::string prefix = t.tail();
if(prefix == "")
throw std::runtime_error("Expected filename");
if(vid_dumper)
throw std::runtime_error("JMD dumping already in progress");
2012-01-06 17:28:01 +02:00
unsigned long level2 = (unsigned long)level2;
2011-10-12 03:29:52 +03:00
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();
2011-10-12 03:29:52 +03:00
throw std::runtime_error(x.str());
}
messages << "Dumping to " << prefix << " at level " << level2 << std::endl;
});
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) {
if(!vid_dumper)
throw std::runtime_error("No JMD video dump in progress");
2011-10-12 03:29:52 +03:00
try {
vid_dumper->on_dump_end();
messages << "JMD Dump finished" << std::endl;
2011-10-12 03:29:52 +03:00
} catch(std::bad_alloc& e) {
throw;
} catch(std::exception& e) {
messages << "Error ending JMD dump: " << e.what() << std::endl;
2011-10-12 03:29:52 +03:00
}
delete vid_dumper;
vid_dumper = NULL;
});
}