source: src/io/sink_flac.c @ 68b991e

feature/autosinkfeature/cnnfeature/crepefix/ffmpeg5
Last change on this file since 68b991e was 68b991e, checked in by Paul Brossier <piem@piem.org>, 5 years ago

[io] sink_flac: validate input sizes, avoid crash on null path and closing twice

  • Property mode set to 100644
File size: 9.8 KB
Line 
1/*
2  Copyright (C) 2018 Paul Brossier <piem@aubio.org>
3
4  This file is part of aubio.
5
6  aubio is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10
11  aubio 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
14  GNU General Public License for more details.
15
16  You should have received a copy of the GNU General Public License
17  along with aubio.  If not, see <http://www.gnu.org/licenses/>.
18
19*/
20
21/*
22  This file is largely inspired by `examples/c/encode/file/main.c` in the
23  flac source package (versions 1.3.2 and later) available online at
24  https://xiph.org/flac/
25*/
26
27#include "aubio_priv.h"
28
29#ifdef HAVE_FLAC
30
31#include "io/ioutils.h"
32#include "fmat.h"
33
34#include <FLAC/metadata.h>
35#include <FLAC/stream_encoder.h>
36
37#include <string.h> // strerror
38#include <errno.h> // errno
39
40#define MAX_WRITE_SIZE 4096
41
42// swap host to little endian
43#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
44#define HTOLES(x) SWAPS(x)
45#elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
46#define HTOLES(x) x
47#else
48#ifdef HAVE_WIN_HACKS
49#define HTOLES(x) x
50#else
51#define HTOLES(x) SWAPS(htons(x))
52#endif
53#endif
54
55// convert to short, taking care of endianness
56#define FLOAT_TO_SHORT(x) (HTOLES((FLAC__int32)(x * 32768)))
57
58struct _aubio_sink_flac_t {
59  uint_t samplerate;
60  uint_t channels;
61  char_t *path;
62
63  FILE *fid;            // file id
64  FLAC__StreamEncoder* encoder;
65  FLAC__int32 *buffer;
66  FLAC__StreamMetadata *metadata[2];
67};
68
69typedef struct _aubio_sink_flac_t aubio_sink_flac_t;
70
71uint_t aubio_sink_flac_preset_channels(aubio_sink_flac_t *s,
72    uint_t channels);
73uint_t aubio_sink_flac_preset_samplerate(aubio_sink_flac_t *s,
74    uint_t samplerate);
75uint_t aubio_sink_flac_open(aubio_sink_flac_t *s);
76uint_t aubio_sink_flac_close (aubio_sink_flac_t *s);
77void del_aubio_sink_flac (aubio_sink_flac_t *s);
78
79#if 0
80static void aubio_sink_flac_callback(const FLAC__StreamEncoder* encoder,
81    FLAC__uint64 bytes_written, FLAC__uint64 samples_written,
82    unsigned frames_writtten, unsigned total_frames_estimate,
83    void *client_data);
84#endif
85
86aubio_sink_flac_t * new_aubio_sink_flac (const char_t *uri,
87    uint_t samplerate)
88{
89  aubio_sink_flac_t * s = AUBIO_NEW(aubio_sink_flac_t);
90
91  if (!uri) {
92    AUBIO_ERROR("sink_flac: Aborted opening null path\n");
93    goto failure;
94  }
95
96  s->path = AUBIO_ARRAY(char_t, strnlen(uri, PATH_MAX) + 1);
97  strncpy(s->path, uri, strnlen(uri, PATH_MAX) + 1);
98  s->path[strnlen(uri, PATH_MAX)] = '\0';
99
100  s->channels = 0;
101  s->samplerate = 0;
102
103  if ((sint_t)samplerate == 0)
104    return s;
105
106  aubio_sink_flac_preset_samplerate(s, samplerate);
107  s->channels = 1;
108
109  if (aubio_sink_flac_open(s) != AUBIO_OK)
110    goto failure;
111
112  return s;
113
114failure:
115  del_aubio_sink_flac(s);
116  return NULL;
117}
118
119void del_aubio_sink_flac (aubio_sink_flac_t *s)
120{
121  if (s->fid)
122    aubio_sink_flac_close(s);
123  if (s->buffer)
124    AUBIO_FREE(s->buffer);
125  if (s->path)
126    AUBIO_FREE(s->path);
127  AUBIO_FREE(s);
128}
129
130uint_t aubio_sink_flac_open(aubio_sink_flac_t *s)
131{
132  uint_t ret = AUBIO_FAIL;
133  FLAC__bool ok = true;
134  FLAC__StreamEncoderInitStatus init_status;
135  const unsigned comp_level = 5;
136  const unsigned bps = 16;
137
138  if (s->samplerate == 0 || s->channels == 0) return AUBIO_FAIL;
139
140  s->buffer = AUBIO_ARRAY(FLAC__int32, s->channels * MAX_WRITE_SIZE);
141  if (!s->buffer) {
142    AUBIO_ERR("sink_flac: failed allocating buffer for %s\n", s->path);
143    return AUBIO_FAIL;
144  }
145
146  s->fid = fopen((const char *)s->path, "wb");
147  if (!s->fid) {
148    AUBIO_ERR("sink_flac: failed opening %s, %s\n", s->path, strerror(errno));
149    return AUBIO_FAIL;
150  }
151
152  if((s->encoder = FLAC__stream_encoder_new()) == NULL) {
153    AUBIO_ERR("sink_flac: failed allocating encoder for %s\n", s->path);
154    goto failure;
155  }
156  ok &= FLAC__stream_encoder_set_verify(s->encoder, true);
157  ok &= FLAC__stream_encoder_set_compression_level(s->encoder, comp_level);
158  ok &= FLAC__stream_encoder_set_channels(s->encoder, s->channels);
159  ok &= FLAC__stream_encoder_set_bits_per_sample(s->encoder, bps);
160  ok &= FLAC__stream_encoder_set_sample_rate(s->encoder, s->samplerate);
161  // the total number of samples can not be estimated (streaming)
162  // it will be set by the encoder in FLAC__stream_encoder_finish
163  //ok &= FLAC__stream_encoder_set_total_samples_estimate(s->encoder, 0);
164
165  if (!ok) {
166    AUBIO_ERR("sink_flac: failed setting metadata for %s\n", s->path);
167    goto failure;
168  }
169
170  s->metadata[0] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
171  if (!s->metadata[0]) {
172    AUBIO_ERR("sink_flac: failed allocating vorbis comment %s\n", s->path);
173    goto failure;
174  }
175
176  s->metadata[1] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING);
177  if (!s->metadata[1]) {
178    AUBIO_ERR("sink_flac: failed allocating vorbis comment %s\n", s->path);
179    goto failure;
180  }
181
182  FLAC__StreamMetadata_VorbisComment_Entry entry;
183  ok = FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry,
184      "encoder", "aubio");
185  ok &= FLAC__metadata_object_vorbiscomment_append_comment(s->metadata[0],
186      entry, false);
187  if (!ok) {
188    AUBIO_ERR("sink_flac: failed setting metadata for %s\n", s->path);
189    goto failure;
190  }
191
192  // padding length
193  s->metadata[1]->length = 1234;
194  if (!FLAC__stream_encoder_set_metadata(s->encoder, s->metadata, 2)) {
195    AUBIO_ERR("sink_flac: failed setting metadata for %s\n", s->path);
196    goto failure;
197  }
198
199  // initialize encoder
200  init_status = FLAC__stream_encoder_init_file(s->encoder, s->path,
201      NULL, NULL);
202      //aubio_sink_flac_callback, s);
203  if (init_status == FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_SAMPLE_RATE) {
204    AUBIO_ERR("sink_flac: failed initilizing encoder for %s"
205       " (invalid samplerate %d)\n", s->path, s->samplerate);
206    goto failure;
207  } else if (init_status ==
208      FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_NUMBER_OF_CHANNELS) {
209    AUBIO_ERR("sink_flac: failed initilizing encoder for %s"
210       " (invalid number of channel %d)\n", s->path, s->channels);
211    goto failure;
212  } else if (init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) {
213    AUBIO_ERR("sink_flac: failed initilizing encoder for %s (%d)\n",
214        s->path, (int)init_status);
215    goto failure;
216  }
217
218  // mark success
219  ret = AUBIO_OK;
220
221failure:
222
223  return ret;
224}
225
226uint_t aubio_sink_flac_preset_samplerate(aubio_sink_flac_t *s,
227    uint_t samplerate)
228{
229  if (aubio_io_validate_samplerate("sink_flac", s->path, samplerate))
230    return AUBIO_FAIL;
231  s->samplerate = samplerate;
232  if (s->samplerate != 0 && s->channels != 0)
233    return aubio_sink_flac_open(s);
234  return AUBIO_OK;
235}
236
237uint_t aubio_sink_flac_preset_channels(aubio_sink_flac_t *s,
238    uint_t channels)
239{
240  if (aubio_io_validate_channels("sink_flac", s->path, channels)) {
241    return AUBIO_FAIL;
242  }
243  s->channels = channels;
244  // automatically open when both samplerate and channels have been set
245  if (s->samplerate != 0 && s->channels != 0) {
246    return aubio_sink_flac_open(s);
247  }
248  return AUBIO_OK;
249}
250
251uint_t aubio_sink_flac_get_samplerate(const aubio_sink_flac_t *s)
252{
253  return s->samplerate;
254}
255
256uint_t aubio_sink_flac_get_channels(const aubio_sink_flac_t *s)
257{
258  return s->channels;
259}
260
261void aubio_sink_flac_do(aubio_sink_flac_t *s, fvec_t *write_data,
262    uint_t write)
263{
264  uint_t c, v;
265  uint_t length = aubio_sink_validate_input_length("sink_flac", s->path,
266      MAX_WRITE_SIZE, write_data->length, write);
267  // fill buffer
268  if (!write) {
269    return;
270  } else {
271    for (c = 0; c < s->channels; c++) {
272      for (v = 0; v < length; v++) {
273        s->buffer[v * s->channels + c] = FLOAT_TO_SHORT(write_data->data[v]);
274      }
275    }
276  }
277  // send to encoder
278  FLAC__stream_encoder_process_interleaved(s->encoder,
279      (const FLAC__int32*)s->buffer, length);
280}
281
282void aubio_sink_flac_do_multi(aubio_sink_flac_t *s, fmat_t *write_data,
283    uint_t write)
284{
285  uint_t c, v;
286  uint_t channels = aubio_sink_validate_input_channels("sink_flac", s->path,
287      s->channels, write_data->height);
288  uint_t length = aubio_sink_validate_input_length("sink_flac", s->path,
289      MAX_WRITE_SIZE, write_data->length, write);
290  // fill buffer
291  if (!write) {
292    return;
293  } else {
294    for (c = 0; c < channels; c++) {
295      for (v = 0; v < length; v++) {
296        s->buffer[v * s->channels + c] = FLOAT_TO_SHORT(write_data->data[c][v]);
297      }
298    }
299    // send to encoder
300    FLAC__stream_encoder_process_interleaved(s->encoder,
301        (const FLAC__int32*)s->buffer, length);
302  }
303}
304
305uint_t aubio_sink_flac_close (aubio_sink_flac_t *s)
306{
307  uint_t ret = AUBIO_OK;
308
309  if (!s->fid) return AUBIO_FAIL;
310
311  if (s->encoder) {
312    // mark the end of stream
313    if (!FLAC__stream_encoder_finish(s->encoder)) {
314      FLAC__StreamEncoderState state =
315        FLAC__stream_encoder_get_state(s->encoder);
316      AUBIO_ERR("sink_flac: Error closing encoder for %s (%s)\n",
317          s->path, FLAC__StreamEncoderStateString[state]);
318      ret &= AUBIO_FAIL;
319    }
320
321    FLAC__stream_encoder_delete(s->encoder);
322  }
323
324  if (s->metadata) {
325    // clean up metadata after stream finished
326    FLAC__metadata_object_delete(s->metadata[0]);
327    FLAC__metadata_object_delete(s->metadata[1]);
328  }
329
330  if (s->fid && fclose(s->fid)) {
331    AUBIO_ERR("sink_flac: Error closing file %s (%s)\n",
332        s->path, strerror(errno));
333    ret &= AUBIO_FAIL;
334  }
335  s->fid = NULL;
336
337  return ret;
338}
339
340#if 0
341static void aubio_sink_flac_callback(const FLAC__StreamEncoder* encoder UNUSED,
342    FLAC__uint64 bytes_written, FLAC__uint64 samples_written,
343    unsigned frames_written, unsigned total_frames_estimate,
344    void *client_data UNUSED)
345{
346  AUBIO_WRN("sink_flac: %d bytes_written, %d samples_written,"
347      " %d/%d frames writen\n",
348      bytes_written, samples_written, frames_written, total_frames_estimate);
349}
350#endif
351
352#endif /* HAVE_FLAC */
Note: See TracBrowser for help on using the repository browser.