• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) 2006 Thomas Vander Stichele <thomas at apestaart dot 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 /**
21  * SECTION:element-gdpdepay
22  * @title: gdpdepay
23  * @see_also: gdppay
24  *
25  * This element depayloads GStreamer Data Protocol buffers back to deserialized
26  * buffers and events.
27  *
28  * |[
29  * gst-launch-1.0 -v -m filesrc location=test.gdp ! gdpdepay ! xvimagesink
30  * ]| This pipeline plays back a serialized video stream as created in the
31  * example for gdppay.
32  *
33  */
34 
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
38 
39 #include <string.h>
40 
41 #include "dataprotocol.h"
42 
43 #include "gstgdpdepay.h"
44 
45 enum
46 {
47   PROP_0,
48   PROP_TS_OFFSET
49 };
50 
51 static GstStaticPadTemplate gdp_depay_sink_template =
52 GST_STATIC_PAD_TEMPLATE ("sink",
53     GST_PAD_SINK,
54     GST_PAD_ALWAYS,
55     GST_STATIC_CAPS ("application/x-gdp"));
56 
57 static GstStaticPadTemplate gdp_depay_src_template =
58 GST_STATIC_PAD_TEMPLATE ("src",
59     GST_PAD_SRC,
60     GST_PAD_ALWAYS,
61     GST_STATIC_CAPS_ANY);
62 
63 GST_DEBUG_CATEGORY_STATIC (gst_gdp_depay_debug);
64 #define GST_CAT_DEFAULT gst_gdp_depay_debug
65 
66 #define _do_init \
67     GST_DEBUG_CATEGORY_INIT (gst_gdp_depay_debug, "gdpdepay", 0, \
68     "GDP depayloader");
69 #define gst_gdp_depay_parent_class parent_class
70 G_DEFINE_TYPE_WITH_CODE (GstGDPDepay, gst_gdp_depay,
71     GST_TYPE_ELEMENT, _do_init);
72 
73 static gboolean gst_gdp_depay_sink_event (GstPad * pad, GstObject * parent,
74     GstEvent * event);
75 static gboolean gst_gdp_depay_src_event (GstPad * pad, GstObject * parent,
76     GstEvent * event);
77 
78 static GstFlowReturn gst_gdp_depay_chain (GstPad * pad, GstObject * parent,
79     GstBuffer * buffer);
80 
81 static GstStateChangeReturn gst_gdp_depay_change_state (GstElement *
82     element, GstStateChange transition);
83 
84 static void gst_gdp_depay_finalize (GObject * object);
85 static void gst_gdp_depay_set_property (GObject * object, guint prop_id,
86     const GValue * value, GParamSpec * pspec);
87 static void gst_gdp_depay_get_property (GObject * object, guint prop_id,
88     GValue * value, GParamSpec * pspec);
89 static void gst_gdp_depay_decide_allocation (GstGDPDepay * depay);
90 
91 static void
gst_gdp_depay_class_init(GstGDPDepayClass * klass)92 gst_gdp_depay_class_init (GstGDPDepayClass * klass)
93 {
94   GObjectClass *gobject_class;
95   GstElementClass *gstelement_class;
96 
97   gobject_class = (GObjectClass *) klass;
98   gstelement_class = (GstElementClass *) klass;
99 
100   gobject_class->set_property = gst_gdp_depay_set_property;
101   gobject_class->get_property = gst_gdp_depay_get_property;
102 
103   g_object_class_install_property (gobject_class, PROP_TS_OFFSET,
104       g_param_spec_int64 ("ts-offset", "Timestamp Offset",
105           "Timestamp Offset",
106           G_MININT64, G_MAXINT64, 0,
107           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
108 
109   gst_element_class_set_static_metadata (gstelement_class,
110       "GDP Depayloader", "GDP/Depayloader",
111       "Depayloads GStreamer Data Protocol buffers",
112       "Thomas Vander Stichele <thomas at apestaart dot org>");
113 
114   gst_element_class_add_static_pad_template (gstelement_class,
115       &gdp_depay_sink_template);
116   gst_element_class_add_static_pad_template (gstelement_class,
117       &gdp_depay_src_template);
118 
119   gstelement_class->change_state =
120       GST_DEBUG_FUNCPTR (gst_gdp_depay_change_state);
121   gobject_class->finalize = gst_gdp_depay_finalize;
122 }
123 
124 static void
gst_gdp_depay_init(GstGDPDepay * gdpdepay)125 gst_gdp_depay_init (GstGDPDepay * gdpdepay)
126 {
127   gdpdepay->sinkpad =
128       gst_pad_new_from_static_template (&gdp_depay_sink_template, "sink");
129   gst_pad_set_chain_function (gdpdepay->sinkpad,
130       GST_DEBUG_FUNCPTR (gst_gdp_depay_chain));
131   gst_pad_set_event_function (gdpdepay->sinkpad,
132       GST_DEBUG_FUNCPTR (gst_gdp_depay_sink_event));
133   gst_element_add_pad (GST_ELEMENT (gdpdepay), gdpdepay->sinkpad);
134 
135   gdpdepay->srcpad =
136       gst_pad_new_from_static_template (&gdp_depay_src_template, "src");
137   gst_pad_set_event_function (gdpdepay->srcpad,
138       GST_DEBUG_FUNCPTR (gst_gdp_depay_src_event));
139   /* our caps will always be decided by the incoming GDP caps buffers */
140   gst_pad_use_fixed_caps (gdpdepay->srcpad);
141   gst_element_add_pad (GST_ELEMENT (gdpdepay), gdpdepay->srcpad);
142 
143   gdpdepay->adapter = gst_adapter_new ();
144 
145   gdpdepay->allocator = NULL;
146   gst_allocation_params_init (&gdpdepay->allocation_params);
147 }
148 
149 static void
gst_gdp_depay_finalize(GObject * gobject)150 gst_gdp_depay_finalize (GObject * gobject)
151 {
152   GstGDPDepay *this;
153 
154   this = GST_GDP_DEPAY (gobject);
155   if (this->caps)
156     gst_caps_unref (this->caps);
157   g_free (this->header);
158   gst_adapter_clear (this->adapter);
159   g_object_unref (this->adapter);
160   if (this->allocator)
161     gst_object_unref (this->allocator);
162 
163   GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (gobject));
164 }
165 
166 static void
gst_gdp_depay_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)167 gst_gdp_depay_set_property (GObject * object, guint prop_id,
168     const GValue * value, GParamSpec * pspec)
169 {
170   GstGDPDepay *this;
171 
172   this = GST_GDP_DEPAY (object);
173 
174   switch (prop_id) {
175     case PROP_TS_OFFSET:
176       this->ts_offset = g_value_get_int64 (value);
177       break;
178     default:
179       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
180       break;
181   }
182 }
183 
184 static void
gst_gdp_depay_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)185 gst_gdp_depay_get_property (GObject * object, guint prop_id,
186     GValue * value, GParamSpec * pspec)
187 {
188   GstGDPDepay *this;
189 
190   this = GST_GDP_DEPAY (object);
191 
192   switch (prop_id) {
193     case PROP_TS_OFFSET:
194       g_value_set_int64 (value, this->ts_offset);
195       break;
196     default:
197       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
198       break;
199   }
200 }
201 
202 static gboolean
gst_gdp_depay_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)203 gst_gdp_depay_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
204 {
205   GstGDPDepay *this;
206   gboolean res = TRUE;
207 
208   this = GST_GDP_DEPAY (parent);
209 
210   switch (GST_EVENT_TYPE (event)) {
211     case GST_EVENT_FLUSH_START:
212       /* forward flush start */
213       res = gst_pad_push_event (this->srcpad, event);
214       break;
215     case GST_EVENT_FLUSH_STOP:
216       /* clear adapter on flush */
217       gst_adapter_clear (this->adapter);
218       /* forward flush stop */
219       res = gst_pad_push_event (this->srcpad, event);
220       break;
221     case GST_EVENT_EOS:
222       /* after EOS, we don't expect to output anything anymore */
223       res = gst_pad_push_event (this->srcpad, event);
224       break;
225     case GST_EVENT_SEGMENT:
226     case GST_EVENT_TAG:
227     case GST_EVENT_BUFFERSIZE:
228     default:
229       /* we unref most events as we take them from the datastream */
230       gst_event_unref (event);
231       break;
232   }
233 
234   return res;
235 }
236 
237 static gboolean
gst_gdp_depay_src_event(GstPad * pad,GstObject * parent,GstEvent * event)238 gst_gdp_depay_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
239 {
240   GstGDPDepay *this;
241   gboolean res = TRUE;
242 
243   this = GST_GDP_DEPAY (parent);
244 
245   switch (GST_EVENT_TYPE (event)) {
246     case GST_EVENT_SEEK:
247       /* we refuse seek for now. */
248       gst_event_unref (event);
249       res = FALSE;
250       break;
251     case GST_EVENT_QOS:
252     case GST_EVENT_NAVIGATION:
253     default:
254       /* everything else is passed */
255       res = gst_pad_push_event (this->sinkpad, event);
256       break;
257   }
258 
259   return res;
260 }
261 
262 static GstFlowReturn
gst_gdp_depay_chain(GstPad * pad,GstObject * parent,GstBuffer * buffer)263 gst_gdp_depay_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
264 {
265   GstGDPDepay *this;
266   GstFlowReturn ret = GST_FLOW_OK;
267   GstCaps *caps;
268   GstBuffer *buf;
269   GstEvent *event;
270   guint available;
271 
272   this = GST_GDP_DEPAY (parent);
273 
274   if (gst_pad_check_reconfigure (this->srcpad)) {
275     gst_gdp_depay_decide_allocation (this);
276   }
277 
278   /* On DISCONT, get rid of accumulated data. We assume a buffer after the
279    * DISCONT contains (part of) a new valid header, if not we error because we
280    * lost sync */
281   if (GST_BUFFER_IS_DISCONT (buffer)) {
282     gst_adapter_clear (this->adapter);
283     this->state = GST_GDP_DEPAY_STATE_HEADER;
284   }
285   gst_adapter_push (this->adapter, buffer);
286 
287   while (TRUE) {
288     switch (this->state) {
289       case GST_GDP_DEPAY_STATE_HEADER:
290       {
291         guint8 *header;
292 
293         /* collect a complete header, validate and store the header. Figure out
294          * the payload length and switch to the PAYLOAD state */
295         available = gst_adapter_available (this->adapter);
296         if (available < GST_DP_HEADER_LENGTH)
297           goto done;
298 
299         GST_LOG_OBJECT (this, "reading GDP header from adapter");
300         header = gst_adapter_take (this->adapter, GST_DP_HEADER_LENGTH);
301         if (!gst_dp_validate_header (GST_DP_HEADER_LENGTH, header)) {
302           g_free (header);
303           goto header_validate_error;
304         }
305 
306         /* store types and payload length. Also store the header, which we need
307          * to make the payload. */
308         this->payload_length = gst_dp_header_payload_length (header);
309         this->payload_type = gst_dp_header_payload_type (header);
310         /* free previous header and store new one. */
311         g_free (this->header);
312         this->header = header;
313 
314         GST_LOG_OBJECT (this,
315             "read GDP header, payload size %d, payload type %d, switching to state PAYLOAD",
316             this->payload_length, this->payload_type);
317         this->state = GST_GDP_DEPAY_STATE_PAYLOAD;
318         break;
319       }
320       case GST_GDP_DEPAY_STATE_PAYLOAD:
321       {
322         /* in this state we wait for all the payload data to be available in the
323          * adapter. Then we switch to the state where we actually process the
324          * payload. */
325         available = gst_adapter_available (this->adapter);
326         if (available < this->payload_length)
327           goto done;
328 
329         /* change state based on type */
330         if (this->payload_type == GST_DP_PAYLOAD_BUFFER) {
331           GST_LOG_OBJECT (this, "switching to state BUFFER");
332           this->state = GST_GDP_DEPAY_STATE_BUFFER;
333         } else if (this->payload_type == GST_DP_PAYLOAD_CAPS) {
334           GST_LOG_OBJECT (this, "switching to state CAPS");
335           this->state = GST_GDP_DEPAY_STATE_CAPS;
336         } else if (this->payload_type >= GST_DP_PAYLOAD_EVENT_NONE) {
337           GST_LOG_OBJECT (this, "switching to state EVENT");
338           this->state = GST_GDP_DEPAY_STATE_EVENT;
339         } else {
340           goto wrong_type;
341         }
342 
343         if (this->payload_length) {
344           const guint8 *data;
345           gboolean res;
346 
347           data = gst_adapter_map (this->adapter, this->payload_length);
348           res = gst_dp_validate_payload (GST_DP_HEADER_LENGTH, this->header,
349               data);
350           gst_adapter_unmap (this->adapter);
351 
352           if (!res)
353             goto payload_validate_error;
354         }
355 
356         break;
357       }
358       case GST_GDP_DEPAY_STATE_BUFFER:
359       {
360         /* if we receive a buffer without caps first, we error out */
361         if (!this->caps)
362           goto no_caps;
363 
364         GST_LOG_OBJECT (this, "reading GDP buffer from adapter");
365         buf =
366             gst_dp_buffer_from_header (GST_DP_HEADER_LENGTH, this->header,
367             this->allocator, &this->allocation_params);
368         if (!buf)
369           goto buffer_failed;
370 
371         /* now take the payload if there is any */
372         if (this->payload_length > 0) {
373           GstMapInfo map;
374 
375           gst_buffer_map (buf, &map, GST_MAP_WRITE);
376           gst_adapter_copy (this->adapter, map.data, 0, this->payload_length);
377           gst_buffer_unmap (buf, &map);
378 
379           gst_adapter_flush (this->adapter, this->payload_length);
380         }
381 
382         if (GST_BUFFER_TIMESTAMP (buf) > -this->ts_offset)
383           GST_BUFFER_TIMESTAMP (buf) += this->ts_offset;
384         else
385           GST_BUFFER_TIMESTAMP (buf) = 0;
386 
387         if (GST_BUFFER_DTS (buf) > -this->ts_offset)
388           GST_BUFFER_DTS (buf) += this->ts_offset;
389         else
390           GST_BUFFER_DTS (buf) = 0;
391 
392         /* set caps and push */
393         GST_LOG_OBJECT (this, "deserialized buffer %p, pushing, timestamp %"
394             GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT
395             ", offset %" G_GINT64_FORMAT ", offset_end %" G_GINT64_FORMAT
396             ", size %" G_GSIZE_FORMAT ", flags 0x%x",
397             buf,
398             GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
399             GST_TIME_ARGS (GST_BUFFER_DURATION (buf)),
400             GST_BUFFER_OFFSET (buf), GST_BUFFER_OFFSET_END (buf),
401             gst_buffer_get_size (buf), GST_BUFFER_FLAGS (buf));
402         ret = gst_pad_push (this->srcpad, buf);
403         if (ret != GST_FLOW_OK)
404           goto push_error;
405 
406         GST_LOG_OBJECT (this, "switching to state HEADER");
407         this->state = GST_GDP_DEPAY_STATE_HEADER;
408         break;
409       }
410       case GST_GDP_DEPAY_STATE_CAPS:
411       {
412         guint8 *payload;
413 
414         /* take the payload of the caps */
415         GST_LOG_OBJECT (this, "reading GDP caps from adapter");
416         payload = gst_adapter_take (this->adapter, this->payload_length);
417         caps = gst_dp_caps_from_packet (GST_DP_HEADER_LENGTH, this->header,
418             payload);
419         g_free (payload);
420         if (!caps)
421           goto caps_failed;
422 
423         GST_DEBUG_OBJECT (this, "deserialized caps %" GST_PTR_FORMAT, caps);
424         gst_caps_replace (&(this->caps), caps);
425         gst_pad_set_caps (this->srcpad, caps);
426         gst_gdp_depay_decide_allocation (this);
427         /* drop the creation ref we still have */
428         gst_caps_unref (caps);
429 
430         GST_LOG_OBJECT (this, "switching to state HEADER");
431         this->state = GST_GDP_DEPAY_STATE_HEADER;
432         break;
433       }
434       case GST_GDP_DEPAY_STATE_EVENT:
435       {
436         guint8 *payload;
437 
438         GST_LOG_OBJECT (this, "reading GDP event from adapter");
439 
440         /* adapter doesn't like 0 length payload */
441         if (this->payload_length > 0)
442           payload = gst_adapter_take (this->adapter, this->payload_length);
443         else
444           payload = NULL;
445         event = gst_dp_event_from_packet (GST_DP_HEADER_LENGTH, this->header,
446             payload);
447         g_free (payload);
448         if (!event)
449           goto event_failed;
450 
451         GST_DEBUG_OBJECT (this, "deserialized event %p of type %s, pushing",
452             event, gst_event_type_get_name (event->type));
453         gst_pad_push_event (this->srcpad, event);
454 
455         GST_LOG_OBJECT (this, "switching to state HEADER");
456         this->state = GST_GDP_DEPAY_STATE_HEADER;
457         break;
458       }
459     }
460   }
461 
462 done:
463   return ret;
464 
465   /* ERRORS */
466 header_validate_error:
467   {
468     GST_ELEMENT_ERROR (this, STREAM, DECODE, (NULL),
469         ("GDP packet header does not validate"));
470     ret = GST_FLOW_ERROR;
471     goto done;
472   }
473 payload_validate_error:
474   {
475     GST_ELEMENT_ERROR (this, STREAM, DECODE, (NULL),
476         ("GDP packet payload does not validate"));
477     ret = GST_FLOW_ERROR;
478     goto done;
479   }
480 wrong_type:
481   {
482     GST_ELEMENT_ERROR (this, STREAM, DECODE, (NULL),
483         ("GDP packet header is of wrong type"));
484     ret = GST_FLOW_ERROR;
485     goto done;
486   }
487 no_caps:
488   {
489     GST_ELEMENT_ERROR (this, STREAM, DECODE, (NULL),
490         ("Received a buffer without first receiving caps"));
491     ret = GST_FLOW_NOT_NEGOTIATED;
492     goto done;
493   }
494 buffer_failed:
495   {
496     GST_ELEMENT_ERROR (this, STREAM, DECODE, (NULL),
497         ("could not create buffer from GDP packet"));
498     ret = GST_FLOW_ERROR;
499     goto done;
500   }
501 push_error:
502   {
503     GST_WARNING_OBJECT (this, "pushing depayloaded buffer returned %d", ret);
504     goto done;
505   }
506 caps_failed:
507   {
508     GST_ELEMENT_ERROR (this, STREAM, DECODE, (NULL),
509         ("could not create caps from GDP packet"));
510     ret = GST_FLOW_ERROR;
511     goto done;
512   }
513 event_failed:
514   {
515     GST_ELEMENT_ERROR (this, STREAM, DECODE, (NULL),
516         ("could not create event from GDP packet"));
517     ret = GST_FLOW_ERROR;
518     goto done;
519   }
520 }
521 
522 static GstStateChangeReturn
gst_gdp_depay_change_state(GstElement * element,GstStateChange transition)523 gst_gdp_depay_change_state (GstElement * element, GstStateChange transition)
524 {
525   GstStateChangeReturn ret;
526   GstGDPDepay *this = GST_GDP_DEPAY (element);
527 
528   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
529 
530   switch (transition) {
531     case GST_STATE_CHANGE_PAUSED_TO_READY:
532       if (this->caps) {
533         gst_caps_unref (this->caps);
534         this->caps = NULL;
535       }
536       gst_adapter_clear (this->adapter);
537       if (this->allocator)
538         gst_object_unref (this->allocator);
539       this->allocator = NULL;
540       gst_allocation_params_init (&this->allocation_params);
541       break;
542     default:
543       break;
544   }
545   return ret;
546 }
547 
548 static void
gst_gdp_depay_decide_allocation(GstGDPDepay * gdpdepay)549 gst_gdp_depay_decide_allocation (GstGDPDepay * gdpdepay)
550 {
551   GstAllocator *allocator;
552   GstAllocationParams params;
553   GstQuery *query = NULL;
554   GstCaps *caps;
555 
556   caps = gst_pad_query_caps (gdpdepay->srcpad, NULL);
557   if (!caps) {
558     GST_LOG_OBJECT (gdpdepay,
559         "No peer pad caps found. Using default allocator.");
560     return;
561   }
562 
563   if (!gst_caps_is_fixed (caps)) {
564     GST_LOG_OBJECT (gdpdepay, "Caps on src pad are not fixed. Not querying.");
565     return;
566   }
567 
568   query = gst_query_new_allocation (caps, TRUE);
569   if (!gst_pad_peer_query (gdpdepay->srcpad, query)) {
570     GST_WARNING_OBJECT (gdpdepay, "Peer allocation query failed.");
571   }
572 
573   if (gst_query_get_n_allocation_params (query) > 0) {
574     gst_query_parse_nth_allocation_param (query, 0, &allocator, &params);
575   } else {
576     allocator = NULL;
577     gst_allocation_params_init (&params);
578   }
579 
580   if (gdpdepay->allocator)
581     gst_object_unref (gdpdepay->allocator);
582 
583   gdpdepay->allocator = allocator;
584   gdpdepay->allocation_params = params;
585 
586   gst_caps_unref (caps);
587   gst_query_unref (query);
588 }
589 
590 gboolean
gst_gdp_depay_plugin_init(GstPlugin * plugin)591 gst_gdp_depay_plugin_init (GstPlugin * plugin)
592 {
593   if (!gst_element_register (plugin, "gdpdepay", GST_RANK_NONE,
594           GST_TYPE_GDP_DEPAY))
595     return FALSE;
596 
597   return TRUE;
598 }
599