avcodec/vorbisenc: Use a bufqueue in encoding with smaller lengths

Switching the vorbis encoder to use a buffer queue for input frames allows
saving lookahead samples more easily and safely for psychoacoustic systems,
requiring less pointer arithmetic in the case of transient windows.
This commit is contained in:
Tyler Jones 2017-05-30 11:28:16 -06:00 committed by Rostislav Pehlivanov
parent 25260b5161
commit 29c13fed68
1 changed files with 106 additions and 14 deletions

View File

@ -1044,20 +1044,117 @@ static int apply_window_and_mdct(vorbis_enc_context *venc,
return 1;
}
/* Used for padding the last encoded packet */
static AVFrame *spawn_empty_frame(AVCodecContext *avctx, int channels)
{
AVFrame *f = av_frame_alloc();
if (!f)
return NULL;
f->format = avctx->sample_fmt;
f->nb_samples = avctx->frame_size;
f->channel_layout = avctx->channel_layout;
if (av_frame_get_buffer(f, 4)) {
av_frame_free(&f);
return NULL;
}
for (int ch = 0; ch < channels; ch++) {
size_t bps = av_get_bytes_per_sample(f->format);
memset(f->extended_data[ch], 0, bps * f->nb_samples);
}
return f;
}
static float **alloc_audio_arrays(int channels, int frame_size)
{
float **audio = av_mallocz_array(channels, sizeof(float *));
if (!audio)
return NULL;
for (int ch = 0; ch < channels; ch++) {
audio[ch] = av_mallocz_array(frame_size, sizeof(float));
if (!audio[ch]) {
// alloc has failed, free everything allocated thus far
for (ch--; ch >= 0; ch--)
av_free(audio[ch]);
av_free(audio);
return NULL;
}
}
return audio;
}
/* Concatenate audio frames into an appropriately sized array of samples */
static void move_audio(vorbis_enc_context *venc, float **audio, int *samples, int sf_size)
{
AVFrame *cur = NULL;
int frame_size = 1 << (venc->log2_blocksize[1] - 1);
int subframes = frame_size / sf_size;
for (int sf = 0; sf < subframes; sf++) {
cur = ff_bufqueue_get(&venc->bufqueue);
*samples += cur->nb_samples;
for (int ch = 0; ch < venc->channels; ch++) {
const float *input = (float *) cur->extended_data[ch];
const size_t len = cur->nb_samples * sizeof(float);
memcpy(&audio[ch][sf*sf_size], input, len);
}
av_frame_free(&cur);
}
}
static int vorbis_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
const AVFrame *frame, int *got_packet_ptr)
{
vorbis_enc_context *venc = avctx->priv_data;
float **audio = frame ? (float **)frame->extended_data : NULL;
int samples = frame ? frame->nb_samples : 0;
float **audio = NULL;
int i, ret, need_more;
int samples = 0, frame_size = 1 << (venc->log2_blocksize[1] - 1);
vorbis_enc_mode *mode;
vorbis_enc_mapping *mapping;
PutBitContext pb;
int i, ret;
if (frame) {
if ((ret = ff_af_queue_add(&venc->afq, frame)) < 0)
return ret;
ff_bufqueue_add(avctx, &venc->bufqueue, av_frame_clone(frame));
} else
if (!venc->afq.remaining_samples)
return 0;
need_more = venc->bufqueue.available * avctx->frame_size < frame_size;
need_more = frame && need_more;
if (need_more)
return 0;
audio = alloc_audio_arrays(venc->channels, frame_size);
if (!audio)
return AVERROR(ENOMEM);
/* Pad the bufqueue with empty frames for encoding the last packet. */
if (!frame) {
if (venc->bufqueue.available * avctx->frame_size < frame_size) {
int frames_needed = (frame_size/avctx->frame_size) - venc->bufqueue.available;
for (int i = 0; i < frames_needed; i++) {
AVFrame *empty = spawn_empty_frame(avctx, venc->channels);
if (!empty)
return AVERROR(ENOMEM);
ff_bufqueue_add(avctx, &venc->bufqueue, empty);
}
}
}
move_audio(venc, audio, &samples, avctx->frame_size);
if (!apply_window_and_mdct(venc, audio, samples))
return 0;
samples = 1 << (venc->log2_blocksize[0] - 1);
if ((ret = ff_alloc_packet2(avctx, avpkt, 8192, 0)) < 0)
return ret;
@ -1116,16 +1213,11 @@ static int vorbis_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
flush_put_bits(&pb);
avpkt->size = put_bits_count(&pb) >> 3;
avpkt->duration = ff_samples_to_time_base(avctx, avctx->frame_size);
if (frame) {
if (frame->pts != AV_NOPTS_VALUE)
avpkt->pts = ff_samples_to_time_base(avctx, frame->pts);
} else {
avpkt->pts = venc->next_pts;
}
if (avpkt->pts != AV_NOPTS_VALUE)
venc->next_pts = avpkt->pts + avpkt->duration;
for (int ch = 0; ch < venc->channels; ch++)
av_free(audio[ch]);
av_free(audio);
ff_af_queue_remove(&venc->afq, frame_size, &avpkt->pts, &avpkt->duration);
*got_packet_ptr = 1;
return 0;
}
@ -1217,7 +1309,7 @@ static av_cold int vorbis_encode_init(AVCodecContext *avctx)
goto error;
avctx->extradata_size = ret;
avctx->frame_size = 1 << (venc->log2_blocksize[0] - 1);
avctx->frame_size = 64;
ff_af_queue_init(avctx, &venc->afq);