gst/gdp/gstgdppay.c: add crc-header and crc-payload properties don't error out on...
[platform/upstream/gstreamer.git] / gst / gdp / gstgdppay.c
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., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /**
21  * SECTION:element-gdppay
22  * @see_also: gdpdepay
23  *
24  * <refsect2>
25  * <para>
26  * This element payloads GStreamer buffers and events using the
27  * GStreamer Data Protocol.
28  * </para>
29  * </refsect2>
30  */
31
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
35
36 #include <gst/dataprotocol/dataprotocol.h>
37
38 #include "gstgdppay.h"
39
40 /* elementfactory information */
41 static const GstElementDetails gdp_pay_details =
42 GST_ELEMENT_DETAILS ("GDP Payloader",
43     "GDP/Payloader",
44     "Payloads GStreamer Data Protocol buffers",
45     "Thomas Vander Stichele <thomas at apestaart dot org>");
46
47 static GstStaticPadTemplate gdp_pay_sink_template =
48 GST_STATIC_PAD_TEMPLATE ("sink",
49     GST_PAD_SINK,
50     GST_PAD_ALWAYS,
51     GST_STATIC_CAPS_ANY);
52
53 static GstStaticPadTemplate gdp_pay_src_template =
54 GST_STATIC_PAD_TEMPLATE ("src",
55     GST_PAD_SRC,
56     GST_PAD_ALWAYS,
57     GST_STATIC_CAPS ("application/x-gdp"));
58
59 GST_DEBUG_CATEGORY (gst_gdp_pay_debug);
60 #define GST_CAT_DEFAULT gst_gdp_pay_debug
61
62 #define DEFAULT_CRC_HEADER TRUE
63 #define DEFAULT_CRC_PAYLOAD FALSE
64
65 enum
66 {
67   PROP_0,
68   PROP_CRC_HEADER,
69   PROP_CRC_PAYLOAD,
70 };
71
72 #define _do_init(x) \
73     GST_DEBUG_CATEGORY_INIT (gst_gdp_pay_debug, "gdppay", 0, \
74     "GDP payloader");
75
76 GST_BOILERPLATE_FULL (GstGDPPay, gst_gdp_pay, GstElement,
77     GST_TYPE_ELEMENT, _do_init);
78
79 static GstFlowReturn gst_gdp_pay_chain (GstPad * pad, GstBuffer * buffer);
80 static gboolean gst_gdp_pay_sink_event (GstPad * pad, GstEvent * event);
81 static GstStateChangeReturn gst_gdp_pay_change_state (GstElement *
82     element, GstStateChange transition);
83
84 static void gst_gdp_pay_set_property (GObject * object, guint prop_id,
85     const GValue * value, GParamSpec * pspec);
86 static void gst_gdp_pay_get_property (GObject * object, guint prop_id,
87     GValue * value, GParamSpec * pspec);
88
89 static void gst_gdp_pay_dispose (GObject * gobject);
90
91 static void
92 gst_gdp_pay_base_init (gpointer g_class)
93 {
94   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
95
96   gst_element_class_set_details (element_class, &gdp_pay_details);
97
98   gst_element_class_add_pad_template (element_class,
99       gst_static_pad_template_get (&gdp_pay_sink_template));
100   gst_element_class_add_pad_template (element_class,
101       gst_static_pad_template_get (&gdp_pay_src_template));
102 }
103
104 static void
105 gst_gdp_pay_class_init (GstGDPPayClass * klass)
106 {
107   GObjectClass *gobject_class;
108   GstElementClass *gstelement_class;
109
110   gobject_class = (GObjectClass *) klass;
111   gstelement_class = (GstElementClass *) klass;
112
113   parent_class = g_type_class_peek_parent (klass);
114
115   gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_gdp_pay_set_property);
116   gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_gdp_pay_get_property);
117   gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_gdp_pay_dispose);
118   gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_gdp_pay_change_state);
119
120   g_object_class_install_property (gobject_class, PROP_CRC_HEADER,
121       g_param_spec_boolean ("crc-header", "CRC Header",
122           "Calculate and store a CRC checksum on the header",
123           DEFAULT_CRC_HEADER, G_PARAM_READWRITE));
124   g_object_class_install_property (gobject_class, PROP_CRC_PAYLOAD,
125       g_param_spec_boolean ("crc-payload", "CRC Payload",
126           "Calculate and store a CRC checksum on the payload",
127           DEFAULT_CRC_PAYLOAD, G_PARAM_READWRITE));
128 }
129
130 static void
131 gst_gdp_pay_init (GstGDPPay * gdppay, GstGDPPayClass * g_class)
132 {
133   gdppay->sinkpad =
134       gst_pad_new_from_static_template (&gdp_pay_sink_template, "sink");
135   gst_pad_set_chain_function (gdppay->sinkpad,
136       GST_DEBUG_FUNCPTR (gst_gdp_pay_chain));
137   gst_pad_set_event_function (gdppay->sinkpad,
138       GST_DEBUG_FUNCPTR (gst_gdp_pay_sink_event));
139   gst_element_add_pad (GST_ELEMENT (gdppay), gdppay->sinkpad);
140
141   gdppay->srcpad =
142       gst_pad_new_from_static_template (&gdp_pay_src_template, "src");
143   gst_element_add_pad (GST_ELEMENT (gdppay), gdppay->srcpad);
144
145   gdppay->offset = 0;
146
147   gdppay->crc_header = DEFAULT_CRC_HEADER;
148   gdppay->crc_payload = DEFAULT_CRC_PAYLOAD;
149   gdppay->header_flag = gdppay->crc_header | gdppay->crc_payload;
150 }
151
152 static void
153 gst_gdp_pay_dispose (GObject * gobject)
154 {
155   GstGDPPay *this = GST_GDP_PAY (gobject);
156
157   if (this->caps_buf) {
158     gst_buffer_unref (this->caps_buf);
159     this->caps_buf = NULL;
160   }
161   if (this->new_segment_buf) {
162     gst_buffer_unref (this->new_segment_buf);
163     this->new_segment_buf = NULL;
164   }
165   GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (gobject));
166 }
167
168 /* set OFFSET and OFFSET_END with running count */
169 static void
170 gst_gdp_stamp_buffer (GstGDPPay * this, GstBuffer * buffer)
171 {
172   GST_BUFFER_OFFSET (buffer) = this->offset;
173   GST_BUFFER_OFFSET_END (buffer) = this->offset + GST_BUFFER_SIZE (buffer);
174   this->offset = GST_BUFFER_OFFSET_END (buffer);
175 }
176
177 static GstBuffer *
178 gst_gdp_buffer_from_caps (GstGDPPay * this, GstCaps * caps)
179 {
180   GstBuffer *headerbuf;
181   GstBuffer *payloadbuf;
182   guint8 *header, *payload;
183   guint len;
184
185   if (!gst_dp_packet_from_caps (caps, this->header_flag, &len, &header,
186           &payload)) {
187     GST_WARNING_OBJECT (this, "could not create GDP header from caps");
188     return NULL;
189   }
190
191   GST_LOG_OBJECT (this, "creating GDP header and payload buffer from caps");
192   headerbuf = gst_buffer_new ();
193   gst_buffer_set_data (headerbuf, header, len);
194   GST_BUFFER_MALLOCDATA (headerbuf) = header;
195
196   payloadbuf = gst_buffer_new ();
197   gst_buffer_set_data (payloadbuf, payload,
198       gst_dp_header_payload_length (header));
199   GST_BUFFER_MALLOCDATA (payloadbuf) = payload;
200
201   return gst_buffer_join (headerbuf, payloadbuf);
202 }
203
204 static GstBuffer *
205 gst_gdp_pay_buffer_from_buffer (GstGDPPay * this, GstBuffer * buffer)
206 {
207   GstBuffer *headerbuf;
208   guint8 *header;
209   guint len;
210
211   if (!gst_dp_header_from_buffer (buffer, this->header_flag, &len, &header)) {
212     GST_WARNING_OBJECT (this, "could not create GDP header from buffer");
213     return NULL;
214   }
215
216   GST_LOG_OBJECT (this, "creating GDP header and payload buffer from buffer");
217   headerbuf = gst_buffer_new ();
218   gst_buffer_set_data (headerbuf, header, len);
219   GST_BUFFER_MALLOCDATA (headerbuf) = header;
220
221   /* we do not want to lose the ref on the incoming buffer */
222   gst_buffer_ref (buffer);
223   return gst_buffer_join (headerbuf, buffer);
224 }
225
226 static GstBuffer *
227 gst_gdp_buffer_from_event (GstGDPPay * this, GstEvent * event)
228 {
229   GstBuffer *headerbuf;
230   GstBuffer *payloadbuf;
231   guint8 *header, *payload;
232   guint len;
233
234   if (!gst_dp_packet_from_event (event, this->header_flag, &len, &header,
235           &payload)) {
236     GST_WARNING_OBJECT (this, "could not create GDP header from event %s (%d)",
237         gst_event_type_get_name (event->type), event->type);
238     return NULL;
239   }
240
241   GST_LOG_OBJECT (this, "creating GDP header and payload buffer from event");
242   headerbuf = gst_buffer_new ();
243   gst_buffer_set_data (headerbuf, header, len);
244   GST_BUFFER_MALLOCDATA (headerbuf) = header;
245
246   payloadbuf = gst_buffer_new ();
247   gst_buffer_set_data (payloadbuf, payload,
248       gst_dp_header_payload_length (header));
249   GST_BUFFER_MALLOCDATA (payloadbuf) = payload;
250
251   return gst_buffer_join (headerbuf, payloadbuf);
252 }
253
254
255 /* set our caps with streamheader, based on the latest newsegment and caps,
256  * and (possibly) GDP-serialized buffers of the streamheaders on the src pad */
257 static GstFlowReturn
258 gst_gdp_pay_reset_streamheader (GstGDPPay * this)
259 {
260   GstCaps *caps;
261   GstStructure *structure;
262   GstBuffer *new_segment_buf, *caps_buf;
263   GstFlowReturn r = GST_FLOW_OK;
264
265   GValue array = { 0 };
266   GValue value = { 0 };
267
268   /* we need both new segment and caps before we can set streamheader */
269   if (!this->new_segment_buf || !this->caps_buf)
270     return GST_FLOW_OK;
271
272   /* we copy to avoid circular refcounts */
273   new_segment_buf = gst_buffer_copy (this->new_segment_buf);
274   caps_buf = gst_buffer_copy (this->caps_buf);
275
276   /* put copies of the buffers in a fixed list */
277   g_value_init (&array, GST_TYPE_ARRAY);
278
279   g_value_init (&value, GST_TYPE_BUFFER);
280   gst_value_set_buffer (&value, new_segment_buf);
281   gst_value_array_append_value (&array, &value);
282   g_value_unset (&value);
283
284   g_value_init (&value, GST_TYPE_BUFFER);
285   gst_value_set_buffer (&value, caps_buf);
286   gst_value_array_append_value (&array, &value);
287   g_value_unset (&value);
288
289   /* we also need to add GDP serializations of the streamheaders of the
290    * incoming caps */
291   structure = gst_caps_get_structure (this->caps, 0);
292   if (gst_structure_has_field (structure, "streamheader")) {
293     const GValue *sh;
294     GArray *buffers;
295     GstBuffer *buffer;
296     int i;
297
298     sh = gst_structure_get_value (structure, "streamheader");
299     buffers = g_value_peek_pointer (sh);
300     GST_DEBUG_OBJECT (this,
301         "Need to serialize %d incoming streamheader buffers on ours",
302         buffers->len);
303     for (i = 0; i < buffers->len; ++i) {
304       GValue *bufval;
305       GstBuffer *outbuffer;
306
307       bufval = &g_array_index (buffers, GValue, i);
308       buffer = g_value_peek_pointer (bufval);
309       outbuffer = gst_gdp_pay_buffer_from_buffer (this, buffer);
310       if (outbuffer) {
311         g_value_init (&value, GST_TYPE_BUFFER);
312         gst_value_set_buffer (&value, outbuffer);
313         gst_value_array_append_value (&array, &value);
314         g_value_unset (&value);
315       }
316       /* FIXME: if one or more in this loop fail to produce and outbuffer,
317        * should we error out ? Once ? Every time ? */
318     }
319   }
320
321   caps = gst_caps_from_string ("application/x-gdp");
322   structure = gst_caps_get_structure (caps, 0);
323
324   gst_structure_set_value (structure, "streamheader", &array);
325   g_value_unset (&array);
326
327   /* Unref our copies */
328   gst_buffer_unref (new_segment_buf);
329   gst_buffer_unref (caps_buf);
330
331   GST_DEBUG_OBJECT (this, "Setting caps on src pad %" GST_PTR_FORMAT, caps);
332   gst_pad_set_caps (this->srcpad, caps);
333   gst_buffer_set_caps (this->caps_buf, caps);
334   gst_buffer_set_caps (this->new_segment_buf, caps);
335
336   /* if these are our first ever buffers, send out new_segment first */
337   if (!this->sent_streamheader) {
338     GstEvent *event =
339         gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_BYTES, 0, -1, 0);
340     GST_DEBUG_OBJECT (this, "Sending out new_segment event %p", event);
341     if (!gst_pad_push_event (this->srcpad, event)) {
342       GST_WARNING_OBJECT (this, "pushing new segment failed");
343       return GST_FLOW_ERROR;
344     }
345   }
346
347   /* push out these streamheader buffers, then flush our internal queue */
348   GST_DEBUG_OBJECT (this, "Pushing GDP new_segment buffer %p",
349       this->new_segment_buf);
350   /* we stored these bufs with refcount 1, so make sure we keep a ref */
351   r = gst_pad_push (this->srcpad, gst_buffer_ref (this->new_segment_buf));
352   if (r != GST_FLOW_OK) {
353     GST_WARNING_OBJECT (this, "pushing GDP newsegment buffer returned %d", r);
354     return r;
355   }
356   GST_DEBUG_OBJECT (this, "Pushing GDP caps buffer %p", this->new_segment_buf);
357   r = gst_pad_push (this->srcpad, gst_buffer_ref (this->caps_buf));
358   if (r != GST_FLOW_OK) {
359     GST_WARNING_OBJECT (this, "pushing GDP caps buffer returned %d", r);
360     return r;
361   }
362   this->sent_streamheader = TRUE;
363   GST_DEBUG_OBJECT (this, "need to push %d queued buffers",
364       g_list_length (this->queue));
365   if (this->queue) {
366     GList *l;
367
368     for (l = this->queue; l; l = g_list_next (l)) {
369       GST_DEBUG_OBJECT (this, "Pushing queued GDP buffer %p", l->data);
370       gst_buffer_set_caps (l->data, caps);
371       r = gst_pad_push (this->srcpad, l->data);
372       if (r != GST_FLOW_OK) {
373         GST_WARNING_OBJECT (this, "pushing queued GDP buffer returned %d", r);
374         return r;
375       }
376     }
377   }
378
379   return r;
380 }
381
382 /* queue a buffer internally if we haven't sent streamheader buffers yet;
383  * otherwise, just push on */
384 static GstFlowReturn
385 gst_gdp_queue_buffer (GstGDPPay * this, GstBuffer * buffer)
386 {
387   if (this->sent_streamheader) {
388     GST_LOG_OBJECT (this, "Pushing GDP buffer %p", buffer);
389     GST_LOG_OBJECT (this, "set caps %" GST_PTR_FORMAT, this->caps);
390     return gst_pad_push (this->srcpad, buffer);
391   }
392
393   /* store it on an internal queue */
394   this->queue = g_list_append (this->queue, buffer);
395   GST_DEBUG_OBJECT (this, "queued buffer %p, now %d buffers queued",
396       buffer, g_list_length (this->queue));
397   return GST_FLOW_OK;
398 }
399
400 static GstFlowReturn
401 gst_gdp_pay_chain (GstPad * pad, GstBuffer * buffer)
402 {
403   GstGDPPay *this;
404   GstCaps *caps;
405   GstBuffer *outbuffer;
406   GstFlowReturn ret;
407
408   this = GST_GDP_PAY (gst_pad_get_parent (pad));
409
410   /* we should have received a new_segment before, otherwise it's a bug.
411    * fake one in that case */
412   if (!this->new_segment_buf) {
413     GstEvent *event;
414
415     GST_WARNING_OBJECT (this,
416         "did not receive new-segment before first buffer");
417     event = gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_BYTES, 0, -1, 0);
418     outbuffer = gst_gdp_buffer_from_event (this, event);
419     gst_event_unref (event);
420
421     /* GDP 0.2 doesn't know about new-segment, so this is not fatal */
422     if (!outbuffer) {
423       GST_ELEMENT_WARNING (this, STREAM, ENCODE, (NULL),
424           ("Could not create GDP buffer from new segment event"));
425 /*
426       ret = GST_FLOW_ERROR;
427       goto done;
428 */
429     } else {
430
431       gst_gdp_stamp_buffer (this, outbuffer);
432       GST_BUFFER_TIMESTAMP (outbuffer) = GST_BUFFER_TIMESTAMP (buffer);
433       GST_BUFFER_DURATION (outbuffer) = 0;
434       GST_DEBUG_OBJECT (this, "Storing buffer %p as new_segment_buf",
435           outbuffer);
436       this->new_segment_buf = outbuffer;
437     }
438   }
439
440   /* make sure we've received caps before */
441   caps = gst_buffer_get_caps (buffer);
442   if (!this->caps && !caps) {
443     GST_WARNING_OBJECT (this, "first received buffer does not have caps set");
444     if (caps)
445       gst_caps_unref (caps);
446     ret = GST_FLOW_NOT_NEGOTIATED;
447     goto done;
448   }
449
450   /* if the caps have changed, process caps first */
451   if (caps && !gst_caps_is_equal (this->caps, caps)) {
452     GST_LOG_OBJECT (this, "caps changed to %p, %" GST_PTR_FORMAT, caps, caps);
453     gst_caps_replace (&(this->caps), caps);
454     outbuffer = gst_gdp_buffer_from_caps (this, caps);
455     if (!outbuffer) {
456       GST_ELEMENT_ERROR (this, STREAM, ENCODE, (NULL),
457           ("Could not create GDP buffer from caps %" GST_PTR_FORMAT, caps));
458       gst_caps_unref (caps);
459       ret = GST_FLOW_ERROR;
460       goto done;
461     }
462
463     gst_gdp_stamp_buffer (this, outbuffer);
464     GST_BUFFER_TIMESTAMP (outbuffer) = GST_BUFFER_TIMESTAMP (buffer);
465     GST_BUFFER_DURATION (outbuffer) = 0;
466     GST_BUFFER_FLAG_SET (outbuffer, GST_BUFFER_FLAG_IN_CAPS);
467     this->caps_buf = outbuffer;
468     gst_gdp_pay_reset_streamheader (this);
469   }
470
471   /* create a GDP header packet,
472    * then create a GST buffer of the header packet and the buffer contents */
473   outbuffer = gst_gdp_pay_buffer_from_buffer (this, buffer);
474   if (!outbuffer) {
475     GST_ELEMENT_ERROR (this, STREAM, ENCODE, (NULL),
476         ("Could not create GDP buffer from buffer"));
477     ret = GST_FLOW_ERROR;
478     goto done;
479   }
480
481   gst_gdp_stamp_buffer (this, outbuffer);
482   GST_BUFFER_TIMESTAMP (outbuffer) = GST_BUFFER_TIMESTAMP (buffer);
483   GST_BUFFER_DURATION (outbuffer) = GST_BUFFER_DURATION (buffer);
484
485   ret = gst_gdp_queue_buffer (this, outbuffer);
486
487 done:
488   gst_buffer_unref (buffer);
489   gst_object_unref (this);
490   return ret;
491 }
492
493 static gboolean
494 gst_gdp_pay_sink_event (GstPad * pad, GstEvent * event)
495 {
496   GstBuffer *outbuffer;
497   GstGDPPay *this = GST_GDP_PAY (gst_pad_get_parent (pad));
498   GstFlowReturn flowret;
499   gboolean ret = TRUE;
500
501   GST_DEBUG_OBJECT (this, "received event %s (%d)",
502       gst_event_type_get_name (event->type), event->type);
503
504   /* now turn the event into a buffer */
505   outbuffer = gst_gdp_buffer_from_event (this, event);
506   if (!outbuffer) {
507     GST_ELEMENT_WARNING (this, STREAM, ENCODE, (NULL),
508         ("Could not create GDP buffer from received event"));
509     ret = FALSE;
510     goto done;
511   }
512   gst_gdp_stamp_buffer (this, outbuffer);
513   GST_BUFFER_TIMESTAMP (outbuffer) = GST_EVENT_TIMESTAMP (event);
514   GST_BUFFER_DURATION (outbuffer) = 0;
515
516   /* if we got a new segment, we should put it on our streamheader,
517    * and not send it on */
518   if (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT) {
519     GST_DEBUG_OBJECT (this, "received new_segment event");
520     if (this->new_segment_buf) {
521       gst_buffer_unref (this->new_segment_buf);
522     }
523     GST_DEBUG_OBJECT (this, "Storing buffer %p as new_segment_buf", outbuffer);
524     this->new_segment_buf = outbuffer;
525     gst_gdp_pay_reset_streamheader (this);
526   } else {
527     flowret = gst_gdp_queue_buffer (this, outbuffer);
528     if (flowret != GST_FLOW_OK) {
529       GST_WARNING_OBJECT (this, "queueing GDP caps buffer returned %d",
530           flowret);
531       ret = FALSE;
532       goto done;
533     }
534   }
535
536   /* if we have EOS, we should send on EOS ourselves */
537   if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
538     GST_DEBUG_OBJECT (this, "Sending on EOS event %p", event);
539     return gst_pad_push_event (this->srcpad, event);
540   };
541
542 done:
543   gst_object_unref (this);
544   gst_event_unref (event);
545   return ret;
546 }
547
548 static void
549 gst_gdp_pay_set_property (GObject * object, guint prop_id,
550     const GValue * value, GParamSpec * pspec)
551 {
552   GstGDPPay *this;
553
554   g_return_if_fail (GST_IS_GDP_PAY (object));
555   this = GST_GDP_PAY (object);
556
557   switch (prop_id) {
558     case PROP_CRC_HEADER:
559       this->crc_header = g_value_get_boolean (value);
560       this->header_flag = this->crc_header | this->crc_payload;
561       break;
562     case PROP_CRC_PAYLOAD:
563       this->crc_payload = g_value_get_boolean (value);
564       this->header_flag = this->crc_header | this->crc_payload;
565       break;
566     default:
567       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
568       break;
569   }
570 }
571
572 static void
573 gst_gdp_pay_get_property (GObject * object, guint prop_id,
574     GValue * value, GParamSpec * pspec)
575 {
576   GstGDPPay *this;
577
578   g_return_if_fail (GST_IS_GDP_PAY (object));
579   this = GST_GDP_PAY (object);
580
581   switch (prop_id) {
582     case PROP_CRC_HEADER:
583       g_value_set_boolean (value, this->crc_header);
584       break;
585     case PROP_CRC_PAYLOAD:
586       g_value_set_boolean (value, this->crc_payload);
587       break;
588     default:
589       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
590       break;
591   }
592 }
593
594 static GstStateChangeReturn
595 gst_gdp_pay_change_state (GstElement * element, GstStateChange transition)
596 {
597   GstStateChangeReturn ret;
598   GstGDPPay *this = GST_GDP_PAY (element);
599
600   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
601
602   switch (transition) {
603     case GST_STATE_CHANGE_READY_TO_NULL:
604       if (this->caps) {
605         gst_caps_unref (this->caps);
606         this->caps = NULL;
607       }
608       break;
609     default:
610       break;
611   }
612
613   return ret;
614 }
615
616 gboolean
617 gst_gdp_pay_plugin_init (GstPlugin * plugin)
618 {
619   if (!gst_element_register (plugin, "gdppay", GST_RANK_NONE, GST_TYPE_GDP_PAY))
620     return FALSE;
621
622   return TRUE;
623 }