Commit 65325be8 authored by Ghislain MARY's avatar Ghislain MARY
Browse files

Asynchronous ICE candidates gathering.

parent 4e611444
......@@ -109,8 +109,16 @@ typedef struct _IceSession {
uint8_t keepalive_timeout; /**< Configuration parameter to define the timeout between each keepalive packets (default is 15s) */
uint64_t event_time; /**< Time when an event must be sent */
bool_t send_event; /**< Boolean value telling whether an event must be sent or not */
struct sockaddr_storage ss; /**< STUN server address to use for the candidates gathering process */
socklen_t ss_len; /**< Length of the STUN server address to use for the candidates gathering process */
} IceSession;
typedef struct _IceStunServerCheck {
ortp_socket_t sock;
uint64_t transmission_time;
uint8_t nb_transmissions;
} IceStunServerCheck;
/**
* Structure representing an ICE transport address.
*/
......@@ -175,8 +183,10 @@ typedef struct _IceValidCandidatePair {
*/
typedef struct _IceCheckList {
IceSession *session; /**< Pointer to the ICE session */
RtpSession *rtp_session; /**< Pointer to the RTP session associated with this ICE check list */
char *remote_ufrag; /**< Remote username fragment for this check list (provided via SDP by the peer) */
char *remote_pwd; /**< Remote password for this check list (provided via SDP by the peer) */
MSList *stun_server_checks; /**< List of IceStunServerCheck structures */
MSList *local_candidates; /**< List of IceCandidate structures */
MSList *remote_candidates; /**< List of IceCandidate structures */
MSList *pairs; /**< List of IceCandidatePair structures */
......@@ -191,6 +201,7 @@ typedef struct _IceCheckList {
uint64_t keepalive_time; /**< Time when the last keepalive packet has been sent for this stream */
uint32_t foundation_generator; /**< Autoincremented integer to generate unique foundation values */
bool_t mismatch; /**< Boolean value telling whether there was a mismatch during the answer/offer process */
bool_t gathering_candidates; /**< Boolean value telling whether a candidate gathering process is running or not */
} IceCheckList;
......@@ -346,6 +357,15 @@ MS2_PUBLIC void ice_session_set_keepalive_timeout(IceSession *session, uint8_t t
*/
MS2_PUBLIC void ice_session_add_check_list(IceSession *session, IceCheckList *cl);
/**
* Gather ICE local candidates for an ICE session.
*
* @param session A pointer to a session
* @param ss The STUN server address
* @param ss_len The length of the STUN server address
*/
MS2_PUBLIC void ice_session_gather_candidates(IceSession *session, struct sockaddr_storage ss, socklen_t ss_len);
/**
* Get the state of an ICE check list.
*
......@@ -362,6 +382,14 @@ MS2_PUBLIC IceCheckListState ice_check_list_state(const IceCheckList *cl);
*/
MS2_PUBLIC void ice_check_list_set_state(IceCheckList *cl, IceCheckListState state);
/**
* Assign an RTP session to an ICE check list.
*
* @param cl A pointer to a check list
* @param rtp_session A pointer to the RTP session to assign to the check list
*/
MS2_PUBLIC void ice_check_list_set_rtp_session(IceCheckList *cl, RtpSession *rtp_session);
/**
* Get the local username fragment of an ICE check list.
*
......
......@@ -65,6 +65,7 @@ struct _AudioStream
MSFilter *write_resampler;
MSFilter *equalizer;
MSFilter *dummy;
MSFilter *voidsink;
uint64_t last_packet_count;
time_t last_packet_time;
EchoLimiterType el_type; /*use echo limiter: two MSVolume, measured input level controlling local output level*/
......@@ -149,6 +150,8 @@ MS2_PUBLIC int audio_stream_start_full(AudioStream *stream, RtpProfile *profile,
const char *rem_rtcp_ip, int rem_rtcp_port, int payload,int jitt_comp, const char *infile, const char *outfile,
MSSndCard *playcard, MSSndCard *captcard, bool_t use_ec);
MS2_PUBLIC void audio_stream_start_ice_gathering(AudioStream *stream);
MS2_PUBLIC void audio_stream_play(AudioStream *st, const char *name);
MS2_PUBLIC void audio_stream_record(AudioStream *st, const char *name);
......@@ -305,6 +308,7 @@ struct _VideoStream
MSFilter *tee2;
MSFilter *jpegwriter;
MSFilter *output2;
MSFilter *voidsink;
OrtpEvQueue *evq;
MSVideoSize sent_vsize;
int corner; /*for selfview*/
......@@ -339,6 +343,7 @@ MS2_PUBLIC void video_stream_set_event_callback(VideoStream *s, VideoStreamEvent
MS2_PUBLIC void video_stream_set_display_filter_name(VideoStream *s, const char *fname);
MS2_PUBLIC int video_stream_start(VideoStream * stream, RtpProfile *profile, const char *rem_rtp_ip, int rem_rtp_port, const char *rem_rtcp_ip, int rem_rtcp_port,
int payload, int jitt_comp, MSWebCam *device);
MS2_PUBLIC void video_stream_start_ice_gathering(VideoStream *stream);
MS2_PUBLIC void video_stream_set_relay_session_id(VideoStream *stream, const char *relay_session_id);
......
......@@ -73,6 +73,7 @@ void audio_stream_free(AudioStream *stream)
if (stream->write_resampler!=NULL) ms_filter_destroy(stream->write_resampler);
if (stream->dtmfgen_rtp!=NULL) ms_filter_destroy(stream->dtmfgen_rtp);
if (stream->dummy) ms_filter_destroy(stream->dummy);
if (stream->voidsink) ms_filter_destroy(stream->voidsink);
if (stream->rc) ms_bitrate_controller_destroy(stream->rc);
if (stream->qi) ms_quality_indicator_destroy(stream->qi);
ms_free(stream);
......@@ -322,6 +323,14 @@ static void stop_preload_graph(AudioStream *stream){
stream->dummy=NULL;
}
static void stop_ice_gathering_graph(AudioStream *stream){
ms_ticker_detach(stream->ticker,stream->voidsink);
ms_filter_unlink(stream->rtprecv,0,stream->voidsink,0);
ms_filter_destroy(stream->voidsink);
ms_filter_destroy(stream->rtprecv);
stream->voidsink=stream->rtprecv=NULL;
}
bool_t audio_stream_started(AudioStream *stream){
return stream->start_time!=0;
}
......@@ -544,7 +553,7 @@ int audio_stream_start_full(AudioStream *stream, RtpProfile *profile, const char
if (stream->ticker==NULL) start_ticker(stream);
else{
/*we were using the dummy preload graph, destroy it*/
stop_preload_graph(stream);
if (stream->dummy) stop_preload_graph(stream);
}
/* and then connect all */
......@@ -718,6 +727,17 @@ int audio_stream_start_now(AudioStream *stream, RtpProfile * prof, const char *
payload_type,jitt_comp,NULL,NULL,playcard,captcard,use_ec);
}
void audio_stream_start_ice_gathering(AudioStream *stream)
{
if (stream->ticker==NULL) start_ticker(stream);
stream->voidsink=ms_filter_new(MS_VOID_SINK_ID);
stream->rtprecv=ms_filter_new(MS_RTP_RECV_ID);
rtp_session_set_payload_type(stream->session,0);
ms_filter_call_method(stream->rtprecv,MS_RTP_RECV_SET_SESSION,stream->session);
ms_filter_link(stream->rtprecv,0,stream->voidsink,0);
ms_ticker_attach(stream->ticker,stream->rtprecv);
}
void audio_stream_set_relay_session_id(AudioStream *stream, const char *id){
ms_filter_call_method(stream->rtpsend, MS_RTP_SEND_SET_RELAY_SESSION_ID,(void*)id);
}
......@@ -794,6 +814,8 @@ void audio_stream_stop(AudioStream * stream)
if (stream->dummy){
stop_preload_graph(stream);
}else if (stream->voidsink){
stop_ice_gathering_graph(stream);
}else if (stream->start_time!=0){
ms_ticker_detach(stream->ticker,stream->soundread);
......
......@@ -92,13 +92,14 @@ typedef struct _Addr_Ports {
} Addr_Ports;
// TODO: We need this function to push events in the rtp event queue but it should not be made public in oRTP.
// Should we not move ice processing to oRTP instead?
// WARNING: We need this function to push events in the rtp event queue but it should not be made public in oRTP.
extern void rtp_session_dispatch_event(RtpSession *session, OrtpEvent *ev);
static void ice_send_stun_server_binding_request(ortp_socket_t sock, const struct sockaddr *server, socklen_t addrlen, int id);
static int ice_compare_transport_addresses(const IceTransportAddress *ta1, const IceTransportAddress *ta2);
static int ice_compare_pair_priorities(const IceCandidatePair *p1, const IceCandidatePair *p2);
static int ice_find_host_candidate(const IceCandidate *candidate, const uint16_t *componentID);
static int ice_find_nominated_valid_pair_from_componentID(const IceValidCandidatePair* valid_pair, const uint16_t* componentID);
static void ice_pair_set_state(IceCandidatePair *pair, IceCandidatePairState state);
static void ice_compute_candidate_foundation(IceCandidate *candidate, IceCheckList *cl);
......@@ -208,7 +209,9 @@ void ice_session_destroy(IceSession *session)
static void ice_check_list_init(IceCheckList *cl)
{
cl->session = NULL;
cl->rtp_session = NULL;
cl->remote_ufrag = cl->remote_pwd = NULL;
cl->stun_server_checks = NULL;
cl->local_candidates = cl->remote_candidates = cl->pairs = cl->triggered_checks_queue = cl->check_list = cl->valid_list = NULL;
cl->local_componentIDs = cl->remote_componentIDs = cl->foundations = NULL;
cl->state = ICL_Running;
......@@ -216,6 +219,7 @@ static void ice_check_list_init(IceCheckList *cl)
cl->keepalive_time = 0;
cl->foundation_generator = 1;
cl->mismatch = FALSE;
cl->gathering_candidates = FALSE;
}
IceCheckList * ice_check_list_new(void)
......@@ -268,6 +272,11 @@ static IceCandidatePair *ice_pair_new(IceCheckList *cl, IceCandidate* local_cand
return pair;
}
static void ice_free_stun_server_check(IceStunServerCheck *check)
{
ms_free(check);
}
static void ice_free_pair_foundation(IcePairFoundation *foundation)
{
ms_free(foundation);
......@@ -292,11 +301,13 @@ void ice_check_list_destroy(IceCheckList *cl)
{
if (cl->remote_ufrag) ms_free(cl->remote_ufrag);
if (cl->remote_pwd) ms_free(cl->remote_pwd);
ms_list_for_each(cl->stun_server_checks, (void (*)(void*))ice_free_stun_server_check);
ms_list_for_each(cl->foundations, (void (*)(void*))ice_free_pair_foundation);
ms_list_for_each(cl->valid_list, (void (*)(void*))ice_free_valid_pair);
ms_list_for_each(cl->pairs, (void (*)(void*))ice_free_candidate_pair);
ms_list_for_each(cl->remote_candidates, (void (*)(void*))ice_free_candidate);
ms_list_for_each(cl->local_candidates, (void (*)(void*))ice_free_candidate);
ms_list_free(cl->stun_server_checks);
ms_list_free(cl->foundations);
ms_list_free(cl->local_componentIDs);
ms_list_free(cl->remote_componentIDs);
......@@ -355,6 +366,11 @@ void ice_check_list_set_state(IceCheckList *cl, IceCheckListState state)
cl->state = state;
}
void ice_check_list_set_rtp_session(IceCheckList *cl, RtpSession *rtp_session)
{
cl->rtp_session = rtp_session;
}
const char * ice_check_list_local_ufrag(const IceCheckList* cl)
{
/* Do not handle media specific ufrag for the moment, so use the session local ufrag. */
......@@ -622,10 +638,89 @@ void ice_session_check_mismatch(IceSession *session)
}
/******************************************************************************
* CANDIDATES GATHERING *
*****************************************************************************/
static void ice_check_list_gather_candidates(IceCheckList *cl, IceSession *session)
{
IceStunServerCheck *check;
ortp_socket_t sock = -1;
uint64_t curtime = session->ticker->time;
int index = ms_list_index(session->streams, cl);
if (cl->rtp_session != NULL) {
cl->gathering_candidates = TRUE;
sock = rtp_session_get_rtp_socket(cl->rtp_session);
if (sock > 0) {
check = (IceStunServerCheck *)ms_new0(IceStunServerCheck, 1);
check->sock = sock;
if (index == 0) {
check->transmission_time = curtime + ICE_DEFAULT_RTO_DURATION;
check->nb_transmissions = 1;
ice_send_stun_server_binding_request(sock, (struct sockaddr *)&cl->session->ss, cl->session->ss_len, check->sock);
} else {
check->transmission_time = curtime + 2 * index * ICE_DEFAULT_TA_DURATION;
}
cl->stun_server_checks = ms_list_append(cl->stun_server_checks, check);
}
sock = rtp_session_get_rtcp_socket(cl->rtp_session);
if (sock > 0) {
check = (IceStunServerCheck *)ms_new0(IceStunServerCheck, 1);
check->sock = sock;
check->transmission_time = curtime + 2 * index * ICE_DEFAULT_TA_DURATION + ICE_DEFAULT_TA_DURATION;
cl->stun_server_checks = ms_list_append(cl->stun_server_checks, check);
}
}
}
void ice_session_gather_candidates(IceSession *session, struct sockaddr_storage ss, socklen_t ss_len)
{
session->ss = ss;
session->ss_len = ss_len;
ms_list_for_each2(session->streams, (void (*)(void*,void*))ice_check_list_gather_candidates, session);
}
/******************************************************************************
* STUN PACKETS HANDLING *
*****************************************************************************/
static void ice_send_stun_server_binding_request(ortp_socket_t sock, const struct sockaddr *server, socklen_t addrlen, int id)
{
StunMessage msg;
StunAtrString username;
StunAtrString password;
char buf[STUN_MAX_MESSAGE_SIZE];
int len = STUN_MAX_MESSAGE_SIZE;
const struct sockaddr_in *servaddr = (const struct sockaddr_in *)server;
memset(&msg, 0, sizeof(StunMessage));
memset(&username,0,sizeof(username));
memset(&password,0,sizeof(password));
stunBuildReqSimple(&msg, &username, FALSE, FALSE, id);
len = stunEncodeMessage(&msg, buf, len, &password);
if (len > 0) {
sendMessage(sock, buf, len, htonl(servaddr->sin_addr.s_addr), htons(servaddr->sin_port));
}
}
static int ice_parse_stun_server_binding_response(const StunMessage *msg, char *addr, int addr_len, int *port)
{
struct in_addr ia;
if (msg->hasXorMappedAddress) {
*port = msg->xorMappedAddress.ipv4.port;
ia.s_addr = htonl(msg->xorMappedAddress.ipv4.addr);
} else if (msg->hasMappedAddress) {
*port = msg->mappedAddress.ipv4.port;
ia.s_addr = htonl(msg->mappedAddress.ipv4.addr);
} else return -1;
strncpy(addr, inet_ntoa(ia), addr_len);
return 0;
}
/* Send a STUN binding request for ICE connectivity checks according to 7.1.2. */
static void ice_send_binding_request(IceCheckList *cl, IceCandidatePair *pair, const RtpSession *rtp_session)
{
......@@ -719,6 +814,17 @@ static void ice_send_binding_request(IceCheckList *cl, IceCandidatePair *pair, c
}
}
static int ice_get_componentID_from_rtp_session(const OrtpEventData *evt_data)
{
if (evt_data->info.socket_type == OrtpRTPSocket) {
return 1;
} else if (evt_data->info.socket_type == OrtpRTCPSocket) {
return 2;
}
return -1;
}
static int ice_get_socket_from_rtp_session(const RtpSession *rtp_session, const OrtpEventData *evt_data)
{
if (evt_data->info.socket_type == OrtpRTPSocket) {
......@@ -977,13 +1083,10 @@ static IceCandidate * ice_learn_peer_reflexive_candidate(IceCheckList *cl, const
char foundation[32];
IceCandidate *candidate = NULL;
MSList *elem;
uint16_t componentID;
int componentID;
if (evt_data->info.socket_type == OrtpRTPSocket) {
componentID = 1;
} else if (evt_data->info.socket_type == OrtpRTCPSocket) {
componentID = 2;
} else return NULL;
componentID = ice_get_componentID_from_rtp_session(evt_data);
if (componentID < 0) return NULL;
elem = ms_list_find_custom(cl->remote_candidates, (MSCompareFunc)ice_find_candidate_from_transport_address, taddr);
if (elem == NULL) {
......@@ -1098,6 +1201,16 @@ static void ice_handle_received_binding_request(IceCheckList *cl, RtpSession *rt
ice_conclude_processing(cl, rtp_session);
}
static int ice_find_stun_server_check(const IceStunServerCheck *check, const ortp_socket_t *sock)
{
return !(check->sock == *sock);
}
static int ice_find_check_list_gathering_candidates(const IceCheckList *cl, const void *dummy)
{
return (cl->gathering_candidates == FALSE);
}
static int ice_find_pair_from_transactionID(const IceCandidatePair *pair, const UInt96 *transactionID)
{
return memcmp(&pair->transactionID, transactionID, sizeof(pair->transactionID));
......@@ -1258,7 +1371,45 @@ static void ice_handle_received_binding_response(IceCheckList *cl, RtpSession *r
IceCandidatePair *valid_pair;
IceCandidate *candidate;
IceCandidatePairState succeeded_pair_previous_state;
MSList *elem = ms_list_find_custom(cl->check_list, (MSCompareFunc)ice_find_pair_from_transactionID, &msg->msgHdr.tr_id);
MSList *elem;
MSList *base_elem;
OrtpEvent *ev;
char addr[64];
int port;
ortp_socket_t sock;
int componentID;
const struct sockaddr_in *servaddr = (const struct sockaddr_in *)&cl->session->ss;
if (cl->gathering_candidates == TRUE) {
if ((htonl(remote_addr->addr) == servaddr->sin_addr.s_addr) && (htons(remote_addr->port) == servaddr->sin_port)) {
sock = ice_get_socket_from_rtp_session(rtp_session, evt_data);
elem = ms_list_find_custom(cl->stun_server_checks, (MSCompareFunc)ice_find_stun_server_check, &sock);
if (elem != NULL) {
componentID = ice_get_componentID_from_rtp_session(evt_data);
if ((componentID > 0) && (ice_parse_stun_server_binding_response(msg, addr, sizeof(addr), &port) >= 0)) {
base_elem = ms_list_find_custom(cl->local_candidates, (MSCompareFunc)ice_find_host_candidate, &componentID);
if (base_elem != NULL) {
candidate = (IceCandidate *)base_elem->data;
ice_add_local_candidate(cl, "srflx", addr, port, componentID, candidate);
}
}
cl->stun_server_checks = ms_list_remove_link(cl->stun_server_checks, elem);
}
if (ms_list_size(cl->stun_server_checks) == 0) {
cl->gathering_candidates = FALSE;
ms_message("Finished candidates gathering for check list %p", cl);
ice_dump_candidates(cl);
if (ms_list_find_custom(cl->session->streams, (MSCompareFunc)ice_find_check_list_gathering_candidates, NULL) == NULL) {
/* Notify the application when there is no longer any check list gathering candidates. */
ev = ortp_event_new(ORTP_EVENT_ICE_GATHERING_FINISHED);
ortp_event_get_data(ev)->info.ice_processing_successful = TRUE;
rtp_session_dispatch_event(rtp_session, ev);
}
}
}
}
elem = ms_list_find_custom(cl->check_list, (MSCompareFunc)ice_find_pair_from_transactionID, &msg->msgHdr.tr_id);
if (elem == NULL) {
/* We received an error response concerning an unknown binding request, ignore it... */
char tr_id_str[25];
......@@ -2039,6 +2190,19 @@ static void ice_conclude_processing(IceCheckList *cl, RtpSession *rtp_session)
* GLOBAL PROCESS *
*****************************************************************************/
static void ice_send_stun_server_checks(IceStunServerCheck *check, IceCheckList *cl)
{
uint64_t curtime = cl->session->ticker->time;
if (curtime >= check->transmission_time) {
check->nb_transmissions++;
if (check->nb_transmissions <= ICE_MAX_RETRANSMISSIONS) {
check->transmission_time = curtime + ICE_DEFAULT_RTO_DURATION;
ice_send_stun_server_binding_request(check->sock, (struct sockaddr *)&cl->session->ss, cl->session->ss_len, check->sock);
}
}
}
static void ice_handle_connectivity_check_retransmission(IceCandidatePair *pair, const CheckList_RtpSession_Time *params)
{
if ((pair->state == ICP_InProgress) && ((params->time - pair->transmission_time) >= pair->rto)) {
......@@ -2071,9 +2235,15 @@ void ice_check_list_process(IceCheckList *cl, RtpSession *rtp_session)
bool_t retransmissions_pending = FALSE;
if (cl->session == NULL) return;
curtime = cl->session->ticker->time;
/* Send STUN server requests to gather candidates if needed. */
if (cl->gathering_candidates == TRUE) {
ms_list_for_each2(cl->stun_server_checks, (void (*)(void*,void*))ice_send_stun_server_checks, cl);
}
if ((cl->session->state == IS_Stopped) || (cl->session->state == IS_Failed)) return;
curtime = cl->session->ticker->time;
switch (cl->state) {
case ICL_Completed:
/* Handle keepalive. */
......
......@@ -69,6 +69,8 @@ void video_stream_free (VideoStream * stream)
ms_filter_destroy(stream->jpegwriter);
if (stream->output2!=NULL)
ms_filter_destroy(stream->output2);
if (stream->voidsink!=NULL)
ms_filter_destroy(stream->voidsink);
if (stream->ticker != NULL)
ms_ticker_destroy (stream->ticker);
if (stream->evq!=NULL)
......@@ -155,6 +157,14 @@ static void video_steam_process_rtcp(VideoStream *stream, mblk_t *m){
}while(rtcp_next_packet(m));
}
static void stop_ice_gathering_graph(VideoStream *stream){
ms_ticker_detach(stream->ticker,stream->voidsink);
ms_filter_unlink(stream->rtprecv,0,stream->voidsink,0);
ms_filter_destroy(stream->voidsink);
ms_filter_destroy(stream->rtprecv);
stream->voidsink=stream->rtprecv=NULL;
}
static void video_stream_set_remote_from_ice(VideoStream *stream){
char rtp_addr[64];
char rtcp_addr[64];
......@@ -369,6 +379,17 @@ static void configure_video_source(VideoStream *stream){
}
}
static void start_ticker(VideoStream *stream){
MSTickerParams params={0};
params.name="Video MSTicker";
#ifdef __ios
params.prio=MS_TICKER_PRIO_HIGH;
#else
params.prio=MS_TICKER_PRIO_NORMAL;
#endif
stream->ticker = ms_ticker_new_with_params(&params);
}
int video_stream_start (VideoStream *stream, RtpProfile *profile, const char *rem_rtp_ip, int rem_rtp_port,
const char *rem_rtcp_ip, int rem_rtcp_port, int payload, int jitt_comp, MSWebCam *cam){
PayloadType *pt;
......@@ -520,14 +541,7 @@ int video_stream_start (VideoStream *stream, RtpProfile *profile, const char *re
}
/* create the ticker */
MSTickerParams params={0};
params.name="Video MSTicker";
#ifdef __ios
params.prio=MS_TICKER_PRIO_HIGH;
#else
params.prio=MS_TICKER_PRIO_NORMAL;
#endif
stream->ticker = ms_ticker_new_with_params(&params);
start_ticker(stream);
/* attach the graphs */
if (stream->source)
......@@ -537,6 +551,16 @@ int video_stream_start (VideoStream *stream, RtpProfile *profile, const char *re
return 0;
}
void video_stream_start_ice_gathering(VideoStream *stream){
if (stream->ticker==NULL) start_ticker(stream);
stream->voidsink=ms_filter_new(MS_VOID_SINK_ID);
stream->rtprecv=ms_filter_new(MS_RTP_RECV_ID);
rtp_session_set_payload_type(stream->session,0);
ms_filter_call_method(stream->rtprecv,MS_RTP_RECV_SET_SESSION,stream->session);
ms_filter_link(stream->rtprecv,0,stream->voidsink,0);
ms_ticker_attach(stream->ticker,stream->rtprecv);
}
void video_stream_update_video_params(VideoStream *stream){
/*calling video_stream_change_camera() does the job of unplumbing/replumbing and configuring the new graph*/
video_stream_change_camera(stream,stream->cam);
......@@ -593,37 +617,41 @@ video_stream_stop (VideoStream * stream)
stream->eventcb = NULL;
stream->event_pointer = NULL;
if (stream->ticker){
if (stream->source)
ms_ticker_detach(stream->ticker,stream->source);
if (stream->rtprecv)
ms_ticker_detach(stream->ticker,stream->rtprecv);
if (stream->ice_check_list != NULL) ice_check_list_print_route(stream->ice_check_list, "Video session's route");
rtp_stats_display(rtp_session_get_stats(stream->session),"Video session's RTP statistics");
if (stream->source){
ms_filter_unlink(stream->source,0,stream->pixconv,0);
ms_filter_unlink (stream->pixconv, 0, stream->sizeconv, 0);
ms_filter_unlink (stream->sizeconv, 0, stream->tee, 0);
ms_filter_unlink(stream->tee,0,stream->encoder,0);
ms_filter_unlink(stream->encoder, 0, stream->rtpsend,0);
if (stream->output2){
ms_filter_unlink(stream->tee,1,stream->output2,0);
if (stream->voidsink) {
stop_ice_gathering_graph(stream);
} else {
if (stream->source)
ms_ticker_detach(stream->ticker,stream->source);
if (stream->rtprecv)
ms_ticker_detach(stream->ticker,stream->rtprecv);
if (stream->ice_check_list != NULL) ice_check_list_print_route(stream->ice_check_list, "Video session's route");
rtp_stats_display(rtp_session_get_stats(stream->session),"Video session's RTP statistics");
if (stream->source){
ms_filter_unlink(stream->source,0,stream->pixconv,0);
ms_filter_unlink (stream->pixconv, 0, stream->sizeconv, 0);
ms_filter_unlink (stream->sizeconv, 0, stream->tee, 0);
ms_filter_unlink(stream->tee,0,stream->encoder,0);
ms_filter_unlink(stream->encoder, 0, stream->rtpsend,0);
if (stream->output2){
ms_filter_unlink(stream->tee,1,stream->output2,0);
}
}
}
if (stream->rtprecv){
MSConnectionHelper h;
ms_connection_helper_start (&h);
ms_connection_helper_unlink (&h,stream->rtprecv,-1,0);
ms_connection_helper_unlink (&h,stream->decoder,0,0);
if (stream->tee2){
ms_connection_helper_unlink (&h,stream->tee2,0,0);
ms_filter_unlink(stream->tee2,1,stream->jpegwriter,0);
if (stream->rtprecv){
MSConnectionHelper h;
ms_connection_helper_start (&h);
ms_connection_helper_unlink (&h,stream->rtprecv,-1,0);
ms_connection_helper_unlink (&h,stream->decoder,0,0);
if (stream->tee2){
ms_connection_helper_unlink (&h,stream->tee2,0,0);
ms_filter_unlink(stream->tee2,1,stream->jpegwriter,0);
}
if(stream->output)
ms_connection_helper_unlink (&h,stream->output,0,-1);
if (stream->tee && stream->output && stream->output2==NULL)
ms_filter_unlink(stream->tee,1,stream->output,1);
}
if(stream->output)
ms_connection_helper_unlink (&h,stream->output,0,-1);
if (stream->tee && stream->output && stream->output2==NULL)
ms_filter_unlink(stream->tee,1,stream->output,1);
}
}
video_stream_free (stream);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment