source: src/io/sink_vorbis.c @ 4c37e87

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

[io] sink_vorbis: prevent crash on null path

  • Property mode set to 100644
File size: 8.0 KB
RevLine 
[dc46037b]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
[7e93013]21/*
22  This file is largely inspired by `examples/encoder_example.c` in the
23  libvorbis source package (versions 1.3.5 and later) available online at
24  https://xiph.org/vorbis/
25*/
26
[dc46037b]27#include "aubio_priv.h"
28
29#ifdef HAVE_VORBISENC
30
31#include "io/ioutils.h"
32#include "fmat.h"
33
34#include <vorbis/vorbisenc.h>
35#include <string.h> // strerror
36#include <errno.h> // errno
37#include <time.h> // time
38
39struct _aubio_sink_vorbis_t {
40  FILE *fid;            // file id
41  ogg_stream_state os;  // stream
42  ogg_page og;          // page
43  ogg_packet op;        // data packet
44  vorbis_info vi;       // vorbis bitstream settings
45  vorbis_comment vc;    // user comment
46  vorbis_dsp_state vd;  // working state
47  vorbis_block vb;      // working space
48
49  uint_t samplerate;
50  uint_t channels;
51  char_t *path;
52};
53
54typedef struct _aubio_sink_vorbis_t aubio_sink_vorbis_t;
55
56uint_t aubio_sink_vorbis_preset_channels(aubio_sink_vorbis_t *s,
57    uint_t channels);
58uint_t aubio_sink_vorbis_preset_samplerate(aubio_sink_vorbis_t *s,
59    uint_t samplerate);
60uint_t aubio_sink_vorbis_open(aubio_sink_vorbis_t *s);
61uint_t aubio_sink_vorbis_close (aubio_sink_vorbis_t *s);
62void del_aubio_sink_vorbis (aubio_sink_vorbis_t *s);
63
64aubio_sink_vorbis_t * new_aubio_sink_vorbis (const char_t *uri,
65    uint_t samplerate)
66{
67  aubio_sink_vorbis_t * s = AUBIO_NEW(aubio_sink_vorbis_t);
68
[4c37e87]69  if (!uri) {
70    AUBIO_ERROR("sink_apple_audio: Aborted opening null path\n");
71    goto failure;
72  }
73
[dc46037b]74  s->path = AUBIO_ARRAY(char_t, strnlen(uri, PATH_MAX) + 1);
75  strncpy(s->path, uri, strnlen(uri, PATH_MAX) + 1);
[be63940]76  s->path[strnlen(uri, PATH_MAX)] = '\0';
[dc46037b]77
78  s->channels = 0;
79
80  if ((sint_t)samplerate == 0)
81    return s;
82
83  aubio_sink_vorbis_preset_samplerate(s, samplerate);
84  s->channels = 1;
85
86  if (aubio_sink_vorbis_open(s) != AUBIO_OK)
87    goto failure;
88
89  return s;
90
91failure:
92  del_aubio_sink_vorbis(s);
93  return NULL;
94}
95
96void del_aubio_sink_vorbis (aubio_sink_vorbis_t *s)
97{
98  if (s->fid) aubio_sink_vorbis_close(s);
99  // clean up
100  ogg_stream_clear(&s->os);
101  vorbis_block_clear(&s->vb);
102  vorbis_dsp_clear(&s->vd);
103  vorbis_comment_clear(&s->vc);
104  vorbis_info_clear(&s->vi);
105
[be63940]106  if (s->path) AUBIO_FREE(s->path);
[dc46037b]107  AUBIO_FREE(s);
108}
109
110uint_t aubio_sink_vorbis_open(aubio_sink_vorbis_t *s)
111{
112  float quality_mode = .9;
113
[6031419]114  if (s->samplerate == 0 || s->channels == 0) return AUBIO_FAIL;
[dc46037b]115
116  s->fid = fopen((const char *)s->path, "wb");
[0850e54]117  if (!s->fid) {
118    AUBIO_ERR("sink_vorbis: Error opening file %s (%s)\n",
119        s->path, strerror(errno));
120    return AUBIO_FAIL;
121  }
[dc46037b]122
123  vorbis_info_init(&s->vi);
124  if (vorbis_encode_init_vbr(&s->vi, s->channels, s->samplerate, quality_mode))
125  {
126    AUBIO_ERR("sink_vorbis: vorbis_encode_init_vbr failed\n");
127    return AUBIO_FAIL;
128  }
129
130  // add comment
131  vorbis_comment_init(&s->vc);
132  vorbis_comment_add_tag(&s->vc, "ENCODER", "aubio");
133
134  // initalise analysis and block
135  vorbis_analysis_init(&s->vd, &s->vi);
136  vorbis_block_init(&s->vd, &s->vb);
137
138  // pick randome serial number
139  srand(time(NULL));
140  ogg_stream_init(&s->os, rand());
141
142  // write header
143  {
144    int ret = 0;
[0850e54]145    size_t wrote;
[dc46037b]146    ogg_packet header;
147    ogg_packet header_comm;
148    ogg_packet header_code;
149
150    vorbis_analysis_headerout(&s->vd, &s->vc, &header, &header_comm,
151        &header_code);
152
153    ogg_stream_packetin(&s->os, &header);
154    ogg_stream_packetin(&s->os, &header_comm);
155    ogg_stream_packetin(&s->os, &header_code);
156
157    // make sure audio data will start on a new page
158    while (1)
159    {
160      ret = ogg_stream_flush(&s->os, &s->og);
161      if (ret==0) break;
[0850e54]162      wrote = fwrite(s->og.header, 1, s->og.header_len, s->fid);
163      ret = (wrote == (unsigned)s->og.header_len);
164      wrote = fwrite(s->og.body,   1, s->og.body_len,   s->fid);
165      ret &= (wrote == (unsigned)s->og.body_len);
166      if (ret == 0) {
167        AUBIO_ERR("sink_vorbis: failed writing \'%s\' to disk (%s)\n",
168            s->path, strerror(errno));
169        return AUBIO_FAIL;
170      }
[dc46037b]171    }
172  }
173
174  return AUBIO_OK;
175}
176
177uint_t aubio_sink_vorbis_preset_samplerate(aubio_sink_vorbis_t *s,
178    uint_t samplerate)
179{
180  if (aubio_io_validate_samplerate("sink_vorbis", s->path, samplerate))
181    return AUBIO_FAIL;
182  s->samplerate = samplerate;
183  if (s->samplerate != 0 && s->channels != 0)
184    return aubio_sink_vorbis_open(s);
185  return AUBIO_OK;
186}
187
188uint_t aubio_sink_vorbis_preset_channels(aubio_sink_vorbis_t *s,
189    uint_t channels)
190{
191  if (aubio_io_validate_channels("sink_vorbis", s->path, channels)) {
192    return AUBIO_FAIL;
193  }
194  s->channels = channels;
195  // automatically open when both samplerate and channels have been set
196  if (s->samplerate != 0 && s->channels != 0) {
197    return aubio_sink_vorbis_open(s);
198  }
199  return AUBIO_OK;
200}
201
202uint_t aubio_sink_vorbis_get_samplerate(const aubio_sink_vorbis_t *s)
203{
204  return s->samplerate;
205}
206
207uint_t aubio_sink_vorbis_get_channels(const aubio_sink_vorbis_t *s)
208{
209  return s->channels;
210}
211
212void aubio_sink_vorbis_write(aubio_sink_vorbis_t *s)
213{
[0850e54]214  int result;
215  size_t wrote;
[dc46037b]216  // pre-analysis
217  while (vorbis_analysis_blockout(&s->vd, &s->vb) == 1) {
218
219    vorbis_analysis(&s->vb, NULL);
220    vorbis_bitrate_addblock(&s->vb);
221
222    while (vorbis_bitrate_flushpacket(&s->vd, &s->op))
223    {
224      ogg_stream_packetin(&s->os, &s->op);
225
226      while (1) {
[0850e54]227        result = ogg_stream_pageout (&s->os, &s->og);
[dc46037b]228        if (result == 0) break;
[0850e54]229        wrote = fwrite(s->og.header, 1, s->og.header_len, s->fid);
230        result = (wrote == (unsigned)s->og.header_len);
231        wrote = fwrite(s->og.body, 1, s->og.body_len,     s->fid);
232        result &= (wrote == (unsigned)s->og.body_len);
233        if (result == 0) {
234          AUBIO_WRN("sink_vorbis: failed writing \'%s\' to disk (%s)\n",
235              s->path, strerror(errno));
236        }
[dc46037b]237        if (ogg_page_eos(&s->og)) break;
238      }
239    }
240  }
241}
242
243void aubio_sink_vorbis_do(aubio_sink_vorbis_t *s, fvec_t *write_data,
244    uint_t write)
245{
246  uint_t c, v;
247  float **buffer = vorbis_analysis_buffer(&s->vd, (long)write);
248  // fill buffer
249  if (!write) {
250    return;
251  } else if (!buffer) {
252    AUBIO_WRN("sink_vorbis: failed fetching buffer of size %d\n", write);
[4b9481f9]253    return;
[dc46037b]254  } else {
255    for (c = 0; c < s->channels; c++) {
256      for (v = 0; v < write; v++) {
257        buffer[c][v] = write_data->data[v];
258      }
259    }
260    // tell vorbis how many frames were written
261    vorbis_analysis_wrote(&s->vd, (long)write);
262  }
263  // write to file
264  aubio_sink_vorbis_write(s);
265}
266
267void aubio_sink_vorbis_do_multi(aubio_sink_vorbis_t *s, fmat_t *write_data,
268    uint_t write)
269{
270  uint_t c, v;
[6031419]271  uint_t channels = MIN(s->channels, write_data->height);
[dc46037b]272  float **buffer = vorbis_analysis_buffer(&s->vd, (long)write);
273  // fill buffer
274  if (!write) {
275    return;
276  } else if (!buffer) {
277    AUBIO_WRN("sink_vorbis: failed fetching buffer of size %d\n", write);
[4b9481f9]278    return;
[dc46037b]279  } else {
[6031419]280    for (c = 0; c < channels; c++) {
[dc46037b]281      for (v = 0; v < write; v++) {
282        buffer[c][v] = write_data->data[c][v];
283      }
284    }
285    // tell vorbis how many frames were written
286    vorbis_analysis_wrote(&s->vd, (long)write);
287  }
288
289  aubio_sink_vorbis_write(s);
290}
291
292uint_t aubio_sink_vorbis_close (aubio_sink_vorbis_t *s)
293{
294  //mark the end of stream
295  vorbis_analysis_wrote(&s->vd, 0);
296
297  aubio_sink_vorbis_write(s);
298
[7107ed9]299  if (s->fid && fclose(s->fid)) {
[dc46037b]300    AUBIO_ERR("sink_vorbis: Error closing file %s (%s)\n",
301        s->path, strerror(errno));
302    return AUBIO_FAIL;
303  }
[7107ed9]304  s->fid = NULL;
[dc46037b]305  return AUBIO_OK;
306}
307
308#endif /* HAVE_VORBISENC */
Note: See TracBrowser for help on using the repository browser.