• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
gst_rtp_mp4v_pay_class_init(GstRtpMP4VPayClass * klass)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
gst_rtp_mp4v_pay_init(GstRtpMP4VPay * rtpmp4vpay)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
gst_rtp_mp4v_pay_finalize(GObject * object)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
gst_rtp_mp4v_pay_new_caps(GstRtpMP4VPay * rtpmp4vpay)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
gst_rtp_mp4v_pay_setcaps(GstRTPBasePayload * payload,GstCaps * caps)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
gst_rtp_mp4v_pay_empty(GstRtpMP4VPay * rtpmp4vpay)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
gst_rtp_mp4v_pay_flush(GstRtpMP4VPay * rtpmp4vpay)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     gst_rtp_buffer_unmap (&rtp);
301     gst_rtp_copy_video_meta (rtpmp4vpay, outbuf, outbuf_data);
302     outbuf = gst_buffer_append (outbuf, outbuf_data);
303 
304     GST_BUFFER_PTS (outbuf) = rtpmp4vpay->first_timestamp;
305 
306     /* add to list */
307     gst_buffer_list_insert (list, -1, outbuf);
308   }
309 
310   /* push the whole buffer list at once */
311   ret =
312       gst_rtp_base_payload_push_list (GST_RTP_BASE_PAYLOAD (rtpmp4vpay), list);
313 
314   return ret;
315 }
316 
317 #define VOS_STARTCODE                   0x000001B0
318 #define VOS_ENDCODE                     0x000001B1
319 #define USER_DATA_STARTCODE             0x000001B2
320 #define GOP_STARTCODE                   0x000001B3
321 #define VISUAL_OBJECT_STARTCODE         0x000001B5
322 #define VOP_STARTCODE                   0x000001B6
323 
324 static gboolean
gst_rtp_mp4v_pay_depay_data(GstRtpMP4VPay * enc,guint8 * data,guint size,gint * strip,gboolean * vopi)325 gst_rtp_mp4v_pay_depay_data (GstRtpMP4VPay * enc, guint8 * data, guint size,
326     gint * strip, gboolean * vopi)
327 {
328   guint32 code;
329   gboolean result;
330   *vopi = FALSE;
331 
332   *strip = 0;
333 
334   if (size < 5)
335     return FALSE;
336 
337   code = GST_READ_UINT32_BE (data);
338   GST_DEBUG_OBJECT (enc, "start code 0x%08x", code);
339 
340   switch (code) {
341     case VOS_STARTCODE:
342     case 0x00000101:
343     {
344       gint i;
345       guint8 profile;
346       gboolean newprofile = FALSE;
347       gboolean equal;
348 
349       if (code == VOS_STARTCODE) {
350         /* profile_and_level_indication */
351         profile = data[4];
352 
353         GST_DEBUG_OBJECT (enc, "VOS profile 0x%08x", profile);
354 
355         if (profile != enc->profile) {
356           newprofile = TRUE;
357           enc->profile = profile;
358         }
359       }
360 
361       /* up to the next GOP_STARTCODE or VOP_STARTCODE is
362        * the config information */
363       code = 0xffffffff;
364       for (i = 5; i < size - 4; i++) {
365         code = (code << 8) | data[i];
366         if (code == GOP_STARTCODE || code == VOP_STARTCODE)
367           break;
368       }
369       i -= 3;
370       /* see if config changed */
371       equal = FALSE;
372       if (enc->config) {
373         if (gst_buffer_get_size (enc->config) == i) {
374           equal = gst_buffer_memcmp (enc->config, 0, data, i) == 0;
375         }
376       }
377       /* if config string changed or new profile, make new caps */
378       if (!equal || newprofile) {
379         if (enc->config)
380           gst_buffer_unref (enc->config);
381         enc->config = gst_buffer_new_and_alloc (i);
382 
383         gst_buffer_fill (enc->config, 0, data, i);
384 
385         gst_rtp_mp4v_pay_new_caps (enc);
386       }
387       *strip = i;
388       /* we need to flush out the current packet. */
389       result = TRUE;
390       break;
391     }
392     case VOP_STARTCODE:
393       GST_DEBUG_OBJECT (enc, "VOP");
394       /* VOP startcode, we don't have to flush the packet */
395       result = FALSE;
396       /* vop-coding-type == I-frame */
397       if (size > 4 && (data[4] >> 6 == 0)) {
398         GST_DEBUG_OBJECT (enc, "VOP-I");
399         *vopi = TRUE;
400       }
401       break;
402     case GOP_STARTCODE:
403       GST_DEBUG_OBJECT (enc, "GOP");
404       *vopi = TRUE;
405       result = TRUE;
406       break;
407     case 0x00000100:
408       enc->need_config = FALSE;
409       result = TRUE;
410       break;
411     default:
412       if (code >= 0x20 && code <= 0x2f) {
413         GST_DEBUG_OBJECT (enc, "short header");
414         result = FALSE;
415       } else {
416         GST_DEBUG_OBJECT (enc, "other startcode");
417         /* all other startcodes need a flush */
418         result = TRUE;
419       }
420       break;
421   }
422   return result;
423 }
424 
425 /* we expect buffers starting on startcodes.
426  */
427 static GstFlowReturn
gst_rtp_mp4v_pay_handle_buffer(GstRTPBasePayload * basepayload,GstBuffer * buffer)428 gst_rtp_mp4v_pay_handle_buffer (GstRTPBasePayload * basepayload,
429     GstBuffer * buffer)
430 {
431   GstRtpMP4VPay *rtpmp4vpay;
432   GstFlowReturn ret;
433   guint avail;
434   guint packet_len;
435   GstMapInfo map;
436   gsize size;
437   gboolean flush;
438   gint strip;
439   GstClockTime timestamp, duration;
440   gboolean vopi;
441   gboolean send_config;
442   GstClockTime running_time = GST_CLOCK_TIME_NONE;
443 
444   ret = GST_FLOW_OK;
445   send_config = FALSE;
446 
447   rtpmp4vpay = GST_RTP_MP4V_PAY (basepayload);
448 
449   gst_buffer_map (buffer, &map, GST_MAP_READ);
450   size = map.size;
451   timestamp = GST_BUFFER_PTS (buffer);
452   duration = GST_BUFFER_DURATION (buffer);
453   avail = gst_adapter_available (rtpmp4vpay->adapter);
454 
455   if (duration == -1)
456     duration = 0;
457 
458   /* empty buffer, take timestamp */
459   if (avail == 0) {
460     rtpmp4vpay->first_timestamp = timestamp;
461     rtpmp4vpay->duration = 0;
462   }
463 
464   /* depay incoming data and see if we need to start a new RTP
465    * packet */
466   flush =
467       gst_rtp_mp4v_pay_depay_data (rtpmp4vpay, map.data, size, &strip, &vopi);
468   gst_buffer_unmap (buffer, &map);
469 
470   if (strip) {
471     /* strip off config if requested, do not strip off if the
472      * config_interval is set to -1 */
473     if (!(rtpmp4vpay->config_interval > 0)
474         && !(rtpmp4vpay->config_interval == -1)) {
475       GstBuffer *subbuf;
476 
477       GST_LOG_OBJECT (rtpmp4vpay, "stripping config at %d, size %d", strip,
478           (gint) size - strip);
479 
480       /* strip off header */
481       subbuf = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL, strip,
482           size - strip);
483       GST_BUFFER_PTS (subbuf) = timestamp;
484       gst_buffer_unref (buffer);
485       buffer = subbuf;
486 
487       size = gst_buffer_get_size (buffer);
488     } else {
489       running_time =
490           gst_segment_to_running_time (&basepayload->segment, GST_FORMAT_TIME,
491           timestamp);
492 
493       GST_LOG_OBJECT (rtpmp4vpay, "found config in stream");
494       rtpmp4vpay->last_config = running_time;
495     }
496   }
497 
498   /* there is a config request, see if we need to insert it */
499   if (vopi && (rtpmp4vpay->config_interval > 0) && rtpmp4vpay->config) {
500     running_time =
501         gst_segment_to_running_time (&basepayload->segment, GST_FORMAT_TIME,
502         timestamp);
503 
504     if (rtpmp4vpay->last_config != -1) {
505       guint64 diff;
506 
507       GST_LOG_OBJECT (rtpmp4vpay,
508           "now %" GST_TIME_FORMAT ", last VOP-I %" GST_TIME_FORMAT,
509           GST_TIME_ARGS (running_time),
510           GST_TIME_ARGS (rtpmp4vpay->last_config));
511 
512       /* calculate diff between last config in milliseconds */
513       if (running_time > rtpmp4vpay->last_config) {
514         diff = running_time - 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       if (GST_TIME_AS_SECONDS (diff) >= rtpmp4vpay->config_interval) {
524         GST_DEBUG_OBJECT (rtpmp4vpay, "time to send config");
525         send_config = TRUE;
526       }
527     } else {
528       /* no known previous config time, send now */
529       GST_DEBUG_OBJECT (rtpmp4vpay, "no previous config time, send now");
530       send_config = TRUE;
531     }
532   }
533 
534   if (vopi && (rtpmp4vpay->config_interval == -1)) {
535     GST_DEBUG_OBJECT (rtpmp4vpay, "sending config before current IDR frame");
536     /* send config before every IDR frame */
537     send_config = TRUE;
538   }
539 
540   if (send_config) {
541     /* we need to send config now first */
542     GST_LOG_OBJECT (rtpmp4vpay, "inserting config in stream");
543 
544     /* insert header */
545     buffer = gst_buffer_append (gst_buffer_ref (rtpmp4vpay->config), buffer);
546 
547     GST_BUFFER_PTS (buffer) = timestamp;
548     size = gst_buffer_get_size (buffer);
549 
550     if (running_time != -1) {
551       rtpmp4vpay->last_config = running_time;
552     }
553   }
554 
555   /* if we need to flush, do so now */
556   if (flush) {
557     ret = gst_rtp_mp4v_pay_flush (rtpmp4vpay);
558     rtpmp4vpay->first_timestamp = timestamp;
559     rtpmp4vpay->duration = 0;
560     avail = 0;
561   }
562 
563   /* get packet length of data and see if we exceeded MTU. */
564   packet_len = gst_rtp_buffer_calc_packet_len (avail + size, 0, 0);
565 
566   if (gst_rtp_base_payload_is_filled (basepayload,
567           packet_len, rtpmp4vpay->duration + duration)) {
568     ret = gst_rtp_mp4v_pay_flush (rtpmp4vpay);
569     rtpmp4vpay->first_timestamp = timestamp;
570     rtpmp4vpay->duration = 0;
571   }
572 
573   /* push new data */
574   gst_adapter_push (rtpmp4vpay->adapter, buffer);
575 
576   rtpmp4vpay->duration += duration;
577 
578   return ret;
579 }
580 
581 static gboolean
gst_rtp_mp4v_pay_sink_event(GstRTPBasePayload * pay,GstEvent * event)582 gst_rtp_mp4v_pay_sink_event (GstRTPBasePayload * pay, GstEvent * event)
583 {
584   GstRtpMP4VPay *rtpmp4vpay;
585 
586   rtpmp4vpay = GST_RTP_MP4V_PAY (pay);
587 
588   GST_DEBUG ("Got event: %s", GST_EVENT_TYPE_NAME (event));
589 
590   switch (GST_EVENT_TYPE (event)) {
591     case GST_EVENT_SEGMENT:
592     case GST_EVENT_EOS:
593       /* This flush call makes sure that the last buffer is always pushed
594        * to the base payloader */
595       gst_rtp_mp4v_pay_flush (rtpmp4vpay);
596       break;
597     case GST_EVENT_FLUSH_STOP:
598       gst_rtp_mp4v_pay_empty (rtpmp4vpay);
599       break;
600     default:
601       break;
602   }
603 
604   /* let parent handle event too */
605   return GST_RTP_BASE_PAYLOAD_CLASS (parent_class)->sink_event (pay, event);
606 }
607 
608 static void
gst_rtp_mp4v_pay_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)609 gst_rtp_mp4v_pay_set_property (GObject * object, guint prop_id,
610     const GValue * value, GParamSpec * pspec)
611 {
612   GstRtpMP4VPay *rtpmp4vpay;
613 
614   rtpmp4vpay = GST_RTP_MP4V_PAY (object);
615 
616   switch (prop_id) {
617     case PROP_CONFIG_INTERVAL:
618       rtpmp4vpay->config_interval = g_value_get_int (value);
619       break;
620     default:
621       break;
622   }
623 }
624 
625 static void
gst_rtp_mp4v_pay_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)626 gst_rtp_mp4v_pay_get_property (GObject * object, guint prop_id,
627     GValue * value, GParamSpec * pspec)
628 {
629   GstRtpMP4VPay *rtpmp4vpay;
630 
631   rtpmp4vpay = GST_RTP_MP4V_PAY (object);
632 
633   switch (prop_id) {
634     case PROP_CONFIG_INTERVAL:
635       g_value_set_int (value, rtpmp4vpay->config_interval);
636       break;
637     default:
638       break;
639   }
640 }
641