mirror of https://github.com/mpv-player/mpv
af_lavcac3enc: use planar formats
Remove the awkward planarization. It had to be done because the AC3 encoder requires planar formats, but now we support them natively. Try to simplify buffer management with mp_audio_buffer. Improve checking for buffer overflows and out of bound writes. In theory, these shouldn't happen due to AC3 fixed frame sizes, but being paranoid is better.
This commit is contained in:
parent
a72072c605
commit
6f557aef42
|
@ -34,7 +34,8 @@
|
|||
|
||||
#include "config.h"
|
||||
#include "af.h"
|
||||
#include "audio/reorder_ch.h"
|
||||
#include "audio/audio_buffer.h"
|
||||
#include "audio/fmt-conversion.h"
|
||||
|
||||
|
||||
#define AC3_MAX_CHANNELS 6
|
||||
|
@ -53,10 +54,9 @@ typedef struct af_ac3enc_s {
|
|||
bool planarize;
|
||||
int add_iec61937_header;
|
||||
int bit_rate;
|
||||
int pending_data_size;
|
||||
char *pending_data;
|
||||
int pending_len;
|
||||
int expect_len;
|
||||
struct mp_audio_buffer *pending;
|
||||
int in_samples; // samples of input per AC3 frame
|
||||
int out_samples; // upper bound on encoded output per AC3 frame
|
||||
int min_channel_num;
|
||||
int in_sampleformat;
|
||||
} af_ac3enc_t;
|
||||
|
@ -93,17 +93,18 @@ static int control(struct af_instance *af, int cmd, void *arg)
|
|||
if (!mp_audio_config_equals(in, &orig_in))
|
||||
return AF_FALSE;
|
||||
|
||||
s->pending_len = 0;
|
||||
int expect_samples = AC3_FRAME_SIZE;
|
||||
s->expect_len = expect_samples * in->nch * in->bps;
|
||||
assert(s->expect_len <= s->pending_data_size);
|
||||
if (s->add_iec61937_header)
|
||||
af->mul = 1;
|
||||
else
|
||||
af->mul = (double)(AC3_MAX_CODED_FRAME_SIZE / (2 * 2)) / expect_samples;
|
||||
s->in_samples = AC3_FRAME_SIZE;
|
||||
if (s->add_iec61937_header) {
|
||||
s->out_samples = AC3_FRAME_SIZE;
|
||||
} else {
|
||||
s->out_samples = AC3_MAX_CODED_FRAME_SIZE / af->data->sstride;
|
||||
}
|
||||
af->mul = s->out_samples / (double)s->in_samples;
|
||||
|
||||
mp_audio_buffer_reinit(s->pending, in);
|
||||
|
||||
mp_msg(MSGT_AFILTER, MSGL_DBG2, "af_lavcac3enc reinit: %d, %d, %f, %d.\n",
|
||||
in->nch, in->rate, af->mul, s->expect_len);
|
||||
in->nch, in->rate, af->mul, s->in_samples);
|
||||
|
||||
bit_rate = s->bit_rate ? s->bit_rate : default_bit_rate[in->nch];
|
||||
|
||||
|
@ -172,83 +173,56 @@ static void uninit(struct af_instance* af)
|
|||
avcodec_close(s->lavc_actx);
|
||||
av_free(s->lavc_actx);
|
||||
}
|
||||
free(s->pending_data);
|
||||
free(s);
|
||||
}
|
||||
}
|
||||
|
||||
// Filter data through filter
|
||||
static struct mp_audio* play(struct af_instance* af, struct mp_audio* audio)
|
||||
{
|
||||
struct mp_audio *out = af->data;
|
||||
af_ac3enc_t *s = af->setup;
|
||||
struct mp_audio *c = audio; // Current working data
|
||||
struct mp_audio *l;
|
||||
int left, outsize = 0;
|
||||
char *buf, *src;
|
||||
int max_output_len;
|
||||
int frame_num = (mp_audio_psize(audio) + s->pending_len) / s->expect_len;
|
||||
int samplesize = af_fmt2bits(s->in_sampleformat) / 8;
|
||||
int num_frames = (audio->samples + mp_audio_buffer_samples(s->pending))
|
||||
/ s->in_samples;
|
||||
|
||||
if (s->add_iec61937_header)
|
||||
max_output_len = AC3_FRAME_SIZE * 2 * 2 * frame_num;
|
||||
else
|
||||
max_output_len = AC3_MAX_CODED_FRAME_SIZE * frame_num;
|
||||
int max_out_samples = s->out_samples * num_frames;
|
||||
mp_audio_realloc_min(out, max_out_samples);
|
||||
out->samples = 0;
|
||||
|
||||
mp_audio_realloc_min(af->data, max_output_len / af->data->sstride);
|
||||
af->data->samples = max_output_len / af->data->sstride;
|
||||
|
||||
l = af->data; // Local data
|
||||
buf = l->planes[0];
|
||||
src = c->planes[0];
|
||||
left = mp_audio_psize(c);
|
||||
|
||||
|
||||
while (left > 0) {
|
||||
while (audio->samples > 0) {
|
||||
int ret;
|
||||
|
||||
if (left + s->pending_len < s->expect_len) {
|
||||
memcpy(s->pending_data + s->pending_len, src, left);
|
||||
src += left;
|
||||
s->pending_len += left;
|
||||
left = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
char *src2 = src;
|
||||
|
||||
if (s->pending_len) {
|
||||
int needs = s->expect_len - s->pending_len;
|
||||
if (needs > 0) {
|
||||
memcpy(s->pending_data + s->pending_len, src, needs);
|
||||
src += needs;
|
||||
left -= needs;
|
||||
int consumed_pending = 0;
|
||||
struct mp_audio in_frame;
|
||||
int pending = mp_audio_buffer_samples(s->pending);
|
||||
if (pending == 0 && audio->samples >= s->in_samples) {
|
||||
in_frame = *audio;
|
||||
mp_audio_skip_samples(audio, s->in_samples);
|
||||
} else {
|
||||
if (pending > 0 && pending < s->in_samples) {
|
||||
struct mp_audio tmp = *audio;
|
||||
tmp.samples = MPMIN(tmp.samples, s->in_samples);
|
||||
mp_audio_buffer_append(s->pending, &tmp);
|
||||
mp_audio_skip_samples(audio, tmp.samples);
|
||||
}
|
||||
src2= s->pending_data;
|
||||
}
|
||||
|
||||
void *data = (void *) src2;
|
||||
if (s->planarize) {
|
||||
void *data2 = malloc(s->expect_len);
|
||||
reorder_to_planar(data2, data, samplesize,
|
||||
c->nch, s->expect_len / samplesize / c->nch);
|
||||
data = data2;
|
||||
mp_audio_buffer_peek(s->pending, &in_frame);
|
||||
if (in_frame.samples < s->in_samples)
|
||||
break;
|
||||
consumed_pending = s->in_samples;
|
||||
}
|
||||
in_frame.samples = s->in_samples;
|
||||
|
||||
AVFrame *frame = avcodec_alloc_frame();
|
||||
if (!frame) {
|
||||
mp_msg(MSGT_AFILTER, MSGL_FATAL, "[libaf] Could not allocate memory \n");
|
||||
return NULL;
|
||||
}
|
||||
frame->nb_samples = AC3_FRAME_SIZE;
|
||||
frame->nb_samples = s->in_samples;
|
||||
frame->format = s->lavc_actx->sample_fmt;
|
||||
frame->channel_layout = s->lavc_actx->channel_layout;
|
||||
|
||||
ret = avcodec_fill_audio_frame(frame, c->nch, s->lavc_actx->sample_fmt,
|
||||
(const uint8_t*)data, s->expect_len, 0);
|
||||
if (ret < 0) {
|
||||
mp_msg(MSGT_AFILTER, MSGL_FATAL, "[lavac3enc] Frame setup failed.\n");
|
||||
return NULL;
|
||||
}
|
||||
assert(in_frame.num_planes <= AV_NUM_DATA_POINTERS);
|
||||
for (int n = 0; n < in_frame.num_planes; n++)
|
||||
frame->data[n] = in_frame.planes[n];
|
||||
frame->linesize[0] = s->in_samples * audio->sstride;
|
||||
|
||||
int ok;
|
||||
ret = avcodec_encode_audio2(s->lavc_actx, &s->pkt, frame, &ok);
|
||||
|
@ -257,58 +231,52 @@ static struct mp_audio* play(struct af_instance* af, struct mp_audio* audio)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (s->planarize)
|
||||
free(data);
|
||||
|
||||
avcodec_free_frame(&frame);
|
||||
|
||||
if (s->pending_len) {
|
||||
s->pending_len = 0;
|
||||
} else {
|
||||
src += s->expect_len;
|
||||
left -= s->expect_len;
|
||||
}
|
||||
mp_audio_buffer_skip(s->pending, consumed_pending);
|
||||
|
||||
mp_msg(MSGT_AFILTER, MSGL_DBG2, "avcodec_encode_audio got %d, pending %d.\n",
|
||||
s->pkt.size, s->pending_len);
|
||||
s->pkt.size, mp_audio_buffer_samples(s->pending));
|
||||
|
||||
int len = s->pkt.size;
|
||||
int frame_size = s->pkt.size;
|
||||
int header_len = 0;
|
||||
if (s->add_iec61937_header) {
|
||||
assert(s->pkt.size > 5);
|
||||
char hdr[8];
|
||||
|
||||
if (s->add_iec61937_header && s->pkt.size > 5) {
|
||||
int bsmod = s->pkt.data[5] & 0x7;
|
||||
int len = frame_size;
|
||||
|
||||
AV_WB16(buf, 0xF872); // iec 61937 syncword 1
|
||||
AV_WB16(buf + 2, 0x4E1F); // iec 61937 syncword 2
|
||||
buf[4] = bsmod; // bsmod
|
||||
buf[5] = 0x01; // data-type ac3
|
||||
AV_WB16(buf + 6, len << 3); // number of bits in payload
|
||||
|
||||
memset(buf + 8 + len, 0, AC3_FRAME_SIZE * 2 * 2 - 8 - len);
|
||||
frame_size = AC3_FRAME_SIZE * 2 * 2;
|
||||
header_len = 8;
|
||||
len = AC3_FRAME_SIZE * 2 * 2;
|
||||
|
||||
AV_WB16(hdr, 0xF872); // iec 61937 syncword 1
|
||||
AV_WB16(hdr + 2, 0x4E1F); // iec 61937 syncword 2
|
||||
hdr[4] = bsmod; // bsmod
|
||||
hdr[5] = 0x01; // data-type ac3
|
||||
AV_WB16(hdr + 6, len << 3); // number of bits in payload
|
||||
}
|
||||
|
||||
assert(buf + len <= (char *)af->data->planes[0] + mp_audio_psize(af->data));
|
||||
assert(s->pkt.size <= len - header_len);
|
||||
size_t max_size = (max_out_samples - out->samples) * out->sstride;
|
||||
if (frame_size > max_size)
|
||||
abort();
|
||||
|
||||
char *buf = (char *)out->planes[0] + out->samples * out->sstride;
|
||||
memcpy(buf, hdr, header_len);
|
||||
memcpy(buf + header_len, s->pkt.data, s->pkt.size);
|
||||
|
||||
outsize += len;
|
||||
buf += len;
|
||||
memset(buf + header_len + s->pkt.size, 0,
|
||||
frame_size - (header_len + s->pkt.size));
|
||||
out->samples += frame_size / out->sstride;
|
||||
}
|
||||
c->planes[0] = l->planes[0];
|
||||
mp_audio_set_num_channels(c, 2);
|
||||
mp_audio_set_format(c, af->data->format);
|
||||
c->samples = outsize / c->sstride;
|
||||
mp_msg(MSGT_AFILTER, MSGL_DBG2, "play return size %d, pending %d\n",
|
||||
outsize, s->pending_len);
|
||||
return c;
|
||||
|
||||
mp_audio_buffer_append(s->pending, audio);
|
||||
|
||||
*audio = *out;
|
||||
return audio;
|
||||
}
|
||||
|
||||
static int af_open(struct af_instance* af){
|
||||
|
||||
af_ac3enc_t *s = calloc(1,sizeof(af_ac3enc_t));
|
||||
af_ac3enc_t *s = talloc_zero(af, af_ac3enc_t);
|
||||
af->control=control;
|
||||
af->uninit=uninit;
|
||||
af->play=play;
|
||||
|
@ -326,45 +294,25 @@ static int af_open(struct af_instance* af){
|
|||
return AF_ERROR;
|
||||
}
|
||||
const enum AVSampleFormat *fmts = s->lavc_acodec->sample_fmts;
|
||||
for (int i = 0; ; i++) {
|
||||
if (fmts[i] == AV_SAMPLE_FMT_NONE) {
|
||||
mp_msg(MSGT_AFILTER, MSGL_ERR, "Audio LAVC, encoder doesn't "
|
||||
"support expected sample formats!\n");
|
||||
return AF_ERROR;
|
||||
} else if (fmts[i] == AV_SAMPLE_FMT_S16) {
|
||||
s->in_sampleformat = AF_FORMAT_S16_NE;
|
||||
for (int i = 0; fmts[i] != AV_SAMPLE_FMT_NONE; i++) {
|
||||
s->in_sampleformat = af_from_avformat(fmts[i]);
|
||||
if (s->in_sampleformat) {
|
||||
s->lavc_actx->sample_fmt = fmts[i];
|
||||
s->planarize = 0;
|
||||
break;
|
||||
} else if (fmts[i] == AV_SAMPLE_FMT_FLT) {
|
||||
s->in_sampleformat = AF_FORMAT_FLOAT_NE;
|
||||
s->lavc_actx->sample_fmt = fmts[i];
|
||||
s->planarize = 0;
|
||||
break;
|
||||
} else if (fmts[i] == AV_SAMPLE_FMT_S16P) {
|
||||
s->in_sampleformat = AF_FORMAT_S16_NE;
|
||||
s->lavc_actx->sample_fmt = fmts[i];
|
||||
s->planarize = 1;
|
||||
break;
|
||||
} else if (fmts[i] == AV_SAMPLE_FMT_FLTP) {
|
||||
s->in_sampleformat = AF_FORMAT_FLOAT_NE;
|
||||
s->lavc_actx->sample_fmt = fmts[i];
|
||||
s->planarize = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!s->in_sampleformat) {
|
||||
mp_msg(MSGT_AFILTER, MSGL_ERR, "Audio LAVC, encoder doesn't "
|
||||
"support expected sample formats!\n");
|
||||
return AF_ERROR;
|
||||
}
|
||||
mp_msg(MSGT_AFILTER, MSGL_V, "[af_lavcac3enc]: in sample format: %s\n",
|
||||
af_fmt_to_str(s->in_sampleformat));
|
||||
s->pending_data_size = AF_NCH * AC3_FRAME_SIZE *
|
||||
af_fmt2bits(s->in_sampleformat) / 8;
|
||||
s->pending_data = malloc(s->pending_data_size);
|
||||
|
||||
if (s->planarize)
|
||||
mp_msg(MSGT_AFILTER, MSGL_WARN,
|
||||
"[af_lavcac3enc]: need to planarize audio data\n");
|
||||
|
||||
av_init_packet(&s->pkt);
|
||||
|
||||
s->pending = mp_audio_buffer_create(af);
|
||||
|
||||
return AF_OK;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue