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