Tizen 2.0 Release
[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_static_pad_template (element_class,
91       &gst_rtp_mp4v_pay_src_template);
92   gst_element_class_add_static_pad_template (element_class,
93       &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(deprecated "
116           "see config-interval)",
117           DEFAULT_SEND_CONFIG, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
118
119   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BUFFER_LIST,
120       g_param_spec_boolean ("buffer-list", "Buffer Array",
121           "Use Buffer Arrays",
122           DEFAULT_BUFFER_LIST, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
123
124   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_CONFIG_INTERVAL,
125       g_param_spec_uint ("config-interval", "Config Send Interval",
126           "Send Config Insertion Interval in seconds (configuration headers "
127           "will be multiplexed in the data stream when detected.) (0 = disabled)",
128           0, 3600, DEFAULT_CONFIG_INTERVAL,
129           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
130       );
131
132   gobject_class->finalize = gst_rtp_mp4v_pay_finalize;
133
134   gstbasertppayload_class->set_caps = gst_rtp_mp4v_pay_setcaps;
135   gstbasertppayload_class->handle_buffer = gst_rtp_mp4v_pay_handle_buffer;
136   gstbasertppayload_class->handle_event = gst_rtp_mp4v_pay_handle_event;
137
138   GST_DEBUG_CATEGORY_INIT (rtpmp4vpay_debug, "rtpmp4vpay", 0,
139       "MP4 video RTP Payloader");
140 }
141
142 static void
143 gst_rtp_mp4v_pay_init (GstRtpMP4VPay * rtpmp4vpay, GstRtpMP4VPayClass * klass)
144 {
145   rtpmp4vpay->adapter = gst_adapter_new ();
146   rtpmp4vpay->rate = 90000;
147   rtpmp4vpay->profile = 1;
148   rtpmp4vpay->buffer_list = DEFAULT_BUFFER_LIST;
149   rtpmp4vpay->send_config = DEFAULT_SEND_CONFIG;
150   rtpmp4vpay->need_config = TRUE;
151   rtpmp4vpay->config_interval = DEFAULT_CONFIG_INTERVAL;
152   rtpmp4vpay->last_config = -1;
153
154   rtpmp4vpay->config = NULL;
155 }
156
157 static void
158 gst_rtp_mp4v_pay_finalize (GObject * object)
159 {
160   GstRtpMP4VPay *rtpmp4vpay;
161
162   rtpmp4vpay = GST_RTP_MP4V_PAY (object);
163
164   if (rtpmp4vpay->config) {
165     gst_buffer_unref (rtpmp4vpay->config);
166     rtpmp4vpay->config = NULL;
167   }
168   g_object_unref (rtpmp4vpay->adapter);
169   rtpmp4vpay->adapter = NULL;
170
171   G_OBJECT_CLASS (parent_class)->finalize (object);
172 }
173
174 static gboolean
175 gst_rtp_mp4v_pay_new_caps (GstRtpMP4VPay * rtpmp4vpay)
176 {
177   gchar *profile, *config;
178   GValue v = { 0 };
179   gboolean res;
180
181   profile = g_strdup_printf ("%d", rtpmp4vpay->profile);
182   g_value_init (&v, GST_TYPE_BUFFER);
183   gst_value_set_buffer (&v, rtpmp4vpay->config);
184   config = gst_value_serialize (&v);
185
186   res = gst_basertppayload_set_outcaps (GST_BASE_RTP_PAYLOAD (rtpmp4vpay),
187       "profile-level-id", G_TYPE_STRING, profile,
188       "config", G_TYPE_STRING, config, NULL);
189
190   g_value_unset (&v);
191
192   g_free (profile);
193   g_free (config);
194
195   return res;
196 }
197
198 static gboolean
199 gst_rtp_mp4v_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps)
200 {
201   GstRtpMP4VPay *rtpmp4vpay;
202   GstStructure *structure;
203   const GValue *codec_data;
204   gboolean res;
205
206   rtpmp4vpay = GST_RTP_MP4V_PAY (payload);
207
208   gst_basertppayload_set_options (payload, "video", TRUE, "MP4V-ES",
209       rtpmp4vpay->rate);
210
211   res = TRUE;
212
213   structure = gst_caps_get_structure (caps, 0);
214   codec_data = gst_structure_get_value (structure, "codec_data");
215   if (codec_data) {
216     GST_LOG_OBJECT (rtpmp4vpay, "got codec_data");
217     if (G_VALUE_TYPE (codec_data) == GST_TYPE_BUFFER) {
218       GstBuffer *buffer;
219       guint8 *data;
220       guint size;
221
222       buffer = gst_value_get_buffer (codec_data);
223
224       data = GST_BUFFER_DATA (buffer);
225       size = GST_BUFFER_SIZE (buffer);
226
227       if (size < 5)
228         goto done;
229
230       rtpmp4vpay->profile = data[4];
231       GST_LOG_OBJECT (rtpmp4vpay, "configuring codec_data, profile %d",
232           data[4]);
233
234       if (rtpmp4vpay->config)
235         gst_buffer_unref (rtpmp4vpay->config);
236       rtpmp4vpay->config = gst_buffer_copy (buffer);
237       res = gst_rtp_mp4v_pay_new_caps (rtpmp4vpay);
238     }
239   }
240
241 done:
242   return res;
243 }
244
245 static void
246 gst_rtp_mp4v_pay_empty (GstRtpMP4VPay * rtpmp4vpay)
247 {
248   gst_adapter_clear (rtpmp4vpay->adapter);
249 }
250
251 static GstFlowReturn
252 gst_rtp_mp4v_pay_flush (GstRtpMP4VPay * rtpmp4vpay)
253 {
254   guint avail;
255   GstBuffer *outbuf;
256   GstBuffer *outbuf_data = NULL;
257   GstFlowReturn ret;
258   GstBufferList *list = NULL;
259   GstBufferListIterator *it = NULL;
260
261   /* the data available in the adapter is either smaller
262    * than the MTU or bigger. In the case it is smaller, the complete
263    * adapter contents can be put in one packet. In the case the
264    * adapter has more than one MTU, we need to split the MP4V data
265    * over multiple packets. */
266   avail = gst_adapter_available (rtpmp4vpay->adapter);
267
268   if (rtpmp4vpay->config == NULL && rtpmp4vpay->need_config) {
269     /* when we don't have a config yet, flush things out */
270     gst_adapter_flush (rtpmp4vpay->adapter, avail);
271     avail = 0;
272   }
273
274   if (!avail)
275     return GST_FLOW_OK;
276
277   ret = GST_FLOW_OK;
278
279   if (rtpmp4vpay->buffer_list) {
280     /* Use buffer lists. Each frame will be put into a list
281      * of buffers and the whole list will be pushed downstream
282      * at once */
283     list = gst_buffer_list_new ();
284     it = gst_buffer_list_iterate (list);
285   }
286
287   while (avail > 0) {
288     guint towrite;
289     guint8 *payload;
290     guint payload_len;
291     guint packet_len;
292
293     /* this will be the total lenght of the packet */
294     packet_len = gst_rtp_buffer_calc_packet_len (avail, 0, 0);
295
296     /* fill one MTU or all available bytes */
297     towrite = MIN (packet_len, GST_BASE_RTP_PAYLOAD_MTU (rtpmp4vpay));
298
299     /* this is the payload length */
300     payload_len = gst_rtp_buffer_calc_payload_len (towrite, 0, 0);
301
302     if (rtpmp4vpay->buffer_list) {
303       /* create buffer without payload. The payload will be put
304        * in next buffer instead. Both buffers will be then added
305        * to the list */
306       outbuf = gst_rtp_buffer_new_allocate (0, 0, 0);
307
308       /* Take buffer with the payload from the adapter */
309       outbuf_data = gst_adapter_take_buffer (rtpmp4vpay->adapter, payload_len);
310     } else {
311       /* create buffer to hold the payload */
312       outbuf = gst_rtp_buffer_new_allocate (payload_len, 0, 0);
313
314       /* copy payload */
315       payload = gst_rtp_buffer_get_payload (outbuf);
316
317       gst_adapter_copy (rtpmp4vpay->adapter, payload, 0, payload_len);
318       gst_adapter_flush (rtpmp4vpay->adapter, payload_len);
319     }
320
321     avail -= payload_len;
322
323     gst_rtp_buffer_set_marker (outbuf, avail == 0);
324
325     GST_BUFFER_TIMESTAMP (outbuf) = rtpmp4vpay->first_timestamp;
326
327     if (rtpmp4vpay->buffer_list) {
328       /* create a new group to hold the rtp header and the payload */
329       gst_buffer_list_iterator_add_group (it);
330       gst_buffer_list_iterator_add (it, outbuf);
331       gst_buffer_list_iterator_add (it, outbuf_data);
332     } else {
333       ret = gst_basertppayload_push (GST_BASE_RTP_PAYLOAD (rtpmp4vpay), outbuf);
334     }
335   }
336
337   if (rtpmp4vpay->buffer_list) {
338     gst_buffer_list_iterator_free (it);
339     /* push the whole buffer list at once */
340     ret =
341         gst_basertppayload_push_list (GST_BASE_RTP_PAYLOAD (rtpmp4vpay), list);
342   }
343
344   return ret;
345 }
346
347 #define VOS_STARTCODE                   0x000001B0
348 #define VOS_ENDCODE                     0x000001B1
349 #define USER_DATA_STARTCODE             0x000001B2
350 #define GOP_STARTCODE                   0x000001B3
351 #define VISUAL_OBJECT_STARTCODE         0x000001B5
352 #define VOP_STARTCODE                   0x000001B6
353
354 static gboolean
355 gst_rtp_mp4v_pay_depay_data (GstRtpMP4VPay * enc, guint8 * data, guint size,
356     gint * strip, gboolean * vopi)
357 {
358   guint32 code;
359   gboolean result;
360   *vopi = FALSE;
361
362   *strip = 0;
363
364   if (size < 5)
365     return FALSE;
366
367   code = GST_READ_UINT32_BE (data);
368   GST_DEBUG_OBJECT (enc, "start code 0x%08x", code);
369
370   switch (code) {
371     case VOS_STARTCODE:
372     case 0x00000101:
373     {
374       gint i;
375       guint8 profile;
376       gboolean newprofile = FALSE;
377       gboolean equal;
378
379       if (code == VOS_STARTCODE) {
380         /* profile_and_level_indication */
381         profile = data[4];
382
383         GST_DEBUG_OBJECT (enc, "VOS profile 0x%08x", profile);
384
385         if (profile != enc->profile) {
386           newprofile = TRUE;
387           enc->profile = profile;
388         }
389       }
390
391       /* up to the next GOP_STARTCODE or VOP_STARTCODE is
392        * the config information */
393       code = 0xffffffff;
394       for (i = 5; i < size - 4; i++) {
395         code = (code << 8) | data[i];
396         if (code == GOP_STARTCODE || code == VOP_STARTCODE)
397           break;
398       }
399       i -= 3;
400       /* see if config changed */
401       equal = FALSE;
402       if (enc->config) {
403         if (GST_BUFFER_SIZE (enc->config) == i) {
404           equal = memcmp (GST_BUFFER_DATA (enc->config), data, i) == 0;
405         }
406       }
407       /* if config string changed or new profile, make new caps */
408       if (!equal || newprofile) {
409         if (enc->config)
410           gst_buffer_unref (enc->config);
411         enc->config = gst_buffer_new_and_alloc (i);
412         memcpy (GST_BUFFER_DATA (enc->config), data, i);
413         gst_rtp_mp4v_pay_new_caps (enc);
414       }
415       *strip = i;
416       /* we need to flush out the current packet. */
417       result = TRUE;
418       break;
419     }
420     case VOP_STARTCODE:
421       GST_DEBUG_OBJECT (enc, "VOP");
422       /* VOP startcode, we don't have to flush the packet */
423       result = FALSE;
424       /* vop-coding-type == I-frame */
425       if (size > 4 && (data[4] >> 6 == 0)) {
426         GST_DEBUG_OBJECT (enc, "VOP-I");
427         *vopi = TRUE;
428       }
429       break;
430     case GOP_STARTCODE:
431       GST_DEBUG_OBJECT (enc, "GOP");
432       *vopi = TRUE;
433       result = TRUE;
434       break;
435     case 0x00000100:
436       enc->need_config = FALSE;
437       result = TRUE;
438       break;
439     default:
440       if (code >= 0x20 && code <= 0x2f) {
441         GST_DEBUG_OBJECT (enc, "short header");
442         result = FALSE;
443       } else {
444         GST_DEBUG_OBJECT (enc, "other startcode");
445         /* all other startcodes need a flush */
446         result = TRUE;
447       }
448       break;
449   }
450   return result;
451 }
452
453 /* we expect buffers starting on startcodes. 
454  */
455 static GstFlowReturn
456 gst_rtp_mp4v_pay_handle_buffer (GstBaseRTPPayload * basepayload,
457     GstBuffer * buffer)
458 {
459   GstRtpMP4VPay *rtpmp4vpay;
460   GstFlowReturn ret;
461   guint size, avail;
462   guint packet_len;
463   guint8 *data;
464   gboolean flush;
465   gint strip;
466   GstClockTime timestamp, duration;
467   gboolean vopi;
468   gboolean send_config;
469
470   ret = GST_FLOW_OK;
471   send_config = FALSE;
472
473   rtpmp4vpay = GST_RTP_MP4V_PAY (basepayload);
474
475   size = GST_BUFFER_SIZE (buffer);
476   data = GST_BUFFER_DATA (buffer);
477   timestamp = GST_BUFFER_TIMESTAMP (buffer);
478   duration = GST_BUFFER_DURATION (buffer);
479   avail = gst_adapter_available (rtpmp4vpay->adapter);
480
481   if (duration == -1)
482     duration = 0;
483
484   /* empty buffer, take timestamp */
485   if (avail == 0) {
486     rtpmp4vpay->first_timestamp = timestamp;
487     rtpmp4vpay->duration = 0;
488   }
489
490   /* depay incomming data and see if we need to start a new RTP
491    * packet */
492   flush = gst_rtp_mp4v_pay_depay_data (rtpmp4vpay, data, size, &strip, &vopi);
493   if (strip) {
494     /* strip off config if requested */
495     if (!(rtpmp4vpay->config_interval > 0)) {
496       GstBuffer *subbuf;
497
498       GST_LOG_OBJECT (rtpmp4vpay, "stripping config at %d, size %d", strip,
499           size - strip);
500
501       /* strip off header */
502       subbuf = gst_buffer_create_sub (buffer, strip, size - strip);
503       GST_BUFFER_TIMESTAMP (subbuf) = timestamp;
504       gst_buffer_unref (buffer);
505       buffer = subbuf;
506
507       size = GST_BUFFER_SIZE (buffer);
508     } else {
509       GST_LOG_OBJECT (rtpmp4vpay, "found config in stream");
510       rtpmp4vpay->last_config = timestamp;
511     }
512   }
513
514   /* there is a config request, see if we need to insert it */
515   if (vopi && (rtpmp4vpay->config_interval > 0) && 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       /* send the configuration once every minute */
633       if (rtpmp4vpay->send_config && !(rtpmp4vpay->config_interval > 0)) {
634         rtpmp4vpay->config_interval = 60;
635       }
636       break;
637     case ARG_BUFFER_LIST:
638       rtpmp4vpay->buffer_list = g_value_get_boolean (value);
639       break;
640     case ARG_CONFIG_INTERVAL:
641       rtpmp4vpay->config_interval = g_value_get_uint (value);
642       break;
643     default:
644       break;
645   }
646 }
647
648 static void
649 gst_rtp_mp4v_pay_get_property (GObject * object, guint prop_id,
650     GValue * value, GParamSpec * pspec)
651 {
652   GstRtpMP4VPay *rtpmp4vpay;
653
654   rtpmp4vpay = GST_RTP_MP4V_PAY (object);
655
656   switch (prop_id) {
657     case ARG_SEND_CONFIG:
658       g_value_set_boolean (value, rtpmp4vpay->send_config);
659       break;
660     case ARG_BUFFER_LIST:
661       g_value_set_boolean (value, rtpmp4vpay->buffer_list);
662       break;
663     case ARG_CONFIG_INTERVAL:
664       g_value_set_uint (value, rtpmp4vpay->config_interval);
665       break;
666     default:
667       break;
668   }
669 }
670
671 gboolean
672 gst_rtp_mp4v_pay_plugin_init (GstPlugin * plugin)
673 {
674   return gst_element_register (plugin, "rtpmp4vpay",
675       GST_RANK_SECONDARY, GST_TYPE_RTP_MP4V_PAY);
676 }