Direct framebuffer
[lsnes.git] / src / core / avidump-control.cpp
blob29fed837f3b00eb9dfdec62238db459990dcb1e7
1 #include "cscd.hpp"
2 #include "sox.hpp"
4 #include "core/command.hpp"
5 #include "core/dispatch.hpp"
6 #include "core/lua.hpp"
7 #include "core/misc.hpp"
8 #include "core/settings.hpp"
10 #include <iomanip>
11 #include <cassert>
12 #include <cstring>
13 #include <sstream>
14 #include <zlib.h>
16 namespace
18 struct avi_info
20 unsigned compression_level;
21 uint32_t audio_sampling_rate;
22 uint32_t keyframe_interval;
23 uint32_t max_frames_per_segment;
26 boolean_setting dump_large("avi-large", false);
27 numeric_setting dtb("avi-top-border", 0, 8191, 0);
28 numeric_setting dbb("avi-bottom-border", 0, 8191, 0);
29 numeric_setting dlb("avi-left-border", 0, 8191, 0);
30 numeric_setting drb("avi-right-border", 0, 8191, 0);
31 numeric_setting max_frames_per_segment("avi-maxframes", 0, 999999999, 0);
33 class avi_avsnoop : public information_dispatch
35 public:
36 avi_avsnoop(const std::string& prefix, struct avi_info parameters) throw(std::bad_alloc)
37 : information_dispatch("dump-avi-cscd")
39 _parameters = parameters;
40 avi_cscd_dumper::global_parameters gp;
41 avi_cscd_dumper::segment_parameters sp;
42 gp.sampling_rate = parameters.audio_sampling_rate;
43 gp.channel_count = 2;
44 gp.audio_16bit = true;
45 sp.fps_n = 60;
46 sp.fps_d = 1;
47 sp.dataformat = avi_cscd_dumper::PIXFMT_RGB15_NE;
48 sp.width = 256;
49 sp.height = 224;
50 sp.default_stride = true;
51 sp.stride = 512;
52 sp.keyframe_distance = parameters.keyframe_interval;
53 sp.deflate_level = parameters.compression_level;
54 sp.max_segment_frames = parameters.max_frames_per_segment;
55 vid_dumper = new avi_cscd_dumper(prefix, gp, sp);
56 soundrate = get_sound_rate();
57 audio_record_rate = parameters.audio_sampling_rate;
58 soxdumper = new sox_dumper(prefix + ".sox", static_cast<double>(soundrate.first) /
59 soundrate.second, 2);
60 dcounter = 0;
61 have_dumped_frame = false;
64 ~avi_avsnoop() throw()
66 delete vid_dumper;
67 delete soxdumper;
70 void on_frame(struct lcscreen& _frame, uint32_t fps_n, uint32_t fps_d)
72 uint32_t hscl = 1;
73 uint32_t vscl = 1;
74 if(dump_large && _frame.width < 400)
75 hscl = 2;
76 if(dump_large && _frame.height < 400)
77 vscl = 2;
79 struct lua_render_context lrc;
80 render_queue rq;
81 lrc.left_gap = dlb;
82 lrc.right_gap = drb;
83 lrc.bottom_gap = dbb;
84 lrc.top_gap = dtb;
85 lrc.queue = &rq;
86 lrc.width = _frame.width * hscl;
87 lrc.height = _frame.height * vscl;
88 lua_callback_do_video(&lrc);
90 vid_dumper->wait_frame_processing();
91 avi_cscd_dumper::segment_parameters sp;
92 sp.fps_n = fps_n;
93 sp.fps_d = fps_d;
94 uint32_t x = 0x18100800;
95 if(*reinterpret_cast<const uint8_t*>(&x) == 0x18)
96 sp.dataformat = avi_cscd_dumper::PIXFMT_XRGB;
97 else
98 sp.dataformat = avi_cscd_dumper::PIXFMT_BGRX;
99 sp.width = lrc.left_gap + hscl * _frame.width + lrc.right_gap;
100 sp.height = lrc.top_gap + vscl * _frame.height + lrc.bottom_gap;
101 sp.default_stride = true;
102 sp.stride = 1024;
103 sp.keyframe_distance = _parameters.keyframe_interval;
104 sp.deflate_level = _parameters.compression_level;
105 sp.max_segment_frames = _parameters.max_frames_per_segment;
106 vid_dumper->set_segment_parameters(sp);
107 dscr.reallocate(lrc.left_gap + hscl * _frame.width + lrc.right_gap, lrc.top_gap + vscl *
108 _frame.height + lrc.bottom_gap, false);
109 dscr.set_origin(lrc.left_gap, lrc.top_gap);
110 dscr.copy_from(_frame, hscl, vscl);
111 rq.run(dscr);
112 vid_dumper->video(dscr.memory);
113 have_dumped_frame = true;
114 vid_dumper->wait_frame_processing();
117 void on_sample(short l, short r)
119 dcounter += soundrate.first;
120 while(dcounter < soundrate.second * audio_record_rate + soundrate.first) {
121 if(have_dumped_frame)
122 vid_dumper->audio(&l, &r, 1, avi_cscd_dumper::SNDFMT_SIGNED_16NE);
123 dcounter += soundrate.first;
125 dcounter -= (soundrate.second * audio_record_rate + soundrate.first);
126 if(have_dumped_frame)
127 soxdumper->sample(l, r);
130 void on_dump_end()
132 vid_dumper->end();
133 soxdumper->close();
136 bool get_dumper_flag() throw()
138 return true;
140 private:
141 avi_cscd_dumper* vid_dumper;
142 sox_dumper* soxdumper;
143 screen dscr;
144 unsigned dcounter;
145 struct avi_info _parameters;
146 bool have_dumped_frame;
147 std::pair<uint32_t, uint32_t> soundrate;
148 uint32_t audio_record_rate;
151 avi_avsnoop* vid_dumper;
153 function_ptr_command<const std::string&> avi_dump("dump-avi", "Start AVI capture",
154 "Syntax: dump-avi <level> <prefix>\nStart AVI capture to <prefix> using compression\n"
155 "level <level> (0-18).\n",
156 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
157 tokensplitter t(args);
158 std::string level = t;
159 std::string prefix = t.tail();
160 if(prefix == "")
161 throw std::runtime_error("Expected prefix");
162 if(vid_dumper)
163 throw std::runtime_error("AVI(CSCD) dumping already in progress");
164 unsigned long level2;
165 try {
166 level2 = parse_value<unsigned long>(level);
167 if(level2 > 18)
168 throw std::runtime_error("AVI(CSCD) level must be 0-18");
169 } catch(std::bad_alloc& e) {
170 throw;
171 } catch(std::runtime_error& e) {
172 throw std::runtime_error("Bad AVI(CSCD) compression level '" + level + "': " +
173 e.what());
175 struct avi_info parameters;
176 parameters.compression_level = (level2 > 9) ? (level2 - 9) : level2;
177 parameters.audio_sampling_rate = 32000;
178 parameters.keyframe_interval = (level2 > 9) ? 300 : 1;
179 parameters.max_frames_per_segment = max_frames_per_segment;
180 try {
181 vid_dumper = new avi_avsnoop(prefix, parameters);
182 } catch(std::bad_alloc& e) {
183 throw;
184 } catch(std::exception& e) {
185 std::ostringstream x;
186 x << "Error starting AVI(CSCD) dump: " << e.what();
187 throw std::runtime_error(x.str());
189 messages << "Dumping AVI(CSCD) to " << prefix << " at level " << level2 << std::endl;
192 function_ptr_command<> end_avi("end-avi", "End AVI capture",
193 "Syntax: end-avi\nEnd a AVI capture.\n",
194 []() throw(std::bad_alloc, std::runtime_error) {
195 if(!vid_dumper)
196 throw std::runtime_error("No AVI(CSCD) video dump in progress");
197 try {
198 vid_dumper->on_dump_end();
199 messages << "AVI(CSCD) Dump finished" << std::endl;
200 } catch(std::bad_alloc& e) {
201 throw;
202 } catch(std::exception& e) {
203 messages << "Error ending AVI(CSCD) dump: " << e.what() << std::endl;
205 delete vid_dumper;
206 vid_dumper = NULL;