aboutsummaryrefslogtreecommitdiffstats
path: root/libavresample/audio_data.c
diff options
context:
space:
mode:
authorJustin Ruggles <justin.ruggles@gmail.com>2012-03-23 17:42:17 -0400
committerJustin Ruggles <justin.ruggles@gmail.com>2012-04-24 21:28:27 -0400
commitc8af852b97447491823ff9b91413e32415e2babf (patch)
tree6c02f850cf954612c7077f266a75d663bb9cde57 /libavresample/audio_data.c
parentc5671aeb77abb18a5a10ace314ab49e8fd3d0cb3 (diff)
downloadffmpeg-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.c345
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;
+}