lavr: fix the decision for writing directly to the output buffer
[libav.git] / 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 #include <string.h>
23
24 #include "libavutil/mem.h"
25 #include "audio_data.h"
26
27 static 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 */
36 static 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
51 int 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
65 int 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
110 AudioData *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
153 int 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
208 void ff_audio_data_free(AudioData **a)
209 {
210 if (!*a)
211 return;
212 av_free((*a)->buffer);
213 av_freep(a);
214 }
215
216 int ff_audio_data_copy(AudioData *dst, AudioData *src)
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 the input is empty, just empty the output */
225 if (!src->nb_samples) {
226 dst->nb_samples = 0;
227 return 0;
228 }
229
230 /* reallocate output if necessary */
231 ret = ff_audio_data_realloc(dst, src->nb_samples);
232 if (ret < 0)
233 return ret;
234
235 /* copy data */
236 for (p = 0; p < src->planes; p++)
237 memcpy(dst->data[p], src->data[p], src->nb_samples * src->stride);
238 dst->nb_samples = src->nb_samples;
239
240 return 0;
241 }
242
243 int ff_audio_data_combine(AudioData *dst, int dst_offset, AudioData *src,
244 int src_offset, int nb_samples)
245 {
246 int ret, p, dst_offset2, dst_move_size;
247
248 /* validate input/output compatibility */
249 if (dst->sample_fmt != src->sample_fmt || dst->channels != src->channels) {
250 av_log(src, AV_LOG_ERROR, "sample format mismatch\n");
251 return AVERROR(EINVAL);
252 }
253
254 /* validate offsets are within the buffer bounds */
255 if (dst_offset < 0 || dst_offset > dst->nb_samples ||
256 src_offset < 0 || src_offset > src->nb_samples) {
257 av_log(src, AV_LOG_ERROR, "offset out-of-bounds: src=%d dst=%d\n",
258 src_offset, dst_offset);
259 return AVERROR(EINVAL);
260 }
261
262 /* check offsets and sizes to see if we can just do nothing and return */
263 if (nb_samples > src->nb_samples - src_offset)
264 nb_samples = src->nb_samples - src_offset;
265 if (nb_samples <= 0)
266 return 0;
267
268 /* validate that the output is not read-only */
269 if (dst->read_only) {
270 av_log(dst, AV_LOG_ERROR, "dst is read-only\n");
271 return AVERROR(EINVAL);
272 }
273
274 /* reallocate output if necessary */
275 ret = ff_audio_data_realloc(dst, dst->nb_samples + nb_samples);
276 if (ret < 0) {
277 av_log(dst, AV_LOG_ERROR, "error reallocating dst\n");
278 return ret;
279 }
280
281 dst_offset2 = dst_offset + nb_samples;
282 dst_move_size = dst->nb_samples - dst_offset;
283
284 for (p = 0; p < src->planes; p++) {
285 if (dst_move_size > 0) {
286 memmove(dst->data[p] + dst_offset2 * dst->stride,
287 dst->data[p] + dst_offset * dst->stride,
288 dst_move_size * dst->stride);
289 }
290 memcpy(dst->data[p] + dst_offset * dst->stride,
291 src->data[p] + src_offset * src->stride,
292 nb_samples * src->stride);
293 }
294 dst->nb_samples += nb_samples;
295
296 return 0;
297 }
298
299 void ff_audio_data_drain(AudioData *a, int nb_samples)
300 {
301 if (a->nb_samples <= nb_samples) {
302 /* drain the whole buffer */
303 a->nb_samples = 0;
304 } else {
305 int p;
306 int move_offset = a->stride * nb_samples;
307 int move_size = a->stride * (a->nb_samples - nb_samples);
308
309 for (p = 0; p < a->planes; p++)
310 memmove(a->data[p], a->data[p] + move_offset, move_size);
311
312 a->nb_samples -= nb_samples;
313 }
314 }
315
316 int ff_audio_data_add_to_fifo(AVAudioFifo *af, AudioData *a, int offset,
317 int nb_samples)
318 {
319 uint8_t *offset_data[AVRESAMPLE_MAX_CHANNELS];
320 int offset_size, p;
321
322 if (offset >= a->nb_samples)
323 return 0;
324 offset_size = offset * a->stride;
325 for (p = 0; p < a->planes; p++)
326 offset_data[p] = a->data[p] + offset_size;
327
328 return av_audio_fifo_write(af, (void **)offset_data, nb_samples);
329 }
330
331 int ff_audio_data_read_from_fifo(AVAudioFifo *af, AudioData *a, int nb_samples)
332 {
333 int ret;
334
335 if (a->read_only)
336 return AVERROR(EINVAL);
337
338 ret = ff_audio_data_realloc(a, nb_samples);
339 if (ret < 0)
340 return ret;
341
342 ret = av_audio_fifo_read(af, (void **)a->data, nb_samples);
343 if (ret >= 0)
344 a->nb_samples = ret;
345 return ret;
346 }