Lua: Don't lua_error() out of context with pending dtors
[lsnes.git] / src / video / avi.cpp
blob8fbae2e9b09ea060305003493694d7e20b05f844
1 #include "video/sox.hpp"
2 #include "video/avi/writer.hpp"
4 #include "video/avi/codec.hpp"
6 #include "core/advdumper.hpp"
7 #include "core/dispatch.hpp"
8 #include "lua/lua.hpp"
9 #include "library/minmax.hpp"
10 #include "library/workthread.hpp"
11 #include "core/messages.hpp"
12 #include "core/instance.hpp"
13 #include "core/settings.hpp"
14 #include "core/moviedata.hpp"
15 #include "core/moviefile.hpp"
16 #include "core/rom.hpp"
18 #include <iomanip>
19 #include <cassert>
20 #include <cstring>
21 #include <cmath>
22 #include <sstream>
23 #include <zlib.h>
24 #ifdef WITH_SECRET_RABBIT_CODE
25 #include <samplerate.h>
26 #endif
27 #define RESAMPLE_BUFFER 1024
29 namespace
31 class avi_avsnoop;
33 uint32_t rates[] = {8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000,
34 128000, 176400, 192000};
36 uint32_t topowerof2(uint32_t base)
38 if((base & (base - 1)) == 0)
39 return base; //Already power of two.
40 base |= (base >> 16);
41 base |= (base >> 8);
42 base |= (base >> 4);
43 base |= (base >> 2);
44 base |= (base >> 1);
45 return base + 1;
48 uint32_t get_rate(uint32_t n, uint32_t d, unsigned mode)
50 if(mode == 0) {
51 auto best = std::make_pair(1e99, static_cast<size_t>(0));
52 for(size_t i = 0; i < sizeof(rates) / sizeof(rates[0]); i++)
53 best = ::min(best, std::make_pair(fabs(log(static_cast<double>(d) * rates[i] / n)),
54 i));
55 return rates[best.second];
56 } else if(mode == 1) {
57 return static_cast<uint32_t>(n / d);
58 } else if(mode == 2) {
59 return static_cast<uint32_t>((n + d - 1) / d);
60 } else if(mode == 3) {
61 uint32_t x = n;
62 uint32_t y = d;
63 while(y) {
64 uint32_t t = x % d;
65 x = y;
66 y = t;
68 return static_cast<uint32_t>(n / x);
69 } else if(mode == 4) {
70 uint32_t base = static_cast<uint32_t>((n + d - 1) / d);
71 //Handle large values specially.
72 if(base > 0xFA000000U) return 0xFFFFFFFFU;
73 if(base > 0xBB800000U) return 0xFA000000U;
74 if(base > 0xAC440000U) return 0xBB800000U;
75 uint32_t base_A = topowerof2((base + 7999) / 8000);
76 uint32_t base_B = topowerof2((base + 11024) / 11025);
77 uint32_t base_C = topowerof2((base + 11999) / 12000);
78 return min(base_A * 8000, min(base_B * 11025, base_C * 12000));
79 } else if(mode == 5)
80 return 48000;
81 return 48000;
84 settingvar::supervariable<settingvar::model_bool<settingvar::yes_no>> dump_large(lsnes_setgrp, "avi-large",
85 "AVI‣Large dump", false);
86 settingvar::supervariable<settingvar::model_int<0, 32>> fixed_xfact(lsnes_setgrp, "avi-xfactor",
87 "AVI‣Fixed X factor", 0);
88 settingvar::supervariable<settingvar::model_int<0, 32>> fixed_yfact(lsnes_setgrp, "avi-yfactor",
89 "AVI‣Fixed Y factor", 0);
90 settingvar::supervariable<settingvar::model_int<0, 8191>> dtb(lsnes_setgrp, "avi-top-border",
91 "AVI‣Top padding", 0);
92 settingvar::supervariable<settingvar::model_int<0, 8191>> dbb(lsnes_setgrp, "avi-bottom-border",
93 "AVI‣Bottom padding", 0);
94 settingvar::supervariable<settingvar::model_int<0, 8191>> dlb(lsnes_setgrp, "avi-left-border",
95 "AVI‣Left padding", 0);
96 settingvar::supervariable<settingvar::model_int<0, 8191>> drb(lsnes_setgrp, "avi-right-border",
97 "AVI‣Right padding", 0);
98 settingvar::supervariable<settingvar::model_int<0, 999999999>> max_frames_per_segment(lsnes_setgrp,
99 "avi-maxframes", "AVI‣Max frames per segment", 0);
100 #ifdef WITH_SECRET_RABBIT_CODE
101 settingvar::enumeration soundrates {"nearest-common", "round-down", "round-up", "multiply",
102 "High quality 44.1kHz", "High quality 48kHz"};
103 settingvar::supervariable<settingvar::model_enumerated<&soundrates>> soundrate_setting(lsnes_setgrp,
104 "avi-soundrate", "AVI‣Sound mode", 5);
105 #else
106 settingvar::enumeration soundrates {"nearest-common", "round-down", "round-up", "multiply"};
107 settingvar::supervariable<settingvar::model_enumerated<&soundrates>> soundrate_setting(lsnes_setgrp,
108 "avi-soundrate", "AVI‣Sound mode", 2);
109 #endif
111 std::pair<avi_video_codec_type*, avi_audio_codec_type*> find_codecs(const std::string& mode)
113 avi_video_codec_type* v = NULL;
114 avi_audio_codec_type* a = NULL;
115 std::string _mode = mode;
116 size_t s = _mode.find_first_of("/");
117 if(s < _mode.length()) {
118 std::string vcodec = _mode.substr(0, s);
119 std::string acodec = _mode.substr(s + 1);
120 v = avi_video_codec_type::find(vcodec);
121 a = avi_audio_codec_type::find(acodec);
123 return std::make_pair(v, a);
126 struct avi_info
128 std::string prefix;
129 struct avi_video_codec* vcodec;
130 struct avi_audio_codec* acodec;
131 uint32_t sample_rate;
132 uint16_t audio_chans;
133 uint32_t max_frames;
136 struct avi_worker;
138 struct resample_worker : public workthread
140 resample_worker(avi_worker* _worker, double _ratio, uint32_t _nch);
141 ~resample_worker();
142 void entry();
143 void sendblock(short* block, size_t frames);
144 void sendend();
145 void set_ratio(double _ratio);
146 private:
147 std::vector<short> buffers;
148 std::vector<float> buffers2;
149 std::vector<float> buffers3;
150 std::vector<short> buffers4;
151 size_t bufused;
152 double ratio;
153 uint32_t nch;
154 void* resampler;
155 avi_worker* worker;
158 struct avi_worker : public workthread
160 avi_worker(const struct avi_info& info);
161 ~avi_worker();
162 void entry();
163 void queue_video(uint32_t* _frame, uint32_t stride, uint32_t width, uint32_t height, uint32_t fps_n,
164 uint32_t fps_d);
165 void queue_audio(int16_t* data, size_t samples);
166 private:
167 avi_writer aviout;
168 uint32_t* frame;
169 uint32_t frame_width;
170 uint32_t frame_stride;
171 uint32_t frame_height;
172 uint32_t frame_fps_n;
173 uint32_t frame_fps_d;
174 uint32_t segframes;
175 uint32_t max_segframes;
176 bool closed;
177 avi_video_codec* ivcodec;
180 #define WORKFLAG_QUEUE_FRAME 1
181 #define WORKFLAG_FLUSH 2
182 #define WORKFLAG_END 4
184 avi_worker::avi_worker(const struct avi_info& info)
185 : aviout(info.prefix, *info.vcodec, *info.acodec, info.sample_rate, info.audio_chans)
187 ivcodec = info.vcodec;
188 segframes = 0;
189 max_segframes = info.max_frames;
190 fire();
193 avi_worker::~avi_worker()
197 void avi_worker::queue_video(uint32_t* _frame, uint32_t stride, uint32_t width, uint32_t height,
198 uint32_t fps_n, uint32_t fps_d)
200 rethrow();
201 wait_busy();
202 frame = _frame;
203 frame_width = width;
204 frame_stride = stride;
205 frame_height = height;
206 frame_fps_n = fps_n;
207 frame_fps_d = fps_d;
208 set_busy();
209 set_workflag(WORKFLAG_QUEUE_FRAME);
212 void avi_worker::queue_audio(int16_t* data, size_t samples)
214 rethrow();
215 aviout.audio_queue().push(data, samples);
216 set_workflag(WORKFLAG_FLUSH);
219 void avi_worker::entry()
221 while(1) {
222 wait_workflag();
223 uint32_t work = clear_workflag(~workthread::quit_request);
224 //Flush the queue first in order to provode backpressure.
225 if(work & WORKFLAG_FLUSH) {
226 clear_workflag(WORKFLAG_FLUSH);
227 aviout.flush();
229 //Then add frames if any.
230 if(work & WORKFLAG_QUEUE_FRAME) {
231 frame_object f;
232 f.stride = frame_stride;
233 f.odata = new uint32_t[f.stride * frame_height + 16];
234 f.data = f.odata;
235 while(reinterpret_cast<size_t>(f.data) % 16)
236 f.data++;
237 f.width = frame_width;
238 f.height = frame_height;
239 f.fps_n = frame_fps_n;
240 f.fps_d = frame_fps_d;
241 f.force_break = (segframes == max_segframes && max_segframes > 0);
242 if(f.force_break)
243 segframes = 0;
244 auto wc = get_wait_count();
245 ivcodec->send_performance_counters(wc.first, wc.second);
246 framebuffer::copy_swap4(reinterpret_cast<uint8_t*>(f.data), frame,
247 f.stride * frame_height);
248 frame = NULL;
249 clear_workflag(WORKFLAG_QUEUE_FRAME);
250 clear_busy();
251 aviout.video_queue().push_back(f);
252 segframes++;
253 set_workflag(WORKFLAG_FLUSH);
255 //End the streaam if that is flagged.
256 if(work & WORKFLAG_END) {
257 if(!closed)
258 aviout.close();
259 closed = true;
260 clear_workflag(WORKFLAG_END | WORKFLAG_FLUSH | WORKFLAG_QUEUE_FRAME);
262 //If signaled to quit and no more work, do so.
263 if(work == workthread::quit_request) {
264 if(!closed)
265 aviout.close();
266 closed = true;
267 break;
272 resample_worker::resample_worker(avi_worker* _worker, double _ratio, uint32_t _nch)
273 : worker(_worker)
275 ratio = _ratio;
276 nch = _nch;
277 buffers.resize(RESAMPLE_BUFFER * nch);
278 buffers2.resize(RESAMPLE_BUFFER * nch);
279 buffers3.resize((RESAMPLE_BUFFER * nch * ratio) + 128 * nch);
280 buffers4.resize((RESAMPLE_BUFFER * nch * ratio) + 128 * nch);
281 bufused = 0;
282 #ifdef WITH_SECRET_RABBIT_CODE
283 int errc = 0;
284 resampler = src_new(SRC_SINC_BEST_QUALITY, nch, &errc);
285 if(errc)
286 throw std::runtime_error(std::string("Error initing libsamplerate: ") +
287 src_strerror(errc));
288 #else
289 throw std::runtime_error("HQ sample rate conversion not available");
290 #endif
291 fire();
294 resample_worker::~resample_worker()
296 #ifdef WITH_SECRET_RABBIT_CODE
297 src_delete((SRC_STATE*)resampler);
298 #endif
301 void resample_worker::set_ratio(double _ratio)
303 ratio = _ratio;
304 buffers3.resize((RESAMPLE_BUFFER * nch * ratio) + 128 * nch);
305 buffers4.resize((RESAMPLE_BUFFER * nch * ratio) + 128 * nch);
308 void resample_worker::sendend()
310 rethrow();
311 set_workflag(WORKFLAG_END);
312 request_quit();
315 void resample_worker::sendblock(short* block, size_t frames)
317 again:
318 rethrow();
319 wait_busy();
320 if(bufused + frames < RESAMPLE_BUFFER) {
321 memcpy(&buffers[bufused * nch], block, 2 * nch * frames);
322 bufused += frames;
323 block += (frames * nch);
324 frames = 0;
325 } else if(bufused < RESAMPLE_BUFFER) {
326 size_t processable = RESAMPLE_BUFFER - bufused;
327 memcpy(&buffers[bufused * nch], block, 2 * nch * processable);
328 block += (processable * nch);
329 frames -= processable;
330 bufused = RESAMPLE_BUFFER;
332 set_busy();
333 set_workflag(WORKFLAG_QUEUE_FRAME);
334 if(frames > 0)
335 goto again;
338 class avi_dumper_obj : public dumper_base
340 public:
341 avi_dumper_obj(master_dumper& _mdumper, dumper_factory_base& _fbase, const std::string& mode,
342 const std::string& prefix)
343 : dumper_base(_mdumper, _fbase), mdumper(_mdumper)
345 auto& core = CORE();
346 avi_video_codec_type* vcodec;
347 avi_audio_codec_type* acodec;
349 if(prefix == "")
350 throw std::runtime_error("Expected prefix");
351 struct avi_info info;
352 info.audio_chans = 2;
353 info.sample_rate = 32000;
354 info.max_frames = max_frames_per_segment(*core.settings);
355 info.prefix = prefix;
356 rpair(vcodec, acodec) = find_codecs(mode);
357 info.vcodec = vcodec->get_instance();
358 info.acodec = acodec->get_instance();
359 try {
360 unsigned srate_setting = soundrate_setting(*core.settings);
361 chans = info.audio_chans = 2;
362 soundrate = mdumper.get_rate();
363 audio_record_rate = info.sample_rate = get_rate(soundrate.first, soundrate.second,
364 srate_setting);
365 worker = new avi_worker(info);
366 soxdumper = new sox_dumper(info.prefix + ".sox",
367 static_cast<double>(soundrate.first) / soundrate.second, 2);
368 dcounter = 0;
369 have_dumped_frame = false;
370 resampler_w = NULL;
371 if(srate_setting == 4 || srate_setting == 5) {
372 double ratio = 1.0 * audio_record_rate * soundrate.second / soundrate.first;
373 sbuffer_fill = 0;
374 sbuffer.resize(RESAMPLE_BUFFER * chans);
375 resampler_w = new resample_worker(worker, ratio, chans);
377 mdumper.add_dumper(*this);
378 } catch(std::bad_alloc& e) {
379 throw;
380 } catch(std::exception& e) {
381 std::ostringstream x;
382 x << "Error starting AVI dump: " << e.what();
383 throw std::runtime_error(x.str());
385 messages << "Dumping AVI (" << vcodec->get_hname() << " / " << acodec->get_hname()
386 << ") to " << prefix << std::endl;
388 ~avi_dumper_obj() throw()
390 if(worker) {
391 if(resampler_w)
392 resampler_w->sendend();
393 worker->request_quit();
395 mdumper.drop_dumper(*this);
396 if(resampler_w)
397 delete resampler_w;
398 delete worker;
399 delete soxdumper;
400 messages << "AVI Dump finished" << std::endl;
402 void on_frame(struct framebuffer::raw& _frame, uint32_t fps_n, uint32_t fps_d)
404 auto& core = CORE();
405 uint32_t hscl = 1, vscl = 1;
406 unsigned fxfact = fixed_xfact(*core.settings);
407 unsigned fyfact = fixed_yfact(*core.settings);
408 if(fxfact != 0 && fyfact != 0) {
409 hscl = fxfact;
410 vscl = fyfact;
411 } else if(dump_large(*core.settings)) {
412 rpair(hscl, vscl) = core.rom->get_scale_factors(_frame.get_width(),
413 _frame.get_height());
415 if(!render_video_hud(dscr, _frame, fps_n, fps_d, hscl, vscl, dlb(*core.settings),
416 dtb(*core.settings), drb(*core.settings), dbb(*core.settings),
417 [this]() -> void { this->worker->wait_busy(); }))
418 return;
419 worker->queue_video(dscr.rowptr(0), dscr.get_stride(), dscr.get_width(), dscr.get_height(),
420 fps_n, fps_d);
421 have_dumped_frame = true;
423 void on_sample(short l, short r)
425 if(resampler_w) {
426 if(!have_dumped_frame)
427 return;
428 sbuffer[sbuffer_fill++] = l;
429 sbuffer[sbuffer_fill++] = r;
430 if(sbuffer_fill == sbuffer.size()) {
431 resampler_w->sendblock(&sbuffer[0], sbuffer_fill / chans);
432 sbuffer_fill = 0;
434 soxdumper->sample(l, r);
435 return;
437 short x[2];
438 x[0] = l;
439 x[1] = r;
440 dcounter += soundrate.first;
441 while(dcounter < soundrate.second * audio_record_rate + soundrate.first) {
442 if(have_dumped_frame)
443 worker->queue_audio(x, 2);
444 dcounter += soundrate.first;
446 dcounter -= (soundrate.second * audio_record_rate + soundrate.first);
447 if(have_dumped_frame)
448 soxdumper->sample(l, r);
450 void on_rate_change(uint32_t n, uint32_t d)
452 messages << "Warning: Changing AVI sound rate mid-dump is not supported!" << std::endl;
453 //Try to do it anyway.
454 soundrate = mdumper.get_rate();
455 dcounter = 0;
456 double ratio = 1.0 * audio_record_rate * soundrate.second / soundrate.first;
457 if(resampler_w)
458 resampler_w->set_ratio(ratio);
460 void on_gameinfo_change(const master_dumper::gameinfo& gi)
462 //Do nothing.
464 void on_end()
466 delete this;
468 avi_worker* worker;
469 resample_worker* resampler_w;
470 private:
471 master_dumper& mdumper;
472 sox_dumper* soxdumper;
473 framebuffer::fb<false> dscr;
474 unsigned dcounter;
475 bool have_dumped_frame;
476 std::pair<uint32_t, uint32_t> soundrate;
477 uint32_t audio_record_rate;
478 std::vector<short> sbuffer;
479 size_t sbuffer_fill;
480 uint32_t chans;
483 class adv_avi_dumper : public dumper_factory_base
485 public:
486 adv_avi_dumper() : dumper_factory_base("INTERNAL-AVI")
488 ctor_notify();
490 ~adv_avi_dumper() throw();
491 std::set<std::string> list_submodes() throw(std::bad_alloc)
493 std::set<std::string> x;
494 for(auto v = avi_video_codec_type::find_next(NULL); v; v = avi_video_codec_type::find_next(v))
495 for(auto a = avi_audio_codec_type::find_next(NULL); a;
496 a = avi_audio_codec_type::find_next(a))
497 x.insert(v->get_iname() + std::string("/") + a->get_iname());
498 return x;
500 unsigned mode_details(const std::string& mode) throw()
502 return target_type_prefix;
504 std::string mode_extension(const std::string& mode) throw()
506 return ""; //Not interesting
508 std::string name() throw(std::bad_alloc)
510 return "AVI (internal)";
512 std::string modename(const std::string& mode) throw(std::bad_alloc)
514 avi_video_codec_type* vcodec;
515 avi_audio_codec_type* acodec;
516 rpair(vcodec, acodec) = find_codecs(mode);
517 return vcodec->get_hname() + std::string(" / ") + acodec->get_hname();
519 avi_dumper_obj* start(master_dumper& _mdumper, const std::string& mode, const std::string& prefix)
520 throw(std::bad_alloc, std::runtime_error)
522 return new avi_dumper_obj(_mdumper, *this, mode, prefix);
524 } adv;
526 adv_avi_dumper::~adv_avi_dumper() throw()
530 void resample_worker::entry()
532 while(1) {
533 wait_workflag();
534 uint32_t work = clear_workflag(~workthread::quit_request);
535 if(work & (WORKFLAG_QUEUE_FRAME | WORKFLAG_END)) {
536 #ifdef WITH_SECRET_RABBIT_CODE
537 again:
538 SRC_DATA block;
539 src_short_to_float_array(&buffers[0], &buffers2[0], bufused * nch);
540 block.data_in = &buffers2[0];
541 block.data_out = &buffers3[0];
542 block.input_frames = bufused;
543 block.input_frames_used = 0;
544 block.output_frames = buffers3.size() / nch;
545 block.output_frames_gen = 0;
546 block.end_of_input = (work & WORKFLAG_END) ? 1 : 0;
547 block.src_ratio = ratio;
548 int errc = src_process((SRC_STATE*)resampler, &block);
549 if(errc)
550 throw std::runtime_error(std::string("Error using libsamplerate: ") +
551 src_strerror(errc));
552 src_float_to_short_array(&buffers3[0], &buffers4[0], block.output_frames_gen * nch);
553 worker->queue_audio(&buffers4[0], block.output_frames_gen * nch);
554 if((size_t)block.input_frames_used < bufused)
555 memmove(&buffers[0], &buffers[block.output_frames_gen * nch], (bufused -
556 block.input_frames_used) * nch);
557 bufused -= block.input_frames_used;
558 if(block.output_frames_gen > 0 && work & WORKFLAG_END)
559 goto again; //Try again to get all the samples.
560 #endif
561 clear_workflag(WORKFLAG_END | WORKFLAG_FLUSH | WORKFLAG_QUEUE_FRAME);
562 clear_busy();
563 if(work & WORKFLAG_END)
564 return;
566 if(work == workthread::quit_request)
567 break;