[rygel] server,media-engines: Add support for playspeed requests
- From: Jens Georg <jensgeorg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [rygel] server,media-engines: Add support for playspeed requests
- Date: Tue, 17 Feb 2015 00:06:36 +0000 (UTC)
commit 41e57590992aa93a69844a4bef78528287d71dbf
Author: Jens Georg <mail jensge org>
Date: Fri Feb 13 14:49:33 2015 +0100
server,media-engines: Add support for playspeed requests
Code based on Cablelabs's CVP-2 implementation
Signed-off-by: Jens Georg <mail jensge org>
src/librygel-server/filelist.am | 5 +-
src/librygel-server/rygel-data-source.vala | 13 ++-
src/librygel-server/rygel-http-get-handler.vala | 8 ++
src/librygel-server/rygel-http-get.vala | 56 +++++++++-
.../rygel-http-resource-handler.vala | 4 +
src/librygel-server/rygel-http-response.vala | 4 +-
.../rygel-http-time-seek-request.vala | 56 +++++++++-
src/librygel-server/rygel-media-container.vala | 9 ++-
src/librygel-server/rygel-playspeed-request.vala | 115 ++++++++++++++++++++
src/librygel-server/rygel-playspeed-response.vala | 90 +++++++++++++++
src/librygel-server/rygel-playspeed.vala | 100 +++++++++++++++++
.../gstreamer/rygel-gst-data-source.vala | 9 ++-
.../simple/rygel-simple-data-source.vala | 10 ++-
13 files changed, 457 insertions(+), 22 deletions(-)
---
diff --git a/src/librygel-server/filelist.am b/src/librygel-server/filelist.am
index ef4d309..e2ed3b2 100644
--- a/src/librygel-server/filelist.am
+++ b/src/librygel-server/filelist.am
@@ -81,4 +81,7 @@ LIBRYGEL_SERVER_NONVAPI_SOURCE_FILES = \
rygel-wmp-hacks.vala \
rygel-xbmc-hacks.vala \
rygel-xbox-hacks.vala \
- rygel-data-sink.vala
+ rygel-data-sink.vala \
+ rygel-playspeed.vala \
+ rygel-playspeed-request.vala \
+ rygel-playspeed-response.vala
diff --git a/src/librygel-server/rygel-data-source.vala b/src/librygel-server/rygel-data-source.vala
index d663ff8..8937fc4 100644
--- a/src/librygel-server/rygel-data-source.vala
+++ b/src/librygel-server/rygel-data-source.vala
@@ -25,6 +25,7 @@
public errordomain Rygel.DataSourceError {
GENERAL,
SEEK_FAILED,
+ PLAYSPEED_FAILED
}
/**
@@ -61,20 +62,24 @@ public errordomain Rygel.DataSourceError {
*/
public interface Rygel.DataSource : GLib.Object {
/**
- * Preroll the data with the given seek
+ * Preroll the data with the given seek and playspeed.
*
* @param seek optional seek/range specifier
- *
+ * @param playspeed optional playback rate specifier. This will only be provided
+ * when a scaled rate is requested (the speed will not be 0.0 or 1.0)
+ *
* @return List of HTTPResponseElements appropriate for the content request and
- * optional seek (e.g. Content-Range, TimeSeekRange.dlna.org,
+ * optional seek/playspeed (e.g. Content-Range, TimeSeekRange.dlna.org,
* etc) or null/empty list if none are appropriate. Note: the list will
* be processed in-order by the caller.
*
* @throws Error if anything goes wrong while prerolling the stream.
* Throws DataSourceError.SEEK_FAILED if a seek method is not supported or the
* range is not fulfillable.
+ * Throws PLAYSPEED_FAILED if the rate is not supported or fulfillable.
*/
- public abstract Gee.List<HTTPResponseElement> ? preroll (HTTPSeekRequest? seek)
+ public abstract Gee.List<HTTPResponseElement> ? preroll (HTTPSeekRequest? seek,
+ PlaySpeedRequest? playspeed)
throws Error;
/**
diff --git a/src/librygel-server/rygel-http-get-handler.vala b/src/librygel-server/rygel-http-get-handler.vala
index 20dee38..9c68d4d 100644
--- a/src/librygel-server/rygel-http-get-handler.vala
+++ b/src/librygel-server/rygel-http-get-handler.vala
@@ -99,6 +99,14 @@ public abstract class Rygel.HTTPGetHandler: GLib.Object {
public virtual bool supports_time_seek () {
return false;
}
+
+ /**
+ * Returns true if the handler supports any play speed requests.
+ */
+ public virtual bool supports_playspeed () {
+ return false;
+ }
+
/**
* Create an HTTPResponse object that will render the body.
*/
diff --git a/src/librygel-server/rygel-http-get.vala b/src/librygel-server/rygel-http-get.vala
index 6230f38..1b759ec 100644
--- a/src/librygel-server/rygel-http-get.vala
+++ b/src/librygel-server/rygel-http-get.vala
@@ -35,6 +35,7 @@ public class Rygel.HTTPGet : HTTPRequest {
private const string TRANSFER_MODE_HEADER = "transferMode.dlna.org";
public HTTPSeekRequest seek;
+ public PlaySpeedRequest speed_request;
public HTTPGetHandler handler;
@@ -130,6 +131,37 @@ public class Rygel.HTTPGet : HTTPRequest {
}
}
+ // Check for DLNA PlaySpeed request only if Range or Range.dtcp.com is not
+ // in the request. DLNA 7.5.4.3.3.19.2, DLNA Link Protection : 7.6.4.4.2.12
+ // (is 7.5.4.3.3.19.2 compatible with the use case in 7.5.4.3.2.24.5?)
+ // Note: We need to check the speed first since direction factors into validating
+ // the time-seek request
+ try {
+ if ( !requested_byte_seek
+ && PlaySpeedRequest.requested (this) ) {
+ this.speed_request = new PlaySpeedRequest.from_request (this);
+ debug ("Processing playspeed %s", speed_request.speed.to_string ());
+ if (this.speed_request.speed.is_normal_rate ()) {
+ // This is not a scaled-rate request. Treat it as if it wasn't even there
+ this.speed_request = null;
+ }
+ } else {
+ this.speed_request = null;
+ }
+ } catch (PlaySpeedError error) {
+ this.server.unpause_message (this.msg);
+ if (error is PlaySpeedError.INVALID_SPEED_FORMAT) {
+ this.end (Soup.Status.BAD_REQUEST, error.message);
+ // Per DLNA 7.5.4.3.3.16.3
+ } else if (error is PlaySpeedError.SPEED_NOT_PRESENT) {
+ this.end (Soup.Status.NOT_ACCEPTABLE, error.message);
+ // Per DLNA 7.5.4.3.3.16.5
+ } else {
+ throw error;
+ }
+ debug ("Error processing PlaySpeed: %s", error.message);
+ return;
+ }
try {
// Order is intentional here
if (supports_byte_seek && requested_byte_seek) {
@@ -139,7 +171,8 @@ public class Rygel.HTTPGet : HTTPRequest {
this.seek = byte_seek;
} else if (supports_time_seek && requested_time_seek) {
// Assert: speed_request has been checked/processed
- var time_seek = new HTTPTimeSeekRequest (this);
+ var time_seek = new HTTPTimeSeekRequest (this, ((this.speed_request == null) ? null
+ : this.speed_request.speed) );
debug ("Processing " + time_seek.to_string ());
this.seek = time_seek;
} else {
@@ -201,7 +234,13 @@ public class Rygel.HTTPGet : HTTPRequest {
{
Soup.Encoding response_body_encoding;
// See DLNA 7.5.4.3.2.15 for requirements
- if (response_size > 0) {
+ if ((this.speed_request != null)
+ && (this.msg.get_http_version () != Soup HTTPVersion 1_0) ) {
+ // We'll want the option to insert PlaySpeed position information
+ // whether or not we know the length (see DLNA 7.5.4.3.3.17)
+ response_body_encoding = Soup.Encoding.CHUNKED;
+ debug ("Response encoding set to CHUNKED");
+ } else if (response_size > 0) {
// TODO: Incorporate ChunkEncodingMode.dlna.org request into this block
response_body_encoding = Soup.Encoding.CONTENT_LENGTH;
debug ("Response encoding set to CONTENT-LENGTH");
@@ -220,9 +259,10 @@ public class Rygel.HTTPGet : HTTPRequest {
// Determine the Vary header (if not HTTP 1.0)
{
- // Per DLNA 7.5.4.3.2.35.4, the Vary header needs to include the timeseek
- // header if it is supported for the resource/uri
- if (supports_time_seek) {
+ // Per DLNA 7.5.4.3.2.35.4, the Vary header needs to include the timeseek and/or
+ // playspeed header if both/either are supported for the resource/uri
+ bool supports_playspeed = PlaySpeedRequest.supported (this);
+ if (supports_time_seek || supports_playspeed) {
if (this.msg.get_http_version () != Soup HTTPVersion 1_0) {
var vary_header = new StringBuilder
(this.msg.response_headers.get_list ("Vary"));
@@ -232,6 +272,12 @@ public class Rygel.HTTPGet : HTTPRequest {
}
vary_header.append (HTTPTimeSeekRequest.TIMESEEKRANGE_HEADER);
}
+ if (supports_playspeed) {
+ if (vary_header.len > 0) {
+ vary_header.append (",");
+ }
+ vary_header.append (PlaySpeedRequest.PLAYSPEED_HEADER);
+ }
this.msg.response_headers.replace ("Vary", vary_header.str);
}
}
diff --git a/src/librygel-server/rygel-http-resource-handler.vala
b/src/librygel-server/rygel-http-resource-handler.vala
index 51a49a4..2b0b78e 100644
--- a/src/librygel-server/rygel-http-resource-handler.vala
+++ b/src/librygel-server/rygel-http-resource-handler.vala
@@ -107,4 +107,8 @@ internal class Rygel.HTTPMediaResourceHandler : HTTPGetHandler {
return media_resource.supports_arbitrary_time_seek ()
|| media_resource.supports_limited_time_seek ();
}
+
+ public override bool supports_playspeed () {
+ return media_resource.supports_playspeed ();
+ }
}
diff --git a/src/librygel-server/rygel-http-response.vala b/src/librygel-server/rygel-http-response.vala
index 3845689..ddc6546 100644
--- a/src/librygel-server/rygel-http-response.vala
+++ b/src/librygel-server/rygel-http-response.vala
@@ -34,6 +34,7 @@ public class Rygel.HTTPResponse : GLib.Object, Rygel.StateMachine {
public Cancellable cancellable { get; set; }
public HTTPSeekRequest seek;
+ public PlaySpeedRequest speed;
private SourceFunc run_continue;
private int _priority = -1;
@@ -71,6 +72,7 @@ public class Rygel.HTTPResponse : GLib.Object, Rygel.StateMachine {
this.msg = request.msg;
this.cancellable = request_handler.cancellable;
this.seek = request.seek;
+ this.speed = request.speed_request;
this.src = src;
this.sink = new DataSink (this.src, this.server, this.msg, this.seek);
this.src.done.connect ( () => {
@@ -102,7 +104,7 @@ public class Rygel.HTTPResponse : GLib.Object, Rygel.StateMachine {
}
public Gee.List<HTTPResponseElement> ? preroll () throws Error {
- return this.src.preroll (this.seek);
+ return this.src.preroll (this.seek, this.speed);
}
public async void run () {
diff --git a/src/librygel-server/rygel-http-time-seek-request.vala
b/src/librygel-server/rygel-http-time-seek-request.vala
index 70fceb0..008c77d 100644
--- a/src/librygel-server/rygel-http-time-seek-request.vala
+++ b/src/librygel-server/rygel-http-time-seek-request.vala
@@ -59,13 +59,21 @@ public class Rygel.HTTPTimeSeekRequest : Rygel.HTTPSeekRequest {
* Note: This constructor will check the syntax of the request (per DLNA 7.5.4.3.2.24.3)
* as well as perform some range validation. If the provided request is associated
* with a handler that can provide content duration, the start and end time will
- * be checked for out-of-bounds conditions.
+ * be checked for out-of-bounds conditions. Additionally, the start and end will
+ * be checked according to playspeed direction (with rate +1.0 assumed when speed
+ * is not provided). When speed is provided, the range end parameter check is
+ * relaxed when the rate is not +1.0 (per DLNA 7.5.4.3.2.24.4).
+ *
* @param request The HTTP GET/HEAD request
+ * @speed speed An associated speed request
*/
- internal HTTPTimeSeekRequest (HTTPGet request)
+ internal HTTPTimeSeekRequest (HTTPGet request, PlaySpeed ? speed)
throws HTTPSeekRequestError {
base ();
+ bool positive_rate = (speed == null) || speed.is_positive ();
+ bool trick_mode = (speed != null) && !speed.is_normal_rate ();
+
this.total_duration = request.handler.get_resource_duration ();
if (this.total_duration <= 0) {
this.total_duration = UNSPECIFIED;
@@ -100,15 +108,37 @@ public class Rygel.HTTPTimeSeekRequest : Rygel.HTTPSeekRequest {
TIMESEEKRANGE_HEADER, range);
}
- this.start_time = start;
+ // Check for out-of-bounds range start and clamp it in if in trick/scan mode
+ if ((this.total_duration != UNSPECIFIED) && (start > this.total_duration)) {
+ if (trick_mode && !positive_rate) { // Per DLNA 7.5.4.3.2.24.4
+ this.start_time = this.total_duration;
+ } else { // See DLNA 7.5.4.3.2.24.8
+ throw new HTTPSeekRequestError.OUT_OF_RANGE
+ ("Invalid %s start time %lldns is beyond the content duration of %lldns",
+ TIMESEEKRANGE_HEADER, start, this.total_duration);
+ }
+ } else { // Nothing to check it against - just store it
+ this.start_time = start;
+ }
// Look for an end time
int64 end = UNSPECIFIED;
if (parse_npt_time (range_tokens[1], ref end)) {
// The end time was specified in the npt ("start-end")
// Check for valid range
- {
- this.end_time = end;
+ if (positive_rate) {
+ // Check for out-of-bounds range end or fence it in
+ if ((this.total_duration != UNSPECIFIED) && (end > this.total_duration)) {
+ if (trick_mode) { // Per DLNA 7.5.4.3.2.24.4
+ this.end_time = this.total_duration;
+ } else { // Per DLNA 7.5.4.3.2.24.8
+ throw new HTTPSeekRequestError.OUT_OF_RANGE
+ ("Invalid %s end time %lldns is beyond the content duration of %lldns",
+ TIMESEEKRANGE_HEADER, end,this.total_duration);
+ }
+ } else {
+ this.end_time = end;
+ }
this.range_duration = this.end_time - this.start_time;
// At positive rate, start < end
@@ -117,6 +147,16 @@ public class Rygel.HTTPTimeSeekRequest : Rygel.HTTPSeekRequest {
("Invalid %s value (start time after end time - forward scan): '%s'",
TIMESEEKRANGE_HEADER, range);
}
+ } else { // Negative rate
+ // Note: start_time has already been checked/clamped
+ this.end_time = end;
+ this.range_duration = this.start_time - this.end_time;
+ // At negative rate, start > end
+ if (this.range_duration <= 0) { // See DLNA 7.5.4.3.2.24.12
+ throw new HTTPSeekRequestError.INVALID_RANGE
+ ("Invalid %s value (start time before end time - reverse scan): '%s'",
+ TIMESEEKRANGE_HEADER, range);
+ }
}
} else { // End time not specified in the npt field ("start-")
// See DLNA 7.5.4.3.2.24.4
@@ -124,7 +164,11 @@ public class Rygel.HTTPTimeSeekRequest : Rygel.HTTPSeekRequest {
if (this.total_duration == UNSPECIFIED) {
this.range_duration = UNSPECIFIED;
} else {
- this.range_duration = this.total_duration - this.start_time;
+ if (positive_rate) {
+ this.range_duration = this.total_duration - this.start_time;
+ } else {
+ this.range_duration = this.start_time; // Going backward from start to 0
+ }
}
}
}
diff --git a/src/librygel-server/rygel-media-container.vala b/src/librygel-server/rygel-media-container.vala
index 46ff5fd..d6e25f7 100644
--- a/src/librygel-server/rygel-media-container.vala
+++ b/src/librygel-server/rygel-media-container.vala
@@ -58,17 +58,22 @@ internal class Rygel.PlaylistDatasource : Rygel.DataSource, Object {
public signal void data_ready ();
- public Gee.List<HTTPResponseElement> ? preroll ( HTTPSeekRequest? seek_request)
+ public Gee.List<HTTPResponseElement> ? preroll ( HTTPSeekRequest? seek_request,
+ PlaySpeedRequest? playspeed_request)
throws Error {
if (seek_request != null) {
throw new DataSourceError.SEEK_FAILED
(_("Seeking not supported"));
}
+ if (playspeed_request != null) {
+ throw new DataSourceError.PLAYSPEED_FAILED
+ (_("Speed not supported"));
+ }
+
return null;
}
-
public void start () throws Error {
if (this.data == null) {
this.data_ready.connect ( () => {
diff --git a/src/librygel-server/rygel-playspeed-request.vala
b/src/librygel-server/rygel-playspeed-request.vala
new file mode 100644
index 0000000..4e8ee91
--- /dev/null
+++ b/src/librygel-server/rygel-playspeed-request.vala
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2013 Cable Television Laboratories, Inc.
+ *
+ * Author: Craig Pratt <craig ecaspia com>
+ *
+ * This file is part of Rygel.
+ *
+ * Rygel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CABLE TELEVISION LABORATORIES
+ * INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+public errordomain Rygel.PlaySpeedError {
+ INVALID_SPEED_FORMAT,
+ SPEED_NOT_PRESENT
+}
+
+/**
+ * This class represents a DLNA PlaySpeed request (PlaySpeed.dlna.org)
+ */
+public class Rygel.PlaySpeedRequest : GLib.Object {
+ public static const string PLAYSPEED_HEADER = "PlaySpeed.dlna.org";
+ public PlaySpeed speed { get; private set; }
+
+ /**
+ * Return true if playspeed is supported
+ *
+ * This method utilizes elements associated with the request to determine if a
+ * PlaySpeed request is supported for the given request/resource.
+ */
+ public static bool supported (HTTPGet request) {
+ return request.handler.supports_playspeed ();
+ }
+
+ internal static bool requested (HTTPGet request) {
+ return request.msg.request_headers.get_one (PLAYSPEED_HEADER) != null;
+ }
+
+ public PlaySpeedRequest (int numerator, uint denominator) {
+ base ();
+ this.speed = new PlaySpeed (numerator, denominator);
+ }
+
+ public PlaySpeedRequest.from_string (string speed) throws PlaySpeedError {
+ base ();
+ this.speed = new PlaySpeed.from_string (speed);
+ }
+
+ internal PlaySpeedRequest.from_request (Rygel.HTTPGet request) throws PlaySpeedError {
+ base ();
+ // Format: PlaySpeed.dlna.org: speed=<rate>
+ string speed_string = request.msg.request_headers.get_one (PLAYSPEED_HEADER);
+
+ if (speed_string == null) {
+ throw new PlaySpeedError.SPEED_NOT_PRESENT ("Could not find %s",
+ PLAYSPEED_HEADER);
+ }
+
+ var elements = speed_string.split ("=");
+
+ if ((elements.length != 2) || (elements[0] != "speed")) {
+ throw new PlaySpeedError.INVALID_SPEED_FORMAT ("ill-formed value for "
+ + PLAYSPEED_HEADER + ": "
+ + speed_string );
+ }
+
+ speed = new PlaySpeed.from_string (elements[1]);
+
+ // Normal rate is always valid. Just check for valid scaled rate
+ if (!speed.is_normal_rate ()) {
+ // Validate if playspeed is listed in the protocolInfo
+ if (request.handler is HTTPMediaResourceHandler) {
+ MediaResource resource = (request.handler as HTTPMediaResourceHandler)
+ .media_resource;
+ var speeds = resource.play_speeds;
+ bool found_speed = false;
+ foreach (var speed in speeds) {
+ var cur_speed = new PlaySpeedRequest.from_string (speed);
+ if (this.equals (cur_speed)) {
+ found_speed = true;
+ break;
+ }
+ }
+ if (!found_speed) {
+ throw new PlaySpeedError
+ .SPEED_NOT_PRESENT ("Unknown playspeed requested (%s)",
+ speed_string);
+ }
+ }
+ }
+ }
+
+ public bool equals (PlaySpeedRequest that) {
+ if (that == null) return false;
+
+ return (this.speed.equals (that.speed));
+ }
+}
diff --git a/src/librygel-server/rygel-playspeed-response.vala
b/src/librygel-server/rygel-playspeed-response.vala
new file mode 100644
index 0000000..82057ce
--- /dev/null
+++ b/src/librygel-server/rygel-playspeed-response.vala
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2013 Cable Television Laboratories, Inc.
+ *
+ * Author: Craig Pratt <craig ecaspia com>
+ *
+ * This file is part of Rygel.
+ *
+ * Rygel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CABLE TELEVISION LABORATORIES
+ * INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * This class represents a DLNA PlaySpeed response (PlaySpeed.dlna.org)
+ */
+public class Rygel.PlaySpeedResponse : Rygel.HTTPResponseElement {
+ public static const string FRAMERATE_HEADER = "FrameRateInTrickMode.dlna.org";
+
+ PlaySpeed speed;
+ public static const int NO_FRAMERATE = -1;
+
+ /**
+ * The framerate supported for the given rate, in frames per second
+ */
+ public int framerate;
+
+ public PlaySpeedResponse (int numerator, uint denominator, int framerate) {
+ base ();
+ this.speed = new PlaySpeed (numerator, denominator);
+ this.framerate = NO_FRAMERATE;
+ }
+
+ public PlaySpeedResponse.from_speed (PlaySpeed speed, int framerate)
+ throws PlaySpeedError {
+ base ();
+ this.speed = speed;
+ this.framerate = framerate;
+ }
+
+ public PlaySpeedResponse.from_string (string speed, int framerate)
+ throws PlaySpeedError {
+ base ();
+ this.speed = new PlaySpeed.from_string (speed);
+ this.framerate = NO_FRAMERATE;
+ }
+
+ public bool equals (PlaySpeedRequest that) {
+ if (that == null) return false;
+
+ return (this.speed.equals (that.speed));
+ }
+
+ public override void add_response_headers (Rygel.HTTPRequest request) {
+ if (!this.speed.is_normal_rate ()) {
+ // Format: PlaySpeed.dlna.org: speed=<rate>
+ request.msg.response_headers.append (PlaySpeedRequest.PLAYSPEED_HEADER,
+ "speed=" + this.speed.to_string ());
+ if (this.framerate > 0) {
+ // Format: FrameRateInTrickMode.dlna.org: rate=<2-digit framerate>
+ var framerate_val = "rate=%02d".printf(this.framerate);
+ request.msg.response_headers.append (FRAMERATE_HEADER, framerate_val);
+ }
+ if (request.msg.get_http_version () == Soup HTTPVersion 1_0) {
+ request.msg.response_headers.replace ("Pragma","no-cache");
+ }
+ }
+ }
+
+ public override string to_string () {
+ return ("PlaySpeedResponse(speed=%s, framerate=%d)"
+ .printf (this.speed.to_string (), this.framerate));
+ }
+}
diff --git a/src/librygel-server/rygel-playspeed.vala b/src/librygel-server/rygel-playspeed.vala
new file mode 100644
index 0000000..ad96c6e
--- /dev/null
+++ b/src/librygel-server/rygel-playspeed.vala
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2013 Cable Television Laboratories, Inc.
+ *
+ * Author: Craig Pratt <craig ecaspia com>
+ *
+ * This file is part of Rygel.
+ *
+ * Rygel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CABLE TELEVISION LABORATORIES
+ * INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * This is a container for a PlaySpeed value.
+ *
+ * A Playspeed can be positive or negative whole numbers or fractions.
+ * e.g. "2". "1/2", "-1/4"
+ */
+public class Rygel.PlaySpeed {
+ public int numerator; // Sign of the speed will be attached to the numerator
+ public uint denominator;
+
+ public PlaySpeed (int numerator, uint denominator) {
+ this.numerator = numerator;
+ this.denominator = denominator;
+ }
+
+ public PlaySpeed.from_string (string speed) throws PlaySpeedError {
+ parse (speed);
+ }
+
+ public bool equals (PlaySpeed that) {
+ if (that == null) return false;
+
+ return ( (this.numerator == that.numerator)
+ && (this.denominator == that.denominator) );
+ }
+
+ public bool is_positive () {
+ return (this.numerator > 0);
+ }
+
+ public bool is_normal_rate () {
+ return (this.numerator == 1) && (this.denominator == 1);
+ }
+
+ public string to_string () {
+ if (this.denominator == 1) {
+ return numerator.to_string ();
+ } else {
+ return this.numerator.to_string () + "/" + this.denominator.to_string ();
+ }
+ }
+
+ public float to_float () {
+ return (float)numerator/denominator;
+ }
+
+ public double to_double () {
+ return (double)numerator/denominator;
+ }
+
+ private void parse (string speed) throws PlaySpeedError {
+ if (! ("/" in speed)) {
+ this.numerator = int.parse (speed);
+ this.denominator = 1;
+ } else {
+ var elements = speed.split ("/");
+ if (elements.length != 2) {
+ throw new PlaySpeedError.INVALID_SPEED_FORMAT ("Missing/extra numerator/denominator");
+ }
+ this.numerator = int.parse (elements[0]);
+ this.denominator = int.parse (elements[1]);
+ }
+ // "0" isn't a valid numerator or denominator (and parse returns "0" on parse error)
+ if (this.numerator == 0) {
+ throw new PlaySpeedError.INVALID_SPEED_FORMAT ("Invalid numerator in speed: " + speed);
+ }
+ if (this.denominator <= 0) {
+ throw new PlaySpeedError.INVALID_SPEED_FORMAT ("Invalid denominator in speed: " + speed);
+ }
+ }
+}
diff --git a/src/media-engines/gstreamer/rygel-gst-data-source.vala
b/src/media-engines/gstreamer/rygel-gst-data-source.vala
index f49f0ce..fb08a4f 100644
--- a/src/media-engines/gstreamer/rygel-gst-data-source.vala
+++ b/src/media-engines/gstreamer/rygel-gst-data-source.vala
@@ -61,10 +61,17 @@ internal class Rygel.GstDataSource : Rygel.DataSource, GLib.Object {
this.src = element;
}
- public Gee.List<HTTPResponseElement> ? preroll ( HTTPSeekRequest? seek_request)
+ public Gee.List<HTTPResponseElement> ? preroll ( HTTPSeekRequest? seek_request,
+ PlaySpeedRequest? playspeed_request)
throws Error {
var response_list = new Gee.ArrayList<HTTPResponseElement>();
+ if (playspeed_request != null) {
+ throw new DataSourceError.PLAYSPEED_FAILED
+ (_("Playspeed not supported"));
+ }
+
+
if (seek_request == null) {
debug("No seek requested - sending entire binary");
} else if (seek_request is HTTPByteSeekRequest) {
diff --git a/src/media-engines/simple/rygel-simple-data-source.vala
b/src/media-engines/simple/rygel-simple-data-source.vala
index 1700eff..1ed0577 100644
--- a/src/media-engines/simple/rygel-simple-data-source.vala
+++ b/src/media-engines/simple/rygel-simple-data-source.vala
@@ -50,9 +50,10 @@ internal class Rygel.SimpleDataSource : DataSource, Object {
this.stop ();
}
- public Gee.List<HTTPResponseElement> ? preroll (HTTPSeekRequest? seek_request)
+ public Gee.List<HTTPResponseElement> ? preroll (HTTPSeekRequest? seek_request,
+ PlaySpeedRequest? playspeed_request)
throws Error {
- var response_list = new Gee.ArrayList<HTTPResponseElement> ();
+ var response_list = new Gee.ArrayList<HTTPResponseElement> ();
if (seek_request != null) {
if (!(seek_request is HTTPByteSeekRequest)) {
@@ -73,6 +74,11 @@ internal class Rygel.SimpleDataSource : DataSource, Object {
this.last_byte = 0; // Indicates the entire file
}
+ if (playspeed_request != null) {
+ throw new DataSourceError.PLAYSPEED_FAILED
+ (_("Playspeed not supported"));
+ }
+
return response_list;
}
public void start () throws Error {
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]