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"
9 #include "library/minmax.hpp"
10 #include "library/workthread.hpp"
11 #include "core/misc.hpp"
12 #include "core/settings.hpp"
13 #include "core/moviedata.hpp"
14 #include "core/moviefile.hpp"
22 #ifdef WITH_SECRET_RABBIT_CODE
23 #include <samplerate.h>
25 #define RESAMPLE_BUFFER 1024
30 avi_avsnoop
* vid_dumper
;
34 uint32_t rates
[] = {8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000,
35 128000, 176400, 192000};
37 uint32_t topowerof2(uint32_t base
)
39 if((base
& (base
- 1)) == 0)
40 return base
; //Already power of two.
49 uint32_t get_rate(uint32_t n
, uint32_t d
, unsigned mode
)
52 auto best
= std::make_pair(1e99
, static_cast<size_t>(0));
53 for(size_t i
= 0; i
< sizeof(rates
) / sizeof(rates
[0]); i
++)
54 best
= ::min(best
, std::make_pair(fabs(log(static_cast<double>(d
) * rates
[i
] / n
)),
56 return rates
[best
.second
];
57 } else if(mode
== 1) {
58 return static_cast<uint32_t>(n
/ d
);
59 } else if(mode
== 2) {
60 return static_cast<uint32_t>((n
+ d
- 1) / d
);
61 } else if(mode
== 3) {
69 return static_cast<uint32_t>(n
/ x
);
70 } else if(mode
== 4) {
71 uint32_t base
= static_cast<uint32_t>((n
+ d
- 1) / d
);
72 //Handle large values specially.
73 if(base
> 0xFA000000U
) return 0xFFFFFFFFU
;
74 if(base
> 0xBB800000U
) return 0xFA000000U
;
75 if(base
> 0xAC440000U
) return 0xBB800000U
;
76 uint32_t base_A
= topowerof2((base
+ 7999) / 8000);
77 uint32_t base_B
= topowerof2((base
+ 11024) / 11025);
78 uint32_t base_C
= topowerof2((base
+ 11999) / 12000);
79 return min(base_A
* 8000, min(base_B
* 11025, base_C
* 12000));
85 settingvar::variable
<settingvar::model_bool
<settingvar::yes_no
>> dump_large(lsnes_vset
, "avi-large",
86 "AVI‣Large dump", false);
87 settingvar::variable
<settingvar::model_int
<0, 32>> fixed_xfact(lsnes_vset
, "avi-xfactor",
88 "AVI‣Fixed X factor", 0);
89 settingvar::variable
<settingvar::model_int
<0, 32>> fixed_yfact(lsnes_vset
, "avi-yfactor",
90 "AVI‣Fixed Y factor", 0);
91 settingvar::variable
<settingvar::model_int
<0, 8191>> dtb(lsnes_vset
, "avi-top-border", "AVI‣Top padding", 0);
92 settingvar::variable
<settingvar::model_int
<0, 8191>> dbb(lsnes_vset
, "avi-bottom-border",
93 "AVI‣Bottom padding", 0);
94 settingvar::variable
<settingvar::model_int
<0, 8191>> dlb(lsnes_vset
, "avi-left-border",
95 "AVI‣Left padding", 0);
96 settingvar::variable
<settingvar::model_int
<0, 8191>> drb(lsnes_vset
, "avi-right-border", "AVI‣Right padding",
98 settingvar::variable
<settingvar::model_int
<0, 999999999>> max_frames_per_segment(lsnes_vset
, "avi-maxframes",
99 "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::variable
<settingvar::model_enumerated
<&soundrates
>> soundrate_setting(lsnes_vset
, "avi-soundrate",
104 "AVI‣Sound mode", 5);
106 settingvar::enumeration soundrates
{"nearest-common", "round-down", "round-up", "multiply"};
107 settingvar::variable
<settingvar::model_enumerated
<&soundrates
>> soundrate_setting(lsnes_vset
, "avi-soundrate",
108 "AVI‣Sound mode", 2);
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
);
129 struct avi_video_codec
* vcodec
;
130 struct avi_audio_codec
* acodec
;
131 uint32_t sample_rate
;
132 uint16_t audio_chans
;
136 struct resample_worker
: public worker_thread
138 resample_worker(double _ratio
, uint32_t _nch
);
141 void sendblock(short* block
, size_t frames
);
144 std::vector
<short> buffers
;
145 std::vector
<float> buffers2
;
146 std::vector
<float> buffers3
;
147 std::vector
<short> buffers4
;
154 struct avi_worker
: public worker_thread
156 avi_worker(const struct avi_info
& info
);
159 void queue_video(uint32_t* _frame
, uint32_t width
, uint32_t height
, uint32_t fps_n
, uint32_t fps_d
);
160 void queue_audio(int16_t* data
, size_t samples
);
164 uint32_t frame_width
;
165 uint32_t frame_height
;
166 uint32_t frame_fps_n
;
167 uint32_t frame_fps_d
;
169 uint32_t max_segframes
;
171 avi_video_codec
* ivcodec
;
174 #define WORKFLAG_QUEUE_FRAME 1
175 #define WORKFLAG_FLUSH 2
176 #define WORKFLAG_END 4
178 avi_worker::avi_worker(const struct avi_info
& info
)
179 : aviout(info
.prefix
, *info
.vcodec
, *info
.acodec
, info
.sample_rate
, info
.audio_chans
)
181 ivcodec
= info
.vcodec
;
183 max_segframes
= info
.max_frames
;
187 avi_worker::~avi_worker()
191 void avi_worker::queue_video(uint32_t* _frame
, uint32_t width
, uint32_t height
, uint32_t fps_n
, uint32_t fps_d
)
197 frame_height
= height
;
201 set_workflag(WORKFLAG_QUEUE_FRAME
);
204 void avi_worker::queue_audio(int16_t* data
, size_t samples
)
207 aviout
.audio_queue().push(data
, samples
);
208 set_workflag(WORKFLAG_FLUSH
);
211 void avi_worker::entry()
215 uint32_t work
= clear_workflag(~WORKFLAG_QUIT_REQUEST
);
216 //Flush the queue first in order to provode backpressure.
217 if(work
& WORKFLAG_FLUSH
) {
218 clear_workflag(WORKFLAG_FLUSH
);
221 //Then add frames if any.
222 if(work
& WORKFLAG_QUEUE_FRAME
) {
224 f
.data
= new uint32_t[frame_width
* frame_height
];
225 f
.width
= frame_width
;
226 f
.height
= frame_height
;
227 f
.fps_n
= frame_fps_n
;
228 f
.fps_d
= frame_fps_d
;
229 f
.force_break
= (segframes
== max_segframes
&& max_segframes
> 0);
232 auto wc
= get_wait_count();
233 ivcodec
->send_performance_counters(wc
.first
, wc
.second
);
234 memcpy(&f
.data
[0], frame
, 4 * frame_width
* frame_height
);
236 clear_workflag(WORKFLAG_QUEUE_FRAME
);
238 aviout
.video_queue().push_back(f
);
240 set_workflag(WORKFLAG_FLUSH
);
242 //End the streaam if that is flagged.
243 if(work
& WORKFLAG_END
) {
247 clear_workflag(WORKFLAG_END
| WORKFLAG_FLUSH
| WORKFLAG_QUEUE_FRAME
);
249 //If signaled to quit and no more work, do so.
250 if(work
== WORKFLAG_QUIT_REQUEST
) {
259 resample_worker::resample_worker(double _ratio
, uint32_t _nch
)
263 buffers
.resize(RESAMPLE_BUFFER
* nch
);
264 buffers2
.resize(RESAMPLE_BUFFER
* nch
);
265 buffers3
.resize((RESAMPLE_BUFFER
* nch
* ratio
) + 128 * nch
);
266 buffers4
.resize((RESAMPLE_BUFFER
* nch
* ratio
) + 128 * nch
);
268 #ifdef WITH_SECRET_RABBIT_CODE
270 resampler
= src_new(SRC_SINC_BEST_QUALITY
, nch
, &errc
);
272 throw std::runtime_error(std::string("Error initing libsamplerate: ") +
275 throw std::runtime_error("HQ sample rate conversion not available");
280 resample_worker::~resample_worker()
282 #ifdef WITH_SECRET_RABBIT_CODE
283 src_delete((SRC_STATE
*)resampler
);
287 void resample_worker::sendend()
290 set_workflag(WORKFLAG_END
);
294 void resample_worker::sendblock(short* block
, size_t frames
)
299 if(bufused
+ frames
< RESAMPLE_BUFFER
) {
300 memcpy(&buffers
[bufused
* nch
], block
, 2 * nch
* frames
);
302 block
+= (frames
* nch
);
304 } else if(bufused
< RESAMPLE_BUFFER
) {
305 size_t processable
= RESAMPLE_BUFFER
- bufused
;
306 memcpy(&buffers
[bufused
* nch
], block
, 2 * nch
* processable
);
307 block
+= (processable
* nch
);
308 frames
-= processable
;
309 bufused
= RESAMPLE_BUFFER
;
312 set_workflag(WORKFLAG_QUEUE_FRAME
);
319 class avi_avsnoop
: public information_dispatch
322 avi_avsnoop(avi_info
& info
) throw(std::bad_alloc
, std::runtime_error
)
323 : information_dispatch("dump-avi-int")
326 chans
= info
.audio_chans
= 2;
327 soundrate
= get_sound_rate();
328 audio_record_rate
= info
.sample_rate
= get_rate(soundrate
.first
, soundrate
.second
,
330 worker
= new avi_worker(info
);
331 soxdumper
= new sox_dumper(info
.prefix
+ ".sox", static_cast<double>(soundrate
.first
) /
332 soundrate
.second
, 2);
334 have_dumped_frame
= false;
336 if(soundrate_setting
== 4 || soundrate_setting
== 5) {
337 double ratio
= 1.0 * audio_record_rate
* soundrate
.second
/ soundrate
.first
;
339 sbuffer
.resize(RESAMPLE_BUFFER
* chans
);
340 resampler_w
= new resample_worker(ratio
, chans
);
344 ~avi_avsnoop() throw()
352 void on_frame(struct framebuffer::raw
& _frame
, uint32_t fps_n
, uint32_t fps_d
)
356 auto scl
= our_rom
.rtype
->get_scale_factors(_frame
.get_width(), _frame
.get_height());
357 if(fixed_xfact
!= 0 && fixed_yfact
!= 0) {
360 } else if(dump_large
) {
364 if(!render_video_hud(dscr
, _frame
, hscl
, vscl
, 0, 8, 16, dlb
, dtb
, drb
, dbb
, waitfn
)) {
365 akill
+= killed_audio_length(fps_n
, fps_d
, akillfrac
);
368 worker
->queue_video(dscr
.rowptr(0), dscr
.get_width(), dscr
.get_height(), fps_n
, fps_d
);
369 have_dumped_frame
= true;
372 void on_sample(short l
, short r
)
379 if(!have_dumped_frame
)
381 sbuffer
[sbuffer_fill
++] = l
;
382 sbuffer
[sbuffer_fill
++] = r
;
383 if(sbuffer_fill
== sbuffer
.size()) {
384 resampler_w
->sendblock(&sbuffer
[0], sbuffer_fill
/ chans
);
387 soxdumper
->sample(l
, r
);
393 dcounter
+= soundrate
.first
;
394 while(dcounter
< soundrate
.second
* audio_record_rate
+ soundrate
.first
) {
395 if(have_dumped_frame
)
396 worker
->queue_audio(x
, 2);
397 dcounter
+= soundrate
.first
;
399 dcounter
-= (soundrate
.second
* audio_record_rate
+ soundrate
.first
);
400 if(have_dumped_frame
)
401 soxdumper
->sample(l
, r
);
408 resampler_w
->sendend();
409 worker
->request_quit();
419 bool get_dumper_flag() throw()
424 resample_worker
* resampler_w
;
426 sox_dumper
* soxdumper
;
427 framebuffer::fb
<false> dscr
;
429 bool have_dumped_frame
;
430 std::pair
<uint32_t, uint32_t> soundrate
;
431 uint32_t audio_record_rate
;
432 std::vector
<short> sbuffer
;
439 vid_dumper
->worker
->wait_busy();
442 class adv_avi_dumper
: public adv_dumper
445 adv_avi_dumper() : adv_dumper("INTERNAL-AVI") {information_dispatch::do_dumper_update(); }
446 ~adv_avi_dumper() throw();
447 std::set
<std::string
> list_submodes() throw(std::bad_alloc
)
449 std::set
<std::string
> x
;
450 for(auto v
= avi_video_codec_type::find_next(NULL
); v
; v
= avi_video_codec_type::find_next(v
))
451 for(auto a
= avi_audio_codec_type::find_next(NULL
); a
;
452 a
= avi_audio_codec_type::find_next(a
))
453 x
.insert(v
->get_iname() + std::string("/") + a
->get_iname());
457 unsigned mode_details(const std::string
& mode
) throw()
459 return target_type_prefix
;
462 std::string
mode_extension(const std::string
& mode
) throw()
464 return ""; //Not interesting
468 std::string
name() throw(std::bad_alloc
)
470 return "AVI (internal)";
473 std::string
modename(const std::string
& mode
) throw(std::bad_alloc
)
475 auto c
= find_codecs(mode
);
476 return c
.first
->get_hname() + std::string(" / ") + c
.second
->get_hname();
481 return (vid_dumper
!= NULL
);
484 void start(const std::string
& mode
, const std::string
& prefix
) throw(std::bad_alloc
,
488 throw std::runtime_error("Expected prefix");
490 throw std::runtime_error("AVI dumping already in progress");
491 struct avi_info info
;
492 info
.audio_chans
= 2;
493 info
.sample_rate
= 32000;
494 info
.max_frames
= max_frames_per_segment
;
495 info
.prefix
= prefix
;
496 auto c
= find_codecs(mode
);
497 info
.vcodec
= c
.first
->get_instance();
498 info
.acodec
= c
.second
->get_instance();
500 vid_dumper
= new avi_avsnoop(info
);
501 } catch(std::bad_alloc
& e
) {
503 } catch(std::exception
& e
) {
504 std::ostringstream x
;
505 x
<< "Error starting AVI dump: " << e
.what();
506 throw std::runtime_error(x
.str());
508 messages
<< "Dumping AVI (" << c
.first
->get_hname() << " / " << c
.second
->get_hname()
509 << ") to " << prefix
<< std::endl
;
510 information_dispatch::do_dumper_update();
518 throw std::runtime_error("No AVI video dump in progress");
520 vid_dumper
->on_dump_end();
521 messages
<< "AVI Dump finished" << std::endl
;
522 } catch(std::bad_alloc
& e
) {
524 } catch(std::exception
& e
) {
525 messages
<< "Error ending AVI dump: " << e
.what() << std::endl
;
529 information_dispatch::do_dumper_update();
533 adv_avi_dumper::~adv_avi_dumper() throw()
537 void resample_worker::entry()
541 uint32_t work
= clear_workflag(~WORKFLAG_QUIT_REQUEST
);
542 if(work
& (WORKFLAG_QUEUE_FRAME
| WORKFLAG_END
)) {
543 #ifdef WITH_SECRET_RABBIT_CODE
546 src_short_to_float_array(&buffers
[0], &buffers2
[0], bufused
* nch
);
547 block
.data_in
= &buffers2
[0];
548 block
.data_out
= &buffers3
[0];
549 block
.input_frames
= bufused
;
550 block
.input_frames_used
= 0;
551 block
.output_frames
= buffers3
.size() / nch
;
552 block
.output_frames_gen
= 0;
553 block
.end_of_input
= (work
& WORKFLAG_END
) ? 1 : 0;
554 block
.src_ratio
= ratio
;
555 int errc
= src_process((SRC_STATE
*)resampler
, &block
);
557 throw std::runtime_error(std::string("Error using libsamplerate: ") +
559 src_float_to_short_array(&buffers3
[0], &buffers4
[0], block
.output_frames_gen
* nch
);
560 vid_dumper
->worker
->queue_audio(&buffers4
[0], block
.output_frames_gen
* nch
);
561 if((size_t)block
.input_frames_used
< bufused
)
562 memmove(&buffers
[0], &buffers
[block
.output_frames_gen
* nch
], (bufused
-
563 block
.input_frames_used
) * nch
);
564 bufused
-= block
.input_frames_used
;
565 if(block
.output_frames_gen
> 0 && work
& WORKFLAG_END
)
566 goto again
; //Try again to get all the samples.
568 clear_workflag(WORKFLAG_END
| WORKFLAG_FLUSH
| WORKFLAG_QUEUE_FRAME
);
570 if(work
& WORKFLAG_END
)
573 if(work
== WORKFLAG_QUIT_REQUEST
)