netsim.c 8.51 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
/*
  The oRTP library is an RTP (Realtime Transport Protocol - rfc3550) stack.
  Copyright (C) 2011 Belledonne Communications SARL
  Author: Simon MORLAT simon.morlat@linphone.org

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library 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
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "ortp/ortp.h"
#include "utils.h"
#include "ortp/rtpsession.h"
#include "rtpsession_priv.h"

static OrtpNetworkSimulatorCtx* simulator_ctx_new(void){
	OrtpNetworkSimulatorCtx *ctx=(OrtpNetworkSimulatorCtx*)ortp_malloc0(sizeof(OrtpNetworkSimulatorCtx));
28
	qinit(&ctx->latency_q);
29 30 31 32 33
	qinit(&ctx->q);
	return ctx;
}

void ortp_network_simulator_destroy(OrtpNetworkSimulatorCtx *sim){
34
	int drop_by_flush=sim->latency_q.q_mcount+sim->q.q_mcount;
35 36 37 38 39 40 41 42 43 44
	if (sim->total_count>0){
		ortp_message("Network simulation: destroyed. Statistics are:"
			"%d/%d(%.1f%%, param=%.1f) packets dropped by loss, "
			"%d/%d(%.1f%%) packets dropped by congestion, "
			"%d/%d(%.1f%%) packets flushed."
			, sim->drop_by_loss, sim->total_count, sim->drop_by_loss*100.f/sim->total_count, sim->params.loss_rate
			, sim->drop_by_congestion, sim->total_count, sim->drop_by_congestion*100.f/sim->total_count
			, drop_by_flush, sim->total_count, drop_by_flush*100.f/sim->total_count
		);
	}
45
	flushq(&sim->latency_q,0);
46 47 48 49 50 51 52
	flushq(&sim->q,0);
	ortp_free(sim);
}

void rtp_session_enable_network_simulation(RtpSession *session, const OrtpNetworkSimulatorParams *params){
	OrtpNetworkSimulatorCtx *sim=session->net_sim_ctx;
	if (params->enabled){
53 54
		if (sim==NULL)
			sim=simulator_ctx_new();
55
		sim->drop_by_congestion=sim->drop_by_loss=sim->total_count=0;
56
		sim->params=*params;
57
		if (sim->params.jitter_burst_density>0 && sim->params.jitter_strength>0 && sim->params.max_bandwidth==0){
58 59 60 61
			sim->params.max_bandwidth=1024000;
			ortp_message("Network simulation: jitter requested but max_bandwidth is not set. Using default value of %f bits/s.",
				sim->params.max_bandwidth);
		}
jehan's avatar
jehan committed
62 63
		if (sim->params.max_bandwidth && sim->params.max_buffer_size==0) {
			sim->params.max_buffer_size=sim->params.max_bandwidth;
64
			ortp_message("Network simulation: Max buffer size not set for RTP session [%p], using [%i]",session,sim->params.max_buffer_size);
jehan's avatar
jehan committed
65
		}
66
		session->net_sim_ctx=sim;
67

68
		ortp_message("Network simulation: enabled with the following parameters:\n"
69
				"\tlatency=%d\n"
70 71 72
				"\tloss_rate=%.1f\n"
				"\tconsecutive_loss_probability=%.1f\n"
				"\tmax_bandwidth=%.1f\n"
73
				"\tmax_buffer_size=%d\n"
74 75
				"\tjitter_density=%.1f\n"
				"\tjitter_strength=%.1f\n",
76 77 78 79 80
				params->latency,
				params->loss_rate,
				params->consecutive_loss_probability,
				params->max_bandwidth,
				params->max_buffer_size,
81
				params->jitter_burst_density,
82
				params->jitter_strength);
83 84 85 86 87 88 89 90 91 92
	}else{
		if (sim!=NULL) ortp_network_simulator_destroy(sim);
		session->net_sim_ctx=NULL;
	}
}

static int64_t elapsed_us(struct timeval *tv1, struct timeval *tv2){
	return ((tv2->tv_sec-tv1->tv_sec)*1000000LL)+((tv2->tv_usec-tv1->tv_usec));
}

93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
static mblk_t * simulate_latency(RtpSession *session, mblk_t *input){
	OrtpNetworkSimulatorCtx *sim=session->net_sim_ctx;
	struct timeval current;
	mblk_t *output=NULL;
	uint32_t current_ts;
	ortp_gettimeofday(&current,NULL);
	/*since we must store expiration date in reserved2(32bits) only(reserved1
	already used), we need to reduce time stamp to milliseconds only*/
	current_ts = 1000*current.tv_sec + current.tv_usec/1000;

	/*queue the packet - store expiration timestamps in reserved fields*/
	if (input){
		input->reserved2 = current_ts + sim->params.latency;
		putq(&sim->latency_q,input);
	}

	if ((output=peekq(&sim->latency_q))!=NULL){
		if (TIME_IS_NEWER_THAN(current_ts, output->reserved2)){
			output->reserved2=0;
			getq(&sim->latency_q);

			/*return the first dequeued packet*/
			return output;
		}
	}

	return NULL;
}

122
static int simulate_jitter_by_bit_budget_reduction(OrtpNetworkSimulatorCtx *sim, int budget_increase){
123
	unsigned int r=ortp_random()%1000;
124
	float threshold,score;
125
	int budget_adjust=0;
126
	uint64_t now=ortp_get_cur_time_ms();
127

128 129 130
	if (sim->last_jitter_event==0){
		sim->last_jitter_event=ortp_get_cur_time_ms();
	}
131

132
	if (sim->in_jitter_event){
133 134 135 136
		threshold=100;
		score=(float)r;
	}else{
		score=1000.0*(float)r*(now-sim->last_jitter_event)*sim->params.jitter_burst_density*1e-6;
137 138
		threshold=500;
	}
139 140
	if (score>(int)threshold){
		int64_t strength_rand=sim->params.jitter_strength * (float)(ortp_random()%1000);
141
		sim->in_jitter_event=TRUE;
142 143
		budget_adjust=-((int64_t)budget_increase*strength_rand/1000LL);
		/*ortp_message("jitter in progress... bit_budget_adjustement=%i, bit_budget=%i",budget_adjust,sim->bit_budget);*/
144
	}else if (sim->in_jitter_event){
145
		/*ortp_message("jitter ended.");*/
146
		sim->in_jitter_event=FALSE;
147
		sim->last_jitter_event=ortp_get_cur_time_ms();
148 149 150 151 152
	}
	return budget_adjust;
}

static mblk_t *simulate_bandwidth_limit_and_jitter(RtpSession *session, mblk_t *input){
153 154 155 156
	OrtpNetworkSimulatorCtx *sim=session->net_sim_ctx;
	struct timeval current;
	int64_t elapsed;
	int bits;
157
	int budget_increase;
158
	mblk_t *output=NULL;
159
	int overhead=(session->rtp.gs.sockfamily==AF_INET6) ? IP6_UDP_OVERHEAD : IP_UDP_OVERHEAD;
160

161
	ortp_gettimeofday(&current,NULL);
162

163 164
	if (sim->last_check.tv_sec==0){
		sim->last_check=current;
Simon Morlat's avatar
Simon Morlat committed
165
		sim->bit_budget=0;
166 167 168
	}
	/*update the budget */
	elapsed=elapsed_us(&sim->last_check,&current);
169 170 171
	budget_increase=(elapsed*(int64_t)sim->params.max_bandwidth)/1000000LL;
	sim->bit_budget+=budget_increase;
	sim->bit_budget+=simulate_jitter_by_bit_budget_reduction(sim,budget_increase);
172 173 174 175 176 177 178 179
	sim->last_check=current;
	/* queue the packet for sending*/
	if (input){
		putq(&sim->q,input);
		bits=(msgdsize(input)+overhead)*8;
		sim->qsize+=bits;
	}
	/*flow control*/
jehan's avatar
jehan committed
180
	while (sim->qsize>=sim->params.max_buffer_size){
181
		// ortp_message("rtp_session_network_simulate(): discarding packets.");
182 183 184 185
		output=getq(&sim->q);
		if (output){
			bits=(msgdsize(output)+overhead)*8;
			sim->qsize-=bits;
186
			sim->drop_by_congestion++;
187
			freemsg(output);
188 189
		}
	}
190

191
	output=NULL;
192

193 194 195 196 197 198 199 200 201
	/*see if we can output a packet*/
	if (sim->bit_budget>=0){
		output=getq(&sim->q);
		if (output){
			bits=(msgdsize(output)+overhead)*8;
			sim->bit_budget-=bits;
			sim->qsize-=bits;
		}
	}
Simon Morlat's avatar
Simon Morlat committed
202 203 204 205
	if (output==NULL && input==NULL && sim->bit_budget>=0){
		/* unused budget is lost...*/
		sim->last_check.tv_sec=0;
	}
206 207 208
	return output;
}

209
static mblk_t *simulate_loss_rate(OrtpNetworkSimulatorCtx *net_sim_ctx, mblk_t *input){
210
	int rrate;
211
	float loss_rate=net_sim_ctx->params.loss_rate*10.0;
212 213 214

	/*in order to simulate bursts of dropped packets, take into account a different probability after a loss occured*/
	if (net_sim_ctx->consecutive_drops>0){
215
		loss_rate=net_sim_ctx->params.consecutive_loss_probability*1000.0;
216
	}
217

218 219
	rrate = ortp_random() % 1000;

220
	if (rrate >= loss_rate) {
221 222
		if (net_sim_ctx->consecutive_drops){
			/*after a burst of lost packets*/
223
			net_sim_ctx->drops_to_ignore=net_sim_ctx->consecutive_drops - ((net_sim_ctx->consecutive_drops*net_sim_ctx->params.loss_rate)/100);
224 225
			net_sim_ctx->consecutive_drops=0;
		}
226 227 228 229
		return input;
	}
	if (net_sim_ctx->drops_to_ignore>0){
		net_sim_ctx->drops_to_ignore--;
Yann Diorcet's avatar
Yann Diorcet committed
230 231
		return input;
	}
232 233 234
	if (net_sim_ctx->params.consecutive_loss_probability>0){
		net_sim_ctx->consecutive_drops++;
	}
235
	net_sim_ctx->drop_by_loss++;
236
	freemsg(input);
Yann Diorcet's avatar
Yann Diorcet committed
237 238 239
	return NULL;
}

240
mblk_t * rtp_session_network_simulate(RtpSession *session, mblk_t *input, bool_t *is_rtp_packet){
241 242
	OrtpNetworkSimulatorCtx *sim=session->net_sim_ctx;
	mblk_t *om=NULL;
243

244
	om=input;
245 246

	/*while packet is stored in network simulator queue, keep its type in reserved1 space*/
247
	if (om != NULL){
248
		sim->total_count++;
249 250 251 252 253
		om->reserved1 = *is_rtp_packet;
	}

	if (sim->params.latency>0){
		om=simulate_latency(session,om);
254 255
	}

256
	if (sim->params.max_bandwidth>0){
257
		om=simulate_bandwidth_limit_and_jitter(session,om);
258
	}
259
	if (sim->params.loss_rate>0 && om){
260
		om=simulate_loss_rate(sim,om);
261
	}
262 263 264 265 266 267
	/*finally when releasing the packet from the simulator, reset the reserved1 space to default,
	since it will be used by mediastreamer later*/
	if (om != NULL){
		*is_rtp_packet = om->reserved1;
		om->reserved1 = 0;
	}
268 269 270
	return om;
}