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