diff --git a/README.md b/README.md index 3e46a028d..75d2e38dd 100644 --- a/README.md +++ b/README.md @@ -502,6 +502,9 @@ key-value pair), it can be escaped by using two dashes instead, e.g. The default (highest) bitrates for AMR and AMR-WB are 6700 and 14250, respectively. +If a Codec Mode Request (CMR) is received from the AMR peer, then *rtpengine* will adhere to the request +and switch encoder bitrate unconditionally, even if it's a higher bitrate than originally desired. + Call recording ============== diff --git a/daemon/codec.c b/daemon/codec.c index d491d4cec..ed1b14195 100644 --- a/daemon/codec.c +++ b/daemon/codec.c @@ -1723,6 +1723,19 @@ uint64_t codec_decoder_unskip_pts(struct codec_ssrc_handler *ch) { } static int codec_decoder_event(enum codec_event event, void *ptr, void *data) { + struct call_media *media = data; + if (!media) + return 0; + + switch (event) { + case CE_AMR_CMR_RECV: + // ignore locking and races for this + media->u.amr.cmr.cmr_in = GPOINTER_TO_UINT(ptr); + media->u.amr.cmr.cmr_in_ts = rtpe_now; + break; + default: + break; + } return 0; } @@ -1985,6 +1998,10 @@ static int packet_decoded_common(decoder_t *decoder, AVFrame *frame, void *u1, v __dtmf_detect(ch, frame); + // locking deliberately ignored + if (mp->media_out) + ch->encoder->codec_options.amr.cmr = mp->media_out->u.amr.cmr; + input_func(ch->encoder, frame, h->packet_encoded, ch, mp); discard: diff --git a/include/call.h b/include/call.h index 01cfc0da0..c4c18d191 100644 --- a/include/call.h +++ b/include/call.h @@ -336,6 +336,13 @@ struct call_media { struct codec_handler *dtmf_injector; struct t38_gateway *t38_gateway; struct codec_handler *t38_handler; +#ifdef WITH_TRANSCODING + union { + struct { + struct amr_cmr cmr; + } amr; + } u; +#endif int ptime; // either from SDP or overridden diff --git a/lib/codeclib.c b/lib/codeclib.c index 095c5fe5f..0a2f57732 100644 --- a/lib/codeclib.c +++ b/lib/codeclib.c @@ -57,6 +57,7 @@ static int avc_encoder_input(encoder_t *enc, AVFrame **frame); static void avc_encoder_close(encoder_t *enc); static int amr_decoder_input(decoder_t *dec, const str *data, GQueue *out); +static void amr_encoder_got_packet(encoder_t *enc); static int ilbc_decoder_input(decoder_t *dec, const str *data, GQueue *out); static const char *dtmf_decoder_init(decoder_t *, const str *); @@ -90,6 +91,7 @@ static const codec_type_t codec_type_amr = { .decoder_close = avc_decoder_close, .encoder_init = avc_encoder_init, .encoder_input = avc_encoder_input, + .encoder_got_packet = amr_encoder_got_packet, .encoder_close = avc_encoder_close, }; static const codec_type_t codec_type_dtmf = { @@ -1234,10 +1236,11 @@ int encoder_input_data(encoder_t *enc, AVFrame *frame, return -1; if (enc->avpkt.size) { - //av_write_frame(output->fmtctx, &output->avpkt); + if (enc->def->codec_type->encoder_got_packet) + enc->def->codec_type->encoder_got_packet(enc); + callback(enc, u1, u2); - //output->fifo_pts += output->frame->nb_samples; enc->mux_dts = enc->avpkt.dts + 1; // min next expected dts av_packet_unref(&enc->avpkt); @@ -1645,7 +1648,10 @@ static int amr_decoder_input(decoder_t *dec, const str *data, GQueue *out) { err = "no CMR"; if (bitstr_shift_ret(&d, 4, &cmr)) goto err; - // XXX handle CMR? + + unsigned int cmr_int = cmr_chr[0] >> 4; + if (cmr_int != 15) + decoder_event(dec, CE_AMR_CMR_RECV, GUINT_TO_POINTER(cmr_int)); if (dec->codec_options.amr.octet_aligned) { if (bitstr_shift(&d, 4)) @@ -1746,6 +1752,24 @@ err: return -1; } +static void amr_encoder_mode_change(encoder_t *enc) { + if (!memcmp(&enc->codec_options.amr.cmr.cmr_in_ts, + &enc->u.avc.u.amr.cmr_in_ts, sizeof(struct timeval))) + return; + unsigned int cmr = enc->codec_options.amr.cmr.cmr_in; + if (cmr >= AMR_FT_TYPES) + return; + // ignore CMR for invalid modes + if (enc->codec_options.amr.mode_set && !(enc->codec_options.amr.mode_set & (1 << cmr))) + return; + int req_br = enc->codec_options.amr.bitrates[cmr]; + if (!req_br) + return; + enc->u.avc.avcctx->bit_rate = req_br; +} +static void amr_encoder_got_packet(encoder_t *enc) { + amr_encoder_mode_change(enc); +} static int packetizer_amr(AVPacket *pkt, GString *buf, str *output, encoder_t *enc) { assert(pkt->size >= 1); diff --git a/lib/codeclib.h b/lib/codeclib.h index 2b5ee3e2f..fa8479319 100644 --- a/lib/codeclib.h +++ b/lib/codeclib.h @@ -81,9 +81,15 @@ struct codec_type_s { const char *(*encoder_init)(encoder_t *, const str *); int (*encoder_input)(encoder_t *, AVFrame **); + void (*encoder_got_packet)(encoder_t *); void (*encoder_close)(encoder_t *); }; +struct amr_cmr { + struct timeval cmr_in_ts; + unsigned int cmr_in; +}; + union codec_options_u { struct { int interleaving; @@ -94,6 +100,8 @@ union codec_options_u { const unsigned int *bits_per_frame; const unsigned int *bitrates; + + struct amr_cmr cmr; // input from external calling code } amr; }; @@ -145,7 +153,7 @@ struct resample_s { }; enum codec_event { - CE_DUMMY = -1, + CE_AMR_CMR_RECV, }; struct decoder_s { @@ -191,6 +199,12 @@ struct encoder_s { struct { AVCodec *codec; AVCodecContext *avcctx; + + union { + struct { + struct timeval cmr_in_ts; // used internally + } amr; + } u; } avc; #ifdef HAVE_BCG729 bcg729EncoderChannelContextStruct *bcg729;