rtspsrc: add non-aggregate control

Add non-aggregate control.
Separate retrieving thr SDP from parsing and setting up the streaming from the
SDP.
This commit is contained in:
Wim Taymans 2010-06-17 13:06:56 +02:00
parent cf095fb9bc
commit ddc214d322

View file

@ -3104,6 +3104,7 @@ gst_rtspsrc_send_keep_alive (GstRTSPSrc * src)
GstRTSPMessage request = { 0 }; GstRTSPMessage request = { 0 };
GstRTSPResult res; GstRTSPResult res;
GstRTSPMethod method; GstRTSPMethod method;
gchar *control;
GST_DEBUG_OBJECT (src, "creating server keep-alive"); GST_DEBUG_OBJECT (src, "creating server keep-alive");
@ -3113,7 +3114,15 @@ gst_rtspsrc_send_keep_alive (GstRTSPSrc * src)
else else
method = GST_RTSP_OPTIONS; method = GST_RTSP_OPTIONS;
res = gst_rtsp_message_init_request (&request, method, src->req_location); if (src->control)
control = src->control;
else
control = src->req_location;
if (control == NULL)
goto no_control;
res = gst_rtsp_message_init_request (&request, method, control);
if (res < 0) if (res < 0)
goto send_error; goto send_error;
@ -3130,6 +3139,11 @@ gst_rtspsrc_send_keep_alive (GstRTSPSrc * src)
return GST_RTSP_OK; return GST_RTSP_OK;
/* ERRORS */ /* ERRORS */
no_control:
{
GST_WARNING_OBJECT (src, "no control url to send keepalive");
return GST_RTSP_OK;
}
send_error: send_error:
{ {
gchar *str = gst_rtsp_strresult (res); gchar *str = gst_rtsp_strresult (res);
@ -4870,6 +4884,77 @@ gst_rtspsrc_parse_range (GstRTSPSrc * src, const gchar * range,
return TRUE; return TRUE;
} }
static gboolean
gst_rtspsrc_from_sdp (GstRTSPSrc * src, guint8 * data, guint size)
{
GstSDPMessage sdp = { 0 };
gint i, n_streams;
GST_DEBUG_OBJECT (src, "parse SDP...");
gst_sdp_message_init (&sdp);
gst_sdp_message_parse_buffer (data, size, &sdp);
if (src->debug)
gst_sdp_message_dump (&sdp);
gst_rtsp_ext_list_parse_sdp (src->extensions, &sdp, src->props);
/* parse range for duration reporting. */
{
const gchar *range;
for (i = 0;; i++) {
range = gst_sdp_message_get_attribute_val_n (&sdp, "range", i);
if (range == NULL)
break;
/* keep track of the range and configure it in the segment */
if (gst_rtspsrc_parse_range (src, range, &src->segment))
break;
}
}
/* try to find a global control attribute */
{
const gchar *control;
for (i = 0;; i++) {
control = gst_sdp_message_get_attribute_val_n (&sdp, "control", i);
if (control == NULL)
break;
/* only take fully qualified urls */
if (g_str_has_prefix (control, "rtsp://"))
break;
}
g_free (src->control);
src->control = g_strdup (control);
}
/* create streams */
n_streams = gst_sdp_message_medias_len (&sdp);
for (i = 0; i < n_streams; i++) {
gst_rtspsrc_create_stream (src, &sdp, i);
}
src->state = GST_RTSP_STATE_INIT;
/* setup streams */
if (!gst_rtspsrc_setup_streams (src))
goto setup_failed;
src->state = GST_RTSP_STATE_READY;
gst_sdp_message_uninit (&sdp);
return TRUE;
setup_failed:
{
gst_sdp_message_uninit (&sdp);
return FALSE;
}
}
static gboolean static gboolean
gst_rtspsrc_open (GstRTSPSrc * src) gst_rtspsrc_open (GstRTSPSrc * src)
{ {
@ -4878,8 +4963,6 @@ gst_rtspsrc_open (GstRTSPSrc * src)
GstRTSPMessage response = { 0 }; GstRTSPMessage response = { 0 };
guint8 *data; guint8 *data;
guint size; guint size;
gint i, n_streams;
GstSDPMessage sdp = { 0 };
gchar *respcont = NULL; gchar *respcont = NULL;
GstRTSPUrl *url; GstRTSPUrl *url;
@ -4998,66 +5081,12 @@ restart:
if (data == NULL) if (data == NULL)
goto no_describe; goto no_describe;
GST_DEBUG_OBJECT (src, "parse SDP..."); if (!gst_rtspsrc_from_sdp (src, data, size))
gst_sdp_message_init (&sdp); goto sdp_failed;
gst_sdp_message_parse_buffer (data, size, &sdp);
if (src->debug)
gst_sdp_message_dump (&sdp);
gst_rtsp_ext_list_parse_sdp (src->extensions, &sdp, src->props);
/* parse range for duration reporting. */
{
const gchar *range;
for (i = 0;; i++) {
range = gst_sdp_message_get_attribute_val_n (&sdp, "range", i);
if (range == NULL)
break;
/* keep track of the range and configure it in the segment */
if (gst_rtspsrc_parse_range (src, range, &src->segment))
break;
}
}
/* try to find a global control attribute */
g_free (src->control);
src->control = NULL;
{
const gchar *control;
for (i = 0;; i++) {
control = gst_sdp_message_get_attribute_val_n (&sdp, "control", i);
if (control == NULL)
break;
if (g_str_has_prefix (control, "rtsp://")) {
src->control = g_strdup (control);
break;
}
}
}
/* create streams */
n_streams = gst_sdp_message_medias_len (&sdp);
for (i = 0; i < n_streams; i++) {
gst_rtspsrc_create_stream (src, &sdp, i);
}
src->state = GST_RTSP_STATE_INIT;
/* setup streams */
if (!gst_rtspsrc_setup_streams (src))
goto setup_failed;
src->state = GST_RTSP_STATE_READY;
GST_RTSP_STATE_UNLOCK (src);
/* clean up any messages */ /* clean up any messages */
gst_rtsp_message_unset (&request); gst_rtsp_message_unset (&request);
gst_rtsp_message_unset (&response); gst_rtsp_message_unset (&response);
gst_sdp_message_uninit (&sdp);
return TRUE; return TRUE;
@ -5118,7 +5147,7 @@ no_describe:
("Server can not provide an SDP.")); ("Server can not provide an SDP."));
goto cleanup_error; goto cleanup_error;
} }
setup_failed: sdp_failed:
{ {
gst_rtspsrc_close (src); gst_rtspsrc_close (src);
/* error was posted */ /* error was posted */
@ -5135,7 +5164,6 @@ cleanup_error:
GST_RTSP_STATE_UNLOCK (src); GST_RTSP_STATE_UNLOCK (src);
gst_rtsp_message_unset (&request); gst_rtsp_message_unset (&request);
gst_rtsp_message_unset (&response); gst_rtsp_message_unset (&response);
gst_sdp_message_uninit (&sdp);
return FALSE; return FALSE;
} }
} }
@ -5163,6 +5191,7 @@ gst_rtspsrc_close (GstRTSPSrc * src)
GstRTSPMessage request = { 0 }; GstRTSPMessage request = { 0 };
GstRTSPMessage response = { 0 }; GstRTSPMessage response = { 0 };
GstRTSPResult res; GstRTSPResult res;
GList *walk;
gboolean ret = FALSE; gboolean ret = FALSE;
gchar *control; gchar *control;
@ -5214,9 +5243,22 @@ gst_rtspsrc_close (GstRTSPSrc * src)
else else
control = src->req_location; control = src->req_location;
if (src->methods & (GST_RTSP_PLAY | GST_RTSP_TEARDOWN)) { if (!(src->methods & (GST_RTSP_PLAY | GST_RTSP_TEARDOWN)))
goto not_supported;
for (walk = src->streams; walk; walk = g_list_next (walk)) {
GstRTSPStream *stream = (GstRTSPStream *) walk->data;
gchar *setup_url;
/* try aggregate control first but do non-aggregate control otherwise */
if (control)
setup_url = control;
else if ((setup_url = stream->setup_url) == NULL)
continue;
/* do TEARDOWN */ /* do TEARDOWN */
res = gst_rtsp_message_init_request (&request, GST_RTSP_TEARDOWN, control); res =
gst_rtsp_message_init_request (&request, GST_RTSP_TEARDOWN, setup_url);
if (res < 0) if (res < 0)
goto create_request_failed; goto create_request_failed;
@ -5226,9 +5268,10 @@ gst_rtspsrc_close (GstRTSPSrc * src)
/* FIXME, parse result? */ /* FIXME, parse result? */
gst_rtsp_message_unset (&request); gst_rtsp_message_unset (&request);
gst_rtsp_message_unset (&response); gst_rtsp_message_unset (&response);
} else {
GST_DEBUG_OBJECT (src, /* early exit when we did aggregate control */
"TEARDOWN and PLAY not supported, can't do TEARDOWN"); if (control)
break;
} }
close: close:
@ -5267,6 +5310,12 @@ send_error:
ret = FALSE; ret = FALSE;
goto close; goto close;
} }
not_supported:
{
GST_DEBUG_OBJECT (src,
"TEARDOWN and PLAY not supported, can't do TEARDOWN");
goto close;
}
} }
/* RTP-Info is of the format: /* RTP-Info is of the format:
@ -5390,6 +5439,7 @@ gst_rtspsrc_play (GstRTSPSrc * src, GstSegment * segment)
GstRTSPMessage request = { 0 }; GstRTSPMessage request = { 0 };
GstRTSPMessage response = { 0 }; GstRTSPMessage response = { 0 };
GstRTSPResult res; GstRTSPResult res;
GList *walk;
gchar *hval; gchar *hval;
gfloat fval; gfloat fval;
gint hval_idx; gint hval_idx;
@ -5408,12 +5458,6 @@ gst_rtspsrc_play (GstRTSPSrc * src, GstSegment * segment)
if (!src->connection || !src->connected) if (!src->connection || !src->connected)
goto done; goto done;
/* construct a control url */
if (src->control)
control = src->control;
else
control = src->req_location;
/* waiting for connection idle, we were flushing so any attempt at doing data /* waiting for connection idle, we were flushing so any attempt at doing data
* transfer will result in pausing the tasks. */ * transfer will result in pausing the tasks. */
GST_DEBUG_OBJECT (src, "wait for connection idle"); GST_DEBUG_OBJECT (src, "wait for connection idle");
@ -5424,64 +5468,85 @@ gst_rtspsrc_play (GstRTSPSrc * src, GstSegment * segment)
GST_DEBUG_OBJECT (src, "stop connection flush"); GST_DEBUG_OBJECT (src, "stop connection flush");
gst_rtsp_connection_flush (src->connection, FALSE); gst_rtsp_connection_flush (src->connection, FALSE);
/* do play */ /* construct a control url */
res = gst_rtsp_message_init_request (&request, GST_RTSP_PLAY, control); if (src->control)
if (res < 0) control = src->control;
goto create_request_failed; else
control = src->req_location;
if (src->need_range) { for (walk = src->streams; walk; walk = g_list_next (walk)) {
hval = gen_range_header (src, segment); GstRTSPStream *stream = (GstRTSPStream *) walk->data;
gchar *setup_url;
gst_rtsp_message_add_header (&request, GST_RTSP_HDR_RANGE, hval); /* try aggregate control first but do non-aggregate control otherwise */
g_free (hval); if (control)
src->need_range = FALSE; setup_url = control;
else if ((setup_url = stream->setup_url) == NULL)
continue;
/* do play */
res = gst_rtsp_message_init_request (&request, GST_RTSP_PLAY, setup_url);
if (res < 0)
goto create_request_failed;
if (src->need_range) {
hval = gen_range_header (src, segment);
gst_rtsp_message_add_header (&request, GST_RTSP_HDR_RANGE, hval);
g_free (hval);
src->need_range = FALSE;
}
if (segment->rate != 1.0) {
hval = gst_rtspsrc_dup_printf ("%f", segment->rate);
if (src->skip)
gst_rtsp_message_add_header (&request, GST_RTSP_HDR_SCALE, hval);
else
gst_rtsp_message_add_header (&request, GST_RTSP_HDR_SPEED, hval);
g_free (hval);
}
if (gst_rtspsrc_send (src, &request, &response, NULL) < 0)
goto send_error;
gst_rtsp_message_unset (&request);
/* parse RTP npt field. This is the current position in the stream (Normal
* Play Time) and should be put in the NEWSEGMENT position field. */
if (gst_rtsp_message_get_header (&response, GST_RTSP_HDR_RANGE, &hval,
0) == GST_RTSP_OK)
gst_rtspsrc_parse_range (src, hval, segment);
/* assume 1.0 rate now, overwrite when the SCALE or SPEED headers are present. */
segment->rate = 1.0;
/* parse Speed header. This is the intended playback rate of the stream
* and should be put in the NEWSEGMENT rate field. */
if (gst_rtsp_message_get_header (&response, GST_RTSP_HDR_SPEED, &hval,
0) == GST_RTSP_OK) {
if (gst_rtspsrc_get_float (hval, &fval) > 0)
segment->rate = fval;
} else if (gst_rtsp_message_get_header (&response, GST_RTSP_HDR_SCALE,
&hval, 0) == GST_RTSP_OK) {
if (gst_rtspsrc_get_float (hval, &fval) > 0)
segment->rate = fval;
}
/* parse the RTP-Info header field (if ANY) to get the base seqnum and timestamp
* for the RTP packets. If this is not present, we assume all starts from 0...
* This is info for the RTP session manager that we pass to it in caps. */
hval_idx = 0;
while (gst_rtsp_message_get_header (&response, GST_RTSP_HDR_RTP_INFO,
&hval, hval_idx++) == GST_RTSP_OK)
gst_rtspsrc_parse_rtpinfo (src, hval);
gst_rtsp_message_unset (&response);
/* early exit when we did aggregate control */
if (control)
break;
} }
if (segment->rate != 1.0) {
hval = gst_rtspsrc_dup_printf ("%f", segment->rate);
if (src->skip)
gst_rtsp_message_add_header (&request, GST_RTSP_HDR_SCALE, hval);
else
gst_rtsp_message_add_header (&request, GST_RTSP_HDR_SPEED, hval);
g_free (hval);
}
if (gst_rtspsrc_send (src, &request, &response, NULL) < 0)
goto send_error;
gst_rtsp_message_unset (&request);
/* parse RTP npt field. This is the current position in the stream (Normal
* Play Time) and should be put in the NEWSEGMENT position field. */
if (gst_rtsp_message_get_header (&response, GST_RTSP_HDR_RANGE, &hval,
0) == GST_RTSP_OK)
gst_rtspsrc_parse_range (src, hval, segment);
/* assume 1.0 rate now, overwrite when the SCALE or SPEED headers are present. */
segment->rate = 1.0;
/* parse Speed header. This is the intended playback rate of the stream
* and should be put in the NEWSEGMENT rate field. */
if (gst_rtsp_message_get_header (&response, GST_RTSP_HDR_SPEED, &hval,
0) == GST_RTSP_OK) {
if (gst_rtspsrc_get_float (hval, &fval) > 0)
segment->rate = fval;
} else if (gst_rtsp_message_get_header (&response, GST_RTSP_HDR_SCALE, &hval,
0) == GST_RTSP_OK) {
if (gst_rtspsrc_get_float (hval, &fval) > 0)
segment->rate = fval;
}
/* parse the RTP-Info header field (if ANY) to get the base seqnum and timestamp
* for the RTP packets. If this is not present, we assume all starts from 0...
* This is info for the RTP session manager that we pass to it in caps. */
hval_idx = 0;
while (gst_rtsp_message_get_header (&response, GST_RTSP_HDR_RTP_INFO,
&hval, hval_idx++) == GST_RTSP_OK)
gst_rtspsrc_parse_rtpinfo (src, hval);
gst_rtsp_message_unset (&response);
/* configure the caps of the streams after we parsed all headers. */ /* configure the caps of the streams after we parsed all headers. */
gst_rtspsrc_configure_caps (src, segment); gst_rtspsrc_configure_caps (src, segment);
@ -5536,6 +5601,7 @@ gst_rtspsrc_pause (GstRTSPSrc * src, gboolean idle)
{ {
GstRTSPMessage request = { 0 }; GstRTSPMessage request = { 0 };
GstRTSPMessage response = { 0 }; GstRTSPMessage response = { 0 };
GList *walk;
gchar *control; gchar *control;
GST_RTSP_STATE_LOCK (src); GST_RTSP_STATE_LOCK (src);
@ -5567,15 +5633,31 @@ gst_rtspsrc_pause (GstRTSPSrc * src, gboolean idle)
else else
control = src->req_location; control = src->req_location;
/* do pause */ /* loop over the streams. We might exit the loop early when we could do an
if (gst_rtsp_message_init_request (&request, GST_RTSP_PAUSE, control) < 0) * aggregate control */
goto create_request_failed; for (walk = src->streams; walk; walk = g_list_next (walk)) {
GstRTSPStream *stream = (GstRTSPStream *) walk->data;
gchar *setup_url;
if (gst_rtspsrc_send (src, &request, &response, NULL) < 0) /* try aggregate control first but do non-aggregate control otherwise */
goto send_error; if (control)
setup_url = control;
else if ((setup_url = stream->setup_url) == NULL)
continue;
gst_rtsp_message_unset (&request); if (gst_rtsp_message_init_request (&request, GST_RTSP_PAUSE, setup_url) < 0)
gst_rtsp_message_unset (&response); goto create_request_failed;
if (gst_rtspsrc_send (src, &request, &response, NULL) < 0)
goto send_error;
gst_rtsp_message_unset (&request);
gst_rtsp_message_unset (&response);
/* exit early when we did agregate control */
if (control)
break;
}
if (idle && src->task) { if (idle && src->task) {
GST_DEBUG_OBJECT (src, "starting idle task again"); GST_DEBUG_OBJECT (src, "starting idle task again");