#include "asterisk/frame.h"
#include "asterisk/linkedlists.h"
+/* For struct ast_rtp_rtcp_report and struct ast_rtp_rtcp_report_block */
+#include "asterisk/rtp_engine.h"
+
/* codec variables */
static int quality = 3;
static int complexity = 2;
static float vbr_quality = 4;
static int abr = 0;
static int dtx = 0; /* set to 1 to enable silence detection */
+static int exp_rtcp_fb = 0; /* set to 1 to use experimental RTCP feedback for changing bitrate */
static int preproc = 0;
static int pp_vad = 0;
SpeexBits bits;
int framesize;
int silent_state;
+
+ int fraction_lost;
+ int quality;
+ int default_quality;
+
#ifdef _SPEEX_TYPES_H
SpeexPreprocessState *pp;
spx_int16_t buf[BUFFER_SAMPLES];
speex_encoder_ctl(tmp->speex, SPEEX_SET_DTX, &dtx);
tmp->silent_state = 0;
+ tmp->fraction_lost = 0;
+ tmp->default_quality = vbr ? vbr_quality : quality;
+ tmp->quality = tmp->default_quality;
+ ast_debug(3, "Default quality (%s): %d\n", vbr ? "vbr" : "cbr", tmp->default_quality);
+
return 0;
}
return result;
}
+/*! \brief handle incoming RTCP feedback and possibly edit encoder settings */
+static void lintospeex_feedback(struct ast_trans_pvt *pvt, struct ast_frame *feedback)
+{
+ struct speex_coder_pvt *tmp = pvt->pvt;
+
+ struct ast_rtp_rtcp_report *rtcp_report;
+ struct ast_rtp_rtcp_report_block *report_block;
+
+ int fraction_lost;
+ int percent;
+ int bitrate;
+ int q;
+
+ if(!exp_rtcp_fb)
+ return;
+
+ rtcp_report = (struct ast_rtp_rtcp_report *)feedback->data.ptr;
+ if (rtcp_report->reception_report_count == 0)
+ return;
+ report_block = rtcp_report->report_block[0];
+ fraction_lost = report_block->lost_count.fraction;
+ if (fraction_lost == tmp->fraction_lost)
+ return;
+ /* Per RFC3550, fraction lost is defined to be the number of packets lost
+ * divided by the number of packets expected. Since it's a 8-bit value,
+ * and we want a percentage value, we multiply by 100 and divide by 256. */
+ percent = (fraction_lost*100)/256;
+ bitrate = 0;
+ q = -1;
+ ast_debug(3, "Fraction lost changed: %d --> %d percent loss\n", fraction_lost, percent);
+ /* Handle change */
+ speex_encoder_ctl(tmp->speex, SPEEX_GET_BITRATE, &bitrate);
+ ast_debug(3, "Current bitrate: %d\n", bitrate);
+ ast_debug(3, "Current quality: %d/%d\n", tmp->quality, tmp->default_quality);
+ /* FIXME BADLY Very ugly example of how this could be handled: probably sucks */
+ if (percent < 10) {
+ /* Not that bad, default quality is fine */
+ q = tmp->default_quality;
+ } else if (percent < 20) {
+ /* Quite bad, let's go down a bit */
+ q = tmp->default_quality-1;
+ } else if (percent < 30) {
+ /* Very bad, let's go down even more */
+ q = tmp->default_quality-2;
+ } else {
+ /* Really bad, use the lowest quality possible */
+ q = 0;
+ }
+ if (q < 0)
+ q = 0;
+ if (q != tmp->quality) {
+ ast_debug(3, " -- Setting to %d\n", q);
+ if (vbr) {
+ float vbr_q = q;
+ speex_encoder_ctl(tmp->speex, SPEEX_SET_VBR_QUALITY, &vbr_q);
+ } else {
+ speex_encoder_ctl(tmp->speex, SPEEX_SET_QUALITY, &q);
+ }
+ tmp->quality = q;
+ }
+ tmp->fraction_lost = fraction_lost;
+}
+
static void speextolin_destroy(struct ast_trans_pvt *arg)
{
struct speex_coder_pvt *pvt = arg->pvt;
.newpvt = lintospeex_new,
.framein = lintospeex_framein,
.frameout = lintospeex_frameout,
+ .feedback = lintospeex_feedback,
.destroy = lintospeex_destroy,
.sample = slin8_sample,
.desc_size = sizeof(struct speex_coder_pvt),
.newpvt = lin16tospeexwb_new,
.framein = lintospeex_framein,
.frameout = lintospeex_frameout,
+ .feedback = lintospeex_feedback,
.destroy = lintospeex_destroy,
.sample = slin16_sample,
.desc_size = sizeof(struct speex_coder_pvt),
.newpvt = lin32tospeexuwb_new,
.framein = lintospeex_framein,
.frameout = lintospeex_frameout,
+ .feedback = lintospeex_feedback,
.destroy = lintospeex_destroy,
.desc_size = sizeof(struct speex_coder_pvt),
.buffer_samples = BUFFER_SAMPLES,
pp_dereverb_level = res_f;
} else
ast_log(LOG_ERROR,"Error! Preprocessor Dereverb Level must be >= 0\n");
+ } else if (!strcasecmp(var->name, "experimental_rtcp_feedback")) {
+ exp_rtcp_fb = ast_true(var->value) ? 1 : 0;
+ ast_verb(3, "CODEC SPEEX: Experimental RTCP Feedback. [%s]\n",exp_rtcp_fb ? "on" : "off");
}
}
ast_config_destroy(cfg);
*
* As a minimum, a translator should supply name, srcfmt and dstfmt,
* the required buf_size (in bytes) and buffer_samples (in samples),
- * and a few callbacks (framein, frameout, sample).
+ * and a few callbacks (framein, frameout, feedback, sample).
* The outbuf is automatically prepended by AST_FRIENDLY_OFFSET
* spare bytes so generic routines can place data in there.
*
/*!< Output frame callback. Generate a frame
* with outbuf content. */
+ void (*feedback)(struct ast_trans_pvt *pvt, struct ast_frame *feedback);
+ /*!< Feedback frame callback. Handle
+ * input frame. */
+
void (*destroy)(struct ast_trans_pvt *pvt);
/*!< cleanup private data, if needed
* (often unnecessary). */
/*!
* \brief translates one or more frames
* Apply an input frame into the translator and receive zero or one output frames. Consume
- * determines whether the original frame should be freed
+ * determines whether the original frame should be freed. In case the frame type is
+ * AST_FRAME_RTCP, the frame is not translated but passed to the translator codecs
+ * via the feedback callback, and a pointer to ast_null_frame is returned after that.
* \param path tr translator structure to use for translation
* \param f frame to translate
* \param consume Whether or not to free the original frame
rtcp_report,
message_blob);
ast_json_unref(message_blob);
+
+ /* Return an AST_FRAME_RTCP frame with the ast_rtp_rtcp_report
+ * object as a its data */
+ rtp->f.frametype = AST_FRAME_RTCP;
+ rtp->f.data.ptr = rtp->rawdata + AST_FRIENDLY_OFFSET;
+ memcpy(rtp->f.data.ptr, rtcp_report, sizeof(struct ast_rtp_rtcp_report));
+ rtp->f.datalen = sizeof(struct ast_rtp_rtcp_report);
+ if (rc > 0) {
+ /* There's always a single report block stored, here */
+ struct ast_rtp_rtcp_report *rtcp_report2;
+ report_block = rtp->f.data.ptr + rtp->f.datalen + sizeof(struct ast_rtp_rtcp_report_block *);
+ memcpy(report_block, rtcp_report->report_block[report_counter-1], sizeof(struct ast_rtp_rtcp_report_block));
+ rtcp_report2 = (struct ast_rtp_rtcp_report *)rtp->f.data.ptr;
+ rtcp_report2->report_block[report_counter-1] = report_block;
+ rtp->f.datalen += sizeof(struct ast_rtp_rtcp_report_block);
+ }
+ rtp->f.offset = AST_FRIENDLY_OFFSET;
+ rtp->f.samples = 0;
+ rtp->f.mallocd = 0;
+ rtp->f.delivery.tv_sec = 0;
+ rtp->f.delivery.tv_usec = 0;
+ rtp->f.src = "RTP";
+ f = &rtp->f;
break;
case RTCP_PT_FUR:
/* Handle RTCP FIR as FUR */