Asterisk - The Open Source Telephony Project  18.5.0
format_ogg_speex.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2011-2016, Timo Teräs
5  *
6  * See http://www.asterisk.org for more information about
7  * the Asterisk project. Please do not directly contact
8  * any of the maintainers of this project for assistance;
9  * the project provides a web site, mailing lists and IRC
10  * channels for your use.
11  *
12  * This program is free software, distributed under the terms of
13  * the GNU General Public License Version 2. See the LICENSE file
14  * at the top of the source tree.
15  */
16 
17 /*! \file
18  *
19  * \brief OGG/Speex streams.
20  * \arg File name extension: spx
21  * \ingroup formats
22  */
23 
24 /*** MODULEINFO
25  <depend>speex</depend>
26  <depend>ogg</depend>
27  <support_level>extended</support_level>
28  ***/
29 
30 #include "asterisk.h"
31 
32 #include "asterisk/mod_format.h"
33 #include "asterisk/module.h"
34 #include "asterisk/format_cache.h"
35 
36 #include <speex/speex_header.h>
37 #include <ogg/ogg.h>
38 
39 #define BLOCK_SIZE 4096 /* buffer size for feeding OGG routines */
40 #define BUF_SIZE 200
41 
42 struct speex_desc { /* format specific parameters */
43  /* structures for handling the Ogg container */
44  ogg_sync_state oy;
45  ogg_stream_state os;
46  ogg_page og;
47  ogg_packet op;
48 
49  int serialno;
50 
51  /*! \brief Indicates whether an End of Stream condition has been detected. */
52  int eos;
53 };
54 
55 static int read_packet(struct ast_filestream *fs)
56 {
57  struct speex_desc *s = (struct speex_desc *)fs->_private;
58  char *buffer;
59  int result;
60  size_t bytes;
61 
62  while (1) {
63  /* Get one packet */
64  result = ogg_stream_packetout(&s->os, &s->op);
65  if (result > 0) {
66  if (s->op.bytes >= 5 && !memcmp(s->op.packet, "Speex", 5)) {
67  s->serialno = s->os.serialno;
68  }
69  if (s->serialno == -1 || s->os.serialno != s->serialno) {
70  continue;
71  }
72  return 0;
73  }
74 
75  if (result < 0) {
77  "Corrupt or missing data at this page position; continuing...\n");
78  }
79 
80  /* No more packets left in the current page... */
81  if (s->eos) {
82  /* No more pages left in the stream */
83  return -1;
84  }
85 
86  while (!s->eos) {
87  /* See if OGG has any pages in it's internal buffers */
88  result = ogg_sync_pageout(&s->oy, &s->og);
89  if (result > 0) {
90  /* Read all streams. */
91  if (ogg_page_serialno(&s->og) != s->os.serialno) {
92  ogg_stream_reset_serialno(&s->os, ogg_page_serialno(&s->og));
93  }
94  /* Yes, OGG has more pages in it's internal buffers,
95  add the page to the stream state */
96  result = ogg_stream_pagein(&s->os, &s->og);
97  if (result == 0) {
98  /* Yes, got a new, valid page */
99  if (ogg_page_eos(&s->og) &&
100  ogg_page_serialno(&s->og) == s->serialno)
101  s->eos = 1;
102  break;
103  }
105  "Invalid page in the bitstream; continuing...\n");
106  }
107 
108  if (result < 0) {
110  "Corrupt or missing data in bitstream; continuing...\n");
111  }
112 
113  /* No, we need to read more data from the file descrptor */
114  /* get a buffer from OGG to read the data into */
115  buffer = ogg_sync_buffer(&s->oy, BLOCK_SIZE);
116  bytes = fread(buffer, 1, BLOCK_SIZE, fs->f);
117  ogg_sync_wrote(&s->oy, bytes);
118  if (bytes == 0) {
119  s->eos = 1;
120  }
121  }
122  }
123 }
124 
125 /*!
126  * \brief Create a new OGG/Speex filestream and set it up for reading.
127  * \param fs File that points to on disk storage of the OGG/Speex data.
128  * \return The new filestream.
129  */
130 static int ogg_speex_open(struct ast_filestream *fs)
131 {
132  char *buffer;
133  size_t bytes;
134  struct speex_desc *s = (struct speex_desc *)fs->_private;
135  SpeexHeader *hdr = NULL;
136  int i, result, expected_rate;
137 
138  expected_rate = ast_format_get_sample_rate(fs->fmt->format);
139  s->serialno = -1;
140  ogg_sync_init(&s->oy);
141 
142  buffer = ogg_sync_buffer(&s->oy, BLOCK_SIZE);
143  bytes = fread(buffer, 1, BLOCK_SIZE, fs->f);
144  ogg_sync_wrote(&s->oy, bytes);
145 
146  result = ogg_sync_pageout(&s->oy, &s->og);
147  if (result != 1) {
148  if(bytes < BLOCK_SIZE) {
149  ast_log(LOG_ERROR, "Run out of data...\n");
150  } else {
151  ast_log(LOG_ERROR, "Input does not appear to be an Ogg bitstream.\n");
152  }
153  ogg_sync_clear(&s->oy);
154  return -1;
155  }
156 
157  ogg_stream_init(&s->os, ogg_page_serialno(&s->og));
158  if (ogg_stream_pagein(&s->os, &s->og) < 0) {
159  ast_log(LOG_ERROR, "Error reading first page of Ogg bitstream data.\n");
160  goto error;
161  }
162 
163  if (read_packet(fs) < 0) {
164  ast_log(LOG_ERROR, "Error reading initial header packet.\n");
165  goto error;
166  }
167 
168  hdr = speex_packet_to_header((char*)s->op.packet, s->op.bytes);
169  if (memcmp(hdr->speex_string, "Speex ", 8)) {
170  ast_log(LOG_ERROR, "OGG container does not contain Speex audio!\n");
171  goto error;
172  }
173  if (hdr->frames_per_packet != 1) {
174  ast_log(LOG_ERROR, "Only one frame-per-packet OGG/Speex files are currently supported!\n");
175  goto error;
176  }
177  if (hdr->nb_channels != 1) {
178  ast_log(LOG_ERROR, "Only monophonic OGG/Speex files are currently supported!\n");
179  goto error;
180  }
181  if (hdr->rate != expected_rate) {
182  ast_log(LOG_ERROR, "Unexpected sampling rate (%d != %d)!\n",
183  hdr->rate, expected_rate);
184  goto error;
185  }
186 
187  /* this packet is the comment */
188  if (read_packet(fs) < 0) {
189  ast_log(LOG_ERROR, "Error reading comment packet.\n");
190  goto error;
191  }
192  for (i = 0; i < hdr->extra_headers; i++) {
193  if (read_packet(fs) < 0) {
194  ast_log(LOG_ERROR, "Error reading extra header packet %d.\n", i+1);
195  goto error;
196  }
197  }
198  speex_header_free(hdr);
199 
200  return 0;
201 error:
202  if (hdr) {
203  speex_header_free(hdr);
204  }
205  ogg_stream_clear(&s->os);
206  ogg_sync_clear(&s->oy);
207  return -1;
208 }
209 
210 /*!
211  * \brief Close a OGG/Speex filestream.
212  * \param fs A OGG/Speex filestream.
213  */
214 static void ogg_speex_close(struct ast_filestream *fs)
215 {
216  struct speex_desc *s = (struct speex_desc *)fs->_private;
217 
218  ogg_stream_clear(&s->os);
219  ogg_sync_clear(&s->oy);
220 }
221 
222 /*!
223  * \brief Read a frame full of audio data from the filestream.
224  * \param fs The filestream.
225  * \param whennext Number of sample times to schedule the next call.
226  * \return A pointer to a frame containing audio data or NULL ifthere is no more audio data.
227  */
228 static struct ast_frame *ogg_speex_read(struct ast_filestream *fs,
229  int *whennext)
230 {
231  struct speex_desc *s = (struct speex_desc *)fs->_private;
232 
233  if (read_packet(fs) < 0) {
234  return NULL;
235  }
236 
238  memcpy(fs->fr.data.ptr, s->op.packet, s->op.bytes);
239  fs->fr.datalen = s->op.bytes;
240  fs->fr.samples = *whennext = ast_codec_samples_count(&fs->fr);
241 
242  return &fs->fr;
243 }
244 
245 /*!
246  * \brief Trucate an OGG/Speex filestream.
247  * \param s The filestream to truncate.
248  * \return 0 on success, -1 on failure.
249  */
250 
251 static int ogg_speex_trunc(struct ast_filestream *s)
252 {
253  ast_log(LOG_WARNING, "Truncation is not supported on OGG/Speex streams!\n");
254  return -1;
255 }
256 
257 /*!
258  * \brief Seek to a specific position in an OGG/Speex filestream.
259  * \param s The filestream to truncate.
260  * \param sample_offset New position for the filestream, measured in 8KHz samples.
261  * \param whence Location to measure
262  * \return 0 on success, -1 on failure.
263  */
264 static int ogg_speex_seek(struct ast_filestream *s, off_t sample_offset, int whence)
265 {
266  ast_log(LOG_WARNING, "Seeking is not supported on OGG/Speex streams!\n");
267  return -1;
268 }
269 
270 static off_t ogg_speex_tell(struct ast_filestream *s)
271 {
272  ast_log(LOG_WARNING, "Telling is not supported on OGG/Speex streams!\n");
273  return -1;
274 }
275 
276 static struct ast_format_def speex_f = {
277  .name = "ogg_speex",
278  .exts = "spx",
279  .open = ogg_speex_open,
280  .seek = ogg_speex_seek,
281  .trunc = ogg_speex_trunc,
282  .tell = ogg_speex_tell,
283  .read = ogg_speex_read,
284  .close = ogg_speex_close,
285  .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET,
286  .desc_size = sizeof(struct speex_desc),
287 };
288 
289 static struct ast_format_def speex16_f = {
290  .name = "ogg_speex16",
291  .exts = "spx16",
292  .open = ogg_speex_open,
293  .seek = ogg_speex_seek,
294  .trunc = ogg_speex_trunc,
295  .tell = ogg_speex_tell,
296  .read = ogg_speex_read,
297  .close = ogg_speex_close,
298  .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET,
299  .desc_size = sizeof(struct speex_desc),
300 };
301 
302 static struct ast_format_def speex32_f = {
303  .name = "ogg_speex32",
304  .exts = "spx32",
305  .open = ogg_speex_open,
306  .seek = ogg_speex_seek,
307  .trunc = ogg_speex_trunc,
308  .tell = ogg_speex_tell,
309  .read = ogg_speex_read,
310  .close = ogg_speex_close,
311  .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET,
312  .desc_size = sizeof(struct speex_desc),
313 };
314 
315 static int unload_module(void)
316 {
317  int res = 0;
318  res |= ast_format_def_unregister(speex_f.name);
319  res |= ast_format_def_unregister(speex16_f.name);
320  res |= ast_format_def_unregister(speex32_f.name);
321  return res;
322 }
323 
324 static int load_module(void)
325 {
326  speex_f.format = ast_format_speex;
327  speex16_f.format = ast_format_speex16;
328  speex32_f.format = ast_format_speex32;
329 
330  if (ast_format_def_register(&speex_f) ||
331  ast_format_def_register(&speex16_f) ||
332  ast_format_def_register(&speex32_f)) {
333  unload_module();
335  }
336 
338 }
339 
341  .support_level = AST_MODULE_SUPPORT_EXTENDED,
342  .load = load_module,
343  .unload = unload_module,
344  .load_pri = AST_MODPRI_APP_DEPEND
345 );
Asterisk main include file. File version handling, generic pbx functions.
ogg_sync_state oy
ogg_packet op
static int ogg_speex_trunc(struct ast_filestream *s)
Trucate an OGG/Speex filestream.
#define LOG_WARNING
Definition: logger.h:274
int eos
Indicates whether an End of Stream condition has been detected.
if(!yyg->yy_init)
Definition: ast_expr2f.c:868
static int ogg_speex_seek(struct ast_filestream *s, off_t sample_offset, int whence)
Seek to a specific position in an OGG/Speex filestream.
#define NULL
Definition: resample.c:96
Each supported file format is described by the following structure.
Definition: mod_format.h:43
#define BUF_SIZE
Header for providers of file and format handling routines. Clients of these routines should include "...
static struct ast_format_def speex32_f
int ast_format_def_unregister(const char *name)
Unregisters a file format.
Definition: file.c:162
#define ast_log
Definition: astobj2.c:42
#define AST_FRIENDLY_OFFSET
Offset into a frame&#39;s data buffer.
static void ogg_speex_close(struct ast_filestream *fs)
Close a OGG/Speex filestream.
static int read_packet(struct ast_filestream *fs)
while(1)
Definition: ast_expr2f.c:894
#define ast_format_def_register(f)
Definition: mod_format.h:136
struct ast_format_def * fmt
Definition: mod_format.h:103
struct ast_frame fr
frame produced by read, typically
Definition: mod_format.h:122
unsigned int ast_codec_samples_count(struct ast_frame *frame)
Get the number of samples contained within a frame.
Definition: codec.c:378
struct ast_format * ast_format_speex16
Built-in cached speex at 16kHz format.
Definition: format_cache.c:141
static int load_module(void)
#define BLOCK_SIZE
struct ast_format * format
Definition: mod_format.h:48
void * _private
Definition: mod_format.h:124
#define LOG_ERROR
Definition: logger.h:285
#define AST_FRAME_SET_BUFFER(fr, _base, _ofs, _datalen)
static struct ast_format_def speex16_f
struct ast_format * ast_format_speex
Built-in cached speex format.
Definition: format_cache.c:136
char name[80]
Definition: mod_format.h:44
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
static struct ast_frame * ogg_speex_read(struct ast_filestream *fs, int *whennext)
Read a frame full of audio data from the filestream.
ogg_stream_state os
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS|AST_MODFLAG_LOAD_ORDER, "HTTP Phone Provisioning",.support_level=AST_MODULE_SUPPORT_EXTENDED,.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CHANNEL_DEPEND,.requires="http",)
This structure is allocated by file.c in one chunk, together with buf_size and desc_size bytes of mem...
Definition: mod_format.h:101
unsigned int ast_format_get_sample_rate(const struct ast_format *format)
Get the sample rate of a media format.
Definition: format.c:379
static PGresult * result
Definition: cel_pgsql.c:88
Data structure associated with a single frame of data.
static struct ast_format_def speex_f
int error(const char *format,...)
Definition: utils/frame.c:999
union ast_frame::@263 data
static int unload_module(void)
static int ogg_speex_open(struct ast_filestream *fs)
Create a new OGG/Speex filestream and set it up for reading.
struct ast_format * ast_format_speex32
Built-in cached speex at 32kHz format.
Definition: format_cache.c:146
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
static off_t ogg_speex_tell(struct ast_filestream *s)
Media Format Cache API.