94e76a306281e254cf979340ce99b3a5a91f01e7
[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  * <para>
30  * <programlisting>
31  * gst-launch -v -m videotestsrc num-buffers=50 ! gdppay ! filesink location=test.gdp
32  * </programlisting>
33  * This pipeline creates a serialized video stream that can be played back
34  * with the example shown in gdpdepay.
35  * </para>
36  * </refsect2>
37  */
38
39 #ifdef HAVE_CONFIG_H
40 #include "config.h"
41 #endif
42
43 #include <gst/dataprotocol/dataprotocol.h>
44
45 #include "gstgdppay.h"
46
47 /* elementfactory information */
48 static const GstElementDetails gdp_pay_details =
49 GST_ELEMENT_DETAILS ("GDP Payloader",
50     "GDP/Payloader",
51     "Payloads GStreamer Data Protocol buffers",
52     "Thomas Vander Stichele <thomas at apestaart dot org>");
53
54 static GstStaticPadTemplate gdp_pay_sink_template =
55 GST_STATIC_PAD_TEMPLATE ("sink",
56     GST_PAD_SINK,
57     GST_PAD_ALWAYS,
58     GST_STATIC_CAPS_ANY);
59
60 static GstStaticPadTemplate gdp_pay_src_template =
61 GST_STATIC_PAD_TEMPLATE ("src",
62     GST_PAD_SRC,
63     GST_PAD_ALWAYS,
64     GST_STATIC_CAPS ("application/x-gdp"));
65
66 GST_DEBUG_CATEGORY_STATIC (gst_gdp_pay_debug);
67 #define GST_CAT_DEFAULT gst_gdp_pay_debug
68
69 #define DEFAULT_CRC_HEADER TRUE
70 #define DEFAULT_CRC_PAYLOAD FALSE
71 #define DEFAULT_VERSION GST_DP_VERSION_1_0
72
73 enum
74 {
75   PROP_0,
76   PROP_CRC_HEADER,
77   PROP_CRC_PAYLOAD,
78   PROP_VERSION,
79 };
80
81 #define _do_init(x) \
82     GST_DEBUG_CATEGORY_INIT (gst_gdp_pay_debug, "gdppay", 0, \
83     "GDP payloader");
84
85 GST_BOILERPLATE_FULL (GstGDPPay, gst_gdp_pay, GstElement,
86     GST_TYPE_ELEMENT, _do_init);
87
88 static void gst_gdp_pay_reset (GstGDPPay * this);
89 static GstFlowReturn gst_gdp_pay_chain (GstPad * pad, GstBuffer * buffer);
90
91 static gboolean gst_gdp_pay_src_event (GstPad * pad, GstEvent * event);
92 static gboolean gst_gdp_pay_sink_event (GstPad * pad, GstEvent * event);
93
94 static GstStateChangeReturn gst_gdp_pay_change_state (GstElement *
95     element, GstStateChange transition);
96
97 static void gst_gdp_pay_set_property (GObject * object, guint prop_id,
98     const GValue * value, GParamSpec * pspec);
99 static void gst_gdp_pay_get_property (GObject * object, guint prop_id,
100     GValue * value, GParamSpec * pspec);
101
102 static void gst_gdp_pay_finalize (GObject * gobject);
103
104 static void
105 gst_gdp_pay_base_init (gpointer g_class)
106 {
107   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
108
109   gst_element_class_set_details (element_class, &gdp_pay_details);
110
111   gst_element_class_add_pad_template (element_class,
112       gst_static_pad_template_get (&gdp_pay_sink_template));
113   gst_element_class_add_pad_template (element_class,
114       gst_static_pad_template_get (&gdp_pay_src_template));
115 }
116
117 static void
118 gst_gdp_pay_class_init (GstGDPPayClass * klass)
119 {
120   GObjectClass *gobject_class;
121   GstElementClass *gstelement_class;
122
123   gobject_class = (GObjectClass *) klass;
124   gstelement_class = (GstElementClass *) klass;
125
126   gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_gdp_pay_set_property);
127   gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_gdp_pay_get_property);
128   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_gdp_pay_finalize);
129
130   g_object_class_install_property (gobject_class, PROP_CRC_HEADER,
131       g_param_spec_boolean ("crc-header", "CRC Header",
132           "Calculate and store a CRC checksum on the header",
133           DEFAULT_CRC_HEADER, G_PARAM_READWRITE));
134   g_object_class_install_property (gobject_class, PROP_CRC_PAYLOAD,
135       g_param_spec_boolean ("crc-payload", "CRC Payload",
136           "Calculate and store a CRC checksum on the payload",
137           DEFAULT_CRC_PAYLOAD, G_PARAM_READWRITE));
138   g_object_class_install_property (gobject_class, PROP_VERSION,
139       g_param_spec_enum ("version", "Version",
140           "Version of the GStreamer Data Protocol",
141           GST_TYPE_DP_VERSION, DEFAULT_VERSION, G_PARAM_READWRITE));
142
143   gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_gdp_pay_change_state);
144 }
145
146 static void
147 gst_gdp_pay_init (GstGDPPay * gdppay, GstGDPPayClass * g_class)
148 {
149   gdppay->sinkpad =
150       gst_pad_new_from_static_template (&gdp_pay_sink_template, "sink");
151   gst_pad_set_chain_function (gdppay->sinkpad,
152       GST_DEBUG_FUNCPTR (gst_gdp_pay_chain));
153   gst_pad_set_event_function (gdppay->sinkpad,
154       GST_DEBUG_FUNCPTR (gst_gdp_pay_sink_event));
155   gst_element_add_pad (GST_ELEMENT (gdppay), gdppay->sinkpad);
156
157   gdppay->srcpad =
158       gst_pad_new_from_static_template (&gdp_pay_src_template, "src");
159   gst_pad_set_event_function (gdppay->srcpad,
160       GST_DEBUG_FUNCPTR (gst_gdp_pay_src_event));
161   gst_element_add_pad (GST_ELEMENT (gdppay), gdppay->srcpad);
162
163   gdppay->crc_header = DEFAULT_CRC_HEADER;
164   gdppay->crc_payload = DEFAULT_CRC_PAYLOAD;
165   gdppay->header_flag = gdppay->crc_header | gdppay->crc_payload;
166   gdppay->version = DEFAULT_VERSION;
167   gdppay->offset = 0;
168
169   gdppay->packetizer = gst_dp_packetizer_new (gdppay->version);
170 }
171
172 static void
173 gst_gdp_pay_finalize (GObject * gobject)
174 {
175   GstGDPPay *this = GST_GDP_PAY (gobject);
176
177   gst_gdp_pay_reset (this);
178   gst_dp_packetizer_free (this->packetizer);
179
180   GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (gobject));
181 }
182
183 static void
184 gst_gdp_pay_reset (GstGDPPay * this)
185 {
186   GST_DEBUG_OBJECT (this, "Resetting GDP object");
187   /* clear the queued buffers */
188   while (this->queue) {
189     GstBuffer *buffer;
190
191     buffer = GST_BUFFER_CAST (this->queue->data);
192     GST_DEBUG_OBJECT (this, "Pushing queued GDP buffer %p", buffer);
193
194     /* delete buffer from queue now */
195     this->queue = g_list_delete_link (this->queue, this->queue);
196   }
197   if (this->caps) {
198     gst_caps_unref (this->caps);
199     this->caps = NULL;
200   }
201   if (this->caps_buf) {
202     gst_buffer_unref (this->caps_buf);
203     this->caps_buf = NULL;
204   }
205   if (this->tag_buf) {
206     gst_buffer_unref (this->tag_buf);
207     this->tag_buf = NULL;
208   }
209   if (this->new_segment_buf) {
210     gst_buffer_unref (this->new_segment_buf);
211     this->new_segment_buf = NULL;
212   }
213   this->sent_streamheader = FALSE;
214   this->offset = 0;
215 }
216
217 /* set OFFSET and OFFSET_END with running count */
218 static void
219 gst_gdp_stamp_buffer (GstGDPPay * this, GstBuffer * buffer)
220 {
221   GST_BUFFER_OFFSET (buffer) = this->offset;
222   GST_BUFFER_OFFSET_END (buffer) = this->offset + GST_BUFFER_SIZE (buffer);
223   this->offset = GST_BUFFER_OFFSET_END (buffer);
224 }
225
226 static GstBuffer *
227 gst_gdp_buffer_from_caps (GstGDPPay * this, GstCaps * caps)
228 {
229   GstBuffer *headerbuf;
230   GstBuffer *payloadbuf;
231   guint8 *header, *payload;
232   guint len;
233
234   if (!this->packetizer->packet_from_caps (caps, this->header_flag, &len,
235           &header, &payload))
236     goto packet_failed;
237
238   GST_LOG_OBJECT (this, "creating GDP header and payload buffer from caps");
239   headerbuf = gst_buffer_new ();
240   gst_buffer_set_data (headerbuf, header, len);
241   GST_BUFFER_MALLOCDATA (headerbuf) = header;
242
243   payloadbuf = gst_buffer_new ();
244   gst_buffer_set_data (payloadbuf, payload,
245       gst_dp_header_payload_length (header));
246   GST_BUFFER_MALLOCDATA (payloadbuf) = payload;
247
248   return gst_buffer_join (headerbuf, payloadbuf);
249
250   /* ERRORS */
251 packet_failed:
252   {
253     GST_WARNING_OBJECT (this, "could not create GDP header from caps");
254     return NULL;
255   }
256 }
257
258 static GstBuffer *
259 gst_gdp_pay_buffer_from_buffer (GstGDPPay * this, GstBuffer * buffer)
260 {
261   GstBuffer *headerbuf;
262   guint8 *header;
263   guint len;
264
265   if (!this->packetizer->header_from_buffer (buffer, this->header_flag, &len,
266           &header))
267     goto no_buffer;
268
269   GST_LOG_OBJECT (this, "creating GDP header and payload buffer from buffer");
270   headerbuf = gst_buffer_new ();
271   gst_buffer_set_data (headerbuf, header, len);
272   GST_BUFFER_MALLOCDATA (headerbuf) = header;
273
274   /* we do not want to lose the ref on the incoming buffer */
275   gst_buffer_ref (buffer);
276
277   return gst_buffer_join (headerbuf, buffer);
278
279   /* ERRORS */
280 no_buffer:
281   {
282     GST_WARNING_OBJECT (this, "could not create GDP header from buffer");
283     return NULL;
284   }
285 }
286
287 static GstBuffer *
288 gst_gdp_buffer_from_event (GstGDPPay * this, GstEvent * event)
289 {
290   GstBuffer *headerbuf;
291   GstBuffer *payloadbuf;
292   guint8 *header, *payload;
293   guint len;
294   gboolean ret;
295
296   ret =
297       this->packetizer->packet_from_event (event, this->header_flag, &len,
298       &header, &payload);
299   if (!ret)
300     goto no_event;
301
302   GST_LOG_OBJECT (this, "creating GDP header and payload buffer from event");
303   headerbuf = gst_buffer_new ();
304   gst_buffer_set_data (headerbuf, header, len);
305   GST_BUFFER_MALLOCDATA (headerbuf) = header;
306
307   payloadbuf = gst_buffer_new ();
308   gst_buffer_set_data (payloadbuf, payload,
309       gst_dp_header_payload_length (header));
310   GST_BUFFER_MALLOCDATA (payloadbuf) = payload;
311
312   return gst_buffer_join (headerbuf, payloadbuf);
313
314   /* ERRORS */
315 no_event:
316   {
317     GST_WARNING_OBJECT (this, "could not create GDP header from event %s (%d)",
318         gst_event_type_get_name (event->type), event->type);
319     return NULL;
320   }
321 }
322
323
324 /* set our caps with streamheader, based on the latest newsegment and caps,
325  * and (possibly) GDP-serialized buffers of the streamheaders on the src pad */
326 static GstFlowReturn
327 gst_gdp_pay_reset_streamheader (GstGDPPay * this)
328 {
329   GstCaps *caps;
330
331   /* We use copies of these to avoid circular refcounts */
332   GstBuffer *new_segment_buf, *caps_buf, *tag_buf;
333   GstStructure *structure;
334   GstFlowReturn r = GST_FLOW_OK;
335   gboolean version_one_zero = TRUE;
336
337   GValue array = { 0 };
338   GValue value = { 0 };
339
340   GST_DEBUG_OBJECT (this, "start");
341   /* In version 0.2, we didn't need or send new segment or tags */
342   if (this->version == GST_DP_VERSION_0_2)
343     version_one_zero = FALSE;
344
345   if (version_one_zero) {
346     if (!this->new_segment_buf || !this->caps_buf) {
347       GST_DEBUG_OBJECT (this, "1.0, missing new_segment or caps, returning");
348       return GST_FLOW_OK;
349     }
350   } else {
351     if (!this->caps_buf) {
352       GST_DEBUG_OBJECT (this, "0.2, missing caps, returning");
353       return GST_FLOW_OK;
354     }
355   }
356
357   /* put copies of the buffers in a fixed list */
358   g_value_init (&array, GST_TYPE_ARRAY);
359
360   if (version_one_zero) {
361     GST_DEBUG_OBJECT (this, "1.0, appending copy of new segment buffer %p",
362         this->new_segment_buf);
363     new_segment_buf = gst_buffer_copy (this->new_segment_buf);
364     g_value_init (&value, GST_TYPE_BUFFER);
365     gst_value_set_buffer (&value, new_segment_buf);
366     gst_value_array_append_value (&array, &value);
367     g_value_unset (&value);
368     gst_buffer_unref (new_segment_buf);
369
370     if (this->tag_buf) {
371       GST_DEBUG_OBJECT (this, "1.0, appending copy of tag buffer %p",
372           this->tag_buf);
373       tag_buf = gst_buffer_copy (this->tag_buf);
374       g_value_init (&value, GST_TYPE_BUFFER);
375       gst_value_set_buffer (&value, tag_buf);
376       gst_value_array_append_value (&array, &value);
377       g_value_unset (&value);
378       gst_buffer_unref (tag_buf);
379     }
380   }
381
382   GST_DEBUG_OBJECT (this, "appending copy of caps buffer %p", this->caps_buf);
383   caps_buf = gst_buffer_copy (this->caps_buf);
384   g_value_init (&value, GST_TYPE_BUFFER);
385   gst_value_set_buffer (&value, caps_buf);
386   gst_value_array_append_value (&array, &value);
387   g_value_unset (&value);
388   gst_buffer_unref (caps_buf);
389
390   /* we also need to add GDP serializations of the streamheaders of the
391    * incoming caps */
392   structure = gst_caps_get_structure (this->caps, 0);
393   if (gst_structure_has_field (structure, "streamheader")) {
394     const GValue *sh;
395     GArray *buffers;
396     GstBuffer *buffer;
397     int i;
398
399     sh = gst_structure_get_value (structure, "streamheader");
400     buffers = g_value_peek_pointer (sh);
401     GST_DEBUG_OBJECT (this,
402         "Need to serialize %d incoming streamheader buffers on ours",
403         buffers->len);
404     for (i = 0; i < buffers->len; ++i) {
405       GValue *bufval;
406       GstBuffer *outbuffer;
407
408       bufval = &g_array_index (buffers, GValue, i);
409       buffer = g_value_peek_pointer (bufval);
410       outbuffer = gst_gdp_pay_buffer_from_buffer (this, buffer);
411       if (!outbuffer) {
412         g_value_unset (&array);
413         goto no_buffer;
414       }
415
416       g_value_init (&value, GST_TYPE_BUFFER);
417       gst_value_set_buffer (&value, outbuffer);
418       gst_value_array_append_value (&array, &value);
419       g_value_unset (&value);
420
421       gst_buffer_unref (outbuffer);
422     }
423   } else {
424     GST_DEBUG_OBJECT (this, "no streamheader to serialize");
425   }
426
427   GST_DEBUG_OBJECT (this, "%d serialized buffers on streamheaders",
428       gst_value_array_get_size (&array));
429   caps = gst_caps_from_string ("application/x-gdp");
430   structure = gst_caps_get_structure (caps, 0);
431
432   gst_structure_set_value (structure, "streamheader", &array);
433   g_value_unset (&array);
434
435   GST_DEBUG_OBJECT (this, "Setting caps on src pad %" GST_PTR_FORMAT, caps);
436   gst_pad_set_caps (this->srcpad, caps);
437   gst_buffer_set_caps (this->caps_buf, caps);
438   gst_buffer_set_caps (this->new_segment_buf, caps);
439
440   /* if these are our first ever buffers, send out new_segment first */
441   if (!this->sent_streamheader) {
442     GstEvent *event =
443         gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_BYTES, 0, -1, 0);
444     GST_DEBUG_OBJECT (this, "Sending out new_segment event %p", event);
445     if (!gst_pad_push_event (this->srcpad, event)) {
446       GST_WARNING_OBJECT (this, "pushing new segment failed");
447       r = GST_FLOW_ERROR;
448       goto done;
449     }
450   }
451
452   /* push out these streamheader buffers, then flush our internal queue */
453   GST_DEBUG_OBJECT (this, "Pushing GDP new_segment buffer %p with offset %"
454       G_GINT64_FORMAT ", offset_end %" G_GINT64_FORMAT, this->new_segment_buf,
455       GST_BUFFER_OFFSET (this->new_segment_buf),
456       GST_BUFFER_OFFSET_END (this->new_segment_buf));
457   /* we stored these bufs with refcount 1, so make sure we keep a ref */
458   r = gst_pad_push (this->srcpad, gst_buffer_ref (this->new_segment_buf));
459   if (r != GST_FLOW_OK) {
460     GST_WARNING_OBJECT (this, "pushing GDP newsegment buffer returned %d", r);
461     goto done;
462   }
463   if (this->tag_buf) {
464     GST_DEBUG_OBJECT (this, "Pushing GDP tag buffer %p", this->tag_buf);
465     /* we stored these bufs with refcount 1, so make sure we keep a ref */
466     r = gst_pad_push (this->srcpad, gst_buffer_ref (this->tag_buf));
467     if (r != GST_FLOW_OK) {
468       GST_WARNING_OBJECT (this, "pushing GDP tag buffer returned %d", r);
469       goto done;
470     }
471   }
472   GST_DEBUG_OBJECT (this, "Pushing GDP caps buffer %p", this->caps_buf);
473   r = gst_pad_push (this->srcpad, gst_buffer_ref (this->caps_buf));
474   if (r != GST_FLOW_OK) {
475     GST_WARNING_OBJECT (this, "pushing GDP caps buffer returned %d", r);
476     goto done;
477   }
478   this->sent_streamheader = TRUE;
479   GST_DEBUG_OBJECT (this, "need to push %d queued buffers",
480       g_list_length (this->queue));
481   while (this->queue) {
482     GstBuffer *buffer;
483
484     buffer = GST_BUFFER_CAST (this->queue->data);
485     GST_DEBUG_OBJECT (this, "Pushing queued GDP buffer %p", buffer);
486
487     /* delete buffer from queue now */
488     this->queue = g_list_delete_link (this->queue, this->queue);
489
490     /* set caps and push */
491     gst_buffer_set_caps (buffer, caps);
492     r = gst_pad_push (this->srcpad, buffer);
493     if (r != GST_FLOW_OK) {
494       GST_WARNING_OBJECT (this, "pushing queued GDP buffer returned %d", r);
495       goto done;
496     }
497   }
498
499 done:
500   gst_caps_unref (caps);
501   GST_DEBUG_OBJECT (this, "stop");
502   return r;
503
504   /* ERRORS */
505 no_buffer:
506   {
507     GST_ELEMENT_ERROR (this, STREAM, FORMAT, (NULL),
508         ("failed to create GDP buffer from streamheader"));
509     return GST_FLOW_ERROR;
510   }
511 }
512
513 /* queue a buffer internally if we haven't sent streamheader buffers yet;
514  * otherwise, just push on, this takes ownership of the buffer. */
515 static GstFlowReturn
516 gst_gdp_queue_buffer (GstGDPPay * this, GstBuffer * buffer)
517 {
518   if (this->sent_streamheader) {
519     GST_LOG_OBJECT (this, "Pushing GDP buffer %p, caps %" GST_PTR_FORMAT,
520         buffer, this->caps);
521     return gst_pad_push (this->srcpad, buffer);
522   }
523
524   /* store it on an internal queue. buffer remains reffed. */
525   this->queue = g_list_append (this->queue, buffer);
526   GST_DEBUG_OBJECT (this, "streamheader not sent yet, "
527       "queued buffer %p, now %d buffers queued",
528       buffer, g_list_length (this->queue));
529
530   return GST_FLOW_OK;
531 }
532
533 static GstFlowReturn
534 gst_gdp_pay_chain (GstPad * pad, GstBuffer * buffer)
535 {
536   GstGDPPay *this;
537   GstCaps *caps;
538   GstBuffer *outbuffer;
539   GstFlowReturn ret;
540
541   this = GST_GDP_PAY (gst_pad_get_parent (pad));
542
543   /* we should have received a new_segment before, otherwise it's a bug.
544    * fake one in that case */
545   if (!this->new_segment_buf) {
546     GstEvent *event;
547
548     GST_WARNING_OBJECT (this,
549         "did not receive new-segment before first buffer");
550     event = gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_BYTES, 0, -1, 0);
551     outbuffer = gst_gdp_buffer_from_event (this, event);
552     gst_event_unref (event);
553
554     /* GDP 0.2 doesn't know about new-segment, so this is not fatal */
555     if (!outbuffer) {
556       GST_ELEMENT_WARNING (this, STREAM, ENCODE, (NULL),
557           ("Could not create GDP buffer from new segment event"));
558     } else {
559       gst_gdp_stamp_buffer (this, outbuffer);
560       GST_BUFFER_TIMESTAMP (outbuffer) = GST_BUFFER_TIMESTAMP (buffer);
561       GST_BUFFER_DURATION (outbuffer) = 0;
562       GST_DEBUG_OBJECT (this, "Storing buffer %p as new_segment_buf",
563           outbuffer);
564       this->new_segment_buf = outbuffer;
565     }
566   }
567
568   /* make sure we've received caps before */
569   caps = gst_buffer_get_caps (buffer);
570   if (!this->caps && !caps)
571     goto no_caps;
572
573   /* if the caps have changed, process caps first */
574   if (caps && !gst_caps_is_equal (this->caps, caps)) {
575     GST_LOG_OBJECT (this, "caps changed to %p, %" GST_PTR_FORMAT, caps, caps);
576     gst_caps_replace (&(this->caps), caps);
577     outbuffer = gst_gdp_buffer_from_caps (this, caps);
578     if (!outbuffer)
579       goto no_caps_buffer;
580
581     gst_gdp_stamp_buffer (this, outbuffer);
582     GST_BUFFER_TIMESTAMP (outbuffer) = GST_BUFFER_TIMESTAMP (buffer);
583     GST_BUFFER_DURATION (outbuffer) = 0;
584     GST_BUFFER_FLAG_SET (outbuffer, GST_BUFFER_FLAG_IN_CAPS);
585     this->caps_buf = outbuffer;
586     gst_gdp_pay_reset_streamheader (this);
587   }
588
589   if (caps)
590     gst_caps_unref (caps);
591
592   /* create a GDP header packet,
593    * then create a GST buffer of the header packet and the buffer contents */
594   outbuffer = gst_gdp_pay_buffer_from_buffer (this, buffer);
595   if (!outbuffer)
596     goto no_buffer;
597
598   gst_gdp_stamp_buffer (this, outbuffer);
599   GST_BUFFER_TIMESTAMP (outbuffer) = GST_BUFFER_TIMESTAMP (buffer);
600   GST_BUFFER_DURATION (outbuffer) = GST_BUFFER_DURATION (buffer);
601
602   ret = gst_gdp_queue_buffer (this, outbuffer);
603
604 done:
605   gst_buffer_unref (buffer);
606   gst_object_unref (this);
607   return ret;
608
609   /* ERRORS */
610 no_caps:
611   {
612     /* when returning a fatal error as a GstFlowReturn we must post an error
613      * message */
614     GST_ELEMENT_ERROR (this, STREAM, FORMAT, (NULL),
615         ("first received buffer does not have caps set"));
616     if (caps)
617       gst_caps_unref (caps);
618     ret = GST_FLOW_NOT_NEGOTIATED;
619     goto done;
620   }
621 no_caps_buffer:
622   {
623     GST_ELEMENT_ERROR (this, STREAM, ENCODE, (NULL),
624         ("Could not create GDP buffer from caps %" GST_PTR_FORMAT, caps));
625     gst_caps_unref (caps);
626     ret = GST_FLOW_ERROR;
627     goto done;
628   }
629 no_buffer:
630   {
631     GST_ELEMENT_ERROR (this, STREAM, ENCODE, (NULL),
632         ("Could not create GDP buffer from buffer"));
633     ret = GST_FLOW_ERROR;
634     goto done;
635   }
636 }
637
638 static gboolean
639 gst_gdp_pay_sink_event (GstPad * pad, GstEvent * event)
640 {
641   GstBuffer *outbuffer;
642   GstGDPPay *this = GST_GDP_PAY (gst_pad_get_parent (pad));
643   GstFlowReturn flowret;
644   gboolean ret = TRUE;
645
646   GST_DEBUG_OBJECT (this, "received event %p of type %s (%d)",
647       event, gst_event_type_get_name (event->type), event->type);
648
649   /* now turn the event into a buffer */
650   outbuffer = gst_gdp_buffer_from_event (this, event);
651   if (!outbuffer)
652     goto no_outbuffer;
653
654   gst_gdp_stamp_buffer (this, outbuffer);
655   GST_BUFFER_TIMESTAMP (outbuffer) = GST_EVENT_TIMESTAMP (event);
656   GST_BUFFER_DURATION (outbuffer) = 0;
657
658   /* if we got a new segment, we should put it on our streamheader,
659    * and not send it on */
660   switch (GST_EVENT_TYPE (event)) {
661     case GST_EVENT_NEWSEGMENT:
662       GST_DEBUG_OBJECT (this, "Storing buffer %p as new_segment_buf",
663           outbuffer);
664
665       if (this->new_segment_buf)
666         gst_buffer_unref (this->new_segment_buf);
667       this->new_segment_buf = outbuffer;
668
669       gst_gdp_pay_reset_streamheader (this);
670       break;
671     case GST_EVENT_TAG:
672       GST_DEBUG_OBJECT (this, "Storing buffer %p as tag_buf", outbuffer);
673
674       if (this->tag_buf)
675         gst_buffer_unref (this->tag_buf);
676       this->tag_buf = outbuffer;
677
678       gst_gdp_pay_reset_streamheader (this);
679       break;
680     default:
681       GST_DEBUG_OBJECT (this, "queuing GDP buffer %p of event %p", outbuffer,
682           event);
683       flowret = gst_gdp_queue_buffer (this, outbuffer);
684       if (flowret != GST_FLOW_OK)
685         goto push_error;
686       break;
687   }
688
689   /* if we have EOS, we should send on EOS ourselves */
690   if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
691     GST_DEBUG_OBJECT (this, "Sending on EOS event %p", event);
692     /* ref, we unref later again */
693     ret = gst_pad_push_event (this->srcpad, gst_event_ref (event));
694   }
695
696 done:
697   gst_event_unref (event);
698   gst_object_unref (this);
699
700   return ret;
701
702   /* ERRORS */
703 no_outbuffer:
704   {
705     GST_ELEMENT_WARNING (this, STREAM, ENCODE, (NULL),
706         ("Could not create GDP buffer from received event (type %s)",
707             gst_event_type_get_name (event->type)));
708     ret = FALSE;
709     goto done;
710   }
711 push_error:
712   {
713     GST_WARNING_OBJECT (this, "queueing GDP event buffer returned %d", flowret);
714     ret = FALSE;
715     goto done;
716   }
717 }
718
719 static gboolean
720 gst_gdp_pay_src_event (GstPad * pad, GstEvent * event)
721 {
722   GstGDPPay *this;
723   gboolean res = TRUE;
724
725   this = GST_GDP_PAY (gst_pad_get_parent (pad));
726
727   switch (GST_EVENT_TYPE (event)) {
728     case GST_EVENT_SEEK:
729       /* we refuse seek for now. */
730       gst_event_unref (event);
731       res = FALSE;
732       break;
733     case GST_EVENT_QOS:
734     case GST_EVENT_NAVIGATION:
735     default:
736       /* everything else is passed */
737       res = gst_pad_push_event (this->sinkpad, event);
738       break;
739   }
740   gst_object_unref (this);
741
742   return res;
743 }
744
745 static void
746 gst_gdp_pay_set_property (GObject * object, guint prop_id,
747     const GValue * value, GParamSpec * pspec)
748 {
749   GstGDPPay *this;
750
751   g_return_if_fail (GST_IS_GDP_PAY (object));
752   this = GST_GDP_PAY (object);
753
754   switch (prop_id) {
755     case PROP_CRC_HEADER:
756       this->crc_header =
757           g_value_get_boolean (value) ? GST_DP_HEADER_FLAG_CRC_HEADER : 0;
758       this->header_flag = this->crc_header | this->crc_payload;
759       break;
760     case PROP_CRC_PAYLOAD:
761       this->crc_payload =
762           g_value_get_boolean (value) ? GST_DP_HEADER_FLAG_CRC_PAYLOAD : 0;
763       this->header_flag = this->crc_header | this->crc_payload;
764       break;
765     case PROP_VERSION:
766       this->version = g_value_get_enum (value);
767       break;
768     default:
769       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
770       break;
771   }
772 }
773
774 static void
775 gst_gdp_pay_get_property (GObject * object, guint prop_id,
776     GValue * value, GParamSpec * pspec)
777 {
778   GstGDPPay *this;
779
780   g_return_if_fail (GST_IS_GDP_PAY (object));
781   this = GST_GDP_PAY (object);
782
783   switch (prop_id) {
784     case PROP_CRC_HEADER:
785       g_value_set_boolean (value, this->crc_header);
786       break;
787     case PROP_CRC_PAYLOAD:
788       g_value_set_boolean (value, this->crc_payload);
789       break;
790     case PROP_VERSION:
791       g_value_set_enum (value, this->version);
792       break;
793     default:
794       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
795       break;
796   }
797 }
798
799 static GstStateChangeReturn
800 gst_gdp_pay_change_state (GstElement * element, GstStateChange transition)
801 {
802   GstStateChangeReturn ret;
803   GstGDPPay *this = GST_GDP_PAY (element);
804
805   switch (transition) {
806     case GST_STATE_CHANGE_READY_TO_PAUSED:
807       break;
808     default:
809       break;
810   }
811
812   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
813
814   switch (transition) {
815     case GST_STATE_CHANGE_PAUSED_TO_READY:
816       gst_gdp_pay_reset (this);
817       break;
818     default:
819       break;
820   }
821
822   return ret;
823 }
824
825 gboolean
826 gst_gdp_pay_plugin_init (GstPlugin * plugin)
827 {
828   if (!gst_element_register (plugin, "gdppay", GST_RANK_NONE, GST_TYPE_GDP_PAY))
829     return FALSE;
830
831   return TRUE;
832 }