diff options
author | Justin Ruggles <justin.ruggles@gmail.com> | 2012-03-23 17:42:17 -0400 |
---|---|---|
committer | Justin Ruggles <justin.ruggles@gmail.com> | 2012-04-24 21:28:27 -0400 |
commit | c8af852b97447491823ff9b91413e32415e2babf (patch) | |
tree | 6c02f850cf954612c7077f266a75d663bb9cde57 /libavresample/audio_data.c | |
parent | c5671aeb77abb18a5a10ace314ab49e8fd3d0cb3 (diff) | |
download | ffmpeg-c8af852b97447491823ff9b91413e32415e2babf.tar.gz |
Add libavresample
This is a new library for audio sample format, channel layout, and sample rate
conversion.
Diffstat (limited to 'libavresample/audio_data.c')
-rw-r--r-- | libavresample/audio_data.c | 345 |
1 files changed, 345 insertions, 0 deletions
diff --git a/libavresample/audio_data.c b/libavresample/audio_data.c new file mode 100644 index 0000000000..3f82c50ef0 --- /dev/null +++ b/libavresample/audio_data.c @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com> + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdint.h> + +#include "libavutil/mem.h" +#include "audio_data.h" + +static const AVClass audio_data_class = { + .class_name = "AudioData", + .item_name = av_default_item_name, + .version = LIBAVUTIL_VERSION_INT, +}; + +/* + * Calculate alignment for data pointers. + */ +static void calc_ptr_alignment(AudioData *a) +{ + int p; + int min_align = 128; + + for (p = 0; p < a->planes; p++) { + int cur_align = 128; + while ((intptr_t)a->data[p] % cur_align) + cur_align >>= 1; + if (cur_align < min_align) + min_align = cur_align; + } + a->ptr_align = min_align; +} + +int ff_audio_data_set_channels(AudioData *a, int channels) +{ + if (channels < 1 || channels > AVRESAMPLE_MAX_CHANNELS || + channels > a->allocated_channels) + return AVERROR(EINVAL); + + a->channels = channels; + a->planes = a->is_planar ? channels : 1; + + calc_ptr_alignment(a); + + return 0; +} + +int ff_audio_data_init(AudioData *a, void **src, int plane_size, int channels, + int nb_samples, enum AVSampleFormat sample_fmt, + int read_only, const char *name) +{ + int p; + + memset(a, 0, sizeof(*a)); + a->class = &audio_data_class; + + if (channels < 1 || channels > AVRESAMPLE_MAX_CHANNELS) { + av_log(a, AV_LOG_ERROR, "invalid channel count: %d\n", channels); + return AVERROR(EINVAL); + } + + a->sample_size = av_get_bytes_per_sample(sample_fmt); + if (!a->sample_size) { + av_log(a, AV_LOG_ERROR, "invalid sample format\n"); + return AVERROR(EINVAL); + } + a->is_planar = av_sample_fmt_is_planar(sample_fmt); + a->planes = a->is_planar ? channels : 1; + a->stride = a->sample_size * (a->is_planar ? 1 : channels); + + for (p = 0; p < (a->is_planar ? channels : 1); p++) { + if (!src[p]) { + av_log(a, AV_LOG_ERROR, "invalid NULL pointer for src[%d]\n", p); + return AVERROR(EINVAL); + } + a->data[p] = src[p]; + } + a->allocated_samples = nb_samples * !read_only; + a->nb_samples = nb_samples; + a->sample_fmt = sample_fmt; + a->channels = channels; + a->allocated_channels = channels; + a->read_only = read_only; + a->allow_realloc = 0; + a->name = name ? name : "{no name}"; + + calc_ptr_alignment(a); + a->samples_align = plane_size / a->stride; + + return 0; +} + +AudioData *ff_audio_data_alloc(int channels, int nb_samples, + enum AVSampleFormat sample_fmt, const char *name) +{ + AudioData *a; + int ret; + + if (channels < 1 || channels > AVRESAMPLE_MAX_CHANNELS) + return NULL; + + a = av_mallocz(sizeof(*a)); + if (!a) + return NULL; + + a->sample_size = av_get_bytes_per_sample(sample_fmt); + if (!a->sample_size) { + av_free(a); + return NULL; + } + a->is_planar = av_sample_fmt_is_planar(sample_fmt); + a->planes = a->is_planar ? channels : 1; + a->stride = a->sample_size * (a->is_planar ? 1 : channels); + + a->class = &audio_data_class; + a->sample_fmt = sample_fmt; + a->channels = channels; + a->allocated_channels = channels; + a->read_only = 0; + a->allow_realloc = 1; + a->name = name ? name : "{no name}"; + + if (nb_samples > 0) { + ret = ff_audio_data_realloc(a, nb_samples); + if (ret < 0) { + av_free(a); + return NULL; + } + return a; + } else { + calc_ptr_alignment(a); + return a; + } +} + +int ff_audio_data_realloc(AudioData *a, int nb_samples) +{ + int ret, new_buf_size, plane_size, p; + + /* check if buffer is already large enough */ + if (a->allocated_samples >= nb_samples) + return 0; + + /* validate that the output is not read-only and realloc is allowed */ + if (a->read_only || !a->allow_realloc) + return AVERROR(EINVAL); + + new_buf_size = av_samples_get_buffer_size(&plane_size, + a->allocated_channels, nb_samples, + a->sample_fmt, 0); + if (new_buf_size < 0) + return new_buf_size; + + /* if there is already data in the buffer and the sample format is planar, + allocate a new buffer and copy the data, otherwise just realloc the + internal buffer and set new data pointers */ + if (a->nb_samples > 0 && a->is_planar) { + uint8_t *new_data[AVRESAMPLE_MAX_CHANNELS] = { NULL }; + + ret = av_samples_alloc(new_data, &plane_size, a->allocated_channels, + nb_samples, a->sample_fmt, 0); + if (ret < 0) + return ret; + + for (p = 0; p < a->planes; p++) + memcpy(new_data[p], a->data[p], a->nb_samples * a->stride); + + av_freep(&a->buffer); + memcpy(a->data, new_data, sizeof(new_data)); + a->buffer = a->data[0]; + } else { + av_freep(&a->buffer); + a->buffer = av_malloc(new_buf_size); + if (!a->buffer) + return AVERROR(ENOMEM); + ret = av_samples_fill_arrays(a->data, &plane_size, a->buffer, + a->allocated_channels, nb_samples, + a->sample_fmt, 0); + if (ret < 0) + return ret; + } + a->buffer_size = new_buf_size; + a->allocated_samples = nb_samples; + + calc_ptr_alignment(a); + a->samples_align = plane_size / a->stride; + + return 0; +} + +void ff_audio_data_free(AudioData **a) +{ + if (!*a) + return; + av_free((*a)->buffer); + av_freep(a); +} + +int ff_audio_data_copy(AudioData *dst, AudioData *src) +{ + int ret, p; + + /* validate input/output compatibility */ + if (dst->sample_fmt != src->sample_fmt || dst->channels < src->channels) + return AVERROR(EINVAL); + + /* if the input is empty, just empty the output */ + if (!src->nb_samples) { + dst->nb_samples = 0; + return 0; + } + + /* reallocate output if necessary */ + ret = ff_audio_data_realloc(dst, src->nb_samples); + if (ret < 0) + return ret; + + /* copy data */ + for (p = 0; p < src->planes; p++) + memcpy(dst->data[p], src->data[p], src->nb_samples * src->stride); + dst->nb_samples = src->nb_samples; + + return 0; +} + +int ff_audio_data_combine(AudioData *dst, int dst_offset, AudioData *src, + int src_offset, int nb_samples) +{ + int ret, p, dst_offset2, dst_move_size; + + /* validate input/output compatibility */ + if (dst->sample_fmt != src->sample_fmt || dst->channels != src->channels) { + av_log(src, AV_LOG_ERROR, "sample format mismatch\n"); + return AVERROR(EINVAL); + } + + /* validate offsets are within the buffer bounds */ + if (dst_offset < 0 || dst_offset > dst->nb_samples || + src_offset < 0 || src_offset > src->nb_samples) { + av_log(src, AV_LOG_ERROR, "offset out-of-bounds: src=%d dst=%d\n", + src_offset, dst_offset); + return AVERROR(EINVAL); + } + + /* check offsets and sizes to see if we can just do nothing and return */ + if (nb_samples > src->nb_samples - src_offset) + nb_samples = src->nb_samples - src_offset; + if (nb_samples <= 0) + return 0; + + /* validate that the output is not read-only */ + if (dst->read_only) { + av_log(dst, AV_LOG_ERROR, "dst is read-only\n"); + return AVERROR(EINVAL); + } + + /* reallocate output if necessary */ + ret = ff_audio_data_realloc(dst, dst->nb_samples + nb_samples); + if (ret < 0) { + av_log(dst, AV_LOG_ERROR, "error reallocating dst\n"); + return ret; + } + + dst_offset2 = dst_offset + nb_samples; + dst_move_size = dst->nb_samples - dst_offset; + + for (p = 0; p < src->planes; p++) { + if (dst_move_size > 0) { + memmove(dst->data[p] + dst_offset2 * dst->stride, + dst->data[p] + dst_offset * dst->stride, + dst_move_size * dst->stride); + } + memcpy(dst->data[p] + dst_offset * dst->stride, + src->data[p] + src_offset * src->stride, + nb_samples * src->stride); + } + dst->nb_samples += nb_samples; + + return 0; +} + +void ff_audio_data_drain(AudioData *a, int nb_samples) +{ + if (a->nb_samples <= nb_samples) { + /* drain the whole buffer */ + a->nb_samples = 0; + } else { + int p; + int move_offset = a->stride * nb_samples; + int move_size = a->stride * (a->nb_samples - nb_samples); + + for (p = 0; p < a->planes; p++) + memmove(a->data[p], a->data[p] + move_offset, move_size); + + a->nb_samples -= nb_samples; + } +} + +int ff_audio_data_add_to_fifo(AVAudioFifo *af, AudioData *a, int offset, + int nb_samples) +{ + uint8_t *offset_data[AVRESAMPLE_MAX_CHANNELS]; + int offset_size, p; + + if (offset >= a->nb_samples) + return 0; + offset_size = offset * a->stride; + for (p = 0; p < a->planes; p++) + offset_data[p] = a->data[p] + offset_size; + + return av_audio_fifo_write(af, (void **)offset_data, nb_samples); +} + +int ff_audio_data_read_from_fifo(AVAudioFifo *af, AudioData *a, int nb_samples) +{ + int ret; + + if (a->read_only) + return AVERROR(EINVAL); + + ret = ff_audio_data_realloc(a, nb_samples); + if (ret < 0) + return ret; + + ret = av_audio_fifo_read(af, (void **)a->data, nb_samples); + if (ret >= 0) + a->nb_samples = ret; + return ret; +} |