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_type_t srtp_type;
tmedia_srtp_mode_t srtp_mode; 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_upload_kbps;
int32_t bandwidth_max_download_kbps; int32_t bandwidth_max_download_kbps;
int32_t fps; 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 { else {
TMEDIA_CODEC(vp8)->bandwidth_max_upload = max_bw_new; 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; reconf = tsk_true;
} }
else if (tsk_striequals(param->key, "bandwidth-max-upload")) { 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; return ret;
} }

View File

@ -70,15 +70,23 @@
#define TDAV_SESSION_VIDEO_PKT_LOSS_MEDIUM 22 #define TDAV_SESSION_VIDEO_PKT_LOSS_MEDIUM 22
#define TDAV_SESSION_VIDEO_PKT_LOSS_HIGH 63 #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 // The maximum number of pakcet loss allowed
#define TDAV_SESSION_VIDEO_PKT_LOSS_MAX_COUNT_TO_REQUEST_FIR 50 #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_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_up = tmedia_codec_action_bw_up;
static const tmedia_codec_action_t __action_encode_bw_down = tmedia_codec_action_bw_down; static const tmedia_codec_action_t __action_encode_bw_down = tmedia_codec_action_bw_down;
// FIXME: lock ? // 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; \ static tmedia_param_t* __param = tsk_null; \
if(!__param){ \ if(!__param){ \
@ -86,8 +94,8 @@ static const tmedia_codec_action_t __action_encode_bw_down = tmedia_codec_action
tmedia_video, \ tmedia_video, \
tmedia_ppt_codec, \ tmedia_ppt_codec, \
tmedia_pvt_int32, \ tmedia_pvt_int32, \
"action", \ __key, \
(void*)&value); \ (void*)&__value); \
} \ } \
if((__self)->encoder.codec && __param){ \ if((__self)->encoder.codec && __param){ \
/*tsk_mutex_lock((__self)->encoder.h_mutex);*/ \ /*tsk_mutex_lock((__self)->encoder.h_mutex);*/ \
@ -127,6 +135,8 @@ static const tmedia_codec_action_t __action_encode_bw_down = tmedia_codec_action
} }
#define _tdav_session_video_bw_up(__self) _tdav_session_video_codec_set(__self, "action", __action_encode_bw_up) #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_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) \ #define _tdav_session_video_reset_loss_prob(__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 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 ++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) { 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); // without audio session iOS "audio" background mode is useless and UDP sockets will be closed: e.g. GE's video-only sessions
goto bail; #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); rtp_hdr_size = TRTP_RTP_HEADER_MIN_SIZE + (packet->header->csrc_count << 2);
// Save packet // 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); 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 // 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 // Hack the RTP packet payload to point to the the SRTP data instead of unencrypted ptr
@ -539,6 +557,7 @@ static int tdav_session_video_rtp_cb(const void* callback_data, const trtp_rtp_p
// RTCP callback (Network -> This) // RTCP callback (Network -> This)
static int tdav_session_video_rtcp_cb(const void* callback_data, const trtp_rtcp_packet_t* packet) 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_psfb_t* psfb;
const trtp_rtcp_report_rtpfb_t* rtpfb; const trtp_rtcp_report_rtpfb_t* rtpfb;
const trtp_rtcp_rblocks_L_t* blocks = tsk_null; const trtp_rtcp_rblocks_L_t* blocks = tsk_null;
@ -555,6 +574,7 @@ static int tdav_session_video_rtcp_cb(const void* callback_data, const trtp_rtcp
if(!(block = item->data)) continue; if(!(block = item->data)) continue;
if(base->rtp_manager->rtp.ssrc.local == block->ssrc){ 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; 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; 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; 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
@ -610,9 +630,59 @@ static int tdav_session_video_rtcp_cb(const void* callback_data, const trtp_rtcp
case trtp_rtcp_psfb_fci_type_afb: case trtp_rtcp_psfb_fci_type_afb:
{ {
if (psfb->afb.type == trtp_rtcp_psfb_afb_type_remb) { if (psfb->afb.type == trtp_rtcp_psfb_afb_type_remb) {
uint32_t bandwidth = ((psfb->afb.remb.mantissa << psfb->afb.remb.exp) / 1024); 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", ((const trtp_rtcp_report_fb_t*)psfb)->ssrc_media, psfb->afb.remb.exp, psfb->afb.remb.mantissa, bandwidth); 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 // for now we just don't respect the requested bandwidth
#endif /* TDAV_GOOG_REMB_FULL_SUPPORT */
} }
break; break;
} }
@ -680,7 +750,7 @@ static int tdav_session_video_rtcp_cb(const void* callback_data, const trtp_rtcp
}// switch }// switch
}// while(rtcp-pkt) }// while(rtcp-pkt)
return 0; return ret;
} }
static int _tdav_session_video_set_defaults(tdav_session_video_t* self) 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: 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); _tdav_session_video_local_request_idr(session, "TMFR", data->ssrc);
} }
case tdav_video_jb_cb_data_type_fl: 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){ if(data->fl.count > TDAV_SESSION_VIDEO_PKT_LOSS_MAX_COUNT_TO_REQUEST_FIR){
_tdav_session_video_local_request_idr(session, "TMFR", data->ssrc); _tdav_session_video_local_request_idr(session, "TMFR", data->ssrc);
} }
@ -929,10 +1001,10 @@ static int _tdav_session_video_decode(tdav_session_video_t* self, const trtp_rtp
// - congestion control is enabled and // - congestion control is enabled and
// - fps changed or // - fps changed or
// - first frame or // - first frame or
// - approximately every 5 minutes (300 = 60 * 5) // - 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 * 300)) == 0))){ 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_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 already computed in start() but the decoded video size was not correct and based on the SDP negotiation
bandwidth_max_download_kbps = TSK_MIN( 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), 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),
@ -942,9 +1014,41 @@ static int _tdav_session_video_decode(tdav_session_video_t* self, const trtp_rtp
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), 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); 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 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); 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, 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 // inc() frame count and consume decoded video
++self->decoder.codec_decoded_frames_count; ++self->decoder.codec_decoded_frames_count;
@ -1112,7 +1216,6 @@ static int tdav_session_video_start(tmedia_session_t* self)
tsk_mutex_lock(video->encoder.h_mutex); tsk_mutex_lock(video->encoder.h_mutex);
TSK_OBJECT_SAFE_FREE(video->encoder.codec); TSK_OBJECT_SAFE_FREE(video->encoder.codec);
video->encoder.codec = tsk_object_ref((tsk_object_t*)codec); video->encoder.codec = tsk_object_ref((tsk_object_t*)codec);
// initialize the encoder using user-defined values // initialize the encoder using user-defined values
if ((ret = tdav_session_av_init_encoder(base, video->encoder.codec))) { 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); 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; 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) { if (updated) {
// set callbacks // set callbacks
ret = _tdav_session_video_set_callbacks(self); ret = _tdav_session_video_set_callbacks(self);

View File

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

View File

@ -1,7 +1,5 @@
/* /*
* Copyright (C) 2010-2011 Mamadou Diop. * Copyright (C) 2010-2015 Mamadou DIOP
*
* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
* *
* This file is part of Open Source Doubango Framework. * 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-19
* http://tools.ietf.org/html/draft-ietf-mmusic-ice-tcp-08 * http://tools.ietf.org/html/draft-ietf-mmusic-ice-tcp-08
* *
* @author Mamadou Diop <diopmamadou(at)doubango.org>
*
*/ */
#ifndef TNET_ICE_H #ifndef TNET_ICE_H
#define 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); : _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) const char* tnet_ice_ctx_get_ufrag(const struct tnet_ice_ctx_s* self)
{ {
return (self && self->ufrag) ? self->ufrag : tsk_null; return (self && self->ufrag) ? self->ufrag : tsk_null;

View File

@ -1,7 +1,5 @@
/* /*
* Copyright (C) 2012 Doubango Telecom <http://www.doubango.org>. * Copyright (C) 2012-2015 Doubango Telecom <http://www.doubango.org>.
*
* Contact: Mamadou Diop <diopmamadou(at)doubango[dot]org>
* *
* This file is part of Open Source Doubango Framework. * This file is part of Open Source Doubango Framework.
* *
@ -22,7 +20,6 @@
/**@file tnet_ice_ctx.h /**@file tnet_ice_ctx.h
* @brief Interactive Connectivity Establishment (ICE) implementation as per RFC 5245. * @brief Interactive Connectivity Establishment (ICE) implementation as per RFC 5245.
* @author Mamadou Diop <diopmamadou(at)doubango[dot]org>
*/ */
#ifndef TNET_ICE_CTX_H #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_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_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_ufrag(const struct tnet_ice_ctx_s* self);
TINYNET_API const char* tnet_ice_ctx_get_pwd(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); 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 /**@ingroup tnet_socket_group
* Closes a socket. * Closes a socket.
* @param sock The socket to close. * @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_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 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_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; 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; 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. * Connects a socket.
* @param handle The transport to use to connect() the socket. The new socket will be managed by this transport. * @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) #define TNET_TRANSPORT_CB_F(callback) ((tnet_transport_cb_f)callback)
typedef void tnet_transport_handle_t;
typedef enum tnet_transport_event_type_e typedef enum tnet_transport_event_type_e
{ {
event_data, event_data,
@ -51,6 +49,7 @@ typedef enum tnet_transport_event_type_e
event_removed, event_removed,
event_connected, event_connected,
event_accepted, 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_started,
event_dtls_handshake_succeed, 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_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 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); TINYNET_API int tnet_transport_shutdown(tnet_transport_handle_t* handle);
typedef struct tnet_transport_s typedef struct tnet_transport_s
@ -138,6 +138,9 @@ typedef struct tnet_transport_s
tsk_object_t *context; tsk_object_t *context;
tsk_bool_t prepared; tsk_bool_t prepared;
uint64_t bytes_out;
uint64_t bytes_in;
//unsigned connected:1; //unsigned connected:1;
void* mainThreadId[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) 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; tnet_transport_t *transport = (tnet_transport_t*)handle;
int numberOfBytesSent = 0; int numberOfBytesSent = 0, ret;
if (!transport) { if (!transport) {
TSK_DEBUG_ERROR("Invalid server handle"); 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; goto bail;
} }
if ((numberOfBytesSent = (int)sendto(from, buf, size, 0, to, tnet_get_sockaddr_size(to))) < size) { while (numberOfBytesSent < size && (ret = (int)sendto(from, buf, size, 0, to, tnet_get_sockaddr_size(to))) >= 0) {
TNET_PRINT_LAST_ERROR("sendto have failed"); numberOfBytesSent += ret;
goto bail; }
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: bail:

View File

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

View File

@ -285,6 +285,7 @@ try_again:
} }
bail: bail:
transport->bytes_out += sent;
return sent; return sent;
} }
@ -312,6 +313,7 @@ tsk_size_t tnet_transport_sendto(const tnet_transport_handle_t *handle, tnet_fd_
} }
bail: bail:
transport->bytes_out += numberOfBytesSent;
return numberOfBytesSent; return numberOfBytesSent;
} }
@ -739,6 +741,7 @@ void* TSK_STDCALL tnet_transport_mainthread(void *param)
else else
{ {
tnet_transport_event_t* e = tnet_transport_event_create(event_data, transport->callback_data, active_socket->fd); 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->data = wsaBuffer.buf;
e->size = wsaBuffer.len; e->size = wsaBuffer.len;
e->remote_addr = remote_addr; 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 uint8_t tnet_mac_address[6];
typedef unsigned char tnet_fingerprint_t[TNET_FINGERPRINT_MAX + 1]; 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_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*/ 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_INTR WSAEINTR
# define TNET_ERROR_ISCONN WSAEISCONN # define TNET_ERROR_ISCONN WSAEISCONN
# define TNET_ERROR_EAGAIN TNET_ERROR_WOULDBLOCK /* WinSock FIX */ # 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 (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) # if !defined (WC_ERR_INVALID_CHARS)
# define WC_ERR_INVALID_CHARS 0 # 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_INTR EINTR
# define TNET_ERROR_ISCONN EISCONN # define TNET_ERROR_ISCONN EISCONN
# define TNET_ERROR_EAGAIN EAGAIN # define TNET_ERROR_EAGAIN EAGAIN
# define TNET_ERROR_BROKENPIPE EPIPE
# define tnet_gai_strerror gai_strerror # define tnet_gai_strerror gai_strerror
#endif #endif
#define TNET_INVALID_FD TNET_INVALID_SOCKET #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 #endif
if (ret <= 0) { if (ret <= 0) {
if (tnet_geterrno() == TNET_ERROR_WOULDBLOCK) { if (tnet_geterrno() == TNET_ERROR_WOULDBLOCK) {
TSK_DEBUG_INFO("SendUdp() - WouldBlock. Retrying..."); TSK_DEBUG_INFO("SendUdp(fd=%d) - WouldBlock. Retrying...", fd);
if (try_guard--) { if (try_guard--) {
tsk_thread_sleep(10); tsk_thread_sleep(10);
goto try_again; goto try_again;
} }
} }
else { else {
TNET_PRINT_LAST_ERROR("sendto() failed"); TNET_PRINT_LAST_ERROR("sendto(fd=%d) failed", fd);
} }
goto bail; 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; 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 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; int ret = 0;
@ -2022,6 +2031,22 @@ static int _tnet_turn_session_transport_layer_process_cb(const tnet_transport_ev
switch(e->type){ switch(e->type){
case event_data: case event_data:
break; 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: case event_connected:
if (p_ss->p_lcl_sock && p_ss->p_lcl_sock->fd == e->local_fd) { if (p_ss->p_lcl_sock && p_ss->p_lcl_sock->fd == e->local_fd) {
tsk_safeobj_lock(p_ss); 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_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_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_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_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_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); 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_rtcp_packet_s;
struct trtp_rtp_packet_s; struct trtp_rtp_packet_s;
struct tnet_ice_ctx_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); 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_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); 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 TRTP_END_DECLS
#endif /* TINYMEDIA_RTCP_SESSION_H */ #endif /* TINYMEDIA_RTCP_SESSION_H */

View File

@ -106,6 +106,7 @@ typedef struct trtp_manager_s
struct{ struct{
void* ptr; void* ptr;
tsk_size_t size; tsk_size_t size;
tsk_size_t index;
} serial_buffer; } serial_buffer;
} rtp; } 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 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(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 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 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_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); 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 "ice/tnet_ice_ctx.h"
#include "turn/tnet_turn_session.h" #include "turn/tnet_turn_session.h"
#include "tnet_transport.h"
#include "tnet_utils.h" #include "tnet_utils.h"
@ -268,8 +269,9 @@ typedef struct trtp_rtcp_session_s
tsk_bool_t is_started; tsk_bool_t is_started;
tnet_fd_t local_fd; tnet_fd_t local_fd;
struct tnet_transport_s* transport; // not starter -> do not stop
const struct sockaddr * remote_addr; 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; tsk_bool_t is_ice_turn_active;
const void* callback_data; 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->sources);
TSK_OBJECT_SAFE_FREE(session->source_local); TSK_OBJECT_SAFE_FREE(session->source_local);
TSK_OBJECT_SAFE_FREE(session->sdes); 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_FREE(session->cname);
TSK_OBJECT_SAFE_FREE(session->transport); // not starter -> do not stop
// release the handle for the global timer manager // release the handle for the global timer manager
tsk_timer_mgr_global_unref(&session->timer.handle_global); 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); 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) static tsk_bool_t _trtp_rtcp_session_have_source(trtp_rtcp_session_t* self, uint32_t ssrc)
{ {
tsk_list_item_t* item; 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 ret = (tnet_ice_ctx_send_turn_rtcp(self->ice_ctx, data, size) == 0) ? size : 0; // returns #0 if ok
} }
else { else {
if (tnet_sockfd_sendto(self->local_fd, self->remote_addr, data, size) == size){ // returns number of sent bytes ret = self->transport
ret = size; ? 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; 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); 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 #if HAVE_SRTP
/* DTLS - SRTP events */ /* DTLS - SRTP events */
case event_dtls_handshake_succeed: 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; err_status_t status;
if(self->srtp_ctx_neg_remote){ 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 = 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)); 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; return -1;
} }
} }
}
#endif #endif
if((packet_rtp = trtp_rtp_packet_deserialize(data_ptr, data_size))){ if((packet_rtp = trtp_rtp_packet_deserialize(data_ptr, data_size))){
// update remote SSRC based on received RTP packet // update remote SSRC based on received RTP packet
@ -1463,6 +1502,7 @@ int trtp_manager_start(trtp_manager_t* self)
if(self->rtcp.session){ 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_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_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))){ 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"); TSK_DEBUG_ERROR("Failed to start RTCP session");
goto bail; 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); tsk_safeobj_lock(self);
// reset index
self->rtp.serial_buffer.index = 0;
/* check if transport is started */ /* check if transport is started */
if(!self->is_started || !self->transport || !self->transport->master){ if(!self->is_started || !self->transport || !self->transport->master){
TSK_DEBUG_WARN("RTP engine not ready yet"); TSK_DEBUG_WARN("RTP engine not ready yet");
@ -1610,6 +1653,7 @@ tsk_size_t trtp_manager_send_rtp_packet(trtp_manager_t* self, const struct trtp_
} }
} }
#endif #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) { if (/* number of bytes sent */(ret = (int)trtp_manager_send_rtp_raw(self, data_ptr, data_size)) > 0) {
// forward packet to the RTCP session // forward packet to the RTCP session
if (self->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 ret = (tnet_ice_ctx_send_turn_rtp(self->ice_ctx, data, size) == 0) ? size : 0; // returns #0 if ok
} }
else { 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 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); tsk_safeobj_unlock(self);
return ret; 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) int trtp_manager_set_app_bandwidth_max(trtp_manager_t* self, int32_t bw_upload_kbps, int32_t bw_download_kbps)
{ {
if(self){ if(self){
@ -1660,6 +1727,7 @@ int trtp_manager_set_app_bandwidth_max(trtp_manager_t* self, int32_t bw_upload_k
} }
return -1; 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) 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){ 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; return -1;
} }
int trtp_manager_signal_frame_corrupted(trtp_manager_t* self, uint32_t ssrc_media) int trtp_manager_signal_frame_corrupted(trtp_manager_t* self, uint32_t ssrc_media)
{ {
if(self && self->rtcp.session){ 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) // Stop the RTCP session first (will send BYE)
if(self->rtcp.session){ if(self->rtcp.session){
ret = trtp_rtcp_session_stop(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 // Free transport to force next call to start() to create new one with new sockets