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