<settings>
<param name="use-vbr" value="1"/>
<param name="complexity" value="10"/>
- <!-- Packet Loss Percent, Default 0 -->
- <!--<param name="packet-loss-percent" value="20"/>-->
<!--
maxaveragebitrate: the maximum average codec bitrate (values: 6000 to 510000 in bps) 0 is not considered
uint32_t jitter; /* An estimate of the statistical variance of the RTP data packet interarrival time, measured in timestamp units and expressed as an unsigned integer. */
uint32_t lsr; /* The middle 32 bits out of 64 in the NTP timestamp */
uint32_t dlsr; /* The delay, expressed in units of 1/65536 seconds, between receiving the last SR packet from source SSRC_n and sending this reception report block */
+ uint32_t loss_avg;
};
/*! \brief An abstraction of a rtcp frame */
#define stfu_n_resize(_i, _ql) _stfu_n_resize(_i, _ql, __LINE__)
stfu_status_t stfu_n_add_data(stfu_instance_t *i, uint32_t ts, uint16_t seq, uint32_t pt, void *data, size_t datalen, uint32_t timer_ts, int last);
stfu_frame_t *stfu_n_read_a_frame(stfu_instance_t *i);
-SWITCH_DECLARE(int32_t) stfu_n_copy_next_frame(stfu_instance_t *jb, uint32_t timestamp, uint16_t seq, uint16_t distance, stfu_frame_t *next_frame);
+SWITCH_DECLARE(int32_t) stfu_n_peek_frame(stfu_instance_t *jb, uint32_t timestamp, uint16_t seq, uint16_t distance, stfu_frame_t **rframe);
void _stfu_n_reset(stfu_instance_t *i, const char *file, const char *func, int line);
#define stfu_n_reset(_i) _stfu_n_reset(_i, STFU_PRE)
stfu_status_t stfu_n_sync(stfu_instance_t *i, uint32_t packets);
typedef enum {
SCC_VIDEO_REFRESH = 0,
SCC_VIDEO_BANDWIDTH,
- SCC_VIDEO_RESET
+ SCC_VIDEO_RESET,
+ SCC_AUDIO_PACKET_LOSS
} switch_codec_control_command_t;
typedef enum {
OpusDecoder *decoder_object;
uint32_t enc_frame_size;
uint32_t dec_frame_size;
- uint32_t counter_plc_fec;
+ uint32_t old_plpct;
+ opus_codec_settings_t codec_settings;
};
struct {
memset(&codec_fmtp, '\0', sizeof(struct switch_codec_fmtp));
codec_fmtp.private_info = &opus_codec_settings;
switch_opus_fmtp_parse(codec->fmtp_in, &codec_fmtp);
+ context->codec_settings = opus_codec_settings;
/* Verify if the local or remote configuration are lowering maxaveragebitrate and/or maxplaybackrate */
if ( opus_prefs.maxaveragebitrate && (opus_prefs.maxaveragebitrate < opus_codec_settings.maxaveragebitrate || !opus_codec_settings.maxaveragebitrate) ) {
return SWITCH_STATUS_GENERR;
}
- context->counter_plc_fec = 0;
}
codec->private_info = context;
if (context) {
if (context->decoder_object) {
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "tried PLC or FEC %d times \n", context->counter_plc_fec);
opus_decoder_destroy(context->decoder_object);
context->decoder_object = NULL;
}
unsigned int *flag)
{
struct opus_context *context = codec->private_info;
- switch_core_session_t *session = codec->session;
- stfu_instance_t *jb = NULL;
- stfu_frame_t next_frame;
int samples = 0;
- uint32_t frame_size;
+ int fec = 0, plc = 0;
+ int32_t frame_size;
uint32_t frame_samples;
- uint32_t found_frame;
if (!context) {
return SWITCH_STATUS_FALSE;
frame_samples = *decoded_data_len / 2 / codec->implementation->number_of_channels;
frame_size = frame_samples - (frame_samples % (codec->implementation->actual_samples_per_second / 400));
-
- if (*flag & SFF_PLC) {
- if (session) {
- jb = switch_core_session_get_jb(session, SWITCH_MEDIA_TYPE_AUDIO);
- }
-
- if (jb && codec->cur_frame) {
-
- found_frame = stfu_n_copy_next_frame(jb, (uint32_t)codec->cur_frame->timestamp, codec->cur_frame->seq, 1, &next_frame);
- if (found_frame) {
- samples = opus_decode(context->decoder_object, next_frame.data, next_frame.dlen, decoded_data, frame_size, 1);
+ if (*flag & SFF_PLC) {
+ plc = 1;
+ encoded_data = NULL;
+ opus_decoder_ctl(context->decoder_object, OPUS_GET_LAST_PACKET_DURATION(&frame_size));
- if (samples < 0 ) {
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Decoder Error (FEC): %s!\n", opus_strerror(samples));
- } else {
- context->counter_plc_fec++;
- *flag &= ~SFF_PLC;
- *decoded_data_len = samples * 2 * codec->implementation->number_of_channels;
- return SWITCH_STATUS_SUCCESS;
- }
- }
+ if (context->codec_settings.useinbandfec) {
+ fec = 1;
}
- }
- /* opus_decode() does PLC if there's no FEC in the packet*/
- samples = opus_decode(context->decoder_object, (*flag & SFF_PLC) ? NULL : encoded_data, encoded_data_len, decoded_data, frame_size, 0);
-
- if (*flag & SFF_PLC) {
- context->counter_plc_fec++;
*flag &= ~SFF_PLC;
}
+
+ samples = opus_decode(context->decoder_object, encoded_data, encoded_data_len, decoded_data, frame_size, fec);
if (samples < 0) {
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Decoder Error: %s fs:%u plc:%d!\n", opus_strerror(samples), frame_size, !!(*flag & SFF_PLC));
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Decoder Error: %s fs:%u plc:%s!\n",
+ opus_strerror(samples), frame_size, plc ? "true" : "false");
return SWITCH_STATUS_GENERR;
}
return status;
}
+static switch_status_t switch_opus_control(switch_codec_t *codec,
+ switch_codec_control_command_t cmd,
+ switch_codec_control_type_t ctype,
+ void *cmd_data,
+ switch_codec_control_type_t *rtype,
+ void **ret_data)
+{
+ struct opus_context *context = codec->private_info;
+
+ switch(cmd) {
+ case SCC_AUDIO_PACKET_LOSS:
+ {
+ uint32_t plpct = *((uint32_t *) cmd_data);
+ uint32_t calc;
+
+ if (plpct < 0) {
+ plpct = 0;
+ }
+
+ if (plpct > 100) {
+ plpct = 100;
+ }
+
+ calc = plpct % 10;
+ plpct = plpct - calc + ( calc ? 10 : 0);
+
+ if (plpct != context->old_plpct) {
+ opus_encoder_ctl(context->encoder_object, OPUS_SET_PACKET_LOSS_PERC(plpct));
+ }
+ context->old_plpct = plpct;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+
SWITCH_MODULE_LOAD_FUNCTION(mod_opus_load)
{
switch_codec_interface_t *codec_interface;
switch_opus_encode, /* function to encode raw data into encoded data */
switch_opus_decode, /* function to decode encoded data into raw data */
switch_opus_destroy); /* deinitalize a codec handle using this implementation */
+
+ codec_interface->implementations->codec_control = switch_opus_control;
settings.stereo = 1;
if (x < 2) {
switch_opus_encode, /* function to encode raw data into encoded data */
switch_opus_decode, /* function to decode encoded data into raw data */
switch_opus_destroy); /* deinitalize a codec handle using this implementation */
+ codec_interface->implementations->codec_control = switch_opus_control;
}
bytes *= 2;
samples *= 2;
return SWITCH_STATUS_SUCCESS;
}
+
+
+
/* For Emacs:
* Local Variables:
* mode:c
return SWITCH_STATUS_SUCCESS;
}
+static stfu_instance_t *rtc_get_jb(switch_core_session_t *session, switch_media_type_t type)
+{
+ private_object_t *tech_pvt = (private_object_t *) switch_core_session_get_private(session);
+
+ return switch_core_media_get_jb(tech_pvt->session, type);
+}
+
switch_io_routines_t rtc_io_routines = {
/*.outgoing_channel */ rtc_outgoing_channel,
/*.read_frame */ rtc_read_frame,
/*.read_video_frame */ rtc_read_video_frame,
/*.write_video_frame */ rtc_write_video_frame,
/*.state_run*/ NULL,
- /*.get_jb*/ NULL
+ /*.get_jb*/ rtc_get_jb
};
switch_state_handler_table_t rtc_event_handlers = {
snprintf(header, sizeof(header), "Source%u-Lost", i);
snprintf(value, sizeof(value), "%u", rtcp_frame.reports[i].lost);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, header, value);
+ snprintf(header, sizeof(header), "Source%u-Loss-Avg", i);
+ snprintf(value, sizeof(value), "%u", rtcp_frame.reports[i].loss_avg);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, header, value);
snprintf(header, sizeof(header), "Source%u-Highest-Sequence-Number-Received", i);
snprintf(value, sizeof(value), "%u", rtcp_frame.reports[i].highest_sequence_number_received);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, header, value);
switch_sockaddr_t *local_addr, *rtcp_local_addr;
rtp_msg_t send_msg;
rtcp_msg_t rtcp_send_msg;
+ switch_rtcp_frame_t rtcp_frame;
uint8_t fir_seq;
uint16_t fir_count;
static switch_status_t process_rtcp_report(switch_rtp_t *rtp_session, rtcp_msg_t *msg, switch_size_t bytes)
{
switch_status_t status = SWITCH_STATUS_FALSE;
-
+ int i;
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_DEBUG3,
"RTCP packet bytes %" SWITCH_SIZE_T_FMT " type %d pad %d\n",
ntohl(sr->sender_info.ts),
ntohl(sr->sender_info.pc),
ntohl(sr->sender_info.oc));
+
+
+ rtp_session->rtcp_frame.ssrc = ntohl(sr->ssrc);
+ rtp_session->rtcp_frame.packet_type = (uint16_t)rtp_session->rtcp_recv_msg_p->header.type;
+ rtp_session->rtcp_frame.ntp_msw = ntohl(sr->sender_info.ntp_msw);
+ rtp_session->rtcp_frame.ntp_lsw = ntohl(sr->sender_info.ntp_lsw);
+ rtp_session->rtcp_frame.timestamp = ntohl(sr->sender_info.ts);
+ rtp_session->rtcp_frame.packet_count = ntohl(sr->sender_info.pc);
+ rtp_session->rtcp_frame.octect_count = ntohl(sr->sender_info.oc);
+
+ for (i = 0; i < (int)msg->header.count && i < MAX_REPORT_BLOCKS ; i++) {
+ struct switch_rtcp_report_block *report = (struct switch_rtcp_report_block *) (msg->body + (sizeof(struct switch_rtcp_sr_head) + (i * sizeof(struct switch_rtcp_report_block))));
+ uint32_t old_avg = rtp_session->rtcp_frame.reports[i].loss_avg;
+
+ if (!rtp_session->rtcp_frame.reports[i].loss_avg) {
+ rtp_session->rtcp_frame.reports[i].loss_avg = (uint8_t)report->fraction;
+ } else {
+ rtp_session->rtcp_frame.reports[i].loss_avg = (uint32_t)(((float)rtp_session->rtcp_frame.reports[i].loss_avg * .7) +
+ ((float)(uint8_t)report->fraction * .3));
+ }
+
+ if (!rtp_session->flags[SWITCH_RTP_FLAG_VIDEO] && rtp_session->rtcp_frame.reports[i].loss_avg != old_avg) {
+ switch_core_media_codec_control(rtp_session->session, SWITCH_MEDIA_TYPE_AUDIO, SWITCH_IO_WRITE, SCC_AUDIO_PACKET_LOSS, SCCT_INT, (void *)&rtp_session->rtcp_frame.reports[i].loss_avg, NULL, NULL);
+ }
+
+ rtp_session->rtcp_frame.reports[i].ssrc = ntohl(report->ssrc);
+ rtp_session->rtcp_frame.reports[i].fraction = (uint8_t)report->fraction;
+ rtp_session->rtcp_frame.reports[i].lost = ntohl(report->lost);
+ rtp_session->rtcp_frame.reports[i].highest_sequence_number_received = ntohl(report->highest_sequence_number_received);
+ rtp_session->rtcp_frame.reports[i].jitter = ntohl(report->jitter);
+ rtp_session->rtcp_frame.reports[i].lsr = ntohl(report->lsr);
+ rtp_session->rtcp_frame.reports[i].dlsr = ntohl(report->dlsr);
+ }
+ rtp_session->rtcp_frame.report_count = (uint16_t)i;
+
} else { /* Receiver report */
struct switch_rtcp_receiver_report* rr = (struct switch_rtcp_receiver_report*)msg->body;
report_block = &rr->report_block;
packet_ssrc = rr->ssrc;
+ memset(&rtp_session->rtcp_frame, 0, sizeof(rtp_session->rtcp_frame));
}
/* Currently in passthru mode RTT will not be accurate, some work as to be done (something like mapping the NTP timestamp with a local one) to have RTT from both legs */
SWITCH_DECLARE(switch_status_t) switch_rtcp_zerocopy_read_frame(switch_rtp_t *rtp_session, switch_rtcp_frame_t *frame)
{
-
-
if (!rtp_session->flags[SWITCH_RTP_FLAG_ENABLE_RTCP]) {
return SWITCH_STATUS_FALSE;
}
/* A fresh frame has been found! */
if (rtp_session->rtcp_fresh_frame) {
- struct switch_rtcp_sender_report* sr = (struct switch_rtcp_sender_report*)rtp_session->rtcp_recv_msg_p->body;
- int i = 0;
-
/* turn the flag off! */
rtp_session->rtcp_fresh_frame = 0;
- frame->ssrc = ntohl(sr->ssrc);
- frame->packet_type = (uint16_t)rtp_session->rtcp_recv_msg_p->header.type;
- frame->ntp_msw = ntohl(sr->sender_info.ntp_msw);
- frame->ntp_lsw = ntohl(sr->sender_info.ntp_lsw);
- frame->timestamp = ntohl(sr->sender_info.ts);
- frame->packet_count = ntohl(sr->sender_info.pc);
- frame->octect_count = ntohl(sr->sender_info.oc);
-
- for (i = 0; i < (int)rtp_session->rtcp_recv_msg_p->header.count && i < MAX_REPORT_BLOCKS ; i++) {
- struct switch_rtcp_report_block* report = (struct switch_rtcp_report_block*) (rtp_session->rtcp_recv_msg_p->body + (sizeof(struct switch_rtcp_sr_head) + (i * sizeof(struct switch_rtcp_report_block))));
- frame->reports[i].ssrc = ntohl(report->ssrc);
- frame->reports[i].fraction = (uint8_t)report->fraction;
- frame->reports[i].lost = ntohl(report->lost);
- frame->reports[i].highest_sequence_number_received = ntohl(report->highest_sequence_number_received);
- frame->reports[i].jitter = ntohl(report->jitter);
- frame->reports[i].lsr = ntohl(report->lsr);
- frame->reports[i].dlsr = ntohl(report->dlsr);
- }
- frame->report_count = (uint16_t)i;
+ *frame = rtp_session->rtcp_frame;
return SWITCH_STATUS_SUCCESS;
}
return rframe;
}
-SWITCH_DECLARE(int32_t) stfu_n_copy_next_frame(stfu_instance_t *jb, uint32_t timestamp, uint16_t seq, uint16_t distance, stfu_frame_t *next_frame)
+SWITCH_DECLARE(int32_t) stfu_n_peek_frame(stfu_instance_t *jb, uint32_t timestamp, uint16_t seq, uint16_t distance, stfu_frame_t **rframe)
{
- uint32_t i = 0, j = 0;
-#ifdef WIN32
-#pragma warning (disable:4204)
-#endif
- stfu_queue_t *queues[] = { jb->out_queue, jb->in_queue, jb->old_queue};
-#ifdef WIN32
-#pragma warning (default:4204)
-#endif
- stfu_queue_t *queue = NULL;
+ uint32_t i = 0, qi = 0;
stfu_frame_t *frame = NULL;
+ uint16_t want_seq = seq + distance;
+ stfu_queue_t *queues[2] = {jb->out_queue, jb->in_queue};
- uint32_t target_ts = 0;
-
-#ifdef WIN32
- UNREFERENCED_PARAMETER(seq);
-#endif
- if (!next_frame) return 0;
-
- target_ts = timestamp + (distance - 1) * jb->samples_per_packet;
+ switch_assert(rframe);
- for (i = 0; i < sizeof(queues)/sizeof(queues[0]); i++) {
- queue = queues[i];
+ for (qi = 0; qi < 2; qi++) {
+ stfu_queue_t *queue = queues[qi];
- if (!queue) continue;
+ for(i = 0; i < queue->array_len; i++) {
+ frame = &queue->array[i];
- for(j = 0; j < queue->array_size; j++) {
- frame = &queue->array[j];
- /* FIXME: ts rollover happened? bad luck */
- if (frame->ts > target_ts) {
- memcpy(next_frame, frame, sizeof(stfu_frame_t));
+ if (frame->seq == want_seq) {
+ *rframe = frame;
return 1;
}
}