1/*
2 * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21#include <stdint.h>
22#include <string.h>
23
24#include "libavutil/mem.h"
25#include "audio_data.h"
26
27static const AVClass audio_data_class = {
28    .class_name = "AudioData",
29    .item_name  = av_default_item_name,
30    .version    = LIBAVUTIL_VERSION_INT,
31};
32
33/*
34 * Calculate alignment for data pointers.
35 */
36static void calc_ptr_alignment(AudioData *a)
37{
38    int p;
39    int min_align = 128;
40
41    for (p = 0; p < a->planes; p++) {
42        int cur_align = 128;
43        while ((intptr_t)a->data[p] % cur_align)
44            cur_align >>= 1;
45        if (cur_align < min_align)
46            min_align = cur_align;
47    }
48    a->ptr_align = min_align;
49}
50
51int ff_audio_data_set_channels(AudioData *a, int channels)
52{
53    if (channels < 1 || channels > AVRESAMPLE_MAX_CHANNELS ||
54        channels > a->allocated_channels)
55        return AVERROR(EINVAL);
56
57    a->channels  = channels;
58    a->planes    = a->is_planar ? channels : 1;
59
60    calc_ptr_alignment(a);
61
62    return 0;
63}
64
65int ff_audio_data_init(AudioData *a, uint8_t **src, int plane_size, int channels,
66                       int nb_samples, enum AVSampleFormat sample_fmt,
67                       int read_only, const char *name)
68{
69    int p;
70
71    memset(a, 0, sizeof(*a));
72    a->class = &audio_data_class;
73
74    if (channels < 1 || channels > AVRESAMPLE_MAX_CHANNELS) {
75        av_log(a, AV_LOG_ERROR, "invalid channel count: %d\n", channels);
76        return AVERROR(EINVAL);
77    }
78
79    a->sample_size = av_get_bytes_per_sample(sample_fmt);
80    if (!a->sample_size) {
81        av_log(a, AV_LOG_ERROR, "invalid sample format\n");
82        return AVERROR(EINVAL);
83    }
84    a->is_planar = av_sample_fmt_is_planar(sample_fmt);
85    a->planes    = a->is_planar ? channels : 1;
86    a->stride    = a->sample_size * (a->is_planar ? 1 : channels);
87
88    for (p = 0; p < (a->is_planar ? channels : 1); p++) {
89        if (!src[p]) {
90            av_log(a, AV_LOG_ERROR, "invalid NULL pointer for src[%d]\n", p);
91            return AVERROR(EINVAL);
92        }
93        a->data[p] = src[p];
94    }
95    a->allocated_samples  = nb_samples * !read_only;
96    a->nb_samples         = nb_samples;
97    a->sample_fmt         = sample_fmt;
98    a->channels           = channels;
99    a->allocated_channels = channels;
100    a->read_only          = read_only;
101    a->allow_realloc      = 0;
102    a->name               = name ? name : "{no name}";
103
104    calc_ptr_alignment(a);
105    a->samples_align = plane_size / a->stride;
106
107    return 0;
108}
109
110AudioData *ff_audio_data_alloc(int channels, int nb_samples,
111                               enum AVSampleFormat sample_fmt, const char *name)
112{
113    AudioData *a;
114    int ret;
115
116    if (channels < 1 || channels > AVRESAMPLE_MAX_CHANNELS)
117        return NULL;
118
119    a = av_mallocz(sizeof(*a));
120    if (!a)
121        return NULL;
122
123    a->sample_size = av_get_bytes_per_sample(sample_fmt);
124    if (!a->sample_size) {
125        av_free(a);
126        return NULL;
127    }
128    a->is_planar = av_sample_fmt_is_planar(sample_fmt);
129    a->planes    = a->is_planar ? channels : 1;
130    a->stride    = a->sample_size * (a->is_planar ? 1 : channels);
131
132    a->class              = &audio_data_class;
133    a->sample_fmt         = sample_fmt;
134    a->channels           = channels;
135    a->allocated_channels = channels;
136    a->read_only          = 0;
137    a->allow_realloc      = 1;
138    a->name               = name ? name : "{no name}";
139
140    if (nb_samples > 0) {
141        ret = ff_audio_data_realloc(a, nb_samples);
142        if (ret < 0) {
143            av_free(a);
144            return NULL;
145        }
146        return a;
147    } else {
148        calc_ptr_alignment(a);
149        return a;
150    }
151}
152
153int ff_audio_data_realloc(AudioData *a, int nb_samples)
154{
155    int ret, new_buf_size, plane_size, p;
156
157    /* check if buffer is already large enough */
158    if (a->allocated_samples >= nb_samples)
159        return 0;
160
161    /* validate that the output is not read-only and realloc is allowed */
162    if (a->read_only || !a->allow_realloc)
163        return AVERROR(EINVAL);
164
165    new_buf_size = av_samples_get_buffer_size(&plane_size,
166                                              a->allocated_channels, nb_samples,
167                                              a->sample_fmt, 0);
168    if (new_buf_size < 0)
169        return new_buf_size;
170
171    /* if there is already data in the buffer and the sample format is planar,
172       allocate a new buffer and copy the data, otherwise just realloc the
173       internal buffer and set new data pointers */
174    if (a->nb_samples > 0 && a->is_planar) {
175        uint8_t *new_data[AVRESAMPLE_MAX_CHANNELS] = { NULL };
176
177        ret = av_samples_alloc(new_data, &plane_size, a->allocated_channels,
178                               nb_samples, a->sample_fmt, 0);
179        if (ret < 0)
180            return ret;
181
182        for (p = 0; p < a->planes; p++)
183            memcpy(new_data[p], a->data[p], a->nb_samples * a->stride);
184
185        av_freep(&a->buffer);
186        memcpy(a->data, new_data, sizeof(new_data));
187        a->buffer = a->data[0];
188    } else {
189        av_freep(&a->buffer);
190        a->buffer = av_malloc(new_buf_size);
191        if (!a->buffer)
192            return AVERROR(ENOMEM);
193        ret = av_samples_fill_arrays(a->data, &plane_size, a->buffer,
194                                     a->allocated_channels, nb_samples,
195                                     a->sample_fmt, 0);
196        if (ret < 0)
197            return ret;
198    }
199    a->buffer_size       = new_buf_size;
200    a->allocated_samples = nb_samples;
201
202    calc_ptr_alignment(a);
203    a->samples_align = plane_size / a->stride;
204
205    return 0;
206}
207
208void ff_audio_data_free(AudioData **a)
209{
210    if (!*a)
211        return;
212    av_free((*a)->buffer);
213    av_freep(a);
214}
215
216int ff_audio_data_copy(AudioData *dst, AudioData *src, ChannelMapInfo *map)
217{
218    int ret, p;
219
220    /* validate input/output compatibility */
221    if (dst->sample_fmt != src->sample_fmt || dst->channels < src->channels)
222        return AVERROR(EINVAL);
223
224    if (map && !src->is_planar) {
225        av_log(src, AV_LOG_ERROR, "cannot remap packed format during copy\n");
226        return AVERROR(EINVAL);
227    }
228
229    /* if the input is empty, just empty the output */
230    if (!src->nb_samples) {
231        dst->nb_samples = 0;
232        return 0;
233    }
234
235    /* reallocate output if necessary */
236    ret = ff_audio_data_realloc(dst, src->nb_samples);
237    if (ret < 0)
238        return ret;
239
240    /* copy data */
241    if (map) {
242        if (map->do_remap) {
243            for (p = 0; p < src->planes; p++) {
244                if (map->channel_map[p] >= 0)
245                    memcpy(dst->data[p], src->data[map->channel_map[p]],
246                           src->nb_samples * src->stride);
247            }
248        }
249        if (map->do_copy || map->do_zero) {
250            for (p = 0; p < src->planes; p++) {
251                if (map->channel_copy[p])
252                    memcpy(dst->data[p], dst->data[map->channel_copy[p]],
253                           src->nb_samples * src->stride);
254                else if (map->channel_zero[p])
255                    av_samples_set_silence(&dst->data[p], 0, src->nb_samples,
256                                           1, dst->sample_fmt);
257            }
258        }
259    } else {
260        for (p = 0; p < src->planes; p++)
261            memcpy(dst->data[p], src->data[p], src->nb_samples * src->stride);
262    }
263
264    dst->nb_samples = src->nb_samples;
265
266    return 0;
267}
268
269int ff_audio_data_combine(AudioData *dst, int dst_offset, AudioData *src,
270                          int src_offset, int nb_samples)
271{
272    int ret, p, dst_offset2, dst_move_size;
273
274    /* validate input/output compatibility */
275    if (dst->sample_fmt != src->sample_fmt || dst->channels != src->channels) {
276        av_log(src, AV_LOG_ERROR, "sample format mismatch\n");
277        return AVERROR(EINVAL);
278    }
279
280    /* validate offsets are within the buffer bounds */
281    if (dst_offset < 0 || dst_offset > dst->nb_samples ||
282        src_offset < 0 || src_offset > src->nb_samples) {
283        av_log(src, AV_LOG_ERROR, "offset out-of-bounds: src=%d dst=%d\n",
284               src_offset, dst_offset);
285        return AVERROR(EINVAL);
286    }
287
288    /* check offsets and sizes to see if we can just do nothing and return */
289    if (nb_samples > src->nb_samples - src_offset)
290        nb_samples = src->nb_samples - src_offset;
291    if (nb_samples <= 0)
292        return 0;
293
294    /* validate that the output is not read-only */
295    if (dst->read_only) {
296        av_log(dst, AV_LOG_ERROR, "dst is read-only\n");
297        return AVERROR(EINVAL);
298    }
299
300    /* reallocate output if necessary */
301    ret = ff_audio_data_realloc(dst, dst->nb_samples + nb_samples);
302    if (ret < 0) {
303        av_log(dst, AV_LOG_ERROR, "error reallocating dst\n");
304        return ret;
305    }
306
307    dst_offset2   = dst_offset + nb_samples;
308    dst_move_size = dst->nb_samples - dst_offset;
309
310    for (p = 0; p < src->planes; p++) {
311        if (dst_move_size > 0) {
312            memmove(dst->data[p] + dst_offset2 * dst->stride,
313                    dst->data[p] + dst_offset  * dst->stride,
314                    dst_move_size * dst->stride);
315        }
316        memcpy(dst->data[p] + dst_offset * dst->stride,
317               src->data[p] + src_offset * src->stride,
318               nb_samples * src->stride);
319    }
320    dst->nb_samples += nb_samples;
321
322    return 0;
323}
324
325void ff_audio_data_drain(AudioData *a, int nb_samples)
326{
327    if (a->nb_samples <= nb_samples) {
328        /* drain the whole buffer */
329        a->nb_samples = 0;
330    } else {
331        int p;
332        int move_offset = a->stride * nb_samples;
333        int move_size   = a->stride * (a->nb_samples - nb_samples);
334
335        for (p = 0; p < a->planes; p++)
336            memmove(a->data[p], a->data[p] + move_offset, move_size);
337
338        a->nb_samples -= nb_samples;
339    }
340}
341
342int ff_audio_data_add_to_fifo(AVAudioFifo *af, AudioData *a, int offset,
343                              int nb_samples)
344{
345    uint8_t *offset_data[AVRESAMPLE_MAX_CHANNELS];
346    int offset_size, p;
347
348    if (offset >= a->nb_samples)
349        return 0;
350    offset_size = offset * a->stride;
351    for (p = 0; p < a->planes; p++)
352        offset_data[p] = a->data[p] + offset_size;
353
354    return av_audio_fifo_write(af, (void **)offset_data, nb_samples);
355}
356
357int ff_audio_data_read_from_fifo(AVAudioFifo *af, AudioData *a, int nb_samples)
358{
359    int ret;
360
361    if (a->read_only)
362        return AVERROR(EINVAL);
363
364    ret = ff_audio_data_realloc(a, nb_samples);
365    if (ret < 0)
366        return ret;
367
368    ret = av_audio_fifo_read(af, (void **)a->data, nb_samples);
369    if (ret >= 0)
370        a->nb_samples = ret;
371    return ret;
372}
373