diff options
author | wm4 <wm4@nowhere> | 2013-11-12 23:42:04 +0100 |
---|---|---|
committer | wm4 <wm4@nowhere> | 2013-11-12 23:42:04 +0100 |
commit | e4bbb1d348dafbb32722f413648006a7bd9d0897 (patch) | |
tree | 7165ed9f86a77b751187600d0a9de8b35416f380 /audio | |
parent | e4f2fcc0ecd31322df65141edf0ddbff9c075f5d (diff) | |
parent | 22b3f522cacfbdba76d311c86efd6091512eb089 (diff) | |
download | mpv-e4bbb1d348dafbb32722f413648006a7bd9d0897.tar.bz2 mpv-e4bbb1d348dafbb32722f413648006a7bd9d0897.tar.xz |
Merge branch 'planar_audio'
Conflicts:
audio/out/ao_lavc.c
Diffstat (limited to 'audio')
59 files changed, 1460 insertions, 1406 deletions
diff --git a/audio/audio.c b/audio/audio.c index 9d41928436..2a67e22e5e 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -17,42 +17,60 @@ #include <assert.h> +#include <libavutil/mem.h> + +#include "mpvcore/mp_common.h" #include "mpvcore/mp_talloc.h" #include "audio.h" +static void update_redundant_info(struct mp_audio *mpa) +{ + assert(mp_chmap_is_empty(&mpa->channels) || + mp_chmap_is_valid(&mpa->channels)); + mpa->nch = mpa->channels.num; + mpa->bps = af_fmt2bits(mpa->format) / 8; + if (af_fmt_is_planar(mpa->format)) { + mpa->spf = 1; + mpa->num_planes = mpa->nch; + mpa->sstride = mpa->bps; + } else { + mpa->spf = mpa->nch; + mpa->num_planes = 1; + mpa->sstride = mpa->bps * mpa->nch; + } +} + void mp_audio_set_format(struct mp_audio *mpa, int format) { mpa->format = format; - mpa->bps = af_fmt2bits(format) / 8; + update_redundant_info(mpa); } void mp_audio_set_num_channels(struct mp_audio *mpa, int num_channels) { - struct mp_chmap map; - mp_chmap_from_channels(&map, num_channels); - mp_audio_set_channels(mpa, &map); + mp_chmap_from_channels(&mpa->channels, num_channels); + update_redundant_info(mpa); } // Use old MPlayer/ALSA channel layout. void mp_audio_set_channels_old(struct mp_audio *mpa, int num_channels) { - struct mp_chmap map; - mp_chmap_from_channels_alsa(&map, num_channels); - mp_audio_set_channels(mpa, &map); + mp_chmap_from_channels_alsa(&mpa->channels, num_channels); + update_redundant_info(mpa); } void mp_audio_set_channels(struct mp_audio *mpa, const struct mp_chmap *chmap) { - assert(mp_chmap_is_empty(chmap) || mp_chmap_is_valid(chmap)); mpa->channels = *chmap; - mpa->nch = mpa->channels.num; + update_redundant_info(mpa); } void mp_audio_copy_config(struct mp_audio *dst, const struct mp_audio *src) { - mp_audio_set_format(dst, src->format); - mp_audio_set_channels(dst, &src->channels); + dst->format = src->format; + dst->channels = src->channels; dst->rate = src->rate; + update_redundant_info(dst); } bool mp_audio_config_equals(const struct mp_audio *a, const struct mp_audio *b) @@ -74,3 +92,135 @@ char *mp_audio_config_to_str(struct mp_audio *mpa) { return mp_audio_fmt_to_str(mpa->rate, &mpa->channels, mpa->format); } + +void mp_audio_force_interleaved_format(struct mp_audio *mpa) +{ + if (af_fmt_is_planar(mpa->format)) + mp_audio_set_format(mpa, af_fmt_from_planar(mpa->format)); +} + +// Return used size of a plane. (The size is the same for all planes.) +int mp_audio_psize(struct mp_audio *mpa) +{ + return mpa->samples * mpa->sstride; +} + +void mp_audio_set_null_data(struct mp_audio *mpa) +{ + for (int n = 0; n < MP_NUM_CHANNELS; n++) + mpa->planes[n] = NULL; + mpa->samples = 0; +} + +static void mp_audio_destructor(void *ptr) +{ + struct mp_audio *mpa = ptr; + for (int n = mpa->num_planes; n < MP_NUM_CHANNELS; n++) { + // Note: don't free if not allocated by mp_audio_realloc + if (mpa->allocated[n]) + av_free(mpa->planes[n]); + } +} + +/* Reallocate the data stored in mpa->planes[n] so that enough samples are + * available on every plane. The previous data is kept (for the smallest + * common number of samples before/after resize). + * + * mpa->samples is not set or used. + * + * This function is flexible enough to handle format and channel layout + * changes. In these cases, all planes are reallocated as needed. Unused + * planes are freed. + * + * mp_audio_realloc(mpa, 0) will still yield non-NULL for mpa->data[n]. + * + * Allocated data is implicitly freed on talloc_free(mpa). + */ +void mp_audio_realloc(struct mp_audio *mpa, int samples) +{ + assert(samples >= 0); + int size = MPMAX(samples * mpa->sstride, 1); + for (int n = 0; n < mpa->num_planes; n++) { + if (size != mpa->allocated[n]) { + // Note: av_realloc() can't be used (see libavutil doxygen) + void *new = av_malloc(size); + if (!new) + abort(); + if (mpa->allocated[n]) + memcpy(new, mpa->planes[n], MPMIN(mpa->allocated[n], size)); + av_free(mpa->planes[n]); + mpa->planes[n] = new; + mpa->allocated[n] = size; + } + } + for (int n = mpa->num_planes; n < MP_NUM_CHANNELS; n++) { + av_free(mpa->planes[n]); + mpa->planes[n] = NULL; + mpa->allocated[n] = 0; + } + talloc_set_destructor(mpa, mp_audio_destructor); +} + +// Like mp_audio_realloc(), but only reallocate if the audio grows in size. +void mp_audio_realloc_min(struct mp_audio *mpa, int samples) +{ + if (samples > mp_audio_get_allocated_size(mpa)) + mp_audio_realloc(mpa, samples); +} + +/* Get the size allocated for the data, in number of samples. If the allocated + * size isn't on sample boundaries (e.g. after format changes), the returned + * sample number is a rounded down value. + * + * Note that this only works in situations where mp_audio_realloc() also works! + */ +int mp_audio_get_allocated_size(struct mp_audio *mpa) +{ + int size = 0; + for (int n = 0; n < mpa->num_planes; n++) { + int s = mpa->allocated[n] / mpa->sstride; + size = n == 0 ? s : MPMIN(size, s); + } + return size; +} + +// Clear the samples [start, start + length) with silence. +void mp_audio_fill_silence(struct mp_audio *mpa, int start, int length) +{ + assert(start >= 0 && length >= 0 && start + length <= mpa->samples); + int offset = start * mpa->sstride; + int size = length * mpa->sstride; + for (int n = 0; n < mpa->num_planes; n++) { + if (n > 0 && mpa->planes[n] == mpa->planes[0]) + continue; // silly optimization for special cases + af_fill_silence((char *)mpa->planes[n] + offset, size, mpa->format); + } +} + +// All integer parameters are in samples. +// dst and src can overlap. +void mp_audio_copy(struct mp_audio *dst, int dst_offset, + struct mp_audio *src, int src_offset, int length) +{ + assert(mp_audio_config_equals(dst, src)); + assert(length >= 0); + assert(dst_offset >= 0 && dst_offset + length <= dst->samples); + assert(src_offset >= 0 && src_offset + length <= src->samples); + + for (int n = 0; n < dst->num_planes; n++) { + memmove((char *)dst->planes[n] + dst_offset * dst->sstride, + (char *)src->planes[n] + src_offset * src->sstride, + length * dst->sstride); + } +} + +// Set data to the audio after the given number of samples (i.e. slice it). +void mp_audio_skip_samples(struct mp_audio *data, int samples) +{ + assert(samples >= 0 && samples <= data->samples); + + for (int n = 0; n < data->num_planes; n++) + data->planes[n] = (uint8_t *)data->planes[n] + samples * data->sstride; + + data->samples -= samples; +} diff --git a/audio/audio.h b/audio/audio.h index de35e697c8..54ac2d5aac 100644 --- a/audio/audio.h +++ b/audio/audio.h @@ -23,14 +23,22 @@ // Audio data chunk struct mp_audio { - void *audio; // data buffer - int len; // buffer length (in bytes) - int rate; // sample rate + int samples; // number of samples in data (per channel) + void *planes[MP_NUM_CHANNELS]; // data buffer (one per plane) + int rate; // sample rate struct mp_chmap channels; // channel layout, use mp_audio_set_*() to set int format; // format (AF_FORMAT_...), use mp_audio_set_format() to set // Redundant fields, for convenience - int nch; // number of channels (redundant with chmap) - int bps; // bytes per sample (redundant with format) + int sstride; // distance between 2 samples in bytes on a plane + // interleaved: bps * nch + // planar: bps + int nch; // number of channels (redundant with chmap) + int spf; // sub-samples per sample on each plane + int num_planes; // number of planes + int bps; // size of sub-samples (af_fmt2bits(format) / 8) + + // private + int allocated[MP_NUM_CHANNELS]; // use mp_audio_get_allocated_size() }; void mp_audio_set_format(struct mp_audio *mpa, int format); @@ -43,4 +51,20 @@ bool mp_audio_config_equals(const struct mp_audio *a, const struct mp_audio *b); char *mp_audio_fmt_to_str(int srate, const struct mp_chmap *chmap, int format); char *mp_audio_config_to_str(struct mp_audio *mpa); +void mp_audio_force_interleaved_format(struct mp_audio *mpa); + +int mp_audio_psize(struct mp_audio *mpa); + +void mp_audio_set_null_data(struct mp_audio *mpa); + +void mp_audio_realloc(struct mp_audio *mpa, int samples); +void mp_audio_realloc_min(struct mp_audio *mpa, int samples); +int mp_audio_get_allocated_size(struct mp_audio *mpa); + +void mp_audio_fill_silence(struct mp_audio *mpa, int start, int length); + +void mp_audio_copy(struct mp_audio *dst, int dst_offset, + struct mp_audio *src, int src_offset, int length); +void mp_audio_skip_samples(struct mp_audio *data, int samples); + #endif diff --git a/audio/audio_buffer.c b/audio/audio_buffer.c new file mode 100644 index 0000000000..53563f90c6 --- /dev/null +++ b/audio/audio_buffer.c @@ -0,0 +1,152 @@ +/* + * This file is part of mpv. + * + * mpv is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mpv is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with mpv. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stddef.h> +#include <limits.h> +#include <assert.h> + +#include "mpvcore/mp_common.h" + +#include "audio_buffer.h" +#include "audio.h" +#include "format.h" + +struct mp_audio_buffer { + struct mp_audio *buffer; +}; + +struct mp_audio_buffer *mp_audio_buffer_create(void *talloc_ctx) +{ + struct mp_audio_buffer *ab = talloc(talloc_ctx, struct mp_audio_buffer); + *ab = (struct mp_audio_buffer) { + .buffer = talloc_zero(ab, struct mp_audio), + }; + return ab; +} + +// Reinitialize the buffer, set a new format, drop old data. +// The audio data in fmt is not used, only the format. +void mp_audio_buffer_reinit(struct mp_audio_buffer *ab, struct mp_audio *fmt) +{ + mp_audio_copy_config(ab->buffer, fmt); + mp_audio_realloc(ab->buffer, 1); + ab->buffer->samples = 0; +} + +void mp_audio_buffer_reinit_fmt(struct mp_audio_buffer *ab, int format, + const struct mp_chmap *channels, int srate) +{ + struct mp_audio mpa = {0}; + mp_audio_set_format(&mpa, format); + mp_audio_set_channels(&mpa, channels); + mpa.rate = srate; + mp_audio_buffer_reinit(ab, &mpa); +} + +void mp_audio_buffer_get_format(struct mp_audio_buffer *ab, + struct mp_audio *out_fmt) +{ + *out_fmt = (struct mp_audio){0}; + mp_audio_copy_config(out_fmt, ab->buffer); +} + +// Make the total size of the internal buffer at least this number of samples. +void mp_audio_buffer_preallocate_min(struct mp_audio_buffer *ab, int samples) +{ + mp_audio_realloc_min(ab->buffer, samples); +} + +// Get number of samples that can be written without forcing a resize of the +// internal buffer. +int mp_audio_buffer_get_write_available(struct mp_audio_buffer *ab) +{ + return mp_audio_get_allocated_size(ab->buffer) - ab->buffer->samples; +} + +// Get a pointer to the end of the buffer (where writing would append). If the +// internal buffer is too small for the given number of samples, it's resized. +// After writing to the buffer, mp_audio_buffer_finish_write() has to be used +// to make the written data part of the readable buffer. +void mp_audio_buffer_get_write_buffer(struct mp_audio_buffer *ab, int samples, + struct mp_audio *out_buffer) +{ + assert(samples >= 0); + mp_audio_realloc_min(ab->buffer, ab->buffer->samples + samples); + *out_buffer = *ab->buffer; + out_buffer->samples = ab->buffer->samples + samples; + mp_audio_skip_samples(out_buffer, ab->buffer->samples); +} + +void mp_audio_buffer_finish_write(struct mp_audio_buffer *ab, int samples) +{ + assert(samples >= 0 && samples <= mp_audio_buffer_get_write_available(ab)); + ab->buffer->samples += samples; +} + +// Append data to the end of the buffer. +// If the buffer is not large enough, it is transparently resized. +// For now always copies the data. +void mp_audio_buffer_append(struct mp_audio_buffer *ab, struct mp_audio *mpa) +{ + int offset = ab->buffer->samples; + ab->buffer->samples += mpa->samples; + mp_audio_realloc_min(ab->buffer, ab->buffer->samples); + mp_audio_copy(ab->buffer, offset, mpa, 0, mpa->samples); +} + +// Prepend silence to the start of the buffer. +void mp_audio_buffer_prepend_silence(struct mp_audio_buffer *ab, int samples) +{ + assert(samples >= 0); + int oldlen = ab->buffer->samples; + ab->buffer->samples += samples; + mp_audio_realloc_min(ab->buffer, ab->buffer->samples); + mp_audio_copy(ab->buffer, samples, ab->buffer, 0, oldlen); + mp_audio_fill_silence(ab->buffer, 0, samples); +} + +// Get the start of the current readable buffer. +void mp_audio_buffer_peek(struct mp_audio_buffer *ab, struct mp_audio *out_mpa) +{ + *out_mpa = *ab->buffer; +} + +// Skip leading samples. (Used with mp_audio_buffer_peek() to read data.) +void mp_audio_buffer_skip(struct mp_audio_buffer *ab, int samples) +{ + assert(samples >= 0 && samples <= ab->buffer->samples); + mp_audio_copy(ab->buffer, 0, ab->buffer, samples, + ab->buffer->samples - samples); + ab->buffer->samples -= samples; +} + +void mp_audio_buffer_clear(struct mp_audio_buffer *ab) +{ + ab->buffer->samples = 0; +} + +// Return number of buffered audio samples +int mp_audio_buffer_samples(struct mp_audio_buffer *ab) +{ + return ab->buffer->samples; +} + +// Return amount of buffered audio in seconds. +double mp_audio_buffer_seconds(struct mp_audio_buffer *ab) +{ + return ab->buffer->samples / (double)ab->buffer->rate; +} diff --git a/audio/audio_buffer.h b/audio/audio_buffer.h new file mode 100644 index 0000000000..8cd0df30d0 --- /dev/null +++ b/audio/audio_buffer.h @@ -0,0 +1,44 @@ +/* + * This file is part of mpv. + * + * mpv is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mpv is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with mpv. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef MP_AUDIO_BUFFER_H +#define MP_AUDIO_BUFFER_H + +struct mp_audio_buffer; +struct mp_audio; +struct mp_chmap; + +struct mp_audio_buffer *mp_audio_buffer_create(void *talloc_ctx); +void mp_audio_buffer_reinit(struct mp_audio_buffer *ab, struct mp_audio *fmt); +void mp_audio_buffer_reinit_fmt(struct mp_audio_buffer *ab, int format, + const struct mp_chmap *channels, int srate); +void mp_audio_buffer_get_format(struct mp_audio_buffer *ab, + struct mp_audio *out_fmt); +void mp_audio_buffer_preallocate_min(struct mp_audio_buffer *ab, int samples); +int mp_audio_buffer_get_write_available(struct mp_audio_buffer *ab); +void mp_audio_buffer_get_write_buffer(struct mp_audio_buffer *ab, int minsamples, + struct mp_audio *out_buffer); +void mp_audio_buffer_finish_write(struct mp_audio_buffer *ab, int samples); +void mp_audio_buffer_append(struct mp_audio_buffer *ab, struct mp_audio *mpa); +void mp_audio_buffer_prepend_silence(struct mp_audio_buffer *ab, int samples); +void mp_audio_buffer_peek(struct mp_audio_buffer *ab, struct mp_audio *out_mpa); +void mp_audio_buffer_skip(struct mp_audio_buffer *ab, int samples); +void mp_audio_buffer_clear(struct mp_audio_buffer *ab); +int mp_audio_buffer_samples(struct mp_audio_buffer *ab); +double mp_audio_buffer_seconds(struct mp_audio_buffer *ab); + +#endif diff --git a/audio/decode/ad.h b/audio/decode/ad.h index 30e739d135..6c76e8dfd0 100644 --- a/audio/decode/ad.h +++ b/audio/decode/ad.h @@ -24,6 +24,7 @@ #include "demux/demux.h" #include "audio/format.h" +#include "audio/audio.h" struct mp_decoder_list; @@ -35,8 +36,7 @@ struct ad_functions { int (*init)(sh_audio_t *sh, const char *decoder); void (*uninit)(sh_audio_t *sh); int (*control)(sh_audio_t *sh, int cmd, void *arg); - int (*decode_audio)(sh_audio_t *sh, unsigned char *buffer, int minlen, - int maxlen); + int (*decode_audio)(sh_audio_t *sh, struct mp_audio *buffer, int maxlen); }; enum ad_ctrl { diff --git a/audio/decode/ad_lavc.c b/audio/decode/ad_lavc.c index 72bb51e00c..2e0cade8c5 100644 --- a/audio/decode/ad_lavc.c +++ b/audio/decode/ad_lavc.c @@ -36,25 +36,20 @@ #include "mpvcore/av_opts.h" #include "ad.h" -#include "audio/reorder_ch.h" #include "audio/fmt-conversion.h" -#include "compat/mpbswap.h" #include "compat/libav.h" struct priv { AVCodecContext *avctx; AVFrame *avframe; - uint8_t *output; - uint8_t *output_packed; // used by deplanarize to store packed audio samples - int output_left; - int unitsize; + struct mp_audio frame; bool force_channel_map; struct demux_packet *packet; }; static void uninit(sh_audio_t *sh); -static int decode_audio(sh_audio_t *sh,unsigned char *buffer,int minlen,int maxlen); +static int decode_new_packet(struct sh_audio *sh); #define OPT_BASE_STRUCT struct MPOpts @@ -138,22 +133,21 @@ static int preinit(sh_audio_t *sh) return 1; } -/* Prefer playing audio with the samplerate given in container data - * if available, but take number the number of channels and sample format - * from the codec, since if the codec isn't using the correct values for - * those everything breaks anyway. - */ -static int setup_format(sh_audio_t *sh_audio, - const AVCodecContext *lavc_context) +static int setup_format(sh_audio_t *sh_audio) { struct priv *priv = sh_audio->context; - int sample_format = - af_from_avformat(av_get_packed_sample_fmt(lavc_context->sample_fmt)); - int samplerate = lavc_context->sample_rate; - // If not set, try container samplerate + AVCodecContext *lavc_context = priv->avctx; + + int sample_format = af_from_avformat(lavc_context->sample_fmt); + if (!sample_format) + return -1; + |