28 #if defined(ASTERISK_REGISTER_FILE)
29 ASTERISK_REGISTER_FILE()
30 #elif defined(ASTERISK_FILE_VERSION)
31 ASTERISK_FILE_VERSION(__FILE__,
"$Revision: $")
41 #if defined(HAVE_OPUSENC)
47 #include <opus/opus.h>
48 #include <opus/opusfile.h>
49 #if defined(HAVE_OPUSENC)
50 #include <opus/opusenc.h>
53 #include "asterisk/opus.h"
56 #define SAMPLES_MAX 5760
57 #define BUF_SIZE (2 * SAMPLES_MAX)
59 #if defined(HAVE_OPUSENC)
61 static int complexity = 10;
62 static int maxbitrate = CODEC_OPUS_DEFAULT_BITRATE;
68 #if defined(HAVE_OPUSENC)
70 OggOpusComments *comments;
74 off_t writing_pcm_pos;
77 static int fread_wrapper(
void *_stream,
unsigned char *_ptr,
int _nbytes)
79 FILE *stream = _stream;
82 if (!stream || _nbytes < 0) {
86 bytes_read = fread(_ptr, 1, _nbytes, stream);
88 return bytes_read > 0 || feof(stream) ? (int) bytes_read : OP_EREAD;
91 static int fseek_wrapper(
void *_stream, opus_int64 _offset,
int _whence)
93 FILE *stream = _stream;
95 return fseeko(stream, (off_t) _offset, _whence);
98 static opus_int64 ftell_wrapper(
void *_stream)
100 FILE *stream = _stream;
102 return ftello(stream);
108 OpusFileCallbacks cb = {
109 .read = fread_wrapper,
110 .seek = fseek_wrapper,
111 .tell = ftell_wrapper,
115 memset(desc, 0,
sizeof(*desc));
116 desc->of = op_open_callbacks(s->f, &cb, NULL, 0, NULL);
124 #if defined(HAVE_OPUSENC)
125 static int fwrite_wrapper(
void *user_data,
const unsigned char *ptr, opus_int32 len)
127 FILE *stream = user_data;
129 return fwrite(ptr, 1, len, stream) != (size_t) len;
132 static int fclose_wrapper(
void *user_data)
137 static const OpusEncCallbacks enc_callbacks = {
138 .write = fwrite_wrapper,
139 .close = fclose_wrapper,
142 static int ogg_opus_rewrite(
struct ast_filestream *fs,
const char *comment)
145 int err, rate, channels, family;
148 desc->writing_pcm_pos = 0;
150 desc->comments = ope_comments_create();
151 ope_comments_add(desc->comments,
"ENCODER",
"Asterisk PBX");
153 ope_comments_add(desc->comments,
"COMMENT", comment);
156 #if defined(ASTERISK_VERSION_NUM) && (ASTERISK_VERSION_NUM < 150000)
167 desc->enc = ope_encoder_create_callbacks(&enc_callbacks, fs->f, desc->comments, rate, channels, family, &err);
170 ast_log(AST_LOG_ERROR,
"Error creating the OGG/Opus encoder: %s\n", ope_strerror(err));
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));
186 if (!desc->writing) {
187 ast_log(LOG_ERROR,
"This OGG/Opus stream is not set up for writing!\n");
195 err = ope_encoder_write(desc->enc, f->
data.ptr, f->
samples);
197 ast_log(AST_LOG_ERROR,
"Error encoding OGG/Opus frame: %s\n", ope_strerror(err));
201 desc->writing_pcm_pos += f->
samples;
205 static int ogg_opus_rewrite(
struct ast_filestream *fs,
const char *comment)
207 ast_log(LOG_ERROR,
"Writing OGG/Opus streams is not built-in\n");
213 ast_log(LOG_ERROR,
"Writing OGG/Opus streams is not built-in\n");
218 static int ogg_opus_seek(
struct ast_filestream *fs, off_t sample_offset,
int whence)
220 int seek_result = -1;
221 off_t relative_pcm_pos;
230 seek_result = op_pcm_seek(desc->of, sample_offset);
233 if ((relative_pcm_pos = op_pcm_tell(desc->of)) < 0) {
237 seek_result = op_pcm_seek(desc->of, relative_pcm_pos + sample_offset);
240 if ((relative_pcm_pos = op_pcm_total(desc->of, -1)) < 0) {
244 seek_result = op_pcm_seek(desc->of, relative_pcm_pos - sample_offset);
247 ast_log(LOG_WARNING,
"Unknown *whence* to seek on OGG/Opus streams!\n");
252 return (seek_result == 0) ? 0 : -1;
269 pos = (off_t) op_pcm_tell(desc->of);
284 ast_log(LOG_WARNING,
"Reading is not supported on OGG/Opus in writing mode.\n");
290 out_buf = (opus_int16 *) fs->fr.data.ptr;
293 samples_read = op_read(
299 if (samples_read != OP_HOLE) {
304 if (samples_read <= 0) {
320 #if defined(HAVE_OPUSENC)
321 ope_encoder_drain(desc->enc);
322 ope_encoder_destroy(desc->enc);
323 ope_comments_destroy(desc->comments);
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,
345 static int parse_config(
int reload)
347 #if defined(HAVE_OPUSENC)
353 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
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) {
362 ast_log(LOG_ERROR,
"Complexity must be in 0-10\n");
368 i = atoi(var->
value);
369 if (i < 500 || i > 512000) {
386 static int load_module(
void)
388 if (parse_config(0)) {
393 if (ast_format_def_register(&opus_f)) {
400 static int reload_module(
void)
402 if (parse_config(1)) {
409 static int unload_module(
void)
415 .support_level = AST_MODULE_SUPPORT_CORE,
417 .reload = reload_module,
418 .unload = unload_module,
#define CODEC_OPUS_DEFAULT_SAMPLE_RATE
Default attribute values.
struct ast_variable * next
Asterisk main include file. File version handling, generic pbx functions.
Structure for variables, used for configurations and for channel variables.
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
Asterisk internal frame definitions.
struct ast_frame fr
frame produced by read, typically
#define AST_FRAME_SET_BUFFER(fr, _base, _ofs, _datalen)
union ast_frame::@224 data
Module could not be loaded properly.
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.
Structure used to handle boolean flags.
This structure is allocated by file.c in one chunk, together with buf_size and desc_size bytes of mem...
Data structure associated with a single frame of data.
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
#define ASTERISK_GPL_KEY
The text the key() function should return.
Asterisk module definitions.