gstreamer/gst/rtsp/gstrtpdec.c
Wim Taymans f80444aaec gst/rtsp/: Morph RTPDec into something compatible with RTPBin as a fallback.
Original commit message from CVS:
* gst/rtsp/Makefile.am:
* gst/rtsp/gstrtpdec.c: (find_session_by_id), (create_session),
(free_session), (gst_rtp_dec_base_init), (gst_rtp_dec_class_init),
(gst_rtp_dec_init), (gst_rtp_dec_finalize),
(gst_rtp_dec_query_src), (gst_rtp_dec_chain_rtp),
(gst_rtp_dec_chain_rtcp), (gst_rtp_dec_set_property),
(gst_rtp_dec_get_property), (gst_rtp_dec_provide_clock),
(gst_rtp_dec_change_state), (create_recv_rtp), (create_recv_rtcp),
(create_rtcp), (gst_rtp_dec_request_new_pad),
(gst_rtp_dec_release_pad):
* gst/rtsp/gstrtpdec.h:
* gst/rtsp/gstrtsp.c: (plugin_init):
Morph RTPDec into something compatible with RTPBin as a fallback.
Various other style fixes.
* gst/rtsp/gstrtspsrc.c: (find_stream_by_id),
(find_stream_by_udpsrc), (gst_rtspsrc_stream_free),
(gst_rtspsrc_cleanup), (gst_rtspsrc_media_to_caps),
(new_session_pad), (gst_rtspsrc_stream_configure_transport),
(gst_rtspsrc_activate_streams), (gst_rtspsrc_loop_interleaved),
(gst_rtspsrc_loop_udp), (gst_rtspsrc_setup_auth),
(gst_rtspsrc_handle_message), (gst_rtspsrc_change_state):
* gst/rtsp/gstrtspsrc.h:
Implement RTPBin session manager handling.
Don't try to add empty properties to caps.
Implement fallback session manager, handling.
Don't combine errors from RTCP streams, just ignore them.
* gst/rtsp/rtsptransport.c: (rtsp_transport_get_manager):
* gst/rtsp/rtsptransport.h:
Implement fallback session manager.
Make RTPBin the default one when available.
2007-04-06 12:54:16 +00:00

758 lines
20 KiB
C

/* GStreamer
* Copyright (C) <2005,2006> Wim Taymans <wim@fluendo.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/*
* Unless otherwise indicated, Source Code is licensed under MIT license.
* See further explanation attached in License Statement (distributed in the file
* LICENSE).
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/* Element-Checklist-Version: 5 */
/**
* SECTION:element-rtpdec
*
* <refsect2>
* <para>
* A simple RTP session manager used internally by rtspsrc.
* </para>
* </refsect2>
*
* Last reviewed on 2006-06-20 (0.10.4)
*/
/* #define HAVE_RTCP */
#include <gst/rtp/gstrtpbuffer.h>
#ifdef HAVE_RTCP
#include <gst/rtp/gstrtcpbuffer.h>
#endif
#include "gstrtpdec.h"
GST_DEBUG_CATEGORY_STATIC (rtpdec_debug);
#define GST_CAT_DEFAULT (rtpdec_debug)
/* elementfactory information */
static const GstElementDetails rtpdec_details =
GST_ELEMENT_DETAILS ("RTP Decoder",
"Codec/Parser/Network",
"Accepts raw RTP and RTCP packets and sends them forward",
"Wim Taymans <wim@fluendo.com>");
/* GstRTPDec signals and args */
enum
{
LAST_SIGNAL
};
enum
{
PROP_0,
};
static GstStaticPadTemplate gst_rtp_dec_recv_rtp_sink_template =
GST_STATIC_PAD_TEMPLATE ("recv_rtp_sink_%d",
GST_PAD_SINK,
GST_PAD_REQUEST,
GST_STATIC_CAPS ("application/x-rtp")
);
static GstStaticPadTemplate gst_rtp_dec_recv_rtcp_sink_template =
GST_STATIC_PAD_TEMPLATE ("recv_rtcp_sink_%d",
GST_PAD_SINK,
GST_PAD_REQUEST,
GST_STATIC_CAPS ("application/x-rtcp")
);
static GstStaticPadTemplate gst_rtp_dec_recv_rtp_src_template =
GST_STATIC_PAD_TEMPLATE ("recv_rtp_src_%d_%d_%d",
GST_PAD_SRC,
GST_PAD_SOMETIMES,
GST_STATIC_CAPS ("application/x-rtp")
);
static GstStaticPadTemplate gst_rtp_dec_rtcp_src_template =
GST_STATIC_PAD_TEMPLATE ("rtcp_src_%d",
GST_PAD_SRC,
GST_PAD_REQUEST,
GST_STATIC_CAPS ("application/x-rtcp")
);
static void gst_rtp_dec_finalize (GObject * object);
static void gst_rtp_dec_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec);
static void gst_rtp_dec_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec);
static GstClock *gst_rtp_dec_provide_clock (GstElement * element);
static GstStateChangeReturn gst_rtp_dec_change_state (GstElement * element,
GstStateChange transition);
static GstPad *gst_rtp_dec_request_new_pad (GstElement * element,
GstPadTemplate * templ, const gchar * name);
static void gst_rtp_dec_release_pad (GstElement * element, GstPad * pad);
static GstFlowReturn gst_rtp_dec_chain_rtp (GstPad * pad, GstBuffer * buffer);
static GstFlowReturn gst_rtp_dec_chain_rtcp (GstPad * pad, GstBuffer * buffer);
/* Manages the receiving end of the packets.
*
* There is one such structure for each RTP session (audio/video/...).
* We get the RTP/RTCP packets and stuff them into the session manager.
*/
struct _GstRTPDecSession
{
/* session id */
gint id;
/* the parent bin */
GstRTPDec *dec;
gboolean active;
/* we only support one ssrc and one pt */
guint32 ssrc;
guint8 pt;
GstCaps *caps;
/* the pads of the session */
GstPad *recv_rtp_sink;
GstPad *recv_rtp_src;
GstPad *recv_rtcp_sink;
GstPad *rtcp_src;
};
/* find a session with the given id */
static GstRTPDecSession *
find_session_by_id (GstRTPDec * rtpdec, gint id)
{
GSList *walk;
for (walk = rtpdec->sessions; walk; walk = g_slist_next (walk)) {
GstRTPDecSession *sess = (GstRTPDecSession *) walk->data;
if (sess->id == id)
return sess;
}
return NULL;
}
/* create a session with the given id */
static GstRTPDecSession *
create_session (GstRTPDec * rtpdec, gint id)
{
GstRTPDecSession *sess;
sess = g_new0 (GstRTPDecSession, 1);
sess->id = id;
sess->dec = rtpdec;
rtpdec->sessions = g_slist_prepend (rtpdec->sessions, sess);
return sess;
}
static void
free_session (GstRTPDecSession * session)
{
g_free (session);
}
/*static guint gst_rtp_dec_signals[LAST_SIGNAL] = { 0 };*/
GST_BOILERPLATE (GstRTPDec, gst_rtp_dec, GstElement, GST_TYPE_ELEMENT);
static void
gst_rtp_dec_base_init (gpointer klass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
/* sink pads */
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&gst_rtp_dec_recv_rtp_sink_template));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&gst_rtp_dec_recv_rtcp_sink_template));
/* src pads */
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&gst_rtp_dec_recv_rtp_src_template));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&gst_rtp_dec_rtcp_src_template));
gst_element_class_set_details (element_class, &rtpdec_details);
}
static void
gst_rtp_dec_class_init (GstRTPDecClass * g_class)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
GstRTPDecClass *klass;
klass = (GstRTPDecClass *) g_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gobject_class->finalize = gst_rtp_dec_finalize;
gobject_class->set_property = gst_rtp_dec_set_property;
gobject_class->get_property = gst_rtp_dec_get_property;
gstelement_class->provide_clock =
GST_DEBUG_FUNCPTR (gst_rtp_dec_provide_clock);
gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_rtp_dec_change_state);
gstelement_class->request_new_pad =
GST_DEBUG_FUNCPTR (gst_rtp_dec_request_new_pad);
gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_rtp_dec_release_pad);
GST_DEBUG_CATEGORY_INIT (rtpdec_debug, "rtpdec", 0, "RTP decoder");
}
static void
gst_rtp_dec_init (GstRTPDec * rtpdec, GstRTPDecClass * klass)
{
rtpdec->provided_clock = gst_system_clock_obtain ();
}
static void
gst_rtp_dec_finalize (GObject * object)
{
GstRTPDec *rtpdec;
rtpdec = GST_RTP_DEC (object);
g_slist_foreach (rtpdec->sessions, (GFunc) free_session, NULL);
g_slist_free (rtpdec->sessions);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static gboolean
gst_rtp_dec_query_src (GstPad * pad, GstQuery * query)
{
GstRTPDec *rtpdec;
gboolean res;
rtpdec = GST_RTP_DEC (GST_PAD_PARENT (pad));
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_LATENCY:
{
/* we pretend to be live with a 3 second latency */
gst_query_set_latency (query, TRUE, 3 * GST_SECOND, -1);
res = TRUE;
break;
}
default:
res = gst_pad_query_default (pad, query);
break;
}
return res;
}
static GstFlowReturn
gst_rtp_dec_chain_rtp (GstPad * pad, GstBuffer * buffer)
{
GstFlowReturn res;
GstRTPDec *rtpdec;
GstRTPDecSession *session;
guint32 ssrc;
guint8 pt;
rtpdec = GST_RTP_DEC (GST_PAD_PARENT (pad));
GST_DEBUG_OBJECT (rtpdec, "got rtp packet");
if (!gst_rtp_buffer_validate (buffer))
goto bad_packet;
ssrc = gst_rtp_buffer_get_ssrc (buffer);
pt = gst_rtp_buffer_get_payload_type (buffer);
GST_DEBUG_OBJECT (rtpdec, "SSRC %d, PT %d", ssrc, pt);
/* find session */
session = gst_pad_get_element_private (pad);
/* see if we have the pad */
if (!session->active) {
GstPadTemplate *templ;
GstElementClass *klass;
gchar *name;
GST_DEBUG_OBJECT (rtpdec, "creating stream");
session->ssrc = ssrc;
session->pt = pt;
name = g_strdup_printf ("recv_rtp_src_%d_%u_%d", session->id, ssrc, pt);
klass = GST_ELEMENT_GET_CLASS (rtpdec);
templ = gst_element_class_get_pad_template (klass, "recv_rtp_src_%d_%d_%d");
session->recv_rtp_src = gst_pad_new_from_template (templ, name);
g_free (name);
gst_pad_set_element_private (session->recv_rtp_src, session);
gst_pad_set_query_function (session->recv_rtp_src, gst_rtp_dec_query_src);
gst_pad_set_active (session->recv_rtp_src, TRUE);
gst_element_add_pad (GST_ELEMENT_CAST (rtpdec), session->recv_rtp_src);
session->active = TRUE;
}
res = gst_pad_push (session->recv_rtp_src, buffer);
return res;
bad_packet:
{
GST_ELEMENT_WARNING (rtpdec, STREAM, DECODE, (NULL),
("RTP packet did not validate, dropping"));
return GST_FLOW_OK;
}
}
static GstFlowReturn
gst_rtp_dec_chain_rtcp (GstPad * pad, GstBuffer * buffer)
{
GstRTPDec *src;
#ifdef HAVE_RTCP
gboolean valid;
GstRTCPPacket packet;
gboolean more;
#endif
src = GST_RTP_DEC (GST_PAD_PARENT (pad));
GST_DEBUG_OBJECT (src, "got rtcp packet");
#ifdef HAVE_RTCP
valid = gst_rtcp_buffer_validate (buffer);
if (!valid)
goto bad_packet;
/* position on first packet */
more = gst_rtcp_buffer_get_first_packet (buffer, &packet);
while (more) {
switch (gst_rtcp_packet_get_type (&packet)) {
case GST_RTCP_TYPE_SR:
{
guint32 ssrc, rtptime, packet_count, octet_count;
guint64 ntptime;
guint count, i;
gst_rtcp_packet_sr_get_sender_info (&packet, &ssrc, &ntptime, &rtptime,
&packet_count, &octet_count);
GST_DEBUG_OBJECT (src,
"got SR packet: SSRC %08x, NTP %" G_GUINT64_FORMAT
", RTP %u, PC %u, OC %u", ssrc, ntptime, rtptime, packet_count,
octet_count);
count = gst_rtcp_packet_get_rb_count (&packet);
for (i = 0; i < count; i++) {
guint32 ssrc, exthighestseq, jitter, lsr, dlsr;
guint8 fractionlost;
gint32 packetslost;
gst_rtcp_packet_get_rb (&packet, i, &ssrc, &fractionlost,
&packetslost, &exthighestseq, &jitter, &lsr, &dlsr);
GST_DEBUG_OBJECT (src, "got RB packet %d: SSRC %08x, FL %u"
", PL %u, HS %u, JITTER %u, LSR %u, DLSR %u", ssrc, fractionlost,
packetslost, exthighestseq, jitter, lsr, dlsr);
}
break;
}
case GST_RTCP_TYPE_RR:
{
guint32 ssrc;
guint count, i;
ssrc = gst_rtcp_packet_rr_get_ssrc (&packet);
GST_DEBUG_OBJECT (src, "got RR packet: SSRC %08x", ssrc);
count = gst_rtcp_packet_get_rb_count (&packet);
for (i = 0; i < count; i++) {
guint32 ssrc, exthighestseq, jitter, lsr, dlsr;
guint8 fractionlost;
gint32 packetslost;
gst_rtcp_packet_get_rb (&packet, i, &ssrc, &fractionlost,
&packetslost, &exthighestseq, &jitter, &lsr, &dlsr);
GST_DEBUG_OBJECT (src, "got RB packet %d: SSRC %08x, FL %u"
", PL %u, HS %u, JITTER %u, LSR %u, DLSR %u", ssrc, fractionlost,
packetslost, exthighestseq, jitter, lsr, dlsr);
}
break;
}
case GST_RTCP_TYPE_SDES:
{
guint chunks, i, j;
gboolean more_chunks, more_items;
chunks = gst_rtcp_packet_sdes_get_chunk_count (&packet);
GST_DEBUG_OBJECT (src, "got SDES packet with %d chunks", chunks);
more_chunks = gst_rtcp_packet_sdes_first_chunk (&packet);
i = 0;
while (more_chunks) {
guint32 ssrc;
ssrc = gst_rtcp_packet_sdes_get_ssrc (&packet);
GST_DEBUG_OBJECT (src, "chunk %d, SSRC %08x", i, ssrc);
more_items = gst_rtcp_packet_sdes_first_item (&packet);
j = 0;
while (more_items) {
GstRTCPSDESType type;
guint8 len;
gchar *data;
gst_rtcp_packet_sdes_get_item (&packet, &type, &len, &data);
GST_DEBUG_OBJECT (src, "item %d, type %d, len %d, data %s", j,
type, len, data);
more_items = gst_rtcp_packet_sdes_next_item (&packet);
j++;
}
more_chunks = gst_rtcp_packet_sdes_next_chunk (&packet);
i++;
}
break;
}
case GST_RTCP_TYPE_BYE:
{
guint count, i;
gchar *reason;
reason = gst_rtcp_packet_bye_get_reason (&packet);
GST_DEBUG_OBJECT (src, "got BYE packet (reason: %s)",
GST_STR_NULL (reason));
g_free (reason);
count = gst_rtcp_packet_bye_get_ssrc_count (&packet);
for (i = 0; i < count; i++) {
guint32 ssrc;
ssrc = gst_rtcp_packet_bye_get_nth_ssrc (&packet, i);
GST_DEBUG_OBJECT (src, "SSRC: %08x", ssrc);
}
break;
}
case GST_RTCP_TYPE_APP:
GST_DEBUG_OBJECT (src, "got APP packet");
break;
default:
GST_WARNING_OBJECT (src, "got unknown RTCP packet");
break;
}
more = gst_rtcp_packet_move_to_next (&packet);
}
gst_buffer_unref (buffer);
return GST_FLOW_OK;
bad_packet:
{
GST_WARNING_OBJECT (src, "got invalid RTCP packet");
return GST_FLOW_OK;
}
#else
return GST_FLOW_OK;
#endif
}
static void
gst_rtp_dec_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstRTPDec *src;
src = GST_RTP_DEC (object);
switch (prop_id) {
default:
break;
}
}
static void
gst_rtp_dec_get_property (GObject * object, guint prop_id, GValue * value,
GParamSpec * pspec)
{
GstRTPDec *src;
src = GST_RTP_DEC (object);
switch (prop_id) {
default:
break;
}
}
static GstClock *
gst_rtp_dec_provide_clock (GstElement * element)
{
GstRTPDec *rtpdec;
rtpdec = GST_RTP_DEC (element);
return GST_CLOCK_CAST (gst_object_ref (rtpdec->provided_clock));
}
static GstStateChangeReturn
gst_rtp_dec_change_state (GstElement * element, GstStateChange transition)
{
GstStateChangeReturn ret;
GstRTPDec *rtpdec;
rtpdec = GST_RTP_DEC (element);
switch (transition) {
default:
break;
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
switch (transition) {
case GST_STATE_CHANGE_READY_TO_PAUSED:
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
/* we're NO_PREROLL when going to PAUSED */
ret = GST_STATE_CHANGE_NO_PREROLL;
break;
default:
break;
}
return ret;
}
/* Create a pad for receiving RTP for the session in @name
*/
static GstPad *
create_recv_rtp (GstRTPDec * rtpdec, GstPadTemplate * templ, const gchar * name)
{
guint sessid;
GstRTPDecSession *session;
/* first get the session number */
if (name == NULL || sscanf (name, "recv_rtp_sink_%d", &sessid) != 1)
goto no_name;
GST_DEBUG_OBJECT (rtpdec, "finding session %d", sessid);
/* get or create session */
session = find_session_by_id (rtpdec, sessid);
if (!session) {
GST_DEBUG_OBJECT (rtpdec, "creating session %d", sessid);
/* create session now */
session = create_session (rtpdec, sessid);
if (session == NULL)
goto create_error;
}
/* check if pad was requested */
if (session->recv_rtp_sink != NULL)
goto existed;
GST_DEBUG_OBJECT (rtpdec, "getting RTP sink pad");
session->recv_rtp_sink = gst_pad_new_from_template (templ, name);
gst_pad_set_element_private (session->recv_rtp_sink, session);
gst_pad_set_chain_function (session->recv_rtp_sink, gst_rtp_dec_chain_rtp);
gst_pad_set_active (session->recv_rtp_sink, TRUE);
gst_element_add_pad (GST_ELEMENT_CAST (rtpdec), session->recv_rtp_sink);
return session->recv_rtp_sink;
/* ERRORS */
no_name:
{
g_warning ("rtpdec: invalid name given");
return NULL;
}
create_error:
{
/* create_session already warned */
return NULL;
}
existed:
{
g_warning ("rtpdec: recv_rtp pad already requested for session %d", sessid);
return NULL;
}
}
/* Create a pad for receiving RTCP for the session in @name
*/
static GstPad *
create_recv_rtcp (GstRTPDec * rtpdec, GstPadTemplate * templ,
const gchar * name)
{
guint sessid;
GstRTPDecSession *session;
/* first get the session number */
if (name == NULL || sscanf (name, "recv_rtcp_sink_%d", &sessid) != 1)
goto no_name;
GST_DEBUG_OBJECT (rtpdec, "finding session %d", sessid);
/* get the session, it must exist or we error */
session = find_session_by_id (rtpdec, sessid);
if (!session)
goto no_session;
/* check if pad was requested */
if (session->recv_rtcp_sink != NULL)
goto existed;
GST_DEBUG_OBJECT (rtpdec, "getting RTCP sink pad");
session->recv_rtcp_sink = gst_pad_new_from_template (templ, name);
gst_pad_set_element_private (session->recv_rtp_sink, session);
gst_pad_set_chain_function (session->recv_rtcp_sink, gst_rtp_dec_chain_rtcp);
gst_pad_set_active (session->recv_rtcp_sink, TRUE);
gst_element_add_pad (GST_ELEMENT_CAST (rtpdec), session->recv_rtcp_sink);
return session->recv_rtcp_sink;
/* ERRORS */
no_name:
{
g_warning ("rtpdec: invalid name given");
return NULL;
}
no_session:
{
g_warning ("rtpdec: no session with id %d", sessid);
return NULL;
}
existed:
{
g_warning ("rtpdec: recv_rtcp pad already requested for session %d",
sessid);
return NULL;
}
}
/* Create a pad for sending RTCP for the session in @name
*/
static GstPad *
create_rtcp (GstRTPDec * rtpdec, GstPadTemplate * templ, const gchar * name)
{
guint sessid;
GstRTPDecSession *session;
/* first get the session number */
if (name == NULL || sscanf (name, "rtcp_src_%d", &sessid) != 1)
goto no_name;
/* get or create session */
session = find_session_by_id (rtpdec, sessid);
if (!session)
goto no_session;
/* check if pad was requested */
if (session->rtcp_src != NULL)
goto existed;
session->rtcp_src = gst_pad_new_from_template (templ, name);
gst_pad_set_active (session->rtcp_src, TRUE);
gst_element_add_pad (GST_ELEMENT_CAST (rtpdec), session->rtcp_src);
return session->rtcp_src;
/* ERRORS */
no_name:
{
g_warning ("rtpdec: invalid name given");
return NULL;
}
no_session:
{
g_warning ("rtpdec: session with id %d does not exist", sessid);
return NULL;
}
existed:
{
g_warning ("rtpdec: rtcp_src pad already requested for session %d", sessid);
return NULL;
}
}
/*
*/
static GstPad *
gst_rtp_dec_request_new_pad (GstElement * element,
GstPadTemplate * templ, const gchar * name)
{
GstRTPDec *rtpdec;
GstElementClass *klass;
GstPad *result;
g_return_val_if_fail (templ != NULL, NULL);
g_return_val_if_fail (GST_IS_RTP_DEC (element), NULL);
rtpdec = GST_RTP_DEC (element);
klass = GST_ELEMENT_GET_CLASS (element);
/* figure out the template */
if (templ == gst_element_class_get_pad_template (klass, "recv_rtp_sink_%d")) {
result = create_recv_rtp (rtpdec, templ, name);
} else if (templ == gst_element_class_get_pad_template (klass,
"recv_rtcp_sink_%d")) {
result = create_recv_rtcp (rtpdec, templ, name);
} else if (templ == gst_element_class_get_pad_template (klass, "rtcp_src_%d")) {
result = create_rtcp (rtpdec, templ, name);
} else
goto wrong_template;
return result;
/* ERRORS */
wrong_template:
{
g_warning ("rtpdec: this is not our template");
return NULL;
}
}
static void
gst_rtp_dec_release_pad (GstElement * element, GstPad * pad)
{
}