1 /* GStreamer
2 * Copyright (C) <2005,2006> 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 * Unless otherwise indicated, Source Code is licensed under MIT license.
21 * See further explanation attached in License Statement (distributed in the file
22 * LICENSE).
23 *
24 * Permission is hereby granted, free of charge, to any person obtaining a copy of
25 * this software and associated documentation files (the "Software"), to deal in
26 * the Software without restriction, including without limitation the rights to
27 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
28 * of the Software, and to permit persons to whom the Software is furnished to do
29 * so, subject to the following conditions:
30 *
31 * The above copyright notice and this permission notice shall be included in all
32 * copies or substantial portions of the Software.
33 *
34 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
37 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
40 * SOFTWARE.
41 */
42 /* Element-Checklist-Version: 5 */
43
44 /**
45 * SECTION:element-rtpdec
46 *
47 * A simple RTP session manager used internally by rtspsrc.
48 */
49
50 /* #define HAVE_RTCP */
51
52 #include <gst/rtp/gstrtpbuffer.h>
53
54 #ifdef HAVE_RTCP
55 #include <gst/rtp/gstrtcpbuffer.h>
56 #endif
57
58 #include "gstrtpdec.h"
59 #include <stdio.h>
60
61 GST_DEBUG_CATEGORY_STATIC (rtpdec_debug);
62 #define GST_CAT_DEFAULT (rtpdec_debug)
63
64 /* GstRTPDec signals and args */
65 enum
66 {
67 SIGNAL_REQUEST_PT_MAP,
68 SIGNAL_CLEAR_PT_MAP,
69
70 SIGNAL_ON_NEW_SSRC,
71 SIGNAL_ON_SSRC_COLLISION,
72 SIGNAL_ON_SSRC_VALIDATED,
73 SIGNAL_ON_BYE_SSRC,
74 SIGNAL_ON_BYE_TIMEOUT,
75 SIGNAL_ON_TIMEOUT,
76 LAST_SIGNAL
77 };
78
79 #define DEFAULT_LATENCY_MS 200
80
81 enum
82 {
83 PROP_0,
84 PROP_LATENCY
85 };
86
87 static GstStaticPadTemplate gst_rtp_dec_recv_rtp_sink_template =
88 GST_STATIC_PAD_TEMPLATE ("recv_rtp_sink_%u",
89 GST_PAD_SINK,
90 GST_PAD_REQUEST,
91 GST_STATIC_CAPS ("application/x-rtp")
92 );
93
94 static GstStaticPadTemplate gst_rtp_dec_recv_rtcp_sink_template =
95 GST_STATIC_PAD_TEMPLATE ("recv_rtcp_sink_%u",
96 GST_PAD_SINK,
97 GST_PAD_REQUEST,
98 GST_STATIC_CAPS ("application/x-rtcp")
99 );
100
101 static GstStaticPadTemplate gst_rtp_dec_recv_rtp_src_template =
102 GST_STATIC_PAD_TEMPLATE ("recv_rtp_src_%u_%u_%u",
103 GST_PAD_SRC,
104 GST_PAD_SOMETIMES,
105 GST_STATIC_CAPS ("application/x-rtp")
106 );
107
108 static GstStaticPadTemplate gst_rtp_dec_rtcp_src_template =
109 GST_STATIC_PAD_TEMPLATE ("rtcp_src_%u",
110 GST_PAD_SRC,
111 GST_PAD_REQUEST,
112 GST_STATIC_CAPS ("application/x-rtcp")
113 );
114
115 static void gst_rtp_dec_finalize (GObject * object);
116 static void gst_rtp_dec_set_property (GObject * object,
117 guint prop_id, const GValue * value, GParamSpec * pspec);
118 static void gst_rtp_dec_get_property (GObject * object,
119 guint prop_id, GValue * value, GParamSpec * pspec);
120
121 static GstClock *gst_rtp_dec_provide_clock (GstElement * element);
122 static GstStateChangeReturn gst_rtp_dec_change_state (GstElement * element,
123 GstStateChange transition);
124 static GstPad *gst_rtp_dec_request_new_pad (GstElement * element,
125 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
126 static void gst_rtp_dec_release_pad (GstElement * element, GstPad * pad);
127
128 static GstFlowReturn gst_rtp_dec_chain_rtp (GstPad * pad, GstObject * parent,
129 GstBuffer * buffer);
130 static GstFlowReturn gst_rtp_dec_chain_rtcp (GstPad * pad, GstObject * parent,
131 GstBuffer * buffer);
132
133
134 /* Manages the receiving end of the packets.
135 *
136 * There is one such structure for each RTP session (audio/video/...).
137 * We get the RTP/RTCP packets and stuff them into the session manager.
138 */
139 struct _GstRTPDecSession
140 {
141 /* session id */
142 gint id;
143 /* the parent bin */
144 GstRTPDec *dec;
145
146 gboolean active;
147 /* we only support one ssrc and one pt */
148 guint32 ssrc;
149 guint8 pt;
150 GstCaps *caps;
151
152 /* the pads of the session */
153 GstPad *recv_rtp_sink;
154 GstPad *recv_rtp_src;
155 GstPad *recv_rtcp_sink;
156 GstPad *rtcp_src;
157 };
158
159 /* find a session with the given id */
160 static GstRTPDecSession *
find_session_by_id(GstRTPDec * rtpdec,gint id)161 find_session_by_id (GstRTPDec * rtpdec, gint id)
162 {
163 GSList *walk;
164
165 for (walk = rtpdec->sessions; walk; walk = g_slist_next (walk)) {
166 GstRTPDecSession *sess = (GstRTPDecSession *) walk->data;
167
168 if (sess->id == id)
169 return sess;
170 }
171 return NULL;
172 }
173
174 /* create a session with the given id */
175 static GstRTPDecSession *
create_session(GstRTPDec * rtpdec,gint id)176 create_session (GstRTPDec * rtpdec, gint id)
177 {
178 GstRTPDecSession *sess;
179
180 sess = g_new0 (GstRTPDecSession, 1);
181 sess->id = id;
182 sess->dec = rtpdec;
183 rtpdec->sessions = g_slist_prepend (rtpdec->sessions, sess);
184
185 return sess;
186 }
187
188 static void
free_session(GstRTPDecSession * session)189 free_session (GstRTPDecSession * session)
190 {
191 g_free (session);
192 }
193
194 static guint gst_rtp_dec_signals[LAST_SIGNAL] = { 0 };
195
196 #define gst_rtp_dec_parent_class parent_class
197 G_DEFINE_TYPE (GstRTPDec, gst_rtp_dec, GST_TYPE_ELEMENT);
198
199 static void
gst_rtp_dec_class_init(GstRTPDecClass * g_class)200 gst_rtp_dec_class_init (GstRTPDecClass * g_class)
201 {
202 GObjectClass *gobject_class;
203 GstElementClass *gstelement_class;
204 GstRTPDecClass *klass;
205
206 klass = (GstRTPDecClass *) g_class;
207 gobject_class = (GObjectClass *) klass;
208 gstelement_class = (GstElementClass *) klass;
209
210 GST_DEBUG_CATEGORY_INIT (rtpdec_debug, "rtpdec", 0, "RTP decoder");
211
212 gobject_class->finalize = gst_rtp_dec_finalize;
213 gobject_class->set_property = gst_rtp_dec_set_property;
214 gobject_class->get_property = gst_rtp_dec_get_property;
215
216 g_object_class_install_property (gobject_class, PROP_LATENCY,
217 g_param_spec_uint ("latency", "Buffer latency in ms",
218 "Amount of ms to buffer", 0, G_MAXUINT, DEFAULT_LATENCY_MS,
219 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
220
221 /**
222 * GstRTPDec::request-pt-map:
223 * @rtpdec: the object which received the signal
224 * @session: the session
225 * @pt: the pt
226 *
227 * Request the payload type as #GstCaps for @pt in @session.
228 */
229 gst_rtp_dec_signals[SIGNAL_REQUEST_PT_MAP] =
230 g_signal_new ("request-pt-map", G_TYPE_FROM_CLASS (klass),
231 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTPDecClass, request_pt_map),
232 NULL, NULL, g_cclosure_marshal_generic, GST_TYPE_CAPS, 2, G_TYPE_UINT,
233 G_TYPE_UINT);
234
235 gst_rtp_dec_signals[SIGNAL_CLEAR_PT_MAP] =
236 g_signal_new ("clear-pt-map", G_TYPE_FROM_CLASS (klass),
237 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTPDecClass, clear_pt_map),
238 NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
239
240 /**
241 * GstRTPDec::on-new-ssrc:
242 * @rtpbin: the object which received the signal
243 * @session: the session
244 * @ssrc: the SSRC
245 *
246 * Notify of a new SSRC that entered @session.
247 */
248 gst_rtp_dec_signals[SIGNAL_ON_NEW_SSRC] =
249 g_signal_new ("on-new-ssrc", G_TYPE_FROM_CLASS (klass),
250 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTPDecClass, on_new_ssrc),
251 NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT,
252 G_TYPE_UINT);
253 /**
254 * GstRTPDec::on-ssrc_collision:
255 * @rtpbin: the object which received the signal
256 * @session: the session
257 * @ssrc: the SSRC
258 *
259 * Notify when we have an SSRC collision
260 */
261 gst_rtp_dec_signals[SIGNAL_ON_SSRC_COLLISION] =
262 g_signal_new ("on-ssrc-collision", G_TYPE_FROM_CLASS (klass),
263 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTPDecClass, on_ssrc_collision),
264 NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT,
265 G_TYPE_UINT);
266 /**
267 * GstRTPDec::on-ssrc_validated:
268 * @rtpbin: the object which received the signal
269 * @session: the session
270 * @ssrc: the SSRC
271 *
272 * Notify of a new SSRC that became validated.
273 */
274 gst_rtp_dec_signals[SIGNAL_ON_SSRC_VALIDATED] =
275 g_signal_new ("on-ssrc-validated", G_TYPE_FROM_CLASS (klass),
276 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTPDecClass, on_ssrc_validated),
277 NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT,
278 G_TYPE_UINT);
279
280 /**
281 * GstRTPDec::on-bye-ssrc:
282 * @rtpbin: the object which received the signal
283 * @session: the session
284 * @ssrc: the SSRC
285 *
286 * Notify of an SSRC that became inactive because of a BYE packet.
287 */
288 gst_rtp_dec_signals[SIGNAL_ON_BYE_SSRC] =
289 g_signal_new ("on-bye-ssrc", G_TYPE_FROM_CLASS (klass),
290 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTPDecClass, on_bye_ssrc),
291 NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT,
292 G_TYPE_UINT);
293 /**
294 * GstRTPDec::on-bye-timeout:
295 * @rtpbin: the object which received the signal
296 * @session: the session
297 * @ssrc: the SSRC
298 *
299 * Notify of an SSRC that has timed out because of BYE
300 */
301 gst_rtp_dec_signals[SIGNAL_ON_BYE_TIMEOUT] =
302 g_signal_new ("on-bye-timeout", G_TYPE_FROM_CLASS (klass),
303 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTPDecClass, on_bye_timeout),
304 NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT,
305 G_TYPE_UINT);
306 /**
307 * GstRTPDec::on-timeout:
308 * @rtpbin: the object which received the signal
309 * @session: the session
310 * @ssrc: the SSRC
311 *
312 * Notify of an SSRC that has timed out
313 */
314 gst_rtp_dec_signals[SIGNAL_ON_TIMEOUT] =
315 g_signal_new ("on-timeout", G_TYPE_FROM_CLASS (klass),
316 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTPDecClass, on_timeout),
317 NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT,
318 G_TYPE_UINT);
319
320 gstelement_class->provide_clock =
321 GST_DEBUG_FUNCPTR (gst_rtp_dec_provide_clock);
322 gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_rtp_dec_change_state);
323 gstelement_class->request_new_pad =
324 GST_DEBUG_FUNCPTR (gst_rtp_dec_request_new_pad);
325 gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_rtp_dec_release_pad);
326
327 /* sink pads */
328 gst_element_class_add_static_pad_template (gstelement_class,
329 &gst_rtp_dec_recv_rtp_sink_template);
330 gst_element_class_add_static_pad_template (gstelement_class,
331 &gst_rtp_dec_recv_rtcp_sink_template);
332 /* src pads */
333 gst_element_class_add_static_pad_template (gstelement_class,
334 &gst_rtp_dec_recv_rtp_src_template);
335 gst_element_class_add_static_pad_template (gstelement_class,
336 &gst_rtp_dec_rtcp_src_template);
337
338 gst_element_class_set_static_metadata (gstelement_class, "RTP Decoder",
339 "Codec/Parser/Network",
340 "Accepts raw RTP and RTCP packets and sends them forward",
341 "Wim Taymans <wim.taymans@gmail.com>");
342 }
343
344 static void
gst_rtp_dec_init(GstRTPDec * rtpdec)345 gst_rtp_dec_init (GstRTPDec * rtpdec)
346 {
347 rtpdec->provided_clock = gst_system_clock_obtain ();
348 rtpdec->latency = DEFAULT_LATENCY_MS;
349
350 GST_OBJECT_FLAG_SET (rtpdec, GST_ELEMENT_FLAG_PROVIDE_CLOCK);
351 }
352
353 static void
gst_rtp_dec_finalize(GObject * object)354 gst_rtp_dec_finalize (GObject * object)
355 {
356 GstRTPDec *rtpdec;
357
358 rtpdec = GST_RTP_DEC (object);
359
360 gst_object_unref (rtpdec->provided_clock);
361 g_slist_foreach (rtpdec->sessions, (GFunc) free_session, NULL);
362 g_slist_free (rtpdec->sessions);
363
364 G_OBJECT_CLASS (parent_class)->finalize (object);
365 }
366
367 static gboolean
gst_rtp_dec_query_src(GstPad * pad,GstObject * parent,GstQuery * query)368 gst_rtp_dec_query_src (GstPad * pad, GstObject * parent, GstQuery * query)
369 {
370 gboolean res;
371
372 switch (GST_QUERY_TYPE (query)) {
373 case GST_QUERY_LATENCY:
374 {
375 /* we pretend to be live with a 3 second latency */
376 /* FIXME: Do we really have infinite maximum latency? */
377 gst_query_set_latency (query, TRUE, 3 * GST_SECOND, -1);
378 res = TRUE;
379 break;
380 }
381 default:
382 res = gst_pad_query_default (pad, parent, query);
383 break;
384 }
385 return res;
386 }
387
388 static GstFlowReturn
gst_rtp_dec_chain_rtp(GstPad * pad,GstObject * parent,GstBuffer * buffer)389 gst_rtp_dec_chain_rtp (GstPad * pad, GstObject * parent, GstBuffer * buffer)
390 {
391 GstFlowReturn res;
392 GstRTPDec *rtpdec;
393 GstRTPDecSession *session;
394 guint32 ssrc;
395 guint8 pt;
396 GstRTPBuffer rtp = { NULL, };
397
398 rtpdec = GST_RTP_DEC (parent);
399
400 GST_DEBUG_OBJECT (rtpdec, "got rtp packet");
401
402 if (!gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp))
403 goto bad_packet;
404
405 ssrc = gst_rtp_buffer_get_ssrc (&rtp);
406 pt = gst_rtp_buffer_get_payload_type (&rtp);
407 gst_rtp_buffer_unmap (&rtp);
408
409 GST_DEBUG_OBJECT (rtpdec, "SSRC %08x, PT %d", ssrc, pt);
410
411 /* find session */
412 session = gst_pad_get_element_private (pad);
413
414 /* see if we have the pad */
415 if (!session->active) {
416 GstPadTemplate *templ;
417 GstElementClass *klass;
418 gchar *name;
419 GstCaps *caps;
420 GValue ret = { 0 };
421 GValue args[3] = { {0}
422 , {0}
423 , {0}
424 };
425
426 GST_DEBUG_OBJECT (rtpdec, "creating stream");
427
428 session->ssrc = ssrc;
429 session->pt = pt;
430
431 /* get pt map */
432 g_value_init (&args[0], GST_TYPE_ELEMENT);
433 g_value_set_object (&args[0], rtpdec);
434 g_value_init (&args[1], G_TYPE_UINT);
435 g_value_set_uint (&args[1], session->id);
436 g_value_init (&args[2], G_TYPE_UINT);
437 g_value_set_uint (&args[2], pt);
438
439 g_value_init (&ret, GST_TYPE_CAPS);
440 g_value_set_boxed (&ret, NULL);
441
442 g_signal_emitv (args, gst_rtp_dec_signals[SIGNAL_REQUEST_PT_MAP], 0, &ret);
443
444 caps = (GstCaps *) g_value_get_boxed (&ret);
445
446 name = g_strdup_printf ("recv_rtp_src_%u_%u_%u", session->id, ssrc, pt);
447 klass = GST_ELEMENT_GET_CLASS (rtpdec);
448 templ = gst_element_class_get_pad_template (klass, "recv_rtp_src_%u_%u_%u");
449 session->recv_rtp_src = gst_pad_new_from_template (templ, name);
450 g_free (name);
451
452 gst_pad_set_caps (session->recv_rtp_src, caps);
453
454 gst_pad_set_element_private (session->recv_rtp_src, session);
455 gst_pad_set_query_function (session->recv_rtp_src, gst_rtp_dec_query_src);
456 gst_pad_set_active (session->recv_rtp_src, TRUE);
457 gst_element_add_pad (GST_ELEMENT_CAST (rtpdec), session->recv_rtp_src);
458
459 session->active = TRUE;
460 }
461
462 res = gst_pad_push (session->recv_rtp_src, buffer);
463
464 return res;
465
466 bad_packet:
467 {
468 GST_ELEMENT_WARNING (rtpdec, STREAM, DECODE, (NULL),
469 ("RTP packet did not validate, dropping"));
470 gst_buffer_unref (buffer);
471 return GST_FLOW_OK;
472 }
473 }
474
475 static GstFlowReturn
gst_rtp_dec_chain_rtcp(GstPad * pad,GstObject * parent,GstBuffer * buffer)476 gst_rtp_dec_chain_rtcp (GstPad * pad, GstObject * parent, GstBuffer * buffer)
477 {
478 GstRTPDec *src;
479
480 #ifdef HAVE_RTCP
481 gboolean valid;
482 GstRTCPPacket packet;
483 gboolean more;
484 #endif
485
486 src = GST_RTP_DEC (parent);
487
488 GST_DEBUG_OBJECT (src, "got rtcp packet");
489
490 #ifdef HAVE_RTCP
491 valid = gst_rtcp_buffer_validate (buffer);
492 if (!valid)
493 goto bad_packet;
494
495 /* position on first packet */
496 more = gst_rtcp_buffer_get_first_packet (buffer, &packet);
497 while (more) {
498 switch (gst_rtcp_packet_get_type (&packet)) {
499 case GST_RTCP_TYPE_SR:
500 {
501 guint32 ssrc, rtptime, packet_count, octet_count;
502 guint64 ntptime;
503 guint count, i;
504
505 gst_rtcp_packet_sr_get_sender_info (&packet, &ssrc, &ntptime, &rtptime,
506 &packet_count, &octet_count);
507
508 GST_DEBUG_OBJECT (src,
509 "got SR packet: SSRC %08x, NTP %" G_GUINT64_FORMAT
510 ", RTP %u, PC %u, OC %u", ssrc, ntptime, rtptime, packet_count,
511 octet_count);
512
513 count = gst_rtcp_packet_get_rb_count (&packet);
514 for (i = 0; i < count; i++) {
515 guint32 ssrc, exthighestseq, jitter, lsr, dlsr;
516 guint8 fractionlost;
517 gint32 packetslost;
518
519 gst_rtcp_packet_get_rb (&packet, i, &ssrc, &fractionlost,
520 &packetslost, &exthighestseq, &jitter, &lsr, &dlsr);
521
522 GST_DEBUG_OBJECT (src, "got RB packet %d: SSRC %08x, FL %u"
523 ", PL %u, HS %u, JITTER %u, LSR %u, DLSR %u", ssrc, fractionlost,
524 packetslost, exthighestseq, jitter, lsr, dlsr);
525 }
526 break;
527 }
528 case GST_RTCP_TYPE_RR:
529 {
530 guint32 ssrc;
531 guint count, i;
532
533 ssrc = gst_rtcp_packet_rr_get_ssrc (&packet);
534
535 GST_DEBUG_OBJECT (src, "got RR packet: SSRC %08x", ssrc);
536
537 count = gst_rtcp_packet_get_rb_count (&packet);
538 for (i = 0; i < count; i++) {
539 guint32 ssrc, exthighestseq, jitter, lsr, dlsr;
540 guint8 fractionlost;
541 gint32 packetslost;
542
543 gst_rtcp_packet_get_rb (&packet, i, &ssrc, &fractionlost,
544 &packetslost, &exthighestseq, &jitter, &lsr, &dlsr);
545
546 GST_DEBUG_OBJECT (src, "got RB packet %d: SSRC %08x, FL %u"
547 ", PL %u, HS %u, JITTER %u, LSR %u, DLSR %u", ssrc, fractionlost,
548 packetslost, exthighestseq, jitter, lsr, dlsr);
549 }
550 break;
551 }
552 case GST_RTCP_TYPE_SDES:
553 {
554 guint chunks, i, j;
555 gboolean more_chunks, more_items;
556
557 chunks = gst_rtcp_packet_sdes_get_chunk_count (&packet);
558 GST_DEBUG_OBJECT (src, "got SDES packet with %d chunks", chunks);
559
560 more_chunks = gst_rtcp_packet_sdes_first_chunk (&packet);
561 i = 0;
562 while (more_chunks) {
563 guint32 ssrc;
564
565 ssrc = gst_rtcp_packet_sdes_get_ssrc (&packet);
566
567 GST_DEBUG_OBJECT (src, "chunk %d, SSRC %08x", i, ssrc);
568
569 more_items = gst_rtcp_packet_sdes_first_item (&packet);
570 j = 0;
571 while (more_items) {
572 GstRTCPSDESType type;
573 guint8 len;
574 gchar *data;
575
576 gst_rtcp_packet_sdes_get_item (&packet, &type, &len, &data);
577
578 GST_DEBUG_OBJECT (src, "item %d, type %d, len %d, data %s", j,
579 type, len, data);
580
581 more_items = gst_rtcp_packet_sdes_next_item (&packet);
582 j++;
583 }
584 more_chunks = gst_rtcp_packet_sdes_next_chunk (&packet);
585 i++;
586 }
587 break;
588 }
589 case GST_RTCP_TYPE_BYE:
590 {
591 guint count, i;
592 gchar *reason;
593
594 reason = gst_rtcp_packet_bye_get_reason (&packet);
595 GST_DEBUG_OBJECT (src, "got BYE packet (reason: %s)",
596 GST_STR_NULL (reason));
597 g_free (reason);
598
599 count = gst_rtcp_packet_bye_get_ssrc_count (&packet);
600 for (i = 0; i < count; i++) {
601 guint32 ssrc;
602
603
604 ssrc = gst_rtcp_packet_bye_get_nth_ssrc (&packet, i);
605
606 GST_DEBUG_OBJECT (src, "SSRC: %08x", ssrc);
607 }
608 break;
609 }
610 case GST_RTCP_TYPE_APP:
611 GST_DEBUG_OBJECT (src, "got APP packet");
612 break;
613 default:
614 GST_WARNING_OBJECT (src, "got unknown RTCP packet");
615 break;
616 }
617 more = gst_rtcp_packet_move_to_next (&packet);
618 }
619 gst_buffer_unref (buffer);
620 return GST_FLOW_OK;
621
622 bad_packet:
623 {
624 GST_WARNING_OBJECT (src, "got invalid RTCP packet");
625 gst_buffer_unref (buffer);
626 return GST_FLOW_OK;
627 }
628 #else
629 gst_buffer_unref (buffer);
630 return GST_FLOW_OK;
631 #endif
632 }
633
634 static void
gst_rtp_dec_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)635 gst_rtp_dec_set_property (GObject * object, guint prop_id,
636 const GValue * value, GParamSpec * pspec)
637 {
638 GstRTPDec *src;
639
640 src = GST_RTP_DEC (object);
641
642 switch (prop_id) {
643 case PROP_LATENCY:
644 src->latency = g_value_get_uint (value);
645 break;
646 default:
647 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
648 break;
649 }
650 }
651
652 static void
gst_rtp_dec_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)653 gst_rtp_dec_get_property (GObject * object, guint prop_id, GValue * value,
654 GParamSpec * pspec)
655 {
656 GstRTPDec *src;
657
658 src = GST_RTP_DEC (object);
659
660 switch (prop_id) {
661 case PROP_LATENCY:
662 g_value_set_uint (value, src->latency);
663 break;
664 default:
665 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
666 break;
667 }
668 }
669
670 static GstClock *
gst_rtp_dec_provide_clock(GstElement * element)671 gst_rtp_dec_provide_clock (GstElement * element)
672 {
673 GstRTPDec *rtpdec;
674
675 rtpdec = GST_RTP_DEC (element);
676
677 return GST_CLOCK_CAST (gst_object_ref (rtpdec->provided_clock));
678 }
679
680 static GstStateChangeReturn
gst_rtp_dec_change_state(GstElement * element,GstStateChange transition)681 gst_rtp_dec_change_state (GstElement * element, GstStateChange transition)
682 {
683 GstStateChangeReturn ret;
684
685 switch (transition) {
686 default:
687 break;
688 }
689
690 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
691
692 switch (transition) {
693 case GST_STATE_CHANGE_READY_TO_PAUSED:
694 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
695 /* we're NO_PREROLL when going to PAUSED */
696 ret = GST_STATE_CHANGE_NO_PREROLL;
697 break;
698 default:
699 break;
700 }
701
702 return ret;
703 }
704
705 /* Create a pad for receiving RTP for the session in @name
706 */
707 static GstPad *
create_recv_rtp(GstRTPDec * rtpdec,GstPadTemplate * templ,const gchar * name)708 create_recv_rtp (GstRTPDec * rtpdec, GstPadTemplate * templ, const gchar * name)
709 {
710 guint sessid;
711 GstRTPDecSession *session;
712
713 /* first get the session number */
714 if (name == NULL || sscanf (name, "recv_rtp_sink_%u", &sessid) != 1)
715 goto no_name;
716
717 GST_DEBUG_OBJECT (rtpdec, "finding session %d", sessid);
718
719 /* get or create session */
720 session = find_session_by_id (rtpdec, sessid);
721 if (!session) {
722 GST_DEBUG_OBJECT (rtpdec, "creating session %d", sessid);
723 /* create session now */
724 session = create_session (rtpdec, sessid);
725 if (session == NULL)
726 goto create_error;
727 }
728 /* check if pad was requested */
729 if (session->recv_rtp_sink != NULL)
730 goto existed;
731
732 GST_DEBUG_OBJECT (rtpdec, "getting RTP sink pad");
733
734 session->recv_rtp_sink = gst_pad_new_from_template (templ, name);
735 gst_pad_set_element_private (session->recv_rtp_sink, session);
736 gst_pad_set_chain_function (session->recv_rtp_sink, gst_rtp_dec_chain_rtp);
737 gst_pad_set_active (session->recv_rtp_sink, TRUE);
738 gst_element_add_pad (GST_ELEMENT_CAST (rtpdec), session->recv_rtp_sink);
739
740 return session->recv_rtp_sink;
741
742 /* ERRORS */
743 no_name:
744 {
745 g_warning ("rtpdec: invalid name given");
746 return NULL;
747 }
748 create_error:
749 {
750 /* create_session already warned */
751 return NULL;
752 }
753 existed:
754 {
755 g_warning ("rtpdec: recv_rtp pad already requested for session %d", sessid);
756 return NULL;
757 }
758 }
759
760 /* Create a pad for receiving RTCP for the session in @name
761 */
762 static GstPad *
create_recv_rtcp(GstRTPDec * rtpdec,GstPadTemplate * templ,const gchar * name)763 create_recv_rtcp (GstRTPDec * rtpdec, GstPadTemplate * templ,
764 const gchar * name)
765 {
766 guint sessid;
767 GstRTPDecSession *session;
768
769 /* first get the session number */
770 if (name == NULL || sscanf (name, "recv_rtcp_sink_%u", &sessid) != 1)
771 goto no_name;
772
773 GST_DEBUG_OBJECT (rtpdec, "finding session %d", sessid);
774
775 /* get the session, it must exist or we error */
776 session = find_session_by_id (rtpdec, sessid);
777 if (!session)
778 goto no_session;
779
780 /* check if pad was requested */
781 if (session->recv_rtcp_sink != NULL)
782 goto existed;
783
784 GST_DEBUG_OBJECT (rtpdec, "getting RTCP sink pad");
785
786 session->recv_rtcp_sink = gst_pad_new_from_template (templ, name);
787 gst_pad_set_element_private (session->recv_rtp_sink, session);
788 gst_pad_set_chain_function (session->recv_rtcp_sink, gst_rtp_dec_chain_rtcp);
789 gst_pad_set_active (session->recv_rtcp_sink, TRUE);
790 gst_element_add_pad (GST_ELEMENT_CAST (rtpdec), session->recv_rtcp_sink);
791
792 return session->recv_rtcp_sink;
793
794 /* ERRORS */
795 no_name:
796 {
797 g_warning ("rtpdec: invalid name given");
798 return NULL;
799 }
800 no_session:
801 {
802 g_warning ("rtpdec: no session with id %d", sessid);
803 return NULL;
804 }
805 existed:
806 {
807 g_warning ("rtpdec: recv_rtcp pad already requested for session %d",
808 sessid);
809 return NULL;
810 }
811 }
812
813 /* Create a pad for sending RTCP for the session in @name
814 */
815 static GstPad *
create_rtcp(GstRTPDec * rtpdec,GstPadTemplate * templ,const gchar * name)816 create_rtcp (GstRTPDec * rtpdec, GstPadTemplate * templ, const gchar * name)
817 {
818 guint sessid;
819 GstRTPDecSession *session;
820
821 /* first get the session number */
822 if (name == NULL || sscanf (name, "rtcp_src_%u", &sessid) != 1)
823 goto no_name;
824
825 /* get or create session */
826 session = find_session_by_id (rtpdec, sessid);
827 if (!session)
828 goto no_session;
829
830 /* check if pad was requested */
831 if (session->rtcp_src != NULL)
832 goto existed;
833
834 session->rtcp_src = gst_pad_new_from_template (templ, name);
835 gst_pad_set_active (session->rtcp_src, TRUE);
836 gst_element_add_pad (GST_ELEMENT_CAST (rtpdec), session->rtcp_src);
837
838 return session->rtcp_src;
839
840 /* ERRORS */
841 no_name:
842 {
843 g_warning ("rtpdec: invalid name given");
844 return NULL;
845 }
846 no_session:
847 {
848 g_warning ("rtpdec: session with id %d does not exist", sessid);
849 return NULL;
850 }
851 existed:
852 {
853 g_warning ("rtpdec: rtcp_src pad already requested for session %d", sessid);
854 return NULL;
855 }
856 }
857
858 /*
859 */
860 static GstPad *
gst_rtp_dec_request_new_pad(GstElement * element,GstPadTemplate * templ,const gchar * name,const GstCaps * caps)861 gst_rtp_dec_request_new_pad (GstElement * element,
862 GstPadTemplate * templ, const gchar * name, const GstCaps * caps)
863 {
864 GstRTPDec *rtpdec;
865 GstElementClass *klass;
866 GstPad *result;
867
868 g_return_val_if_fail (templ != NULL, NULL);
869 g_return_val_if_fail (GST_IS_RTP_DEC (element), NULL);
870
871 rtpdec = GST_RTP_DEC (element);
872 klass = GST_ELEMENT_GET_CLASS (element);
873
874 /* figure out the template */
875 if (templ == gst_element_class_get_pad_template (klass, "recv_rtp_sink_%u")) {
876 result = create_recv_rtp (rtpdec, templ, name);
877 } else if (templ == gst_element_class_get_pad_template (klass,
878 "recv_rtcp_sink_%u")) {
879 result = create_recv_rtcp (rtpdec, templ, name);
880 } else if (templ == gst_element_class_get_pad_template (klass, "rtcp_src_%u")) {
881 result = create_rtcp (rtpdec, templ, name);
882 } else
883 goto wrong_template;
884
885 return result;
886
887 /* ERRORS */
888 wrong_template:
889 {
890 g_warning ("rtpdec: this is not our template");
891 return NULL;
892 }
893 }
894
895 static void
gst_rtp_dec_release_pad(GstElement * element,GstPad * pad)896 gst_rtp_dec_release_pad (GstElement * element, GstPad * pad)
897 {
898 }
899