]> git.sesse.net Git - ffmpeg/blob - libavresample/audio_data.c
Merge remote-tracking branch 'qatar/master'
[ffmpeg] / libavresample / audio_data.c
1 /*
2  * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
3  *
4  * This file is part of Libav.
5  *
6  * Libav 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  * Libav 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 Libav; 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
23 #include "libavutil/mem.h"
24 #include "audio_data.h"
25
26 static const AVClass audio_data_class = {
27     .class_name = "AudioData",
28     .item_name  = av_default_item_name,
29     .version    = LIBAVUTIL_VERSION_INT,
30 };
31
32 /*
33  * Calculate alignment for data pointers.
34  */
35 static void calc_ptr_alignment(AudioData *a)
36 {
37     int p;
38     int min_align = 128;
39
40     for (p = 0; p < a->planes; p++) {
41         int cur_align = 128;
42         while ((intptr_t)a->data[p] % cur_align)
43             cur_align >>= 1;
44         if (cur_align < min_align)
45             min_align = cur_align;
46     }
47     a->ptr_align = min_align;
48 }
49
50 int ff_audio_data_set_channels(AudioData *a, int channels)
51 {
52     if (channels < 1 || channels > AVRESAMPLE_MAX_CHANNELS ||
53         channels > a->allocated_channels)
54         return AVERROR(EINVAL);
55
56     a->channels  = channels;
57     a->planes    = a->is_planar ? channels : 1;
58
59     calc_ptr_alignment(a);
60
61     return 0;
62 }
63
64 int ff_audio_data_init(AudioData *a, void **src, int plane_size, int channels,
65                        int nb_samples, enum AVSampleFormat sample_fmt,
66                        int read_only, const char *name)
67 {
68     int p;
69
70     memset(a, 0, sizeof(*a));
71     a->class = &audio_data_class;
72
73     if (channels < 1 || channels > AVRESAMPLE_MAX_CHANNELS) {
74         av_log(a, AV_LOG_ERROR, "invalid channel count: %d\n", channels);
75         return AVERROR(EINVAL);
76     }
77
78     a->sample_size = av_get_bytes_per_sample(sample_fmt);
79     if (!a->sample_size) {
80         av_log(a, AV_LOG_ERROR, "invalid sample format\n");
81         return AVERROR(EINVAL);
82     }
83     a->is_planar = av_sample_fmt_is_planar(sample_fmt);
84     a->planes    = a->is_planar ? channels : 1;
85     a->stride    = a->sample_size * (a->is_planar ? 1 : channels);
86
87     for (p = 0; p < (a->is_planar ? channels : 1); p++) {
88         if (!src[p]) {
89             av_log(a, AV_LOG_ERROR, "invalid NULL pointer for src[%d]\n", p);
90             return AVERROR(EINVAL);
91         }
92         a->data[p] = src[p];
93     }
94     a->allocated_samples  = nb_samples * !read_only;
95     a->nb_samples         = nb_samples;
96     a->sample_fmt         = sample_fmt;
97     a->channels           = channels;
98     a->allocated_channels = channels;
99     a->read_only          = read_only;
100     a->allow_realloc      = 0;
101     a->name               = name ? name : "{no name}";
102
103     calc_ptr_alignment(a);
104     a->samples_align = plane_size / a->stride;
105
106     return 0;
107 }
108
109 AudioData *ff_audio_data_alloc(int channels, int nb_samples,
110                                enum AVSampleFormat sample_fmt, const char *name)
111 {
112     AudioData *a;
113     int ret;
114
115     if (channels < 1 || channels > AVRESAMPLE_MAX_CHANNELS)
116         return NULL;
117
118     a = av_mallocz(sizeof(*a));
119     if (!a)
120         return NULL;
121
122     a->sample_size = av_get_bytes_per_sample(sample_fmt);
123     if (!a->sample_size) {
124         av_free(a);
125         return NULL;
126     }
127     a->is_planar = av_sample_fmt_is_planar(sample_fmt);
128     a->planes    = a->is_planar ? channels : 1;
129     a->stride    = a->sample_size * (a->is_planar ? 1 : channels);
130
131     a->class              = &audio_data_class;
132     a->sample_fmt         = sample_fmt;
133     a->channels           = channels;
134     a->allocated_channels = channels;
135     a->read_only          = 0;
136     a->allow_realloc      = 1;
137     a->name               = name ? name : "{no name}";
138
139     if (nb_samples > 0) {
140         ret = ff_audio_data_realloc(a, nb_samples);
141         if (ret < 0) {
142             av_free(a);
143             return NULL;
144         }
145         return a;
146     } else {
147         calc_ptr_alignment(a);
148         return a;
149     }
150 }
151
152 int ff_audio_data_realloc(AudioData *a, int nb_samples)
153 {
154     int ret, new_buf_size, plane_size, p;
155
156     /* check if buffer is already large enough */
157     if (a->allocated_samples >= nb_samples)
158         return 0;
159
160     /* validate that the output is not read-only and realloc is allowed */
161     if (a->read_only || !a->allow_realloc)
162         return AVERROR(EINVAL);
163
164     new_buf_size = av_samples_get_buffer_size(&plane_size,
165                                               a->allocated_channels, nb_samples,
166                                               a->sample_fmt, 0);
167     if (new_buf_size < 0)
168         return new_buf_size;
169
170     /* if there is already data in the buffer and the sample format is planar,
171        allocate a new buffer and copy the data, otherwise just realloc the
172        internal buffer and set new data pointers */
173     if (a->nb_samples > 0 && a->is_planar) {
174         uint8_t *new_data[AVRESAMPLE_MAX_CHANNELS] = { NULL };
175
176         ret = av_samples_alloc(new_data, &plane_size, a->allocated_channels,
177                                nb_samples, a->sample_fmt, 0);
178         if (ret < 0)
179             return ret;
180
181         for (p = 0; p < a->planes; p++)
182             memcpy(new_data[p], a->data[p], a->nb_samples * a->stride);
183
184         av_freep(&a->buffer);
185         memcpy(a->data, new_data, sizeof(new_data));
186         a->buffer = a->data[0];
187     } else {
188         av_freep(&a->buffer);
189         a->buffer = av_malloc(new_buf_size);
190         if (!a->buffer)
191             return AVERROR(ENOMEM);
192         ret = av_samples_fill_arrays(a->data, &plane_size, a->buffer,
193                                      a->allocated_channels, nb_samples,
194                                      a->sample_fmt, 0);
195         if (ret < 0)
196             return ret;
197     }
198     a->buffer_size       = new_buf_size;
199     a->allocated_samples = nb_samples;
200
201     calc_ptr_alignment(a);
202     a->samples_align = plane_size / a->stride;
203
204     return 0;
205 }
206
207 void ff_audio_data_free(AudioData **a)
208 {
209     if (!*a)
210         return;
211     av_free((*a)->buffer);
212     av_freep(a);
213 }
214
215 int ff_audio_data_copy(AudioData *dst, AudioData *src)
216 {
217     int ret, p;
218
219     /* validate input/output compatibility */
220     if (dst->sample_fmt != src->sample_fmt || dst->channels < src->channels)
221         return AVERROR(EINVAL);
222
223     /* if the input is empty, just empty the output */
224     if (!src->nb_samples) {
225         dst->nb_samples = 0;
226         return 0;
227     }
228
229     /* reallocate output if necessary */
230     ret = ff_audio_data_realloc(dst, src->nb_samples);
231     if (ret < 0)
232         return ret;
233
234     /* copy data */
235     for (p = 0; p < src->planes; p++)
236         memcpy(dst->data[p], src->data[p], src->nb_samples * src->stride);
237     dst->nb_samples = src->nb_samples;
238
239     return 0;
240 }
241
242 int ff_audio_data_combine(AudioData *dst, int dst_offset, AudioData *src,
243                           int src_offset, int nb_samples)
244 {
245     int ret, p, dst_offset2, dst_move_size;
246
247     /* validate input/output compatibility */
248     if (dst->sample_fmt != src->sample_fmt || dst->channels != src->channels) {
249         av_log(src, AV_LOG_ERROR, "sample format mismatch\n");
250         return AVERROR(EINVAL);
251     }
252
253     /* validate offsets are within the buffer bounds */
254     if (dst_offset < 0 || dst_offset > dst->nb_samples ||
255         src_offset < 0 || src_offset > src->nb_samples) {
256         av_log(src, AV_LOG_ERROR, "offset out-of-bounds: src=%d dst=%d\n",
257                src_offset, dst_offset);
258         return AVERROR(EINVAL);
259     }
260
261     /* check offsets and sizes to see if we can just do nothing and return */
262     if (nb_samples > src->nb_samples - src_offset)
263         nb_samples = src->nb_samples - src_offset;
264     if (nb_samples <= 0)
265         return 0;
266
267     /* validate that the output is not read-only */
268     if (dst->read_only) {
269         av_log(dst, AV_LOG_ERROR, "dst is read-only\n");
270         return AVERROR(EINVAL);
271     }
272
273     /* reallocate output if necessary */
274     ret = ff_audio_data_realloc(dst, dst->nb_samples + nb_samples);
275     if (ret < 0) {
276         av_log(dst, AV_LOG_ERROR, "error reallocating dst\n");
277         return ret;
278     }
279
280     dst_offset2   = dst_offset + nb_samples;
281     dst_move_size = dst->nb_samples - dst_offset;
282
283     for (p = 0; p < src->planes; p++) {
284         if (dst_move_size > 0) {
285             memmove(dst->data[p] + dst_offset2 * dst->stride,
286                     dst->data[p] + dst_offset  * dst->stride,
287                     dst_move_size * dst->stride);
288         }
289         memcpy(dst->data[p] + dst_offset * dst->stride,
290                src->data[p] + src_offset * src->stride,
291                nb_samples * src->stride);
292     }
293     dst->nb_samples += nb_samples;
294
295     return 0;
296 }
297
298 void ff_audio_data_drain(AudioData *a, int nb_samples)
299 {
300     if (a->nb_samples <= nb_samples) {
301         /* drain the whole buffer */
302         a->nb_samples = 0;
303     } else {
304         int p;
305         int move_offset = a->stride * nb_samples;
306         int move_size   = a->stride * (a->nb_samples - nb_samples);
307
308         for (p = 0; p < a->planes; p++)
309             memmove(a->data[p], a->data[p] + move_offset, move_size);
310
311         a->nb_samples -= nb_samples;
312     }
313 }
314
315 int ff_audio_data_add_to_fifo(AVAudioFifo *af, AudioData *a, int offset,
316                               int nb_samples)
317 {
318     uint8_t *offset_data[AVRESAMPLE_MAX_CHANNELS];
319     int offset_size, p;
320
321     if (offset >= a->nb_samples)
322         return 0;
323     offset_size = offset * a->stride;
324     for (p = 0; p < a->planes; p++)
325         offset_data[p] = a->data[p] + offset_size;
326
327     return av_audio_fifo_write(af, (void **)offset_data, nb_samples);
328 }
329
330 int ff_audio_data_read_from_fifo(AVAudioFifo *af, AudioData *a, int nb_samples)
331 {
332     int ret;
333
334     if (a->read_only)
335         return AVERROR(EINVAL);
336
337     ret = ff_audio_data_realloc(a, nb_samples);
338     if (ret < 0)
339         return ret;
340
341     ret = av_audio_fifo_read(af, (void **)a->data, nb_samples);
342     if (ret >= 0)
343         a->nb_samples = ret;
344     return ret;
345 }