1 /* GStreamer
2  * Copyright (C) 2010 David Schleef <ds@schleef.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 /**
20  * SECTION:element-gstchopmydata
21  * @title: gstchopmydata
22  *
23  * The chopmydata element takes an incoming stream and chops it up
24  * into randomly sized buffers.  Size of outgoing buffers are determined
25  * by the max-size, min-size, and step-size properties.
26  *
27  * ## Example launch line
28  * |[
29  * gst-launch-1.0 -v audiotestsrc num-buffers=10 ! chopmydata min-size=100
30  * max-size=200 step-size=2 ! fakesink -v
31  * ]|
32  *
33  * This pipeline will create 10 buffers that are by default 2048 bytes
34  * each (1024 samples each), and chop them up into buffers that range
35  * in size from 100 bytes to 200 bytes, with the restriction that sizes
36  * are a multiple of 2.  This restriction is important, because the
37  * default sample size for audiotestsrc is 2 bytes (one channel, 16-bit
38  * audio).
39  *
40  */
41 
42 #ifdef HAVE_CONFIG_H
43 #include "config.h"
44 #endif
45 
46 #include <gst/gst.h>
47 #include <gst/gst.h>
48 #include "gstchopmydata.h"
49 
50 /* prototypes */
51 
52 
53 static void gst_chop_my_data_set_property (GObject * object,
54     guint property_id, const GValue * value, GParamSpec * pspec);
55 static void gst_chop_my_data_get_property (GObject * object,
56     guint property_id, GValue * value, GParamSpec * pspec);
57 
58 static GstStateChangeReturn
59 gst_chop_my_data_change_state (GstElement * element, GstStateChange transition);
60 
61 static GstFlowReturn gst_chop_my_data_chain (GstPad * pad, GstObject * parent,
62     GstBuffer * buffer);
63 static gboolean gst_chop_my_data_sink_event (GstPad * pad, GstObject * parent,
64     GstEvent * event);
65 static gboolean gst_chop_my_data_src_event (GstPad * pad, GstObject * parent,
66     GstEvent * event);
67 
68 #define DEFAULT_MAX_SIZE 4096
69 #define DEFAULT_MIN_SIZE 1
70 #define DEFAULT_STEP_SIZE 1
71 
72 enum
73 {
74   PROP_0,
75   PROP_MAX_SIZE,
76   PROP_MIN_SIZE,
77   PROP_STEP_SIZE
78 };
79 
80 /* pad templates */
81 
82 static GstStaticPadTemplate gst_chop_my_data_sink_template =
83 GST_STATIC_PAD_TEMPLATE ("sink",
84     GST_PAD_SINK,
85     GST_PAD_ALWAYS,
86     GST_STATIC_CAPS_ANY);
87 
88 static GstStaticPadTemplate gst_chop_my_data_src_template =
89 GST_STATIC_PAD_TEMPLATE ("src",
90     GST_PAD_SRC,
91     GST_PAD_ALWAYS,
92     GST_STATIC_CAPS_ANY);
93 
94 /* class initialization */
95 
96 #define gst_chop_my_data_parent_class parent_class
97 G_DEFINE_TYPE (GstChopMyData, gst_chop_my_data, GST_TYPE_ELEMENT);
98 
99 static void
gst_chop_my_data_class_init(GstChopMyDataClass * klass)100 gst_chop_my_data_class_init (GstChopMyDataClass * klass)
101 {
102   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
103   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
104 
105   gobject_class->set_property = gst_chop_my_data_set_property;
106   gobject_class->get_property = gst_chop_my_data_get_property;
107   element_class->change_state =
108       GST_DEBUG_FUNCPTR (gst_chop_my_data_change_state);
109 
110   g_object_class_install_property (gobject_class, PROP_MAX_SIZE,
111       g_param_spec_int ("max-size", "max-size",
112           "Maximum size of outgoing buffers", 1, G_MAXINT,
113           DEFAULT_MAX_SIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
114   g_object_class_install_property (gobject_class, PROP_MIN_SIZE,
115       g_param_spec_int ("min-size", "max-size",
116           "Minimum size of outgoing buffers", 1, G_MAXINT,
117           DEFAULT_MIN_SIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
118   g_object_class_install_property (gobject_class, PROP_STEP_SIZE,
119       g_param_spec_int ("step-size", "step-size",
120           "Step increment for random buffer sizes", 1, G_MAXINT,
121           DEFAULT_MAX_SIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
122 
123   gst_element_class_add_static_pad_template (element_class,
124       &gst_chop_my_data_src_template);
125   gst_element_class_add_static_pad_template (element_class,
126       &gst_chop_my_data_sink_template);
127 
128   gst_element_class_set_static_metadata (element_class, "FIXME",
129       "Generic", "FIXME", "David Schleef <ds@schleef.org>");
130 }
131 
132 static void
gst_chop_my_data_init(GstChopMyData * chopmydata)133 gst_chop_my_data_init (GstChopMyData * chopmydata)
134 {
135 
136   chopmydata->sinkpad =
137       gst_pad_new_from_static_template (&gst_chop_my_data_sink_template,
138       "sink");
139   gst_pad_set_event_function (chopmydata->sinkpad,
140       GST_DEBUG_FUNCPTR (gst_chop_my_data_sink_event));
141   gst_pad_set_chain_function (chopmydata->sinkpad,
142       GST_DEBUG_FUNCPTR (gst_chop_my_data_chain));
143   GST_PAD_SET_PROXY_CAPS (chopmydata->sinkpad);
144   gst_element_add_pad (GST_ELEMENT (chopmydata), chopmydata->sinkpad);
145 
146   chopmydata->srcpad =
147       gst_pad_new_from_static_template (&gst_chop_my_data_src_template, "src");
148   gst_pad_set_event_function (chopmydata->srcpad,
149       GST_DEBUG_FUNCPTR (gst_chop_my_data_src_event));
150   GST_PAD_SET_PROXY_CAPS (chopmydata->srcpad);
151   gst_element_add_pad (GST_ELEMENT (chopmydata), chopmydata->srcpad);
152 
153   chopmydata->step_size = DEFAULT_STEP_SIZE;
154   chopmydata->min_size = DEFAULT_MIN_SIZE;
155   chopmydata->max_size = DEFAULT_MAX_SIZE;
156 }
157 
158 void
gst_chop_my_data_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)159 gst_chop_my_data_set_property (GObject * object, guint property_id,
160     const GValue * value, GParamSpec * pspec)
161 {
162   GstChopMyData *chopmydata;
163 
164   g_return_if_fail (GST_IS_CHOP_MY_DATA (object));
165   chopmydata = GST_CHOP_MY_DATA (object);
166 
167   switch (property_id) {
168     case PROP_MAX_SIZE:
169       chopmydata->max_size = g_value_get_int (value);
170       break;
171     case PROP_MIN_SIZE:
172       chopmydata->min_size = g_value_get_int (value);
173       break;
174     case PROP_STEP_SIZE:
175       chopmydata->step_size = g_value_get_int (value);
176       break;
177     default:
178       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
179       break;
180   }
181 }
182 
183 void
gst_chop_my_data_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)184 gst_chop_my_data_get_property (GObject * object, guint property_id,
185     GValue * value, GParamSpec * pspec)
186 {
187   GstChopMyData *chopmydata;
188 
189   g_return_if_fail (GST_IS_CHOP_MY_DATA (object));
190   chopmydata = GST_CHOP_MY_DATA (object);
191 
192   switch (property_id) {
193     case PROP_MAX_SIZE:
194       g_value_set_int (value, chopmydata->max_size);
195       break;
196     case PROP_MIN_SIZE:
197       g_value_set_int (value, chopmydata->min_size);
198       break;
199     case PROP_STEP_SIZE:
200       g_value_set_int (value, chopmydata->step_size);
201       break;
202     default:
203       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
204       break;
205   }
206 }
207 
208 static GstStateChangeReturn
gst_chop_my_data_change_state(GstElement * element,GstStateChange transition)209 gst_chop_my_data_change_state (GstElement * element, GstStateChange transition)
210 {
211   GstChopMyData *chopmydata;
212   GstStateChangeReturn ret;
213 
214   chopmydata = GST_CHOP_MY_DATA (element);
215 
216   switch (transition) {
217     case GST_STATE_CHANGE_NULL_TO_READY:
218       break;
219     case GST_STATE_CHANGE_READY_TO_PAUSED:
220       GST_OBJECT_LOCK (chopmydata);
221       chopmydata->adapter = gst_adapter_new ();
222       chopmydata->rand = g_rand_new ();
223       chopmydata->next_size = 0;
224       GST_OBJECT_UNLOCK (chopmydata);
225       break;
226     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
227       break;
228     default:
229       break;
230   }
231 
232   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
233 
234   switch (transition) {
235     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
236       break;
237     case GST_STATE_CHANGE_PAUSED_TO_READY:
238       GST_OBJECT_LOCK (chopmydata);
239       g_object_unref (chopmydata->adapter);
240       chopmydata->adapter = NULL;
241       g_rand_free (chopmydata->rand);
242       GST_OBJECT_UNLOCK (chopmydata);
243       break;
244     case GST_STATE_CHANGE_READY_TO_NULL:
245       break;
246     default:
247       break;
248   }
249 
250   return ret;
251 }
252 
253 static void
get_next_size(GstChopMyData * chopmydata)254 get_next_size (GstChopMyData * chopmydata)
255 {
256   int begin;
257   int end;
258 
259   begin = (chopmydata->min_size + chopmydata->step_size - 1) /
260       chopmydata->step_size;
261   end = (chopmydata->max_size + chopmydata->step_size) / chopmydata->step_size;
262 
263   if (begin >= end) {
264     chopmydata->next_size = begin * chopmydata->step_size;
265     return;
266   }
267 
268   chopmydata->next_size =
269       g_rand_int_range (chopmydata->rand, begin, end) * chopmydata->step_size;
270 }
271 
272 static GstFlowReturn
gst_chop_my_data_process(GstChopMyData * chopmydata,gboolean flush)273 gst_chop_my_data_process (GstChopMyData * chopmydata, gboolean flush)
274 {
275   GstFlowReturn ret = GST_FLOW_OK;
276   GstBuffer *buffer;
277 
278   if (chopmydata->next_size == 0) {
279     get_next_size (chopmydata);
280   }
281 
282   while (gst_adapter_available (chopmydata->adapter) >= chopmydata->next_size) {
283     buffer =
284         gst_adapter_take_buffer (chopmydata->adapter, chopmydata->next_size);
285 
286     GST_BUFFER_PTS (buffer) = gst_adapter_prev_pts (chopmydata->adapter, NULL);
287     GST_BUFFER_DTS (buffer) = gst_adapter_prev_dts (chopmydata->adapter, NULL);
288 
289     chopmydata->next_size = 0;
290 
291     ret = gst_pad_push (chopmydata->srcpad, buffer);
292     if (ret != GST_FLOW_OK) {
293       return ret;
294     }
295 
296     get_next_size (chopmydata);
297   }
298 
299   if (flush) {
300     guint min_size = chopmydata->min_size;
301 
302     while (gst_adapter_available (chopmydata->adapter) >= min_size) {
303       buffer = gst_adapter_take_buffer (chopmydata->adapter, min_size);
304       ret = gst_pad_push (chopmydata->srcpad, buffer);
305       if (ret != GST_FLOW_OK)
306         break;
307     }
308     gst_adapter_clear (chopmydata->adapter);
309   }
310 
311   return ret;
312 }
313 
314 static GstFlowReturn
gst_chop_my_data_chain(GstPad * pad,GstObject * parent,GstBuffer * buffer)315 gst_chop_my_data_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
316 {
317   GstChopMyData *chopmydata;
318   GstFlowReturn ret;
319 
320   chopmydata = GST_CHOP_MY_DATA (parent);
321 
322   GST_DEBUG_OBJECT (chopmydata, "chain");
323 
324   gst_adapter_push (chopmydata->adapter, buffer);
325   ret = gst_chop_my_data_process (chopmydata, FALSE);
326 
327   return ret;
328 }
329 
330 static gboolean
gst_chop_my_data_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)331 gst_chop_my_data_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
332 {
333   gboolean res;
334   GstChopMyData *chopmydata;
335 
336   chopmydata = GST_CHOP_MY_DATA (parent);
337 
338   GST_DEBUG_OBJECT (chopmydata, "event");
339 
340   switch (GST_EVENT_TYPE (event)) {
341     case GST_EVENT_FLUSH_START:
342       res = gst_pad_push_event (chopmydata->srcpad, event);
343       break;
344     case GST_EVENT_FLUSH_STOP:
345       gst_adapter_clear (chopmydata->adapter);
346       res = gst_pad_push_event (chopmydata->srcpad, event);
347       break;
348     case GST_EVENT_SEGMENT:
349       res = gst_pad_push_event (chopmydata->srcpad, event);
350       break;
351     case GST_EVENT_EOS:
352       gst_chop_my_data_process (chopmydata, TRUE);
353       res = gst_pad_push_event (chopmydata->srcpad, event);
354       break;
355     default:
356       res = gst_pad_push_event (chopmydata->srcpad, event);
357       break;
358   }
359 
360   return res;
361 }
362 
363 static gboolean
gst_chop_my_data_src_event(GstPad * pad,GstObject * parent,GstEvent * event)364 gst_chop_my_data_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
365 {
366   gboolean res;
367   GstChopMyData *chopmydata;
368 
369   chopmydata = GST_CHOP_MY_DATA (parent);
370 
371   GST_DEBUG_OBJECT (chopmydata, "event");
372 
373   switch (GST_EVENT_TYPE (event)) {
374     case GST_EVENT_SEEK:
375       res = gst_pad_push_event (chopmydata->sinkpad, event);
376       break;
377     default:
378       res = gst_pad_push_event (chopmydata->sinkpad, event);
379       break;
380   }
381 
382   return res;
383 }
384