From c49f305892b3ff902a28a4794f6dd1c61e55dadf Mon Sep 17 00:00:00 2001 From: Ilari Liusvaara Date: Tue, 24 Apr 2012 15:05:29 +0300 Subject: [PATCH] AVI dumper: Do high-quality audio resampling in dedicated thread Gives faster dumping in mode 4 for quadcore. --- src/video/avi.cpp | 189 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 133 insertions(+), 56 deletions(-) diff --git a/src/video/avi.cpp b/src/video/avi.cpp index 8667128a..2e30e9be 100644 --- a/src/video/avi.cpp +++ b/src/video/avi.cpp @@ -25,6 +25,9 @@ namespace { + class avi_avsnoop; + avi_avsnoop* vid_dumper; + uint32_t rates[] = {8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000, 128000, 176400, 192000}; @@ -111,6 +114,24 @@ namespace uint32_t max_frames; }; + struct resample_worker : public worker_thread + { + resample_worker(double _ratio, uint32_t _nch); + ~resample_worker(); + void entry(); + void sendblock(short* block, size_t frames); + void sendend(); + private: + std::vector buffers; + std::vector buffers2; + std::vector buffers3; + std::vector buffers4; + size_t bufused; + double ratio; + uint32_t nch; + void* resampler; + }; + struct avi_worker : public worker_thread { avi_worker(const struct avi_info& info); @@ -134,7 +155,6 @@ namespace #define WORKFLAG_FLUSH 2 #define WORKFLAG_END 4 - avi_worker::avi_worker(const struct avi_info& info) : aviout(info.prefix, *info.vcodec, *info.acodec, info.sample_rate, info.audio_chans) { @@ -212,6 +232,64 @@ namespace } } + resample_worker::resample_worker(double _ratio, uint32_t _nch) + { + ratio = _ratio; + nch = _nch; + buffers.resize(RESAMPLE_BUFFER * nch); + buffers2.resize(RESAMPLE_BUFFER * nch); + buffers3.resize((RESAMPLE_BUFFER * nch * ratio) + 128 * nch); + buffers4.resize((RESAMPLE_BUFFER * nch * ratio) + 128 * nch); + bufused = 0; +#ifdef WITH_SECRET_RABBIT_CODE + int errc = 0; + resampler = src_new(SRC_SINC_BEST_QUALITY, nch, &errc); + if(errc) + throw std::runtime_error(std::string("Error initing libsamplerate: ") + + src_strerror(errc)); +#else + throw std::runtime_error("HQ sample rate conversion not available"); +#endif + fire(); + } + + resample_worker::~resample_worker() + { +#ifdef WITH_SECRET_RABBIT_CODE + src_delete((SRC_STATE*)resampler); +#endif + } + + void resample_worker::sendend() + { + rethrow(); + set_workflag(WORKFLAG_END); + request_quit(); + } + + void resample_worker::sendblock(short* block, size_t frames) + { +again: + rethrow(); + wait_busy(); + if(bufused + frames < RESAMPLE_BUFFER) { + memcpy(&buffers[bufused * nch], block, 2 * nch * frames); + bufused += frames; + block += (frames * nch); + frames = 0; + } else if(bufused < RESAMPLE_BUFFER) { + size_t processable = RESAMPLE_BUFFER - bufused; + memcpy(&buffers[bufused * nch], block, 2 * nch * processable); + block += (processable * nch); + frames -= processable; + bufused = RESAMPLE_BUFFER; + } + set_busy(); + set_workflag(WORKFLAG_QUEUE_FRAME); + if(frames > 0) + goto again; + } + void waitfn(); class avi_avsnoop : public information_dispatch @@ -230,32 +308,19 @@ namespace soundrate.second, 2); dcounter = 0; have_dumped_frame = false; - resampler = NULL; + resampler_w = NULL; if(soundrate_setting == 4) { double ratio = 1.0 * audio_record_rate * soundrate.second / soundrate.first; - sbuffer.resize(RESAMPLE_BUFFER * chans); - sbuffer2.resize(RESAMPLE_BUFFER * chans); - fbuffer.resize((RESAMPLE_BUFFER * ratio + 128) * chans + 128); - fbuffer2.resize((RESAMPLE_BUFFER * ratio + 128) * chans + 128); sbuffer_fill = 0; -#ifdef WITH_SECRET_RABBIT_CODE - int errc = 0; - resampler = src_new(SRC_SINC_BEST_QUALITY, info.audio_chans, &errc); - if(errc) - throw std::runtime_error(std::string("Error initing libsamplerate: ") + - src_strerror(errc)); -#else - throw std::runtime_error("HQ sample rate conversion not available"); -#endif + sbuffer.resize(RESAMPLE_BUFFER * chans); + resampler_w = new resample_worker(ratio, chans); } } ~avi_avsnoop() throw() { -#ifdef WITH_SECRET_RABBIT_CODE - if(resampler) - src_delete((SRC_STATE*)resampler); -#endif + if(resampler_w) + delete resampler_w; delete worker; delete soxdumper; } @@ -275,12 +340,15 @@ namespace void on_sample(short l, short r) { - if(resampler) { + if(resampler_w) { if(!have_dumped_frame) return; sbuffer[sbuffer_fill++] = l; sbuffer[sbuffer_fill++] = r; - forward_samples(false); + if(sbuffer_fill == sbuffer.size()) { + resampler_w->sendblock(&sbuffer[0], sbuffer_fill / chans); + sbuffer_fill = 0; + } soxdumper->sample(l, r); return; } @@ -301,7 +369,8 @@ namespace void on_dump_end() { if(worker) { - forward_samples(true); + if(resampler_w) + resampler_w->sendend(); worker->request_quit(); } if(soxdumper) @@ -317,52 +386,19 @@ namespace return true; } avi_worker* worker; + resample_worker* resampler_w; private: - void forward_samples(bool eos) - { - if(!eos && sbuffer_fill < sbuffer.size()) - return; -#ifdef WITH_SECRET_RABBIT_CODE - double ratio = 1.0 * audio_record_rate * soundrate.second / soundrate.first; - SRC_DATA block; - src_short_to_float_array(&sbuffer[0], &sbuffer2[0], sbuffer_fill); - block.data_in = &sbuffer2[0]; - block.data_out = &fbuffer2[0]; - block.input_frames = sbuffer_fill / chans; - block.input_frames_used = 0; - block.output_frames = fbuffer2.size() / chans; - block.output_frames_gen = 0; - block.end_of_input = eos ? 1 : 0; - block.src_ratio = ratio; - int errc = src_process((SRC_STATE*)resampler, &block); - if(errc) - throw std::runtime_error(std::string("Error using libsamplerate: ") + - src_strerror(errc)); - src_float_to_short_array(&fbuffer2[0], &fbuffer[0], block.output_frames_gen * chans); - worker->queue_audio(&fbuffer[0], block.output_frames_gen * chans); - if(block.input_frames_used * chans < sbuffer_fill) - memmove(&sbuffer[0], &sbuffer[block.output_frames_gen * chans], sbuffer_fill - - block.input_frames_used * chans); - sbuffer_fill -= block.input_frames_used * chans; -#endif - } sox_dumper* soxdumper; screen dscr; unsigned dcounter; bool have_dumped_frame; std::pair soundrate; uint32_t audio_record_rate; - void* resampler; std::vector sbuffer; - std::vector sbuffer2; - std::vector fbuffer2; - std::vector fbuffer; size_t sbuffer_fill; uint32_t chans; }; - avi_avsnoop* vid_dumper; - void waitfn() { vid_dumper->worker->wait_busy(); @@ -454,4 +490,45 @@ namespace adv_avi_dumper::~adv_avi_dumper() throw() { } + + void resample_worker::entry() + { + while(1) { + wait_workflag(); + uint32_t work = clear_workflag(~WORKFLAG_QUIT_REQUEST); + if(work & (WORKFLAG_QUEUE_FRAME | WORKFLAG_END)) { +#ifdef WITH_SECRET_RABBIT_CODE +again: + SRC_DATA block; + src_short_to_float_array(&buffers[0], &buffers2[0], bufused * nch); + block.data_in = &buffers2[0]; + block.data_out = &buffers3[0]; + block.input_frames = bufused; + block.input_frames_used = 0; + block.output_frames = buffers3.size() / nch; + block.output_frames_gen = 0; + block.end_of_input = (work & WORKFLAG_END) ? 1 : 0; + block.src_ratio = ratio; + int errc = src_process((SRC_STATE*)resampler, &block); + if(errc) + throw std::runtime_error(std::string("Error using libsamplerate: ") + + src_strerror(errc)); + src_float_to_short_array(&buffers3[0], &buffers4[0], block.output_frames_gen * nch); + vid_dumper->worker->queue_audio(&buffers4[0], block.output_frames_gen * nch); + if(block.input_frames_used < bufused) + memmove(&buffers[0], &buffers[block.output_frames_gen * nch], (bufused - + block.input_frames_used) * nch); + bufused -= block.input_frames_used; + if(block.output_frames_gen > 0 && work & WORKFLAG_END) + goto again; //Try again to get all the samples. +#endif + clear_workflag(WORKFLAG_END | WORKFLAG_FLUSH | WORKFLAG_QUEUE_FRAME); + clear_busy(); + if(work & WORKFLAG_END) + return; + } + if(work == WORKFLAG_QUIT_REQUEST) + break; + } + } }