gst/rtsp/gstrtspsrc.*: Improve timeout handling.

Original commit message from CVS:
* gst/rtsp/gstrtspsrc.c: (gst_rtspsrc_set_property),
(gst_rtspsrc_flush), (gst_rtspsrc_sink_chain),
(gst_rtspsrc_stream_configure_udp_sink),
(gst_rtspsrc_send_keep_alive), (gst_rtspsrc_loop_interleaved),
(gst_rtspsrc_loop_udp), (gst_rtspsrc_loop_send_cmd),
(gst_rtspsrc_try_send), (gst_rtspsrc_send),
(gst_rtspsrc_parse_methods), (gst_rtspsrc_parse_range),
(gst_rtspsrc_open), (gst_rtspsrc_close), (gst_rtspsrc_pause),
(gst_rtspsrc_handle_message), (gst_rtspsrc_change_state):
* gst/rtsp/gstrtspsrc.h:
Improve timeout handling.
Use the same socket for sending and receiving RTCP packets so that some
servers can track clients better.
Improve connection closed handling. Try to reconnect.
Don't overwrite our content base with NULL.
Improve debugging.
Improve range parsing and handling.
Remove flushing hack now that core does the right thing.
This commit is contained in:
Wim Taymans 2007-08-17 14:15:19 +00:00
parent 2e599ab037
commit 6ef7055041
3 changed files with 210 additions and 75 deletions

View file

@ -1,3 +1,24 @@
2007-08-17 Wim Taymans <wim.taymans@gmail.com>
* gst/rtsp/gstrtspsrc.c: (gst_rtspsrc_set_property),
(gst_rtspsrc_flush), (gst_rtspsrc_sink_chain),
(gst_rtspsrc_stream_configure_udp_sink),
(gst_rtspsrc_send_keep_alive), (gst_rtspsrc_loop_interleaved),
(gst_rtspsrc_loop_udp), (gst_rtspsrc_loop_send_cmd),
(gst_rtspsrc_try_send), (gst_rtspsrc_send),
(gst_rtspsrc_parse_methods), (gst_rtspsrc_parse_range),
(gst_rtspsrc_open), (gst_rtspsrc_close), (gst_rtspsrc_pause),
(gst_rtspsrc_handle_message), (gst_rtspsrc_change_state):
* gst/rtsp/gstrtspsrc.h:
Improve timeout handling.
Use the same socket for sending and receiving RTCP packets so that some
servers can track clients better.
Improve connection closed handling. Try to reconnect.
Don't overwrite our content base with NULL.
Improve debugging.
Improve range parsing and handling.
Remove flushing hack now that core does the right thing.
2007-08-17 Wim Taymans <wim.taymans@gmail.com> 2007-08-17 Wim Taymans <wim.taymans@gmail.com>
* gst/udp/gstmultiudpsink.c: (gst_multiudpsink_class_init), * gst/udp/gstmultiudpsink.c: (gst_multiudpsink_class_init),

View file

@ -381,6 +381,11 @@ gst_rtspsrc_set_property (GObject * object, guint prop_id, const GValue * value,
rtspsrc->tcp_timeout.tv_sec = timeout / G_USEC_PER_SEC; rtspsrc->tcp_timeout.tv_sec = timeout / G_USEC_PER_SEC;
rtspsrc->tcp_timeout.tv_usec = timeout % G_USEC_PER_SEC; rtspsrc->tcp_timeout.tv_usec = timeout % G_USEC_PER_SEC;
if (timeout != 0)
rtspsrc->ptcp_timeout = &rtspsrc->tcp_timeout;
else
rtspsrc->ptcp_timeout = NULL;
break; break;
} }
case PROP_LATENCY: case PROP_LATENCY:
@ -1076,10 +1081,11 @@ gst_rtspsrc_flush (GstRTSPSrc * src, gboolean flush)
if (flush) { if (flush) {
event = gst_event_new_flush_start (); event = gst_event_new_flush_start ();
GST_DEBUG_OBJECT (src, "start flush");
} else { } else {
event = gst_event_new_flush_stop (); event = gst_event_new_flush_stop ();
GST_DEBUG_OBJECT (src, "stop flush");
} }
gst_rtsp_connection_flush (src->connection, flush); gst_rtsp_connection_flush (src->connection, flush);
gst_rtspsrc_push_event (src, event); gst_rtspsrc_push_event (src, event);
@ -1343,13 +1349,17 @@ gst_rtspsrc_sink_chain (GstPad * pad, GstBuffer * buffer)
gst_rtsp_message_init_data (&message, stream->channel[1]); gst_rtsp_message_init_data (&message, stream->channel[1]);
/* lend the body data to the message */
gst_rtsp_message_take_body (&message, data, size); gst_rtsp_message_take_body (&message, data, size);
GST_DEBUG_OBJECT (src, "sending %u bytes RTCP", size); GST_DEBUG_OBJECT (src, "sending %u bytes RTCP", size);
ret = gst_rtsp_connection_send (src->connection, &message, NULL); ret = gst_rtsp_connection_send (src->connection, &message, NULL);
GST_DEBUG_OBJECT (src, "sent RTCP, %d", ret); GST_DEBUG_OBJECT (src, "sent RTCP, %d", ret);
/* and steal it away again because we will free it when unreffing the
* buffer */
gst_rtsp_message_steal_body (&message, &data, &size); gst_rtsp_message_steal_body (&message, &data, &size);
gst_rtsp_message_unset (&message);
gst_buffer_unref (buffer); gst_buffer_unref (buffer);
@ -1845,7 +1855,7 @@ gst_rtspsrc_stream_configure_udp_sink (GstRTSPSrc * src, GstRTSPStream * stream,
GstRTSPTransport * transport) GstRTSPTransport * transport)
{ {
GstPad *pad; GstPad *pad;
gint port; gint port, sockfd = -1;
gchar *destination, *uri, *name; gchar *destination, *uri, *name;
/* no session, we're done */ /* no session, we're done */
@ -1872,13 +1882,25 @@ gst_rtspsrc_stream_configure_udp_sink (GstRTSPSrc * src, GstRTSPStream * stream,
if (stream->udpsink == NULL) if (stream->udpsink == NULL)
goto no_sink_element; goto no_sink_element;
/* no sync needed */
g_object_set (G_OBJECT (stream->udpsink), "sync", FALSE, NULL);
if (stream->udpsrc[1]) {
/* configure socket, we give it the same UDP socket as the udpsrc for RTCP
* because some servers check the port number of where it sends RTCP to identify
* the RTCP packets it receives */
g_object_get (G_OBJECT (stream->udpsrc[1]), "sock", &sockfd, NULL);
GST_DEBUG_OBJECT (src, "UDP src has sock %d", sockfd);
/* configure socket and make sure udpsink does not close it when shutting
* down, it belongs to udpsrc after all. */
g_object_set (G_OBJECT (stream->udpsink), "sockfd", sockfd, NULL);
g_object_set (G_OBJECT (stream->udpsink), "closefd", FALSE, NULL);
}
/* we keep this playing always */ /* we keep this playing always */
gst_element_set_locked_state (stream->udpsink, TRUE); gst_element_set_locked_state (stream->udpsink, TRUE);
gst_element_set_state (stream->udpsink, GST_STATE_PLAYING); gst_element_set_state (stream->udpsink, GST_STATE_PLAYING);
/* no sync needed */
g_object_set (G_OBJECT (stream->udpsink), "sync", FALSE, NULL);
gst_object_ref (stream->udpsink); gst_object_ref (stream->udpsink);
gst_bin_add (GST_BIN_CAST (src), stream->udpsink); gst_bin_add (GST_BIN_CAST (src), stream->udpsink);
@ -2228,6 +2250,9 @@ gst_rtspsrc_send_keep_alive (GstRTSPSrc * src)
if (res < 0) if (res < 0)
goto send_error; goto send_error;
if (src->debug)
gst_rtsp_message_dump (&request);
if ((res = gst_rtsp_connection_send (src->connection, &request, NULL)) < 0) if ((res = gst_rtsp_connection_send (src->connection, &request, NULL)) < 0)
goto send_error; goto send_error;
@ -2263,9 +2288,10 @@ gst_rtspsrc_loop_interleaved (GstRTSPSrc * src)
GstBuffer *buf; GstBuffer *buf;
gboolean is_rtcp, have_data; gboolean is_rtcp, have_data;
/* here we are only interested in data messages */
have_data = FALSE; have_data = FALSE;
do { do {
GTimeVal tv_timeout, *tv; GTimeVal tv_timeout;
/* get the next timeout interval */ /* get the next timeout interval */
gst_rtsp_connection_next_timeout (src->connection, &tv_timeout); gst_rtsp_connection_next_timeout (src->connection, &tv_timeout);
@ -2277,27 +2303,25 @@ gst_rtspsrc_loop_interleaved (GstRTSPSrc * src)
res = gst_rtspsrc_send_keep_alive (src); res = gst_rtspsrc_send_keep_alive (src);
} }
if ((src->tcp_timeout.tv_sec | src->tcp_timeout.tv_usec))
tv = &src->tcp_timeout;
else
tv = NULL;
GST_DEBUG_OBJECT (src, "doing receive"); GST_DEBUG_OBJECT (src, "doing receive");
res = gst_rtsp_connection_receive (src->connection, &message, tv); res =
gst_rtsp_connection_receive (src->connection, &message,
src->ptcp_timeout);
switch (res) { switch (res) {
case GST_RTSP_OK: case GST_RTSP_OK:
GST_DEBUG_OBJECT (src, "we received a server message"); GST_DEBUG_OBJECT (src, "we received a server message");
break; break;
case GST_RTSP_EINTR: case GST_RTSP_EINTR:
/* we got interrupted, see what we have to do */ /* we got interrupted this means we need to stop */
GST_DEBUG_OBJECT (src, "we got interrupted, unset flushing");
/* unset flushing so we can do something else */
gst_rtsp_connection_flush (src->connection, FALSE);
goto interrupt; goto interrupt;
case GST_RTSP_ETIMEOUT: case GST_RTSP_ETIMEOUT:
/* no reply, go EOS */
goto timeout; goto timeout;
case GST_RTSP_EEOF:
/* go EOS when the server closed the connection */
goto server_eof;
default: default:
goto receive_error; goto receive_error;
} }
@ -2311,6 +2335,8 @@ gst_rtspsrc_loop_interleaved (GstRTSPSrc * src)
case GST_RTSP_MESSAGE_RESPONSE: case GST_RTSP_MESSAGE_RESPONSE:
/* we ignore response messages */ /* we ignore response messages */
GST_DEBUG_OBJECT (src, "ignoring response message"); GST_DEBUG_OBJECT (src, "ignoring response message");
if (src->debug)
gst_rtsp_message_dump (&message);
break; break;
case GST_RTSP_MESSAGE_DATA: case GST_RTSP_MESSAGE_DATA:
GST_DEBUG_OBJECT (src, "got data message"); GST_DEBUG_OBJECT (src, "got data message");
@ -2408,14 +2434,26 @@ unknown_stream:
timeout: timeout:
{ {
GST_DEBUG_OBJECT (src, "we got a timeout"); GST_DEBUG_OBJECT (src, "we got a timeout");
GST_ELEMENT_WARNING (src, RESOURCE, READ, (NULL),
("Timeout while waiting for server message."));
gst_rtsp_message_unset (&message); gst_rtsp_message_unset (&message);
ret = GST_FLOW_UNEXPECTED; ret = GST_FLOW_UNEXPECTED;
goto need_pause; goto need_pause;
} }
server_eof:
{
GST_DEBUG_OBJECT (src, "we got an eof from the server");
GST_ELEMENT_WARNING (src, RESOURCE, READ, (NULL),
("The server closed the connection."));
ret = GST_FLOW_UNEXPECTED;
goto need_pause;
}
interrupt: interrupt:
{ {
GST_DEBUG_OBJECT (src, "we got interrupted");
gst_rtsp_message_unset (&message); gst_rtsp_message_unset (&message);
GST_DEBUG_OBJECT (src, "got interrupted: stop connection flush");
/* unset flushing so we can do something else */
gst_rtsp_connection_flush (src->connection, FALSE);
ret = GST_FLOW_WRONG_STATE; ret = GST_FLOW_WRONG_STATE;
goto need_pause; goto need_pause;
} }
@ -2428,7 +2466,7 @@ receive_error:
g_free (str); g_free (str);
gst_rtsp_message_unset (&message); gst_rtsp_message_unset (&message);
ret = GST_FLOW_UNEXPECTED; ret = GST_FLOW_ERROR;
goto need_pause; goto need_pause;
} }
handle_request_failed: handle_request_failed:
@ -2436,16 +2474,16 @@ handle_request_failed:
gchar *str = gst_rtsp_strresult (res); gchar *str = gst_rtsp_strresult (res);
GST_ELEMENT_ERROR (src, RESOURCE, WRITE, (NULL), GST_ELEMENT_ERROR (src, RESOURCE, WRITE, (NULL),
("Could not send message. (%s)", str)); ("Could not handle server message. (%s)", str));
g_free (str); g_free (str);
gst_rtsp_message_unset (&message); gst_rtsp_message_unset (&message);
ret = GST_FLOW_UNEXPECTED; ret = GST_FLOW_ERROR;
goto need_pause; goto need_pause;
} }
invalid_length: invalid_length:
{ {
GST_ELEMENT_WARNING (src, RESOURCE, READ, (NULL), GST_ELEMENT_WARNING (src, RESOURCE, READ, (NULL),
("Short message received.")); ("Short message received, ignoring."));
gst_rtsp_message_unset (&message); gst_rtsp_message_unset (&message);
return; return;
} }
@ -2467,8 +2505,8 @@ need_pause:
gst_rtspsrc_push_event (src, gst_event_new_eos ()); gst_rtspsrc_push_event (src, gst_event_new_eos ());
} }
} else { } else {
/* for fatal errors we post an error message, post the error /* for fatal errors we post an error message, post the error before the
* first so the app knows about the error first. */ * EOS so the app knows about the error first. */
GST_ELEMENT_ERROR (src, STREAM, FAILED, GST_ELEMENT_ERROR (src, STREAM, FAILED,
("Internal data flow error."), ("Internal data flow error."),
("streaming task paused, reason %s (%d)", reason, ret)); ("streaming task paused, reason %s (%d)", reason, ret));
@ -2484,6 +2522,7 @@ gst_rtspsrc_loop_udp (GstRTSPSrc * src)
{ {
gboolean restart = FALSE; gboolean restart = FALSE;
GstRTSPResult res; GstRTSPResult res;
GstFlowReturn ret = GST_FLOW_OK;
GST_OBJECT_LOCK (src); GST_OBJECT_LOCK (src);
if (src->loop_cmd == CMD_STOP) if (src->loop_cmd == CMD_STOP)
@ -2514,7 +2553,7 @@ gst_rtspsrc_loop_udp (GstRTSPSrc * src)
break; break;
case GST_RTSP_EINTR: case GST_RTSP_EINTR:
/* we got interrupted, see what we have to do */ /* we got interrupted, see what we have to do */
GST_DEBUG_OBJECT (src, "we got interrupted, unset flushing"); GST_DEBUG_OBJECT (src, "got interrupted: stop connection flush");
/* unset flushing so we can do something else */ /* unset flushing so we can do something else */
gst_rtsp_connection_flush (src->connection, FALSE); gst_rtsp_connection_flush (src->connection, FALSE);
goto interrupt; goto interrupt;
@ -2523,6 +2562,14 @@ gst_rtspsrc_loop_udp (GstRTSPSrc * src)
GST_DEBUG_OBJECT (src, "timout, sending keep-alive"); GST_DEBUG_OBJECT (src, "timout, sending keep-alive");
res = gst_rtspsrc_send_keep_alive (src); res = gst_rtspsrc_send_keep_alive (src);
continue; continue;
case GST_RTSP_EEOF:
/* server closed the connection. not very fatal for UDP, reconnect and
* see what happens. */
GST_ELEMENT_WARNING (src, RESOURCE, READ, (NULL),
("The server closed the connection."));
gst_rtsp_connection_close (src->connection);
gst_rtsp_connection_connect (src->connection, src->ptcp_timeout);
continue;
default: default:
goto receive_error; goto receive_error;
} }
@ -2536,6 +2583,8 @@ gst_rtspsrc_loop_udp (GstRTSPSrc * src)
case GST_RTSP_MESSAGE_RESPONSE: case GST_RTSP_MESSAGE_RESPONSE:
/* we ignore response and data messages */ /* we ignore response and data messages */
GST_DEBUG_OBJECT (src, "ignoring response message"); GST_DEBUG_OBJECT (src, "ignoring response message");
if (src->debug)
gst_rtsp_message_dump (&message);
break; break;
case GST_RTSP_MESSAGE_DATA: case GST_RTSP_MESSAGE_DATA:
/* we ignore response and data messages */ /* we ignore response and data messages */
@ -2580,7 +2629,8 @@ gst_rtspsrc_loop_udp (GstRTSPSrc * src)
gst_rtspsrc_pause (src); gst_rtspsrc_pause (src);
if (src->task) { if (src->task) {
/* stop task, we cannot join as this would deadlock */ /* stop task, we cannot join as this would deadlock, the task will stop when
* we exit this function below. */
gst_task_stop (src->task); gst_task_stop (src->task);
/* and free the task so that _close will not stop/join it again. */ /* and free the task so that _close will not stop/join it again. */
gst_object_unref (GST_OBJECT (src->task)); gst_object_unref (GST_OBJECT (src->task));
@ -2613,33 +2663,35 @@ done:
/* ERRORS */ /* ERRORS */
stopping: stopping:
{ {
GST_DEBUG_OBJECT (src, "we are stopping");
GST_OBJECT_UNLOCK (src); GST_OBJECT_UNLOCK (src);
src->running = FALSE; ret = GST_FLOW_WRONG_STATE;
gst_task_pause (src->task); goto need_pause;
return; }
{
GST_DEBUG_OBJECT (src, "we got an eof from the server");
ret = GST_FLOW_UNEXPECTED;
goto need_pause;
} }
receive_error: receive_error:
{ {
gchar *str = gst_rtsp_strresult (res); gchar *str = gst_rtsp_strresult (res);
GST_ELEMENT_WARNING (src, RESOURCE, READ, (NULL), GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL),
("Could not receive message. (%s)", str)); ("Could not receive message. (%s)", str));
g_free (str); g_free (str);
/* don't bother continueing if we the connection was closed */ ret = GST_FLOW_ERROR;
if (res == GST_RTSP_EEOF) { goto need_pause;
src->running = FALSE;
gst_task_pause (src->task);
}
return;
} }
handle_request_failed: handle_request_failed:
{ {
gchar *str = gst_rtsp_strresult (res); gchar *str = gst_rtsp_strresult (res);
GST_ELEMENT_WARNING (src, RESOURCE, READ, (NULL), GST_ELEMENT_ERROR (src, RESOURCE, WRITE, (NULL),
("Could not handle server message. (%s)", str)); ("Could not handle server message. (%s)", str));
g_free (str); g_free (str);
return; ret = GST_FLOW_ERROR;
goto need_pause;
} }
no_protocols: no_protocols:
{ {
@ -2647,7 +2699,8 @@ no_protocols:
/* no transport possible, post an error and stop */ /* no transport possible, post an error and stop */
GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL),
("Could not connect to server, no protocols left")); ("Could not connect to server, no protocols left"));
return; ret = GST_FLOW_ERROR;
goto need_pause;
} }
open_failed: open_failed:
{ {
@ -2659,6 +2712,34 @@ play_failed:
GST_DEBUG_OBJECT (src, "play failed"); GST_DEBUG_OBJECT (src, "play failed");
return; return;
} }
need_pause:
{
const gchar *reason = gst_flow_get_name (ret);
GST_DEBUG_OBJECT (src, "pausing task, reason %s", reason);
src->running = FALSE;
gst_task_pause (src->task);
if (GST_FLOW_IS_FATAL (ret) || ret == GST_FLOW_NOT_LINKED) {
if (ret == GST_FLOW_UNEXPECTED) {
/* perform EOS logic */
if (src->segment.flags & GST_SEEK_FLAG_SEGMENT) {
gst_element_post_message (GST_ELEMENT_CAST (src),
gst_message_new_segment_done (GST_OBJECT_CAST (src),
src->segment.format, src->segment.last_stop));
} else {
gst_rtspsrc_push_event (src, gst_event_new_eos ());
}
} else {
/* for fatal errors we post an error message, post the error before the
* EOS so the app knows about the error first. */
GST_ELEMENT_ERROR (src, STREAM, FAILED,
("Internal data flow error."),
("streaming task paused, reason %s (%d)", reason, ret));
gst_rtspsrc_push_event (src, gst_event_new_eos ());
}
}
return;
}
} }
static void static void
@ -2667,7 +2748,7 @@ gst_rtspsrc_loop_send_cmd (GstRTSPSrc * src, gint cmd, gboolean flush)
GST_OBJECT_LOCK (src); GST_OBJECT_LOCK (src);
src->loop_cmd = cmd; src->loop_cmd = cmd;
if (flush) { if (flush) {
GST_DEBUG_OBJECT (src, "start flush"); GST_DEBUG_OBJECT (src, "start connection flush");
gst_rtsp_connection_flush (src->connection, TRUE); gst_rtsp_connection_flush (src->connection, TRUE);
} }
GST_OBJECT_UNLOCK (src); GST_OBJECT_UNLOCK (src);
@ -2832,8 +2913,9 @@ gst_rtspsrc_try_send (GstRTSPSrc * src, GstRTSPMessage * request,
GstRTSPResult res; GstRTSPResult res;
GstRTSPStatusCode thecode; GstRTSPStatusCode thecode;
gchar *content_base = NULL; gchar *content_base = NULL;
GTimeVal *tv; gint try = 0;
again:
gst_rtsp_ext_list_before_send (src->extensions, request); gst_rtsp_ext_list_before_send (src->extensions, request);
GST_DEBUG_OBJECT (src, "sending message"); GST_DEBUG_OBJECT (src, "sending message");
@ -2841,18 +2923,17 @@ gst_rtspsrc_try_send (GstRTSPSrc * src, GstRTSPMessage * request,
if (src->debug) if (src->debug)
gst_rtsp_message_dump (request); gst_rtsp_message_dump (request);
if ((src->tcp_timeout.tv_sec | src->tcp_timeout.tv_usec)) if ((res =
tv = &src->tcp_timeout; gst_rtsp_connection_send (src->connection, request,
else src->ptcp_timeout)) < 0)
tv = NULL;
if ((res = gst_rtsp_connection_send (src->connection, request, tv)) < 0)
goto send_error; goto send_error;
gst_rtsp_connection_reset_timeout (src->connection); gst_rtsp_connection_reset_timeout (src->connection);
next: next:
if ((res = gst_rtsp_connection_receive (src->connection, response, tv)) < 0) if ((res =
gst_rtsp_connection_receive (src->connection, response,
src->ptcp_timeout)) < 0)
goto receive_error; goto receive_error;
if (src->debug) if (src->debug)
@ -2889,9 +2970,10 @@ next:
/* store new content base if any */ /* store new content base if any */
gst_rtsp_message_get_header (response, GST_RTSP_HDR_CONTENT_BASE, gst_rtsp_message_get_header (response, GST_RTSP_HDR_CONTENT_BASE,
&content_base, 0); &content_base, 0);
g_free (src->content_base); if (content_base) {
src->content_base = g_strdup (content_base); g_free (src->content_base);
src->content_base = g_strdup (content_base);
}
gst_rtsp_ext_list_after_send (src->extensions, request, response); gst_rtsp_ext_list_after_send (src->extensions, request, response);
return GST_RTSP_OK; return GST_RTSP_OK;
@ -2908,11 +2990,30 @@ send_error:
} }
receive_error: receive_error:
{ {
gchar *str = gst_rtsp_strresult (res);
GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), switch (res) {
("Could not receive message. (%s)", str)); case GST_RTSP_EEOF:
g_free (str); GST_WARNING_OBJECT (src, "server closed connection, doing reconnect");
if (try == 0) {
gst_rtsp_connection_close (src->connection);
try++;
/* if reconnect succeeds, try again */
if ((res =
gst_rtsp_connection_connect (src->connection,
src->ptcp_timeout)) == 0)
goto again;
}
/* only try once after reconnect, then fallthrough and error out */
default:
{
gchar *str = gst_rtsp_strresult (res);
GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL),
("Could not receive message. (%s)", str));
g_free (str);
break;
}
}
return res; return res;
} }
handle_request_failed: handle_request_failed:
@ -2931,17 +3032,17 @@ handle_request_failed:
* send @request and retrieve the response in @response. optionally @code can be * send @request and retrieve the response in @response. optionally @code can be
* non-NULL in which case it will contain the status code of the response. * non-NULL in which case it will contain the status code of the response.
* *
* If This function returns TRUE, @response will contain a valid response * If This function returns #GST_RTSP_OK, @response will contain a valid response
* message that should be cleaned with gst_rtsp_message_unset() after usage. * message that should be cleaned with gst_rtsp_message_unset() after usage.
* *
* If @code is NULL, this function will return FALSE (with an invalid @response * If @code is NULL, this function will return #GST_RTSP_ERROR (with an invalid
* message) if the response code was not 200 (OK). * @response message) if the response code was not 200 (OK).
* *
* If the attempt results in an authentication failure, then this will attempt * If the attempt results in an authentication failure, then this will attempt
* to retrieve authentication credentials via gst_rtspsrc_setup_auth and retry * to retrieve authentication credentials via gst_rtspsrc_setup_auth and retry
* the request. * the request.
* *
* Returns: GST_RTSP_OK if the processing was successful. * Returns: #GST_RTSP_OK if the processing was successful.
*/ */
static GstRTSPResult static GstRTSPResult
gst_rtspsrc_send (GstRTSPSrc * src, GstRTSPMessage * request, gst_rtspsrc_send (GstRTSPSrc * src, GstRTSPMessage * request,
@ -2996,6 +3097,7 @@ error_response:
break; break;
case GST_RTSP_STS_NOT_ACCEPTABLE: case GST_RTSP_STS_NOT_ACCEPTABLE:
case GST_RTSP_STS_NOT_IMPLEMENTED: case GST_RTSP_STS_NOT_IMPLEMENTED:
case GST_RTSP_STS_METHOD_NOT_ALLOWED:
GST_WARNING_OBJECT (src, "got NOT IMPLEMENTED, disable method %s", GST_WARNING_OBJECT (src, "got NOT IMPLEMENTED, disable method %s",
gst_rtsp_method_as_text (method)); gst_rtsp_method_as_text (method));
src->methods &= ~method; src->methods &= ~method;
@ -3007,8 +3109,10 @@ error_response:
response->type_data.response.reason)); response->type_data.response.reason));
break; break;
} }
/* we return FALSE so we should unset the response ourselves */ /* if we return ERROR we should unset the response ourselves */
gst_rtsp_message_unset (response); if (res == GST_RTSP_ERROR)
gst_rtsp_message_unset (response);
return res; return res;
} }
} }
@ -3034,7 +3138,7 @@ gst_rtspsrc_parse_methods (GstRTSPSrc * src, GstRTSPMessage * response)
gint indx = 0; gint indx = 0;
gint i; gint i;
/* clear supported methods, FIXME, extensions should be able to configure /* reset supported methods, FIXME, extensions should be able to configure
* this. */ * this. */
src->methods = GST_RTSP_PLAY | GST_RTSP_PAUSE; src->methods = GST_RTSP_PLAY | GST_RTSP_PAUSE;
@ -3485,6 +3589,9 @@ gst_rtspsrc_parse_range (GstRTSPSrc * src, const gchar * range)
else else
seconds = therange->min.seconds * GST_SECOND; seconds = therange->min.seconds * GST_SECOND;
GST_DEBUG_OBJECT (src, "range: min %" GST_TIME_FORMAT,
GST_TIME_ARGS (seconds));
gst_segment_set_last_stop (&src->segment, GST_FORMAT_TIME, seconds); gst_segment_set_last_stop (&src->segment, GST_FORMAT_TIME, seconds);
if (therange->max.type == GST_RTSP_TIME_NOW) if (therange->max.type == GST_RTSP_TIME_NOW)
@ -3494,7 +3601,15 @@ gst_rtspsrc_parse_range (GstRTSPSrc * src, const gchar * range)
else else
seconds = therange->max.seconds * GST_SECOND; seconds = therange->max.seconds * GST_SECOND;
gst_segment_set_duration (&src->segment, GST_FORMAT_TIME, seconds); GST_DEBUG_OBJECT (src, "range: max %" GST_TIME_FORMAT,
GST_TIME_ARGS (seconds));
/* don't change duration with unknown value, we might have a valid value
* there that we want to keep. */
if (seconds != -1)
gst_segment_set_duration (&src->segment, GST_FORMAT_TIME, seconds);
} else {
GST_WARNING_OBJECT (src, "could not parse range: '%s'", range);
} }
} }
@ -3530,7 +3645,7 @@ gst_rtspsrc_open (GstRTSPSrc * src)
/* connect */ /* connect */
GST_DEBUG_OBJECT (src, "connecting (%s)...", src->req_location); GST_DEBUG_OBJECT (src, "connecting (%s)...", src->req_location);
if ((res = if ((res =
gst_rtsp_connection_connect (src->connection, &src->tcp_timeout)) < 0) gst_rtsp_connection_connect (src->connection, src->ptcp_timeout)) < 0)
goto could_not_connect; goto could_not_connect;
/* create OPTIONS */ /* create OPTIONS */
@ -3736,10 +3851,10 @@ gst_rtspsrc_close (GstRTSPSrc * src)
src->task = NULL; src->task = NULL;
} }
GST_DEBUG_OBJECT (src, "stop flush"); GST_DEBUG_OBJECT (src, "stop connection flush");
gst_rtsp_connection_flush (src->connection, FALSE); gst_rtsp_connection_flush (src->connection, FALSE);
if (src->methods & GST_RTSP_PLAY) { if (src->methods & (GST_RTSP_PLAY | GST_RTSP_TEARDOWN)) {
/* do TEARDOWN */ /* do TEARDOWN */
res = res =
gst_rtsp_message_init_request (&request, GST_RTSP_TEARDOWN, gst_rtsp_message_init_request (&request, GST_RTSP_TEARDOWN,
@ -3753,6 +3868,9 @@ 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,
"TEARDOWN and PLAY not supported, can't do TEARDOWN");
} }
/* close connection */ /* close connection */
@ -3982,9 +4100,12 @@ gst_rtspsrc_pause (GstRTSPSrc * src)
goto was_paused; goto was_paused;
/* wait for streaming to finish */ /* wait for streaming to finish */
GST_DEBUG_OBJECT (src, "waiting for streaming to finish");
GST_RTSP_STREAM_LOCK (src); GST_RTSP_STREAM_LOCK (src);
GST_DEBUG_OBJECT (src, "streaming finished");
GST_RTSP_STREAM_UNLOCK (src); GST_RTSP_STREAM_UNLOCK (src);
GST_DEBUG_OBJECT (src, "stop connection flush");
gst_rtsp_connection_flush (src->connection, FALSE); gst_rtsp_connection_flush (src->connection, FALSE);
/* do pause */ /* do pause */
@ -4104,11 +4225,6 @@ gst_rtspsrc_handle_message (GstBin * bin, GstMessage * message)
GST_BIN_CLASS (parent_class)->handle_message (bin, message); GST_BIN_CLASS (parent_class)->handle_message (bin, message);
break; break;
} }
case GST_MESSAGE_ASYNC_START:
case GST_MESSAGE_ASYNC_DONE:
/* ignore messages from our internal sinks */
gst_message_unref (message);
break;
default: default:
{ {
GST_BIN_CLASS (parent_class)->handle_message (bin, message); GST_BIN_CLASS (parent_class)->handle_message (bin, message);
@ -4136,7 +4252,7 @@ gst_rtspsrc_change_state (GstElement * element, GstStateChange transition)
goto open_failed; goto open_failed;
break; break;
case GST_STATE_CHANGE_PAUSED_TO_PLAYING: case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
GST_DEBUG_OBJECT (rtspsrc, "stop flush"); GST_DEBUG_OBJECT (rtspsrc, "PAUSED->PLAYING: stop connection flush");
gst_rtsp_connection_flush (rtspsrc->connection, FALSE); gst_rtsp_connection_flush (rtspsrc->connection, FALSE);
/* FIXME, the server might send UDP packets before we activate the UDP /* FIXME, the server might send UDP packets before we activate the UDP
* ports */ * ports */
@ -4144,7 +4260,7 @@ gst_rtspsrc_change_state (GstElement * element, GstStateChange transition)
break; break;
case GST_STATE_CHANGE_PLAYING_TO_PAUSED: case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
case GST_STATE_CHANGE_PAUSED_TO_READY: case GST_STATE_CHANGE_PAUSED_TO_READY:
GST_DEBUG_OBJECT (rtspsrc, "start flush"); GST_DEBUG_OBJECT (rtspsrc, "shutdown: sending stop command");
gst_rtspsrc_loop_send_cmd (rtspsrc, CMD_STOP, TRUE); gst_rtspsrc_loop_send_cmd (rtspsrc, CMD_STOP, TRUE);
break; break;
default: default:
@ -4156,9 +4272,6 @@ gst_rtspsrc_change_state (GstElement * element, GstStateChange transition)
goto done; goto done;
switch (transition) { switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
ret = GST_STATE_CHANGE_SUCCESS;
break;
case GST_STATE_CHANGE_READY_TO_PAUSED: case GST_STATE_CHANGE_READY_TO_PAUSED:
ret = GST_STATE_CHANGE_NO_PREROLL; ret = GST_STATE_CHANGE_NO_PREROLL;
break; break;

View file

@ -152,6 +152,7 @@ struct _GstRTSPSrc {
guint retry; guint retry;
guint64 udp_timeout; guint64 udp_timeout;
GTimeVal tcp_timeout; GTimeVal tcp_timeout;
GTimeVal *ptcp_timeout;
guint latency; guint latency;
guint connection_speed; guint connection_speed;