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, ¶ms);
575 } else {
576 allocator = NULL;
577 gst_allocation_params_init (¶ms);
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