From 933fbf733362eee9db15dd2671c263781abd5635 Mon Sep 17 00:00:00 2001 From: wm4 Date: Wed, 13 Nov 2013 19:19:57 +0100 Subject: ao_lavc: support non-interleaved audio --- audio/out/ao_lavc.c | 235 ++++++++++------------------------------------------ audio/reorder_ch.c | 36 -------- audio/reorder_ch.h | 3 - 3 files changed, 42 insertions(+), 232 deletions(-) (limited to 'audio') diff --git a/audio/out/ao_lavc.c b/audio/out/ao_lavc.c index c1fce6a715..117b6c6832 100644 --- a/audio/out/ao_lavc.c +++ b/audio/out/ao_lavc.c @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -31,22 +32,17 @@ #include "mpvcore/options.h" #include "mpvcore/mp_common.h" #include "audio/format.h" -#include "audio/reorder_ch.h" +#include "audio/fmt-conversion.h" #include "talloc.h" #include "ao.h" #include "mpvcore/mp_msg.h" #include "mpvcore/encode_lavc.h" -static const char *sample_padding_signed = "\x00\x00\x00\x00"; -static const char *sample_padding_u8 = "\x80"; -static const char *sample_padding_float = "\x00\x00\x00\x00"; - struct priv { uint8_t *buffer; size_t buffer_size; AVStream *stream; - bool planarize; int pcmhack; int aframesize; int aframecount; @@ -103,143 +99,34 @@ static int init(struct ao *ao) ac->stream->codec->channel_layout = mp_chmap_to_lavc(&ao->channels); ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_NONE; - ao->format = af_fmt_from_planar(ao->format); - { - // first check if the selected format is somewhere in the list of - // supported formats by the codec - for (sampleformat = codec->sample_fmts; - sampleformat && *sampleformat != AV_SAMPLE_FMT_NONE; - ++sampleformat) { - switch (*sampleformat) { - case AV_SAMPLE_FMT_U8: - case AV_SAMPLE_FMT_U8P: - if (ao->format == AF_FORMAT_U8) - goto out_search; - break; - case AV_SAMPLE_FMT_S16: - case AV_SAMPLE_FMT_S16P: - if (ao->format == AF_FORMAT_S16_BE) - goto out_search; - if (ao->format == AF_FORMAT_S16_LE) - goto out_search; - break; - case AV_SAMPLE_FMT_S32: - case AV_SAMPLE_FMT_S32P: - if (ao->format == AF_FORMAT_S32_BE) - goto out_search; - if (ao->format == AF_FORMAT_S32_LE) - goto out_search; - break; - case AV_SAMPLE_FMT_FLT: - case AV_SAMPLE_FMT_FLTP: - if (ao->format == AF_FORMAT_FLOAT_BE) - goto out_search; - if (ao->format == AF_FORMAT_FLOAT_LE) - goto out_search; - break; - // FIXME do we need support for AV_SAMPLE_FORMAT_DBL/DBLP? - default: - break; - } - } -out_search: - ; + // first check if the selected format is somewhere in the list of + // supported formats by the codec + for (sampleformat = codec->sample_fmts; + sampleformat && *sampleformat != AV_SAMPLE_FMT_NONE; + ++sampleformat) { + if (ao->format == af_from_avformat(*sampleformat)) + break; } if (!sampleformat || *sampleformat == AV_SAMPLE_FMT_NONE) { // if the selected format is not supported, we have to pick the first // one we CAN support - // note: not needing to select endianness here, as the switch() below - // does that anyway for us for (sampleformat = codec->sample_fmts; sampleformat && *sampleformat != AV_SAMPLE_FMT_NONE; ++sampleformat) { - switch (*sampleformat) { - case AV_SAMPLE_FMT_U8: - case AV_SAMPLE_FMT_U8P: - ao->format = AF_FORMAT_U8; - goto out_takefirst; - case AV_SAMPLE_FMT_S16: - case AV_SAMPLE_FMT_S16P: - ao->format = AF_FORMAT_S16_NE; - goto out_takefirst; - case AV_SAMPLE_FMT_S32: - case AV_SAMPLE_FMT_S32P: - ao->format = AF_FORMAT_S32_NE; - goto out_takefirst; - case AV_SAMPLE_FMT_FLT: - case AV_SAMPLE_FMT_FLTP: - ao->format = AF_FORMAT_FLOAT_NE; - goto out_takefirst; - // FIXME do we need support for AV_SAMPLE_FORMAT_DBL/DBLP? - default: + int format = af_from_avformat(*sampleformat); + if (format) { + ao->format = format; break; } } -out_takefirst: - ; - } - - switch (ao->format) { - // now that we have chosen a format, set up the fields for it, boldly - // switching endianness if needed (mplayer code will convert for us - // anyway, but ffmpeg always expects native endianness) - case AF_FORMAT_U8: - ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_U8; - ac->sample_size = 1; - ac->sample_padding = sample_padding_u8; - ao->format = AF_FORMAT_U8; - break; - default: - case AF_FORMAT_S16_BE: - case AF_FORMAT_S16_LE: - ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_S16; - ac->sample_size = 2; - ac->sample_padding = sample_padding_signed; - ao->format = AF_FORMAT_S16_NE; - break; - case AF_FORMAT_S32_BE: - case AF_FORMAT_S32_LE: - ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_S32; - ac->sample_size = 4; - ac->sample_padding = sample_padding_signed; - ao->format = AF_FORMAT_S32_NE; - break; - case AF_FORMAT_FLOAT_BE: - case AF_FORMAT_FLOAT_LE: - ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_FLT; - ac->sample_size = 4; - ac->sample_padding = sample_padding_float; - ao->format = AF_FORMAT_FLOAT_NE; - break; - } - - // detect if we have to planarize - ac->planarize = false; - { - bool found_format = false; - bool found_planar_format = false; - for (sampleformat = codec->sample_fmts; - sampleformat && *sampleformat != AV_SAMPLE_FMT_NONE; - ++sampleformat) { - if (*sampleformat == ac->stream->codec->sample_fmt) - found_format = true; - if (*sampleformat == - av_get_planar_sample_fmt(ac->stream->codec->sample_fmt)) - found_planar_format = true; - } - if (!found_format && found_planar_format) { - ac->stream->codec->sample_fmt = - av_get_planar_sample_fmt(ac->stream->codec->sample_fmt); - ac->planarize = true; - } - if (!found_format && !found_planar_format) { - // shouldn't happen - MP_ERR(ao, "sample format not found\n"); - } + if (!sampleformat) + MP_ERR(ao, "sample format not found\n"); // shouldn't happen } + ac->sample_size = af_fmt2bits(ao->format) / 8; + ac->stream->codec->sample_fmt = af_to_avformat(ao->format); ac->stream->codec->bits_per_raw_sample = ac->sample_size * 8; if (encode_lavc_open_codec(ao->encode_lavc_ctx, ac->stream) < 0) @@ -273,23 +160,9 @@ out_takefirst: ao->untimed = true; ao->priv = ac; - if (ac->planarize) - MP_WARN(ao, "need to planarize audio data\n"); - return 0; } -static void fill_with_padding(void *buf, int cnt, int sz, const void *padding) -{ - int i; - if (sz == 1) { - memset(buf, cnt, *(char *)padding); - return; - } - for (i = 0; i < cnt; ++i) - memcpy((char *) buf + i * sz, padding, sz); -} - // close audio device static void uninit(struct ao *ao, bool cut_audio) { @@ -312,7 +185,7 @@ static int get_space(struct ao *ao) } // must get exactly ac->aframesize amount of data -static int encode(struct ao *ao, double apts, void *data) +static int encode(struct ao *ao, double apts, void **data) { AVFrame *frame; AVPacket packet; @@ -335,22 +208,11 @@ static int encode(struct ao *ao, double apts, void *data) frame = avcodec_alloc_frame(); frame->nb_samples = ac->aframesize; - if (ac->planarize) { - void *data2 = talloc_size(ao, ac->aframesize * ao->channels.num * - ac->sample_size); - reorder_to_planar(data2, data, ac->sample_size, ao->channels.num, - ac->aframesize); - data = data2; - } + assert(ao->channels.num <= AV_NUM_DATA_POINTERS); + for (int n = 0; n < ao->channels.num; n++) + frame->extended_data[n] = data[n]; - size_t audiolen = ac->aframesize * ao->channels.num * ac->sample_size; - if (avcodec_fill_audio_frame(frame, ao->channels.num, - ac->stream->codec->sample_fmt, data, - audiolen, 1)) - { - MP_ERR(ao, "error filling\n"); - return -1; - } + frame->linesize[0] = frame->nb_samples * ao->sstride; if (ectx->options->rawts || ectx->options->copyts) { // real audio pts @@ -380,11 +242,6 @@ static int encode(struct ao *ao, double apts, void *data) } avcodec_free_frame(&frame); - - if (ac->planarize) { - talloc_free(data); - data = NULL; - } } else { @@ -437,23 +294,16 @@ static int encode(struct ao *ao, double apts, void *data) return packet.size; } -// plays 'samples' samples of 'ni_data[0]' -// it should round it down to frame sizes +// this should round samples down to frame sizes // return: number of samples played -static int play(struct ao *ao, void **ni_data, int samples, int flags) +static int play(struct ao *ao, void **data, int samples, int flags) { struct priv *ac = ao->priv; struct encode_lavc_context *ectx = ao->encode_lavc_ctx; int bufpos = 0; - void *paddingbuf = NULL; double nextpts; double pts = ao->pts; double outpts; - void *data = ni_data[0]; - int len = samples * ao->sstride; - int bytelen = len; - - len /= ac->sample_size * ao->channels.num; if (!encode_lavc_start(ectx)) { MP_WARN(ao, "not ready yet for encoding audio\n"); @@ -462,22 +312,22 @@ static int play(struct ao *ao, void **ni_data, int samples, int flags) if (flags & AOPLAY_FINAL_CHUNK) { int written = 0; - if (len > 0) { - size_t extralen = - (ac->aframesize - 1) * ao->channels.num * ac->sample_size; - paddingbuf = talloc_size(NULL, bytelen + extralen); - memcpy(paddingbuf, data, bytelen); - fill_with_padding((char *) paddingbuf + bytelen, - extralen / ac->sample_size, - ac->sample_size, ac->sample_padding); + if (samples > 0) { + void *tmp = talloc_new(NULL); + size_t bytelen = samples * ao->sstride; + size_t extralen = (ac->aframesize - 1) * ao->sstride; + void *padded[MP_NUM_CHANNELS]; + for (int n = 0; n < ao->channels.num; n++) { + padded[n] = talloc_size(tmp, bytelen + extralen); + memcpy(padded[n], data[n], bytelen); + af_fill_silence((char *)padded[n] + bytelen, extralen, ao->format); + } // No danger of recursion, because AOPLAY_FINAL_CHUNK not set - written = - play(ao, &paddingbuf, (bytelen + extralen) / ao->sstride, 0); - if (written * ao->sstride < bytelen) { + written = play(ao, padded, (bytelen + extralen) / ao->sstride, 0); + if (written < samples) { MP_ERR(ao, "did not write enough data at the end\n"); } - talloc_free(paddingbuf); - paddingbuf = NULL; + talloc_free(tmp); } outpts = ac->expected_next_pts; @@ -487,7 +337,7 @@ static int play(struct ao *ao, void **ni_data, int samples, int flags) while (encode(ao, outpts, NULL) > 0) ; - return (FFMIN(written, bytelen)) / ao->sstride; + return FFMIN(written, samples); } if (pts == MP_NOPTS_VALUE) { @@ -559,15 +409,14 @@ static int play(struct ao *ao, void **ni_data, int samples, int flags) // Shift pts by the pts offset first. outpts += encode_lavc_getoffset(ectx, ac->stream); - while (len - bufpos >= ac->aframesize) { - encode(ao, - outpts + bufpos / (double) ao->samplerate, - (char *) data + ac->sample_size * bufpos * ao->channels.num); + while (samples - bufpos >= ac->aframesize) { + void *start[MP_NUM_CHANNELS]; + for (int n = 0; n < ao->channels.num; n++) + start[n] = (char *)data[n] + bufpos * ao->sstride; + encode(ao, outpts + bufpos / (double) ao->samplerate, start); bufpos += ac->aframesize; } - talloc_free(paddingbuf); - // Calculate expected pts of next audio frame (input side). ac->expected_next_pts = pts + bufpos / (double) ao->samplerate; diff --git a/audio/reorder_ch.c b/audio/reorder_ch.c index b99731e6bf..e7bdaaccff 100644 --- a/audio/reorder_ch.c +++ b/audio/reorder_ch.c @@ -27,42 +27,6 @@ #include "chmap.h" #include "reorder_ch.h" -static inline void reorder_to_planar_(void *restrict out, const void *restrict in, - size_t size, size_t nchan, size_t nmemb) -{ - size_t i, c; - char *outptr = (char *) out; - size_t instep = nchan * size; - - for (c = 0; c < nchan; ++c) { - const char *inptr = ((const char *) in) + c * size; - for (i = 0; i < nmemb; ++i, inptr += instep, outptr += size) { - memcpy(outptr, inptr, size); - } - } -} - -void reorder_to_planar(void *restrict out, const void *restrict in, - size_t size, size_t nchan, size_t nmemb) -{ - // special case for mono (nothing to do...) - if (nchan == 1) - memcpy(out, in, size * nchan * nmemb); - // these calls exist to insert an inline copy of to_planar_ here with known - // value of size to help the compiler replace the memcpy calls by mov - // instructions - else if (size == 1) - reorder_to_planar_(out, in, 1, nchan, nmemb); - else if (size == 2) - reorder_to_planar_(out, in, 2, nchan, nmemb); - else if (size == 4) - reorder_to_planar_(out, in, 4, nchan, nmemb); - // general case (calls memcpy a lot, should actually never happen, but - // stays here for correctness purposes) - else - reorder_to_planar_(out, in, size, nchan, nmemb); -} - #define MAX_SAMPLESIZE 8 static void reorder_channels_(uint8_t *restrict data, int *restrict ch_order, diff --git a/audio/reorder_ch.h b/audio/reorder_ch.h index d81aab7dfa..1ad50bbb00 100644 --- a/audio/reorder_ch.h +++ b/audio/reorder_ch.h @@ -25,9 +25,6 @@ #include -void reorder_to_planar(void *restrict out, const void *restrict in, - size_t size, size_t nchan, size_t nmemb); - void reorder_channels(void *restrict data, int *restrict ch_order, size_t sample_size, size_t num_ch, size_t num_frames); -- cgit v1.2.3