Make video dumping to go through generic A/V snooping interface
This commit is contained in:
parent
b015451c38
commit
6877c2f63b
9 changed files with 420 additions and 188 deletions
9
Makefile
9
Makefile
|
@ -1,13 +1,18 @@
|
||||||
FONT_SRC := unifontfull-5.1.20080820.hex
|
FONT_SRC := unifontfull-5.1.20080820.hex
|
||||||
CC := g++-4.5
|
CC := g++-4.5
|
||||||
HOSTCC = $(CC)
|
HOSTCC = $(CC)
|
||||||
OBJECTS = controllerdata.o fieldsplit.o memorymanip.o misc.o movie.o moviefile.o render.o rom.o zip.o fonts/font.o videodumper.o videodumper2.o keymapper.o window.o window-sdl.o settings.o framerate.o mainloop.o rrdata.o specialframes.o png.o lsnesrc.o memorywatch.o command.o
|
OBJECTS = controllerdata.o fieldsplit.o memorymanip.o misc.o movie.o moviefile.o render.o rom.o zip.o fonts/font.o keymapper.o window.o window-sdl.o settings.o framerate.o mainloop.o rrdata.o specialframes.o png.o lsnesrc.o memorywatch.o command.o avsnoop.o
|
||||||
|
|
||||||
|
#AVI dumper
|
||||||
|
OBJECTS += avidump/avidump-control.o avidump/avidump.o
|
||||||
|
|
||||||
PROGRAMS = lsnes.exe movietrunctest.exe
|
PROGRAMS = lsnes.exe movietrunctest.exe
|
||||||
|
|
||||||
CFLAGS = $(shell sdl-config --cflags) $(USER_CFLAGS)
|
CFLAGS = -I. $(shell sdl-config --cflags) $(USER_CFLAGS)
|
||||||
HOSTCCFLAGS = $(USER_HOSTCCFLAGS)
|
HOSTCCFLAGS = $(USER_HOSTCCFLAGS)
|
||||||
LDFLAGS = $(shell sdl-config --libs) $(USER_LDFLAGS)
|
LDFLAGS = $(shell sdl-config --libs) $(USER_LDFLAGS)
|
||||||
|
|
||||||
|
#Lua.
|
||||||
ifdef NO_LUA
|
ifdef NO_LUA
|
||||||
OBJECTS += lua-dummy.o
|
OBJECTS += lua-dummy.o
|
||||||
else
|
else
|
||||||
|
|
|
@ -1,28 +1,94 @@
|
||||||
#include "videodumper.hpp"
|
#include "lua.hpp"
|
||||||
|
#include "avidump.hpp"
|
||||||
#include "settings.hpp"
|
#include "settings.hpp"
|
||||||
#include "videodumper2.hpp"
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
#include "misc.hpp"
|
#include "misc.hpp"
|
||||||
#include "fieldsplit.hpp"
|
#include "avsnoop.hpp"
|
||||||
#include "command.hpp"
|
#include "command.hpp"
|
||||||
|
|
||||||
avidumper* vid_dumper = NULL;
|
|
||||||
|
|
||||||
void update_movie_state();
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
screen dscr;
|
|
||||||
boolean_setting dump_large("large-video", false);
|
boolean_setting dump_large("large-video", false);
|
||||||
|
|
||||||
|
class avi_avsnoop : public av_snooper
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
avi_avsnoop(const std::string& prefix, struct avi_info parameters) throw(std::bad_alloc)
|
||||||
|
{
|
||||||
|
vid_dumper = new avidumper(prefix, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
~avi_avsnoop() throw()
|
||||||
|
{
|
||||||
|
delete vid_dumper;
|
||||||
|
}
|
||||||
|
|
||||||
|
void frame(struct lcscreen& _frame, uint32_t fps_n, uint32_t fps_d, window* win, bool dummy)
|
||||||
|
throw(std::bad_alloc, std::runtime_error)
|
||||||
|
{
|
||||||
|
vid_dumper->wait_idle();
|
||||||
|
uint32_t hscl = 1;
|
||||||
|
uint32_t vscl = 1;
|
||||||
|
if(dump_large && _frame.width < 400)
|
||||||
|
hscl = 2;
|
||||||
|
if(dump_large && _frame.height < 400)
|
||||||
|
vscl = 2;
|
||||||
|
uint32_t _magic = 403703808;
|
||||||
|
uint8_t* magic = reinterpret_cast<uint8_t*>(&_magic);
|
||||||
|
dscr.set_palette(magic[2], magic[1], magic[0]);
|
||||||
|
|
||||||
|
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 * hscl;
|
||||||
|
lrc.height = _frame.height * vscl;
|
||||||
|
lrc.rshift = magic[2];
|
||||||
|
lrc.gshift = magic[1];
|
||||||
|
lrc.bshift = magic[0];
|
||||||
|
lua_callback_do_video(&lrc, win);
|
||||||
|
|
||||||
|
dscr.reallocate(lrc.left_gap + hscl * _frame.width + lrc.right_gap, lrc.top_gap + vscl *
|
||||||
|
_frame.height + lrc.bottom_gap, lrc.left_gap, lrc.top_gap, true);
|
||||||
|
dscr.copy_from(_frame, hscl, vscl);
|
||||||
|
rq.run(dscr);
|
||||||
|
vid_dumper->on_frame(dscr.memory, dscr.width, dscr.height, fps_n, fps_d);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sample(short l, short r) throw(std::bad_alloc, std::runtime_error)
|
||||||
|
{
|
||||||
|
vid_dumper->on_sample(l, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
void end() throw(std::bad_alloc, std::runtime_error)
|
||||||
|
{
|
||||||
|
vid_dumper->on_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void gameinfo(const std::string& gamename, const std::list<std::pair<std::string, std::string>>&
|
||||||
|
authors, double gametime, const std::string& rerecords) throw(std::bad_alloc,
|
||||||
|
std::runtime_error)
|
||||||
|
{
|
||||||
|
//We don't have place for this info and thus ignore it.
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
avidumper* vid_dumper;
|
||||||
|
screen dscr;
|
||||||
|
};
|
||||||
|
|
||||||
|
avi_avsnoop* vid_dumper;
|
||||||
|
|
||||||
class dump_video_command : public command
|
class dump_video_command : public command
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
dump_video_command() throw(std::bad_alloc) : command("dump-video") {}
|
dump_video_command() throw(std::bad_alloc) : command("dump-avi") {}
|
||||||
void invoke(const std::string& args, window* win) throw(std::bad_alloc, std::runtime_error)
|
void invoke(const std::string& args, window* win) throw(std::bad_alloc, std::runtime_error)
|
||||||
{
|
{
|
||||||
tokensplitter t(args);
|
tokensplitter t(args);
|
||||||
|
@ -31,7 +97,7 @@ namespace
|
||||||
if(prefix == "")
|
if(prefix == "")
|
||||||
throw std::runtime_error("Expected prefix");
|
throw std::runtime_error("Expected prefix");
|
||||||
if(vid_dumper)
|
if(vid_dumper)
|
||||||
throw std::runtime_error("Video dumping already in progress");
|
throw std::runtime_error("AVI dumping already in progress");
|
||||||
unsigned long level2;
|
unsigned long level2;
|
||||||
try {
|
try {
|
||||||
level2 = parse_value<unsigned long>(level);
|
level2 = parse_value<unsigned long>(level);
|
||||||
|
@ -40,7 +106,7 @@ namespace
|
||||||
} catch(std::bad_alloc& e) {
|
} catch(std::bad_alloc& e) {
|
||||||
OOM_panic(win);
|
OOM_panic(win);
|
||||||
} catch(std::runtime_error& e) {
|
} catch(std::runtime_error& e) {
|
||||||
throw std::runtime_error("Bad video compression level '" + level + "': " + e.what());
|
throw std::runtime_error("Bad AVI compression level '" + level + "': " + e.what());
|
||||||
}
|
}
|
||||||
struct avi_info parameters;
|
struct avi_info parameters;
|
||||||
parameters.compression_level = (level2 > 9) ? (level2 - 9) : level2;
|
parameters.compression_level = (level2 > 9) ? (level2 - 9) : level2;
|
||||||
|
@ -50,7 +116,7 @@ namespace
|
||||||
parameters.audio_native_sampling_rate = 32040.5;
|
parameters.audio_native_sampling_rate = 32040.5;
|
||||||
parameters.keyframe_interval = (level2 > 9) ? 300 : 1;
|
parameters.keyframe_interval = (level2 > 9) ? 300 : 1;
|
||||||
try {
|
try {
|
||||||
vid_dumper = new avidumper(prefix, parameters);
|
vid_dumper = new avi_avsnoop(prefix, parameters);
|
||||||
} catch(std::bad_alloc& e) {
|
} catch(std::bad_alloc& e) {
|
||||||
OOM_panic(win);
|
OOM_panic(win);
|
||||||
} catch(std::exception& e) {
|
} catch(std::exception& e) {
|
||||||
|
@ -59,13 +125,12 @@ namespace
|
||||||
throw std::runtime_error(x.str());
|
throw std::runtime_error(x.str());
|
||||||
}
|
}
|
||||||
out(win) << "Dumping to " << prefix << " at level " << level2 << std::endl;
|
out(win) << "Dumping to " << prefix << " at level " << level2 << std::endl;
|
||||||
update_movie_state();
|
|
||||||
}
|
}
|
||||||
std::string get_short_help() throw(std::bad_alloc) { return "Start video capture"; }
|
std::string get_short_help() throw(std::bad_alloc) { return "Start AVI capture"; }
|
||||||
std::string get_long_help() throw(std::bad_alloc)
|
std::string get_long_help() throw(std::bad_alloc)
|
||||||
{
|
{
|
||||||
return "Syntax: dump-video <level> <prefix>\n"
|
return "Syntax: dump-avi <level> <prefix>\n"
|
||||||
"Start video capture to <prefix> using compression\n"
|
"Start AVI capture to <prefix> using compression\n"
|
||||||
"level <level> (0-18).\n";
|
"level <level> (0-18).\n";
|
||||||
}
|
}
|
||||||
} dump_video;
|
} dump_video;
|
||||||
|
@ -73,7 +138,7 @@ namespace
|
||||||
class end_video_command : public command
|
class end_video_command : public command
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
end_video_command() throw(std::bad_alloc) : command("end-video") {}
|
end_video_command() throw(std::bad_alloc) : command("end-avi") {}
|
||||||
void invoke(const std::string& args, window* win) throw(std::bad_alloc, std::runtime_error)
|
void invoke(const std::string& args, window* win) throw(std::bad_alloc, std::runtime_error)
|
||||||
{
|
{
|
||||||
if(args != "")
|
if(args != "")
|
||||||
|
@ -81,7 +146,7 @@ namespace
|
||||||
if(!vid_dumper)
|
if(!vid_dumper)
|
||||||
throw std::runtime_error("No video dump in progress");
|
throw std::runtime_error("No video dump in progress");
|
||||||
try {
|
try {
|
||||||
vid_dumper->on_end();
|
vid_dumper->end();
|
||||||
out(win) << "Dump finished" << std::endl;
|
out(win) << "Dump finished" << std::endl;
|
||||||
} catch(std::bad_alloc& e) {
|
} catch(std::bad_alloc& e) {
|
||||||
OOM_panic(win);
|
OOM_panic(win);
|
||||||
|
@ -90,89 +155,12 @@ namespace
|
||||||
}
|
}
|
||||||
delete vid_dumper;
|
delete vid_dumper;
|
||||||
vid_dumper = NULL;
|
vid_dumper = NULL;
|
||||||
update_movie_state();
|
|
||||||
}
|
}
|
||||||
std::string get_short_help() throw(std::bad_alloc) { return "End video capture"; }
|
std::string get_short_help() throw(std::bad_alloc) { return "End AVI capture"; }
|
||||||
std::string get_long_help() throw(std::bad_alloc)
|
std::string get_long_help() throw(std::bad_alloc)
|
||||||
{
|
{
|
||||||
return "Syntax: end-video\n"
|
return "Syntax: end-avi\n"
|
||||||
"End a video capture.\n";
|
"End a AVI capture.\n";
|
||||||
}
|
}
|
||||||
} end_vieo;
|
} end_vieo;
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void end_vid_dump() throw(std::bad_alloc, std::runtime_error)
|
|
||||||
{
|
|
||||||
if(vid_dumper)
|
|
||||||
try {
|
|
||||||
vid_dumper->on_end();
|
|
||||||
} catch(std::bad_alloc& e) {
|
|
||||||
throw;
|
|
||||||
} catch(std::exception& e) {
|
|
||||||
std::cerr << "Error ending dump: " << e.what() << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void dump_frame(lcscreen& ls, render_queue* rq, uint32_t left, uint32_t right, uint32_t top, uint32_t bottom,
|
|
||||||
bool region, window* win) throw(std::bad_alloc, std::runtime_error)
|
|
||||||
{
|
|
||||||
if(vid_dumper)
|
|
||||||
try {
|
|
||||||
vid_dumper->wait_idle();
|
|
||||||
uint32_t hscl = 1;
|
|
||||||
uint32_t vscl = 1;
|
|
||||||
if(dump_large && ls.width < 400)
|
|
||||||
hscl = 2;
|
|
||||||
if(dump_large && ls.height < 400)
|
|
||||||
vscl = 2;
|
|
||||||
uint32_t _magic = 403703808;
|
|
||||||
uint8_t* magic = reinterpret_cast<uint8_t*>(&_magic);
|
|
||||||
dscr.reallocate(left + hscl * ls.width + right, top + vscl * ls.height + bottom, left, top,
|
|
||||||
true);
|
|
||||||
dscr.set_palette(magic[2], magic[1], magic[0]);
|
|
||||||
dscr.copy_from(ls, hscl, vscl);
|
|
||||||
if(rq)
|
|
||||||
rq->run(dscr);
|
|
||||||
assert(dscr.memory);
|
|
||||||
assert(dscr.width);
|
|
||||||
assert(dscr.height);
|
|
||||||
uint32_t fps_n = 10738636;
|
|
||||||
uint32_t fps_d = 178683;
|
|
||||||
if(region) {
|
|
||||||
fps_n = 322445;
|
|
||||||
fps_d = 6448;
|
|
||||||
}
|
|
||||||
vid_dumper->on_frame(dscr.memory, dscr.width, dscr.height, fps_n, fps_d);
|
|
||||||
} catch(std::bad_alloc& e) {
|
|
||||||
OOM_panic(win);
|
|
||||||
} catch(std::exception& e) {
|
|
||||||
out(win) << "Error sending video frame: " << e.what() << std::endl;
|
|
||||||
}
|
|
||||||
if(rq)
|
|
||||||
rq->clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void dump_audio_sample(int16_t l_sample, int16_t r_sample, window* win) throw(std::bad_alloc, std::runtime_error)
|
|
||||||
{
|
|
||||||
if(vid_dumper)
|
|
||||||
try {
|
|
||||||
vid_dumper->on_sample(l_sample, r_sample);
|
|
||||||
} catch(std::bad_alloc& e) {
|
|
||||||
OOM_panic(win);
|
|
||||||
} catch(std::exception& e) {
|
|
||||||
out(win) << "Error sending audio sample: " << e.what() << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool dump_in_progress() throw()
|
|
||||||
{
|
|
||||||
return (vid_dumper != NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void video_fill_shifts(uint32_t& r, uint32_t& g, uint32_t& b)
|
|
||||||
{
|
|
||||||
r = dscr.active_rshift;
|
|
||||||
g = dscr.active_gshift;
|
|
||||||
b = dscr.active_bshift;
|
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
#include "videodumper.hpp"
|
#include "avidump.hpp"
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstring>
|
#include <cstring>
|
|
@ -1,5 +1,5 @@
|
||||||
#ifndef _videodumper__hpp__included__
|
#ifndef _avidump__hpp__included__
|
||||||
#define _videodumper__hpp__included__
|
#define _avidump__hpp__included__
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <iostream>
|
#include <iostream>
|
140
avsnoop.cpp
Normal file
140
avsnoop.cpp
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
#include "avsnoop.hpp"
|
||||||
|
#include "misc.hpp"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::list<av_snooper*>* snoopers;
|
||||||
|
std::list<av_snooper::dump_notification*> notifiers;
|
||||||
|
std::string s_gamename;
|
||||||
|
std::string s_rerecords;
|
||||||
|
double s_gametime;
|
||||||
|
std::list<std::pair<std::string, std::string>> s_authors;
|
||||||
|
bool gameinfo_set;
|
||||||
|
}
|
||||||
|
|
||||||
|
av_snooper::av_snooper() throw(std::bad_alloc)
|
||||||
|
{
|
||||||
|
if(!snoopers)
|
||||||
|
snoopers = new std::list<av_snooper*>();
|
||||||
|
snoopers->push_back(this);
|
||||||
|
for(auto i = notifiers.begin(); i != notifiers.end(); i++)
|
||||||
|
(*i)->dump_starting();
|
||||||
|
}
|
||||||
|
|
||||||
|
av_snooper::~av_snooper() throw()
|
||||||
|
{
|
||||||
|
if(!snoopers)
|
||||||
|
return;
|
||||||
|
for(auto i = snoopers->begin(); i != snoopers->end(); i++)
|
||||||
|
if(*i == this) {
|
||||||
|
snoopers->erase(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for(auto i = notifiers.begin(); i != notifiers.end(); i++)
|
||||||
|
(*i)->dump_ending();
|
||||||
|
}
|
||||||
|
|
||||||
|
void av_snooper::frame(struct lcscreen& _frame, uint32_t fps_n, uint32_t fps_d, window* win) throw()
|
||||||
|
{
|
||||||
|
if(!snoopers)
|
||||||
|
return;
|
||||||
|
for(auto i = snoopers->begin(); i != snoopers->end(); i++)
|
||||||
|
try {
|
||||||
|
(*i)->frame(_frame, fps_n, fps_d, win, true);
|
||||||
|
} catch(std::bad_alloc& e) {
|
||||||
|
OOM_panic(win);
|
||||||
|
} catch(std::exception& e) {
|
||||||
|
try {
|
||||||
|
win->message(std::string("Error dumping frame: ") + e.what());
|
||||||
|
} catch(...) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void av_snooper::sample(short l, short r, window* win) throw()
|
||||||
|
{
|
||||||
|
if(!snoopers)
|
||||||
|
return;
|
||||||
|
for(auto i = snoopers->begin(); i != snoopers->end(); i++)
|
||||||
|
try {
|
||||||
|
(*i)->sample(l, r);
|
||||||
|
} catch(std::bad_alloc& e) {
|
||||||
|
OOM_panic(win);
|
||||||
|
} catch(std::exception& e) {
|
||||||
|
try {
|
||||||
|
win->message(std::string("Error dumping sample: ") + e.what());
|
||||||
|
} catch(...) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void av_snooper::end(window* win) throw()
|
||||||
|
{
|
||||||
|
if(!snoopers)
|
||||||
|
return;
|
||||||
|
for(auto i = snoopers->begin(); i != snoopers->end(); i++)
|
||||||
|
try {
|
||||||
|
(*i)->end();
|
||||||
|
} catch(std::bad_alloc& e) {
|
||||||
|
OOM_panic(win);
|
||||||
|
} catch(std::exception& e) {
|
||||||
|
try {
|
||||||
|
win->message(std::string("Error ending dump: ") + e.what());
|
||||||
|
} catch(...) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void av_snooper::gameinfo(const std::string& gamename, const std::list<std::pair<std::string, std::string>>&
|
||||||
|
authors, double gametime, const std::string& rerecords, window* win) throw(std::bad_alloc)
|
||||||
|
{
|
||||||
|
if(!snoopers)
|
||||||
|
return;
|
||||||
|
s_gamename = gamename;
|
||||||
|
s_authors = authors;
|
||||||
|
s_gametime = gametime;
|
||||||
|
s_rerecords = rerecords;
|
||||||
|
gameinfo_set = true;
|
||||||
|
for(auto i = snoopers->begin(); i != snoopers->end(); i++)
|
||||||
|
(*i)->send_gameinfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
void av_snooper::send_gameinfo() throw()
|
||||||
|
{
|
||||||
|
if(gameinfo_set)
|
||||||
|
try {
|
||||||
|
gameinfo(s_gamename, s_authors, s_gametime, s_rerecords);
|
||||||
|
} catch(...) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool av_snooper::dump_in_progress() throw()
|
||||||
|
{
|
||||||
|
return (snoopers && !snoopers->empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
av_snooper::dump_notification::~dump_notification() throw()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void av_snooper::dump_notification::dump_starting() throw()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void av_snooper::dump_notification::dump_ending() throw()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void av_snooper::add_dump_notifier(av_snooper::dump_notification& notifier) throw(std::bad_alloc)
|
||||||
|
{
|
||||||
|
notifiers.push_back(¬ifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
void av_snooper::remove_dump_notifier(av_snooper::dump_notification& notifier) throw()
|
||||||
|
{
|
||||||
|
for(auto i = notifiers.begin(); i != notifiers.end(); i++)
|
||||||
|
if(*i == ¬ifier) {
|
||||||
|
notifiers.erase(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
157
avsnoop.hpp
Normal file
157
avsnoop.hpp
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
#ifndef _avsnoop__hpp__included__
|
||||||
|
#define _avsnoop__hpp__included__
|
||||||
|
|
||||||
|
#include "render.hpp"
|
||||||
|
#include <list>
|
||||||
|
#include <string>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include "window.hpp"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A/V snooper.
|
||||||
|
*/
|
||||||
|
class av_snooper
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create new A/V snooper.
|
||||||
|
*
|
||||||
|
* throws std::bad_alloc: Not enough memory.
|
||||||
|
*/
|
||||||
|
av_snooper() throw(std::bad_alloc);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy A/V snooper. This will not call end method.
|
||||||
|
*/
|
||||||
|
~av_snooper() throw();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dump a frame.
|
||||||
|
*
|
||||||
|
* parameter _frame: The frame to dump.
|
||||||
|
* parameter fps_n: Current fps numerator.
|
||||||
|
* parameter fps_d: Current fps denomerator.
|
||||||
|
* throws std::bad_alloc: Not enough memory.
|
||||||
|
* throws std::runtime_error: Error dumping frame.
|
||||||
|
*/
|
||||||
|
virtual void frame(struct lcscreen& _frame, uint32_t fps_n, uint32_t fps_d, window* win, bool dummy)
|
||||||
|
throw(std::bad_alloc, std::runtime_error) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dump a frame.
|
||||||
|
*
|
||||||
|
* parameter _frame: The frame to dump.
|
||||||
|
* parameter fps_n: Current fps numerator.
|
||||||
|
* parameter fps_d: Current fps denomerator.
|
||||||
|
* parameter win: Graphics system handle.
|
||||||
|
*/
|
||||||
|
static void frame(struct lcscreen& _frame, uint32_t fps_n, uint32_t fps_d, window* win) throw();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dump a sample.
|
||||||
|
*
|
||||||
|
* parameter l: Left channel sample.
|
||||||
|
* parameter r: Right channel sample.
|
||||||
|
* throws std::bad_alloc: Not enough memory.
|
||||||
|
* throws std::runtime_error: Error dumping sample.
|
||||||
|
*/
|
||||||
|
virtual void sample(short l, short r) throw(std::bad_alloc, std::runtime_error) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dump a sample.
|
||||||
|
*
|
||||||
|
* parameter l: Left channel sample.
|
||||||
|
* parameter r: Right channel sample.
|
||||||
|
* parameter win: Graphics system handle.
|
||||||
|
*/
|
||||||
|
static void sample(short l, short r, window* win) throw();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* End dump.
|
||||||
|
*
|
||||||
|
* throws std::bad_alloc: Not enough memory.
|
||||||
|
* throws std::runtime_error: Error dumping sample.
|
||||||
|
*/
|
||||||
|
virtual void end() throw(std::bad_alloc, std::runtime_error) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* End dump.
|
||||||
|
*
|
||||||
|
* parameter win: Graphics system handle.
|
||||||
|
*/
|
||||||
|
static void end(window* win) throw();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify game information.
|
||||||
|
*
|
||||||
|
* parameter gamename: Name of the game.
|
||||||
|
* parameter authors: Authors of the run.
|
||||||
|
* parameter gametime: Game time.
|
||||||
|
* parameter rercords: Rerecord count.
|
||||||
|
* throws std::bad_alloc: Not enough memory.
|
||||||
|
* throws std::runtime_error: Error recording this info.
|
||||||
|
*/
|
||||||
|
virtual void gameinfo(const std::string& gamename, const std::list<std::pair<std::string, std::string>>&
|
||||||
|
authors, double gametime, const std::string& rerecords) throw(std::bad_alloc, std::runtime_error) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify game information.
|
||||||
|
*
|
||||||
|
* parameter gamename: Name of the game.
|
||||||
|
* parameter authors: Authors of the run.
|
||||||
|
* parameter gametime: Game time.
|
||||||
|
* parameter rercords: Rerecord count.
|
||||||
|
* parameter win: Graphics system handle.
|
||||||
|
* throws std::bad_alloc Not enough memory.
|
||||||
|
*/
|
||||||
|
static void gameinfo(const std::string& gamename, const std::list<std::pair<std::string, std::string>>&
|
||||||
|
authors, double gametime, const std::string& rerecords, window* win) throw(std::bad_alloc);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send game info. This causes gameinfo method to be called on object this method is called on.
|
||||||
|
*/
|
||||||
|
void send_gameinfo() throw();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is there dump in progress?
|
||||||
|
*
|
||||||
|
* returns: True if dump is in progress, false if not.
|
||||||
|
*/
|
||||||
|
static bool dump_in_progress() throw();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifier for dumps starting/ending.
|
||||||
|
*/
|
||||||
|
class dump_notification
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~dump_notification() throw();
|
||||||
|
/**
|
||||||
|
* New dump starting.
|
||||||
|
*/
|
||||||
|
virtual void dump_starting() throw();
|
||||||
|
/**
|
||||||
|
* Dump ending.
|
||||||
|
*/
|
||||||
|
virtual void dump_ending() throw();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a notifier.
|
||||||
|
*
|
||||||
|
* parameter notifier: New notifier to add.
|
||||||
|
*/
|
||||||
|
static void add_dump_notifier(dump_notification& notifier) throw(std::bad_alloc);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a notifier.
|
||||||
|
*
|
||||||
|
* parameter notifier: Existing notifier to remove.
|
||||||
|
*/
|
||||||
|
static void remove_dump_notifier(dump_notification& notifier) throw();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -5,7 +5,6 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include "misc.hpp"
|
#include "misc.hpp"
|
||||||
#include "videodumper2.hpp"
|
|
||||||
#include "memorymanip.hpp"
|
#include "memorymanip.hpp"
|
||||||
#include "fieldsplit.hpp"
|
#include "fieldsplit.hpp"
|
||||||
#include "command.hpp"
|
#include "command.hpp"
|
||||||
|
|
46
mainloop.cpp
46
mainloop.cpp
|
@ -1,4 +1,5 @@
|
||||||
#include "mainloop.hpp"
|
#include "mainloop.hpp"
|
||||||
|
#include "avsnoop.hpp"
|
||||||
#include "command.hpp"
|
#include "command.hpp"
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include "framerate.hpp"
|
#include "framerate.hpp"
|
||||||
|
@ -20,7 +21,6 @@
|
||||||
#include "memorymanip.hpp"
|
#include "memorymanip.hpp"
|
||||||
#include "keymapper.hpp"
|
#include "keymapper.hpp"
|
||||||
#include "render.hpp"
|
#include "render.hpp"
|
||||||
#include "videodumper2.hpp"
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "lsnes.hpp"
|
#include "lsnes.hpp"
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
@ -710,6 +710,18 @@ namespace
|
||||||
queued_saves.insert(filename);
|
queued_saves.insert(filename);
|
||||||
win->message("Pending save on '" + filename + "'");
|
win->message("Pending save on '" + filename + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class dump_watch : public av_snooper::dump_notification
|
||||||
|
{
|
||||||
|
void dump_starting() throw()
|
||||||
|
{
|
||||||
|
update_movie_state();
|
||||||
|
}
|
||||||
|
void dump_ending() throw()
|
||||||
|
{
|
||||||
|
update_movie_state();
|
||||||
|
}
|
||||||
|
} dumpwatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<char>& get_host_memory()
|
std::vector<char>& get_host_memory()
|
||||||
|
@ -745,7 +757,7 @@ void update_movie_state()
|
||||||
x << "PLAY ";
|
x << "PLAY ";
|
||||||
else
|
else
|
||||||
x << "REC ";
|
x << "REC ";
|
||||||
if(dump_in_progress())
|
if(av_snooper::dump_in_progress())
|
||||||
x << "CAP ";
|
x << "CAP ";
|
||||||
_status["Flags"] = x.str();
|
_status["Flags"] = x.str();
|
||||||
}
|
}
|
||||||
|
@ -833,19 +845,15 @@ class my_interface : public SNES::Interface
|
||||||
location_special = SPECIAL_FRAME_VIDEO;
|
location_special = SPECIAL_FRAME_VIDEO;
|
||||||
update_movie_state();
|
update_movie_state();
|
||||||
redraw_framebuffer();
|
redraw_framebuffer();
|
||||||
|
uint32_t fps_n, fps_d;
|
||||||
struct lua_render_context lrc;
|
if(region) {
|
||||||
render_queue rq;
|
fps_n = 322445;
|
||||||
lrc.left_gap = 0;
|
fps_d = 6448;
|
||||||
lrc.right_gap = 0;
|
} else {
|
||||||
lrc.bottom_gap = 0;
|
fps_n = 10738636;
|
||||||
lrc.top_gap = 0;
|
fps_d = 178683;
|
||||||
lrc.queue = &rq;
|
}
|
||||||
lrc.width = framebuffer.width;
|
av_snooper::frame(ls, fps_n, fps_d, win);
|
||||||
lrc.height = framebuffer.height;
|
|
||||||
video_fill_shifts(lrc.rshift, lrc.gshift, lrc.bshift);
|
|
||||||
lua_callback_do_video(&lrc, win);
|
|
||||||
dump_frame(framebuffer, &rq, lrc.left_gap, lrc.right_gap, lrc.top_gap, lrc.bottom_gap, region, win);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio_sample(int16_t l_sample, int16_t r_sample)
|
void audio_sample(int16_t l_sample, int16_t r_sample)
|
||||||
|
@ -853,14 +861,14 @@ class my_interface : public SNES::Interface
|
||||||
uint16_t _l = l_sample;
|
uint16_t _l = l_sample;
|
||||||
uint16_t _r = r_sample;
|
uint16_t _r = r_sample;
|
||||||
win->play_audio_sample(_l + 32768, _r + 32768);
|
win->play_audio_sample(_l + 32768, _r + 32768);
|
||||||
dump_audio_sample(_l, _r, win);
|
av_snooper::sample(_l, _r, win);
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio_sample(uint16_t l_sample, uint16_t r_sample)
|
void audio_sample(uint16_t l_sample, uint16_t r_sample)
|
||||||
{
|
{
|
||||||
//Yes, this interface is broken. The samples are signed but are passed as unsigned!
|
//Yes, this interface is broken. The samples are signed but are passed as unsigned!
|
||||||
win->play_audio_sample(l_sample + 32768, r_sample + 32768);
|
win->play_audio_sample(l_sample + 32768, r_sample + 32768);
|
||||||
dump_audio_sample(l_sample, r_sample, win);
|
av_snooper::sample(l_sample, r_sample, win);
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t input_poll(bool port, SNES::Input::Device device, unsigned index, unsigned id)
|
int16_t input_poll(bool port, SNES::Input::Device device, unsigned index, unsigned id)
|
||||||
|
@ -1639,7 +1647,7 @@ void main_loop(window* _win, struct loaded_rom& rom, struct moviefile& initial)
|
||||||
lua_callback_startup(win);
|
lua_callback_startup(win);
|
||||||
|
|
||||||
//print_controller_mappings();
|
//print_controller_mappings();
|
||||||
|
av_snooper::add_dump_notifier(dumpwatch);
|
||||||
win->set_main_surface(scr);
|
win->set_main_surface(scr);
|
||||||
redraw_framebuffer();
|
redraw_framebuffer();
|
||||||
win->paused(false);
|
win->paused(false);
|
||||||
|
@ -1689,6 +1697,6 @@ void main_loop(window* _win, struct loaded_rom& rom, struct moviefile& initial)
|
||||||
win->wait_msec(to_wait_frame(get_ticks_msec()));
|
win->wait_msec(to_wait_frame(get_ticks_msec()));
|
||||||
first_round = false;
|
first_round = false;
|
||||||
}
|
}
|
||||||
end_vid_dump();
|
av_snooper::end(win);
|
||||||
SNES::system.interface = old_inteface;
|
SNES::system.interface = old_inteface;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,65 +0,0 @@
|
||||||
#ifndef _videodumper2__hpp__included__
|
|
||||||
#define _videodumper2__hpp__included__
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
|
||||||
#include <vector>
|
|
||||||
#include <list>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include "window.hpp"
|
|
||||||
#include "videodumper.hpp"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Forcibly ends dumping. Mainly useful for quitting.
|
|
||||||
*
|
|
||||||
* throws std::bad_alloc: Not enough memory.
|
|
||||||
* throws std::runtime_error: Failed to end dump.
|
|
||||||
*/
|
|
||||||
void end_vid_dump() throw(std::bad_alloc, std::runtime_error);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dumps a frame. Does nothing if dumping is not in progress.
|
|
||||||
*
|
|
||||||
* parameter ls: Screen to dump.
|
|
||||||
* parameter rq: Render queue to run.
|
|
||||||
* parameter left: Left border.
|
|
||||||
* parameter right: Right border.
|
|
||||||
* parameter top: Top border.
|
|
||||||
* parameter bottom: Bottom border.
|
|
||||||
* parameter region: True if PAL, false if NTSC.
|
|
||||||
* parameter win: Graphics system handle.
|
|
||||||
* throws std::bad_alloc: Not enough memory.
|
|
||||||
* throws std::runtime_error: Failed to dump frame.
|
|
||||||
*/
|
|
||||||
void dump_frame(lcscreen& ls, render_queue* rq, uint32_t left, uint32_t right, uint32_t top, uint32_t bottom,
|
|
||||||
bool region, window* win) throw(std::bad_alloc, std::runtime_error);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dumps one sample of audio. Does nothing if dumping is not in progress.
|
|
||||||
*
|
|
||||||
* parameter l_sample Left channel sample (-32768-32767)
|
|
||||||
* parameter r_sample Right channel sample (-32768-32767)
|
|
||||||
* parameter win Graphics System handle.
|
|
||||||
* throws std::bad_alloc: Not enough memory.
|
|
||||||
* throws std::runtime_error: Failed to dump sample.
|
|
||||||
*/
|
|
||||||
void dump_audio_sample(int16_t l_sample, int16_t r_sample, window* win) throw(std::bad_alloc, std::runtime_error);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Is the dump in progress?
|
|
||||||
*
|
|
||||||
* returns: True if dump is in progress, false if not.
|
|
||||||
*/
|
|
||||||
bool dump_in_progress() throw();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fill rendering shifts.
|
|
||||||
*
|
|
||||||
* parameter r: Shift for red component is written here.
|
|
||||||
* parameter g: Shift for green component is written here.
|
|
||||||
* parameter b: Shift for blue component is written here.
|
|
||||||
*/
|
|
||||||
void video_fill_shifts(uint32_t& r, uint32_t& g, uint32_t& b);
|
|
||||||
|
|
||||||
#endif
|
|
Loading…
Add table
Reference in a new issue