Asterisk - The Open Source Telephony Project  21.4.1
asterisk-opus-a959f072d3f364be983dd27e6e250b038aaef747/formats/format_ogg_opus_open_source.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2016, Digium, Inc.
5  *
6  * Mark Michelson <mmichelson@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 /*** MODULEINFO
20  <depend>opusfile</depend>
21  <use type="external">opusenc</use>
22  <conflict>format_ogg_opus</conflict>
23  <defaultenabled>yes</defaultenabled>
24  ***/
25 
26 #include "asterisk.h"
27 
28 #if defined(ASTERISK_REGISTER_FILE)
29 ASTERISK_REGISTER_FILE()
30 #elif defined(ASTERISK_FILE_VERSION)
31 ASTERISK_FILE_VERSION(__FILE__, "$Revision: $")
32 #endif
33 
34 #include <fcntl.h> /* for SEEK_CUR, SEEK_END, SEEK_SET */
35 
36 #include "asterisk/format_cache.h" /* for ast_format_slin48 */
37 #include "asterisk/frame.h" /* for ast_frame, AST_FRIENDLY_OFFSET */
38 #include "asterisk/logger.h" /* for ast_log, LOG_ERROR, AST_LOG_ERROR */
39 #include "asterisk/mod_format.h" /* for ast_filestream, ast_format_def */
40 #include "asterisk/module.h"
41 #if defined(HAVE_OPUSENC)
42 #include "asterisk/config.h" /* for ast_variable, ast_config_destroy */
43 #include "asterisk/format.h" /* for ast_format_get_... */
44 #include "asterisk/utils.h" /* for ast_flags */
45 #endif
46 
47 #include <opus/opus.h>
48 #include <opus/opusfile.h>
49 #if defined(HAVE_OPUSENC)
50 #include <opus/opusenc.h>
51 #endif
52 
53 #include "asterisk/opus.h"
54 
55 /* 120ms of 48KHz audio */
56 #define SAMPLES_MAX 5760
57 #define BUF_SIZE (2 * SAMPLES_MAX)
58 
59 #if defined(HAVE_OPUSENC)
60 /* Variables that can be set in formats.conf */
61 static int complexity = 10; /* OPUS default */
62 static int maxbitrate = CODEC_OPUS_DEFAULT_BITRATE;
63 #endif
64 
65 struct ogg_opus_desc {
66  OggOpusFile *of;
67 
68 #if defined(HAVE_OPUSENC)
69  OggOpusEnc *enc;
70  OggOpusComments *comments;
71 #endif
72 
73  size_t writing;
74  off_t writing_pcm_pos;
75 };
76 
77 static int fread_wrapper(void *_stream, unsigned char *_ptr, int _nbytes)
78 {
79  FILE *stream = _stream;
80  size_t bytes_read;
81 
82  if (!stream || _nbytes < 0) {
83  return -1;
84  }
85 
86  bytes_read = fread(_ptr, 1, _nbytes, stream);
87 
88  return bytes_read > 0 || feof(stream) ? (int) bytes_read : OP_EREAD;
89 }
90 
91 static int fseek_wrapper(void *_stream, opus_int64 _offset, int _whence)
92 {
93  FILE *stream = _stream;
94 
95  return fseeko(stream, (off_t) _offset, _whence);
96 }
97 
98 static opus_int64 ftell_wrapper(void *_stream)
99 {
100  FILE *stream = _stream;
101 
102  return ftello(stream);
103 }
104 
105 static int ogg_opus_open(struct ast_filestream *s)
106 {
107  struct ogg_opus_desc *desc = (struct ogg_opus_desc *) s->_private;
108  OpusFileCallbacks cb = {
109  .read = fread_wrapper,
110  .seek = fseek_wrapper,
111  .tell = ftell_wrapper,
112  .close = NULL,
113  };
114 
115  memset(desc, 0, sizeof(*desc));
116  desc->of = op_open_callbacks(s->f, &cb, NULL, 0, NULL);
117  if (!desc->of) {
118  return -1;
119  }
120 
121  return 0;
122 }
123 
124 #if defined(HAVE_OPUSENC)
125 static int fwrite_wrapper(void *user_data, const unsigned char *ptr, opus_int32 len)
126 {
127  FILE *stream = user_data;
128 
129  return fwrite(ptr, 1, len, stream) != (size_t) len;
130 }
131 
132 static int fclose_wrapper(void *user_data)
133 {
134  return 0;
135 }
136 
137 static const OpusEncCallbacks enc_callbacks = {
138  .write = fwrite_wrapper,
139  .close = fclose_wrapper,
140 };
141 
142 static int ogg_opus_rewrite(struct ast_filestream *fs, const char *comment)
143 {
144  struct ogg_opus_desc *desc = fs->_private;
145  int err, rate, channels, family;
146 
147  desc->writing = 1;
148  desc->writing_pcm_pos = 0;
149 
150  desc->comments = ope_comments_create();
151  ope_comments_add(desc->comments, "ENCODER", "Asterisk PBX");
152  if (comment)
153  ope_comments_add(desc->comments, "COMMENT", comment);
154 
156 #if defined(ASTERISK_VERSION_NUM) && (ASTERISK_VERSION_NUM < 150000)
157  channels = 1;
158 #else
159  channels = ast_format_get_channel_count(fs->fmt->format);
160 #endif
161  if (channels < 3) {
162  family = 0;
163  } else {
164  family = 1;
165  }
166 
167  desc->enc = ope_encoder_create_callbacks(&enc_callbacks, fs->f, desc->comments, rate, channels, family, &err);
168 
169  if (!desc->enc) {
170  ast_log(AST_LOG_ERROR, "Error creating the OGG/Opus encoder: %s\n", ope_strerror(err));
171  return -1;
172  }
173 
174  ope_encoder_ctl(desc->enc, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE));
175  ope_encoder_ctl(desc->enc, OPUS_SET_COMPLEXITY(complexity));
176  ope_encoder_ctl(desc->enc, OPUS_SET_BITRATE(maxbitrate));
177 
178  return 0;
179 }
180 
181 static int ogg_opus_write(struct ast_filestream *fs, struct ast_frame *f)
182 {
183  struct ogg_opus_desc *desc = fs->_private;
184  int err;
185 
186  if (!desc->writing) {
187  ast_log(LOG_ERROR, "This OGG/Opus stream is not set up for writing!\n");
188  return -1;
189  }
190 
191  if (!f->datalen) {
192  return -1;
193  }
194 
195  err = ope_encoder_write(desc->enc, f->data.ptr, f->samples);
196  if (err) {
197  ast_log(AST_LOG_ERROR, "Error encoding OGG/Opus frame: %s\n", ope_strerror(err));
198  return -1;
199  }
200 
201  desc->writing_pcm_pos += f->samples;
202  return 0;
203 }
204 #else
205 static int ogg_opus_rewrite(struct ast_filestream *fs, const char *comment)
206 {
207  ast_log(LOG_ERROR, "Writing OGG/Opus streams is not built-in\n");
208  return -1;
209 }
210 
211 static int ogg_opus_write(struct ast_filestream *fs, struct ast_frame *f)
212 {
213  ast_log(LOG_ERROR, "Writing OGG/Opus streams is not built-in\n");
214  return -1;
215 }
216 #endif
217 
218 static int ogg_opus_seek(struct ast_filestream *fs, off_t sample_offset, int whence)
219 {
220  int seek_result = -1;
221  off_t relative_pcm_pos;
222  struct ogg_opus_desc *desc = fs->_private;
223 
224  if (desc->writing) {
225  return -1;
226  }
227 
228  switch (whence) {
229  case SEEK_SET:
230  seek_result = op_pcm_seek(desc->of, sample_offset);
231  break;
232  case SEEK_CUR:
233  if ((relative_pcm_pos = op_pcm_tell(desc->of)) < 0) {
234  seek_result = -1;
235  break;
236  }
237  seek_result = op_pcm_seek(desc->of, relative_pcm_pos + sample_offset);
238  break;
239  case SEEK_END:
240  if ((relative_pcm_pos = op_pcm_total(desc->of, -1)) < 0) {
241  seek_result = -1;
242  break;
243  }
244  seek_result = op_pcm_seek(desc->of, relative_pcm_pos - sample_offset);
245  break;
246  default:
247  ast_log(LOG_WARNING, "Unknown *whence* to seek on OGG/Opus streams!\n");
248  break;
249  }
250 
251  /* normalize error value to -1,0 */
252  return (seek_result == 0) ? 0 : -1;
253 }
254 
255 static int ogg_opus_trunc(struct ast_filestream *fs)
256 {
257  return -1;
258 }
259 
260 static off_t ogg_opus_tell(struct ast_filestream *fs)
261 {
262  struct ogg_opus_desc *desc = fs->_private;
263  off_t pos;
264 
265  if (desc->writing) {
266  return desc->writing_pcm_pos / CODEC_OPUS_DEFAULT_SAMPLE_RATE * DEFAULT_SAMPLE_RATE;
267  }
268 
269  pos = (off_t) op_pcm_tell(desc->of);
270  if (pos < 0) {
271  return -1;
272  }
273  return pos;
274 }
275 
276 static struct ast_frame *ogg_opus_read(struct ast_filestream *fs, int *whennext)
277 {
278  struct ogg_opus_desc *desc = fs->_private;
279  int hole = 1;
280  int samples_read;
281  opus_int16 *out_buf;
282 
283  if (desc->writing) {
284  ast_log(LOG_WARNING, "Reading is not supported on OGG/Opus in writing mode.\n");
285  return NULL;
286  }
287 
288  AST_FRAME_SET_BUFFER(&fs->fr, fs->buf, AST_FRIENDLY_OFFSET, BUF_SIZE)
289 
290  out_buf = (opus_int16 *) fs->fr.data.ptr;
291 
292  while (hole) {
293  samples_read = op_read(
294  desc->of,
295  out_buf,
296  SAMPLES_MAX,
297  NULL);
298 
299  if (samples_read != OP_HOLE) {
300  hole = 0;
301  }
302  }
303 
304  if (samples_read <= 0) {
305  return NULL;
306  }
307 
308  fs->fr.datalen = samples_read * 2;
309  fs->fr.samples = samples_read;
310  *whennext = fs->fr.samples;
311 
312  return &fs->fr;
313 }
314 
315 static void ogg_opus_close(struct ast_filestream *fs)
316 {
317  struct ogg_opus_desc *desc = fs->_private;
318 
319  if (desc->writing) {
320 #if defined(HAVE_OPUSENC)
321  ope_encoder_drain(desc->enc);
322  ope_encoder_destroy(desc->enc);
323  ope_comments_destroy(desc->comments);
324 #endif
325  } else {
326  op_free(desc->of);
327  }
328 }
329 
330 static struct ast_format_def opus_f = {
331  .name = "ogg_opus",
332  .exts = "opus",
333  .open = ogg_opus_open,
334  .rewrite = ogg_opus_rewrite,
335  .write = ogg_opus_write,
336  .seek = ogg_opus_seek,
337  .trunc = ogg_opus_trunc,
338  .tell = ogg_opus_tell,
339  .read = ogg_opus_read,
340  .close = ogg_opus_close,
341  .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET,
342  .desc_size = sizeof(struct ogg_opus_desc),
343 };
344 
345 static int parse_config(int reload)
346 {
347 #if defined(HAVE_OPUSENC)
348  struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
349  struct ast_config *cfg = ast_config_load("formats.conf", config_flags);
350  struct ast_variable *var;
351  int i, res = 0;
352 
353  if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
354  return res;
355  }
356 
357  for (var = ast_variable_browse(cfg, "opus"); var; var = var->next) {
358  if (!strcasecmp(var->name, "complexity")) {
359  i = atoi(var->value);
360  if (i < 0 || i > 10) {
361  res = 1;
362  ast_log(LOG_ERROR, "Complexity must be in 0-10\n");
363  break;
364  }
365 
366  complexity = i;
367  } else if (!strcasecmp(var->name, CODEC_OPUS_ATTR_MAX_AVERAGE_BITRATE)) {
368  i = atoi(var->value);
369  if (i < 500 || i > 512000) {
370  res = 1;
371  ast_log(LOG_ERROR, CODEC_OPUS_ATTR_MAX_AVERAGE_BITRATE " must be in 500-512000\n");
372  break;
373  }
374 
375  maxbitrate = i;
376  }
377  }
378  ast_config_destroy(cfg);
379 
380  return res;
381 #else
382  return 0;
383 #endif
384 }
385 
386 static int load_module(void)
387 {
388  if (parse_config(0)) {
390  }
391 
392  opus_f.format = ast_format_slin48;
393  if (ast_format_def_register(&opus_f)) {
395  }
396 
398 }
399 
400 static int reload_module(void)
401 {
402  if (parse_config(1)) {
404  }
405 
407 }
408 
409 static int unload_module(void)
410 {
411  return ast_format_def_unregister(opus_f.name);
412 }
413 
414 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "OGG/Opus audio",
415  .support_level = AST_MODULE_SUPPORT_CORE,
416  .load = load_module,
417  .reload = reload_module,
418  .unload = unload_module,
419  .load_pri = AST_MODPRI_APP_DEPEND
420 );
#define CODEC_OPUS_DEFAULT_SAMPLE_RATE
Default attribute values.
struct ast_variable * next
Asterisk main include file. File version handling, generic pbx functions.
unsigned int ast_format_get_channel_count(const struct ast_format *format)
Get the channel count on a format.
Definition: format.c:135
Structure for variables, used for configurations and for channel variables.
Each supported file format is described by the following structure.
Definition: mod_format.h:43
Header for providers of file and format handling routines. Clients of these routines should include "...
Utility functions.
Media Format API.
int ast_format_def_unregister(const char *name)
Unregisters a file format.
Definition: file.c:162
Configuration File Parser.
#define ast_config_load(filename, flags)
Load a config file.
#define AST_FRIENDLY_OFFSET
Offset into a frame's data buffer.
#define CODEC_OPUS_ATTR_MAX_AVERAGE_BITRATE
Maximum average received bit rate (in bits per second)
struct ast_format_def * fmt
Definition: mod_format.h:103
Asterisk internal frame definitions.
struct ast_frame fr
frame produced by read, typically
Definition: mod_format.h:122
struct ast_format * format
Definition: mod_format.h:48
void * _private
Definition: mod_format.h:124
#define AST_FRAME_SET_BUFFER(fr, _base, _ofs, _datalen)
char name[80]
Definition: mod_format.h:44
union ast_frame::@224 data
Module could not be loaded properly.
Definition: module.h:102
Support for logging to various files, console and syslog Configuration in file logger.conf.
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
Structure used to handle boolean flags.
Definition: utils.h:199
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
Data structure associated with a single frame of data.
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
struct ast_format * ast_format_slin48
Built-in cached signed linear 48kHz format.
Definition: format_cache.c:71
Media Format Cache API.