1 /*
2 mediastreamer2 library - modular sound and video processing and streaming
3 Copyright (C) 2017 Belledonne Communications SARL
4 
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 */
19 
20 #include "mediastreamer2/bitratecontrol.h"
21 #include "mediastreamer2/mediastream.h"
22 #include "mediastreamer2/mscommon.h"
23 #include <ortp/ortp.h>
24 
25 
ms_bandwidth_controller_new(void)26 MSBandwidthController *ms_bandwidth_controller_new(void){
27 	MSBandwidthController *obj = ms_new0(MSBandwidthController, 1);
28 	return obj;
29 }
30 
ms_bandwidth_controller_estimate_bandwidths(MSBandwidthController * obj)31 static void ms_bandwidth_controller_estimate_bandwidths(MSBandwidthController *obj){
32 	bctbx_list_t *elem;
33 	float bandwidth = 0;
34 
35 	for (elem = obj->streams; elem != NULL; elem = elem->next){
36 		MediaStream *ms = (MediaStream*) elem->data;
37 		float bw;
38 		if (media_stream_get_state(ms) != MSStreamStarted) continue;
39 		bw = rtp_session_get_recv_bandwidth_smooth(ms->sessions.rtp_session);
40 		bandwidth += bw;
41 		if (ms == obj->controlled_stream) obj->stats.controlled_stream_bandwidth = bw;
42 	}
43 	obj->stats.estimated_download_bandwidth = bandwidth;
44 	ms_message("MSBandwidthController: total received bandwidth is %f kbit/s, controlled stream bandwidth is %f kbit/s", bandwidth*1e-3, obj->stats.controlled_stream_bandwidth*1.e-3);
45 }
46 
compute_target_bandwith_for_controlled_stream(MSBandwidthController * obj,float reduction_factor)47 static float compute_target_bandwith_for_controlled_stream(MSBandwidthController *obj, float reduction_factor){
48 	float bw_used_by_other_streams;
49 	float new_total_bandwidth_requested;
50 	float controlled_stream_bandwidth_requested;
51 
52 	bw_used_by_other_streams = obj->stats.estimated_download_bandwidth - obj->stats.controlled_stream_bandwidth;
53 	new_total_bandwidth_requested = obj->stats.estimated_download_bandwidth * reduction_factor;
54 	controlled_stream_bandwidth_requested = new_total_bandwidth_requested - bw_used_by_other_streams;
55 	if (controlled_stream_bandwidth_requested <= 0){
56 		ms_error("MSBandwidthController: controlled stream bandwidth requested is %f (bug)", controlled_stream_bandwidth_requested);
57 	}
58 	return controlled_stream_bandwidth_requested;
59 }
60 
resync_jitter_buffers(MSBandwidthController * obj)61 static void resync_jitter_buffers(MSBandwidthController *obj){
62 	bctbx_list_t *elem;
63 
64 	for (elem = obj->streams; elem != NULL; elem = elem->next){
65 		MediaStream *ms = (MediaStream*) elem->data;
66 		rtp_session_resync(ms->sessions.rtp_session);
67 	}
68 }
69 
on_congestion_state_changed(const OrtpEventData * evd,void * user_pointer)70 static void on_congestion_state_changed(const OrtpEventData *evd, void *user_pointer){
71 	MediaStream *ms = (MediaStream*)user_pointer;
72 	MSBandwidthController *obj = ms->bandwidth_controller;
73 	float controlled_stream_bandwidth_requested;
74 	RtpSession *session;
75 	OrtpVideoBandwidthEstimatorParams video_bandwidth_estimator_params = {0};
76 
77 	if (ms != obj->controlled_stream){
78 		ms_message("MSBandwidthController: congestion event (%i) received on stream [%p][%s], not the controlled one.", (int)evd->info.congestion_detected,
79 			   ms, ms_format_type_to_string(ms->type));
80 		return;
81 	}
82 	obj->congestion_detected = evd->info.congestion_detected;
83 	session = obj->controlled_stream->sessions.rtp_session;
84 	if (evd->info.congestion_detected){
85 		/*We are detecting a congestion. First estimate the total bandwidth received at the time of the congestion*/
86 		ms_bandwidth_controller_estimate_bandwidths(obj);
87 		/*we need to clear the congestion by firstly requesting a bandwidth usage much lower than the theoritically possible,
88 		 so that the congested router can finaly expedite all the late packets it retains.*/
89 		controlled_stream_bandwidth_requested = compute_target_bandwith_for_controlled_stream(obj, 0.7f);
90 
91 		if (controlled_stream_bandwidth_requested > 0){
92 			ms_message("MSBandwidthController: congestion detected - sending tmmbr for stream [%p][%s] for target [%f] kbit/s",
93 				   obj->controlled_stream, ms_format_type_to_string(obj->controlled_stream->type), controlled_stream_bandwidth_requested*1e-3);
94 		}
95 
96 		video_bandwidth_estimator_params.enabled = FALSE;
97 	}else{
98 		/*now that the congestion has ended, we can submit a new TMMBR to request a bandwidth closer to the maximum available*/
99 		controlled_stream_bandwidth_requested = compute_target_bandwith_for_controlled_stream(obj, 0.9f);
100 
101 		if (controlled_stream_bandwidth_requested > 0){
102 			ms_message("MSBandwidthController: congestion resolved - sending tmmbr for stream [%p][%s] for target [%f] kbit/s",
103 				   obj->controlled_stream, ms_format_type_to_string(obj->controlled_stream->type), controlled_stream_bandwidth_requested*1e-3);
104 		}
105 		/*we shall reset the jitter buffers, so that they recover faster their diverged states*/
106 		resync_jitter_buffers(obj);
107 		video_bandwidth_estimator_params.enabled = FALSE; // Set to TRUE to enable
108 	}
109 	rtp_session_send_rtcp_fb_tmmbr(session, (uint64_t)controlled_stream_bandwidth_requested);
110 	obj->remote_video_bandwidth_available_estimated = 0;
111 	rtp_session_enable_video_bandwidth_estimator(obj->controlled_stream->sessions.rtp_session, &video_bandwidth_estimator_params);
112 }
113 
on_video_bandwidth_estimation_available(const OrtpEventData * evd,void * user_pointer)114 static void on_video_bandwidth_estimation_available(const OrtpEventData *evd, void *user_pointer) {
115 	MediaStream *ms = (MediaStream*)user_pointer;
116 	MSBandwidthController *obj = ms->bandwidth_controller;
117 	if (!obj->congestion_detected) {
118 		RtpSession *session = obj->controlled_stream->sessions.rtp_session;
119 		float estimated_bitrate = evd->info.video_bandwidth_available;
120 		if (estimated_bitrate >= obj->remote_video_bandwidth_available_estimated) {
121 			ms_message("MSBandwidthController: video bandwidth estimation available, sending tmmbr for stream [%p][%s] for target [%f] kbit/s",
122 					obj->controlled_stream, ms_format_type_to_string(obj->controlled_stream->type), estimated_bitrate / 1000);
123 			obj->remote_video_bandwidth_available_estimated = estimated_bitrate;
124 			rtp_session_send_rtcp_fb_tmmbr(session, (uint64_t)estimated_bitrate);
125 		} else {
126 			ms_message("MSBandwidthController: discarding available video bandwidth estimation (%f kbit/s) because it's lower than the previous one (%f kbit/s)", estimated_bitrate, obj->remote_video_bandwidth_available_estimated);
127 		}
128 	}
129 }
130 
131 /*THis function just selects a video stream if any, or an audio stream otherwise.
132  * It could be refined to select the most consuming stream...*/
elect_controlled_stream(MSBandwidthController * obj)133 static void elect_controlled_stream(MSBandwidthController *obj){
134 	bctbx_list_t *elem;
135 	bool_t done = FALSE;
136 	OrtpVideoBandwidthEstimatorParams params = {0};
137 
138 	obj->controlled_stream = NULL;
139 	for (elem = obj->streams; elem != NULL && !done; elem = elem->next){
140 		MediaStream *ms = (MediaStream*) elem->data;
141 		switch(ms->type){
142 			case MSAudio:
143 				obj->controlled_stream = ms;
144 			break;
145 			case MSVideo:
146 				obj->controlled_stream = ms;
147 				done = TRUE;
148 				ortp_ev_dispatcher_connect(media_stream_get_event_dispatcher(ms), ORTP_EVENT_NEW_VIDEO_BANDWIDTH_ESTIMATION_AVAILABLE, 0,
149 											on_video_bandwidth_estimation_available, ms);
150 				params.enabled = FALSE; // Set to TRUE to enable
151 				rtp_session_enable_video_bandwidth_estimator(ms->sessions.rtp_session, &params);
152 			break;
153 			case MSText:
154 			break;
155 			case MSUnknownMedia:
156 			break;
157 		}
158 	}
159 }
160 
ms_bandwidth_controller_add_stream(MSBandwidthController * obj,struct _MediaStream * stream)161 void ms_bandwidth_controller_add_stream(MSBandwidthController *obj, struct _MediaStream *stream){
162 	ortp_ev_dispatcher_connect(media_stream_get_event_dispatcher(stream), ORTP_EVENT_CONGESTION_STATE_CHANGED, 0,
163 		on_congestion_state_changed, stream);
164 	rtp_session_enable_congestion_detection(stream->sessions.rtp_session, TRUE);
165 	stream->bandwidth_controller = obj;
166 	obj->streams = bctbx_list_append(obj->streams, stream);
167 	elect_controlled_stream(obj);
168 }
169 
ms_bandwidth_controller_remove_stream(MSBandwidthController * obj,struct _MediaStream * stream)170 void ms_bandwidth_controller_remove_stream(MSBandwidthController *obj, struct _MediaStream *stream){
171 	OrtpVideoBandwidthEstimatorParams params = {0};
172 	if (bctbx_list_find(obj->streams, stream) == NULL) return;
173 	ortp_ev_dispatcher_disconnect(media_stream_get_event_dispatcher(stream), ORTP_EVENT_CONGESTION_STATE_CHANGED, 0,
174 		on_congestion_state_changed);
175 	rtp_session_enable_congestion_detection(stream->sessions.rtp_session, FALSE);
176 	ortp_ev_dispatcher_disconnect(media_stream_get_event_dispatcher(stream), ORTP_EVENT_NEW_VIDEO_BANDWIDTH_ESTIMATION_AVAILABLE, 0,
177 		on_video_bandwidth_estimation_available);
178 	params.enabled = FALSE;
179 	rtp_session_enable_video_bandwidth_estimator(stream->sessions.rtp_session, &params);
180 	stream->bandwidth_controller = NULL;
181 	obj->streams = bctbx_list_remove(obj->streams, stream);
182 	elect_controlled_stream(obj);
183 }
184 
ms_bandwidth_controller_get_stats(MSBandwidthController * obj)185 const MSBandwidthControllerStats * ms_bandwidth_controller_get_stats(MSBandwidthController *obj){
186 	return &obj->stats;
187 }
188 
ms_bandwidth_controller_destroy(MSBandwidthController * obj)189 void ms_bandwidth_controller_destroy(MSBandwidthController *obj){
190 	bctbx_list_free(obj->streams);
191 	obj->streams = NULL;
192 	ms_free(obj);
193 }
194