Begin adding full support for GOOG-REMB\nFix iOS 'broken pipe' issue when UDP sockets are invalidated by the OS

This commit is contained in:
bossiel 2015-08-12 20:36:14 +00:00
parent 295de8efda
commit 7908865936
23 changed files with 1665 additions and 1306 deletions

View File

@ -58,7 +58,15 @@ typedef struct tdav_session_av_s
tmedia_srtp_type_t srtp_type;
tmedia_srtp_mode_t srtp_mode;
struct {
uint64_t count_last_time;
uint64_t count;
} bytes_in;
struct {
uint64_t count_last_time;
uint64_t count;
} bytes_out;
uint64_t time_last_frame_loss_report; // from jb
int32_t bandwidth_max_upload_kbps;
int32_t bandwidth_max_download_kbps;
int32_t fps;

View File

@ -164,6 +164,8 @@ static int tdav_codec_vp8_set(tmedia_codec_t* self, const tmedia_param_t* param)
else {
TMEDIA_CODEC(vp8)->bandwidth_max_upload = max_bw_new;
}
vp8->encoder.cfg.rc_target_bitrate = TSK_CLAMP(0, vp8->encoder.cfg.rc_target_bitrate, TMEDIA_CODEC(vp8)->bandwidth_max_upload);
TSK_DEBUG_INFO("New target bitrate = %d kbps", vp8->encoder.cfg.rc_target_bitrate);
reconf = tsk_true;
}
else if (tsk_striequals(param->key, "bandwidth-max-upload")) {

View File

@ -765,6 +765,9 @@ int tdav_session_av_stop(tdav_session_av_t* self)
}
}
self->bytes_in.count_last_time = self->bytes_out.count_last_time = 0;
self->bytes_in.count = self->bytes_out.count = 0;
return ret;
}

View File

@ -1,29 +1,29 @@
/*
* Copyright (C) 2010-2014 Mamadou DIOP.
* Copyright (C) 2011-2014 Doubango Telecom.
*
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DOUBANGO is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DOUBANGO.
*
*/
* Copyright (C) 2010-2014 Mamadou DIOP.
* Copyright (C) 2011-2014 Doubango Telecom.
*
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DOUBANGO is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DOUBANGO.
*
*/
/**@file tdav_session_video.c
* @brief Video Session plugin.
*
*/
* @brief Video Session plugin.
*
*/
#include "tinydav/video/tdav_session_video.h"
#include "tinydav/video/tdav_converter_video.h"
#include "tinydav/video/jb/tdav_video_jb.h"
@ -70,69 +70,79 @@
#define TDAV_SESSION_VIDEO_PKT_LOSS_MEDIUM 22
#define TDAV_SESSION_VIDEO_PKT_LOSS_HIGH 63
#if !defined(TDAV_SESSION_VIDEO_PKT_LOSS_NO_REPORT_BEFORE_INCREASING_BW)
# define TDAV_SESSION_VIDEO_PKT_LOSS_NO_REPORT_BEFORE_INCREASING_BW 5000 // millis
#endif
// The maximum number of pakcet loss allowed
#define TDAV_SESSION_VIDEO_PKT_LOSS_MAX_COUNT_TO_REQUEST_FIR 50
#if !defined (TDAV_GOOG_REMB_FULL_SUPPORT)
# define TDAV_GOOG_REMB_FULL_SUPPORT 0
#endif
static const tmedia_codec_action_t __action_encode_idr = tmedia_codec_action_encode_idr;
static const tmedia_codec_action_t __action_encode_bw_up = tmedia_codec_action_bw_up;
static const tmedia_codec_action_t __action_encode_bw_down = tmedia_codec_action_bw_down;
// FIXME: lock ?
#define _tdav_session_video_codec_set(__self, key, value) \
#define _tdav_session_video_codec_set(__self, __key, __value) \
{ \
static tmedia_param_t* __param = tsk_null; \
if(!__param){ \
__param = tmedia_param_create(tmedia_pat_set, \
tmedia_video, \
tmedia_ppt_codec, \
tmedia_pvt_int32, \
"action", \
(void*)&value); \
} \
if((__self)->encoder.codec && __param){ \
/*tsk_mutex_lock((__self)->encoder.h_mutex);*/ \
if(TDAV_SESSION_AV(__self)->producer && TDAV_SESSION_AV(__self)->producer->encoder.codec_id == (__self)->encoder.codec->id) { /* Whether the producer ourput encoded frames */ \
tmedia_producer_set(TDAV_SESSION_AV(__self)->producer, __param); \
} \
else { \
tmedia_codec_set((tmedia_codec_t*)(__self)->encoder.codec, __param); \
static tmedia_param_t* __param = tsk_null; \
if(!__param){ \
__param = tmedia_param_create(tmedia_pat_set, \
tmedia_video, \
tmedia_ppt_codec, \
tmedia_pvt_int32, \
__key, \
(void*)&__value); \
} \
/*tsk_mutex_unlock((__self)->encoder.h_mutex);*/ \
} \
/* TSK_OBJECT_SAFE_FREE(param); */ \
if((__self)->encoder.codec && __param){ \
/*tsk_mutex_lock((__self)->encoder.h_mutex);*/ \
if(TDAV_SESSION_AV(__self)->producer && TDAV_SESSION_AV(__self)->producer->encoder.codec_id == (__self)->encoder.codec->id) { /* Whether the producer ourput encoded frames */ \
tmedia_producer_set(TDAV_SESSION_AV(__self)->producer, __param); \
} \
else { \
tmedia_codec_set((tmedia_codec_t*)(__self)->encoder.codec, __param); \
} \
/*tsk_mutex_unlock((__self)->encoder.h_mutex);*/ \
} \
/* TSK_OBJECT_SAFE_FREE(param); */ \
}
#define _tdav_session_video_remote_requested_idr(__self, __ssrc_media) { \
uint64_t __now = tsk_time_now(); \
tsk_bool_t too_close = tsk_false; \
if((__now - (__self)->avpf.last_fir_time) > TDAV_SESSION_VIDEO_AVPF_FIR_HONOR_INTERVAL_MIN){ /* guard to avoid sending too many FIR */ \
_tdav_session_video_codec_set((__self), "action", __action_encode_idr); \
}else { too_close = tsk_true; TSK_DEBUG_INFO("***IDR request tooo close(%llu ms)...ignoring****", (__now - (__self)->avpf.last_fir_time)); } \
if((__self)->cb_rtcpevent.func){ \
(__self)->cb_rtcpevent.func((__self)->cb_rtcpevent.context, tmedia_rtcp_event_type_fir, (__ssrc_media)); \
} \
if (!too_close) { /* if too close don't update "last_fir_time" to "now" to be sure interval will increase */ \
(__self)->avpf.last_fir_time = __now; \
} \
uint64_t __now = tsk_time_now(); \
tsk_bool_t too_close = tsk_false; \
if((__now - (__self)->avpf.last_fir_time) > TDAV_SESSION_VIDEO_AVPF_FIR_HONOR_INTERVAL_MIN){ /* guard to avoid sending too many FIR */ \
_tdav_session_video_codec_set((__self), "action", __action_encode_idr); \
}else { too_close = tsk_true; TSK_DEBUG_INFO("***IDR request tooo close(%llu ms)...ignoring****", (__now - (__self)->avpf.last_fir_time)); } \
if((__self)->cb_rtcpevent.func){ \
(__self)->cb_rtcpevent.func((__self)->cb_rtcpevent.context, tmedia_rtcp_event_type_fir, (__ssrc_media)); \
} \
if (!too_close) { /* if too close don't update "last_fir_time" to "now" to be sure interval will increase */ \
(__self)->avpf.last_fir_time = __now; \
} \
}
#define _tdav_session_video_local_request_idr(_session, _reason, _ssrc) \
{ \
tdav_session_av_t* _base = (tdav_session_av_t*)_session; \
if ((_base)->avpf_mode_neg || (_base)->is_fb_fir_neg) { \
/*return*/ trtp_manager_signal_frame_corrupted((_base)->rtp_manager, _ssrc); \
} \
else if ((_session)->rfc5168_cb.fun) { \
/*return*/ (_session)->rfc5168_cb.fun((_session)->rfc5168_cb.usrdata, (_session), (_reason), tmedia_session_rfc5168_cmd_picture_fast_update); \
tdav_session_av_t* _base = (tdav_session_av_t*)_session; \
if ((_base)->avpf_mode_neg || (_base)->is_fb_fir_neg) { \
/*return*/ trtp_manager_signal_frame_corrupted((_base)->rtp_manager, _ssrc); \
} \
else if ((_session)->rfc5168_cb.fun) { \
/*return*/ (_session)->rfc5168_cb.fun((_session)->rfc5168_cb.usrdata, (_session), (_reason), tmedia_session_rfc5168_cmd_picture_fast_update); \
} \
}
#define _tdav_session_video_bw_up(__self) _tdav_session_video_codec_set(__self, "action", __action_encode_bw_up)
#define _tdav_session_video_bw_down(__self) _tdav_session_video_codec_set(__self, "action", __action_encode_bw_down)
#define _tdav_session_video_bw_kbps(__self, __bw_kbps) _tdav_session_video_codec_set(__self, "bw_kbps", __bw_kbps)
#define _tdav_session_video_reset_loss_prob(__self) \
{ \
(__self)->encoder.pkt_loss_level = tdav_session_video_pkt_loss_level_low; \
(__self)->encoder.pkt_loss_prob_bad = TDAV_SESSION_VIDEO_PKT_LOSS_PROB_BAD; \
(__self)->encoder.pkt_loss_prob_good = TDAV_SESSION_VIDEO_PKT_LOSS_PROB_GOOD; \
(__self)->encoder.pkt_loss_level = tdav_session_video_pkt_loss_level_low; \
(__self)->encoder.pkt_loss_prob_bad = TDAV_SESSION_VIDEO_PKT_LOSS_PROB_BAD; \
(__self)->encoder.pkt_loss_prob_good = TDAV_SESSION_VIDEO_PKT_LOSS_PROB_GOOD; \
}
static int _tdav_session_video_set_defaults(tdav_session_video_t* self);
@ -227,12 +237,20 @@ static int tdav_session_video_raw_cb(const tmedia_video_encode_result_xt* result
s = trtp_manager_send_rtp_packet(base->rtp_manager, packet, tsk_false); // encrypt and send data
++base->rtp_manager->rtp.seq_num; // seq_num must be incremented here (before the bail) because already used by SRTP context
if(s < TRTP_RTP_HEADER_MIN_SIZE) {
TSK_DEBUG_ERROR("Failed to send packet with seqnum=%u. %u expected but only %u sent", (unsigned)packet->header->seq_num, (unsigned)packet->payload.size, (unsigned)s);
goto bail;
// without audio session iOS "audio" background mode is useless and UDP sockets will be closed: e.g. GE's video-only sessions
#if TDAV_UNDER_IPHONE
if (tnet_geterrno() == TNET_ERROR_BROKENPIPE) {
TSK_DEBUG_INFO("iOS UDP pipe is broken (restoration is progress): failed to send packet with seqnum=%u. %u expected but only %u sent", (unsigned)packet->header->seq_num, (unsigned)packet->payload.size, (unsigned)s);
}
#endif /* TDAV_UNDER_IPHONE */
TSK_DEBUG_ERROR("Failed to send packet with seqnum=%u. %u expected but only %u sent", (unsigned)packet->header->seq_num, (unsigned)packet->payload.size, (unsigned)s);
// save data expected to be sent in order to honor RTCP-NACK requests
s = base->rtp_manager->rtp.serial_buffer.index;
}
rtp_hdr_size = TRTP_RTP_HEADER_MIN_SIZE + (packet->header->csrc_count << 2);
// Save packet
if(base->avpf_mode_neg){
if (base->avpf_mode_neg && (s > TRTP_RTP_HEADER_MIN_SIZE)) {
trtp_rtp_packet_t* packet_avpf = tsk_object_ref(packet);
// when SRTP is used, "serial_buffer" will contains the encoded buffer with both RTP header and payload
// Hack the RTP packet payload to point to the the SRTP data instead of unencrypted ptr
@ -285,7 +303,7 @@ static int tdav_session_video_raw_cb(const tmedia_video_encode_result_xt* result
}
#if 0
// Send RED Packet
if(ret == 0 && video->red.codec){
if (ret == 0 && video->red.codec) {
// don't need to lock as the buffer is never used by other codecs
tsk_size_t red_pay_size = video->red.codec->plugin->encode(
video->red.codec,
@ -302,7 +320,7 @@ static int tdav_session_video_raw_cb(const tmedia_video_encode_result_xt* result
}
#endif
}
else{
else {
TSK_DEBUG_ERROR("Failed to create packet");
}
}
@ -404,7 +422,7 @@ static int tdav_session_video_producer_enc_cb(const void* callback_data, const v
#define PRODUCER_OUTPUT_FIXSIZE (base->producer->video.chroma != tmedia_chroma_mjpeg) // whether the output data has a fixed size/length
#define PRODUCER_OUTPUT_RAW (base->producer->encoder.codec_id == tmedia_codec_id_none) // Otherwise, frames from the producer are already encoded
#define PRODUCER_SIZE_CHANGED ((video->conv.producerWidth && video->conv.producerWidth != base->producer->video.width) || (video->conv.producerHeight && video->conv.producerHeight != base->producer->video.height) \
|| (video->conv.xProducerSize && (video->conv.xProducerSize != size && PRODUCER_OUTPUT_FIXSIZE)))
|| (video->conv.xProducerSize && (video->conv.xProducerSize != size && PRODUCER_OUTPUT_FIXSIZE)))
#define ENCODED_NEED_FLIP (TMEDIA_CODEC_VIDEO(codec_encoder)->out.flip)
#define ENCODED_NEED_RESIZE (base->producer->video.width != TMEDIA_CODEC_VIDEO(codec_encoder)->out.width || base->producer->video.height != TMEDIA_CODEC_VIDEO(codec_encoder)->out.height)
#define PRODUCED_FRAME_NEED_ROTATION (base->producer->video.rotation != 0)
@ -483,7 +501,7 @@ static int tdav_session_video_producer_enc_cb(const void* callback_data, const v
/* Never called, see tdav_session_video_raw_cb() */
trtp_manager_send_rtp(base->rtp_manager, video->encoder.buffer, out_size, 6006, tsk_true, tsk_true);
}
bail:
bail:
TSK_OBJECT_SAFE_FREE(codec_encoder);
}
else {
@ -539,6 +557,7 @@ static int tdav_session_video_rtp_cb(const void* callback_data, const trtp_rtp_p
// RTCP callback (Network -> This)
static int tdav_session_video_rtcp_cb(const void* callback_data, const trtp_rtcp_packet_t* packet)
{
int ret = 0;
const trtp_rtcp_report_psfb_t* psfb;
const trtp_rtcp_report_rtpfb_t* rtpfb;
const trtp_rtcp_rblocks_L_t* blocks = tsk_null;
@ -555,13 +574,14 @@ static int tdav_session_video_rtcp_cb(const void* callback_data, const trtp_rtcp
if(!(block = item->data)) continue;
if(base->rtp_manager->rtp.ssrc.local == block->ssrc){
tdav_session_video_pkt_loss_level_t pkt_loss_level = tdav_session_video_pkt_loss_level_low;
TSK_DEBUG_INFO("RTCP pkt loss fraction=%d", block->fraction);
if(block->fraction > TDAV_SESSION_VIDEO_PKT_LOSS_HIGH) pkt_loss_level = tdav_session_video_pkt_loss_level_high;
else if(block->fraction > TDAV_SESSION_VIDEO_PKT_LOSS_MEDIUM) pkt_loss_level = tdav_session_video_pkt_loss_level_medium;
if(pkt_loss_level == tdav_session_video_pkt_loss_level_high || (pkt_loss_level > video->encoder.pkt_loss_level)){ // high or low -> medium
if (pkt_loss_level == tdav_session_video_pkt_loss_level_high || (pkt_loss_level > video->encoder.pkt_loss_level)){ // high or low -> medium
video->encoder.pkt_loss_level = pkt_loss_level;
if(video->encoder.pkt_loss_prob_bad-- <= 0){
int32_t new_pkt_loss_fact = TSK_CLAMP(TDAV_SESSION_VIDEO_PKT_LOSS_FACT_MIN, (video->encoder.pkt_loss_fact + 1), TDAV_SESSION_VIDEO_PKT_LOSS_FACT_MAX);
if(video->encoder.pkt_loss_fact != new_pkt_loss_fact){
if (video->encoder.pkt_loss_fact != new_pkt_loss_fact) {
TSK_DEBUG_INFO("Downgrade bandwidth %d->%d", video->encoder.pkt_loss_fact, new_pkt_loss_fact);
video->encoder.pkt_loss_fact = new_pkt_loss_fact;
_tdav_session_video_bw_down(video);
@ -570,9 +590,9 @@ static int tdav_session_video_rtcp_cb(const void* callback_data, const trtp_rtcp
}
}
else{
if(video->encoder.pkt_loss_prob_good-- <= 0){
if (video->encoder.pkt_loss_prob_good-- <= 0) {
int32_t new_pkt_loss_fact = TSK_CLAMP(TDAV_SESSION_VIDEO_PKT_LOSS_FACT_MIN, (video->encoder.pkt_loss_fact - 1), TDAV_SESSION_VIDEO_PKT_LOSS_FACT_MAX);
if(video->encoder.pkt_loss_fact != new_pkt_loss_fact){
if (video->encoder.pkt_loss_fact != new_pkt_loss_fact) {
TSK_DEBUG_INFO("Upgrade bandwidth %d->%d", video->encoder.pkt_loss_fact, new_pkt_loss_fact);
video->encoder.pkt_loss_fact = new_pkt_loss_fact;
_tdav_session_video_bw_up(video);
@ -609,10 +629,60 @@ static int tdav_session_video_rtcp_cb(const void* callback_data, const trtp_rtcp
}
case trtp_rtcp_psfb_fci_type_afb:
{
if(psfb->afb.type == trtp_rtcp_psfb_afb_type_remb){
uint32_t bandwidth = ((psfb->afb.remb.mantissa << psfb->afb.remb.exp) / 1024);
TSK_DEBUG_INFO("Receiving RTCP-AFB-REMB (%u), exp=%u, mantissa=%u, bandwidth = %ukbps", ((const trtp_rtcp_report_fb_t*)psfb)->ssrc_media, psfb->afb.remb.exp, psfb->afb.remb.mantissa, bandwidth);
if (psfb->afb.type == trtp_rtcp_psfb_afb_type_remb) {
uint32_t bandwidth_up_reported_kpbs = ((psfb->afb.remb.mantissa << psfb->afb.remb.exp) / 1024);
TSK_DEBUG_INFO("Receiving RTCP-AFB-REMB (%u), exp=%u, mantissa=%u, bandwidth = %ukbps, congestion_ctrl_enabled=%s", ((const trtp_rtcp_report_fb_t*)psfb)->ssrc_media, psfb->afb.remb.exp, psfb->afb.remb.mantissa, bandwidth_up_reported_kpbs, base->congestion_ctrl_enabled ? "yes" : "no");
#if TDAV_GOOG_REMB_FULL_SUPPORT
if (base->congestion_ctrl_enabled) {
uint32_t remb_upload_kbps = 0;
tsk_bool_t remb_ok = tsk_false;
uint64_t bytes_count_now;
uint64_t bytes_count_out;
static uint64_t* bytes_count_in_ptr_null = tsk_null;
if ((ret = trtp_manager_get_bytes_count(base->rtp_manager, bytes_count_in_ptr_null, &bytes_count_out)) == 0) {
uint64_t duration;
bytes_count_now = tsk_time_now();
duration = (bytes_count_now - base->bytes_out.count_last_time);
remb_ok = (base->bytes_out.count_last_time != 0 && duration > 0);
if (remb_ok) {
remb_upload_kbps = (int32_t)((((bytes_count_out - base->bytes_out.count) * 8 * 1000) / 1024) / duration);
TSK_DEBUG_INFO("remb_upload_kbps=%u, bandwidth_up_reported_kpbs=%u", remb_upload_kbps, bandwidth_up_reported_kpbs);
}
base->bytes_out.count_last_time = bytes_count_now;
base->bytes_out.count = bytes_count_out;
}
if (remb_ok) {
int32_t pkt_loss_percent = bandwidth_up_reported_kpbs >= remb_upload_kbps ? 0 : ((remb_upload_kbps - bandwidth_up_reported_kpbs) / remb_upload_kbps) * 100;
TSK_DEBUG_INFO("GOO-REMB: pkt_loss_percent=%d", pkt_loss_percent);
if (pkt_loss_percent > 5) {
// more than 5% pkt loss
TSK_DEBUG_WARN("pkt_loss_percent(%u) > 5%%, using lower bw(%d)", pkt_loss_percent, bandwidth_up_reported_kpbs);
_tdav_session_video_bw_kbps(video, bandwidth_up_reported_kpbs);
}
else if (pkt_loss_percent == 0) {
#if 0
// no pkt loss --> increase bw
int32_t target_bw_max_upload_kbps = base->bandwidth_max_upload_kbps; // user-defined (guard), INT_MAX if not defined
if (video->encoder.codec) {
target_bw_max_upload_kbps = TSK_MIN(
tmedia_get_video_bandwidth_kbps_2(TMEDIA_CODEC_VIDEO(video->encoder.codec)->out.width, TMEDIA_CODEC_VIDEO(video->encoder.codec)->out.height, TMEDIA_CODEC_VIDEO(video->encoder.codec)->out.fps),
target_bw_max_upload_kbps);
}
if (target_bw_max_upload_kbps > remb_upload_kbps + ((remb_upload_kbps / 100) * 20)) {
// target (best) bw is 20% less than what we're sending --> increase by 5%
uint32_t new_upload_kbps = remb_upload_kbps + ((remb_upload_kbps / 100) * 5);
TSK_DEBUG_INFO("current upload bw is too low, increasing from %u to %u", remb_upload_kbps, new_upload_kbps);
_tdav_session_video_bw_kbps(video, new_upload_kbps);
}
#endif /* 0 */
}
}
}
#else
// for now we just don't respect the requested bandwidth
#endif /* TDAV_GOOG_REMB_FULL_SUPPORT */
}
break;
}
@ -680,7 +750,7 @@ static int tdav_session_video_rtcp_cb(const void* callback_data, const trtp_rtcp
}// switch
}// while(rtcp-pkt)
return 0;
return ret;
}
static int _tdav_session_video_set_defaults(tdav_session_video_t* self)
@ -720,10 +790,12 @@ static int _tdav_session_video_jb_cb(const tdav_video_jb_cb_data_xt* data)
}
case tdav_video_jb_cb_data_type_tmfr:
{
base->time_last_frame_loss_report = tsk_time_now();
_tdav_session_video_local_request_idr(session, "TMFR", data->ssrc);
}
case tdav_video_jb_cb_data_type_fl:
{
base->time_last_frame_loss_report = tsk_time_now();
if(data->fl.count > TDAV_SESSION_VIDEO_PKT_LOSS_MAX_COUNT_TO_REQUEST_FIR){
_tdav_session_video_local_request_idr(session, "TMFR", data->ssrc);
}
@ -803,7 +875,7 @@ static int _tdav_session_video_decode(tdav_session_video_t* self, const trtp_rtp
tsk_safeobj_lock(base);
if(self->started && base->consumer && base->consumer->is_started){
if (self->started && base->consumer && base->consumer->is_started) {
tsk_size_t out_size, _size;
const void* _buffer;
tdav_session_video_t* video = (tdav_session_video_t*)base;
@ -929,28 +1001,60 @@ static int _tdav_session_video_decode(tdav_session_video_t* self, const trtp_rtp
// - congestion control is enabled and
// - fps changed or
// - first frame or
// - approximately every 5 minutes (300 = 60 * 5)
if(base->congestion_ctrl_enabled && base->rtp_manager && (self->fps_changed || self->decoder.codec_decoded_frames_count == 0 || ((self->decoder.codec_decoded_frames_count % (TMEDIA_CODEC_VIDEO(self->decoder.codec)->in.fps * 300)) == 0))){
// - approximately every 1 seconds (1 = 1 * 1)
if (base->congestion_ctrl_enabled && base->rtp_manager && (self->fps_changed || self->decoder.codec_decoded_frames_count == 0 || ((self->decoder.codec_decoded_frames_count % (TMEDIA_CODEC_VIDEO(self->decoder.codec)->in.fps * 1)) == 0))){
int32_t bandwidth_max_upload_kbps = base->bandwidth_max_upload_kbps;
int32_t bandwidth_max_download_kbps = base->bandwidth_max_download_kbps;
int32_t bandwidth_max_download_kbps = base->bandwidth_max_download_kbps; // user-defined (guard), INT_MAX if not defined
// bandwidth already computed in start() but the decoded video size was not correct and based on the SDP negotiation
bandwidth_max_download_kbps = TSK_MIN(
tmedia_get_video_bandwidth_kbps_2(TMEDIA_CODEC_VIDEO(self->decoder.codec)->in.width, TMEDIA_CODEC_VIDEO(self->decoder.codec)->in.height, TMEDIA_CODEC_VIDEO(self->decoder.codec)->in.fps),
bandwidth_max_download_kbps);
if(self->encoder.codec){
if (self->encoder.codec) {
bandwidth_max_upload_kbps = TSK_MIN(
tmedia_get_video_bandwidth_kbps_2(TMEDIA_CODEC_VIDEO(self->encoder.codec)->out.width, TMEDIA_CODEC_VIDEO(self->encoder.codec)->out.height, TMEDIA_CODEC_VIDEO(self->encoder.codec)->out.fps),
bandwidth_max_upload_kbps);
}
#if TDAV_GOOG_REMB_FULL_SUPPORT
{
tsk_bool_t remb_ok = tsk_false;
int32_t remb_download_kbps = 0;
uint64_t now = 0;
uint64_t bytes_count_in;
static uint64_t* bytes_count_out_ptr_null = tsk_null;
if ((ret = trtp_manager_get_bytes_count(base->rtp_manager, &bytes_count_in, bytes_count_out_ptr_null)) == 0) {
uint64_t duration;
now = tsk_time_now();
duration = (now - base->bytes_in.count_last_time);
remb_ok = (base->bytes_in.count_last_time != 0 && duration > 0);
if (remb_ok) {
remb_download_kbps = (int32_t)((((bytes_count_in - base->bytes_in.count) * 8 * 1000) / 1024) / duration);
TSK_DEBUG_INFO("remb_download_kbps=%d", remb_download_kbps);
}
base->bytes_in.count_last_time = now;
base->bytes_in.count = bytes_count_in;
}
if (remb_ok) {
// if "remb_ok" is true then "now" has a valid value
if ((now - base->time_last_frame_loss_report) > TDAV_SESSION_VIDEO_PKT_LOSS_NO_REPORT_BEFORE_INCREASING_BW) {
TSK_DEBUG_INFO("No pakt loss since %d millis ... adding 5%% to the estimated max bandwidth", TDAV_SESSION_VIDEO_PKT_LOSS_NO_REPORT_BEFORE_INCREASING_BW);
remb_download_kbps += (remb_download_kbps / 100) * 5; // add 5% to the estimated bandwidth
}
// CLAMP is used to make sure we will not report more than what the user defined as max values even if the estimated values are higher
bandwidth_max_download_kbps = TSK_CLAMP(0, remb_download_kbps, bandwidth_max_download_kbps);
}
}
#endif /* TDAV_GOOG_REMB_FULL_SUPPORT */
self->fps_changed = tsk_false; // reset
TSK_DEBUG_INFO("video with congestion control enabled: max_bw_up=%d kpbs, max_bw_down=%d kpbs", bandwidth_max_upload_kbps, bandwidth_max_download_kbps);
ret = trtp_manager_set_app_bandwidth_max(base->rtp_manager, bandwidth_max_upload_kbps, bandwidth_max_download_kbps);
TSK_DEBUG_INFO("video with congestion control enabled: max_bw_up(unused)=%d kpbs, max_bw_down=%d kpbs", bandwidth_max_upload_kbps, bandwidth_max_download_kbps);
ret = trtp_manager_set_app_bandwidth_max(base->rtp_manager, bandwidth_max_upload_kbps/* unused */, bandwidth_max_download_kbps);
}
// inc() frame count and consume decoded video
++self->decoder.codec_decoded_frames_count;
ret = tmedia_consumer_consume(base->consumer, _buffer, _size, __rtp_header);
}
else if(!base->consumer || !base->consumer->is_started){
else if (!base->consumer || !base->consumer->is_started) {
TSK_DEBUG_INFO("Consumer not started (is_null=%d)", !base->consumer);
}
@ -1112,7 +1216,6 @@ static int tdav_session_video_start(tmedia_session_t* self)
tsk_mutex_lock(video->encoder.h_mutex);
TSK_OBJECT_SAFE_FREE(video->encoder.codec);
video->encoder.codec = tsk_object_ref((tsk_object_t*)codec);
// initialize the encoder using user-defined values
if ((ret = tdav_session_av_init_encoder(base, video->encoder.codec))) {
TSK_DEBUG_ERROR("Failed to initialize the encoder [%s] codec", video->encoder.codec->plugin->desc);
@ -1219,6 +1322,36 @@ static int tdav_session_video_set_ro(tmedia_session_t* self, const tsdp_header_M
return ret;
}
// Check if "RTCP-NACK" and "RTC-FIR" are supported
{
const tmedia_codec_t* codec;
base->is_fb_fir_neg = base->is_fb_nack_neg = base->is_fb_googremb_neg = tsk_false;
if ((codec = tdav_session_av_get_best_neg_codec(base))) {
// a=rtcp-fb:* ccm fir
// a=rtcp-fb:* nack
// a=rtcp-fb:* goog-remb
char attr_fir[256], attr_nack[256], attr_goog_remb[256];
int index = 0;
const tsdp_header_A_t* A;
sprintf(attr_fir, "%s ccm fir", codec->neg_format);
sprintf(attr_nack, "%s nack", codec->neg_format);
sprintf(attr_goog_remb, "%s goog-remb", codec->neg_format);
while ((A = tsdp_header_M_findA_at(m, "rtcp-fb", index++))) {
if (!base->is_fb_fir_neg) {
base->is_fb_fir_neg = (tsk_striequals(A->value, "* ccm fir") || tsk_striequals(A->value, attr_fir));
}
if (!base->is_fb_nack_neg) {
base->is_fb_nack_neg = (tsk_striequals(A->value, "* nack") || tsk_striequals(A->value, attr_nack));
}
if (!base->is_fb_googremb_neg) {
base->is_fb_googremb_neg = (tsk_striequals(A->value, "* goog-remb") || tsk_striequals(A->value, attr_goog_remb));
}
}
}
}
if (updated) {
// set callbacks
ret = _tdav_session_video_set_callbacks(self);

View File

@ -1,7 +1,5 @@
/*
* Copyright (C) 2010-2011 Mamadou Diop.
*
* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
* Copyright (C) 2010-2015 Mamadou DIOP
*
* This file is part of Open Source Doubango Framework.
*
@ -25,8 +23,5 @@
* http://tools.ietf.org/html/draft-ietf-mmusic-ice-19
* http://tools.ietf.org/html/draft-ietf-mmusic-ice-tcp-08
*
* @author Mamadou Diop <diopmamadou(at)doubango.org>
*
*/
#include "tnet_ice.h"

View File

@ -1,7 +1,5 @@
/*
* Copyright (C) 2010-2011 Mamadou Diop.
*
* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
* Copyright (C) 2010-2015 Mamadou DIOP
*
* This file is part of Open Source Doubango Framework.
*
@ -25,9 +23,6 @@
* http://tools.ietf.org/html/draft-ietf-mmusic-ice-19
* http://tools.ietf.org/html/draft-ietf-mmusic-ice-tcp-08
*
* @author Mamadou Diop <diopmamadou(at)doubango.org>
*
*/
#ifndef TNET_ICE_H
#define TNET_ICE_H

View File

@ -997,6 +997,26 @@ int tnet_ice_ctx_send_turn_rtcp(struct tnet_ice_ctx_s* self, const void* data, t
: _tnet_ice_ctx_send_turn_raw(self, self->turn.ss_nominated_rtcp, self->turn.peer_id_rtcp, data, size);
}
int tnet_ice_ctx_turn_get_bytes_count(const struct tnet_ice_ctx_s* self, uint64_t* bytes_in, uint64_t* bytes_out)
{
int ret;
if (!self) {
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
ret = tnet_turn_session_get_bytes_count(self->turn.ss_nominated_rtp, bytes_in, bytes_out);
if (ret == 0 && !self->use_rtcpmux) {
uint64_t _bytes_in, _bytes_out;
ret = tnet_turn_session_get_bytes_count(self->turn.ss_nominated_rtcp, &_bytes_in, &_bytes_out);
if (ret == 0) {
if (bytes_in) *bytes_in += _bytes_in;
if (bytes_out) *bytes_out += _bytes_out;
}
}
return ret;
}
const char* tnet_ice_ctx_get_ufrag(const struct tnet_ice_ctx_s* self)
{
return (self && self->ufrag) ? self->ufrag : tsk_null;

View File

@ -1,7 +1,5 @@
/*
* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org>.
*
* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
* Copyright (C) 2012-2015 Doubango Telecom <http://www.doubango.org>.
*
* This file is part of Open Source Doubango Framework.
*
@ -22,7 +20,6 @@
/**@file tnet_ice_ctx.h
* @brief Interactive Connectivity Establishment (ICE) implementation as per RFC 5245.
* @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
*/
#ifndef TNET_ICE_CTX_H
@ -100,6 +97,7 @@ TINYNET_API int tnet_ice_ctx_recv_stun_message(struct tnet_ice_ctx_s* self, cons
TINYNET_API int tnet_ice_ctx_send_turn_rtp(struct tnet_ice_ctx_s* self, const void* data, tsk_size_t size);
TINYNET_API int tnet_ice_ctx_send_turn_rtcp(struct tnet_ice_ctx_s* self, const void* data, tsk_size_t size);
TINYNET_API int tnet_ice_ctx_turn_get_bytes_count(const struct tnet_ice_ctx_s* self, uint64_t* bytes_in, uint64_t* bytes_out);
TINYNET_API const char* tnet_ice_ctx_get_ufrag(const struct tnet_ice_ctx_s* self);
TINYNET_API const char* tnet_ice_ctx_get_pwd(const struct tnet_ice_ctx_s* self);

View File

@ -251,6 +251,42 @@ int tnet_socket_send_stream(tnet_socket_t* self, const void* data, tsk_size_t si
return (int)tnet_sockfd_send(self->fd, data, size, 0);
}
/**@ingroup tnet_socket_group
* @retval Zero if succeed and nonzero error code otherwise.
*/
int tnet_socket_handle_brokenpipe(tnet_socket_t* self)
{
int ret;
tnet_fd_t fd_old, fd_new;
if (!self || !TNET_SOCKET_TYPE_IS_DGRAM(self->type)) { // Must be UDP
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
fd_old = self->fd;
fd_new = TNET_INVALID_FD;
// close old fd
ret = tnet_sockfd_close(&self->fd);
// try to create an fd binding to the same address
if ((ret = tnet_sockfd_init(self->ip, self->port, self->type, &fd_new)) != 0) {
TNET_PRINT_LAST_ERROR("Find to bind to %s:%d", self->ip, self->port);
// TODO: Create completly new socket?
return ret;
}
#if TNET_UNDER_IPHONE || TNET_UNDER_IPHONE_SIMULATOR
/* disable SIGPIPE signal */
{
int yes = 1;
if (setsockopt(fd_new, SOL_SOCKET, SO_NOSIGPIPE, (char*)&yes, sizeof(int))){
TNET_PRINT_LAST_ERROR("setsockopt(%d, SO_NOSIGPIPE) have failed", fd_new);
}
}
#endif /* TNET_UNDER_IPHONE || TNET_UNDER_IPHONE_SIMULATOR */
TSK_DEBUG_INFO("Broken pipe result for {%s:%d}: %d -> %d", self->ip, self->port, fd_old, fd_new);
self->fd = fd_new;
return 0;
}
/**@ingroup tnet_socket_group
* Closes a socket.
* @param sock The socket to close.

View File

@ -190,6 +190,7 @@ typedef tsk_list_t tnet_sockets_L_t; /**< List of @ref tnet_socket_t elements. *
TINYNET_API tnet_socket_t* tnet_socket_create_2(const char*host, tnet_port_t port, tnet_socket_type_t type, tsk_bool_t nonblocking, tsk_bool_t bindsocket);
TINYNET_API tnet_socket_t* tnet_socket_create(const char* host, tnet_port_t port, tnet_socket_type_t type);
TINYNET_API int tnet_socket_send_stream(tnet_socket_t* self, const void* data, tsk_size_t size);
TINYNET_API int tnet_socket_handle_brokenpipe(tnet_socket_t* self);
TINYNET_GEXTERN const tsk_object_def_t *tnet_socket_def_t;

View File

@ -723,6 +723,17 @@ tnet_fd_t tnet_transport_get_master_fd(const tnet_transport_handle_t *handle)
return ((const tnet_transport_t *)handle)->master ? ((const tnet_transport_t *)handle)->master->fd : TNET_INVALID_FD;
}
int tnet_transport_get_bytes_count(const tnet_transport_handle_t *handle, uint64_t* bytes_in, uint64_t* bytes_out)
{
if (!handle){
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
if (bytes_in) *bytes_in = ((const tnet_transport_t *)handle)->bytes_in;
if (bytes_out) *bytes_out = ((const tnet_transport_t *)handle)->bytes_out;
return 0;
}
/**
* Connects a socket.
* @param handle The transport to use to connect() the socket. The new socket will be managed by this transport.

View File

@ -41,8 +41,6 @@ TNET_BEGIN_DECLS
#define TNET_TRANSPORT_CB_F(callback) ((tnet_transport_cb_f)callback)
typedef void tnet_transport_handle_t;
typedef enum tnet_transport_event_type_e
{
event_data,
@ -51,6 +49,7 @@ typedef enum tnet_transport_event_type_e
event_removed,
event_connected,
event_accepted,
event_brokenpipe, // iOS: UDP sockets closed, to be restored now that the app is on foreground
event_dtls_handshake_started,
event_dtls_handshake_succeed,
@ -121,6 +120,7 @@ TINYNET_API int tnet_transport_dtls_get_handshakingdata(tnet_transport_handle_t*
TINYNET_API tnet_socket_type_t tnet_transport_get_type(const tnet_transport_handle_t *handle);
TINYNET_API tnet_fd_t tnet_transport_get_master_fd(const tnet_transport_handle_t *handle);
TINYNET_API int tnet_transport_get_bytes_count(const tnet_transport_handle_t *handle, uint64_t* bytes_in, uint64_t* bytes_out);
TINYNET_API int tnet_transport_shutdown(tnet_transport_handle_t* handle);
typedef struct tnet_transport_s
@ -138,6 +138,9 @@ typedef struct tnet_transport_s
tsk_object_t *context;
tsk_bool_t prepared;
uint64_t bytes_out;
uint64_t bytes_in;
//unsigned connected:1;
void* mainThreadId[1];

View File

@ -490,7 +490,7 @@ bail:
tsk_size_t tnet_transport_sendto(const tnet_transport_handle_t *handle, tnet_fd_t from, const struct sockaddr *to, const void* buf, tsk_size_t size)
{
tnet_transport_t *transport = (tnet_transport_t*)handle;
int numberOfBytesSent = 0;
int numberOfBytesSent = 0, ret;
if (!transport) {
TSK_DEBUG_ERROR("Invalid server handle");
@ -502,9 +502,17 @@ tsk_size_t tnet_transport_sendto(const tnet_transport_handle_t *handle, tnet_fd_
goto bail;
}
if ((numberOfBytesSent = (int)sendto(from, buf, size, 0, to, tnet_get_sockaddr_size(to))) < size) {
TNET_PRINT_LAST_ERROR("sendto have failed");
goto bail;
while (numberOfBytesSent < size && (ret = (int)sendto(from, buf, size, 0, to, tnet_get_sockaddr_size(to))) >= 0) {
numberOfBytesSent += ret;
}
if (numberOfBytesSent < size) {
if (tnet_geterrno() == TNET_ERROR_BROKENPIPE) {
TSK_DEBUG_INFO("UDP socket with fd=%d returned EPIPE...alerting the sender with 'event_brokenpipe' event", from);
TSK_RUNNABLE_ENQUEUE(transport, event_brokenpipe, transport->callback_data, from);
}
else {
TNET_PRINT_LAST_ERROR("sendto(fd=%d) have failed", from);
}
}
bail:

View File

@ -237,6 +237,7 @@ tsk_size_t tnet_transport_send(const tnet_transport_handle_t *handle, tnet_fd_t
}
bail:
transport->bytes_out += numberOfBytesSent;
return numberOfBytesSent;
}
@ -261,6 +262,7 @@ tsk_size_t tnet_transport_sendto(const tnet_transport_handle_t *handle, tnet_fd_
}
bail:
transport->bytes_out += numberOfBytesSent;
return numberOfBytesSent;
}
@ -811,6 +813,7 @@ void *tnet_transport_mainthread(void *param)
}
if(len > 0){
transport->bytes_in += len;
e = tnet_transport_event_create(event_data, transport->callback_data, active_socket->fd);
e->data = buffer, buffer = tsk_null;
e->size = len;

View File

@ -285,6 +285,7 @@ try_again:
}
bail:
transport->bytes_out += sent;
return sent;
}
@ -312,6 +313,7 @@ tsk_size_t tnet_transport_sendto(const tnet_transport_handle_t *handle, tnet_fd_
}
bail:
transport->bytes_out += numberOfBytesSent;
return numberOfBytesSent;
}
@ -739,6 +741,7 @@ void* TSK_STDCALL tnet_transport_mainthread(void *param)
else
{
tnet_transport_event_t* e = tnet_transport_event_create(event_data, transport->callback_data, active_socket->fd);
transport->bytes_in += wsaBuffer.len;
e->data = wsaBuffer.buf;
e->size = wsaBuffer.len;
e->remote_addr = remote_addr;

View File

@ -77,6 +77,8 @@ typedef char tnet_ip_t[INET6_ADDRSTRLEN];
typedef uint8_t tnet_mac_address[6];
typedef unsigned char tnet_fingerprint_t[TNET_FINGERPRINT_MAX + 1];
typedef void tnet_transport_handle_t;
typedef tsk_list_t tnet_interfaces_L_t; /**< List of @ref tnet_interface_t elements*/
typedef tsk_list_t tnet_addresses_L_t; /**< List of @ref tnet_address_t elements*/
@ -132,6 +134,7 @@ static const char* TNET_DTLS_HASH_NAMES[TNET_DTLS_HASH_TYPE_MAX] =
# define TNET_ERROR_INTR WSAEINTR
# define TNET_ERROR_ISCONN WSAEISCONN
# define TNET_ERROR_EAGAIN TNET_ERROR_WOULDBLOCK /* WinSock FIX */
# define TNET_ERROR_BROKENPIPE WSAECONNABORTED
# if (TNET_UNDER_WINDOWS_RT || TNET_UNDER_WINDOWS_CE) /* gai_strerrorA() links against FormatMessageA which is not allowed on the store */
# if !defined (WC_ERR_INVALID_CHARS)
# define WC_ERR_INVALID_CHARS 0
@ -163,6 +166,7 @@ static const char* TNET_DTLS_HASH_NAMES[TNET_DTLS_HASH_TYPE_MAX] =
# define TNET_ERROR_INTR EINTR
# define TNET_ERROR_ISCONN EISCONN
# define TNET_ERROR_EAGAIN EAGAIN
# define TNET_ERROR_BROKENPIPE EPIPE
# define tnet_gai_strerror gai_strerror
#endif
#define TNET_INVALID_FD TNET_INVALID_SOCKET

View File

@ -1804,14 +1804,14 @@ int tnet_sockfd_sendto(tnet_fd_t fd, const struct sockaddr *to, const void* buf,
#endif
if (ret <= 0) {
if (tnet_geterrno() == TNET_ERROR_WOULDBLOCK) {
TSK_DEBUG_INFO("SendUdp() - WouldBlock. Retrying...");
TSK_DEBUG_INFO("SendUdp(fd=%d) - WouldBlock. Retrying...", fd);
if (try_guard--) {
tsk_thread_sleep(10);
goto try_again;
}
}
else {
TNET_PRINT_LAST_ERROR("sendto() failed");
TNET_PRINT_LAST_ERROR("sendto(fd=%d) failed", fd);
}
goto bail;
}

View File

@ -780,6 +780,15 @@ int tnet_turn_session_get_req_transport(const struct tnet_turn_session_s* pc_sel
return 0;
}
int tnet_turn_session_get_bytes_count(const struct tnet_turn_session_s* pc_self, uint64_t* bytes_in, uint64_t* bytes_out)
{
if (!pc_self) {
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
return tnet_transport_get_bytes_count(pc_self->p_transport, bytes_in, bytes_out);
}
int tnet_turn_session_createpermission(struct tnet_turn_session_s* p_self, const char* pc_peer_addr, uint16_t u_peer_port, tnet_turn_peer_id_t* pu_id)
{
int ret = 0;
@ -2022,6 +2031,22 @@ static int _tnet_turn_session_transport_layer_process_cb(const tnet_transport_ev
switch(e->type){
case event_data:
break;
case event_brokenpipe:
tsk_safeobj_lock(p_ss);
if (p_ss->p_lcl_sock && e->local_fd == p_ss->p_lcl_sock->fd) {
tnet_fd_t broken_fd = e->local_fd;
tsk_bool_t registered_fd = !!tnet_transport_have_socket(p_ss->p_transport, broken_fd);
if (registered_fd) {
tnet_transport_remove_socket(p_ss->p_transport, &broken_fd);
}
if (tnet_socket_handle_brokenpipe(p_ss->p_lcl_sock) == 0) {
if (registered_fd) {
tnet_transport_add_socket(p_ss->p_transport, p_ss->p_lcl_sock->fd, p_ss->p_lcl_sock->type, tsk_false/* do not take ownership */, tsk_true/* only Meaningful for tls*/, tsk_null);
}
}
}
tsk_safeobj_unlock(p_ss);
return 0;
case event_connected:
if (p_ss->p_lcl_sock && p_ss->p_lcl_sock->fd == e->local_fd) {
tsk_safeobj_lock(p_ss);

View File

@ -87,6 +87,7 @@ TINYNET_API int tnet_turn_session_get_socket_local(struct tnet_turn_session_s* p
TINYNET_API int tnet_turn_session_get_state_createperm(const struct tnet_turn_session_s* pc_self, tnet_turn_peer_id_t u_peer_id, enum tnet_stun_state_e *pe_state);
TINYNET_API int tnet_turn_session_get_state_connbind(const struct tnet_turn_session_s* pc_self, tnet_turn_peer_id_t u_peer_id, enum tnet_stun_state_e *pe_state);
TINYNET_API int tnet_turn_session_get_req_transport(const struct tnet_turn_session_s* pc_self, enum tnet_turn_transport_e *pe_transport);
TINYNET_API int tnet_turn_session_get_bytes_count(const struct tnet_turn_session_s* pc_self, uint64_t* bytes_in, uint64_t* bytes_out);
TINYNET_API int tnet_turn_session_createpermission(struct tnet_turn_session_s* p_self, const char* pc_peer_addr, uint16_t u_peer_port, tnet_turn_peer_id_t* pu_peer_id);
TINYNET_API int tnet_turn_session_deletepermission(struct tnet_turn_session_s* p_self, tnet_turn_peer_id_t u_peer_id);
TINYNET_API int tnet_turn_session_chanbind(struct tnet_turn_session_s* p_self, tnet_turn_peer_id_t u_peer_id);

View File

@ -41,6 +41,7 @@ TRTP_BEGIN_DECLS
struct trtp_rtcp_packet_s;
struct trtp_rtp_packet_s;
struct tnet_ice_ctx_s;
struct tnet_transport_s;
typedef int (*trtp_rtcp_cb_f)(const void* callback_data, const struct trtp_rtcp_packet_s* packet);
@ -60,6 +61,10 @@ int trtp_rtcp_session_signal_pkt_loss(struct trtp_rtcp_session_s* self, uint32_t
int trtp_rtcp_session_signal_frame_corrupted(struct trtp_rtcp_session_s* self, uint32_t ssrc_media);
int trtp_rtcp_session_signal_jb_error(struct trtp_rtcp_session_s* self, uint32_t ssrc_media);
tnet_fd_t trtp_rtcp_session_get_local_fd(const struct trtp_rtcp_session_s* self);
int trtp_rtcp_session_set_local_fd(struct trtp_rtcp_session_s* self, tnet_fd_t local_fd);
int trtp_rtcp_session_set_net_transport(struct trtp_rtcp_session_s* self, struct tnet_transport_s* transport);
TRTP_END_DECLS
#endif /* TINYMEDIA_RTCP_SESSION_H */

View File

@ -106,6 +106,7 @@ typedef struct trtp_manager_s
struct{
void* ptr;
tsk_size_t size;
tsk_size_t index;
} serial_buffer;
} rtp;
@ -211,6 +212,7 @@ TINYRTP_API int trtp_manager_set_proxy_info(trtp_manager_t* self, enum tnet_prox
TINYRTP_API int trtp_manager_start(trtp_manager_t* self);
TINYRTP_API tsk_size_t trtp_manager_send_rtp(trtp_manager_t* self, const void* data, tsk_size_t size, uint32_t duration, tsk_bool_t marker, tsk_bool_t last_packet);
TINYRTP_API tsk_size_t trtp_manager_send_rtp_packet(trtp_manager_t* self, const struct trtp_rtp_packet_s* packet, tsk_bool_t bypass_encrypt);
TINYRTP_API int trtp_manager_get_bytes_count(trtp_manager_t* self, uint64_t* bytes_in, uint64_t* bytes_out);
TINYRTP_API tsk_size_t trtp_manager_send_rtp_raw(trtp_manager_t* self, const void* data, tsk_size_t size);
TINYRTP_API int trtp_manager_set_app_bandwidth_max(trtp_manager_t* self, int32_t bw_upload_kbps, int32_t bw_download_kbps);
TINYRTP_API int trtp_manager_signal_pkt_loss(trtp_manager_t* self, uint32_t ssrc_media, const uint16_t* seq_nums, tsk_size_t count);

View File

@ -38,6 +38,7 @@
#include "ice/tnet_ice_ctx.h"
#include "turn/tnet_turn_session.h"
#include "tnet_transport.h"
#include "tnet_utils.h"
@ -268,8 +269,9 @@ typedef struct trtp_rtcp_session_s
tsk_bool_t is_started;
tnet_fd_t local_fd;
struct tnet_transport_s* transport; // not starter -> do not stop
const struct sockaddr * remote_addr;
struct tnet_ice_ctx_s* ice_ctx;
struct tnet_ice_ctx_s* ice_ctx; // not starter -> do not stop
tsk_bool_t is_ice_turn_active;
const void* callback_data;
@ -355,8 +357,9 @@ static tsk_object_t* trtp_rtcp_session_dtor(tsk_object_t * self)
TSK_OBJECT_SAFE_FREE(session->sources);
TSK_OBJECT_SAFE_FREE(session->source_local);
TSK_OBJECT_SAFE_FREE(session->sdes);
TSK_OBJECT_SAFE_FREE(session->ice_ctx);
TSK_OBJECT_SAFE_FREE(session->ice_ctx); // not starter -> do not stop
TSK_FREE(session->cname);
TSK_OBJECT_SAFE_FREE(session->transport); // not starter -> do not stop
// release the handle for the global timer manager
tsk_timer_mgr_global_unref(&session->timer.handle_global);
@ -765,6 +768,36 @@ int trtp_rtcp_session_signal_jb_error(struct trtp_rtcp_session_s* self, uint32_t
return trtp_rtcp_session_signal_frame_corrupted(self, ssrc_media);
}
tnet_fd_t trtp_rtcp_session_get_local_fd(const struct trtp_rtcp_session_s* self)
{
if (!self) {
TSK_DEBUG_ERROR("Invalid parameter");
return TNET_INVALID_FD;
}
return self->local_fd;
}
int trtp_rtcp_session_set_local_fd(struct trtp_rtcp_session_s* self, tnet_fd_t local_fd)
{
if (!self) {
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
self->local_fd = local_fd;
return 0;
}
int trtp_rtcp_session_set_net_transport(struct trtp_rtcp_session_s* self, struct tnet_transport_s* transport)
{
if (!self) {
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
TSK_OBJECT_SAFE_FREE(self->transport);
self->transport = tsk_object_ref(transport);
return 0;
}
static tsk_bool_t _trtp_rtcp_session_have_source(trtp_rtcp_session_t* self, uint32_t ssrc)
{
tsk_list_item_t* item;
@ -925,9 +958,9 @@ static tsk_size_t _trtp_rtcp_session_send_raw(trtp_rtcp_session_t* self, const v
ret = (tnet_ice_ctx_send_turn_rtcp(self->ice_ctx, data, size) == 0) ? size : 0; // returns #0 if ok
}
else {
if (tnet_sockfd_sendto(self->local_fd, self->remote_addr, data, size) == size){ // returns number of sent bytes
ret = size;
}
ret = self->transport
? tnet_transport_sendto(self->transport, self->local_fd, self->remote_addr, data, size)
: tnet_sockfd_sendto(self->local_fd, self->remote_addr, data, size);
}
return ret;
}

View File

@ -93,6 +93,38 @@ static int _trtp_transport_layer_cb(const tnet_transport_event_t* e)
{
return _trtp_manager_recv_data(manager, e->data, e->size, e->local_fd, &e->remote_addr);
}
case event_brokenpipe:
{
tsk_safeobj_lock(manager);
tnet_fd_t broken_fd = e->local_fd;
tnet_socket_t* socket = tsk_null;
tsk_bool_t is_rtcp_socket = tsk_false;
if (manager->transport && manager->transport->master && manager->transport->master->fd == broken_fd) {
socket = manager->transport->master;
}
else if (manager->rtcp.local_socket && manager->rtcp.local_socket->fd == broken_fd) {
socket = manager->rtcp.local_socket;
is_rtcp_socket = tsk_true;
}
if (socket) {
tsk_bool_t registered_fd = !!tnet_transport_have_socket(manager->transport, broken_fd);
if (registered_fd) {
tnet_transport_remove_socket(manager->transport, &broken_fd); // broken_fd=-1
broken_fd = e->local_fd; // restore
}
if (tnet_socket_handle_brokenpipe(socket) == 0) {
if (registered_fd) {
tnet_transport_add_socket(manager->transport, socket->fd, socket->type, tsk_false/* do not take ownership */, tsk_true/* only Meaningful for tls*/, tsk_null);
}
if (manager->rtcp.session && trtp_rtcp_session_get_local_fd(manager->rtcp.session) == broken_fd) {
trtp_rtcp_session_set_local_fd(manager->rtcp.session, socket->fd);
}
}
}
tsk_safeobj_unlock(manager);
return 0;
}
#if HAVE_SRTP
/* DTLS - SRTP events */
case event_dtls_handshake_succeed:
@ -457,10 +489,17 @@ static int _trtp_manager_recv_data(const trtp_manager_t* self, const uint8_t* da
err_status_t status;
if(self->srtp_ctx_neg_remote){
if((status = srtp_unprotect(self->srtp_ctx_neg_remote->rtp.session, (void*)data_ptr, (int*)&data_size)) != err_status_ok){
if (status == err_status_replay_fail) {
// replay (because of RTCP-NACK nothing to worry about)
TSK_DEBUG_INFO("srtp_unprotect(RTP) returned 'err_status_replay_fail'");
return 0;
}
else {
TSK_DEBUG_ERROR("srtp_unprotect(RTP) failed with error code=%d, seq_num=%u", (int)status, (data_size > 4 ? tnet_ntohs_2(&data_ptr[2]) : 0x0000));
return -1;
}
}
}
#endif
if((packet_rtp = trtp_rtp_packet_deserialize(data_ptr, data_size))){
// update remote SSRC based on received RTP packet
@ -1463,6 +1502,7 @@ int trtp_manager_start(trtp_manager_t* self)
if(self->rtcp.session){
ret = trtp_rtcp_session_set_callback(self->rtcp.session, self->rtcp.cb.fun, self->rtcp.cb.usrdata);
ret = trtp_rtcp_session_set_app_bandwidth_max(self->rtcp.session, self->app_bw_max_upload, self->app_bw_max_download);
ret = trtp_rtcp_session_set_net_transport(self->rtcp.session, self->transport);
if((ret = trtp_rtcp_session_start(self->rtcp.session, local_rtcp_fd, (const struct sockaddr *)&self->rtcp.remote_addr))){
TSK_DEBUG_ERROR("Failed to start RTCP session");
goto bail;
@ -1569,6 +1609,9 @@ tsk_size_t trtp_manager_send_rtp_packet(trtp_manager_t* self, const struct trtp_
tsk_safeobj_lock(self);
// reset index
self->rtp.serial_buffer.index = 0;
/* check if transport is started */
if(!self->is_started || !self->transport || !self->transport->master){
TSK_DEBUG_WARN("RTP engine not ready yet");
@ -1598,7 +1641,7 @@ tsk_size_t trtp_manager_send_rtp_packet(trtp_manager_t* self, const struct trtp_
}
/* serialize and send over the network */
if((ret = (int)trtp_rtp_packet_serialize_to(packet, self->rtp.serial_buffer.ptr, xsize))){
if ((ret = (int)trtp_rtp_packet_serialize_to(packet, self->rtp.serial_buffer.ptr, xsize))) {
void* data_ptr = self->rtp.serial_buffer.ptr;
int data_size = ret;
#if HAVE_SRTP
@ -1610,6 +1653,7 @@ tsk_size_t trtp_manager_send_rtp_packet(trtp_manager_t* self, const struct trtp_
}
}
#endif
self->rtp.serial_buffer.index = data_size; // update index
if (/* number of bytes sent */(ret = (int)trtp_manager_send_rtp_raw(self, data_ptr, data_size)) > 0) {
// forward packet to the RTCP session
if (self->rtcp.session) {
@ -1642,12 +1686,35 @@ tsk_size_t trtp_manager_send_rtp_raw(trtp_manager_t* self, const void* data, tsk
ret = (tnet_ice_ctx_send_turn_rtp(self->ice_ctx, data, size) == 0) ? size : 0; // returns #0 if ok
}
else {
#if 1
ret = tnet_transport_sendto(self->transport, self->transport->master->fd, (const struct sockaddr *)&self->rtp.remote_addr, data, size); // returns number of sent bytes
#else
ret = tnet_sockfd_sendto(self->transport->master->fd, (const struct sockaddr *)&self->rtp.remote_addr, data, size); // returns number of sent bytes
#endif
}
tsk_safeobj_unlock(self);
return ret;
}
int trtp_manager_get_bytes_count(trtp_manager_t* self, uint64_t* bytes_in, uint64_t* bytes_out)
{
if (!self) {
TSK_DEBUG_ERROR("Invalid parameter");
return -1;
}
if (!self->is_started) {
TSK_DEBUG_INFO("trtp_manager_get_bytes_count() called before starting RTP manager... returning zeros");
if (bytes_in) *bytes_in = 0;
if (bytes_out) *bytes_out = 0;
return 0;
}
if (self->is_ice_turn_active) {
return tnet_ice_ctx_turn_get_bytes_count(self->ice_ctx, bytes_in, bytes_out);
}
return tnet_transport_get_bytes_count(self->transport, bytes_in, bytes_out);
}
int trtp_manager_set_app_bandwidth_max(trtp_manager_t* self, int32_t bw_upload_kbps, int32_t bw_download_kbps)
{
if(self){
@ -1660,6 +1727,7 @@ int trtp_manager_set_app_bandwidth_max(trtp_manager_t* self, int32_t bw_upload_k
}
return -1;
}
int trtp_manager_signal_pkt_loss(trtp_manager_t* self, uint32_t ssrc_media, const uint16_t* seq_nums, tsk_size_t count)
{
if(self && self->rtcp.session){
@ -1667,6 +1735,7 @@ int trtp_manager_signal_pkt_loss(trtp_manager_t* self, uint32_t ssrc_media, cons
}
return -1;
}
int trtp_manager_signal_frame_corrupted(trtp_manager_t* self, uint32_t ssrc_media)
{
if(self && self->rtcp.session){
@ -1713,6 +1782,7 @@ int trtp_manager_stop(trtp_manager_t* self)
// Stop the RTCP session first (will send BYE)
if(self->rtcp.session){
ret = trtp_rtcp_session_stop(self->rtcp.session);
ret = trtp_rtcp_session_set_net_transport(self->rtcp.session, tsk_null);
}
// Free transport to force next call to start() to create new one with new sockets