AVI dumper: Do high-quality audio resampling in dedicated thread
Gives faster dumping in mode 4 for quadcore.
This commit is contained in:
parent
8e7e6cd168
commit
c49f305892
1 changed files with 133 additions and 56 deletions
|
@ -25,6 +25,9 @@
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
class avi_avsnoop;
|
||||||
|
avi_avsnoop* vid_dumper;
|
||||||
|
|
||||||
uint32_t rates[] = {8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000,
|
uint32_t rates[] = {8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000,
|
||||||
128000, 176400, 192000};
|
128000, 176400, 192000};
|
||||||
|
|
||||||
|
@ -111,6 +114,24 @@ namespace
|
||||||
uint32_t max_frames;
|
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<short> buffers;
|
||||||
|
std::vector<float> buffers2;
|
||||||
|
std::vector<float> buffers3;
|
||||||
|
std::vector<short> buffers4;
|
||||||
|
size_t bufused;
|
||||||
|
double ratio;
|
||||||
|
uint32_t nch;
|
||||||
|
void* resampler;
|
||||||
|
};
|
||||||
|
|
||||||
struct avi_worker : public worker_thread
|
struct avi_worker : public worker_thread
|
||||||
{
|
{
|
||||||
avi_worker(const struct avi_info& info);
|
avi_worker(const struct avi_info& info);
|
||||||
|
@ -134,7 +155,6 @@ namespace
|
||||||
#define WORKFLAG_FLUSH 2
|
#define WORKFLAG_FLUSH 2
|
||||||
#define WORKFLAG_END 4
|
#define WORKFLAG_END 4
|
||||||
|
|
||||||
|
|
||||||
avi_worker::avi_worker(const struct avi_info& info)
|
avi_worker::avi_worker(const struct avi_info& info)
|
||||||
: aviout(info.prefix, *info.vcodec, *info.acodec, info.sample_rate, info.audio_chans)
|
: 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();
|
void waitfn();
|
||||||
|
|
||||||
class avi_avsnoop : public information_dispatch
|
class avi_avsnoop : public information_dispatch
|
||||||
|
@ -230,32 +308,19 @@ namespace
|
||||||
soundrate.second, 2);
|
soundrate.second, 2);
|
||||||
dcounter = 0;
|
dcounter = 0;
|
||||||
have_dumped_frame = false;
|
have_dumped_frame = false;
|
||||||
resampler = NULL;
|
resampler_w = NULL;
|
||||||
if(soundrate_setting == 4) {
|
if(soundrate_setting == 4) {
|
||||||
double ratio = 1.0 * audio_record_rate * soundrate.second / soundrate.first;
|
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;
|
sbuffer_fill = 0;
|
||||||
#ifdef WITH_SECRET_RABBIT_CODE
|
sbuffer.resize(RESAMPLE_BUFFER * chans);
|
||||||
int errc = 0;
|
resampler_w = new resample_worker(ratio, chans);
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
~avi_avsnoop() throw()
|
~avi_avsnoop() throw()
|
||||||
{
|
{
|
||||||
#ifdef WITH_SECRET_RABBIT_CODE
|
if(resampler_w)
|
||||||
if(resampler)
|
delete resampler_w;
|
||||||
src_delete((SRC_STATE*)resampler);
|
|
||||||
#endif
|
|
||||||
delete worker;
|
delete worker;
|
||||||
delete soxdumper;
|
delete soxdumper;
|
||||||
}
|
}
|
||||||
|
@ -275,12 +340,15 @@ namespace
|
||||||
|
|
||||||
void on_sample(short l, short r)
|
void on_sample(short l, short r)
|
||||||
{
|
{
|
||||||
if(resampler) {
|
if(resampler_w) {
|
||||||
if(!have_dumped_frame)
|
if(!have_dumped_frame)
|
||||||
return;
|
return;
|
||||||
sbuffer[sbuffer_fill++] = l;
|
sbuffer[sbuffer_fill++] = l;
|
||||||
sbuffer[sbuffer_fill++] = r;
|
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);
|
soxdumper->sample(l, r);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -301,7 +369,8 @@ namespace
|
||||||
void on_dump_end()
|
void on_dump_end()
|
||||||
{
|
{
|
||||||
if(worker) {
|
if(worker) {
|
||||||
forward_samples(true);
|
if(resampler_w)
|
||||||
|
resampler_w->sendend();
|
||||||
worker->request_quit();
|
worker->request_quit();
|
||||||
}
|
}
|
||||||
if(soxdumper)
|
if(soxdumper)
|
||||||
|
@ -317,52 +386,19 @@ namespace
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
avi_worker* worker;
|
avi_worker* worker;
|
||||||
|
resample_worker* resampler_w;
|
||||||
private:
|
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;
|
sox_dumper* soxdumper;
|
||||||
screen<false> dscr;
|
screen<false> dscr;
|
||||||
unsigned dcounter;
|
unsigned dcounter;
|
||||||
bool have_dumped_frame;
|
bool have_dumped_frame;
|
||||||
std::pair<uint32_t, uint32_t> soundrate;
|
std::pair<uint32_t, uint32_t> soundrate;
|
||||||
uint32_t audio_record_rate;
|
uint32_t audio_record_rate;
|
||||||
void* resampler;
|
|
||||||
std::vector<short> sbuffer;
|
std::vector<short> sbuffer;
|
||||||
std::vector<float> sbuffer2;
|
|
||||||
std::vector<float> fbuffer2;
|
|
||||||
std::vector<short> fbuffer;
|
|
||||||
size_t sbuffer_fill;
|
size_t sbuffer_fill;
|
||||||
uint32_t chans;
|
uint32_t chans;
|
||||||
};
|
};
|
||||||
|
|
||||||
avi_avsnoop* vid_dumper;
|
|
||||||
|
|
||||||
void waitfn()
|
void waitfn()
|
||||||
{
|
{
|
||||||
vid_dumper->worker->wait_busy();
|
vid_dumper->worker->wait_busy();
|
||||||
|
@ -454,4 +490,45 @@ namespace
|
||||||
adv_avi_dumper::~adv_avi_dumper() throw()
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue