2011-09-15 16:42:52 +03:00
|
|
|
#include "lua.hpp"
|
|
|
|
#include "avidump.hpp"
|
2011-09-16 11:25:55 +03:00
|
|
|
#include "sox.hpp"
|
2011-09-14 23:16:24 +03:00
|
|
|
#include "settings.hpp"
|
2011-09-17 01:05:41 +03:00
|
|
|
#include "window.hpp"
|
2011-09-13 17:50:18 +03:00
|
|
|
#include <iomanip>
|
|
|
|
#include <cassert>
|
|
|
|
#include <cstring>
|
|
|
|
#include <sstream>
|
|
|
|
#include <zlib.h>
|
|
|
|
#include "misc.hpp"
|
2011-09-15 16:42:52 +03:00
|
|
|
#include "avsnoop.hpp"
|
2011-09-14 20:06:36 +03:00
|
|
|
#include "command.hpp"
|
2011-09-13 17:50:18 +03:00
|
|
|
|
2011-09-14 20:06:36 +03:00
|
|
|
namespace
|
2011-09-13 17:50:18 +03:00
|
|
|
{
|
2011-09-14 23:16:24 +03:00
|
|
|
boolean_setting dump_large("large-video", false);
|
2011-09-15 22:46:20 +03:00
|
|
|
numeric_setting dtb("default-top-border", 0, 8191, 0);
|
|
|
|
numeric_setting dbb("default-bottom-border", 0, 8191, 0);
|
|
|
|
numeric_setting dlb("default-left-border", 0, 8191, 0);
|
|
|
|
numeric_setting drb("default-right-border", 0, 8191, 0);
|
2011-09-16 15:24:34 +03:00
|
|
|
numeric_setting max_frames_per_segment("max-frames-per-segment", 0, 999999999, 0);
|
2011-09-14 20:06:36 +03:00
|
|
|
|
2011-09-15 16:42:52 +03:00
|
|
|
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);
|
2011-09-16 11:25:55 +03:00
|
|
|
soxdumper = new sox_dumper(prefix + ".sox", 32040.5, 2);
|
|
|
|
dcounter = 0;
|
2011-09-15 16:42:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
~avi_avsnoop() throw()
|
|
|
|
{
|
|
|
|
delete vid_dumper;
|
2011-09-16 11:25:55 +03:00
|
|
|
delete soxdumper;
|
2011-09-15 16:42:52 +03:00
|
|
|
}
|
|
|
|
|
2011-09-17 00:06:20 +03:00
|
|
|
void frame(struct lcscreen& _frame, uint32_t fps_n, uint32_t fps_d)
|
2011-09-15 16:42:52 +03:00
|
|
|
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]);
|
2011-09-15 22:56:33 +03:00
|
|
|
|
2011-09-15 16:42:52 +03:00
|
|
|
struct lua_render_context lrc;
|
|
|
|
render_queue rq;
|
2011-09-15 22:46:20 +03:00
|
|
|
lrc.left_gap = dlb;
|
|
|
|
lrc.right_gap = drb;
|
|
|
|
lrc.bottom_gap = dbb;
|
|
|
|
lrc.top_gap = dtb;
|
2011-09-15 16:42:52 +03:00
|
|
|
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];
|
2011-09-17 00:06:20 +03:00
|
|
|
lua_callback_do_video(&lrc);
|
2011-09-15 16:42:52 +03:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2011-09-16 11:25:55 +03:00
|
|
|
dcounter += 81;
|
|
|
|
if(dcounter < 64081)
|
|
|
|
vid_dumper->on_sample(l, r);
|
|
|
|
else
|
|
|
|
dcounter -= 64081;
|
|
|
|
soxdumper->sample(l, r);
|
2011-09-15 16:42:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void end() throw(std::bad_alloc, std::runtime_error)
|
|
|
|
{
|
|
|
|
vid_dumper->on_end();
|
2011-09-16 11:25:55 +03:00
|
|
|
soxdumper->close();
|
2011-09-15 16:42:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void gameinfo(const std::string& gamename, const std::list<std::pair<std::string, std::string>>&
|
2011-09-15 22:56:33 +03:00
|
|
|
authors, double gametime, const std::string& rerecords) throw(std::bad_alloc,
|
2011-09-15 16:42:52 +03:00
|
|
|
std::runtime_error)
|
|
|
|
{
|
|
|
|
//We don't have place for this info and thus ignore it.
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
avidumper* vid_dumper;
|
2011-09-16 11:25:55 +03:00
|
|
|
sox_dumper* soxdumper;
|
2011-09-15 16:42:52 +03:00
|
|
|
screen dscr;
|
2011-09-16 11:25:55 +03:00
|
|
|
unsigned dcounter;
|
2011-09-15 16:42:52 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
avi_avsnoop* vid_dumper;
|
|
|
|
|
2011-09-14 20:06:36 +03:00
|
|
|
class dump_video_command : public command
|
|
|
|
{
|
|
|
|
public:
|
2011-09-15 16:42:52 +03:00
|
|
|
dump_video_command() throw(std::bad_alloc) : command("dump-avi") {}
|
2011-09-17 01:05:41 +03:00
|
|
|
void invoke(const std::string& args) throw(std::bad_alloc, std::runtime_error)
|
2011-09-14 20:06:36 +03:00
|
|
|
{
|
|
|
|
tokensplitter t(args);
|
|
|
|
std::string level = t;
|
|
|
|
std::string prefix = t.tail();
|
|
|
|
if(prefix == "")
|
|
|
|
throw std::runtime_error("Expected prefix");
|
|
|
|
if(vid_dumper)
|
2011-09-15 16:42:52 +03:00
|
|
|
throw std::runtime_error("AVI dumping already in progress");
|
2011-09-14 20:06:36 +03:00
|
|
|
unsigned long level2;
|
|
|
|
try {
|
|
|
|
level2 = parse_value<unsigned long>(level);
|
|
|
|
if(level2 > 18)
|
|
|
|
throw std::runtime_error("Level must be 0-18");
|
|
|
|
} catch(std::bad_alloc& e) {
|
2011-09-17 00:06:20 +03:00
|
|
|
throw;
|
2011-09-14 20:06:36 +03:00
|
|
|
} catch(std::runtime_error& e) {
|
2011-09-15 16:42:52 +03:00
|
|
|
throw std::runtime_error("Bad AVI compression level '" + level + "': " + e.what());
|
2011-09-14 20:06:36 +03:00
|
|
|
}
|
|
|
|
struct avi_info parameters;
|
|
|
|
parameters.compression_level = (level2 > 9) ? (level2 - 9) : level2;
|
|
|
|
parameters.audio_sampling_rate = 32000;
|
|
|
|
parameters.keyframe_interval = (level2 > 9) ? 300 : 1;
|
2011-09-16 15:24:34 +03:00
|
|
|
parameters.max_frames_per_segment = max_frames_per_segment;
|
2011-09-14 20:06:36 +03:00
|
|
|
try {
|
2011-09-15 16:42:52 +03:00
|
|
|
vid_dumper = new avi_avsnoop(prefix, parameters);
|
2011-09-14 20:06:36 +03:00
|
|
|
} catch(std::bad_alloc& e) {
|
2011-09-17 00:06:20 +03:00
|
|
|
throw;
|
2011-09-14 20:06:36 +03:00
|
|
|
} catch(std::exception& e) {
|
|
|
|
std::ostringstream x;
|
|
|
|
x << "Error starting dump: " << e.what();
|
|
|
|
throw std::runtime_error(x.str());
|
|
|
|
}
|
2011-09-17 01:05:41 +03:00
|
|
|
window::out() << "Dumping to " << prefix << " at level " << level2 << std::endl;
|
2011-09-13 17:50:18 +03:00
|
|
|
}
|
2011-09-15 16:42:52 +03:00
|
|
|
std::string get_short_help() throw(std::bad_alloc) { return "Start AVI capture"; }
|
2011-09-14 20:06:36 +03:00
|
|
|
std::string get_long_help() throw(std::bad_alloc)
|
|
|
|
{
|
2011-09-15 16:42:52 +03:00
|
|
|
return "Syntax: dump-avi <level> <prefix>\n"
|
|
|
|
"Start AVI capture to <prefix> using compression\n"
|
2011-09-14 20:06:36 +03:00
|
|
|
"level <level> (0-18).\n";
|
2011-09-13 17:50:18 +03:00
|
|
|
}
|
2011-09-14 20:06:36 +03:00
|
|
|
} dump_video;
|
|
|
|
|
|
|
|
class end_video_command : public command
|
|
|
|
{
|
|
|
|
public:
|
2011-09-15 16:42:52 +03:00
|
|
|
end_video_command() throw(std::bad_alloc) : command("end-avi") {}
|
2011-09-17 01:05:41 +03:00
|
|
|
void invoke(const std::string& args) throw(std::bad_alloc, std::runtime_error)
|
2011-09-14 20:06:36 +03:00
|
|
|
{
|
|
|
|
if(args != "")
|
|
|
|
throw std::runtime_error("This command does not take parameters");
|
|
|
|
if(!vid_dumper)
|
|
|
|
throw std::runtime_error("No video dump in progress");
|
|
|
|
try {
|
2011-09-15 16:42:52 +03:00
|
|
|
vid_dumper->end();
|
2011-09-17 01:05:41 +03:00
|
|
|
window::out() << "Dump finished" << std::endl;
|
2011-09-14 20:06:36 +03:00
|
|
|
} catch(std::bad_alloc& e) {
|
2011-09-17 00:06:20 +03:00
|
|
|
throw;
|
2011-09-14 20:06:36 +03:00
|
|
|
} catch(std::exception& e) {
|
2011-09-17 01:05:41 +03:00
|
|
|
window::out() << "Error ending dump: " << e.what() << std::endl;
|
2011-09-14 20:06:36 +03:00
|
|
|
}
|
|
|
|
delete vid_dumper;
|
|
|
|
vid_dumper = NULL;
|
2011-09-13 17:50:18 +03:00
|
|
|
}
|
2011-09-15 16:42:52 +03:00
|
|
|
std::string get_short_help() throw(std::bad_alloc) { return "End AVI capture"; }
|
2011-09-14 20:06:36 +03:00
|
|
|
std::string get_long_help() throw(std::bad_alloc)
|
|
|
|
{
|
2011-09-15 16:42:52 +03:00
|
|
|
return "Syntax: end-avi\n"
|
|
|
|
"End a AVI capture.\n";
|
2011-09-14 20:06:36 +03:00
|
|
|
}
|
|
|
|
} end_vieo;
|
2011-09-13 17:50:18 +03:00
|
|
|
}
|