summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Hervey <bilboed@bilboed.com>2014-07-23 09:29:27 +0200
committerEdward Hervey <bilboed@bilboed.com>2014-07-23 09:29:27 +0200
commit8e4a97356217ee46290d3af69a507c7ccdc3dbc7 (patch)
tree9ec513299057459a8cb739b507a0b9744830fb0e
parent0e78c42ebe66e61cd72c90a1d4e92cc78e704f0b (diff)
WIP: dlnasrc: Refactor/simplify seek handlingrefactoring
Key changes: * decision is made as early as possible whether a certain seek event can be handled, based on server capabilities * first seek event will *always* be a TIME-based seek * decision is made as early as possible regarding what server technique will be used (Range, TimeSeekRange, PlaySpeed) * Create adequate BYTE-only seek event for souphttpsrc (which can only handle BYTE seeks) * Intercept outgoing SEGMENT event and modify it based on initial requested seek * Intercept outgoing Request/Response HTTP headers from souphttpsrc, which will be used to properly identify what the server did, and should be used to properly fill in outgoing SEGMENT event TODO: * Use actual server response (HTTP Response headers from souphttpsrc) to fill in the outgoing SEGMENT event. ** PlaySpeed => set rate:1.0, applied_rate:<actual rate the server specifies> ** Both => set start/stop/time to returned npt values * Handle time-based scan mode. Essentially the client needs to continuously fetch "ranges" of data from the server. The size of that "range" should be configurable. * Handle TIME<=>BYTES conversion if the server does not support TimeSeekRange nor PlaySpeed. * Handle byte-based scan mode. Same as for time-based ... except you will need to figure out the TIME=>BYTE conversion.
-rw-r--r--src/gstdlnasrc.c522
-rw-r--r--src/gstdlnasrc.h27
2 files changed, 307 insertions, 242 deletions
diff --git a/src/gstdlnasrc.c b/src/gstdlnasrc.c
index 359b28a..9e95017 100644
--- a/src/gstdlnasrc.c
+++ b/src/gstdlnasrc.c
@@ -307,7 +307,7 @@ static void dlna_src_struct_append_header_value_str_guint64 (GString *
struct_str, gchar * title, gchar * value_str, guint64 value);
static gboolean dlna_src_handle_event_seek (GstDlnaSrc * dlna_src,
- GstPad * pad, GstEvent * event);
+ GstPad * pad, GstEvent * event, GstEvent **new_event);
static gboolean dlna_src_handle_query_duration (GstDlnaSrc * dlna_src,
GstQuery * query);
@@ -315,9 +315,6 @@ static gboolean dlna_src_handle_query_duration (GstDlnaSrc * dlna_src,
static gboolean dlna_src_handle_query_seeking (GstDlnaSrc * dlna_src,
GstQuery * query);
-static gboolean dlna_src_handle_query_segment (GstDlnaSrc * dlna_src,
- GstQuery * query);
-
static gboolean dlna_src_handle_query_convert (GstDlnaSrc * dlna_src,
GstQuery * query);
@@ -330,15 +327,15 @@ dlna_src_parse_npt_range (GstDlnaSrc * dlna_src, const gchar * npt_str,
gchar ** start_str, gchar ** stop_str, gchar ** total_str,
guint64 * start, guint64 * stop, guint64 * total);
+#if 0
static gboolean dlna_src_is_change_valid (GstDlnaSrc * dlna_src, gfloat rate,
GstFormat format, guint64 start,
GstSeekType start_type, guint64 stop, GstSeekType stop_type);
+#endif
static gboolean dlna_src_is_rate_supported (GstDlnaSrc * dlna_src, gfloat rate);
-static gboolean dlna_src_adjust_http_src_headers (GstDlnaSrc * dlna_src,
- gfloat rate, GstFormat format, guint64 start, guint64 stop,
- guint32 new_seqnum);
+static gboolean dlna_src_adjust_http_src_headers (GstDlnaSrc * dlna_src);
static gboolean dlna_src_npt_to_nanos (GstDlnaSrc * dlna_src, gchar * string,
guint64 * media_time_nanos);
@@ -437,15 +434,10 @@ gst_dlna_src_init (GstDlnaSrc * dlna_src)
dlna_src->server_info = NULL;
- dlna_src->rate = 1.0;
- dlna_src->requested_rate = 1.0;
- dlna_src->requested_format = GST_FORMAT_BYTES;
- dlna_src->requested_start = 0;
- dlna_src->requested_stop = -1;
+ gst_segment_init (&dlna_src->requested_segment, GST_FORMAT_UNDEFINED);
+ dlna_src->seek_seqnum = 0;
- dlna_src->time_seek_seqnum = 0;
dlna_src->time_seek_event_start = 0;
- dlna_src->handled_time_seek_seqnum = FALSE;
dlna_src->is_uri_initialized = FALSE;
dlna_src->is_live = FALSE;
@@ -638,13 +630,19 @@ static gboolean
gst_dlna_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
gboolean ret = FALSE;
+ GstEvent *new_event = NULL;
GstDlnaSrc *dlna_src = GST_DLNA_SRC (gst_pad_get_parent (pad));
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEEK:
- GST_INFO_OBJECT (dlna_src, "Got src event: %s",
- GST_EVENT_TYPE_NAME (event));
- ret = dlna_src_handle_event_seek (dlna_src, pad, event);
+ {
+ GST_INFO_OBJECT (dlna_src, "Got src event: %s",
+ GST_EVENT_TYPE_NAME (event));
+ ret = dlna_src_handle_event_seek (dlna_src, pad, event, &new_event);
+ /* If we can handle it, substitute (potentially) modified seek event */
+ if (ret)
+ event = new_event;
+ }
break;
case GST_EVENT_FLUSH_START:
@@ -670,9 +668,12 @@ gst_dlna_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
break;
}
- /* If not handled, pass on to default pad handler */
- if (!ret) {
+ /* If wasn't refused locally, pass on to default pad handler */
+ if (ret) {
ret = gst_pad_event_default (pad, parent, event);
+ /* If we created a new event, unref it */
+ if (new_event)
+ gst_event_unref (new_event);
}
return ret;
@@ -704,10 +705,6 @@ gst_dlna_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
ret = dlna_src_handle_query_seeking (dlna_src, query);
break;
- case GST_QUERY_SEGMENT:
- ret = dlna_src_handle_query_segment (dlna_src, query);
- break;
-
case GST_QUERY_CONVERT:
ret = dlna_src_handle_query_convert (dlna_src, query);
break;
@@ -724,9 +721,12 @@ gst_dlna_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
ret = TRUE;
break;
+#if 0
+ /* REVIEW: dlnasrc is not a live element, you do not need to handle it */
case GST_QUERY_LATENCY:
/* Don't know latency, let some other element handle this */
break;
+#endif
case GST_QUERY_POSITION:
/* Don't know current position in stream, let some other element handle this */
@@ -867,70 +867,6 @@ dlna_src_handle_query_seeking (GstDlnaSrc * dlna_src, GstQuery * query)
return ret;
}
-/**
- * Responds to a segment query by returning rate along with start and stop
- *
- * @param dlna_src this element
- * @param query received segment query to respond to
- *
- * @return true if responded to query, false otherwise
- */
-static gboolean
-dlna_src_handle_query_segment (GstDlnaSrc * dlna_src, GstQuery * query)
-{
- gboolean ret = FALSE;
- GstFormat format;
- gdouble rate = 1.0;
- gint64 start = 0;
- gint64 end = 0;
-
- GST_LOG_OBJECT (dlna_src, "Called");
-
- if ((dlna_src->uri == NULL) || (dlna_src->server_info == NULL)) {
- GST_INFO_OBJECT (dlna_src,
- "No URI and/or HEAD response info, unable to handle query");
- return FALSE;
- }
- gst_query_parse_segment (query, &rate, &format, &start, &end);
-
- if (format == GST_FORMAT_BYTES) {
- if (dlna_src->byte_seek_supported) {
-
- gst_query_set_segment (query, dlna_src->rate, GST_FORMAT_BYTES,
- dlna_src->byte_start, dlna_src->byte_end);
- ret = TRUE;
-
- GST_DEBUG_OBJECT (dlna_src,
- "Segment info in bytes for this content, rate %f, start %"
- G_GUINT64_FORMAT ", end %" G_GUINT64_FORMAT,
- dlna_src->rate, dlna_src->byte_start, dlna_src->byte_end);
- } else
- GST_DEBUG_OBJECT (dlna_src,
- "Segment info in bytes not available for content item");
- } else if (format == GST_FORMAT_TIME) {
-
- if (dlna_src->time_seek_supported) {
- gst_query_set_segment (query, dlna_src->rate, GST_FORMAT_TIME,
- dlna_src->npt_start_nanos, dlna_src->npt_end_nanos);
- ret = TRUE;
-
- GST_DEBUG_OBJECT (dlna_src,
- "Time based segment info for this content by the server, rate %f, start %"
- GST_TIME_FORMAT ", end %" GST_TIME_FORMAT,
- dlna_src->rate,
- GST_TIME_ARGS (dlna_src->npt_start_nanos),
- GST_TIME_ARGS (dlna_src->npt_end_nanos));
- } else
- GST_DEBUG_OBJECT (dlna_src,
- "Segment info in media time not available for content item");
- } else {
- GST_DEBUG_OBJECT (dlna_src,
- "Got segment query with non-supported format type: %s, passing to default handler",
- GST_QUERY_TYPE_NAME (query));
- }
-
- return ret;
-}
/**
* Responds to a convert query by issuing head and returning conversion value
@@ -952,7 +888,7 @@ dlna_src_handle_query_convert (GstDlnaSrc * dlna_src, GstQuery * query)
GST_LOG_OBJECT (dlna_src, "Called");
- if (dlna_src->uri == NULL || !dlna_src->time_seek_supported) {
+ if (dlna_src->uri == NULL) {
GST_INFO_OBJECT (dlna_src, "Not enough info to handle conversion query");
return FALSE;
}
@@ -1004,70 +940,150 @@ dlna_src_handle_query_convert (GstDlnaSrc * dlna_src, GstQuery * query)
* Perform action necessary when seek event is received
*
* @param dlna_src this element
- * @param seek_event seek event which has been received
+ * @param event seek event which has been received
+ * @param return_event (updated) seek_event to send to souphttpsrc
*
- * @return true if this event has been handled, false otherwise
+ * @return true if the seek can be handled, else false
*/
static gboolean
dlna_src_handle_event_seek (GstDlnaSrc * dlna_src, GstPad * pad,
- GstEvent * event)
+ GstEvent *event, GstEvent **return_event)
{
GST_LOG_OBJECT (dlna_src, "Handle seek event");
gdouble rate;
GstFormat format;
GstSeekFlags flags;
- GstSeekType start_type;
- gint64 start;
- GstSeekType stop_type;
- gint64 stop;
- guint32 new_seqnum;
+ GstSeekType start_type, stop_type;
+ gint64 start, stop;
+#if 0
gboolean convert_start = FALSE;
+#endif
+ GstDlnaTrickMode trick_mode = GST_DLNA_MODE_NONE;
+
+ /* WIP: Improve seek handling
+ * souphttpsrc can only handle BYTE seeks, but we'll be sending a bogus BYTE seek
+ * event to it, unless we have working in GST_DLNA_MODE_RANGE.
+ *
+ * 1) Parse the seek values
+ * 2) If the server can handle the requested format (TIME or BYTES), use that
+ * 3) If the server cannot handle the requested format, attempt to convert it
+ *
+ * Note: If a BYTE seek event is seen before a TIME seek event, this is a sign
+ * of a non-standard GStreamer pipeline usage.
+ *
+ * If a seek of a given seqnum was previously handled successfully, following
+ * seek events with the same seqnum shall be refused, regardless of format.
+ */
+ /* FIXME/Unclear
+ * * In case of "live" elements (which increasing s0/sN), we need to get the
+ * updated range before confirming whether the seek can be handled or not
+ */
+
+ gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start, &stop_type, &stop);
+
+ GST_INFO_OBJECT (dlna_src,
+ "Got Seek event %d: rate: %3.1f, format: %s, flags: %d, start type: %d, start: %"
+ G_GUINT64_FORMAT ", stop type: %d, stop: %"
+ G_GUINT64_FORMAT, gst_event_get_seqnum (event), rate,
+ gst_format_get_name (format), flags, start_type, start, stop_type, stop);
if ((dlna_src->uri == NULL) || (dlna_src->server_info == NULL)) {
GST_INFO_OBJECT (dlna_src,
"No URI and/or HEAD response info, event handled");
- return TRUE;
+ return FALSE;
}
- new_seqnum = gst_event_get_seqnum (event);
- if (new_seqnum == dlna_src->time_seek_seqnum) {
- if (dlna_src->handled_time_seek_seqnum) {
- GST_INFO_OBJECT (dlna_src, "Already processed seek event %d", new_seqnum);
- return FALSE;
- } else
- /* *TODO* - see dlnasrc issue #63
- Got same event now byte based since some element converted it to bytes
- Convert here if time seek is supported since it is more accurate */
+ /* If we already handled that seek, return straight away succesfully */
+ if (gst_event_get_seqnum(event) == dlna_src->seek_seqnum) {
+ GST_DEBUG_OBJECT (dlna_src, "Already handled seek with the same seqnum");
+ return FALSE;
+ }
+
+ /* Can we handle the seek event directly ? */
+ if (format == GST_FORMAT_BYTES) {
+ /* If the server can handle Range queries and the requested positions are within
+ * the server range, use it */
+ if (dlna_src->byte_seek_supported) {
+ /* FIXME: support BYTE-based scan-mode ... which is insanely tricky
+ * It would need to work similarly to the TIME-based scan-mode mentionned
+ * below... except that we would need some hints as to what offset in bytes
+ * corresponds to which position in time
+ */
+ if (rate != 1.0) {
+ GST_WARNING_OBJECT (dlna_src, "FIXME: Implement byte-based scan-mode");
+ return FALSE;
+ }
+
+ /* FIXME, For now we just refuse seeks outside of server byte range, need to
+ * check DLNA specs to see what the client is meant to do. It might need to clamp
+ * the requested range */
+ if (start < dlna_src->byte_start || start > dlna_src->byte_end) {
+ GST_WARNING_OBJECT (dlna_src,
+ "Specified start byte %" G_GUINT64_FORMAT
+ " is not valid, valid range: %" G_GUINT64_FORMAT
+ " to %" G_GUINT64_FORMAT, start,
+ dlna_src->byte_start, dlna_src->byte_end);
+ return FALSE;
+ }
+
+ /* Regular BYTE-based playback */
+ trick_mode = GST_DLNA_MODE_RANGE;
+ goto process_seek;
+ }
+ } else if (format == GST_FORMAT_TIME) {
+ /* Server supports playspeed and the requested rate */
+ if (rate != 1.0 && dlna_src_is_rate_supported (dlna_src, rate)) {
+ trick_mode = GST_DLNA_MODE_PLAYSPEED;
+ goto process_seek;
+ }
+
if (dlna_src->time_seek_supported) {
- convert_start = TRUE;
- GST_INFO_OBJECT (dlna_src,
- "Set flag to convert time based seek to byte since server does support time seeks");
- } else
- GST_INFO_OBJECT (dlna_src,
- "Unable to convert time based seek to byte since server does not support time seeks");
+ if (rate != 1.0) {
+ /* FIXME: in order to support rate != 1.0 when the server doesn't support
+ * playspeed, we need to requested "chunks" of the stream (using time seek)
+ * in a loop */
+ GST_WARNING_OBJECT (dlna_src, "FIXME: Implement time-based scan mode");
+ return FALSE;
+ }
+ if (((start == -1) || (start >= dlna_src->npt_start_nanos && start <= dlna_src->npt_end_nanos)) &&
+ ((stop == -1) || (stop >= dlna_src->npt_start_nanos && stop <= dlna_src->npt_end_nanos))) {
+ trick_mode = GST_DLNA_MODE_TIMERANGE;
+ goto process_seek;
+ }
+
+ /* FIXME, For now we just refuse seeks outside of server time range, need to
+ * check DLNA specs to see what the client is meant to do. It might need to clamp
+ * the requested range */
+ GST_WARNING_OBJECT (dlna_src, "Requested time seek outside of server range");
+ return FALSE;
+ }
} else {
- dlna_src->handled_time_seek_seqnum = FALSE;
- dlna_src->time_seek_seqnum = new_seqnum;
- dlna_src->time_seek_event_start = 0;
+ GST_WARNING_OBJECT (dlna_src, "Seek event in unhandled format");
+ return FALSE;
}
- gst_event_parse_seek (event, &rate, (GstFormat *) & format,
- (GstSeekFlags *) & flags,
- (GstSeekType *) & start_type, (gint64 *) & start,
- (GstSeekType *) & stop_type, (gint64 *) & stop);
-
- GST_INFO_OBJECT (dlna_src,
- "Got Seek event %d: rate: %3.1f, format: %s, flags: %d, start type: %d, start: %"
- G_GUINT64_FORMAT ", stop type: %d, stop: %"
- G_GUINT64_FORMAT, gst_event_get_seqnum (event), rate,
- gst_format_get_name (format), flags, start_type, start, stop_type, stop);
+ /* FIXME: If we end up here, it means that the server can't handle the
+ * requested format, we need to convert it
+ */
+ GST_WARNING_OBJECT (dlna_src, "FIXME : Implement BYTE<=>TIME conversion");
+ return FALSE;
+ process_seek:
+ /* IMPORTANT
+ * From this point forwards, we *know* the seek will succeed
+ * IMPORTANT
+ */
+#if 0
if (convert_start) {
GST_INFO_OBJECT (dlna_src,
"Supplied start byte %" G_GUINT64_FORMAT
" ignored, converting seek event start time %" GST_TIME_FORMAT
" to bytes", start, GST_TIME_ARGS (dlna_src->time_seek_event_start));
+ /* FIXME: This makes *no* sense whatsoever.
+ * So the servert doesn't handle TimeSeek ... so you attempt to convert
+ * a npt position query to bytes by doing a HEAD request on the server ...
+ * by using "TimeSeekRange.dlna.org: npt=" ... ????? */
if (!dlna_src_convert_npt_nanos_to_bytes (dlna_src,
dlna_src->time_seek_event_start, (guint64 *) & start)) {
GST_WARNING_OBJECT (dlna_src, "Problems converting to bytes");
@@ -1081,43 +1097,52 @@ dlna_src_handle_event_seek (GstDlnaSrc * dlna_src, GstPad * pad,
return TRUE;
}
- /* *TODO* - is this needed here??? Assign play rate to supplied rate */
- dlna_src->rate = rate;
-
- dlna_src->requested_rate = rate;
- dlna_src->requested_format = format;
- dlna_src->requested_start = start;
- dlna_src->requested_stop = GST_CLOCK_TIME_NONE;
- if (stop > -1)
- dlna_src->requested_stop = stop;
+#endif
+ /* Store values in requested segment */
+ gst_segment_init (&dlna_src->requested_segment, format);
+ dlna_src->requested_segment.start = start;
+ dlna_src->requested_segment.stop = stop;
+ dlna_src->requested_segment.rate = rate;
+ dlna_src->seek_seqnum = gst_event_get_seqnum(event);
+ dlna_src->trick_mode = trick_mode;
+
+#if 0
+ /* FIXME: This sounds weird. Isn't there a reliable mechanism to ask the server
+ * to send data from the current live position ?
+ * furthermore ... "< 0" ??
+ */
if (dlna_src->is_live && stop < 0) {
dlna_src->requested_stop = dlna_src->npt_end_nanos;
GST_INFO_OBJECT (dlna_src,
"Set requested stop to end of range since content is live: %"
G_GUINT64_FORMAT, dlna_src->requested_stop);
}
+#endif
+ /* FIXME/WARNING, something doesn't make sense, adjust_http_src_headers() has codepath
+ * that supposedly work with FORMAT_BYTES... */
/* ignore GST_FORMAT_BYTES from inside GStreamer, don't do http header adjust */
- if ((dlna_src->uri) && (dlna_src->server_info) &&
- (dlna_src->server_info->content_features != NULL) &&
+ if (dlna_src->server_info->content_features != NULL &&
(format == GST_FORMAT_TIME)) {
- if (!dlna_src_adjust_http_src_headers (dlna_src, dlna_src->requested_rate,
- dlna_src->requested_format, dlna_src->requested_start,
- dlna_src->requested_stop, new_seqnum)) {
+ if (!dlna_src_adjust_http_src_headers (dlna_src)) {
GST_ERROR_OBJECT (dlna_src, "Problems adjusting soup http src headers");
- /* Returning true to prevent further processing */
- return TRUE;
+ return FALSE;
}
- } else
+ /* Finally create a new bogus BYTE seek event (proper segment will be modified in return) */
+ *return_event = gst_event_new_seek (1.0, GST_FORMAT_BYTES, flags, GST_SEEK_TYPE_SET, 0,
+ GST_SEEK_TYPE_NONE, -1);
+ } else {
GST_INFO_OBJECT (dlna_src,
"No header adjustments since server only supports Range requests");
+ /* Forward event as-is */
+ *return_event = gst_event_ref (event);
+ }
- GST_DEBUG_OBJECT (dlna_src,
- "returning false to make sure souphttpsrc gets chance to process");
- return FALSE;
+ return TRUE;
}
+#if 0
/**
* Determines if the requested rate and/or position change is valid. Seek type is
* ignored since the position is always treated as absolute since a position must be
@@ -1158,7 +1183,6 @@ dlna_src_is_change_valid (GstDlnaSrc * dlna_src, gfloat rate,
if (format == GST_FORMAT_BYTES) {
if (dlna_src->byte_seek_supported) {
-
if (start < dlna_src->byte_start || start > dlna_src->byte_end) {
GST_WARNING_OBJECT (dlna_src,
"Specified start byte %" G_GUINT64_FORMAT
@@ -1182,6 +1206,8 @@ dlna_src_is_change_valid (GstDlnaSrc * dlna_src, gfloat rate,
} else if (format == GST_FORMAT_TIME) {
if (dlna_src->time_seek_supported) {
+ /* FIXME : Seeking in ... live ??
+ * Shouldn't this be handled in a completely different codepath ? */
if (dlna_src->is_live) {
GST_INFO_OBJECT (dlna_src, "Update live content range info");
if (!dlna_src_soup_issue_head (dlna_src,
@@ -1234,6 +1260,7 @@ dlna_src_is_change_valid (GstDlnaSrc * dlna_src, gfloat rate,
return TRUE;
}
+#endif
/**
* Determines if current rate is supported by server based on current
@@ -1275,19 +1302,16 @@ dlna_src_is_rate_supported (GstDlnaSrc * dlna_src, gfloat rate)
* postion and rate. Instruct souphttpsrc to exclude range header if it conflicts
* with any of the extra headers which have been added.
*
+ * The requested_segment and seqnum should be initialized with requested range/rate/..
+ * before calling this function
+ *
* @param dlna_src this element
- * @param rate requested rate to include in playspeed header
- * @param format create either time or byte based seek header
- * @param start starting position to include, will be either bytes or time depending on format
- * @param stop stopping position to include, will be -1 if unspecified
*
* @return true if extra headers were successfully created, false otherwise
*/
static gboolean
-dlna_src_adjust_http_src_headers (GstDlnaSrc * dlna_src, gfloat rate,
- GstFormat format, guint64 start, guint64 stop, guint32 new_seqnum)
+dlna_src_adjust_http_src_headers (GstDlnaSrc * dlna_src)
{
- GValue struct_value = G_VALUE_INIT;
GstStructure *extra_headers_struct;
gboolean disable_range_header = FALSE;
@@ -1311,11 +1335,9 @@ dlna_src_adjust_http_src_headers (GstDlnaSrc * dlna_src, gfloat rate,
gchar range_dtcp_field_value[64] = { 0 };
/* Make sure range header is included by default */
- GValue boolean_value = G_VALUE_INIT;
- g_value_init (&boolean_value, G_TYPE_BOOLEAN);
- g_value_set_boolean (&boolean_value, FALSE);
- g_object_set_property (G_OBJECT (dlna_src->http_src), "exclude-range-header",
- &boolean_value);
+ /* FIXME : Replace all occurences of g_object_set_property() with
+ * the smaller/saner g_object_set() */
+ g_object_set (dlna_src->http_src, "exclude-range-header", FALSE, NULL);
/* Create header structure with dlna transfer mode header */
extra_headers_struct = gst_structure_new ("extraHeadersStruct",
@@ -1325,19 +1347,20 @@ dlna_src_adjust_http_src_headers (GstDlnaSrc * dlna_src, gfloat rate,
return FALSE;
}
+ /* First try PLAYSPEED */
/* If rate != 1.0, add playspeed header and time seek range header */
- if (rate != 1.0) {
+ if (dlna_src->trick_mode == GST_DLNA_MODE_PLAYSPEED) {
/* Get string representation of rate (use original values to make fractions easy like 1/3) */
for (i = 0; i < dlna_src->server_info->content_features->playspeeds_cnt;
i++) {
- if (dlna_src->server_info->content_features->playspeeds[i] == rate) {
+ if (dlna_src->server_info->content_features->playspeeds[i] == dlna_src->requested_segment.rate) {
rateStr = dlna_src->server_info->content_features->playspeed_strs[i];
break;
}
}
if (rateStr == NULL) {
GST_ERROR_OBJECT (dlna_src,
- "Unable to get string representation of supported rate: %lf", rate);
+ "Unable to get string representation of supported rate: %lf", dlna_src->requested_segment.rate);
return FALSE;
}
g_snprintf (playspeed_field_value, 64, "%s%s",
@@ -1351,86 +1374,49 @@ dlna_src_adjust_http_src_headers (GstDlnaSrc * dlna_src, gfloat rate,
}
/* Add time seek header for all non 1x rates or for time based seeks */
- if (rate != 1.0 || (format == GST_FORMAT_TIME
- && dlna_src->time_seek_supported)) {
- if (format != GST_FORMAT_TIME) {
- if (!dlna_src_convert_bytes_to_npt_nanos (dlna_src, start,
- &start_time_nanos)) {
- GST_WARNING_OBJECT (dlna_src,
- "Problems converting start %" G_GUINT64_FORMAT " to npt ", start);
- return FALSE;
- } else
- GST_INFO_OBJECT (dlna_src,
- "Due to non 1x playspeed, converted start %" G_GUINT64_FORMAT
- " bytes into time %" G_GUINT64_FORMAT, start, start_time_nanos);
-
- if (stop > GST_CLOCK_TIME_NONE) {
- if (!dlna_src_convert_bytes_to_npt_nanos (dlna_src, stop,
- &stop_time_nanos)) {
- GST_WARNING_OBJECT (dlna_src,
- "Problems converting stop %" G_GUINT64_FORMAT " to npt ", stop);
- return FALSE;
- } else
- GST_INFO_OBJECT (dlna_src,
- "Due to non 1x playspeed, converted stop %" G_GUINT64_FORMAT
- " bytes into time %" G_GUINT64_FORMAT, start, start_time_nanos);
- } else
- GST_INFO_OBJECT (dlna_src, "Stop undefined, no conversion needed");
- } else {
- /* Already in time format, no conversion necessary */
- start_time_nanos = start;
- stop_time_nanos = stop;
- GST_INFO_OBJECT (dlna_src,
- "Using start %" G_GUINT64_FORMAT " and stop %" G_GUINT64_FORMAT,
- start_time_nanos, stop_time_nanos);
- }
+ if (dlna_src->trick_mode == GST_DLNA_MODE_PLAYSPEED ||
+ dlna_src->trick_mode == GST_DLNA_MODE_TIMERANGE) {
+ /* Already in time format, no conversion necessary */
+ start_time_nanos = dlna_src->requested_segment.start;
+ stop_time_nanos = dlna_src->requested_segment.stop;
+ GST_INFO_OBJECT (dlna_src,
+ "Using start %" G_GUINT64_FORMAT " and stop %" G_GUINT64_FORMAT,
+ start_time_nanos, stop_time_nanos);
/* Convert time from nanos into secs for header value string */
- if (dlna_src->time_seek_supported) {
- start_time_secs = start_time_nanos / GST_SECOND;
-
- if (stop_time_nanos == GST_CLOCK_TIME_NONE) {
- GST_INFO_OBJECT (dlna_src,
- "Stop time is undefined, just including start time");
- g_snprintf (time_seek_range_field_value, 64,
- "%s%" G_GUINT64_FORMAT ".0-", time_seek_range_field_value_prefix,
- start_time_secs);
- } else {
- stop_time_secs = stop_time_nanos / GST_SECOND;
- GST_INFO_OBJECT (dlna_src, "Including stop secs: %" G_GUINT64_FORMAT,
- stop_time_secs);
- g_snprintf (time_seek_range_field_value, 64,
- "%s%" G_GUINT64_FORMAT ".0-%" G_GUINT64_FORMAT ".0",
- time_seek_range_field_value_prefix, start_time_secs, stop_time_secs);
- }
- gst_structure_set (extra_headers_struct, time_seek_range_field_name,
- G_TYPE_STRING, &time_seek_range_field_value, NULL);
+ start_time_secs = start_time_nanos / GST_SECOND;
+ if (stop_time_nanos == GST_CLOCK_TIME_NONE) {
GST_INFO_OBJECT (dlna_src,
- "Adjust headers by including TimeSeekRange header value: %s",
- time_seek_range_field_value);
+ "Stop time is undefined, just including start time");
+ g_snprintf (time_seek_range_field_value, 64,
+ "%s%" G_GUINT64_FORMAT ".0-", time_seek_range_field_value_prefix,
+ start_time_secs);
+ } else {
+ stop_time_secs = stop_time_nanos / GST_SECOND;
+ GST_INFO_OBJECT (dlna_src, "Including stop secs: %" G_GUINT64_FORMAT,
+ stop_time_secs);
+ g_snprintf (time_seek_range_field_value, 64,
+ "%s%" G_GUINT64_FORMAT ".0-%" G_GUINT64_FORMAT ".0",
+ time_seek_range_field_value_prefix, start_time_secs, stop_time_secs);
}
+ gst_structure_set (extra_headers_struct, time_seek_range_field_name,
+ G_TYPE_STRING, &time_seek_range_field_value, NULL);
+
+ GST_INFO_OBJECT (dlna_src,
+ "Adjust headers by including TimeSeekRange header value: %s",
+ time_seek_range_field_value);
/* Set flag so range header is not included by souphttpsrc */
disable_range_header = TRUE;
-
- /* Record seqnum since have handled this event as time based seek */
- dlna_src->handled_time_seek_seqnum = TRUE;
} else
GST_INFO_OBJECT (dlna_src,
"Not adjusting with playspeed or time seek range ");
- if (format == GST_FORMAT_TIME && !dlna_src->time_seek_supported) {
- GST_INFO_OBJECT (dlna_src,
- "Saving start time incase get this event again in bytes in order to do acurate conversion");
- dlna_src->time_seek_event_start = start;
- }
-
/* If dtcp protected content and rate = 1.0, add range.dtcp.com header */
- if (rate == 1.0 && format == GST_FORMAT_BYTES
- && dlna_src->is_encrypted && dlna_src->byte_seek_supported) {
+ if (dlna_src->trick_mode == GST_DLNA_MODE_RANGE && dlna_src->is_encrypted) {
g_snprintf (range_dtcp_field_value, 64, "%s%" G_GUINT64_FORMAT "-",
- range_dtcp_field_value_prefix, start);
+ range_dtcp_field_value_prefix, dlna_src->requested_segment.start);
gst_structure_set (extra_headers_struct, range_dtcp_field_name,
G_TYPE_STRING, &range_dtcp_field_value, NULL);
@@ -1444,17 +1430,12 @@ dlna_src_adjust_http_src_headers (GstDlnaSrc * dlna_src, gfloat rate,
} else
GST_INFO_OBJECT (dlna_src, "Not adjusting with range.dtcp.com");
- g_value_init (&struct_value, GST_TYPE_STRUCTURE);
- gst_value_set_structure (&struct_value, extra_headers_struct);
- g_object_set_property (G_OBJECT (dlna_src->http_src), "extra-headers",
- &struct_value);
+ g_object_set (dlna_src->http_src, "extra-headers", extra_headers_struct, NULL);
gst_structure_free (extra_headers_struct);
/* Disable range header if necessary */
- if (disable_range_header || !dlna_src->byte_seek_supported) {
- g_value_set_boolean (&boolean_value, TRUE);
- g_object_set_property (G_OBJECT (dlna_src->http_src),
- "exclude-range-header", &boolean_value);
+ if (disable_range_header) {
+ g_object_set (dlna_src->http_src, "exclude-range-header", TRUE, NULL);
GST_INFO_OBJECT (dlna_src,
"Adjust headers by excluding range header, flag: %d, supported: %d",
disable_range_header, dlna_src->byte_seek_supported);
@@ -1560,6 +1541,72 @@ dlna_src_uri_init (GstDlnaSrc * dlna_src, GError ** error)
return TRUE;
}
+static GstPadProbeReturn
+dlna_src_outgoing_probe(GstPad *pad, GstPadProbeInfo *info, GstDlnaSrc *dlna_src)
+{
+ GstEvent *event;
+
+ event = GST_PAD_PROBE_INFO_EVENT (info);
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_SEGMENT:
+ {
+ const GstSegment *segment;
+ GstEvent *new_event;
+ GstSegment new_segment;
+
+ GST_DEBUG_OBJECT (dlna_src, "Saw outgoing segment %"GST_PTR_FORMAT, event);
+ gst_event_parse_segment(event, &segment);
+ gst_segment_copy_into ((const GstSegment*) &dlna_src->requested_segment, &new_segment);
+
+ /* FIXME : *actually* replace with what the server returns (i.e. the response headers in the
+ * custom downstream sticky event below) */
+ switch (dlna_src->trick_mode) {
+ case GST_DLNA_MODE_RANGE:
+ /* Nothing to modify, we normally sent the full seek request */
+ break;
+ case GST_DLNA_MODE_TIMERANGE:
+ {
+ GST_DEBUG_OBJECT (dlna_src, "TIMERANGE MODE");
+ new_segment.time = new_segment.start;
+ new_event = gst_event_new_segment ((const GstSegment*) &new_segment);
+ gst_event_set_seqnum (new_event, dlna_src->seek_seqnum);
+ gst_event_unref (event);
+ GST_DEBUG_OBJECT (dlna_src, "Replacing with event %"GST_PTR_FORMAT, new_event);
+ GST_PAD_PROBE_INFO_DATA (info) = new_event;
+ }
+ break;
+ case GST_DLNA_MODE_PLAYSPEED:
+ GST_DEBUG_OBJECT (dlna_src, "PLAYSPEED MODE");
+ /* Switch rate and applied_rate (server modified the stream) */
+ new_segment.time = new_segment.start;
+ new_segment.applied_rate = new_segment.rate;
+ new_segment.rate = 1.0;
+ new_event = gst_event_new_segment ((const GstSegment*) &new_segment);
+ gst_event_set_seqnum (new_event, dlna_src->seek_seqnum);
+ gst_event_unref (event);
+ GST_DEBUG_OBJECT (dlna_src, "Replacing with event %"GST_PTR_FORMAT, new_event);
+ GST_PAD_PROBE_INFO_DATA (info) = new_event;
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case GST_EVENT_CUSTOM_DOWNSTREAM_STICKY:
+ {
+ if (gst_event_has_name (event, "http-headers")) {
+ GST_DEBUG_OBJECT (dlna_src, "Received request/response headers from souphttpsrc: %"GST_PTR_FORMAT, event);
+ }
+ }
+ default:
+ GST_DEBUG_OBJECT (dlna_src, "Saw event %"GST_PTR_FORMAT, event);
+ break;
+ }
+
+ return GST_PAD_PROBE_OK;
+}
+
/**
* Perform actions necessary based on supplied URI which is called by
* playbin when this element is selected as source.
@@ -1616,6 +1663,10 @@ dlna_src_setup_bin (GstDlnaSrc * dlna_src, GError ** error)
return FALSE;
}
+ gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
+ (GstPadProbeCallback) dlna_src_outgoing_probe,
+ dlna_src, NULL);
+
GST_DEBUG_OBJECT (dlna_src, "Got src pad to use for ghostpad of dlnasrc bin");
dlna_src->src_pad = gst_ghost_pad_new ("src", pad);
gst_pad_set_active (dlna_src->src_pad, TRUE);
@@ -1641,6 +1692,7 @@ dlna_src_setup_bin (GstDlnaSrc * dlna_src, GError ** error)
return TRUE;
}
+
/**
* Setup dtcp decoder element and add to src in order to handle DTCP encrypted
* content
diff --git a/src/gstdlnasrc.h b/src/gstdlnasrc.h
index e414e6f..30f832d 100644
--- a/src/gstdlnasrc.h
+++ b/src/gstdlnasrc.h
@@ -44,6 +44,14 @@ G_BEGIN_DECLS
#define PLAYSPEEDS_MAX_CNT 64
+/* Seek modes used */
+typedef enum {
+ GST_DLNA_MODE_NONE = 1 << 0,
+ GST_DLNA_MODE_PLAYSPEED = 1 << 1,
+ GST_DLNA_MODE_TIMERANGE = 1 << 2,
+ GST_DLNA_MODE_RANGE = 1 << 3
+} GstDlnaTrickMode;
+
typedef struct _GstDlnaSrc GstDlnaSrc;
typedef struct _GstDlnaSrcClass GstDlnaSrcClass;
@@ -61,6 +69,7 @@ struct _GstDlnaSrc
guint dtcp_blocksize;
gchar* dtcp_key_storage;
+ /* Current URI */
gchar *uri;
SoupSession *soup_session;
@@ -68,15 +77,19 @@ struct _GstDlnaSrc
GstDlnaSrcHeadResponse* server_info;
- gfloat rate;
- gfloat requested_rate;
- GstFormat requested_format;
- guint64 requested_start;
- guint64 requested_stop;
+ /* Requested seek values from downstream */
+ GstSegment requested_segment;
+ /* Sequence number from upstream seek event to be used
+ * for any following events */
+ guint32 seek_seqnum;
+ /* Trick mode system used */
+ GstDlnaTrickMode trick_mode;
+
+ /* Active segment (might be different from requested segment)
+ * In sync with current SEGMENT event outputted */
+ GstSegment current_segment;
- guint32 time_seek_seqnum;
guint64 time_seek_event_start;
- gboolean handled_time_seek_seqnum;
gboolean is_uri_initialized;
gboolean is_live;