Asterisk - The Open Source Telephony Project GIT-master-f36a736
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"
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
42struct 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
50
51 /*! \brief Indicates whether an End of Stream condition has been detected. */
52 int eos;
53};
54
55static 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 */
130static 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;
201error:
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 */
214static 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 */
228static 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 Truncate an OGG/Speex filestream.
247 * \param s The filestream to truncate.
248 * \return 0 on success, -1 on failure.
249 */
250
251static 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
257static int ogg_speex_write(struct ast_filestream *s, struct ast_frame *f)
258{
259 ast_log(LOG_WARNING, "Writing is not supported on OGG/Speex streams!\n");
260 return -1;
261}
262
263/*!
264 * \brief Seek to a specific position in an OGG/Speex filestream.
265 * \param s The filestream to truncate.
266 * \param sample_offset New position for the filestream, measured in 8KHz samples.
267 * \param whence Location to measure
268 * \return 0 on success, -1 on failure.
269 */
270static int ogg_speex_seek(struct ast_filestream *s, off_t sample_offset, int whence)
271{
272 ast_log(LOG_WARNING, "Seeking is not supported on OGG/Speex streams!\n");
273 return -1;
274}
275
276static off_t ogg_speex_tell(struct ast_filestream *s)
277{
278 ast_log(LOG_WARNING, "Telling is not supported on OGG/Speex streams!\n");
279 return -1;
280}
281
282static struct ast_format_def speex_f = {
283 .name = "ogg_speex",
284 .exts = "spx",
285 .open = ogg_speex_open,
286 .write = ogg_speex_write,
287 .seek = ogg_speex_seek,
288 .trunc = ogg_speex_trunc,
289 .tell = ogg_speex_tell,
290 .read = ogg_speex_read,
291 .close = ogg_speex_close,
292 .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET,
293 .desc_size = sizeof(struct speex_desc),
294};
295
296static struct ast_format_def speex16_f = {
297 .name = "ogg_speex16",
298 .exts = "spx16",
299 .open = ogg_speex_open,
300 .write = ogg_speex_write,
301 .seek = ogg_speex_seek,
302 .trunc = ogg_speex_trunc,
303 .tell = ogg_speex_tell,
304 .read = ogg_speex_read,
305 .close = ogg_speex_close,
306 .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET,
307 .desc_size = sizeof(struct speex_desc),
308};
309
310static struct ast_format_def speex32_f = {
311 .name = "ogg_speex32",
312 .exts = "spx32",
313 .open = ogg_speex_open,
314 .write = ogg_speex_write,
315 .seek = ogg_speex_seek,
316 .trunc = ogg_speex_trunc,
317 .tell = ogg_speex_tell,
318 .read = ogg_speex_read,
319 .close = ogg_speex_close,
320 .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET,
321 .desc_size = sizeof(struct speex_desc),
322};
323
324static int unload_module(void)
325{
326 int res = 0;
330 return res;
331}
332
333static int load_module(void)
334{
338
344 }
345
347}
348
350 .support_level = AST_MODULE_SUPPORT_EXTENDED,
351 .load = load_module,
352 .unload = unload_module,
353 .load_pri = AST_MODPRI_APP_DEPEND
if(!yyg->yy_init)
Definition: ast_expr2f.c:854
while(1)
Definition: ast_expr2f.c:880
Asterisk main include file. File version handling, generic pbx functions.
#define ast_log
Definition: astobj2.c:42
static PGresult * result
Definition: cel_pgsql.c:84
unsigned int ast_codec_samples_count(struct ast_frame *frame)
Get the number of samples contained within a frame.
Definition: codec.c:379
unsigned int ast_format_get_sample_rate(const struct ast_format *format)
Get the sample rate of a media format.
Definition: format.c:379
Media Format Cache API.
struct ast_format * ast_format_speex16
Built-in cached speex at 16kHz format.
Definition: format_cache.c:136
struct ast_format * ast_format_speex
Built-in cached speex format.
Definition: format_cache.c:131
struct ast_format * ast_format_speex32
Built-in cached speex at 32kHz format.
Definition: format_cache.c:141
static int ogg_speex_open(struct ast_filestream *fs)
Create a new OGG/Speex filestream and set it up for reading.
static struct ast_format_def speex32_f
static int read_packet(struct ast_filestream *fs)
#define BUF_SIZE
static struct ast_format_def speex_f
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.
static int ogg_speex_write(struct ast_filestream *s, struct ast_frame *f)
static off_t ogg_speex_tell(struct ast_filestream *s)
static struct ast_format_def speex16_f
static int load_module(void)
static struct ast_frame * ogg_speex_read(struct ast_filestream *fs, int *whennext)
Read a frame full of audio data from the filestream.
static int unload_module(void)
#define BLOCK_SIZE
static int ogg_speex_trunc(struct ast_filestream *s)
Truncate an OGG/Speex filestream.
static void ogg_speex_close(struct ast_filestream *fs)
Close a OGG/Speex filestream.
#define AST_FRAME_SET_BUFFER(fr, _base, _ofs, _datalen)
#define AST_FRIENDLY_OFFSET
Offset into a frame's data buffer.
#define LOG_ERROR
#define LOG_WARNING
Header for providers of file and format handling routines. Clients of these routines should include "...
int ast_format_def_unregister(const char *name)
Unregisters a file format.
Definition: file.c:162
#define ast_format_def_register(f)
Definition: mod_format.h:136
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:331
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:557
@ AST_MODPRI_APP_DEPEND
Definition: module.h:342
@ AST_MODULE_SUPPORT_EXTENDED
Definition: module.h:122
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
#define NULL
Definition: resample.c:96
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
struct ast_frame fr
frame produced by read, typically
Definition: mod_format.h:122
void * _private
Definition: mod_format.h:124
struct ast_format_def * fmt
Definition: mod_format.h:103
Each supported file format is described by the following structure.
Definition: mod_format.h:43
char name[80]
Definition: mod_format.h:44
struct ast_format * format
Definition: mod_format.h:48
Data structure associated with a single frame of data.
union ast_frame::@226 data
ogg_packet op
int eos
Indicates whether an End of Stream condition has been detected.
ogg_sync_state oy
ogg_stream_state os
int error(const char *format,...)
Definition: utils/frame.c:999