GstRTSPStream * stream, GstEvent * event);
static gboolean gst_rtspsrc_push_event (GstRTSPSrc * src, GstEvent * event);
+typedef struct
+{
+ guint8 pt;
+ GstCaps *caps;
+} PtMapItem;
+
/* commands we send to out loop to notify it of events */
#define CMD_OPEN (1 << 0)
#define CMD_PLAY (1 << 1)
}
static gint
-find_stream_by_pt (GstRTSPStream * stream, gint * pt)
-{
- if (stream->pt == *pt)
- return 0;
-
- return -1;
-}
-
-static gint
find_stream_by_udpsrc (GstRTSPStream * stream, gconstpointer a)
{
GstElement *src = (GstElement *) a;
}
}
+
+/* m=<media> <UDP port> RTP/AVP <payload>
+ */
static void
gst_rtspsrc_collect_payloads (GstRTSPSrc * src, const GstSDPMessage * sdp,
const GstSDPMedia * media, GstRTSPStream * stream)
{
- const gchar *payload;
+ guint i, len;
+ const gchar *proto;
+
+ /* get proto */
+ proto = gst_sdp_media_get_proto (media);
+ if (proto == NULL)
+ goto no_proto;
+
+ if (g_str_equal (proto, "RTP/AVP"))
+ stream->profile = GST_RTSP_PROFILE_AVP;
+ else if (g_str_equal (proto, "RTP/SAVP"))
+ stream->profile = GST_RTSP_PROFILE_SAVP;
+ else if (g_str_equal (proto, "RTP/AVPF"))
+ stream->profile = GST_RTSP_PROFILE_AVPF;
+ else if (g_str_equal (proto, "RTP/SAVPF"))
+ stream->profile = GST_RTSP_PROFILE_SAVPF;
+ else
+ goto unknown_proto;
+
+ len = gst_sdp_media_formats_len (media);
+ for (i = 0; i < len; i++) {
+ gint pt;
+ GstCaps *caps;
+ GstStructure *s;
+ const gchar *enc;
+ PtMapItem item;
+
+ pt = atoi (gst_sdp_media_get_format (media, i));
+
+ GST_DEBUG_OBJECT (src, " looking at %d pt: %d", i, pt);
- /* we must have a payload. No payload means we cannot create caps */
- /* FIXME, handle multiple formats. The problem here is that we just want to
- * take the first available format that we can handle but in order to do that
- * we need to scan for depayloader plugins. Scanning for payloader plugins is
- * also suboptimal because the user maybe just wants to save the raw stream
- * and then we don't care. */
- if ((payload = gst_sdp_media_get_format (media, 0))) {
- stream->pt = atoi (payload);
/* convert caps */
- stream->caps = gst_rtspsrc_media_to_caps (stream->pt, media);
+ caps = gst_rtspsrc_media_to_caps (pt, media);
+ if (caps == NULL) {
+ GST_WARNING_OBJECT (src, " skipping pt %d without caps", pt);
+ continue;
+ }
+ /* do some tweaks */
+ s = gst_caps_get_structure (caps, 0);
+ if ((enc = gst_structure_get_string (s, "encoding-name"))) {
+ stream->is_real = (strstr (enc, "-REAL") != NULL);
+ if (strcmp (enc, "X-ASF-PF") == 0)
+ stream->container = TRUE;
+ }
GST_DEBUG ("mapping sdp session level attributes to caps");
- gst_rtspsrc_sdp_attributes_to_caps (sdp->attributes, stream->caps);
+ gst_rtspsrc_sdp_attributes_to_caps (sdp->attributes, caps);
GST_DEBUG ("mapping sdp media level attributes to caps");
- gst_rtspsrc_sdp_attributes_to_caps (media->attributes, stream->caps);
+ gst_rtspsrc_sdp_attributes_to_caps (media->attributes, caps);
+ /* the first pt will be the default */
+ if (stream->ptmap->len == 0)
+ stream->default_pt = pt;
+
+ item.pt = pt;
+ item.caps = caps;
+ g_array_append_val (stream->ptmap, item);
+
+#if 0
if (stream->pt >= 96) {
/* If we have a dynamic payload type, see if we have a stream with the
* same payload number. If there is one, they are part of the same
stream->pt);
}
}
+#endif
+ }
+ return;
+
+no_proto:
+ {
+ GST_ERROR_OBJECT (src, "can't find proto in media");
+ return;
+ }
+unknown_proto:
+ {
+ GST_ERROR_OBJECT (src, "unknown proto in media %s", proto);
+ return;
}
}
stream->discont = TRUE;
stream->seqbase = -1;
stream->timebase = -1;
+ stream->profile = GST_RTSP_PROFILE_AVP;
+ stream->ptmap = g_array_new (FALSE, FALSE, sizeof (PtMapItem));
/* collect bandwidth information for this steam. FIXME, configure in the RTP
* session manager to scale RTCP. */
/* collect connection info */
gst_rtspsrc_collect_connections (src, sdp, media, stream);
+ /* make the payload type map */
gst_rtspsrc_collect_payloads (src, sdp, media, stream);
/* collect port number */
control_url = gst_sdp_message_get_attribute_val_n (sdp, "control", 0);
GST_DEBUG_OBJECT (src, "stream %d, (%p)", stream->id, stream);
- GST_DEBUG_OBJECT (src, " pt: %d", stream->pt);
GST_DEBUG_OBJECT (src, " port: %d", stream->port);
GST_DEBUG_OBJECT (src, " container: %d", stream->container);
- GST_DEBUG_OBJECT (src, " caps: %" GST_PTR_FORMAT, stream->caps);
GST_DEBUG_OBJECT (src, " control: %s", GST_STR_NULL (control_url));
if (control_url != NULL) {
GST_DEBUG_OBJECT (src, "free stream %p", stream);
- if (stream->caps)
- gst_caps_unref (stream->caps);
+ g_array_free (stream->ptmap, TRUE);
g_free (stream->destination);
g_free (stream->control_url);
}
}
+static const gchar *
+rtsp_get_attribute_for_pt (const GstSDPMedia * media, const gchar * name,
+ gint pt)
+{
+ guint i;
+
+ for (i = 0;; i++) {
+ const gchar *attr;
+ gint val;
+
+ if ((attr = gst_sdp_media_get_attribute_val_n (media, name, i)) == NULL)
+ break;
+
+ if (sscanf (attr, "%d ", &val) != 1)
+ continue;
+
+ if (val == pt)
+ return attr;
+ }
+ return NULL;
+}
+
/*
* Mapping of caps to and from SDP fields:
*
- * m=<media> <UDP port> RTP/AVP <payload>
* a=rtpmap:<payload> <encoding_name>/<clock_rate>[/<encoding_params>]
* a=fmtp:<payload> <param>[=<value>];...
*/
gboolean ret;
/* get and parse rtpmap */
- if ((rtpmap = gst_sdp_media_get_attribute_val (media, "rtpmap"))) {
- ret = gst_rtspsrc_parse_rtpmap (rtpmap, &payload, &name, &rate, ¶ms);
- if (ret) {
- if (payload != pt) {
- /* we ignore the rtpmap if the payload type is different. */
- g_warning ("rtpmap of wrong payload type, ignoring");
- name = NULL;
- rate = -1;
- params = NULL;
- }
- } else {
- /* if we failed to parse the rtpmap for a dynamic payload type, we have an
- * error */
- if (pt >= 96)
- goto no_rtpmap;
- /* else we can ignore */
- g_warning ("error parsing rtpmap, ignoring");
- }
- } else {
- /* dynamic payloads need rtpmap or we fail */
- if (pt >= 96)
- goto no_rtpmap;
+ rtpmap = rtsp_get_attribute_for_pt (media, "rtpmap", pt);
+
+ /* dynamic payloads need rtpmap or we fail */
+ if (rtpmap == NULL && pt >= 96)
+ goto no_rtpmap;
+
+ ret = gst_rtspsrc_parse_rtpmap (rtpmap, &payload, &name, &rate, ¶ms);
+ if (!ret) {
+ g_warning ("error parsing rtpmap, ignoring");
}
+
/* check if we have a rate, if not, we need to look up the rate from the
* default rates based on the payload types. */
if (rate == -1) {
}
/* parse optional fmtp: field */
- if ((fmtp = gst_sdp_media_get_attribute_val (media, "fmtp"))) {
+ if ((fmtp = rtsp_get_attribute_for_pt (media, "fmtp", pt))) {
gchar *p;
gint payload = 0;
}
static GstCaps *
+stream_get_caps_for_pt (GstRTSPStream * stream, guint pt)
+{
+ guint i, len;
+
+ len = stream->ptmap->len;
+ for (i = 0; i < len; i++) {
+ PtMapItem *item = &g_array_index (stream->ptmap, PtMapItem, i);
+ if (item->pt == pt)
+ return item->caps;
+ }
+ return NULL;
+}
+
+static GstCaps *
request_pt_map (GstElement * manager, guint session, guint pt, GstRTSPSrc * src)
{
GstRTSPStream *stream;
if (!stream)
goto unknown_stream;
- caps = stream->caps;
- if (caps)
+ if ((caps = stream_get_caps_for_pt (stream, pt)))
gst_caps_ref (caps);
GST_RTSP_STATE_UNLOCK (src);
/* configure the manager */
if (src->manager == NULL) {
GObjectClass *klass;
- GstStructure *s;
- const gchar *encoding;
- gboolean need_slave;
if (!(src->manager = gst_element_factory_make (manager, "manager"))) {
/* fallback */
* with such input times, e.g. container ones, most notably ASF */
/* TODO alternatives are having an event that indicates these shifts,
* or having rtsp extensions provide suggestion on buffer mode */
- need_slave = stream->container;
- if (stream->caps && (s = gst_caps_get_structure (stream->caps, 0)) &&
- (encoding = gst_structure_get_string (s, "encoding-name")))
- need_slave = need_slave || (strcmp (encoding, "X-ASF-PF") == 0);
/* valid duration implies not likely live pipeline,
* so slaving in jitterbuffer does not make much sense
* (and might mess things up due to bursts) */
if (GST_CLOCK_TIME_IS_VALID (src->segment.duration) &&
- src->segment.duration && !need_slave) {
+ src->segment.duration && !stream->container) {
src->use_buffering = TRUE;
} else {
src->use_buffering = FALSE;
GstPad *outpad = NULL;
GstPadTemplate *template;
gchar *name;
- GstStructure *s;
const gchar *media_type;
+ guint i, len;
src = stream->parent;
GST_DEBUG_OBJECT (src, "configuring transport for stream %p", stream);
- s = gst_caps_get_structure (stream->caps, 0);
-
/* get the proper media type for this stream now */
if (gst_rtsp_transport_get_media_type (transport, &media_type) < 0)
goto unknown_transport;
/* configure the final media type */
GST_DEBUG_OBJECT (src, "setting media type to %s", media_type);
- gst_structure_set_name (s, media_type);
+
+ len = stream->ptmap->len;
+ for (i = 0; i < len; i++) {
+ GstStructure *s;
+ PtMapItem *item = &g_array_index (stream->ptmap, PtMapItem, i);
+
+ if (item->caps == NULL)
+ continue;
+
+ s = gst_caps_get_structure (item->caps, 0);
+ gst_structure_set_name (s, media_type);
+ }
/* try to get and configure a manager, channelpad[0-1] will be configured with
* the pads for the manager, or NULL when no manager is needed. */
/* if we don't have a session manager, set the caps now. If we have a
* session, we will get a notification of the pad and the caps. */
if (!src->manager) {
+ GstCaps *caps;
+
+ caps = stream_get_caps_for_pt (stream, stream->default_pt);
GST_DEBUG_OBJECT (src, "setting pad caps for stream %p", stream);
- gst_pad_set_caps (stream->srcpad, stream->caps);
+ gst_pad_set_caps (stream->srcpad, caps);
}
/* add the pad */
if (!stream->added) {
for (walk = src->streams; walk; walk = g_list_next (walk)) {
GstRTSPStream *stream = (GstRTSPStream *) walk->data;
- GstCaps *caps;
+ guint j, len;
- if ((caps = stream->caps)) {
- caps = gst_caps_make_writable (caps);
+ len = stream->ptmap->len;
+ for (j = 0; j < len; j++) {
+ GstCaps *caps;
+ PtMapItem *item = &g_array_index (stream->ptmap, PtMapItem, j);
+
+ if (item->caps == NULL)
+ continue;
+
+ caps = gst_caps_make_writable (item->caps);
/* update caps */
if (stream->timebase != -1)
gst_caps_set_simple (caps, "clock-base", G_TYPE_UINT,
gst_caps_set_simple (caps, "play-speed", G_TYPE_DOUBLE, play_speed, NULL);
gst_caps_set_simple (caps, "play-scale", G_TYPE_DOUBLE, play_scale, NULL);
- stream->caps = caps;
+ item->caps = caps;
+ GST_DEBUG_OBJECT (src, "stream %p, pt %d, caps %" GST_PTR_FORMAT, stream,
+ item->pt, caps);
}
- GST_DEBUG_OBJECT (src, "stream %p, caps %" GST_PTR_FORMAT, stream, caps);
}
if (reset_manager && src->manager) {
GST_DEBUG_OBJECT (src, "clear session");
static GstRTSPResult
gst_rtspsrc_create_transports_string (GstRTSPSrc * src,
- GstRTSPLowerTrans protocols, gchar ** transports)
+ GstRTSPLowerTrans protocols, GstRTSPProfile profile, gchar ** transports)
{
GstRTSPResult res;
GString *result;
add_udp_str = FALSE;
/* the default RTSP transports */
- result = g_string_new ("");
+ result = g_string_new ("RTP");
+
+ switch (profile) {
+ case GST_RTSP_PROFILE_AVP:
+ g_string_append (result, "/AVP");
+ break;
+ case GST_RTSP_PROFILE_SAVP:
+ g_string_append (result, "/SAVP");
+ break;
+ case GST_RTSP_PROFILE_AVPF:
+ g_string_append (result, "/AVPF");
+ break;
+ case GST_RTSP_PROFILE_SAVPF:
+ g_string_append (result, "/SAVPF");
+ break;
+ default:
+ break;
+ }
+
if (protocols & GST_RTSP_LOWER_TRANS_UDP) {
GST_DEBUG_OBJECT (src, "adding UDP unicast");
-
- g_string_append (result, "RTP/AVP");
if (add_udp_str)
g_string_append (result, "/UDP");
g_string_append (result, ";unicast;client_port=%%u1-%%u2");
} else if (protocols & GST_RTSP_LOWER_TRANS_UDP_MCAST) {
GST_DEBUG_OBJECT (src, "adding UDP multicast");
-
/* we don't have to allocate any UDP ports yet, if the selected transport
* turns out to be multicast we can create them and join the multicast
* group indicated in the transport reply */
- if (result->len > 0)
- g_string_append (result, ",");
- g_string_append (result, "RTP/AVP");
if (add_udp_str)
g_string_append (result, "/UDP");
g_string_append (result, ";multicast");
} else if (protocols & GST_RTSP_LOWER_TRANS_TCP) {
GST_DEBUG_OBJECT (src, "adding TCP");
- if (result->len > 0)
- g_string_append (result, ",");
- g_string_append (result, "RTP/AVP/TCP;unicast;interleaved=%%i1-%%i2");
+ g_string_append (result, "/TCP;unicast;interleaved=%%i1-%%i2");
}
*transports = g_string_free (result, FALSE);
}
}
-static gboolean
-gst_rtspsrc_stream_is_real_media (GstRTSPStream * stream)
-{
- gboolean res = FALSE;
-
- if (stream->caps) {
- GstStructure *s;
- const gchar *enc = NULL;
-
- s = gst_caps_get_structure (stream->caps, 0);
- if ((enc = gst_structure_get_string (s, "encoding-name"))) {
- res = (strstr (enc, "-REAL") != NULL);
- }
- }
- return res;
-}
-
/* Perform the SETUP request for all the streams.
*
* We ask the server for a specific transport, which initially includes all the
gint retry = 0;
guint mask = 0;
gboolean selected;
+ GstCaps *caps;
stream = (GstRTSPStream *) walk->data;
+ caps = stream_get_caps_for_pt (stream, stream->default_pt);
+ if (caps == NULL) {
+ GST_DEBUG_OBJECT (src, "skipping stream %p, no caps", stream);
+ continue;
+ }
/* see if we need to configure this stream */
- if (!gst_rtsp_ext_list_configure_stream (src->extensions, stream->caps)) {
+ if (!gst_rtsp_ext_list_configure_stream (src->extensions, caps)) {
GST_DEBUG_OBJECT (src, "skipping stream %p, disabled by extension",
stream);
stream->disabled = TRUE;
}
g_signal_emit (src, gst_rtspsrc_signals[SIGNAL_SELECT_STREAM], 0,
- stream->id, stream->caps, &selected);
+ stream->id, caps, &selected);
if (!selected) {
GST_DEBUG_OBJECT (src, "skipping stream %p, disabled by signal", stream);
stream->disabled = TRUE;
stream->disabled = FALSE;
/* merge/overwrite global caps */
- if (stream->caps) {
+ if (caps) {
guint j, num;
GstStructure *s;
- s = gst_caps_get_structure (stream->caps, 0);
+ s = gst_caps_get_structure (caps, 0);
num = gst_structure_n_fields (src->props);
for (j = 0; j < num; j++) {
/* create a string with first transport in line */
transports = NULL;
res = gst_rtspsrc_create_transports_string (src,
- protocols & protocol_masks[mask], &transports);
+ protocols & protocol_masks[mask], stream->profile, &transports);
if (res < 0 || transports == NULL)
goto setup_transport_failed;
* but not without checking for lost cause/extension so we can
* post a nicer/more useful error message later */
if (!unsupported_real)
- unsupported_real = gst_rtspsrc_stream_is_real_media (stream);
+ unsupported_real = stream->is_real;
/* select next available protocol, give up on this stream if none */
mask++;
while (protocol_masks[mask] && !(protocols & protocol_masks[mask]))
static void
clear_rtp_base (GstRTSPSrc * src, GstRTSPStream * stream)
{
+ guint i, len;
+
stream->timebase = -1;
stream->seqbase = -1;
- if (stream->caps) {
+
+ len = stream->ptmap->len;
+ for (i = 0; i < len; i++) {
+ PtMapItem *item = &g_array_index (stream->ptmap, PtMapItem, i);
GstStructure *s;
- stream->caps = gst_caps_make_writable (stream->caps);
- s = gst_caps_get_structure (stream->caps, 0);
+ if (item->caps == NULL)
+ continue;
+
+ item->caps = gst_caps_make_writable (item->caps);
+ s = gst_caps_get_structure (item->caps, 0);
gst_structure_remove_fields (s, "clock-base", "seqnum-base", NULL);
}
}