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