Let one control the method AVI dumper preturbs the sampling rate
[lsnes.git] / src / core / avidump-control.cpp
blob3c81e77670429afbcbf9b2776a104bc29cd4c9f3
1 #include "cscd.hpp"
2 #include "sox.hpp"
4 #include "core/advdumper.hpp"
5 #include "core/command.hpp"
6 #include "core/dispatch.hpp"
7 #include "core/lua.hpp"
8 #include "core/misc.hpp"
9 #include "core/settings.hpp"
11 #include <iomanip>
12 #include <cassert>
13 #include <cstring>
14 #include <cmath>
15 #include <sstream>
16 #include <zlib.h>
18 namespace
20 uint32_t rates[] = {8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000,
21 128000, 176400, 192000};
23 uint32_t get_rate(uint32_t n, uint32_t d, unsigned mode)
25 if(mode == 0) {
26 unsigned bestidx = 0;
27 double besterror = 1e99;
28 for(size_t i = 0; i < sizeof(rates) / sizeof(rates[0]); i++) {
29 double error = fabs(log(static_cast<double>(d) * rates[i] / n));
30 if(error < besterror) {
31 besterror = error;
32 bestidx = i;
35 return rates[bestidx];
36 } else if(mode == 1) {
37 return static_cast<uint32_t>(n / d);
38 } else if(mode == 2) {
39 return static_cast<uint32_t>((n + d - 1) / d);
43 struct avi_info
45 unsigned compression_level;
46 uint32_t audio_sampling_rate;
47 uint32_t keyframe_interval;
48 uint32_t max_frames_per_segment;
51 boolean_setting dump_large("avi-large", false);
52 numeric_setting dtb("avi-top-border", 0, 8191, 0);
53 numeric_setting dbb("avi-bottom-border", 0, 8191, 0);
54 numeric_setting dlb("avi-left-border", 0, 8191, 0);
55 numeric_setting drb("avi-right-border", 0, 8191, 0);
56 numeric_setting clevel("avi-compression", 0, 18, 7);
57 numeric_setting max_frames_per_segment("avi-maxframes", 0, 999999999, 0);
58 numeric_setting soundrate_setting("avi-soundrate", 0, 2, 0);
60 class avi_avsnoop : public information_dispatch
62 public:
63 avi_avsnoop(const std::string& prefix, struct avi_info parameters) throw(std::bad_alloc)
64 : information_dispatch("dump-avi-cscd")
66 enable_send_sound();
67 _parameters = parameters;
68 avi_cscd_dumper::global_parameters gp;
69 avi_cscd_dumper::segment_parameters sp;
70 soundrate = get_sound_rate();
71 gp.sampling_rate = get_rate(soundrate.first, soundrate.second, soundrate_setting);
72 gp.channel_count = 2;
73 gp.audio_16bit = true;
74 sp.fps_n = 60;
75 sp.fps_d = 1;
76 sp.dataformat = avi_cscd_dumper::PIXFMT_RGB15_NE;
77 sp.width = 256;
78 sp.height = 224;
79 sp.default_stride = true;
80 sp.stride = 512;
81 sp.keyframe_distance = parameters.keyframe_interval;
82 sp.deflate_level = parameters.compression_level;
83 sp.max_segment_frames = parameters.max_frames_per_segment;
84 vid_dumper = new avi_cscd_dumper(prefix, gp, sp);
85 audio_record_rate = parameters.audio_sampling_rate;
86 soxdumper = new sox_dumper(prefix + ".sox", static_cast<double>(soundrate.first) /
87 soundrate.second, 2);
88 dcounter = 0;
89 have_dumped_frame = false;
92 ~avi_avsnoop() throw()
94 delete vid_dumper;
95 delete soxdumper;
98 void on_frame(struct lcscreen& _frame, uint32_t fps_n, uint32_t fps_d)
100 uint32_t hscl = 1;
101 uint32_t vscl = 1;
102 if(dump_large && _frame.width < 400)
103 hscl = 2;
104 if(dump_large && _frame.height < 400)
105 vscl = 2;
107 struct lua_render_context lrc;
108 render_queue rq;
109 lrc.left_gap = dlb;
110 lrc.right_gap = drb;
111 lrc.bottom_gap = dbb;
112 lrc.top_gap = dtb;
113 lrc.queue = &rq;
114 lrc.width = _frame.width * hscl;
115 lrc.height = _frame.height * vscl;
116 lua_callback_do_video(&lrc);
118 vid_dumper->wait_frame_processing();
119 avi_cscd_dumper::segment_parameters sp;
120 sp.fps_n = fps_n;
121 sp.fps_d = fps_d;
122 uint32_t x = 0x18100800;
123 if(*reinterpret_cast<const uint8_t*>(&x) == 0x18)
124 sp.dataformat = avi_cscd_dumper::PIXFMT_XRGB;
125 else
126 sp.dataformat = avi_cscd_dumper::PIXFMT_BGRX;
127 sp.width = lrc.left_gap + hscl * _frame.width + lrc.right_gap;
128 sp.height = lrc.top_gap + vscl * _frame.height + lrc.bottom_gap;
129 sp.default_stride = true;
130 sp.stride = 1024;
131 sp.keyframe_distance = _parameters.keyframe_interval;
132 sp.deflate_level = _parameters.compression_level;
133 sp.max_segment_frames = _parameters.max_frames_per_segment;
134 vid_dumper->set_segment_parameters(sp);
135 dscr.reallocate(lrc.left_gap + hscl * _frame.width + lrc.right_gap, lrc.top_gap + vscl *
136 _frame.height + lrc.bottom_gap, false);
137 dscr.set_origin(lrc.left_gap, lrc.top_gap);
138 dscr.copy_from(_frame, hscl, vscl);
139 rq.run(dscr);
140 vid_dumper->video(dscr.memory);
141 have_dumped_frame = true;
142 //vid_dumper->wait_frame_processing();
145 void on_sample(short l, short r)
147 dcounter += soundrate.first;
148 while(dcounter < soundrate.second * audio_record_rate + soundrate.first) {
149 if(have_dumped_frame)
150 vid_dumper->audio(&l, &r, 1, avi_cscd_dumper::SNDFMT_SIGNED_16NE);
151 dcounter += soundrate.first;
153 dcounter -= (soundrate.second * audio_record_rate + soundrate.first);
154 if(have_dumped_frame)
155 soxdumper->sample(l, r);
158 void on_dump_end()
160 vid_dumper->wait_frame_processing();
161 vid_dumper->end();
162 soxdumper->close();
165 bool get_dumper_flag() throw()
167 return true;
169 private:
170 avi_cscd_dumper* vid_dumper;
171 sox_dumper* soxdumper;
172 screen dscr;
173 unsigned dcounter;
174 struct avi_info _parameters;
175 bool have_dumped_frame;
176 std::pair<uint32_t, uint32_t> soundrate;
177 uint32_t audio_record_rate;
180 avi_avsnoop* vid_dumper;
182 void startdump(const std::string& prefix)
184 if(prefix == "")
185 throw std::runtime_error("Expected prefix");
186 if(vid_dumper)
187 throw std::runtime_error("AVI(CSCD) dumping already in progress");
188 unsigned long level2 = (unsigned long)clevel;
189 struct avi_info parameters;
190 parameters.compression_level = (level2 > 9) ? (level2 - 9) : level2;
191 parameters.audio_sampling_rate = 32000;
192 parameters.keyframe_interval = (level2 > 9) ? 300 : 1;
193 parameters.max_frames_per_segment = max_frames_per_segment;
194 try {
195 vid_dumper = new avi_avsnoop(prefix, parameters);
196 } catch(std::bad_alloc& e) {
197 throw;
198 } catch(std::exception& e) {
199 std::ostringstream x;
200 x << "Error starting AVI(CSCD) dump: " << e.what();
201 throw std::runtime_error(x.str());
203 messages << "Dumping AVI(CSCD) to " << prefix << " at level " << level2 << std::endl;
204 information_dispatch::do_dumper_update();
207 void enddump()
209 if(!vid_dumper)
210 throw std::runtime_error("No AVI(CSCD) video dump in progress");
211 try {
212 vid_dumper->on_dump_end();
213 messages << "AVI(CSCD) Dump finished" << std::endl;
214 } catch(std::bad_alloc& e) {
215 throw;
216 } catch(std::exception& e) {
217 messages << "Error ending AVI(CSCD) dump: " << e.what() << std::endl;
219 delete vid_dumper;
220 vid_dumper = NULL;
221 information_dispatch::do_dumper_update();
224 function_ptr_command<const std::string&> avi_dump("dump-avi", "Start AVI capture",
225 "Syntax: dump-avi <prefix>\nStart AVI capture to <prefix>.\n",
226 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
227 tokensplitter t(args);
228 std::string prefix = t.tail();
229 startdump(prefix);
232 function_ptr_command<> end_avi("end-avi", "End AVI capture",
233 "Syntax: end-avi\nEnd a AVI capture.\n",
234 []() throw(std::bad_alloc, std::runtime_error) {
235 enddump();
238 class adv_avi_dumper : public adv_dumper
240 public:
241 adv_avi_dumper() : adv_dumper("INTERNAL-AVI-CSCD") {information_dispatch::do_dumper_update(); }
242 ~adv_avi_dumper() throw();
243 std::set<std::string> list_submodes() throw(std::bad_alloc)
245 std::set<std::string> x;
246 return x;
249 bool wants_prefix(const std::string& mode) throw()
251 return true;
254 std::string name() throw(std::bad_alloc)
256 return "AVI (internal CSCD)";
259 std::string modename(const std::string& mode) throw(std::bad_alloc)
261 return "";
264 bool busy()
266 return (vid_dumper != NULL);
269 void start(const std::string& mode, const std::string& targetname) throw(std::bad_alloc,
270 std::runtime_error)
272 startdump(targetname);
275 void end() throw()
277 enddump();
279 } adv;
281 adv_avi_dumper::~adv_avi_dumper() throw()