diff options
author | wm4 <wm4@nowhere> | 2018-01-18 14:44:20 +0100 |
---|---|---|
committer | Kevin Mitchell <kevmitch@gmail.com> | 2018-01-30 03:10:27 -0800 |
commit | b9f804b566c4c528714e4ec5e63675ad7ba5fefd (patch) | |
tree | 49d6fcd42ce6597a67aa2af59b7f20beb21a2e14 /audio/audio.c | |
parent | 76276c92104c31ee936ba5c76a76072f09978c5f (diff) | |
download | mpv-b9f804b566c4c528714e4ec5e63675ad7ba5fefd.tar.bz2 mpv-b9f804b566c4c528714e4ec5e63675ad7ba5fefd.tar.xz |
audio: rewrite filtering glue code
Use the new filtering code for audio too.
Diffstat (limited to 'audio/audio.c')
-rw-r--r-- | audio/audio.c | 625 |
1 files changed, 0 insertions, 625 deletions
diff --git a/audio/audio.c b/audio/audio.c deleted file mode 100644 index 55e4266f45..0000000000 --- a/audio/audio.c +++ /dev/null @@ -1,625 +0,0 @@ -/* - * 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 <stdint.h> -#include <limits.h> -#include <stdlib.h> -#include <assert.h> - -#include <libavutil/buffer.h> -#include <libavutil/frame.h> -#include <libavutil/mem.h> -#include <libavutil/version.h> - -#include "mpv_talloc.h" -#include "common/common.h" -#include "fmt-conversion.h" -#include "audio.h" -#include "aframe.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_fmt_to_bytes(mpa->format); - 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; - update_redundant_info(mpa); -} - -void mp_audio_set_num_channels(struct mp_audio *mpa, int num_channels) -{ - mp_chmap_from_channels(&mpa->channels, num_channels); - update_redundant_info(mpa); -} - -void mp_audio_set_channels(struct mp_audio *mpa, const struct mp_chmap *chmap) -{ - mpa->channels = *chmap; - update_redundant_info(mpa); -} - -void mp_audio_copy_config(struct mp_audio *dst, const struct mp_audio *src) -{ - 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) -{ - return a->format == b->format && a->rate == b->rate && - mp_chmap_equals(&a->channels, &b->channels); -} - -bool mp_audio_config_valid(const struct mp_audio *mpa) -{ - return mp_chmap_is_valid(&mpa->channels) && af_fmt_is_valid(mpa->format) - && mpa->rate >= 1 && mpa->rate < 10000000; -} - -char *mp_audio_config_to_str_buf(char *buf, size_t buf_sz, struct mp_audio *mpa) -{ - char ch[128]; - mp_chmap_to_str_buf(ch, sizeof(ch), &mpa->channels); - char *hr_ch = mp_chmap_to_str_hr(&mpa->channels); - if (strcmp(hr_ch, ch) != 0) - mp_snprintf_cat(ch, sizeof(ch), " (%s)", hr_ch); - snprintf(buf, buf_sz, "%dHz %s %dch %s", mpa->rate, - ch, mpa->channels.num, af_fmt_to_str(mpa->format)); - return buf; -} - -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->allocated[n] = NULL; - } - mpa->samples = 0; -} - -static int get_plane_size(const struct mp_audio *mpa, int samples) -{ - if (samples < 0 || !mp_audio_config_valid(mpa)) - return -1; - if (samples >= INT_MAX / mpa->sstride) - return -1; - return MPMAX(samples * mpa->sstride, 1); -} - -static void mp_audio_destructor(void *ptr) -{ - struct mp_audio *mpa = ptr; - for (int n = 0; n < MP_NUM_CHANNELS; n++) - av_buffer_unref(&mpa->allocated[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). - * - * This also makes sure the resulting buffer is writable (even in the case - * the buffer has the correct size). - * - * 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) -{ - int size = get_plane_size(mpa, samples); - if (size < 0) - abort(); // oom or invalid parameters - if (!mp_audio_is_writeable(mpa)) { - for (int n = 0; n < MP_NUM_CHANNELS; n++) { - av_buffer_unref(&mpa->allocated[n]); - mpa->planes[n] = NULL; - } - } - for (int n = 0; n < mpa->num_planes; n++) { - if (!mpa->allocated[n] || size != mpa->allocated[n]->size) { - if (av_buffer_realloc(&mpa->allocated[n], size) < 0) - abort(); // OOM - } - mpa->planes[n] = mpa->allocated[n]->data; - } - for (int n = mpa->num_planes; n < MP_NUM_CHANNELS; n++) { - av_buffer_unref(&mpa->allocated[n]); - mpa->planes[n] = NULL; - } - talloc_set_destructor(mpa, mp_audio_destructor); -} - -// Like mp_audio_realloc(), but only reallocate if the audio grows in size. -// If the buffer is reallocated, also preallocate. -void mp_audio_realloc_min(struct mp_audio *mpa, int samples) -{ - if (samples > mp_audio_get_allocated_size(mpa) || !mp_audio_is_writeable(mpa)) { - size_t alloc = ta_calc_prealloc_elems(samples); - if (alloc > INT_MAX) - abort(); // oom - mp_audio_realloc(mpa, alloc); - } -} - -/* 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++) { - for (int i = 0; i < MP_NUM_CHANNELS && mpa->allocated[i]; i++) { - uint8_t *start = mpa->allocated[i]->data; - uint8_t *end = start + mpa->allocated[i]->size; - uint8_t *plane = mpa->planes[n]; - if (plane >= start && plane < end) { - int s = MPMIN((end - plane) / mpa->sstride, INT_MAX); - size = n == 0 ? s : MPMIN(size, s); - goto next; - } - } - return 0; // plane is not covered by any buffer - next: ; - } - 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); - } -} - -// Copy fields that describe characteristics of the audio frame, but which are -// not part of the core format (format/channels/rate), and not part of the -// data (samples). -void mp_audio_copy_attributes(struct mp_audio *dst, struct mp_audio *src) -{ - // nothing yet -} - -// 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; - - if (data->pts != MP_NOPTS_VALUE) - data->pts += samples / (double)data->rate; -} - -// Return the timestamp of the sample just after the end of this frame. -double mp_audio_end_pts(struct mp_audio *f) -{ - if (f->pts == MP_NOPTS_VALUE || f->rate < 1) - return MP_NOPTS_VALUE; - return f->pts + f->samples / (double)f->rate; -} - -// Clip the given frame to the given timestamp range. Adjusts the frame size -// and timestamp. -void mp_audio_clip_timestamps(struct mp_audio *f, double start, double end) -{ - double f_end = mp_audio_end_pts(f); - if (f_end == MP_NOPTS_VALUE) - return; - if (end != MP_NOPTS_VALUE) { - if (f_end >= end) { - if (f->pts >= end) { - f->samples = 0; - } else { - int new = (end - f->pts) * f->rate; - f->samples = MPCLAMP(new, 0, f->samples); - } - } - } - if (start != MP_NOPTS_VALUE) { - if (f->pts < start) { - if (f_end <= start) { - f->samples = 0; - f->pts = f_end; - } else { - int skip = (start - f->pts) * f->rate; - skip = MPCLAMP(skip, 0, f->samples); - mp_audio_skip_samples(f, skip); - } - } - } -} - - -// Return false if the frame data is shared, true otherwise. -// Will return true for non-refcounted frames. -bool mp_audio_is_writeable(struct mp_audio *data) -{ - bool ok = true; - for (int n = 0; n < MP_NUM_CHANNELS; n++) { - if (data->allocated[n]) - ok &= av_buffer_is_writable(data->allocated[n]); - } - return ok; -} - -static void mp_audio_steal_data(struct mp_audio *dst, struct mp_audio *src) -{ - talloc_set_destructor(dst, mp_audio_destructor); - mp_audio_destructor(dst); - *dst = *src; - talloc_set_destructor(src, NULL); - talloc_free(src); -} - -// Make sure the frame owns the audio data, and if not, copy the data. -// Return negative value on failure (which means it can't be made writeable). -// Non-refcounted frames are always considered writeable. -int mp_audio_make_writeable(struct mp_audio *data) -{ - if (!mp_audio_is_writeable(data)) { - struct mp_audio *new = talloc(NULL, struct mp_audio); - *new = *data; - mp_audio_set_null_data(new); // use format only - mp_audio_realloc(new, data->samples); - new->samples = data->samples; - mp_audio_copy(new, 0, data, 0, data->samples); - mp_audio_steal_data(data, new); - } - return 0; -} - -struct mp_audio *mp_audio_from_avframe(struct AVFrame *avframe) -{ - AVFrame *tmp = NULL; - struct mp_audio *new = talloc_zero(NULL, struct mp_audio); - talloc_set_destructor(new, mp_audio_destructor); - - mp_audio_set_format(new, af_from_avformat(avframe->format)); - - struct mp_chmap lavc_chmap; - mp_chmap_from_lavc(&lavc_chmap, avframe->channel_layout); - -#if LIBAVUTIL_VERSION_MICRO >= 100 - // FFmpeg being stupid POS again - if (lavc_chmap.num != avframe->channels) - mp_chmap_from_channels(&lavc_chmap, avframe->channels); -#endif - - new->rate = avframe->sample_rate; - - mp_audio_set_channels(new, &lavc_chmap); - - // Force refcounted frame. - if (!avframe->buf[0]) { - tmp = av_frame_alloc(); - if (!tmp) - goto fail; - if (av_frame_ref(tmp, avframe) < 0) - goto fail; - avframe = tmp; - } - - // If we can't handle the format (e.g. too many channels), bail out. - if (!mp_audio_config_valid(new)) - goto fail; - - for (int n = 0; n < AV_NUM_DATA_POINTERS + avframe->nb_extended_buf; n++) { - AVBufferRef *buf = n < AV_NUM_DATA_POINTERS ? avframe->buf[n] - : avframe->extended_buf[n - AV_NUM_DATA_POINTERS]; - if (!buf) - break; - if (n >= MP_NUM_CHANNELS) - goto fail; - new->allocated[n] = av_buffer_ref(buf); - if (!new->allocated[n]) - goto fail; - } - - for (int n = 0; n < new->num_planes; n++) - new->planes[n] = avframe->extended_data[n]; - new->samples = avframe->nb_samples; - - return new; - -fail: - talloc_free(new); - av_frame_free(&tmp); - return NULL; -} - -struct mp_audio *mp_audio_from_aframe(struct mp_aframe *aframe) -{ - if (!aframe) - return NULL; - - struct AVFrame *av = mp_aframe_get_raw_avframe(aframe); - struct mp_audio *res = mp_audio_from_avframe(av); - if (!res) - return NULL; - struct mp_chmap chmap = {0}; - mp_aframe_get_chmap(aframe, &chmap); - mp_audio_set_channels(res, &chmap); - mp_audio_set_format(res, mp_aframe_get_format(aframe)); - res->pts = mp_aframe_get_pts(aframe); - return res; -} - -void mp_audio_config_from_aframe(struct mp_audio *dst, struct mp_aframe *src) -{ - *dst = (struct mp_audio){0}; - struct mp_chmap chmap = {0}; - mp_aframe_get_chmap(src, &chmap); - mp_audio_set_channels(dst, &chmap); - mp_audio_set_format(dst, mp_aframe_get_format(src)); - dst->rate = mp_aframe_get_rate(src); -} - -struct mp_aframe *mp_audio_to_aframe(struct mp_audio *mpa) -{ - if (!mpa) - return NULL; - - struct mp_aframe *aframe = mp_aframe_create(); - struct AVFrame *av = mp_aframe_get_raw_avframe(aframe); - mp_aframe_set_format(aframe, mpa->format); - mp_aframe_set_chmap(aframe, &mpa->channels); - mp_aframe_set_rate(aframe, mpa->rate); - - // bullshit it into ffmpeg-compatible parameters - struct mp_audio mpb = *mpa; - struct mp_chmap chmap; - mp_chmap_set_unknown(&chmap, mpb.channels.num); - mp_audio_set_channels(&mpb, &chmap); - if (af_fmt_is_spdif(mpb.format)) - mp_audio_set_format(&mpb, AF_FORMAT_S16); - - // put the reference into av, which magically puts it into aframe - // aframe keeps its parameters, so the bullshit doesn't matter - if (mp_audio_to_avframe(&mpb, av) < 0) { - talloc_free(aframe); - return NULL; - } - return aframe; -} - -int mp_audio_to_avframe(struct mp_audio *frame, struct AVFrame *avframe) -{ - av_frame_unref(avframe); - - avframe->nb_samples = frame->samples; - avframe->format = af_to_avformat(frame->format); - if (avframe->format == AV_SAMPLE_FMT_NONE) - goto fail; - - avframe->channel_layout = mp_chmap_to_lavc(&frame->channels); - if (!avframe->channel_layout) - goto fail; -#if LIBAVUTIL_VERSION_MICRO >= 100 - // FFmpeg being a stupid POS again - avframe->channels = frame->channels.num; -#endif - avframe->sample_rate = frame->rate; - - if (frame->num_planes > AV_NUM_DATA_POINTERS) { - avframe->extended_data = - av_mallocz_array(frame->num_planes, sizeof(avframe->extended_data[0])); - int extbufs = frame->num_planes - AV_NUM_DATA_POINTERS; - avframe->extended_buf = - av_mallocz_array(extbufs, sizeof(avframe->extended_buf[0])); - if (!avframe->extended_data || !avframe->extended_buf) - goto fail; - avframe->nb_extended_buf = extbufs; - } - - for (int p = 0; p < frame->num_planes; p++) - avframe->extended_data[p] = frame->planes[p]; - avframe->linesize[0] = frame->samples * frame->sstride; - - for (int p = 0; p < AV_NUM_DATA_POINTERS; p++) - avframe->data[p] = avframe->extended_data[p]; - - for (int p = 0; p < frame->num_planes; p++) { - if (!frame->allocated[p]) - break; - AVBufferRef *nref = av_buffer_ref(frame->allocated[p]); - if (!nref) - goto fail; - if (p < AV_NUM_DATA_POINTERS) { - avframe->buf[p] = nref; - } else { - avframe->extended_buf[p - AV_NUM_DATA_POINTERS] = nref; - } - } - - // Force refcounted frame. - if (!avframe->buf[0]) { - AVFrame *tmp = av_frame_alloc(); - if (!tmp) - goto fail; - if (av_frame_ref(tmp, avframe) < 0) - goto fail; - av_frame_free(&avframe); - avframe = tmp; - } - - return 0; - -fail: - av_frame_unref(avframe); - return -1; -} - -// Returns NULL on failure. The input is always unreffed. -struct AVFrame *mp_audio_to_avframe_and_unref(struct mp_audio *frame) -{ - struct AVFrame *avframe = av_frame_alloc(); - if (!avframe) - goto fail; - - if (mp_audio_to_avframe(frame, avframe) < 0) - goto fail; - - talloc_free(frame); - return avframe; - -fail: - av_frame_free(&avframe); - talloc_free(frame); - return NULL; -} - -struct mp_audio_pool { - AVBufferPool *avpool; - int element_size; -}; - -struct mp_audio_pool *mp_audio_pool_create(void *ta_parent) -{ - return talloc_zero(ta_parent, struct mp_audio_pool); -} - -static void mp_audio_pool_destructor(void *p) -{ - struct mp_audio_pool *pool = p; - av_buffer_pool_uninit(&pool->avpool); -} - -// Allocate data using the given format and number of samples. -// Returns NULL on error. -struct mp_audio *mp_audio_pool_get(struct mp_audio_pool *pool, - const struct mp_audio *fmt, int samples) -{ - int size = get_plane_size(fmt, samples); - if (size < 0) - return NULL; - if (!pool->avpool || size > pool->element_size) { - size_t alloc = ta_calc_prealloc_elems(size); - if (alloc >= INT_MAX) - return NULL; - av_buffer_pool_uninit(&pool->avpool); - pool->element_size = alloc; - pool->avpool = av_buffer_pool_init(pool->element_size, NULL); - if (!pool->avpool) - return NULL; - talloc_set_destructor(pool, mp_audio_pool_destructor); - } - struct mp_audio *new = talloc_ptrtype(NULL, new); - talloc_set_destructor(new, mp_audio_destructor); - *new = *fmt; - mp_audio_set_null_data(new); - new->samples = samples; - for (int n = 0; n < new->num_planes; n++) { - new->allocated[n] = av_buffer_pool_get(pool->avpool); - if (!new->allocated[n]) { - talloc_free(new); - return NULL; - } - new->planes[n] = new->allocated[n]->data; - } - return new; -} - -// Return a copy of the given frame. -// Returns NULL on error. -struct mp_audio *mp_audio_pool_new_copy(struct mp_audio_pool *pool, - struct mp_audio *frame) -{ - struct mp_audio *new = mp_audio_pool_get(pool, frame, frame->samples); - if (new) { - mp_audio_copy(new, 0, frame, 0, new->samples); - mp_audio_copy_attributes(new, frame); - } - return new; -} - -// Exactly like mp_audio_make_writeable(), but get the data from the pool. -int mp_audio_pool_make_writeable(struct mp_audio_pool *pool, - struct mp_audio *data) -{ - if (mp_audio_is_writeable(data)) - return 0; - struct mp_audio *new = mp_audio_pool_get(pool, data, data->samples); - if (!new) - return -1; - mp_audio_copy(new, 0, data, 0, data->samples); - mp_audio_copy_attributes(new, data); - mp_audio_steal_data(data, new); - return 0; -} |