381 lines · 11.3 KB · C
RawDownload
/* * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com> * * This file is part of FFmpeg. * * FFmpeg 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. * * FFmpeg 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 FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <stdint.h>#include <string.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_sample_fmt_is_planar(enum AVSampleFormat sample_fmt, int channels){    if (channels == 1)        return 1;    else        return av_sample_fmt_is_planar(sample_fmt);} 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, uint8_t * const *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 = ff_sample_fmt_is_planar(sample_fmt, channels);    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 = ff_sample_fmt_is_planar(sample_fmt, channels);    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, ChannelMapInfo *map){    int ret, p;     /* validate input/output compatibility */    if (dst->sample_fmt != src->sample_fmt || dst->channels < src->channels)        return AVERROR(EINVAL);     if (map && !src->is_planar) {        av_log(src, AV_LOG_ERROR, "cannot remap packed format during copy\n");        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 */    if (map) {        if (map->do_remap) {            for (p = 0; p < src->planes; p++) {                if (map->channel_map[p] >= 0)                    memcpy(dst->data[p], src->data[map->channel_map[p]],                           src->nb_samples * src->stride);            }        }        if (map->do_copy || map->do_zero) {            for (p = 0; p < src->planes; p++) {                if (map->channel_copy[p])                    memcpy(dst->data[p], dst->data[map->channel_copy[p]],                           src->nb_samples * src->stride);                else if (map->channel_zero[p])                    av_samples_set_silence(&dst->data[p], 0, src->nb_samples,                                           1, dst->sample_fmt);            }        }    } else {        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;}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t