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