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