upload tizen1.0 source
[framework/multimedia/gst-plugins-good0.10.git] / gst / rtp / gstrtpmp4vpay.c
1 /* GStreamer
2  * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com>
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 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include <string.h>
25
26 #include <gst/rtp/gstrtpbuffer.h>
27
28 #include "gstrtpmp4vpay.h"
29
30 GST_DEBUG_CATEGORY_STATIC (rtpmp4vpay_debug);
31 #define GST_CAT_DEFAULT (rtpmp4vpay_debug)
32
33 static GstStaticPadTemplate gst_rtp_mp4v_pay_sink_template =
34     GST_STATIC_PAD_TEMPLATE ("sink",
35     GST_PAD_SINK,
36     GST_PAD_ALWAYS,
37     GST_STATIC_CAPS ("video/mpeg,"
38         "mpegversion=(int) 4," "systemstream=(boolean)false;" "video/x-xvid")
39     );
40
41 static GstStaticPadTemplate gst_rtp_mp4v_pay_src_template =
42 GST_STATIC_PAD_TEMPLATE ("src",
43     GST_PAD_SRC,
44     GST_PAD_ALWAYS,
45     GST_STATIC_CAPS ("application/x-rtp, "
46         "media = (string) \"video\", "
47         "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
48         "clock-rate = (int) [1, MAX ], " "encoding-name = (string) \"MP4V-ES\""
49         /* two string params
50          *
51          "profile-level-id = (string) [1,MAX]"
52          "config = (string) [1,MAX]"
53          */
54     )
55     );
56
57 #define DEFAULT_SEND_CONFIG     FALSE
58 #define DEFAULT_BUFFER_LIST     FALSE
59 #define DEFAULT_CONFIG_INTERVAL 0
60
61 enum
62 {
63   ARG_0,
64   ARG_SEND_CONFIG,
65   ARG_BUFFER_LIST,
66   ARG_CONFIG_INTERVAL
67 };
68
69
70 static void gst_rtp_mp4v_pay_finalize (GObject * object);
71
72 static void gst_rtp_mp4v_pay_set_property (GObject * object, guint prop_id,
73     const GValue * value, GParamSpec * pspec);
74 static void gst_rtp_mp4v_pay_get_property (GObject * object, guint prop_id,
75     GValue * value, GParamSpec * pspec);
76
77 static gboolean gst_rtp_mp4v_pay_setcaps (GstBaseRTPPayload * payload,
78     GstCaps * caps);
79 static GstFlowReturn gst_rtp_mp4v_pay_handle_buffer (GstBaseRTPPayload *
80     payload, GstBuffer * buffer);
81 static gboolean gst_rtp_mp4v_pay_handle_event (GstPad * pad, GstEvent * event);
82
83 GST_BOILERPLATE (GstRtpMP4VPay, gst_rtp_mp4v_pay, GstBaseRTPPayload,
84     GST_TYPE_BASE_RTP_PAYLOAD)
85
86      static void gst_rtp_mp4v_pay_base_init (gpointer klass)
87 {
88   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
89
90   gst_element_class_add_pad_template (element_class,
91       gst_static_pad_template_get (&gst_rtp_mp4v_pay_src_template));
92   gst_element_class_add_pad_template (element_class,
93       gst_static_pad_template_get (&gst_rtp_mp4v_pay_sink_template));
94
95   gst_element_class_set_details_simple (element_class,
96       "RTP MPEG4 Video payloader", "Codec/Payloader/Network/RTP",
97       "Payload MPEG-4 video as RTP packets (RFC 3016)",
98       "Wim Taymans <wim.taymans@gmail.com>");
99 }
100
101 static void
102 gst_rtp_mp4v_pay_class_init (GstRtpMP4VPayClass * klass)
103 {
104   GObjectClass *gobject_class;
105   GstBaseRTPPayloadClass *gstbasertppayload_class;
106
107   gobject_class = (GObjectClass *) klass;
108   gstbasertppayload_class = (GstBaseRTPPayloadClass *) klass;
109
110   gobject_class->set_property = gst_rtp_mp4v_pay_set_property;
111   gobject_class->get_property = gst_rtp_mp4v_pay_get_property;
112
113   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SEND_CONFIG,
114       g_param_spec_boolean ("send-config", "Send Config",
115           "Send the config parameters in RTP packets as well",
116           DEFAULT_SEND_CONFIG, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
117
118   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BUFFER_LIST,
119       g_param_spec_boolean ("buffer-list", "Buffer Array",
120           "Use Buffer Arrays",
121           DEFAULT_BUFFER_LIST, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
122
123   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_CONFIG_INTERVAL,
124       g_param_spec_uint ("config-interval", "Config Send Interval",
125           "Send Config Insertion Interval in seconds (configuration headers "
126           "will be multiplexed in the data stream when detected.) (0 = disabled)",
127           0, 3600, DEFAULT_CONFIG_INTERVAL,
128           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
129       );
130
131   gobject_class->finalize = gst_rtp_mp4v_pay_finalize;
132
133   gstbasertppayload_class->set_caps = gst_rtp_mp4v_pay_setcaps;
134   gstbasertppayload_class->handle_buffer = gst_rtp_mp4v_pay_handle_buffer;
135   gstbasertppayload_class->handle_event = gst_rtp_mp4v_pay_handle_event;
136
137   GST_DEBUG_CATEGORY_INIT (rtpmp4vpay_debug, "rtpmp4vpay", 0,
138       "MP4 video RTP Payloader");
139 }
140
141 static void
142 gst_rtp_mp4v_pay_init (GstRtpMP4VPay * rtpmp4vpay, GstRtpMP4VPayClass * klass)
143 {
144   rtpmp4vpay->adapter = gst_adapter_new ();
145   rtpmp4vpay->rate = 90000;
146   rtpmp4vpay->profile = 1;
147   rtpmp4vpay->buffer_list = DEFAULT_BUFFER_LIST;
148   rtpmp4vpay->send_config = DEFAULT_SEND_CONFIG;
149   rtpmp4vpay->need_config = TRUE;
150   rtpmp4vpay->config_interval = DEFAULT_CONFIG_INTERVAL;
151   rtpmp4vpay->last_config = -1;
152
153   rtpmp4vpay->config = NULL;
154 }
155
156 static void
157 gst_rtp_mp4v_pay_finalize (GObject * object)
158 {
159   GstRtpMP4VPay *rtpmp4vpay;
160
161   rtpmp4vpay = GST_RTP_MP4V_PAY (object);
162
163   if (rtpmp4vpay->config) {
164     gst_buffer_unref (rtpmp4vpay->config);
165     rtpmp4vpay->config = NULL;
166   }
167   g_object_unref (rtpmp4vpay->adapter);
168   rtpmp4vpay->adapter = NULL;
169
170   G_OBJECT_CLASS (parent_class)->finalize (object);
171 }
172
173 static gboolean
174 gst_rtp_mp4v_pay_new_caps (GstRtpMP4VPay * rtpmp4vpay)
175 {
176   gchar *profile, *config;
177   GValue v = { 0 };
178   gboolean res;
179
180   profile = g_strdup_printf ("%d", rtpmp4vpay->profile);
181   g_value_init (&v, GST_TYPE_BUFFER);
182   gst_value_set_buffer (&v, rtpmp4vpay->config);
183   config = gst_value_serialize (&v);
184
185   res = gst_basertppayload_set_outcaps (GST_BASE_RTP_PAYLOAD (rtpmp4vpay),
186       "profile-level-id", G_TYPE_STRING, profile,
187       "config", G_TYPE_STRING, config, NULL);
188
189   g_value_unset (&v);
190
191   g_free (profile);
192   g_free (config);
193
194   return res;
195 }
196
197 static gboolean
198 gst_rtp_mp4v_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps)
199 {
200   GstRtpMP4VPay *rtpmp4vpay;
201   GstStructure *structure;
202   const GValue *codec_data;
203   gboolean res;
204
205   rtpmp4vpay = GST_RTP_MP4V_PAY (payload);
206
207   gst_basertppayload_set_options (payload, "video", TRUE, "MP4V-ES",
208       rtpmp4vpay->rate);
209
210   res = TRUE;
211
212   structure = gst_caps_get_structure (caps, 0);
213   codec_data = gst_structure_get_value (structure, "codec_data");
214   if (codec_data) {
215     GST_LOG_OBJECT (rtpmp4vpay, "got codec_data");
216     if (G_VALUE_TYPE (codec_data) == GST_TYPE_BUFFER) {
217       GstBuffer *buffer;
218       guint8 *data;
219       guint size;
220
221       buffer = gst_value_get_buffer (codec_data);
222
223       data = GST_BUFFER_DATA (buffer);
224       size = GST_BUFFER_SIZE (buffer);
225
226       if (size < 5)
227         goto done;
228
229       rtpmp4vpay->profile = data[4];
230       GST_LOG_OBJECT (rtpmp4vpay, "configuring codec_data, profile %d",
231           data[4]);
232
233       if (rtpmp4vpay->config)
234         gst_buffer_unref (rtpmp4vpay->config);
235       rtpmp4vpay->config = gst_buffer_copy (buffer);
236       res = gst_rtp_mp4v_pay_new_caps (rtpmp4vpay);
237     }
238   }
239
240 done:
241   return res;
242 }
243
244 static void
245 gst_rtp_mp4v_pay_empty (GstRtpMP4VPay * rtpmp4vpay)
246 {
247   gst_adapter_clear (rtpmp4vpay->adapter);
248 }
249
250 static GstFlowReturn
251 gst_rtp_mp4v_pay_flush (GstRtpMP4VPay * rtpmp4vpay)
252 {
253   guint avail;
254   GstBuffer *outbuf;
255   GstBuffer *outbuf_data = NULL;
256   GstFlowReturn ret;
257   GstBufferList *list = NULL;
258   GstBufferListIterator *it = NULL;
259
260   /* the data available in the adapter is either smaller
261    * than the MTU or bigger. In the case it is smaller, the complete
262    * adapter contents can be put in one packet. In the case the
263    * adapter has more than one MTU, we need to split the MP4V data
264    * over multiple packets. */
265   avail = gst_adapter_available (rtpmp4vpay->adapter);
266
267   if (rtpmp4vpay->config == NULL && rtpmp4vpay->need_config) {
268     /* when we don't have a config yet, flush things out */
269     gst_adapter_flush (rtpmp4vpay->adapter, avail);
270     avail = 0;
271   }
272
273   if (!avail)
274     return GST_FLOW_OK;
275
276   ret = GST_FLOW_OK;
277
278   if (rtpmp4vpay->buffer_list) {
279     /* Use buffer lists. Each frame will be put into a list
280      * of buffers and the whole list will be pushed downstream
281      * at once */
282     list = gst_buffer_list_new ();
283     it = gst_buffer_list_iterate (list);
284   }
285
286   while (avail > 0) {
287     guint towrite;
288     guint8 *payload;
289     guint payload_len;
290     guint packet_len;
291
292     /* this will be the total lenght of the packet */
293     packet_len = gst_rtp_buffer_calc_packet_len (avail, 0, 0);
294
295     /* fill one MTU or all available bytes */
296     towrite = MIN (packet_len, GST_BASE_RTP_PAYLOAD_MTU (rtpmp4vpay));
297
298     /* this is the payload length */
299     payload_len = gst_rtp_buffer_calc_payload_len (towrite, 0, 0);
300
301     if (rtpmp4vpay->buffer_list) {
302       /* create buffer without payload. The payload will be put
303        * in next buffer instead. Both buffers will be then added
304        * to the list */
305       outbuf = gst_rtp_buffer_new_allocate (0, 0, 0);
306
307       /* Take buffer with the payload from the adapter */
308       outbuf_data = gst_adapter_take_buffer (rtpmp4vpay->adapter, payload_len);
309     } else {
310       /* create buffer to hold the payload */
311       outbuf = gst_rtp_buffer_new_allocate (payload_len, 0, 0);
312
313       /* copy payload */
314       payload = gst_rtp_buffer_get_payload (outbuf);
315
316       gst_adapter_copy (rtpmp4vpay->adapter, payload, 0, payload_len);
317       gst_adapter_flush (rtpmp4vpay->adapter, payload_len);
318     }
319
320     avail -= payload_len;
321
322     gst_rtp_buffer_set_marker (outbuf, avail == 0);
323
324     GST_BUFFER_TIMESTAMP (outbuf) = rtpmp4vpay->first_timestamp;
325
326     if (rtpmp4vpay->buffer_list) {
327       /* create a new group to hold the rtp header and the payload */
328       gst_buffer_list_iterator_add_group (it);
329       gst_buffer_list_iterator_add (it, outbuf);
330       gst_buffer_list_iterator_add (it, outbuf_data);
331     } else {
332       ret = gst_basertppayload_push (GST_BASE_RTP_PAYLOAD (rtpmp4vpay), outbuf);
333     }
334   }
335
336   if (rtpmp4vpay->buffer_list) {
337     gst_buffer_list_iterator_free (it);
338     /* push the whole buffer list at once */
339     ret =
340         gst_basertppayload_push_list (GST_BASE_RTP_PAYLOAD (rtpmp4vpay), list);
341   }
342
343   return ret;
344 }
345
346 #define VOS_STARTCODE                   0x000001B0
347 #define VOS_ENDCODE                     0x000001B1
348 #define USER_DATA_STARTCODE             0x000001B2
349 #define GOP_STARTCODE                   0x000001B3
350 #define VISUAL_OBJECT_STARTCODE         0x000001B5
351 #define VOP_STARTCODE                   0x000001B6
352
353 static gboolean
354 gst_rtp_mp4v_pay_depay_data (GstRtpMP4VPay * enc, guint8 * data, guint size,
355     gint * strip, gboolean * vopi)
356 {
357   guint32 code;
358   gboolean result;
359   *vopi = FALSE;
360
361   *strip = 0;
362
363   if (size < 5)
364     return FALSE;
365
366   code = GST_READ_UINT32_BE (data);
367   GST_DEBUG_OBJECT (enc, "start code 0x%08x", code);
368
369   switch (code) {
370     case VOS_STARTCODE:
371     case 0x00000101:
372     {
373       gint i;
374       guint8 profile;
375       gboolean newprofile = FALSE;
376       gboolean equal;
377
378       if (code == VOS_STARTCODE) {
379         /* profile_and_level_indication */
380         profile = data[4];
381
382         GST_DEBUG_OBJECT (enc, "VOS profile 0x%08x", profile);
383
384         if (profile != enc->profile) {
385           newprofile = TRUE;
386           enc->profile = profile;
387         }
388       }
389
390       /* up to the next GOP_STARTCODE or VOP_STARTCODE is
391        * the config information */
392       code = 0xffffffff;
393       for (i = 5; i < size - 4; i++) {
394         code = (code << 8) | data[i];
395         if (code == GOP_STARTCODE || code == VOP_STARTCODE)
396           break;
397       }
398       i -= 3;
399       /* see if config changed */
400       equal = FALSE;
401       if (enc->config) {
402         if (GST_BUFFER_SIZE (enc->config) == i) {
403           equal = memcmp (GST_BUFFER_DATA (enc->config), data, i) == 0;
404         }
405       }
406       /* if config string changed or new profile, make new caps */
407       if (!equal || newprofile) {
408         if (enc->config)
409           gst_buffer_unref (enc->config);
410         enc->config = gst_buffer_new_and_alloc (i);
411         memcpy (GST_BUFFER_DATA (enc->config), data, i);
412         gst_rtp_mp4v_pay_new_caps (enc);
413       }
414       *strip = i;
415       /* we need to flush out the current packet. */
416       result = TRUE;
417       break;
418     }
419     case VOP_STARTCODE:
420       GST_DEBUG_OBJECT (enc, "VOP");
421       /* VOP startcode, we don't have to flush the packet */
422       result = FALSE;
423       /* vop-coding-type == I-frame */
424       if (size > 4 && (data[4] >> 6 == 0)) {
425         GST_DEBUG_OBJECT (enc, "VOP-I");
426         *vopi = TRUE;
427       }
428       break;
429     case GOP_STARTCODE:
430       GST_DEBUG_OBJECT (enc, "GOP");
431       *vopi = TRUE;
432       result = TRUE;
433       break;
434     case 0x00000100:
435       enc->need_config = FALSE;
436       result = TRUE;
437       break;
438     default:
439       if (code >= 0x20 && code <= 0x2f) {
440         GST_DEBUG_OBJECT (enc, "short header");
441         result = FALSE;
442       } else {
443         GST_DEBUG_OBJECT (enc, "other startcode");
444         /* all other startcodes need a flush */
445         result = TRUE;
446       }
447       break;
448   }
449   return result;
450 }
451
452 /* we expect buffers starting on startcodes. 
453  */
454 static GstFlowReturn
455 gst_rtp_mp4v_pay_handle_buffer (GstBaseRTPPayload * basepayload,
456     GstBuffer * buffer)
457 {
458   GstRtpMP4VPay *rtpmp4vpay;
459   GstFlowReturn ret;
460   guint size, avail;
461   guint packet_len;
462   guint8 *data;
463   gboolean flush;
464   gint strip;
465   GstClockTime timestamp, duration;
466   gboolean vopi;
467   gboolean send_config;
468
469   ret = GST_FLOW_OK;
470   send_config = FALSE;
471
472   rtpmp4vpay = GST_RTP_MP4V_PAY (basepayload);
473
474   size = GST_BUFFER_SIZE (buffer);
475   data = GST_BUFFER_DATA (buffer);
476   timestamp = GST_BUFFER_TIMESTAMP (buffer);
477   duration = GST_BUFFER_DURATION (buffer);
478   avail = gst_adapter_available (rtpmp4vpay->adapter);
479
480   if (duration == -1)
481     duration = 0;
482
483   /* empty buffer, take timestamp */
484   if (avail == 0) {
485     rtpmp4vpay->first_timestamp = timestamp;
486     rtpmp4vpay->duration = 0;
487   }
488
489   /* depay incomming data and see if we need to start a new RTP
490    * packet */
491   flush = gst_rtp_mp4v_pay_depay_data (rtpmp4vpay, data, size, &strip, &vopi);
492   if (strip) {
493     /* strip off config if requested */
494     if (!rtpmp4vpay->send_config) {
495       GstBuffer *subbuf;
496
497       GST_LOG_OBJECT (rtpmp4vpay, "stripping config at %d, size %d", strip,
498           size - strip);
499
500       /* strip off header */
501       subbuf = gst_buffer_create_sub (buffer, strip, size - strip);
502       GST_BUFFER_TIMESTAMP (subbuf) = timestamp;
503       gst_buffer_unref (buffer);
504       buffer = subbuf;
505
506       size = GST_BUFFER_SIZE (buffer);
507     } else {
508       GST_LOG_OBJECT (rtpmp4vpay, "found config in stream");
509       rtpmp4vpay->last_config = timestamp;
510     }
511   }
512
513   /* there is a config request, see if we need to insert it */
514   if (rtpmp4vpay->send_config && vopi && (rtpmp4vpay->config_interval > 0) &&
515       rtpmp4vpay->config) {
516     if (rtpmp4vpay->last_config != -1) {
517       guint64 diff;
518
519       GST_LOG_OBJECT (rtpmp4vpay,
520           "now %" GST_TIME_FORMAT ", last VOP-I %" GST_TIME_FORMAT,
521           GST_TIME_ARGS (timestamp), GST_TIME_ARGS (rtpmp4vpay->last_config));
522
523       /* calculate diff between last config in milliseconds */
524       if (timestamp > rtpmp4vpay->last_config) {
525         diff = timestamp - rtpmp4vpay->last_config;
526       } else {
527         diff = 0;
528       }
529
530       GST_DEBUG_OBJECT (rtpmp4vpay,
531           "interval since last config %" GST_TIME_FORMAT, GST_TIME_ARGS (diff));
532
533       /* bigger than interval, queue config */
534       /* FIXME should convert timestamps to running time */
535       if (GST_TIME_AS_SECONDS (diff) >= rtpmp4vpay->config_interval) {
536         GST_DEBUG_OBJECT (rtpmp4vpay, "time to send config");
537         send_config = TRUE;
538       }
539     } else {
540       /* no known previous config time, send now */
541       GST_DEBUG_OBJECT (rtpmp4vpay, "no previous config time, send now");
542       send_config = TRUE;
543     }
544
545     if (send_config) {
546       /* we need to send config now first */
547       GstBuffer *superbuf;
548
549       GST_LOG_OBJECT (rtpmp4vpay, "inserting config in stream");
550
551       /* insert header */
552       superbuf = gst_buffer_merge (rtpmp4vpay->config, buffer);
553
554       GST_BUFFER_TIMESTAMP (superbuf) = timestamp;
555       gst_buffer_unref (buffer);
556       buffer = superbuf;
557
558       size = GST_BUFFER_SIZE (buffer);
559
560       if (timestamp != -1) {
561         rtpmp4vpay->last_config = timestamp;
562       }
563     }
564   }
565
566   /* if we need to flush, do so now */
567   if (flush) {
568     ret = gst_rtp_mp4v_pay_flush (rtpmp4vpay);
569     rtpmp4vpay->first_timestamp = timestamp;
570     rtpmp4vpay->duration = 0;
571     avail = 0;
572   }
573
574   /* get packet length of data and see if we exceeded MTU. */
575   packet_len = gst_rtp_buffer_calc_packet_len (avail + size, 0, 0);
576
577   if (gst_basertppayload_is_filled (basepayload,
578           packet_len, rtpmp4vpay->duration + duration)) {
579     ret = gst_rtp_mp4v_pay_flush (rtpmp4vpay);
580     rtpmp4vpay->first_timestamp = timestamp;
581     rtpmp4vpay->duration = 0;
582   }
583
584   /* push new data */
585   gst_adapter_push (rtpmp4vpay->adapter, buffer);
586
587   rtpmp4vpay->duration += duration;
588
589   return ret;
590 }
591
592 static gboolean
593 gst_rtp_mp4v_pay_handle_event (GstPad * pad, GstEvent * event)
594 {
595   GstRtpMP4VPay *rtpmp4vpay;
596
597   rtpmp4vpay = GST_RTP_MP4V_PAY (gst_pad_get_parent (pad));
598
599   GST_DEBUG ("Got event: %s", GST_EVENT_TYPE_NAME (event));
600
601   switch (GST_EVENT_TYPE (event)) {
602     case GST_EVENT_NEWSEGMENT:
603     case GST_EVENT_EOS:
604       /* This flush call makes sure that the last buffer is always pushed
605        * to the base payloader */
606       gst_rtp_mp4v_pay_flush (rtpmp4vpay);
607       break;
608     case GST_EVENT_FLUSH_STOP:
609       gst_rtp_mp4v_pay_empty (rtpmp4vpay);
610       break;
611     default:
612       break;
613   }
614
615   g_object_unref (rtpmp4vpay);
616
617   /* let parent handle event too */
618   return FALSE;
619 }
620
621 static void
622 gst_rtp_mp4v_pay_set_property (GObject * object, guint prop_id,
623     const GValue * value, GParamSpec * pspec)
624 {
625   GstRtpMP4VPay *rtpmp4vpay;
626
627   rtpmp4vpay = GST_RTP_MP4V_PAY (object);
628
629   switch (prop_id) {
630     case ARG_SEND_CONFIG:
631       rtpmp4vpay->send_config = g_value_get_boolean (value);
632       break;
633     case ARG_BUFFER_LIST:
634       rtpmp4vpay->buffer_list = g_value_get_boolean (value);
635       break;
636     case ARG_CONFIG_INTERVAL:
637       rtpmp4vpay->config_interval = g_value_get_uint (value);
638       break;
639     default:
640       break;
641   }
642 }
643
644 static void
645 gst_rtp_mp4v_pay_get_property (GObject * object, guint prop_id,
646     GValue * value, GParamSpec * pspec)
647 {
648   GstRtpMP4VPay *rtpmp4vpay;
649
650   rtpmp4vpay = GST_RTP_MP4V_PAY (object);
651
652   switch (prop_id) {
653     case ARG_SEND_CONFIG:
654       g_value_set_boolean (value, rtpmp4vpay->send_config);
655       break;
656     case ARG_BUFFER_LIST:
657       g_value_set_boolean (value, rtpmp4vpay->buffer_list);
658       break;
659     case ARG_CONFIG_INTERVAL:
660       g_value_set_uint (value, rtpmp4vpay->config_interval);
661       break;
662     default:
663       break;
664   }
665 }
666
667 gboolean
668 gst_rtp_mp4v_pay_plugin_init (GstPlugin * plugin)
669 {
670   return gst_element_register (plugin, "rtpmp4vpay",
671       GST_RANK_SECONDARY, GST_TYPE_RTP_MP4V_PAY);
672 }