netsim.c 5.3 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
	flushq(&sim->latency_q,0);
35 36 37 38 39 40 41 42 43
	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){
		if (sim==NULL) sim=simulator_ctx_new();
		sim->params=*params;
jehan's avatar
jehan committed
44 45
		if (sim->params.max_bandwidth && sim->params.max_buffer_size==0) {
			sim->params.max_buffer_size=sim->params.max_bandwidth;
46
			ortp_message("Max buffer size not set for RTP session [%p], using [%i]",session,sim->params.max_buffer_size);
jehan's avatar
jehan committed
47 48
		}

49 50 51 52 53 54 55 56 57 58 59 60 61 62
		session->net_sim_ctx=sim;
	}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));
}

#define IP_UDP_OVERHEAD (20+8)
#define IP6_UDP_OVERHEAD (40+8)

63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
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;
}

92 93 94 95 96 97 98
static mblk_t *simulate_bandwidth_limit(RtpSession *session, mblk_t *input){
	OrtpNetworkSimulatorCtx *sim=session->net_sim_ctx;
	struct timeval current;
	int64_t elapsed;
	int bits;
	mblk_t *output=NULL;
	int overhead=(session->rtp.sockfamily==AF_INET6) ? IP6_UDP_OVERHEAD : IP_UDP_OVERHEAD;
99

100
	ortp_gettimeofday(&current,NULL);
101

102 103
	if (sim->last_check.tv_sec==0){
		sim->last_check=current;
Simon Morlat's avatar
Simon Morlat committed
104
		sim->bit_budget=0;
105 106 107 108 109 110 111 112 113 114 115 116
	}
	/*update the budget */
	elapsed=elapsed_us(&sim->last_check,&current);
	sim->bit_budget+=(elapsed*(int64_t)sim->params.max_bandwidth)/1000000LL;
	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
117
	while (sim->qsize>=sim->params.max_buffer_size){
118
		// ortp_message("rtp_session_network_simulate(): discarding packets.");
119 120 121 122
		output=getq(&sim->q);
		if (output){
			bits=(msgdsize(output)+overhead)*8;
			sim->qsize-=bits;
123
			freemsg(output);
124 125
		}
	}
126

127
	output=NULL;
128

129 130 131 132 133 134 135 136 137
	/*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
138 139 140 141
	if (output==NULL && input==NULL && sim->bit_budget>=0){
		/* unused budget is lost...*/
		sim->last_check.tv_sec=0;
	}
142 143 144
	return output;
}

Yann Diorcet's avatar
Yann Diorcet committed
145
static mblk_t *simulate_loss_rate(RtpSession *session, mblk_t *input, int rate){
146 147 148 149 150 151 152
	int rrate;
#ifdef HAVE_ARC4RANDOM
	rrate = arc4random_uniform(101);
#else
	rrate = rand() % 101;
#endif
	if(rrate >= rate) {
Yann Diorcet's avatar
Yann Diorcet committed
153 154
		return input;
	}
155
	freemsg(input);
Yann Diorcet's avatar
Yann Diorcet committed
156 157 158
	return NULL;
}

159
mblk_t * rtp_session_network_simulate(RtpSession *session, mblk_t *input, bool_t *is_rtp_packet){
160 161
	OrtpNetworkSimulatorCtx *sim=session->net_sim_ctx;
	mblk_t *om=NULL;
162

163
	om=input;
164 165

	/*while packet is stored in network simulator queue, keep its type in reserved1 space*/
166 167 168 169 170 171
	if (om != NULL){
		om->reserved1 = *is_rtp_packet;
	}

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

174
	if (sim->params.max_bandwidth>0){
175
		om=simulate_bandwidth_limit(session,om);
176
	}
177 178 179
	if (sim->params.loss_rate>0 && om){
		om=simulate_loss_rate(session,om, sim->params.loss_rate);
	}
180 181 182 183 184 185
	/*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;
	}
186 187 188
	return om;
}