1 /*
2 * GStreamer
3 *
4 * Copyright 2006 Collabora Ltd,
5 * Copyright 2006 Nokia Corporation
6 * @author: Philippe Kalaf <philippe.kalaf@collabora.co.uk>.
7 * Copyright 2012-2016 Pexip
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
23 *
24 */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include "gstnetsim.h"
31 #include <string.h>
32 #include <math.h>
33 #include <float.h>
34
35 GST_DEBUG_CATEGORY (netsim_debug);
36 #define GST_CAT_DEFAULT (netsim_debug)
37
38 static GType
distribution_get_type(void)39 distribution_get_type (void)
40 {
41 static volatile gsize g_define_type_id__volatile = 0;
42 if (g_once_init_enter (&g_define_type_id__volatile)) {
43 static const GEnumValue values[] = {
44 {DISTRIBUTION_UNIFORM, "uniform", "uniform"},
45 {DISTRIBUTION_NORMAL, "normal", "normal"},
46 {DISTRIBUTION_GAMMA, "gamma", "gamma"},
47 {0, NULL, NULL}
48 };
49 GType g_define_type_id =
50 g_enum_register_static ("GstNetSimDistribution", values);
51 g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
52 }
53 return g_define_type_id__volatile;
54 }
55
56 enum
57 {
58 PROP_0,
59 PROP_MIN_DELAY,
60 PROP_MAX_DELAY,
61 PROP_DELAY_DISTRIBUTION,
62 PROP_DELAY_PROBABILITY,
63 PROP_DROP_PROBABILITY,
64 PROP_DUPLICATE_PROBABILITY,
65 PROP_DROP_PACKETS,
66 PROP_MAX_KBPS,
67 PROP_MAX_BUCKET_SIZE,
68 PROP_ALLOW_REORDERING,
69 };
70
71 /* these numbers are nothing but wild guesses and dont reflect any reality */
72 #define DEFAULT_MIN_DELAY 200
73 #define DEFAULT_MAX_DELAY 400
74 #define DEFAULT_DELAY_DISTRIBUTION DISTRIBUTION_UNIFORM
75 #define DEFAULT_DELAY_PROBABILITY 0.0
76 #define DEFAULT_DROP_PROBABILITY 0.0
77 #define DEFAULT_DUPLICATE_PROBABILITY 0.0
78 #define DEFAULT_DROP_PACKETS 0
79 #define DEFAULT_MAX_KBPS -1
80 #define DEFAULT_MAX_BUCKET_SIZE -1
81 #define DEFAULT_ALLOW_REORDERING TRUE
82
83 static GstStaticPadTemplate gst_net_sim_sink_template =
84 GST_STATIC_PAD_TEMPLATE ("sink",
85 GST_PAD_SINK,
86 GST_PAD_ALWAYS,
87 GST_STATIC_CAPS_ANY);
88
89 static GstStaticPadTemplate gst_net_sim_src_template =
90 GST_STATIC_PAD_TEMPLATE ("src",
91 GST_PAD_SRC,
92 GST_PAD_ALWAYS,
93 GST_STATIC_CAPS_ANY);
94
95 G_DEFINE_TYPE (GstNetSim, gst_net_sim, GST_TYPE_ELEMENT);
96
97 static gboolean
gst_net_sim_source_dispatch(GSource * source,GSourceFunc callback,gpointer user_data)98 gst_net_sim_source_dispatch (GSource * source,
99 GSourceFunc callback, gpointer user_data)
100 {
101 callback (user_data);
102 return FALSE;
103 }
104
105 GSourceFuncs gst_net_sim_source_funcs = {
106 NULL, /* prepare */
107 NULL, /* check */
108 gst_net_sim_source_dispatch,
109 NULL /* finalize */
110 };
111
112 static void
gst_net_sim_loop(GstNetSim * netsim)113 gst_net_sim_loop (GstNetSim * netsim)
114 {
115 GMainLoop *loop;
116
117 GST_TRACE_OBJECT (netsim, "TASK: begin");
118
119 g_mutex_lock (&netsim->loop_mutex);
120 loop = g_main_loop_ref (netsim->main_loop);
121 netsim->running = TRUE;
122 GST_TRACE_OBJECT (netsim, "TASK: signal start");
123 g_cond_signal (&netsim->start_cond);
124 g_mutex_unlock (&netsim->loop_mutex);
125
126 GST_TRACE_OBJECT (netsim, "TASK: run");
127 g_main_loop_run (loop);
128 g_main_loop_unref (loop);
129
130 g_mutex_lock (&netsim->loop_mutex);
131 GST_TRACE_OBJECT (netsim, "TASK: pause");
132 gst_pad_pause_task (netsim->srcpad);
133 netsim->running = FALSE;
134 GST_TRACE_OBJECT (netsim, "TASK: signal end");
135 g_cond_signal (&netsim->start_cond);
136 g_mutex_unlock (&netsim->loop_mutex);
137 GST_TRACE_OBJECT (netsim, "TASK: end");
138 }
139
140 static gboolean
_main_loop_quit_and_remove_source(gpointer user_data)141 _main_loop_quit_and_remove_source (gpointer user_data)
142 {
143 GMainLoop *main_loop = user_data;
144 GST_DEBUG ("MAINLOOP: Quit %p", main_loop);
145 g_main_loop_quit (main_loop);
146 g_assert (!g_main_loop_is_running (main_loop));
147 return FALSE; /* Remove source */
148 }
149
150 static gboolean
gst_net_sim_src_activatemode(GstPad * pad,GstObject * parent,GstPadMode mode,gboolean active)151 gst_net_sim_src_activatemode (GstPad * pad, GstObject * parent,
152 GstPadMode mode, gboolean active)
153 {
154 GstNetSim *netsim = GST_NET_SIM (parent);
155 gboolean result = FALSE;
156
157 g_mutex_lock (&netsim->loop_mutex);
158 if (active) {
159 if (netsim->main_loop == NULL) {
160 GMainContext *main_context = g_main_context_new ();
161 netsim->main_loop = g_main_loop_new (main_context, FALSE);
162 g_main_context_unref (main_context);
163
164 GST_TRACE_OBJECT (netsim, "ACT: Starting task on srcpad");
165 result = gst_pad_start_task (netsim->srcpad,
166 (GstTaskFunction) gst_net_sim_loop, netsim, NULL);
167
168 GST_TRACE_OBJECT (netsim, "ACT: Wait for task to start");
169 g_assert (!netsim->running);
170 while (!netsim->running)
171 g_cond_wait (&netsim->start_cond, &netsim->loop_mutex);
172 GST_TRACE_OBJECT (netsim, "ACT: Task on srcpad started");
173 }
174 } else {
175 if (netsim->main_loop != NULL) {
176 GSource *source;
177 guint id;
178
179 /* Adds an Idle Source which quits the main loop from within.
180 * This removes the possibility for run/quit race conditions. */
181 GST_TRACE_OBJECT (netsim, "DEACT: Stopping main loop on deactivate");
182 source = g_idle_source_new ();
183 g_source_set_callback (source, _main_loop_quit_and_remove_source,
184 g_main_loop_ref (netsim->main_loop),
185 (GDestroyNotify) g_main_loop_unref);
186 id = g_source_attach (source,
187 g_main_loop_get_context (netsim->main_loop));
188 g_source_unref (source);
189 g_assert_cmpuint (id, >, 0);
190 g_main_loop_unref (netsim->main_loop);
191 netsim->main_loop = NULL;
192
193 GST_TRACE_OBJECT (netsim, "DEACT: Wait for mainloop and task to pause");
194 g_assert (netsim->running);
195 while (netsim->running)
196 g_cond_wait (&netsim->start_cond, &netsim->loop_mutex);
197
198 GST_TRACE_OBJECT (netsim, "DEACT: Stopping task on srcpad");
199 result = gst_pad_stop_task (netsim->srcpad);
200 GST_TRACE_OBJECT (netsim, "DEACT: Mainloop and GstTask stopped");
201 }
202 }
203 g_mutex_unlock (&netsim->loop_mutex);
204
205 return result;
206 }
207
208 typedef struct
209 {
210 GstPad *pad;
211 GstBuffer *buf;
212 } PushBufferCtx;
213
214 static inline PushBufferCtx *
push_buffer_ctx_new(GstPad * pad,GstBuffer * buf)215 push_buffer_ctx_new (GstPad * pad, GstBuffer * buf)
216 {
217 PushBufferCtx *ctx = g_slice_new (PushBufferCtx);
218 ctx->pad = gst_object_ref (pad);
219 ctx->buf = gst_buffer_ref (buf);
220 return ctx;
221 }
222
223 static inline void
push_buffer_ctx_free(PushBufferCtx * ctx)224 push_buffer_ctx_free (PushBufferCtx * ctx)
225 {
226 if (G_LIKELY (ctx != NULL)) {
227 gst_buffer_unref (ctx->buf);
228 gst_object_unref (ctx->pad);
229 g_slice_free (PushBufferCtx, ctx);
230 }
231 }
232
233 static gboolean
push_buffer_ctx_push(PushBufferCtx * ctx)234 push_buffer_ctx_push (PushBufferCtx * ctx)
235 {
236 GST_DEBUG_OBJECT (ctx->pad, "Pushing buffer now");
237 gst_pad_push (ctx->pad, gst_buffer_ref (ctx->buf));
238 return FALSE;
239 }
240
241 static gint
get_random_value_uniform(GRand * rand_seed,gint32 min_value,gint32 max_value)242 get_random_value_uniform (GRand * rand_seed, gint32 min_value, gint32 max_value)
243 {
244 return g_rand_int_range (rand_seed, min_value, max_value + 1);
245 }
246
247 /* Use the Box-Muller transform. */
248 static gdouble
random_value_normal(GRand * rand_seed,gdouble mu,gdouble sigma,NormalDistributionState * state)249 random_value_normal (GRand * rand_seed, gdouble mu, gdouble sigma,
250 NormalDistributionState * state)
251 {
252 gdouble u1, u2, t1, t2;
253
254 state->generate = !state->generate;
255
256 if (!state->generate)
257 return state->z1 * sigma + mu;
258
259 do {
260 u1 = g_rand_double (rand_seed);
261 u2 = g_rand_double (rand_seed);
262 } while (u1 <= DBL_EPSILON);
263
264 t1 = sqrt (-2.0 * log (u1));
265 t2 = 2.0 * G_PI * u2;
266 state->z0 = t1 * cos (t2);
267 state->z1 = t1 * sin (t2);
268
269 return state->z0 * sigma + mu;
270 }
271
272 /* Generate a value from a normal distributation with 95% confidense interval
273 * between LOW and HIGH */
274 static gint
get_random_value_normal(GRand * rand_seed,gint32 low,gint32 high,NormalDistributionState * state)275 get_random_value_normal (GRand * rand_seed, gint32 low, gint32 high,
276 NormalDistributionState * state)
277 {
278 gdouble mu = (high + low) / 2.0;
279 gdouble sigma = (high - low) / (2 * 1.96); /* 95% confidence interval */
280 gdouble z = random_value_normal (rand_seed, mu, sigma, state);
281
282 return round (z);
283 }
284
285 /* Marsaglia and Tsang's method */
286 static gdouble
random_value_gamma(GRand * rand_seed,gdouble a,gdouble b,NormalDistributionState * state)287 random_value_gamma (GRand * rand_seed, gdouble a, gdouble b,
288 NormalDistributionState * state)
289 {
290 const gdouble d = a - 1.0 / 3.0;
291 const gdouble c = 1.0 / sqrt (9 * d);
292 gdouble x, u, z, v;
293
294 if (a >= 1.0) {
295 while (TRUE) {
296 z = random_value_normal (rand_seed, 0.0, 1.0, state);
297 if (z > -1.0 / c) {
298 u = g_rand_double (rand_seed);
299 v = 1.0 + c * z;
300 v = v * v * v;
301 if (log (u) < (0.5 * z * z + d * (1 - v + log (v)))) {
302 x = d * v;
303 break;
304 }
305 }
306 }
307 } else {
308 u = g_rand_double (rand_seed);
309 x = random_value_gamma (rand_seed, a + 1, b, state) * pow (u, 1.0 / a);
310 }
311
312 return x * b;
313 }
314
315 static gint
get_random_value_gamma(GRand * rand_seed,gint32 low,gint32 high,NormalDistributionState * state)316 get_random_value_gamma (GRand * rand_seed, gint32 low, gint32 high,
317 NormalDistributionState * state)
318 {
319 /* shape parameter 1.25 gives an OK simulation of wireless networks */
320 /* Find the scale parameter so that P(0 < x < high-low) < 0.95 */
321 /* We know: P(0 < x < R) < 0.95 for gamma(1.25, 1), R = 3.4640381 */
322 gdouble shape = 1.25;
323 gdouble scale = (high - low) / 3.4640381;
324 gdouble x = random_value_gamma (rand_seed, shape, scale, state);
325 /* Add offset so that low is the minimum possible value */
326 return round (x + low);
327 }
328
329 static GstFlowReturn
gst_net_sim_delay_buffer(GstNetSim * netsim,GstBuffer * buf)330 gst_net_sim_delay_buffer (GstNetSim * netsim, GstBuffer * buf)
331 {
332 GstFlowReturn ret = GST_FLOW_OK;
333
334 g_mutex_lock (&netsim->loop_mutex);
335 if (netsim->main_loop != NULL && netsim->delay_probability > 0 &&
336 g_rand_double (netsim->rand_seed) < netsim->delay_probability) {
337 gint delay;
338 PushBufferCtx *ctx;
339 GSource *source;
340 gint64 ready_time, now_time;
341
342 switch (netsim->delay_distribution) {
343 case DISTRIBUTION_UNIFORM:
344 delay = get_random_value_uniform (netsim->rand_seed, netsim->min_delay,
345 netsim->max_delay);
346 break;
347 case DISTRIBUTION_NORMAL:
348 delay = get_random_value_normal (netsim->rand_seed, netsim->min_delay,
349 netsim->max_delay, &netsim->delay_state);
350 break;
351 case DISTRIBUTION_GAMMA:
352 delay = get_random_value_gamma (netsim->rand_seed, netsim->min_delay,
353 netsim->max_delay, &netsim->delay_state);
354 break;
355 default:
356 g_assert_not_reached ();
357 break;
358 }
359
360 if (delay < 0)
361 delay = 0;
362
363 ctx = push_buffer_ctx_new (netsim->srcpad, buf);
364
365 source = g_source_new (&gst_net_sim_source_funcs, sizeof (GSource));
366 now_time = g_get_monotonic_time ();
367 ready_time = now_time + delay * 1000;
368 if (!netsim->allow_reordering && ready_time < netsim->last_ready_time)
369 ready_time = netsim->last_ready_time + 1;
370
371 netsim->last_ready_time = ready_time;
372 GST_DEBUG_OBJECT (netsim, "Delaying packet by %" G_GINT64_FORMAT "ms",
373 (ready_time - now_time) / 1000);
374
375 g_source_set_ready_time (source, ready_time);
376 g_source_set_callback (source, (GSourceFunc) push_buffer_ctx_push,
377 ctx, (GDestroyNotify) push_buffer_ctx_free);
378 g_source_attach (source, g_main_loop_get_context (netsim->main_loop));
379 g_source_unref (source);
380 } else {
381 ret = gst_pad_push (netsim->srcpad, gst_buffer_ref (buf));
382 }
383 g_mutex_unlock (&netsim->loop_mutex);
384
385 return ret;
386 }
387
388 static gint
gst_net_sim_get_tokens(GstNetSim * netsim)389 gst_net_sim_get_tokens (GstNetSim * netsim)
390 {
391 gint tokens = 0;
392 GstClockTimeDiff elapsed_time = 0;
393 GstClockTime current_time = 0;
394 GstClockTimeDiff token_time;
395 GstClock *clock;
396
397 /* check for umlimited kbps and fill up the bucket if that is the case,
398 * if not, calculate the number of tokens to add based on the elapsed time */
399 if (netsim->max_kbps == -1)
400 return netsim->max_bucket_size * 1000 - netsim->bucket_size;
401
402 /* get the current time */
403 clock = gst_element_get_clock (GST_ELEMENT_CAST (netsim));
404 if (clock == NULL) {
405 GST_WARNING_OBJECT (netsim, "No clock, can't get the time");
406 } else {
407 current_time = gst_clock_get_time (clock);
408 }
409
410 /* get the elapsed time */
411 if (GST_CLOCK_TIME_IS_VALID (netsim->prev_time)) {
412 if (current_time < netsim->prev_time) {
413 GST_WARNING_OBJECT (netsim, "Clock is going backwards!!");
414 } else {
415 elapsed_time = GST_CLOCK_DIFF (netsim->prev_time, current_time);
416 }
417 } else {
418 netsim->prev_time = current_time;
419 }
420
421 /* calculate number of tokens and how much time is "spent" by these tokens */
422 tokens =
423 gst_util_uint64_scale_int (elapsed_time, netsim->max_kbps * 1000,
424 GST_SECOND);
425 token_time =
426 gst_util_uint64_scale_int (GST_SECOND, tokens, netsim->max_kbps * 1000);
427
428 /* increment the time with how much we spent in terms of whole tokens */
429 netsim->prev_time += token_time;
430 gst_object_unref (clock);
431 return tokens;
432 }
433
434 static gboolean
gst_net_sim_token_bucket(GstNetSim * netsim,GstBuffer * buf)435 gst_net_sim_token_bucket (GstNetSim * netsim, GstBuffer * buf)
436 {
437 gsize buffer_size;
438 gint tokens;
439
440 /* with an unlimited bucket-size, we have nothing to do */
441 if (netsim->max_bucket_size == -1)
442 return TRUE;
443
444 /* get buffer size in bits */
445 buffer_size = gst_buffer_get_size (buf) * 8;
446 tokens = gst_net_sim_get_tokens (netsim);
447
448 netsim->bucket_size = MIN (G_MAXINT, netsim->bucket_size + tokens);
449 GST_LOG_OBJECT (netsim,
450 "Adding %d tokens to bucket (contains %" G_GSIZE_FORMAT " tokens)",
451 tokens, netsim->bucket_size);
452
453 if (netsim->max_bucket_size != -1 && netsim->bucket_size >
454 netsim->max_bucket_size * 1000)
455 netsim->bucket_size = netsim->max_bucket_size * 1000;
456
457 if (buffer_size > netsim->bucket_size) {
458 GST_DEBUG_OBJECT (netsim,
459 "Buffer size (%" G_GSIZE_FORMAT ") exeedes bucket size (%"
460 G_GSIZE_FORMAT ")", buffer_size, netsim->bucket_size);
461 return FALSE;
462 }
463
464 netsim->bucket_size -= buffer_size;
465 GST_LOG_OBJECT (netsim,
466 "Buffer taking %" G_GSIZE_FORMAT " tokens (%" G_GSIZE_FORMAT " left)",
467 buffer_size, netsim->bucket_size);
468 return TRUE;
469 }
470
471 static GstFlowReturn
gst_net_sim_chain(GstPad * pad,GstObject * parent,GstBuffer * buf)472 gst_net_sim_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
473 {
474 GstNetSim *netsim = GST_NET_SIM (parent);
475 GstFlowReturn ret = GST_FLOW_OK;
476
477 if (!gst_net_sim_token_bucket (netsim, buf))
478 goto done;
479
480 if (netsim->drop_packets > 0) {
481 netsim->drop_packets--;
482 GST_DEBUG_OBJECT (netsim, "Dropping packet (%d left)",
483 netsim->drop_packets);
484 } else if (netsim->drop_probability > 0
485 && g_rand_double (netsim->rand_seed) <
486 (gdouble) netsim->drop_probability) {
487 GST_DEBUG_OBJECT (netsim, "Dropping packet");
488 } else if (netsim->duplicate_probability > 0 &&
489 g_rand_double (netsim->rand_seed) <
490 (gdouble) netsim->duplicate_probability) {
491 GST_DEBUG_OBJECT (netsim, "Duplicating packet");
492 gst_net_sim_delay_buffer (netsim, buf);
493 ret = gst_net_sim_delay_buffer (netsim, buf);
494 } else {
495 ret = gst_net_sim_delay_buffer (netsim, buf);
496 }
497
498 done:
499 gst_buffer_unref (buf);
500 return ret;
501 }
502
503
504 static void
gst_net_sim_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)505 gst_net_sim_set_property (GObject * object,
506 guint prop_id, const GValue * value, GParamSpec * pspec)
507 {
508 GstNetSim *netsim = GST_NET_SIM (object);
509
510 switch (prop_id) {
511 case PROP_MIN_DELAY:
512 netsim->min_delay = g_value_get_int (value);
513 break;
514 case PROP_MAX_DELAY:
515 netsim->max_delay = g_value_get_int (value);
516 break;
517 case PROP_DELAY_DISTRIBUTION:
518 netsim->delay_distribution = g_value_get_enum (value);
519 break;
520 case PROP_DELAY_PROBABILITY:
521 netsim->delay_probability = g_value_get_float (value);
522 break;
523 case PROP_DROP_PROBABILITY:
524 netsim->drop_probability = g_value_get_float (value);
525 break;
526 case PROP_DUPLICATE_PROBABILITY:
527 netsim->duplicate_probability = g_value_get_float (value);
528 break;
529 case PROP_DROP_PACKETS:
530 netsim->drop_packets = g_value_get_uint (value);
531 break;
532 case PROP_MAX_KBPS:
533 netsim->max_kbps = g_value_get_int (value);
534 break;
535 case PROP_MAX_BUCKET_SIZE:
536 netsim->max_bucket_size = g_value_get_int (value);
537 if (netsim->max_bucket_size != -1)
538 netsim->bucket_size = netsim->max_bucket_size * 1000;
539 break;
540 case PROP_ALLOW_REORDERING:
541 netsim->allow_reordering = g_value_get_boolean (value);
542 break;
543 default:
544 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
545 break;
546 }
547 }
548
549 static void
gst_net_sim_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)550 gst_net_sim_get_property (GObject * object,
551 guint prop_id, GValue * value, GParamSpec * pspec)
552 {
553 GstNetSim *netsim = GST_NET_SIM (object);
554
555 switch (prop_id) {
556 case PROP_MIN_DELAY:
557 g_value_set_int (value, netsim->min_delay);
558 break;
559 case PROP_MAX_DELAY:
560 g_value_set_int (value, netsim->max_delay);
561 break;
562 case PROP_DELAY_DISTRIBUTION:
563 g_value_set_enum (value, netsim->delay_distribution);
564 break;
565 case PROP_DELAY_PROBABILITY:
566 g_value_set_float (value, netsim->delay_probability);
567 break;
568 case PROP_DROP_PROBABILITY:
569 g_value_set_float (value, netsim->drop_probability);
570 break;
571 case PROP_DUPLICATE_PROBABILITY:
572 g_value_set_float (value, netsim->duplicate_probability);
573 break;
574 case PROP_DROP_PACKETS:
575 g_value_set_uint (value, netsim->drop_packets);
576 break;
577 case PROP_MAX_KBPS:
578 g_value_set_int (value, netsim->max_kbps);
579 break;
580 case PROP_MAX_BUCKET_SIZE:
581 g_value_set_int (value, netsim->max_bucket_size);
582 break;
583 case PROP_ALLOW_REORDERING:
584 g_value_set_boolean (value, netsim->allow_reordering);
585 break;
586 default:
587 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
588 break;
589 }
590 }
591
592
593 static void
gst_net_sim_init(GstNetSim * netsim)594 gst_net_sim_init (GstNetSim * netsim)
595 {
596 netsim->srcpad =
597 gst_pad_new_from_static_template (&gst_net_sim_src_template, "src");
598 netsim->sinkpad =
599 gst_pad_new_from_static_template (&gst_net_sim_sink_template, "sink");
600
601 gst_element_add_pad (GST_ELEMENT (netsim), netsim->srcpad);
602 gst_element_add_pad (GST_ELEMENT (netsim), netsim->sinkpad);
603
604 g_mutex_init (&netsim->loop_mutex);
605 g_cond_init (&netsim->start_cond);
606 netsim->rand_seed = g_rand_new ();
607 netsim->main_loop = NULL;
608 netsim->prev_time = GST_CLOCK_TIME_NONE;
609
610 GST_OBJECT_FLAG_SET (netsim->sinkpad,
611 GST_PAD_FLAG_PROXY_CAPS | GST_PAD_FLAG_PROXY_ALLOCATION);
612
613 gst_pad_set_chain_function (netsim->sinkpad,
614 GST_DEBUG_FUNCPTR (gst_net_sim_chain));
615 gst_pad_set_activatemode_function (netsim->srcpad,
616 GST_DEBUG_FUNCPTR (gst_net_sim_src_activatemode));
617 }
618
619 static void
gst_net_sim_finalize(GObject * object)620 gst_net_sim_finalize (GObject * object)
621 {
622 GstNetSim *netsim = GST_NET_SIM (object);
623
624 g_rand_free (netsim->rand_seed);
625 g_mutex_clear (&netsim->loop_mutex);
626 g_cond_clear (&netsim->start_cond);
627
628 G_OBJECT_CLASS (gst_net_sim_parent_class)->finalize (object);
629 }
630
631 static void
gst_net_sim_dispose(GObject * object)632 gst_net_sim_dispose (GObject * object)
633 {
634 GstNetSim *netsim = GST_NET_SIM (object);
635
636 g_assert (netsim->main_loop == NULL);
637
638 G_OBJECT_CLASS (gst_net_sim_parent_class)->dispose (object);
639 }
640
641 static void
gst_net_sim_class_init(GstNetSimClass * klass)642 gst_net_sim_class_init (GstNetSimClass * klass)
643 {
644 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
645 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
646
647 gst_element_class_add_static_pad_template (gstelement_class,
648 &gst_net_sim_src_template);
649 gst_element_class_add_static_pad_template (gstelement_class,
650 &gst_net_sim_sink_template);
651
652 gst_element_class_set_metadata (gstelement_class,
653 "Network Simulator",
654 "Filter/Network",
655 "An element that simulates network jitter, "
656 "packet loss and packet duplication",
657 "Philippe Kalaf <philippe.kalaf@collabora.co.uk>, "
658 "Havard Graff <havard@pexip.com>");
659
660 gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_net_sim_dispose);
661 gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_net_sim_finalize);
662
663 gobject_class->set_property = gst_net_sim_set_property;
664 gobject_class->get_property = gst_net_sim_get_property;
665
666 g_object_class_install_property (gobject_class, PROP_MIN_DELAY,
667 g_param_spec_int ("min-delay", "Minimum delay (ms)",
668 "The minimum delay in ms to apply to buffers",
669 G_MININT, G_MAXINT, DEFAULT_MIN_DELAY,
670 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
671
672 g_object_class_install_property (gobject_class, PROP_MAX_DELAY,
673 g_param_spec_int ("max-delay", "Maximum delay (ms)",
674 "The maximum delay (inclusive) in ms to apply to buffers",
675 G_MININT, G_MAXINT, DEFAULT_MAX_DELAY,
676 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
677
678 /**
679 * GstNetSim:delay-distribution:
680 *
681 * Distribution for the amount of delay.
682 *
683 * Since: 1.14
684 */
685 g_object_class_install_property (gobject_class, PROP_DELAY_DISTRIBUTION,
686 g_param_spec_enum ("delay-distribution", "Delay Distribution",
687 "Distribution for the amount of delay",
688 distribution_get_type (), DEFAULT_DELAY_DISTRIBUTION,
689 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
690
691 g_object_class_install_property (gobject_class, PROP_DELAY_PROBABILITY,
692 g_param_spec_float ("delay-probability", "Delay Probability",
693 "The Probability a buffer is delayed",
694 0.0, 1.0, DEFAULT_DELAY_PROBABILITY,
695 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
696
697 g_object_class_install_property (gobject_class, PROP_DROP_PROBABILITY,
698 g_param_spec_float ("drop-probability", "Drop Probability",
699 "The Probability a buffer is dropped",
700 0.0, 1.0, DEFAULT_DROP_PROBABILITY,
701 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
702
703 g_object_class_install_property (gobject_class, PROP_DUPLICATE_PROBABILITY,
704 g_param_spec_float ("duplicate-probability", "Duplicate Probability",
705 "The Probability a buffer is duplicated",
706 0.0, 1.0, DEFAULT_DUPLICATE_PROBABILITY,
707 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
708
709 g_object_class_install_property (gobject_class, PROP_DROP_PACKETS,
710 g_param_spec_uint ("drop-packets", "Drop Packets",
711 "Drop the next n packets",
712 0, G_MAXUINT, DEFAULT_DROP_PACKETS,
713 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
714 /**
715 * GstNetSim:max-kbps:
716 *
717 * The maximum number of kilobits to let through per second. Setting this
718 * property to a positive value enables network congestion simulation using
719 * a token bucket algorithm. Also see the "max-bucket-size" property,
720 *
721 * Since: 1.14
722 */
723 g_object_class_install_property (gobject_class, PROP_MAX_KBPS,
724 g_param_spec_int ("max-kbps", "Maximum Kbps",
725 "The maximum number of kilobits to let through per second "
726 "(-1 = unlimited)", -1, G_MAXINT, DEFAULT_MAX_KBPS,
727 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
728
729 /**
730 * GstNetSim:max-bucket-size:
731 *
732 * The size of the token bucket, related to burstiness resilience.
733 *
734 * Since: 1.14
735 */
736 g_object_class_install_property (gobject_class, PROP_MAX_BUCKET_SIZE,
737 g_param_spec_int ("max-bucket-size", "Maximum Bucket Size (Kb)",
738 "The size of the token bucket, related to burstiness resilience "
739 "(-1 = unlimited)", -1, G_MAXINT, DEFAULT_MAX_BUCKET_SIZE,
740 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
741
742 /**
743 * GstNetSim:allow-reordering:
744 *
745 * When delaying packets, are they allowed to be reordered or not. By
746 * default this is enabled, but in the real world packet reordering is
747 * fairly uncommon, yet the delay functions will always introduce reordering
748 * if delay > packet-spacing, This property allows switching that off.
749 *
750 * Since: 1.14
751 */
752 g_object_class_install_property (gobject_class, PROP_ALLOW_REORDERING,
753 g_param_spec_boolean ("allow-reordering", "Allow Reordering",
754 "When delaying packets, are they allowed to be reordered or not",
755 DEFAULT_ALLOW_REORDERING,
756 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
757
758 GST_DEBUG_CATEGORY_INIT (netsim_debug, "netsim", 0, "Network simulator");
759 }
760
761 static gboolean
gst_net_sim_plugin_init(GstPlugin * plugin)762 gst_net_sim_plugin_init (GstPlugin * plugin)
763 {
764 return gst_element_register (plugin, "netsim",
765 GST_RANK_MARGINAL, GST_TYPE_NET_SIM);
766 }
767
768 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
769 GST_VERSION_MINOR,
770 netsim,
771 "Network Simulator",
772 gst_net_sim_plugin_init, PACKAGE_VERSION, "LGPL", GST_PACKAGE_NAME,
773 GST_PACKAGE_ORIGIN)
774