diff -urN asterisk-11.1.2/build_tools/menuselect-deps.in asterisk-11.1.2-opus/build_tools/menuselect-deps.in --- asterisk-11.1.2/build_tools/menuselect-deps.in 2012-07-25 14:21:54.000000000 +0200 +++ asterisk-11.1.2-opus/build_tools/menuselect-deps.in 2013-05-24 16:22:58.320556018 +0200 @@ -40,6 +40,7 @@ NEON29=@PBX_NEON29@ OGG=@PBX_OGG@ OPENH323=@PBX_OPENH323@ +OPUS=@PBX_OPUS@ OSPTK=@PBX_OSPTK@ OSS=@PBX_OSS@ PGSQL=@PBX_PGSQL@ diff -urN asterisk-11.1.2/channels/chan_sip.c asterisk-11.1.2-opus/channels/chan_sip.c --- asterisk-11.1.2/channels/chan_sip.c 2013-01-02 20:23:44.000000000 +0100 +++ asterisk-11.1.2-opus/channels/chan_sip.c 2013-05-27 12:16:44.907975566 +0200 @@ -7724,8 +7724,25 @@ break; case AST_CONTROL_VIDUPDATE: /* Request a video frame update */ if (p->vrtp && !p->novideo) { - transmit_info_with_vidupdate(p); - /* ast_rtcp_send_h261fur(p->vrtp); */ + /* Only use this for WebRTC users */ + struct ast_format_cap *fcap = ast_channel_nativeformats(ast); + struct ast_format vp8; + ast_format_set(&vp8, AST_FORMAT_VP8, 0); + if(ast_format_cap_iscompatible(fcap, &vp8)) { + sip_pvt_lock(p); + if (p->vrtp) { + ast_log(LOG_WARNING, "chan_sip, sending RTCP FIR to WebRTC user\n"); + /* FIXME Fake RTP write, this will be sent as an RTCP packet */ + struct ast_frame fr; + fr.frametype = AST_FRAME_CONTROL; + fr.subclass.integer = AST_CONTROL_VIDUPDATE; + res = ast_rtp_instance_write(p->vrtp, &fr); + } + sip_pvt_unlock(p); + } else { + transmit_info_with_vidupdate(p); + /* ast_rtcp_send_h261fur(p->vrtp); */ + } } else res = -1; break; @@ -10982,7 +10999,7 @@ struct ast_format *format; if ((format = ast_rtp_codecs_get_payload_format(newaudiortp, codec))) { - unsigned int bit_rate; + unsigned int bit_rate, value; if (!ast_format_sdp_parse(format, fmtp_string)) { found = TRUE; @@ -11021,6 +11038,53 @@ } } break; + /* Opus SDP fmtp parameters (draft-ietf-payload-rtp-opus-00) */ + case AST_FORMAT_OPUS: + if (sscanf(fmtp_string, "maxplaybackrate=%30u", &value) == 1) { + ast_log(LOG_WARNING, "Got Opus maxplaybackrate=%d\n", value); + /* TODO: actually handle this */ + found = TRUE; + } + if (sscanf(fmtp_string, "sprop-maxcapturerate=%30u", &value) == 1) { + ast_log(LOG_WARNING, "Got Opus sprop-maxcapturerate=%d\n", value); + /* TODO: actually handle this */ + found = TRUE; + } + if (sscanf(fmtp_string, "minptime=%30u", &value) == 1) { + ast_log(LOG_WARNING, "Got Opus minptime=%d\n", value); + /* TODO: actually handle this */ + found = TRUE; + } + if (sscanf(fmtp_string, "maxaveragebitrate=%30u", &value) == 1) { + ast_log(LOG_WARNING, "Got Opus maxaveragebitrate=%d\n", value); + /* TODO: actually handle this */ + found = TRUE; + } + if (sscanf(fmtp_string, "stereo=%30u", &value) == 1) { + ast_log(LOG_WARNING, "Got Opus stereo=%d\n", value); + /* TODO: actually handle this */ + found = TRUE; + } + if (sscanf(fmtp_string, "sprop-stereo=%30u", &value) == 1) { + ast_log(LOG_WARNING, "Got Opus sprop-stereo=%d\n", value); + /* TODO: actually handle this */ + found = TRUE; + } + if (sscanf(fmtp_string, "cbr=%30u", &value) == 1) { + ast_log(LOG_WARNING, "Got Opus cbr=%d\n", value); + /* TODO: actually handle this */ + found = TRUE; + } + if (sscanf(fmtp_string, "useinbandfec=%30u", &value) == 1) { + ast_log(LOG_WARNING, "Got Opus useinbandfec=%d\n", value); + /* TODO: actually handle this */ + found = TRUE; + } + if (sscanf(fmtp_string, "usedtx=%30u", &value) == 1) { + ast_log(LOG_WARNING, "Got Opus usedtx=%d\n", value); + /* TODO: actually handle this */ + found = TRUE; + } } } } @@ -11041,7 +11105,9 @@ /* We have a rtpmap to handle */ if (*last_rtpmap_codec < SDP_MAX_RTPMAP_CODECS) { /* Note: should really look at the '#chans' params too */ - if (!strncasecmp(mimeSubtype, "H26", 3) || !strncasecmp(mimeSubtype, "MP4", 3)) { + if (!strncasecmp(mimeSubtype, "H26", 3) || !strncasecmp(mimeSubtype, "MP4", 3) + /* VP8 */ + || !strncasecmp(mimeSubtype, "VP8", 3)) { if (!(ast_rtp_codecs_payloads_set_rtpmap_type_rate(newvideortp, NULL, codec, "video", mimeSubtype, 0, sample_rate))) { if (debug) ast_verbose("Found video description format %s for ID %d\n", mimeSubtype, codec); @@ -12583,7 +12649,11 @@ } else /* I don't see how you couldn't have p->rtp, but good to check for and error out if not there like earlier code */ return; ast_str_append(m_buf, 0, " %d", rtp_code); - ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%d\r\n", rtp_code, mime, rate); + /* Opus mandates 2 channels in rtpmap */ + if((int) format->id == AST_FORMAT_OPUS) + ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%d/2\r\n", rtp_code, mime, rate); + else + ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%d\r\n", rtp_code, mime, rate); ast_format_sdp_generate(format, rtp_code, a_buf); @@ -12612,6 +12682,17 @@ /* Indicate that we only expect 64Kbps */ ast_str_append(a_buf, 0, "a=fmtp:%d bitrate=64000\r\n", rtp_code); break; + /* Opus, pass parameters we care about (FIXME could this be 'fb' and not 'wb'?) */ + case AST_FORMAT_OPUS: + ast_str_append(a_buf, 0, "a=maxptime:%d\r\n", 60); /* FIXME */ + ast_str_append(a_buf, 0, "a=fmtp:%d maxplaybackrate=%d; stereo=%d; sprop-stereo=%d; useinbandfec=%d\r\n", + rtp_code, + 16000, /* maxplaybackrate */ + 0, /* stereo */ + 0, /* sprop-stereo */ + 0 /* useinbandfec FIXME */ + ); + break; } if (fmt.cur_ms && (fmt.cur_ms < *min_packet_size)) diff -urN asterisk-11.1.2/codecs/codec_opus.c asterisk-11.1.2-opus/codecs/codec_opus.c --- asterisk-11.1.2/codecs/codec_opus.c 1970-01-01 01:00:00.000000000 +0100 +++ asterisk-11.1.2-opus/codecs/codec_opus.c 2015-02-13 12:59:46.395779315 +0100 @@ -0,0 +1,539 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2005, Digium, Inc. + * + * Mark Spencer + * + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief Translate between signed linear and Opus (Open Codec) + * + * \author Lorenzo Miniero + * + * \note This work was motivated by Mozilla + * + * \ingroup codecs + * + * \extref The Opus library - http://opus-codec.org + * + */ + +/*** MODULEINFO + opus + core + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 328209 $") + +#include + +#include "asterisk/translate.h" +#include "asterisk/module.h" +#include "asterisk/config.h" +#include "asterisk/utils.h" +#include "asterisk/cli.h" + + +#define BUFFER_SAMPLES 8000 +#define OPUS_SAMPLES 160 + +#define USE_FEC 0 + +#define DEFAULT_COMPLEXITY 4 + + +/* Sample frame data */ +#include "asterisk/slin.h" +#include "ex_opus.h" + +/* FIXME: Test */ +#include "asterisk/file.h" + + +static int encid = 0; +static int decid = 0; + +static int opusdebug = 0; + + +/* Private structures */ +struct opus_coder_pvt { + void *opus; /* May be encoder or decoder */ + int sampling_rate; + int multiplier; + int fec; + + int id; + + int16_t buf[BUFFER_SAMPLES]; /* FIXME */ + int framesize; + + FILE *file; +}; + + +/* Helper methods */ +static int opus_encoder_construct(struct ast_trans_pvt *pvt, int sampling_rate) { + if(sampling_rate != 8000 && sampling_rate != 12000 && sampling_rate != 16000 && sampling_rate != 24000 && sampling_rate != 48000) + return -1; + struct opus_coder_pvt *opvt = pvt->pvt; + opvt->sampling_rate = sampling_rate; + opvt->multiplier = 48000/sampling_rate; + opvt->fec = USE_FEC; + int error = 0; + opvt->opus = opus_encoder_create(sampling_rate, 1, OPUS_APPLICATION_VOIP, &error); + if(error != OPUS_OK) { + if(opusdebug) + ast_verbose("[Opus] Ops! got an error creating the Opus encoder: %d (%s)\n", error, opus_strerror(error)); + return -1; + } + if(sampling_rate == 8000) + opus_encoder_ctl(opvt->opus, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_NARROWBAND)); + else if(sampling_rate == 12000) + opus_encoder_ctl(opvt->opus, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_MEDIUMBAND)); + else if(sampling_rate == 16000) + opus_encoder_ctl(opvt->opus, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_WIDEBAND)); + else if(sampling_rate == 24000) + opus_encoder_ctl(opvt->opus, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_SUPERWIDEBAND)); + else if(sampling_rate == 48000) + opus_encoder_ctl(opvt->opus, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_FULLBAND)); + opus_encoder_ctl(opvt->opus, OPUS_SET_INBAND_FEC(opvt->fec)); + opus_encoder_ctl(opvt->opus, OPUS_SET_COMPLEXITY(DEFAULT_COMPLEXITY)); + opvt->framesize = sampling_rate/50; + opvt->id = ++encid; + if(opusdebug) + ast_verbose("[Opus] Created encoder #%d (%d->opus)\n", opvt->id, sampling_rate); + + return 0; +} + +static int opus_decoder_construct(struct ast_trans_pvt *pvt, int sampling_rate) { + if(sampling_rate != 8000 && sampling_rate != 12000 && sampling_rate != 16000 && sampling_rate != 24000 && sampling_rate != 48000) + return -1; + struct opus_coder_pvt *opvt = pvt->pvt; + opvt->sampling_rate = sampling_rate; + opvt->multiplier = 48000/sampling_rate; + opvt->fec = USE_FEC; /* FIXME: should be triggered by chan_sip */ + int error = 0; + opvt->opus = opus_decoder_create(sampling_rate, 1, &error); + if(error != OPUS_OK) { + if(opusdebug) + ast_verbose("[Opus] Ops! got an error creating the Opus decoder: %d (%s)\n", error, opus_strerror(error)); + return -1; + } + opvt->id = ++decid; + if(opusdebug) + ast_verbose("[Opus] Created decoder #%d (opus->%d)\n", opvt->id, sampling_rate); + + if(opusdebug > 1) { + char filename[50]; + sprintf(filename, "/home/lminiero/opusdec-%04d-%d.raw", opvt->id, opvt->sampling_rate); + opvt->file = fopen(filename, "wb"); + } + + return 0; +} + +/* Translator callbacks */ +static int lintoopus_new(struct ast_trans_pvt *pvt) { + return opus_encoder_construct(pvt, 8000); +} + +static int lin12toopus_new(struct ast_trans_pvt *pvt) { + return opus_encoder_construct(pvt, 12000); +} + +static int lin16toopus_new(struct ast_trans_pvt *pvt) { + return opus_encoder_construct(pvt, 16000); +} + +static int lin24toopus_new(struct ast_trans_pvt *pvt) { + return opus_encoder_construct(pvt, 24000); +} + +static int lin48toopus_new(struct ast_trans_pvt *pvt) { + return opus_encoder_construct(pvt, 48000); +} + +static int opustolin_new(struct ast_trans_pvt *pvt) { + return opus_decoder_construct(pvt, 8000); +} + +static int opustolin12_new(struct ast_trans_pvt *pvt) { + return opus_decoder_construct(pvt, 12000); +} + +static int opustolin16_new(struct ast_trans_pvt *pvt) { + return opus_decoder_construct(pvt, 16000); +} + +static int opustolin24_new(struct ast_trans_pvt *pvt) { + return opus_decoder_construct(pvt, 24000); +} + +static int opustolin48_new(struct ast_trans_pvt *pvt) { + return opus_decoder_construct(pvt, 48000); +} + +static int lintoopus_framein(struct ast_trans_pvt *pvt, struct ast_frame *f) { + struct opus_coder_pvt *opvt = pvt->pvt; + + /* XXX We should look at how old the rest of our stream is, and if it + is too old, then we should overwrite it entirely, otherwise we can + get artifacts of earlier talk that do not belong */ + memcpy(opvt->buf + pvt->samples, f->data.ptr, f->datalen); + pvt->samples += f->samples; + + return 0; +} + +static struct ast_frame *lintoopus_frameout(struct ast_trans_pvt *pvt) { + struct opus_coder_pvt *opvt = pvt->pvt; + + /* We can't work on anything less than a frame in size */ + if (pvt->samples < opvt->framesize) + return NULL; + + int datalen = 0; /* output bytes */ + int samples = 0; /* output samples */ + + while(pvt->samples >= opvt->framesize) { + /* Encode 160 samples (or more if it's not narrowband) */ + if(opusdebug > 1) + ast_verbose("[Opus] [Encoder #%d (%d)] %d samples, %d bytes\n", opvt->id, opvt->sampling_rate, opvt->framesize, opvt->framesize*2); + int len = opus_encode(opvt->opus, opvt->buf, opvt->framesize, pvt->outbuf.uc, BUFFER_SAMPLES); + if(len < 0) { + if(opusdebug) + ast_verbose("[Opus] Ops! got an error encoding the Opus frame: %d (%s)\n", len, opus_strerror(len)); + return NULL; + } + datalen += len; + samples += opvt->framesize; + pvt->samples -= opvt->framesize; + /* Move the data at the end of the buffer to the front */ + if (pvt->samples) + memmove(opvt->buf, opvt->buf + samples, pvt->samples * 2); + + if(opusdebug > 1) + ast_verbose("[Opus] [Encoder #%d (%d)] >> Got %d samples, %d bytes\n", opvt->id, opvt->sampling_rate, opvt->multiplier*samples, datalen); + + if(opvt->file) + fwrite(opvt->buf, sizeof(int16_t), opvt->multiplier*samples, opvt->file); + } + + /* Move the data at the end of the buffer to the front */ + if(pvt->samples) + memmove(opvt->buf, opvt->buf+samples, pvt->samples*2); + + return ast_trans_frameout(pvt, datalen, opvt->multiplier*samples); +} + +static int opustolin_framein(struct ast_trans_pvt *pvt, struct ast_frame *f) { + struct opus_coder_pvt *opvt = pvt->pvt; + /* Decode */ + if(opusdebug > 1) + ast_verbose("[Opus] [Decoder #%d (%d)] %d samples, %d bytes\n", opvt->id, opvt->sampling_rate, f->samples, f->datalen); + int error = opus_decode(opvt->opus, f->data.ptr, f->datalen, pvt->outbuf.i16, BUFFER_SAMPLES, opvt->fec); + if(error < 0) { + if(opusdebug) + ast_verbose("[Opus] Ops! got an error decoding the Opus frame: %d (%s)\n", error, opus_strerror(error)); + return -1; + } + pvt->samples += error; + pvt->datalen += error*2; + + if(opusdebug > 1) + ast_verbose("[Opus] [Decoder #%d (%d)] >> Got %d samples, %d bytes\n", opvt->id, opvt->sampling_rate, pvt->samples, pvt->datalen); + + if(opvt->file) + fwrite(pvt->outbuf.i16, sizeof(int16_t), pvt->samples, opvt->file); + + return 0; +} + +static void lintoopus_destroy(struct ast_trans_pvt *arg) { + struct opus_coder_pvt *opvt = arg->pvt; + if(opvt == NULL || opvt->opus == NULL) + return; + opus_encoder_destroy(opvt->opus); + if(opusdebug) + ast_verbose("[Opus] Destroyed encoder #%d (%d->opus)\n", opvt->id, opvt->sampling_rate); + opvt->opus = NULL; + + if(opvt->file) + fclose(opvt->file); + opvt->file = NULL; +} + +static void opustolin_destroy(struct ast_trans_pvt *arg) { + struct opus_coder_pvt *opvt = arg->pvt; + if(opvt == NULL || opvt->opus == NULL) + return; + opus_decoder_destroy(opvt->opus); + if(opusdebug) + ast_verbose("[Opus] Destroyed decoder #%d (opus->%d)\n", opvt->id, opvt->sampling_rate); + opvt->opus = NULL; + + if(opvt->file) + fclose(opvt->file); + opvt->file = NULL; +} + + +/* Translators */ +static struct ast_translator lintoopus = { + .name = "lintoopus", + .newpvt = lintoopus_new, + .framein = lintoopus_framein, + .frameout = lintoopus_frameout, + .destroy = lintoopus_destroy, + .sample = slin8_sample, + .desc_size = sizeof(struct opus_coder_pvt), + .buffer_samples = BUFFER_SAMPLES, + .buf_size = BUFFER_SAMPLES * 2, +}; + +static struct ast_translator lin12toopus = { + .name = "lin12toopus", + .newpvt = lin12toopus_new, + .framein = lintoopus_framein, + .frameout = lintoopus_frameout, + .destroy = lintoopus_destroy, + .desc_size = sizeof(struct opus_coder_pvt), + .buffer_samples = BUFFER_SAMPLES, + .buf_size = BUFFER_SAMPLES * 2, +}; + +static struct ast_translator lin16toopus = { + .name = "lin16toopus", + .newpvt = lin16toopus_new, + .framein = lintoopus_framein, + .frameout = lintoopus_frameout, + .destroy = lintoopus_destroy, + .sample = slin16_sample, + .desc_size = sizeof(struct opus_coder_pvt), + .buffer_samples = BUFFER_SAMPLES, + .buf_size = BUFFER_SAMPLES * 2, +}; + +static struct ast_translator lin24toopus = { + .name = "lin24toopus", + .newpvt = lin24toopus_new, + .framein = lintoopus_framein, + .frameout = lintoopus_frameout, + .destroy = lintoopus_destroy, + .desc_size = sizeof(struct opus_coder_pvt), + .buffer_samples = BUFFER_SAMPLES, + .buf_size = BUFFER_SAMPLES * 2, +}; + +static struct ast_translator lin48toopus = { + .name = "lin48toopus", + .newpvt = lin48toopus_new, + .framein = lintoopus_framein, + .frameout = lintoopus_frameout, + .destroy = lintoopus_destroy, + .desc_size = sizeof(struct opus_coder_pvt), + .buffer_samples = BUFFER_SAMPLES, + .buf_size = BUFFER_SAMPLES * 2, +}; + +static struct ast_translator opustolin = { + .name = "opustolin", + .newpvt = opustolin_new, + .framein = opustolin_framein, + .destroy = opustolin_destroy, + .sample = opus_sample, + .desc_size = sizeof(struct opus_coder_pvt), + .buffer_samples = BUFFER_SAMPLES, + .buf_size = BUFFER_SAMPLES * 2, + .native_plc = 1, /* FIXME: needed? */ +}; + +static struct ast_translator opustolin12 = { + .name = "opustolin12", + .newpvt = opustolin12_new, + .framein = opustolin_framein, + .destroy = opustolin_destroy, + .sample = opus_sample, + .desc_size = sizeof(struct opus_coder_pvt), + .buffer_samples = BUFFER_SAMPLES, + .buf_size = BUFFER_SAMPLES * 2, + .native_plc = 1, /* FIXME: needed? */ +}; + +static struct ast_translator opustolin16 = { + .name = "opustolin16", + .newpvt = opustolin16_new, + .framein = opustolin_framein, + .destroy = opustolin_destroy, + .sample = opus_sample, + .desc_size = sizeof(struct opus_coder_pvt), + .buffer_samples = BUFFER_SAMPLES, + .buf_size = BUFFER_SAMPLES * 2, + .native_plc = 1, /* FIXME: needed? */ +}; + +static struct ast_translator opustolin24 = { + .name = "opustolin24", + .newpvt = opustolin24_new, + .framein = opustolin_framein, + .destroy = opustolin_destroy, + .sample = opus_sample, + .desc_size = sizeof(struct opus_coder_pvt), + .buffer_samples = BUFFER_SAMPLES, + .buf_size = BUFFER_SAMPLES * 2, + .native_plc = 1, /* FIXME: needed? */ +}; + +static struct ast_translator opustolin48 = { + .name = "opustolin48", + .newpvt = opustolin48_new, + .framein = opustolin_framein, + .destroy = opustolin_destroy, + .sample = opus_sample, + .desc_size = sizeof(struct opus_coder_pvt), + .buffer_samples = BUFFER_SAMPLES, + .buf_size = BUFFER_SAMPLES * 2, + .native_plc = 1, /* FIXME: needed? */ +}; + + +/* Simple CLI interface to enable/disable debugging */ +static char *handle_cli_opus_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + switch (cmd) { + case CLI_INIT: + e->command = "opus set debug"; + e->usage = + "Usage: opus set debug {status|none|normal|huge}\n" + " Enable/Disable Opus debugging: normal only debugs setup and errors, huge debugs every single packet\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if (a->argc != 4) + return CLI_SHOWUSAGE; + + if (!strncasecmp(a->argv[a->argc-1], "status", 6)) { + ast_cli(a->fd, "Opus debugging %s\n", opusdebug > 1 ? "huge" : opusdebug > 0 ? "normal" : "none"); + return CLI_SUCCESS; + } + if (!strncasecmp(a->argv[a->argc-1], "huge", 4)) + opusdebug = 2; + else if (!strncasecmp(a->argv[a->argc-1], "normal", 6)) + opusdebug = 1; + else if (!strncasecmp(a->argv[a->argc-1], "none", 4)) + opusdebug = 0; + else + return CLI_SHOWUSAGE; + + ast_cli(a->fd, "Opus debugging %s\n", opusdebug > 1 ? "huge" : opusdebug > 0 ? "normal" : "none"); + return CLI_SUCCESS; +} + +static struct ast_cli_entry cli_opus[] = { + AST_CLI_DEFINE(handle_cli_opus_set_debug, "Enable/Disable Opus debugging"), +}; + + +/* Configuration and module setup */ +static int parse_config(int reload) { + // TODO + return 0; +} + +static int reload(void) { + if(parse_config(1)) + return AST_MODULE_LOAD_DECLINE; + return AST_MODULE_LOAD_SUCCESS; +} + +static int unload_module(void) { + int res = 0; + + res |= ast_unregister_translator(&opustolin); + res |= ast_unregister_translator(&lintoopus); + res |= ast_unregister_translator(&opustolin12); + res |= ast_unregister_translator(&lin12toopus); + res |= ast_unregister_translator(&opustolin16); + res |= ast_unregister_translator(&lin16toopus); + res |= ast_unregister_translator(&opustolin24); + res |= ast_unregister_translator(&lin24toopus); + res |= ast_unregister_translator(&opustolin48); + res |= ast_unregister_translator(&lin48toopus); + + ast_cli_unregister_multiple(cli_opus, ARRAY_LEN(cli_opus)); + + return res; +} + +static int load_module(void) { + int res = 0; + + if(parse_config(0)) + return AST_MODULE_LOAD_DECLINE; + + /* 8khz (nb) */ + ast_format_set(&opustolin.src_format, AST_FORMAT_OPUS, 0); + ast_format_set(&opustolin.dst_format, AST_FORMAT_SLINEAR, 0); + ast_format_set(&lintoopus.src_format, AST_FORMAT_SLINEAR, 0); + ast_format_set(&lintoopus.dst_format, AST_FORMAT_OPUS, 0); + /* 12khz (mb) */ + ast_format_set(&opustolin12.src_format, AST_FORMAT_OPUS, 0); + ast_format_set(&opustolin12.dst_format, AST_FORMAT_SLINEAR12, 0); + ast_format_set(&lin12toopus.src_format, AST_FORMAT_SLINEAR12, 0); + ast_format_set(&lin12toopus.dst_format, AST_FORMAT_OPUS, 0); + /* 16khz (wb) */ + ast_format_set(&opustolin16.src_format, AST_FORMAT_OPUS, 0); + ast_format_set(&opustolin16.dst_format, AST_FORMAT_SLINEAR16, 0); + ast_format_set(&lin16toopus.src_format, AST_FORMAT_SLINEAR16, 0); + ast_format_set(&lin16toopus.dst_format, AST_FORMAT_OPUS, 0); + /* 24khz (swb) */ + ast_format_set(&opustolin24.src_format, AST_FORMAT_OPUS, 0); + ast_format_set(&opustolin24.dst_format, AST_FORMAT_SLINEAR24, 0); + ast_format_set(&lin24toopus.src_format, AST_FORMAT_SLINEAR24, 0); + ast_format_set(&lin24toopus.dst_format, AST_FORMAT_OPUS, 0); + /* 48khz (fb) */ + ast_format_set(&opustolin48.src_format, AST_FORMAT_OPUS, 0); + ast_format_set(&opustolin48.dst_format, AST_FORMAT_SLINEAR48, 0); + ast_format_set(&lin48toopus.src_format, AST_FORMAT_SLINEAR48, 0); + ast_format_set(&lin48toopus.dst_format, AST_FORMAT_OPUS, 0); + + res |= ast_register_translator(&opustolin); + res |= ast_register_translator(&lintoopus); + res |= ast_register_translator(&opustolin12); + res |= ast_register_translator(&lin12toopus); + res |= ast_register_translator(&opustolin16); + res |= ast_register_translator(&lin16toopus); + res |= ast_register_translator(&opustolin24); + res |= ast_register_translator(&lin24toopus); + res |= ast_register_translator(&opustolin48); + res |= ast_register_translator(&lin48toopus); + + ast_cli_register_multiple(cli_opus, sizeof(cli_opus) / sizeof(struct ast_cli_entry)); + + return res; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Opus Coder/Decoder", + .load = load_module, + .unload = unload_module, + .reload = reload, + ); diff -urN asterisk-11.1.2/codecs/ex_opus.h asterisk-11.1.2-opus/codecs/ex_opus.h --- asterisk-11.1.2/codecs/ex_opus.h 1970-01-01 01:00:00.000000000 +0100 +++ asterisk-11.1.2-opus/codecs/ex_opus.h 2013-05-24 17:06:09.976176036 +0200 @@ -0,0 +1,35 @@ +/*! \file + * \brief 8-bit data + * + * Copyright (C) 2008, Digium, Inc. + * + * Distributed under the terms of the GNU General Public License + * + */ + +/* Opus, a 20ms sample */ +static uint8_t ex_opus[] = { + 0x4b, 0x41, 0x25, 0x0b, 0xe4, 0x55, 0xc6, 0x74, + 0xda, 0xbb, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static struct ast_frame *opus_sample(void) +{ + static struct ast_frame f = { + .frametype = AST_FRAME_VOICE, + .datalen = sizeof(ex_opus), + .samples = 960, //ARRAY_LEN(ex_opus), + .mallocd = 0, + .offset = 0, + .src = __PRETTY_FUNCTION__, + .data.ptr = ex_opus, + }; + + ast_format_set(&f.subclass.format, AST_FORMAT_OPUS, 0); + + return &f; +} diff -urN asterisk-11.1.2/configure.ac asterisk-11.1.2-opus/configure.ac --- asterisk-11.1.2/configure.ac 2012-10-18 22:02:02.000000000 +0200 +++ asterisk-11.1.2-opus/configure.ac 2013-05-24 16:25:25.766936899 +0200 @@ -422,6 +422,7 @@ AST_EXT_LIB_SETUP([NEWT], [newt], [newt]) AST_EXT_LIB_SETUP([OGG], [OGG], [ogg]) AST_EXT_LIB_SETUP([OPENR2], [MFR2], [openr2]) +AST_EXT_LIB_SETUP([OPUS], [Opus], [opus]) AST_EXT_LIB_SETUP([OSPTK], [OSP Toolkit], [osptk]) AST_EXT_LIB_SETUP([OSS], [Open Sound System], [oss]) AST_EXT_LIB_SETUP([PGSQL], [PostgreSQL], [postgres]) @@ -2086,6 +2087,8 @@ AC_SUBST(PBX_SPEEX_PREPROCESS) +AST_EXT_LIB_CHECK([OPUS], [opus], [opus_encoder_create], [opus/opus.h]) + AST_EXT_LIB_CHECK([SQLITE], [sqlite], [sqlite_exec], [sqlite.h]) AST_EXT_LIB_CHECK([SQLITE3], [sqlite3], [sqlite3_open], [sqlite3.h], [${PTHREAD_LIBS}], [${PTHREAD_CFLAGS}]) diff -urN asterisk-11.1.2/formats/format_vp8.c asterisk-11.1.2-opus/formats/format_vp8.c --- asterisk-11.1.2/formats/format_vp8.c 1970-01-01 01:00:00.000000000 +0100 +++ asterisk-11.1.2-opus/formats/format_vp8.c 2013-05-24 17:01:31.070470091 +0200 @@ -0,0 +1,195 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2005, Digium, Inc. + * + * Mark Spencer + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief Save to raw, headerless VP8 data. + * + * \author Lorenzo Miniero + * + * \note Basically a "clone" of the H.264 passthrough format + * + * \arg File name extension: VP8 + * \ingroup formats + * \arg See \ref AstVideo + */ + +/*** MODULEINFO + core + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 328209 $") + +#include "asterisk/mod_format.h" +#include "asterisk/module.h" +#include "asterisk/endian.h" + +/* VP8 passthrough */ + +#define BUF_SIZE 4096 +struct vp8_desc { + unsigned int lastts; +}; + +static int vp8_open(struct ast_filestream *s) +{ + unsigned int ts; + if (fread(&ts, 1, sizeof(ts), s->f) < sizeof(ts)) { + ast_log(LOG_WARNING, "Empty file!\n"); + return -1; + } + return 0; +} + +static struct ast_frame *vp8_read(struct ast_filestream *s, int *whennext) +{ + int res; + int mark = 0; + unsigned short len; + unsigned int ts; + struct vp8_desc *fs = (struct vp8_desc *)s->_private; + + /* Send a frame from the file to the appropriate channel */ + if ((res = fread(&len, 1, sizeof(len), s->f)) < 1) + return NULL; + len = ntohs(len); + mark = (len & 0x8000) ? 1 : 0; + len &= 0x7fff; + if (len > BUF_SIZE) { + ast_log(LOG_WARNING, "Length %d is too long\n", len); + len = BUF_SIZE; /* XXX truncate */ + } + s->fr.frametype = AST_FRAME_VIDEO; + ast_format_set(&s->fr.subclass.format, AST_FORMAT_VP8, 0); + s->fr.mallocd = 0; + AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, len); + if ((res = fread(s->fr.data.ptr, 1, s->fr.datalen, s->f)) != s->fr.datalen) { + if (res) + ast_log(LOG_WARNING, "Short read (%d of %d) (%s)!\n", res, len, strerror(errno)); + return NULL; + } + s->fr.samples = fs->lastts; + s->fr.datalen = len; + if (mark) { + ast_format_set_video_mark(&s->fr.subclass.format); + } + s->fr.delivery.tv_sec = 0; + s->fr.delivery.tv_usec = 0; + if ((res = fread(&ts, 1, sizeof(ts), s->f)) == sizeof(ts)) { + fs->lastts = ntohl(ts); + *whennext = fs->lastts * 4/45; + } else + *whennext = 0; + return &s->fr; +} + +static int vp8_write(struct ast_filestream *s, struct ast_frame *f) +{ + int res; + unsigned int ts; + unsigned short len; + int mark; + + if (f->frametype != AST_FRAME_VIDEO) { + ast_log(LOG_WARNING, "Asked to write non-video frame!\n"); + return -1; + } + mark = ast_format_get_video_mark(&f->subclass.format) ? 0x8000 : 0; + if (f->subclass.format.id != AST_FORMAT_VP8) { + ast_log(LOG_WARNING, "Asked to write non-VP8 frame (%s)!\n", ast_getformatname(&f->subclass.format)); + return -1; + } + ts = htonl(f->samples); + if ((res = fwrite(&ts, 1, sizeof(ts), s->f)) != sizeof(ts)) { + ast_log(LOG_WARNING, "Bad write (%d/4): %s\n", res, strerror(errno)); + return -1; + } + len = htons(f->datalen | mark); + if ((res = fwrite(&len, 1, sizeof(len), s->f)) != sizeof(len)) { + ast_log(LOG_WARNING, "Bad write (%d/2): %s\n", res, strerror(errno)); + return -1; + } + if ((res = fwrite(f->data.ptr, 1, f->datalen, s->f)) != f->datalen) { + ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", res, f->datalen, strerror(errno)); + return -1; + } + return 0; +} + +static int vp8_seek(struct ast_filestream *fs, off_t sample_offset, int whence) +{ + /* No way Jose */ + return -1; +} + +static int vp8_trunc(struct ast_filestream *fs) +{ + int fd; + off_t cur; + + if ((fd = fileno(fs->f)) < 0) { + ast_log(AST_LOG_WARNING, "Unable to determine file descriptor for VP8 filestream %p: %s\n", fs, strerror(errno)); + return -1; + } + if ((cur = ftello(fs->f)) < 0) { + ast_log(AST_LOG_WARNING, "Unable to determine current position in VP8 filestream %p: %s\n", fs, strerror(errno)); + return -1; + } + /* Truncate file to current length */ + return ftruncate(fd, cur); +} + +static off_t vp8_tell(struct ast_filestream *fs) +{ + off_t offset = ftell(fs->f); + return offset; /* XXX totally bogus, needs fixing */ +} + +static struct ast_format_def vp8_f = { + .name = "VP8", + .exts = "vp8", + .open = vp8_open, + .write = vp8_write, + .seek = vp8_seek, + .trunc = vp8_trunc, + .tell = vp8_tell, + .read = vp8_read, + .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET, + .desc_size = sizeof(struct vp8_desc), +}; + +static int load_module(void) +{ + ast_format_set(&vp8_f.format, AST_FORMAT_VP8, 0); + if (ast_format_def_register(&vp8_f)) + return AST_MODULE_LOAD_FAILURE; + return AST_MODULE_LOAD_SUCCESS; +} + +static int unload_module(void) +{ + return ast_format_def_unregister(vp8_f.name); +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Raw VP8 data", + .load = load_module, + .unload = unload_module, + .load_pri = AST_MODPRI_APP_DEPEND +); diff -urN asterisk-11.1.2/include/asterisk/format.h asterisk-11.1.2-opus/include/asterisk/format.h --- asterisk-11.1.2/include/asterisk/format.h 2012-07-13 20:41:07.000000000 +0200 +++ asterisk-11.1.2-opus/include/asterisk/format.h 2013-05-24 16:42:59.833669803 +0200 @@ -101,6 +101,8 @@ AST_FORMAT_SLINEAR192 = 27 + AST_FORMAT_TYPE_AUDIO, AST_FORMAT_SPEEX32 = 28 + AST_FORMAT_TYPE_AUDIO, AST_FORMAT_CELT = 29 + AST_FORMAT_TYPE_AUDIO, + /*! Opus */ + AST_FORMAT_OPUS = 30 + AST_FORMAT_TYPE_AUDIO, /*! H.261 Video */ AST_FORMAT_H261 = 1 + AST_FORMAT_TYPE_VIDEO, @@ -112,6 +114,8 @@ AST_FORMAT_H264 = 4 + AST_FORMAT_TYPE_VIDEO, /*! MPEG4 Video */ AST_FORMAT_MP4_VIDEO = 5 + AST_FORMAT_TYPE_VIDEO, + /*! VP8 */ + AST_FORMAT_VP8 = 6 + AST_FORMAT_TYPE_VIDEO, /*! JPEG Images */ AST_FORMAT_JPEG = 1 + AST_FORMAT_TYPE_IMAGE, diff -urN asterisk-11.1.2/main/channel.c asterisk-11.1.2-opus/main/channel.c --- asterisk-11.1.2/main/channel.c 2013-01-02 20:23:44.000000000 +0100 +++ asterisk-11.1.2-opus/main/channel.c 2013-05-24 16:41:27.279429674 +0200 @@ -909,6 +909,8 @@ AST_FORMAT_SPEEX32, AST_FORMAT_SPEEX16, AST_FORMAT_SPEEX, + /*! Opus */ + AST_FORMAT_OPUS, /*! SILK is pretty awesome. */ AST_FORMAT_SILK, /*! CELT supports crazy high sample rates */ diff -urN asterisk-11.1.2/main/format.c asterisk-11.1.2-opus/main/format.c --- asterisk-11.1.2/main/format.c 2012-10-02 03:27:19.000000000 +0200 +++ asterisk-11.1.2-opus/main/format.c 2013-06-04 17:20:35.935378513 +0200 @@ -430,6 +430,9 @@ /*! SpeeX Wideband (16kHz) Free Compression */ case AST_FORMAT_SPEEX16: return (1ULL << 33); + /*! Opus audio (8kHz, 16kHz, 24kHz, 48Khz) */ + case AST_FORMAT_OPUS: + return (1ULL << 34); /*! Raw mu-law data (G.711) */ case AST_FORMAT_TESTLAW: return (1ULL << 47); @@ -449,6 +452,9 @@ /*! MPEG4 Video */ case AST_FORMAT_MP4_VIDEO: return (1ULL << 22); + /*! VP8 Video */ + case AST_FORMAT_VP8: + return (1ULL << 23); /*! JPEG Images */ case AST_FORMAT_JPEG: @@ -532,6 +538,9 @@ /*! SpeeX Wideband (16kHz) Free Compression */ case (1ULL << 33): return ast_format_set(dst, AST_FORMAT_SPEEX16, 0); + /*! Opus audio (8kHz, 16kHz, 24kHz, 48Khz) */ + case (1ULL << 34): + return ast_format_set(dst, AST_FORMAT_OPUS, 0); /*! Raw mu-law data (G.711) */ case (1ULL << 47): return ast_format_set(dst, AST_FORMAT_TESTLAW, 0); @@ -551,6 +560,9 @@ /*! MPEG4 Video */ case (1ULL << 22): return ast_format_set(dst, AST_FORMAT_MP4_VIDEO, 0); + /*! VP8 Video */ + case (1ULL << 23): + return ast_format_set(dst, AST_FORMAT_VP8, 0); /*! JPEG Images */ case (1ULL << 16): @@ -782,6 +794,9 @@ return samplerate; } } + /* Opus */ + case AST_FORMAT_OPUS: + return 48000; default: return 8000; } @@ -1072,6 +1087,10 @@ format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR48, 0), "slin48", 48000, "16 bit Signed Linear PCM (48kHz)", 960, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE, 0);/*!< Signed linear (48kHz) */ format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR96, 0), "slin96", 96000, "16 bit Signed Linear PCM (96kHz)", 1920, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE, 0);/*!< Signed linear (96kHz) */ format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR192, 0), "slin192", 192000, "16 bit Signed Linear PCM (192kHz)", 3840, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE, 0);/*!< Signed linear (192kHz) */ + /* Opus (FIXME: real min is 3/5/10, real max is 120...) */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_OPUS, 0), "opus", 48000, "Opus Codec", 10, 20, 60, 20, 20, 0, 0); /*!< codec_opus.c */ + /* VP8 (passthrough) */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_VP8, 0), "vp8", 0, "VP8 Video", 0, 0, 0, 0 ,0 ,0, 0); /*!< Passthrough support, see format_h263.c */ return 0; } diff -urN asterisk-11.1.2/main/frame.c asterisk-11.1.2-opus/main/frame.c --- asterisk-11.1.2/main/frame.c 2012-07-24 18:54:26.000000000 +0200 +++ asterisk-11.1.2-opus/main/frame.c 2013-05-24 16:34:23.407329930 +0200 @@ -1002,6 +1002,40 @@ return cnt; } +/* Opus: copied from opus_decoder.c */ +static int opus_samples(unsigned char *data, int len) { + /* Do opus_packet_get_nb_frames first */ + int count, frames; + if (len<1) { + return 0; /* FIXME OPUS_BAD_ARG */ + } else { + count = data[0]&0x3; + if (count==0) + frames = 1; + else if (count!=3) + frames = 2; + else if (len<2) + return 0; /* FIXME OPUS_INVALID_PACKET */ + else + frames = data[1]&0x3F; + } + /* The, do a opus_packet_get_samples_per_frame */ + int audiosize, Fs = 48000; + if (data[0]&0x80) { + audiosize = ((data[0]>>3)&0x3); + audiosize = (Fs<>3)&0x3); + if (audiosize == 3) + audiosize = Fs*60/1000; + else + audiosize = (Fs<subclass.format) / 50; break; + /* Opus */ + case AST_FORMAT_OPUS: + samples = opus_samples(f->data.ptr, f->datalen); + break; default: ast_log(LOG_WARNING, "Unable to calculate samples for format %s\n", ast_getformatname(&f->subclass.format)); } diff -urN asterisk-11.1.2/main/rtp_engine.c asterisk-11.1.2-opus/main/rtp_engine.c --- asterisk-11.1.2/main/rtp_engine.c 2012-09-20 20:18:47.000000000 +0200 +++ asterisk-11.1.2-opus/main/rtp_engine.c 2013-05-24 16:32:34.867048313 +0200 @@ -2268,6 +2268,9 @@ set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_SIREN7, 0), 0, "audio", "G7221", 16000); set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_SIREN14, 0), 0, "audio", "G7221", 32000); set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_G719, 0), 0, "audio", "G719", 48000); + /* Opus and VP8 */ + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_OPUS, 0), 0, "audio", "opus", 48000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_VP8, 0), 0, "video", "VP8", 90000); /* Define the static rtp payload mappings */ add_static_payload(0, ast_format_set(&tmpfmt, AST_FORMAT_ULAW, 0), 0); @@ -2309,6 +2312,9 @@ add_static_payload(118, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR16, 0), 0); /* 16 Khz signed linear */ add_static_payload(119, ast_format_set(&tmpfmt, AST_FORMAT_SPEEX32, 0), 0); add_static_payload(121, NULL, AST_RTP_CISCO_DTMF); /* Must be type 121 */ + /* Opus and VP8 */ + add_static_payload(100, ast_format_set(&tmpfmt, AST_FORMAT_VP8, 0), 0); + add_static_payload(107, ast_format_set(&tmpfmt, AST_FORMAT_OPUS, 0), 0); return 0; } diff -urN asterisk-11.1.2/makeopts.in asterisk-11.1.2-opus/makeopts.in --- asterisk-11.1.2/makeopts.in 2012-10-18 22:02:02.000000000 +0200 +++ asterisk-11.1.2-opus/makeopts.in 2013-05-24 16:23:57.912709903 +0200 @@ -261,6 +261,9 @@ SPEEXDSP_INCLUDE=@SPEEXDSP_INCLUDE@ SPEEXDSP_LIB=@SPEEXDSP_LIB@ +OPUS_INCLUDE=@OPUS_INCLUDE@ +OPUS_LIB=@OPUS_LIB@ + SQLITE_INCLUDE=@SQLITE_INCLUDE@ SQLITE_LIB=@SQLITE_LIB@ diff -urN asterisk-11.1.2/res/res_rtp_asterisk.c asterisk-11.1.2-opus/res/res_rtp_asterisk.c --- asterisk-11.1.2/res/res_rtp_asterisk.c 2012-10-11 18:04:19.000000000 +0200 +++ asterisk-11.1.2-opus/res/res_rtp_asterisk.c 2013-05-24 16:57:10.012799120 +0200 @@ -91,6 +91,8 @@ #define RTCP_PT_SDES 202 #define RTCP_PT_BYE 203 #define RTCP_PT_APP 204 +/* VP8: RTCP Feedback */ +#define RTCP_PT_PSFB 206 #define RTP_MTU 1200 @@ -341,6 +343,9 @@ double normdevrtt; double stdevrtt; unsigned int rtt_count; + + /* VP8: sequence number for the RTCP FIR FCI */ + int firseq; }; struct rtp_red { @@ -2599,6 +2604,41 @@ return 0; } + /* VP8: is this a request to send a RTCP FIR? */ + if(frame->frametype == AST_FRAME_CONTROL && frame->subclass.integer == AST_CONTROL_VIDUPDATE) { + ast_log(LOG_WARNING, "res_rtp_asterisk, requested to send a RTCP FIR packet to the peer\n"); + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + if (!rtp || !rtp->rtcp) + return 0; + unsigned int *rtcpheader; + char bdata[1024]; + if (ast_sockaddr_isnull(&rtp->rtcp->them)) { + /* + * RTCP was stopped. + */ + return 0; + } + /* Prepare RTCP FIR (PT=206, FMT=4) */ + rtp->rtcp->firseq++; + if(rtp->rtcp->firseq == 256) + rtp->rtcp->firseq = 0; + int len = 20; + int ice; + rtcpheader = (unsigned int *)bdata; + rtcpheader[0] = htonl((2 << 30) | (4 << 24) | (RTCP_PT_PSFB << 16) | ((len/4)-1)); + rtcpheader[1] = htonl(rtp->ssrc); + rtcpheader[2] = htonl(rtp->themssrc); + rtcpheader[3] = htonl(rtp->themssrc); /* FCI: SSRC */ + rtcpheader[4] = htonl(rtp->rtcp->firseq << 24); /* FCI: Sequence number */ + int res = rtcp_sendto(instance, (unsigned int *)rtcpheader, len, 0, &rtp->rtcp->them, &ice); + if (res < 0) { + ast_log(LOG_ERROR, "RTCP FIR transmission error: %s\n",strerror(errno)); + return 0; + } + ast_log(LOG_WARNING, " >> RTCP FIR packet sent to the peer!\n"); + return 0; + } + /* If there is no data length we can't very well send the packet */ if (!frame->datalen) { ast_debug(1, "Received frame with no data for RTP instance '%p' so dropping frame\n", instance); @@ -2650,6 +2690,8 @@ case AST_FORMAT_SIREN7: case AST_FORMAT_SIREN14: case AST_FORMAT_G719: + /* Opus */ + case AST_FORMAT_OPUS: /* these are all frame-based codecs and cannot be safely run through a smoother */ break;