mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-26 17:18:15 +00:00
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.
This commit is contained in:
parent
86b40a1c70
commit
f80444aaec
9 changed files with 836 additions and 237 deletions
35
ChangeLog
35
ChangeLog
|
@ -1,3 +1,38 @@
|
|||
2007-04-06 Wim Taymans <wim@fluendo.com>
|
||||
|
||||
* 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-05 Wim Taymans <wim@fluendo.com>
|
||||
|
||||
* gst/rtp/gstrtpmp4adepay.c: (gst_rtp_mp4a_depay_class_init),
|
||||
|
|
|
@ -11,10 +11,12 @@ libgstrtsp_la_SOURCES = gstrtsp.c gstrtspsrc.c \
|
|||
sdpmessage.c \
|
||||
base64.c
|
||||
|
||||
libgstrtsp_la_CFLAGS = $(GST_CFLAGS)
|
||||
libgstrtsp_la_LIBADD = $(GST_LIBS) $(WIN32_LIBS)
|
||||
libgstrtsp_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS)
|
||||
libgstrtsp_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) \
|
||||
-lgstrtp-@GST_MAJORMINOR@ $(GST_LIBS) $(WIN32_LIBS)
|
||||
libgstrtsp_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
||||
|
||||
|
||||
check_PROGRAMS = test
|
||||
|
||||
test_SOURCES = test.c rtspdefs.c rtspurl.c rtspconnection.c rtspmessage.c rtsptransport.c sdpmessage.c base64.c
|
||||
|
|
|
@ -53,6 +53,14 @@
|
|||
* 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);
|
||||
|
@ -68,89 +76,144 @@ GST_ELEMENT_DETAILS ("RTP Decoder",
|
|||
/* GstRTPDec signals and args */
|
||||
enum
|
||||
{
|
||||
/* FILL ME */
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
ARG_0,
|
||||
ARG_SKIP
|
||||
/* FILL ME */
|
||||
PROP_0,
|
||||
};
|
||||
|
||||
static GstStaticPadTemplate gst_rtpdec_src_rtp_template =
|
||||
GST_STATIC_PAD_TEMPLATE ("srcrtp",
|
||||
GST_PAD_SRC,
|
||||
GST_PAD_ALWAYS,
|
||||
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_rtpdec_src_rtcp_template =
|
||||
GST_STATIC_PAD_TEMPLATE ("srcrtcp",
|
||||
GST_PAD_SRC,
|
||||
GST_PAD_ALWAYS,
|
||||
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_rtpdec_sink_rtp_template =
|
||||
GST_STATIC_PAD_TEMPLATE ("sinkrtp",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
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_rtpdec_sink_rtcp_template =
|
||||
GST_STATIC_PAD_TEMPLATE ("sinkrtcp",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
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_rtpdec_class_init (gpointer g_class);
|
||||
static void gst_rtpdec_init (GstRTPDec * rtpdec);
|
||||
|
||||
static GstCaps *gst_rtpdec_getcaps (GstPad * pad);
|
||||
static GstFlowReturn gst_rtpdec_chain_rtp (GstPad * pad, GstBuffer * buffer);
|
||||
static GstFlowReturn gst_rtpdec_chain_rtcp (GstPad * pad, GstBuffer * buffer);
|
||||
|
||||
static void gst_rtpdec_set_property (GObject * object,
|
||||
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_rtpdec_get_property (GObject * object,
|
||||
static void gst_rtp_dec_get_property (GObject * object,
|
||||
guint prop_id, GValue * value, GParamSpec * pspec);
|
||||
|
||||
static GstStateChangeReturn gst_rtpdec_change_state (GstElement * element,
|
||||
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 GstElementClass *parent_class = NULL;
|
||||
static GstFlowReturn gst_rtp_dec_chain_rtp (GstPad * pad, GstBuffer * buffer);
|
||||
static GstFlowReturn gst_rtp_dec_chain_rtcp (GstPad * pad, GstBuffer * buffer);
|
||||
|
||||
/*static guint gst_rtpdec_signals[LAST_SIGNAL] = { 0 };*/
|
||||
|
||||
GType
|
||||
gst_rtpdec_get_type (void)
|
||||
/* 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
|
||||
{
|
||||
static GType rtpdec_type = 0;
|
||||
/* session id */
|
||||
gint id;
|
||||
/* the parent bin */
|
||||
GstRTPDec *dec;
|
||||
|
||||
if (!rtpdec_type) {
|
||||
static const GTypeInfo rtpdec_info = {
|
||||
sizeof (GstRTPDecClass), NULL,
|
||||
NULL,
|
||||
(GClassInitFunc) gst_rtpdec_class_init,
|
||||
NULL,
|
||||
NULL,
|
||||
sizeof (GstRTPDec),
|
||||
0,
|
||||
(GInstanceInitFunc) gst_rtpdec_init,
|
||||
};
|
||||
gboolean active;
|
||||
/* we only support one ssrc and one pt */
|
||||
guint32 ssrc;
|
||||
guint8 pt;
|
||||
GstCaps *caps;
|
||||
|
||||
rtpdec_type =
|
||||
g_type_register_static (GST_TYPE_ELEMENT, "GstRTPDec", &rtpdec_info, 0);
|
||||
/* 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 rtpdec_type;
|
||||
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
|
||||
gst_rtpdec_class_init (gpointer g_class)
|
||||
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;
|
||||
|
@ -160,157 +223,324 @@ gst_rtpdec_class_init (gpointer g_class)
|
|||
gobject_class = (GObjectClass *) klass;
|
||||
gstelement_class = (GstElementClass *) klass;
|
||||
|
||||
gst_element_class_add_pad_template (gstelement_class,
|
||||
gst_static_pad_template_get (&gst_rtpdec_src_rtp_template));
|
||||
gst_element_class_add_pad_template (gstelement_class,
|
||||
gst_static_pad_template_get (&gst_rtpdec_src_rtcp_template));
|
||||
gst_element_class_add_pad_template (gstelement_class,
|
||||
gst_static_pad_template_get (&gst_rtpdec_sink_rtp_template));
|
||||
gst_element_class_add_pad_template (gstelement_class,
|
||||
gst_static_pad_template_get (&gst_rtpdec_sink_rtcp_template));
|
||||
gst_element_class_set_details (gstelement_class, &rtpdec_details);
|
||||
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;
|
||||
|
||||
gobject_class->set_property = gst_rtpdec_set_property;
|
||||
gobject_class->get_property = gst_rtpdec_get_property;
|
||||
|
||||
/* FIXME, this is unused and probably copied from somewhere */
|
||||
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SKIP,
|
||||
g_param_spec_int ("skip", "Skip", "skip (unused)", G_MININT, G_MAXINT, 0,
|
||||
G_PARAM_READWRITE));
|
||||
|
||||
parent_class = g_type_class_peek_parent (klass);
|
||||
|
||||
gstelement_class->change_state = gst_rtpdec_change_state;
|
||||
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_rtpdec_init (GstRTPDec * rtpdec)
|
||||
gst_rtp_dec_init (GstRTPDec * rtpdec, GstRTPDecClass * klass)
|
||||
{
|
||||
/* the input rtp pad */
|
||||
rtpdec->sink_rtp =
|
||||
gst_pad_new_from_static_template (&gst_rtpdec_sink_rtp_template,
|
||||
"sinkrtp");
|
||||
gst_pad_set_getcaps_function (rtpdec->sink_rtp, gst_rtpdec_getcaps);
|
||||
gst_pad_set_chain_function (rtpdec->sink_rtp, gst_rtpdec_chain_rtp);
|
||||
gst_element_add_pad (GST_ELEMENT (rtpdec), rtpdec->sink_rtp);
|
||||
|
||||
/* the input rtcp pad */
|
||||
rtpdec->sink_rtcp =
|
||||
gst_pad_new_from_static_template (&gst_rtpdec_sink_rtcp_template,
|
||||
"sinkrtcp");
|
||||
gst_pad_set_chain_function (rtpdec->sink_rtcp, gst_rtpdec_chain_rtcp);
|
||||
gst_element_add_pad (GST_ELEMENT (rtpdec), rtpdec->sink_rtcp);
|
||||
|
||||
/* the output rtp pad */
|
||||
rtpdec->src_rtp =
|
||||
gst_pad_new_from_static_template (&gst_rtpdec_src_rtp_template, "srcrtp");
|
||||
gst_pad_set_getcaps_function (rtpdec->src_rtp, gst_rtpdec_getcaps);
|
||||
gst_element_add_pad (GST_ELEMENT (rtpdec), rtpdec->src_rtp);
|
||||
|
||||
/* the output rtcp pad */
|
||||
rtpdec->src_rtcp =
|
||||
gst_pad_new_from_static_template (&gst_rtpdec_src_rtcp_template,
|
||||
"srcrtcp");
|
||||
gst_element_add_pad (GST_ELEMENT (rtpdec), rtpdec->src_rtcp);
|
||||
}
|
||||
|
||||
static GstCaps *
|
||||
gst_rtpdec_getcaps (GstPad * pad)
|
||||
{
|
||||
GstRTPDec *src;
|
||||
GstPad *other;
|
||||
GstCaps *caps;
|
||||
const GstCaps *templ;
|
||||
|
||||
src = GST_RTPDEC (GST_PAD_PARENT (pad));
|
||||
|
||||
other = (pad == src->src_rtp ? src->sink_rtp : src->src_rtp);
|
||||
|
||||
caps = gst_pad_peer_get_caps (other);
|
||||
|
||||
templ = gst_pad_get_pad_template_caps (pad);
|
||||
if (caps == NULL) {
|
||||
GST_DEBUG_OBJECT (src, "copy template");
|
||||
caps = gst_caps_copy (templ);
|
||||
} else {
|
||||
GstCaps *intersect;
|
||||
|
||||
GST_DEBUG_OBJECT (src, "intersect with template");
|
||||
|
||||
intersect = gst_caps_intersect (caps, templ);
|
||||
gst_caps_unref (caps);
|
||||
|
||||
caps = intersect;
|
||||
}
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_rtpdec_chain_rtp (GstPad * pad, GstBuffer * buffer)
|
||||
{
|
||||
GstRTPDec *src;
|
||||
|
||||
src = GST_RTPDEC (GST_PAD_PARENT (pad));
|
||||
|
||||
GST_DEBUG_OBJECT (src, "got rtp packet");
|
||||
return gst_pad_push (src->src_rtp, buffer);
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_rtpdec_chain_rtcp (GstPad * pad, GstBuffer * buffer)
|
||||
{
|
||||
GstRTPDec *src;
|
||||
|
||||
src = GST_RTPDEC (GST_PAD_PARENT (pad));
|
||||
|
||||
GST_DEBUG_OBJECT (src, "got rtcp packet");
|
||||
|
||||
gst_buffer_unref (buffer);
|
||||
return GST_FLOW_OK;
|
||||
rtpdec->provided_clock = gst_system_clock_obtain ();
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtpdec_set_property (GObject * object, guint prop_id,
|
||||
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_RTPDEC (object);
|
||||
src = GST_RTP_DEC (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case ARG_SKIP:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtpdec_get_property (GObject * object, guint prop_id, GValue * value,
|
||||
gst_rtp_dec_get_property (GObject * object, guint prop_id, GValue * value,
|
||||
GParamSpec * pspec)
|
||||
{
|
||||
GstRTPDec *src;
|
||||
|
||||
src = GST_RTPDEC (object);
|
||||
src = GST_RTP_DEC (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case ARG_SKIP:
|
||||
break;
|
||||
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_rtpdec_change_state (GstElement * element, GstStateChange transition)
|
||||
gst_rtp_dec_change_state (GstElement * element, GstStateChange transition)
|
||||
{
|
||||
GstStateChangeReturn ret;
|
||||
GstRTPDec *rtpdec;
|
||||
|
||||
rtpdec = GST_RTPDEC (element);
|
||||
rtpdec = GST_RTP_DEC (element);
|
||||
|
||||
switch (transition) {
|
||||
default:
|
||||
|
@ -322,6 +552,7 @@ gst_rtpdec_change_state (GstElement * element, GstStateChange 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:
|
||||
|
@ -330,3 +561,198 @@ gst_rtpdec_change_state (GstElement * element, GstStateChange transition)
|
|||
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -40,39 +40,36 @@
|
|||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __GST_RTPDEC_H__
|
||||
#define __GST_RTPDEC_H__
|
||||
#ifndef __GST_RTP_DEC_H__
|
||||
#define __GST_RTP_DEC_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_RTPDEC (gst_rtpdec_get_type())
|
||||
#define GST_IS_RTPDEC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTPDEC))
|
||||
#define GST_IS_RTPDEC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTPDEC))
|
||||
#define GST_RTPDEC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTPDEC, GstRTPDec))
|
||||
#define GST_RTPDEC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTPDEC, GstRTPDecClass))
|
||||
#define GST_TYPE_RTP_DEC (gst_rtp_dec_get_type())
|
||||
#define GST_IS_RTP_DEC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_DEC))
|
||||
#define GST_IS_RTP_DEC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_DEC))
|
||||
#define GST_RTP_DEC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_DEC, GstRTPDec))
|
||||
#define GST_RTP_DEC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_DEC, GstRTPDecClass))
|
||||
|
||||
typedef struct _GstRTPDec GstRTPDec;
|
||||
typedef struct _GstRTPDecClass GstRTPDecClass;
|
||||
typedef struct _GstRTPDecSession GstRTPDecSession;
|
||||
|
||||
struct _GstRTPDec {
|
||||
GstElement element;
|
||||
GstElement element;
|
||||
|
||||
GstPad *sink_rtp;
|
||||
GstPad *sink_rtcp;
|
||||
GstPad *src_rtp;
|
||||
GstPad *src_rtcp;
|
||||
GSList *sessions;
|
||||
GstClock *provided_clock;
|
||||
};
|
||||
|
||||
struct _GstRTPDecClass {
|
||||
GstElementClass parent_class;
|
||||
};
|
||||
|
||||
gboolean gst_rtpdec_plugin_init (GstPlugin * plugin);
|
||||
|
||||
GType gst_rtpdec_get_type(void);
|
||||
GType gst_rtp_dec_get_type(void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_RTPDEC_H__ */
|
||||
#endif /* __GST_RTP_DEC_H__ */
|
||||
|
|
|
@ -54,7 +54,7 @@ plugin_init (GstPlugin * plugin)
|
|||
if (!gst_element_register (plugin, "rtspsrc", GST_RANK_NONE,
|
||||
GST_TYPE_RTSPSRC))
|
||||
return FALSE;
|
||||
if (!gst_element_register (plugin, "rtpdec", GST_RANK_NONE, GST_TYPE_RTPDEC))
|
||||
if (!gst_element_register (plugin, "rtpdec", GST_RANK_NONE, GST_TYPE_RTP_DEC))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
|
|
|
@ -384,6 +384,17 @@ gst_rtspsrc_get_property (GObject * object, guint prop_id, GValue * value,
|
|||
}
|
||||
}
|
||||
|
||||
static gint
|
||||
find_stream_by_id (GstRTSPStream * stream, gconstpointer a)
|
||||
{
|
||||
gint id = GPOINTER_TO_INT (a);
|
||||
|
||||
if (stream->id == id)
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static gint
|
||||
find_stream_by_channel (GstRTSPStream * stream, gconstpointer a)
|
||||
{
|
||||
|
@ -413,6 +424,8 @@ find_stream_by_udpsrc (GstRTSPStream * stream, gconstpointer a)
|
|||
|
||||
if (stream->udpsrc[0] == src)
|
||||
return 0;
|
||||
if (stream->udpsrc[1] == src)
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
@ -541,11 +554,6 @@ gst_rtspsrc_stream_free (GstRTSPSrc * src, GstRTSPStream * stream)
|
|||
stream->udpsrc[i] = NULL;
|
||||
}
|
||||
}
|
||||
if (stream->sess) {
|
||||
gst_element_set_state (stream->sess, GST_STATE_NULL);
|
||||
gst_bin_remove (GST_BIN_CAST (src), stream->sess);
|
||||
stream->sess = NULL;
|
||||
}
|
||||
if (stream->srcpad) {
|
||||
gst_pad_set_active (stream->srcpad, FALSE);
|
||||
if (stream->added) {
|
||||
|
@ -571,6 +579,15 @@ gst_rtspsrc_cleanup (GstRTSPSrc * src)
|
|||
}
|
||||
g_list_free (src->streams);
|
||||
src->streams = NULL;
|
||||
if (src->session) {
|
||||
if (src->session_sig_id) {
|
||||
g_signal_handler_disconnect (src->session, src->session_sig_id);
|
||||
src->session_sig_id = 0;
|
||||
}
|
||||
gst_element_set_state (src->session, GST_STATE_NULL);
|
||||
gst_bin_remove (GST_BIN_CAST (src), src->session);
|
||||
src->session = NULL;
|
||||
}
|
||||
src->numstreams = 0;
|
||||
if (src->props)
|
||||
gst_structure_free (src->props);
|
||||
|
@ -803,9 +820,11 @@ gst_rtspsrc_media_to_caps (gint pt, SDPMedia * media)
|
|||
}
|
||||
/* strip the key of spaces, convert key to lowercase but not the value. */
|
||||
key = g_strstrip (pairs[i]);
|
||||
tmp = g_ascii_strdown (key, -1);
|
||||
gst_structure_set (s, tmp, G_TYPE_STRING, val, NULL);
|
||||
g_free (tmp);
|
||||
if (strlen (key) > 1) {
|
||||
tmp = g_ascii_strdown (key, -1);
|
||||
gst_structure_set (s, tmp, G_TYPE_STRING, val, NULL);
|
||||
g_free (tmp);
|
||||
}
|
||||
}
|
||||
g_strfreev (pairs);
|
||||
}
|
||||
|
@ -998,6 +1017,66 @@ was_ok:
|
|||
}
|
||||
}
|
||||
|
||||
/* this callback is called when the session manager generated a new src pad with
|
||||
* payloaded RTP packets. We simply ghost the pad here. */
|
||||
static void
|
||||
new_session_pad (GstElement * session, GstPad * pad, GstRTSPSrc * src)
|
||||
{
|
||||
gchar *name;
|
||||
GstPadTemplate *template;
|
||||
gint id, ssrc, pt;
|
||||
GList *lstream;
|
||||
GstRTSPStream *stream;
|
||||
|
||||
GST_DEBUG_OBJECT (src, "got new session pad %" GST_PTR_FORMAT, pad);
|
||||
|
||||
/* find stream */
|
||||
name = gst_object_get_name (GST_OBJECT_CAST (pad));
|
||||
if (sscanf (name, "recv_rtp_src_%d_%d_%d", &id, &ssrc, &pt) != 3)
|
||||
goto unknown_stream;
|
||||
|
||||
GST_DEBUG_OBJECT (src, "stream: %u, SSRC %d, PT %d", id, ssrc, pt);
|
||||
|
||||
lstream = g_list_find_custom (src->streams, GINT_TO_POINTER (id),
|
||||
(GCompareFunc) find_stream_by_id);
|
||||
if (lstream == NULL)
|
||||
goto unknown_stream;
|
||||
|
||||
/* get stream */
|
||||
stream = (GstRTSPStream *) lstream->data;
|
||||
|
||||
/* create a new pad we will use to stream to */
|
||||
template = gst_static_pad_template_get (&rtptemplate);
|
||||
stream->srcpad = gst_ghost_pad_new_from_template (name, pad, template);
|
||||
gst_object_unref (template);
|
||||
g_free (name);
|
||||
|
||||
stream->added = TRUE;
|
||||
gst_pad_set_active (stream->srcpad, TRUE);
|
||||
gst_element_add_pad (GST_ELEMENT_CAST (src), stream->srcpad);
|
||||
|
||||
/* check if we added all streams */
|
||||
for (lstream = src->streams; lstream; lstream = g_list_next (lstream)) {
|
||||
stream = (GstRTSPStream *) lstream->data;
|
||||
if (!stream->added)
|
||||
goto done;
|
||||
}
|
||||
/* when we get here, all stream are added and we can fire the no-more-pads
|
||||
* signal. */
|
||||
gst_element_no_more_pads (GST_ELEMENT_CAST (src));
|
||||
|
||||
done:
|
||||
return;
|
||||
|
||||
/* ERRORS */
|
||||
unknown_stream:
|
||||
{
|
||||
GST_DEBUG_OBJECT (src, "ignoring unknown stream");
|
||||
g_free (name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* sets up all elements needed for streaming over the specified transport.
|
||||
* Does not yet expose the element pads, this will be done when there is actuall
|
||||
* dataflow detected, which might never happen when UDP is blocked in a
|
||||
|
@ -1033,27 +1112,45 @@ gst_rtspsrc_stream_configure_transport (GstRTSPStream * stream,
|
|||
gst_structure_set_name (s, mime);
|
||||
|
||||
/* find a manager */
|
||||
if ((res = rtsp_transport_get_manager (transport->trans, &manager)) < 0)
|
||||
if ((res = rtsp_transport_get_manager (transport->trans, &manager, 0)) < 0)
|
||||
goto no_manager;
|
||||
|
||||
if (manager) {
|
||||
GST_DEBUG_OBJECT (src, "using manager %s", manager);
|
||||
/* FIXME, the session manager needs to be shared with all the streams */
|
||||
if (!(stream->sess = gst_element_factory_make (manager, NULL)))
|
||||
goto no_element;
|
||||
|
||||
/* we manage this element */
|
||||
gst_bin_add (GST_BIN_CAST (src), stream->sess);
|
||||
/* configure the manager */
|
||||
if (src->session == NULL) {
|
||||
if (!(src->session = gst_element_factory_make (manager, NULL))) {
|
||||
/* fallback */
|
||||
if ((res =
|
||||
rtsp_transport_get_manager (transport->trans, &manager, 1)) < 0)
|
||||
goto no_manager;
|
||||
|
||||
ret = gst_element_set_state (stream->sess, GST_STATE_PAUSED);
|
||||
if (ret == GST_STATE_CHANGE_FAILURE)
|
||||
goto start_session_failure;
|
||||
if (!manager)
|
||||
goto use_no_manager;
|
||||
|
||||
/* we stream directly to the manager, FIXME, pad names should not be
|
||||
* hardcoded. */
|
||||
stream->channelpad[0] = gst_element_get_pad (stream->sess, "sinkrtp");
|
||||
stream->channelpad[1] = gst_element_get_pad (stream->sess, "sinkrtcp");
|
||||
if (!(src->session = gst_element_factory_make (manager, NULL)))
|
||||
goto manager_failed;
|
||||
}
|
||||
|
||||
/* we manage this element */
|
||||
gst_bin_add (GST_BIN_CAST (src), src->session);
|
||||
|
||||
ret = gst_element_set_state (src->session, GST_STATE_PAUSED);
|
||||
if (ret == GST_STATE_CHANGE_FAILURE)
|
||||
goto start_session_failure;
|
||||
}
|
||||
|
||||
/* we stream directly to the manager, get some pads. Each RTSP stream goes
|
||||
* into a separate RTP session. */
|
||||
name = g_strdup_printf ("recv_rtp_sink_%d", stream->id);
|
||||
stream->channelpad[0] = gst_element_get_request_pad (src->session, name);
|
||||
g_free (name);
|
||||
name = g_strdup_printf ("recv_rtcp_sink_%d", stream->id);
|
||||
stream->channelpad[1] = gst_element_get_request_pad (src->session, name);
|
||||
g_free (name);
|
||||
}
|
||||
use_no_manager:
|
||||
|
||||
if (transport->lower_transport == RTSP_LOWER_TRANS_TCP) {
|
||||
gint i;
|
||||
|
@ -1093,7 +1190,7 @@ gst_rtspsrc_stream_configure_transport (GstRTSPStream * stream,
|
|||
outpad = gst_object_ref (stream->channelpad[0]);
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (src, "using manager source pad");
|
||||
outpad = gst_element_get_pad (stream->sess, "srcrtp");
|
||||
/* we connected to pad-added signal to get pads from the manager */
|
||||
}
|
||||
} else {
|
||||
/* multicast was selected, create UDP sources and join the multicast
|
||||
|
@ -1168,8 +1265,8 @@ gst_rtspsrc_stream_configure_transport (GstRTSPStream * stream,
|
|||
* the session plugin. */
|
||||
gst_pad_link (outpad, stream->channelpad[0]);
|
||||
gst_object_unref (outpad);
|
||||
/* the real output pad is that of the session manager */
|
||||
outpad = gst_element_get_pad (stream->sess, "srcrtp");
|
||||
outpad = NULL;
|
||||
/* we connected to pad-added signal to get pads from the manager */
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (src, "using UDP src pad as output");
|
||||
}
|
||||
|
@ -1190,6 +1287,13 @@ gst_rtspsrc_stream_configure_transport (GstRTSPStream * stream,
|
|||
}
|
||||
}
|
||||
|
||||
if (src->session && !src->session_sig_id) {
|
||||
GST_DEBUG_OBJECT (src, "connect to pad-added on session manager");
|
||||
src->session_sig_id =
|
||||
g_signal_connect (src->session, "pad-added",
|
||||
(GCallback) new_session_pad, src);
|
||||
}
|
||||
|
||||
if (outpad) {
|
||||
GST_DEBUG_OBJECT (src, "creating ghostpad");
|
||||
|
||||
|
@ -1221,9 +1325,14 @@ no_manager:
|
|||
GST_DEBUG_OBJECT (src, "cannot get a session manager");
|
||||
return FALSE;
|
||||
}
|
||||
manager_failed:
|
||||
{
|
||||
GST_DEBUG_OBJECT (src, "no session manager element %d found", manager);
|
||||
return FALSE;
|
||||
}
|
||||
no_element:
|
||||
{
|
||||
GST_DEBUG_OBJECT (src, "no rtpdec element found");
|
||||
GST_DEBUG_OBJECT (src, "no UDP source element found");
|
||||
return FALSE;
|
||||
}
|
||||
start_session_failure:
|
||||
|
@ -1278,10 +1387,6 @@ gst_rtspsrc_activate_streams (GstRTSPSrc * src)
|
|||
}
|
||||
}
|
||||
|
||||
/* if we got here all was configured. We have dynamic pads so we notify that
|
||||
* we are done */
|
||||
gst_element_no_more_pads (GST_ELEMENT_CAST (src));
|
||||
|
||||
/* unblock all pads */
|
||||
for (walk = src->streams; walk; walk = g_list_next (walk)) {
|
||||
GstRTSPStream *stream = (GstRTSPStream *) walk->data;
|
||||
|
@ -1296,7 +1401,6 @@ gst_rtspsrc_activate_streams (GstRTSPSrc * src)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static GstFlowReturn
|
||||
gst_rtspsrc_combine_flows (GstRTSPSrc * src, GstRTSPStream * stream,
|
||||
GstFlowReturn ret)
|
||||
|
@ -1376,6 +1480,7 @@ gst_rtspsrc_loop_interleaved (GstRTSPSrc * src)
|
|||
GstFlowReturn ret = GST_FLOW_OK;
|
||||
GstCaps *caps = NULL;
|
||||
GstBuffer *buf;
|
||||
gboolean is_rtcp = FALSE;
|
||||
|
||||
do {
|
||||
GST_DEBUG_OBJECT (src, "doing receive");
|
||||
|
@ -1399,6 +1504,7 @@ gst_rtspsrc_loop_interleaved (GstRTSPSrc * src)
|
|||
caps = stream->caps;
|
||||
} else if (channel == stream->channel[1]) {
|
||||
outpad = stream->channelpad[1];
|
||||
is_rtcp = TRUE;
|
||||
}
|
||||
|
||||
/* take a look at the body to figure out what we have */
|
||||
|
@ -1410,6 +1516,7 @@ gst_rtspsrc_loop_interleaved (GstRTSPSrc * src)
|
|||
if (data[1] >= 200 && data[1] <= 204) {
|
||||
/* hmm RTCP message switch to the RTCP pad of the same stream. */
|
||||
outpad = stream->channelpad[1];
|
||||
is_rtcp = TRUE;
|
||||
}
|
||||
|
||||
/* we have no clue what this is, just ignore then. */
|
||||
|
@ -1447,11 +1554,12 @@ gst_rtspsrc_loop_interleaved (GstRTSPSrc * src)
|
|||
else
|
||||
ret = gst_pad_push (outpad, buf);
|
||||
|
||||
/* combine all stream flows */
|
||||
ret = gst_rtspsrc_combine_flows (src, stream, ret);
|
||||
if (ret != GST_FLOW_OK)
|
||||
goto need_pause;
|
||||
|
||||
if (!is_rtcp) {
|
||||
/* combine all stream flows for the data transport */
|
||||
ret = gst_rtspsrc_combine_flows (src, stream, ret);
|
||||
if (ret != GST_FLOW_OK)
|
||||
goto need_pause;
|
||||
}
|
||||
return;
|
||||
|
||||
/* ERRORS */
|
||||
|
@ -1522,6 +1630,8 @@ gst_rtspsrc_loop_udp (GstRTSPSrc * src)
|
|||
if (src->loop_cmd == CMD_STOP)
|
||||
goto stopping;
|
||||
|
||||
/* FIXME, we should continue reading the TCP socket because the server might
|
||||
* send us requests */
|
||||
while (src->loop_cmd == CMD_WAIT) {
|
||||
GST_DEBUG_OBJECT (src, "waiting");
|
||||
GST_RTSP_LOOP_WAIT (src);
|
||||
|
@ -1530,7 +1640,7 @@ gst_rtspsrc_loop_udp (GstRTSPSrc * src)
|
|||
goto stopping;
|
||||
}
|
||||
if (src->loop_cmd == CMD_RECONNECT) {
|
||||
/* FIXME, when we get here we have to reconnect using tcp */
|
||||
/* when we get here we have to reconnect using tcp */
|
||||
src->loop_cmd = CMD_WAIT;
|
||||
|
||||
/* only restart when the pads were not yet activated, else we were
|
||||
|
@ -1573,6 +1683,12 @@ gst_rtspsrc_loop_udp (GstRTSPSrc * src)
|
|||
if (!gst_rtspsrc_open (src))
|
||||
goto open_failed;
|
||||
|
||||
/* flush previous state */
|
||||
gst_element_post_message (GST_ELEMENT_CAST (src),
|
||||
gst_message_new_async_start (GST_OBJECT_CAST (src), TRUE));
|
||||
gst_element_post_message (GST_ELEMENT_CAST (src),
|
||||
gst_message_new_async_done (GST_OBJECT_CAST (src)));
|
||||
|
||||
/* start playback */
|
||||
if (!gst_rtspsrc_play (src))
|
||||
goto play_failed;
|
||||
|
@ -1784,15 +1900,19 @@ gst_rtspsrc_setup_auth (GstRTSPSrc * src, RTSPMessage * response)
|
|||
return TRUE;
|
||||
|
||||
no_auth_available:
|
||||
/* Output an error indicating that we couldn't connect because there were
|
||||
* no supported authentication protocols */
|
||||
GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
|
||||
("No supported authentication protocol was found"));
|
||||
return FALSE;
|
||||
{
|
||||
/* Output an error indicating that we couldn't connect because there were
|
||||
* no supported authentication protocols */
|
||||
GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
|
||||
("No supported authentication protocol was found"));
|
||||
return FALSE;
|
||||
}
|
||||
no_user_pass:
|
||||
/* We don't fire an error message, we just return FALSE and let the
|
||||
* normal NOT_AUTHORIZED error be propagated */
|
||||
return FALSE;
|
||||
{
|
||||
/* We don't fire an error message, we just return FALSE and let the
|
||||
* normal NOT_AUTHORIZED error be propagated */
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2799,6 +2919,9 @@ gst_rtspsrc_handle_message (GstBin * bin, GstMessage * message)
|
|||
|
||||
udpsrc = GST_MESSAGE_SRC (message);
|
||||
|
||||
GST_DEBUG_OBJECT (rtspsrc, "got error from %s",
|
||||
GST_ELEMENT_NAME (udpsrc));
|
||||
|
||||
lstream = g_list_find_custom (rtspsrc->streams, udpsrc,
|
||||
(GCompareFunc) find_stream_by_udpsrc);
|
||||
if (!lstream)
|
||||
|
@ -2806,13 +2929,19 @@ gst_rtspsrc_handle_message (GstBin * bin, GstMessage * message)
|
|||
|
||||
stream = (GstRTSPStream *) lstream->data;
|
||||
|
||||
/* we ignore the RTCP udpsrc */
|
||||
if (stream->udpsrc[1] == GST_ELEMENT_CAST (udpsrc))
|
||||
goto done;
|
||||
|
||||
/* if we get error messages from the udp sources, that's not a problem as
|
||||
* long as not all of them error out. We also don't really know what the
|
||||
* problem is, the message does not give enough detail... */
|
||||
ret = gst_rtspsrc_combine_flows (rtspsrc, stream, GST_FLOW_NOT_LINKED);
|
||||
GST_DEBUG_OBJECT (rtspsrc, "combined flows: %s", gst_flow_get_name (ret));
|
||||
if (ret != GST_FLOW_OK)
|
||||
goto forward;
|
||||
|
||||
done:
|
||||
gst_message_unref (message);
|
||||
break;
|
||||
|
||||
|
@ -2847,6 +2976,8 @@ gst_rtspsrc_change_state (GstElement * element, GstStateChange transition)
|
|||
break;
|
||||
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
|
||||
rtsp_connection_flush (rtspsrc->connection, FALSE);
|
||||
/* FIXME, the server might send UDP packets before we activate the UDP
|
||||
* ports */
|
||||
gst_rtspsrc_play (rtspsrc);
|
||||
break;
|
||||
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
|
||||
|
|
|
@ -97,9 +97,6 @@ struct _GstRTSPStream {
|
|||
/* our udp sink back to the server */
|
||||
GstElement *udpsink;
|
||||
|
||||
/* the session manager */
|
||||
GstElement *sess;
|
||||
|
||||
/* state */
|
||||
gint pt;
|
||||
gboolean container;
|
||||
|
@ -149,6 +146,10 @@ struct _GstRTSPSrc {
|
|||
/* supported methods */
|
||||
gint methods;
|
||||
|
||||
/* session management */
|
||||
GstElement *session;
|
||||
gulong session_sig_id;
|
||||
|
||||
RTSPConnection *connection;
|
||||
|
||||
RTSPExtensionCtx *extension;
|
||||
|
|
|
@ -45,19 +45,21 @@
|
|||
|
||||
#include "rtsptransport.h"
|
||||
|
||||
#define MAX_MANAGERS 2
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const gchar *name;
|
||||
const RTSPTransMode mode;
|
||||
const gchar *gst_mime;
|
||||
const gchar *manager;
|
||||
const gchar *manager[MAX_MANAGERS];
|
||||
} RTSPTransMap;
|
||||
|
||||
static const RTSPTransMap transports[] = {
|
||||
{"rtp", RTSP_TRANS_RTP, "application/x-rtp", "rtpdec"},
|
||||
{"x-real-rdt", RTSP_TRANS_RDT, "application/x-rdt", NULL},
|
||||
{"x-pn-tng", RTSP_TRANS_RDT, "application/x-rdt", NULL},
|
||||
{NULL, RTSP_TRANS_UNKNOWN, NULL, NULL}
|
||||
{"rtp", RTSP_TRANS_RTP, "application/x-rtp", {"rtpbin", "rtpdec"}},
|
||||
{"x-real-rdt", RTSP_TRANS_RDT, "application/x-rdt", {NULL, NULL}},
|
||||
{"x-pn-tng", RTSP_TRANS_RDT, "application/x-rdt", {NULL, NULL}},
|
||||
{NULL, RTSP_TRANS_UNKNOWN, NULL, {NULL, NULL}}
|
||||
};
|
||||
|
||||
typedef struct
|
||||
|
@ -135,7 +137,8 @@ rtsp_transport_get_mime (RTSPTransMode trans, const gchar ** mime)
|
|||
}
|
||||
|
||||
RTSPResult
|
||||
rtsp_transport_get_manager (RTSPTransMode trans, const gchar ** manager)
|
||||
rtsp_transport_get_manager (RTSPTransMode trans, const gchar ** manager,
|
||||
guint option)
|
||||
{
|
||||
gint i;
|
||||
|
||||
|
@ -144,7 +147,11 @@ rtsp_transport_get_manager (RTSPTransMode trans, const gchar ** manager)
|
|||
for (i = 0; transports[i].name; i++)
|
||||
if (transports[i].mode == trans)
|
||||
break;
|
||||
*manager = transports[i].manager;
|
||||
|
||||
if (option < MAX_MANAGERS)
|
||||
*manager = transports[i].manager[option];
|
||||
else
|
||||
*manager = NULL;
|
||||
|
||||
return RTSP_OK;
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ RTSPResult rtsp_transport_init (RTSPTransport *transport);
|
|||
RTSPResult rtsp_transport_parse (const gchar *str, RTSPTransport *transport);
|
||||
|
||||
RTSPResult rtsp_transport_get_mime (RTSPTransMode trans, const gchar **mime);
|
||||
RTSPResult rtsp_transport_get_manager (RTSPTransMode trans, const gchar **manager);
|
||||
RTSPResult rtsp_transport_get_manager (RTSPTransMode trans, const gchar **manager, guint option);
|
||||
|
||||
RTSPResult rtsp_transport_free (RTSPTransport *transport);
|
||||
|
||||
|
|
Loading…
Reference in a new issue