diff --git a/ChangeLog b/ChangeLog index 5fa4c3c7c6..af8c0dcc21 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,38 @@ +2007-04-06 Wim Taymans + + * 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 * gst/rtp/gstrtpmp4adepay.c: (gst_rtp_mp4a_depay_class_init), diff --git a/gst/rtsp/Makefile.am b/gst/rtsp/Makefile.am index 8f0049c779..86c144b4ee 100644 --- a/gst/rtsp/Makefile.am +++ b/gst/rtsp/Makefile.am @@ -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 diff --git a/gst/rtsp/gstrtpdec.c b/gst/rtsp/gstrtpdec.c index 9320f7df8c..ed119a5d75 100644 --- a/gst/rtsp/gstrtpdec.c +++ b/gst/rtsp/gstrtpdec.c @@ -53,6 +53,14 @@ * Last reviewed on 2006-06-20 (0.10.4) */ +/* #define HAVE_RTCP */ + +#include + +#ifdef HAVE_RTCP +#include +#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) +{ +} diff --git a/gst/rtsp/gstrtpdec.h b/gst/rtsp/gstrtpdec.h index 3475f4c4ce..9c8e936511 100644 --- a/gst/rtsp/gstrtpdec.h +++ b/gst/rtsp/gstrtpdec.h @@ -40,39 +40,36 @@ * SOFTWARE. */ -#ifndef __GST_RTPDEC_H__ -#define __GST_RTPDEC_H__ +#ifndef __GST_RTP_DEC_H__ +#define __GST_RTP_DEC_H__ #include 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__ */ diff --git a/gst/rtsp/gstrtsp.c b/gst/rtsp/gstrtsp.c index 311fe713ba..045c03a606 100644 --- a/gst/rtsp/gstrtsp.c +++ b/gst/rtsp/gstrtsp.c @@ -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; diff --git a/gst/rtsp/gstrtspsrc.c b/gst/rtsp/gstrtspsrc.c index 1726cc6029..d64da03314 100644 --- a/gst/rtsp/gstrtspsrc.c +++ b/gst/rtsp/gstrtspsrc.c @@ -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: diff --git a/gst/rtsp/gstrtspsrc.h b/gst/rtsp/gstrtspsrc.h index c3780756f8..d628dffcbc 100644 --- a/gst/rtsp/gstrtspsrc.h +++ b/gst/rtsp/gstrtspsrc.h @@ -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; diff --git a/gst/rtsp/rtsptransport.c b/gst/rtsp/rtsptransport.c index e833080ad5..567df96cef 100644 --- a/gst/rtsp/rtsptransport.c +++ b/gst/rtsp/rtsptransport.c @@ -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; } diff --git a/gst/rtsp/rtsptransport.h b/gst/rtsp/rtsptransport.h index 84c0996ead..76e291058b 100644 --- a/gst/rtsp/rtsptransport.h +++ b/gst/rtsp/rtsptransport.h @@ -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);