gst/rtsp/: Preliminary seek support.

Original commit message from CVS:
* gst/rtsp/gstrtspsrc.c: (gst_rtspsrc_flush),
(gst_rtspsrc_do_seek), (gst_rtspsrc_perform_seek),
(gst_rtspsrc_handle_src_event),
(gst_rtspsrc_stream_configure_manager),
(gst_rtspsrc_stream_configure_tcp), (gst_rtspsrc_loop_interleaved),
(gst_rtspsrc_send_keep_alive), (gst_rtspsrc_open),
(gst_rtspsrc_parse_rtpinfo), (gst_rtspsrc_play):
* gst/rtsp/gstrtspsrc.h:
* gst/rtsp/rtspdefs.h:
Preliminary seek support.
Activate internal pads so that we can receive events on them.
Don't try to parse a range string when it's NULL.
This commit is contained in:
Wim Taymans 2007-05-11 15:09:39 +00:00
parent 5bc71b661d
commit 02fa0a7992
4 changed files with 233 additions and 16 deletions

View file

@ -1,3 +1,18 @@
2007-05-11 Wim Taymans <wim@fluendo.com>
* gst/rtsp/gstrtspsrc.c: (gst_rtspsrc_flush),
(gst_rtspsrc_do_seek), (gst_rtspsrc_perform_seek),
(gst_rtspsrc_handle_src_event),
(gst_rtspsrc_stream_configure_manager),
(gst_rtspsrc_stream_configure_tcp), (gst_rtspsrc_loop_interleaved),
(gst_rtspsrc_send_keep_alive), (gst_rtspsrc_open),
(gst_rtspsrc_parse_rtpinfo), (gst_rtspsrc_play):
* gst/rtsp/gstrtspsrc.h:
* gst/rtsp/rtspdefs.h:
Preliminary seek support.
Activate internal pads so that we can receive events on them.
Don't try to parse a range string when it's NULL.
2007-05-11 Wim Taymans <wim@fluendo.com> 2007-05-11 Wim Taymans <wim@fluendo.com>
* gst/rtp/README: * gst/rtp/README:

View file

@ -203,6 +203,7 @@ static gboolean gst_rtspsrc_uri_set_uri (GstURIHandler * handler,
static gboolean gst_rtspsrc_activate_streams (GstRTSPSrc * src); static gboolean gst_rtspsrc_activate_streams (GstRTSPSrc * src);
static void gst_rtspsrc_loop (GstRTSPSrc * src); static void gst_rtspsrc_loop (GstRTSPSrc * src);
static void gst_rtspsrc_push_event (GstRTSPSrc * src, GstEvent * event);
/* commands we send to out loop to notify it of events */ /* commands we send to out loop to notify it of events */
#define CMD_WAIT 0 #define CMD_WAIT 0
@ -1018,11 +1019,192 @@ cleanup:
} }
} }
static void
gst_rtspsrc_flush (GstRTSPSrc * src, gboolean flush)
{
GstEvent *event;
if (flush) {
event = gst_event_new_flush_start ();
} else {
event = gst_event_new_flush_stop ();
}
rtsp_connection_flush (src->connection, flush);
gst_rtspsrc_push_event (src, event);
}
static gboolean
gst_rtspsrc_do_seek (GstRTSPSrc * src, GstSegment * segment)
{
gboolean res;
/* PLAY from new position, we are flushing now */
src->position = ((gdouble) segment->last_stop) / GST_SECOND;
src->state = RTSP_STATE_SEEKING;
res = gst_rtspsrc_play (src);
return res;
}
static gboolean
gst_rtspsrc_perform_seek (GstRTSPSrc * src, GstEvent * event)
{
gboolean res;
gdouble rate;
GstFormat format;
GstSeekFlags flags;
GstSeekType cur_type = GST_SEEK_TYPE_NONE, stop_type;
gint64 cur, stop;
gboolean flush;
gboolean update;
GstSegment seeksegment = { 0, };
gint64 last_stop;
if (event) {
GST_DEBUG_OBJECT (src, "doing seek with event");
gst_event_parse_seek (event, &rate, &format, &flags,
&cur_type, &cur, &stop_type, &stop);
/* no negative rates yet */
if (rate < 0.0)
goto negative_rate;
/* we need TIME format */
if (format != src->segment.format)
goto no_format;
} else {
GST_DEBUG_OBJECT (src, "doing seek without event");
flags = 0;
cur_type = GST_SEEK_TYPE_SET;
stop_type = GST_SEEK_TYPE_SET;
}
/* get flush flag */
flush = flags & GST_SEEK_FLAG_FLUSH;
/* now we need to make sure the streaming thread is stopped. We do this by
* either sending a FLUSH_START event downstream which will cause the
* streaming thread to stop with a WRONG_STATE.
* For a non-flushing seek we simply pause the task, which will happen as soon
* as it completes one iteration (and thus might block when the sink is
* blocking in preroll). */
if (flush) {
GST_DEBUG_OBJECT (src, "starting flush");
gst_rtspsrc_flush (src, TRUE);
} else {
//gst_pad_pause_task (src->sinkpad);
}
/* we should now be able to grab the streaming thread because we stopped it
* with the above flush/pause code */
//GST_PAD_STREAM_LOCK (src->sinkpad);
/* save current position */
last_stop = src->segment.last_stop;
GST_DEBUG_OBJECT (src, "stopped streaming at %" G_GINT64_FORMAT, last_stop);
/* copy segment, we need this because we still need the old
* segment when we close the current segment. */
memcpy (&seeksegment, &src->segment, sizeof (GstSegment));
/* configure the seek parameters in the seeksegment. We will then have the
* right values in the segment to perform the seek */
if (event) {
GST_DEBUG_OBJECT (src, "configuring seek");
gst_segment_set_seek (&seeksegment, rate, format, flags,
cur_type, cur, stop_type, stop, &update);
}
/* figure out the last position we need to play. If it's configured (stop !=
* -1), use that, else we play until the total duration of the file */
if ((stop = seeksegment.stop) == -1)
stop = seeksegment.duration;
res = gst_rtspsrc_do_seek (src, &seeksegment);
/* prepare for streaming again */
if (flush) {
/* if we started flush, we stop now */
GST_DEBUG_OBJECT (src, "stopping flush");
gst_rtspsrc_flush (src, FALSE);
} else if (src->running) {
/* we are running the current segment and doing a non-flushing seek,
* close the segment first based on the previous last_stop. */
GST_DEBUG_OBJECT (src, "closing running segment %" G_GINT64_FORMAT
" to %" G_GINT64_FORMAT, src->segment.accum, src->segment.last_stop);
/* queue the segment for sending in the stream thread */
if (src->close_segment)
gst_event_unref (src->close_segment);
src->close_segment = gst_event_new_new_segment (TRUE,
src->segment.rate, src->segment.format,
src->segment.accum, src->segment.last_stop, src->segment.accum);
/* keep track of our last_stop */
seeksegment.accum = src->segment.last_stop;
}
/* now we did the seek and can activate the new segment values */
memcpy (&src->segment, &seeksegment, sizeof (GstSegment));
/* if we're doing a segment seek, post a SEGMENT_START message */
if (src->segment.flags & GST_SEEK_FLAG_SEGMENT) {
gst_element_post_message (GST_ELEMENT_CAST (src),
gst_message_new_segment_start (GST_OBJECT_CAST (src),
src->segment.format, src->segment.last_stop));
}
/* now create the newsegment */
GST_DEBUG_OBJECT (src, "Creating newsegment from %" G_GINT64_FORMAT
" to %" G_GINT64_FORMAT, src->segment.last_stop, stop);
/* store the newsegment event so it can be sent from the streaming thread. */
if (src->start_segment)
gst_event_unref (src->start_segment);
src->start_segment =
gst_event_new_new_segment (FALSE, src->segment.rate,
src->segment.format, src->segment.last_stop, stop,
src->segment.last_stop);
/* mark discont if we are going to stream from another position. */
if (last_stop != src->segment.last_stop) {
GST_DEBUG_OBJECT (src, "mark DISCONT, we did a seek to another position");
//src->discont = TRUE;
}
/* and start the streaming task again */
src->running = TRUE;
//gst_pad_start_task (src->sinkpad, (GstTaskFunction) gst_srcparse_loop,
// src->sinkpad);
//GST_PAD_STREAM_UNLOCK (src->sinkpad);
return TRUE;
/* ERRORS */
negative_rate:
{
GST_DEBUG_OBJECT (src, "negative playback rates are not supported yet.");
return FALSE;
}
no_format:
{
GST_DEBUG_OBJECT (src, "unsupported format given, seek aborted.");
return FALSE;
}
}
static gboolean static gboolean
gst_rtspsrc_handle_src_event (GstPad * pad, GstEvent * event) gst_rtspsrc_handle_src_event (GstPad * pad, GstEvent * event)
{ {
GstRTSPSrc *src; GstRTSPSrc *src;
gboolean res = TRUE; gboolean res = FALSE;
src = GST_RTSPSRC_CAST (gst_pad_get_element_private (pad)); src = GST_RTSPSRC_CAST (gst_pad_get_element_private (pad));
@ -1033,6 +1215,7 @@ gst_rtspsrc_handle_src_event (GstPad * pad, GstEvent * event)
case GST_EVENT_QOS: case GST_EVENT_QOS:
break; break;
case GST_EVENT_SEEK: case GST_EVENT_SEEK:
res = gst_rtspsrc_perform_seek (src, event);
break; break;
case GST_EVENT_NAVIGATION: case GST_EVENT_NAVIGATION:
break; break;
@ -1041,7 +1224,6 @@ gst_rtspsrc_handle_src_event (GstPad * pad, GstEvent * event)
default: default:
break; break;
} }
return res; return res;
} }
@ -1315,7 +1497,6 @@ gst_rtspsrc_stream_configure_manager (GstRTSPSrc * src, GstRTSPStream * stream,
g_free (name); g_free (name);
} }
use_no_manager: use_no_manager:
return TRUE; return TRUE;
@ -1403,6 +1584,7 @@ gst_rtspsrc_stream_configure_tcp (GstRTSPSrc * src, GstRTSPStream * stream,
gst_pad_set_query_function (pad0, gst_rtspsrc_handle_src_query); gst_pad_set_query_function (pad0, gst_rtspsrc_handle_src_query);
gst_pad_link (pad0, stream->channelpad[0]); gst_pad_link (pad0, stream->channelpad[0]);
stream->channelpad[0] = pad0; stream->channelpad[0] = pad0;
gst_pad_set_active (pad0, TRUE);
gst_pad_set_element_private (pad0, src); gst_pad_set_element_private (pad0, src);
if (stream->channelpad[1]) { if (stream->channelpad[1]) {
@ -1411,6 +1593,7 @@ gst_rtspsrc_stream_configure_tcp (GstRTSPSrc * src, GstRTSPStream * stream,
pad1 = gst_pad_new_from_template (template, "internalsrc1"); pad1 = gst_pad_new_from_template (template, "internalsrc1");
gst_pad_link (pad1, stream->channelpad[1]); gst_pad_link (pad1, stream->channelpad[1]);
stream->channelpad[1] = pad1; stream->channelpad[1] = pad1;
gst_pad_set_active (pad1, TRUE);
} }
gst_object_unref (template); gst_object_unref (template);
} }
@ -1982,9 +2165,6 @@ receive_error:
("Could not receive message. (%s)", str)); ("Could not receive message. (%s)", str));
g_free (str); g_free (str);
if (src->debug)
rtsp_message_dump (&message);
rtsp_message_unset (&message); rtsp_message_unset (&message);
ret = GST_FLOW_UNEXPECTED; ret = GST_FLOW_UNEXPECTED;
goto need_pause; goto need_pause;
@ -3016,6 +3196,7 @@ gst_rtspsrc_open (GstRTSPSrc * src)
/* reset our state */ /* reset our state */
gst_segment_init (&src->segment, GST_FORMAT_TIME); gst_segment_init (&src->segment, GST_FORMAT_TIME);
src->position = 0.0;
/* can't continue without a valid url */ /* can't continue without a valid url */
if (G_UNLIKELY (src->url == NULL)) if (G_UNLIKELY (src->url == NULL))
@ -3092,19 +3273,21 @@ gst_rtspsrc_open (GstRTSPSrc * src)
/* parse range for duration reporting. */ /* parse range for duration reporting. */
{ {
gchar *range; gchar *range;
RTSPTimeRange *therange;
range = sdp_message_get_attribute_val (&sdp, "range"); range = sdp_message_get_attribute_val (&sdp, "range");
if (range) {
RTSPTimeRange *therange;
rtsp_range_parse (range, &therange); if (rtsp_range_parse (range, &therange) == RTSP_OK) {
GST_DEBUG_OBJECT (src, "range: '%s', min %f - max %f ",
GST_STR_NULL (range), therange->min.seconds, therange->max.seconds);
GST_DEBUG_OBJECT (src, "range: '%s', min %f - max %f ", gst_segment_set_duration (&src->segment, GST_FORMAT_TIME,
GST_STR_NULL (range), therange->min.seconds, therange->max.seconds); therange->max.seconds * GST_SECOND);
gst_segment_set_last_stop (&src->segment, GST_FORMAT_TIME,
gst_segment_set_duration (&src->segment, GST_FORMAT_TIME, therange->min.seconds * GST_SECOND);
therange->max.seconds * GST_SECOND); }
gst_segment_set_last_stop (&src->segment, GST_FORMAT_TIME, }
therange->min.seconds * GST_SECOND);
} }
/* create streams */ /* create streams */
@ -3367,11 +3550,20 @@ gst_rtspsrc_parse_rtpinfo (GstRTSPSrc * src, gchar * rtpinfo)
stream->seqbase = seqbase; stream->seqbase = seqbase;
stream->timebase = timebase; stream->timebase = timebase;
if ((caps = stream->caps)) { if ((caps = stream->caps)) {
caps = gst_caps_make_writable (caps);
/* update caps */ /* update caps */
if (timebase != -1) if (timebase != -1)
gst_caps_set_simple (caps, "clock-base", G_TYPE_UINT, timebase, NULL); gst_caps_set_simple (caps, "clock-base", G_TYPE_UINT, timebase, NULL);
if (seqbase != -1) if (seqbase != -1)
gst_caps_set_simple (caps, "seqnum-base", G_TYPE_UINT, seqbase, NULL); gst_caps_set_simple (caps, "seqnum-base", G_TYPE_UINT, seqbase, NULL);
if (stream->caps != caps) {
gst_caps_unref (stream->caps);
stream->caps = caps;
}
if (src->session) {
g_signal_emit_by_name (src->session, "clear-pt-map", NULL);
}
} }
} }
} }
@ -3403,7 +3595,13 @@ gst_rtspsrc_play (GstRTSPSrc * src)
if (res < 0) if (res < 0)
goto create_request_failed; goto create_request_failed;
rtsp_message_add_header (&request, RTSP_HDR_RANGE, "npt=0-"); if (src->position == 0.0)
range = g_strdup_printf ("npt=0-");
else
range = g_strdup_printf ("npt=%f-", src->position);
rtsp_message_add_header (&request, RTSP_HDR_RANGE, range);
g_free (range);
if ((res = gst_rtspsrc_send (src, &request, &response, NULL)) < 0) if ((res = gst_rtspsrc_send (src, &request, &response, NULL)) < 0)
goto send_error; goto send_error;

View file

@ -123,7 +123,10 @@ struct _GstRTSPSrc {
GStaticRecMutex *stream_rec_lock; GStaticRecMutex *stream_rec_lock;
GstSegment segment; GstSegment segment;
gboolean running; gboolean running;
gdouble position;
gint free_channel; gint free_channel;
GstEvent *close_segment;
GstEvent *start_segment;
/* UDP mode loop */ /* UDP mode loop */
gint loop_cmd; gint loop_cmd;

View file

@ -81,6 +81,7 @@ typedef enum {
RTSP_STATE_INVALID, RTSP_STATE_INVALID,
RTSP_STATE_INIT, RTSP_STATE_INIT,
RTSP_STATE_READY, RTSP_STATE_READY,
RTSP_STATE_SEEKING,
RTSP_STATE_PLAYING, RTSP_STATE_PLAYING,
RTSP_STATE_RECORDING, RTSP_STATE_RECORDING,
} RTSPState; } RTSPState;