[rygel] renderer: Implement DIDL_S playlist support
- From: Jens Georg <jensgeorg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [rygel] renderer: Implement DIDL_S playlist support
- Date: Thu, 22 Nov 2012 20:25:03 +0000 (UTC)
commit 6acf09c0f54f7792e63386a7c7d12ac1440f49ee
Author: Jens Georg <jensg openismus com>
Date: Mon Nov 19 17:29:33 2012 +0100
renderer: Implement DIDL_S playlist support
configure.ac | 2 +-
data/rygel.conf | 7 +
data/xml/AVTransport2.xml.in | 1 +
.../rygel-playbin-player.vala | 6 +-
src/librygel-renderer/filelist.am | 1 +
src/librygel-renderer/rygel-av-transport.vala | 265 +++++++++++++-------
.../rygel-media-renderer-plugin.vala | 41 +++
src/librygel-renderer/rygel-player-controller.vala | 225 +++++++++++++++++
.../rygel-sink-connection-manager.vala | 20 +--
9 files changed, 462 insertions(+), 106 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 5aa6867..acc920b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -34,7 +34,7 @@ VALA_REQUIRED=0.18.0
VALADOC_REQUIRED=0.2
GSSDP_REQUIRED=0.13.0
GUPNP_REQUIRED=0.19.0
-GUPNP_AV_REQUIRED=0.11.3
+GUPNP_AV_REQUIRED=0.11.4
GUPNP_DLNA_REQUIRED=0.7.0
GSTREAMER_REQUIRED=1.0
GSTPBU_REQUIRED=1.0
diff --git a/data/rygel.conf b/data/rygel.conf
index 5d831bf..13c1757 100644
--- a/data/rygel.conf
+++ b/data/rygel.conf
@@ -85,6 +85,13 @@ allow-deletion=true
# List of active transcoders. To disable one, remove from list.
transcoders=mp3;lpcm;mp2ts;wmv;aac;avc
+# Options that apply to the renderer framework in general
+[Renderer]
+
+# Default showtime in seconds to use for images in playlists if dlna:lifetime
+# is not set. DLNA wants something between 5 and 15 seconds.
+image-timeout = 15
+
[Tracker]
enabled=true
share-pictures=true
diff --git a/data/xml/AVTransport2.xml.in b/data/xml/AVTransport2.xml.in
index 3344a15..fdda8ae 100644
--- a/data/xml/AVTransport2.xml.in
+++ b/data/xml/AVTransport2.xml.in
@@ -281,6 +281,7 @@
<allowedValueList>
<allowedValue>ABS_TIME</allowedValue>
<allowedValue>REL_TIME</allowedValue>
+ <allowedValue>TRACK_NR</allowedValue>
</allowedValueList>
</stateVariable>
diff --git a/src/librygel-renderer-gst/rygel-playbin-player.vala b/src/librygel-renderer-gst/rygel-playbin-player.vala
index cca07ba..9a6ad26 100644
--- a/src/librygel-renderer-gst/rygel-playbin-player.vala
+++ b/src/librygel-renderer-gst/rygel-playbin-player.vala
@@ -132,6 +132,9 @@ public class Rygel.Playbin.Player : GLib.Object, Rygel.MediaPlayer {
this._playback_state = value;
}
break;
+ case "EOS":
+ this._playback_state = value;
+ break;
default:
break;
}
@@ -163,6 +166,7 @@ public class Rygel.Playbin.Player : GLib.Object, Rygel.MediaPlayer {
this.is_live = this.playbin.set_state (State.PAUSED)
== StateChangeReturn.NO_PREROLL;
break;
+ case "EOS":
case "PLAYING":
// This needs a check if GStreamer and DLNA agree on
// the "liveness" of the source (s0/sn increase in
@@ -397,7 +401,7 @@ public class Rygel.Playbin.Player : GLib.Object, Rygel.MediaPlayer {
case MessageType.EOS:
if (!this.is_rendering_image ()) {
debug ("EOS");
- this.playback_state = "STOPPED";
+ this.playback_state = "EOS";
} else {
debug ("Content is image, ignoring EOS");
}
diff --git a/src/librygel-renderer/filelist.am b/src/librygel-renderer/filelist.am
index 5048e79..b1f7888 100644
--- a/src/librygel-renderer/filelist.am
+++ b/src/librygel-renderer/filelist.am
@@ -5,6 +5,7 @@ LIBRYGEL_RENDERER_VAPI_SOURCE_FILES = \
LIBRYGEL_RENDERER_NONVAPI_SOURCE_FILES = \
rygel-av-transport.vala \
+ rygel-player-controller.vala \
rygel-rendering-control.vala \
rygel-sink-connection-manager.vala \
rygel-time-utils.vala \
diff --git a/src/librygel-renderer/rygel-av-transport.vala b/src/librygel-renderer/rygel-av-transport.vala
index e7ff5ca..74f5099 100644
--- a/src/librygel-renderer/rygel-av-transport.vala
+++ b/src/librygel-renderer/rygel-av-transport.vala
@@ -35,51 +35,21 @@ internal class Rygel.AVTransport : Service {
"urn:schemas-upnp-org:metadata-1-0/AVT/";
private Session session;
+ private string protocol_info;
- // The setters below update the LastChange message
- private uint _n_tracks = 0;
- public uint n_tracks {
- get {
- return this._n_tracks;
- }
-
- set {
- this._n_tracks = value;
-
- this.changelog.log ("NumberOfTracks", this._n_tracks.to_string ());
- }
- }
-
- private uint _track = 0;
- public uint track {
- get {
- return this._track;
- }
+ public string track_metadata {
+ owned get { return this.player.metadata ?? ""; }
set {
- this._track = value;
-
- this.changelog.log ("CurrentTrack", this._track.to_string ());
- }
- }
-
- private string _metadata = "";
- public string metadata {
- owned get {
- if (this._metadata != null) {
- return Markup.escape_text (this._metadata);
+ if (value.has_prefix ("<")) {
+ this.player.metadata = this.unescape (value);
} else {
- return "";
+ this.player.metadata = value;
}
}
-
- set {
- this._metadata = value;
- this.player.metadata = value;
- }
}
- public string uri {
+ public string track_uri {
owned get {
if (this.player.uri != null) {
return Markup.escape_text (this.player.uri);
@@ -134,10 +104,14 @@ internal class Rygel.AVTransport : Service {
private ChangeLog changelog;
private MediaPlayer player;
+ private PlayerController controller;
public override void constructed () {
+ var plugin = this.root_device.resource_factory as MediaRendererPlugin;
+
this.changelog = new ChangeLog (this, LAST_CHANGE_NS);
this.player = this.get_player ();
+ this.controller = plugin.get_controller ();
query_variable["LastChange"].connect (this.query_last_change_cb);
@@ -158,12 +132,18 @@ internal class Rygel.AVTransport : Service {
action_invoked["Next"].connect (this.next_cb);
action_invoked["Previous"].connect (this.previous_cb);
- this.player.notify["playback-state"].connect (this.notify_state_cb);
+ this.controller.notify["playback-state"].connect (this.notify_state_cb);
+ this.controller.notify["n-tracks"].connect (this.notify_n_tracks_cb);
+ this.controller.notify["track"].connect (this.notify_track_cb);
+ this.controller.notify["uri"].connect (this.notify_uri_cb);
+ this.controller.notify["metadata"].connect (this.notify_meta_data_cb);
+
this.player.notify["duration"].connect (this.notify_duration_cb);
- this.player.notify["uri"].connect (this.notify_uri_cb);
- this.player.notify["metadata"].connect (this.notify_meta_data_cb);
+ this.player.notify["uri"].connect (this.notify_track_uri_cb);
+ this.player.notify["metadata"].connect (this.notify_track_meta_data_cb);
this.session = new SessionAsync ();
+ this.protocol_info = plugin.get_protocol_info ();
}
private MediaPlayer get_player () {
@@ -189,14 +169,16 @@ internal class Rygel.AVTransport : Service {
log.log ("RecordMediumWriteStatus", "NOT_IMPLEMENTED");
log.log ("CurrentRecordQualityMode", "NOT_IMPLEMENTED");
log.log ("PossibleRecordQualityMode", "NOT_IMPLEMENTED");
- log.log ("NumberOfTracks", this.n_tracks.to_string ());
- log.log ("CurrentTrack", this.track.to_string ());
+ log.log ("NumberOfTracks", this.controller.n_tracks.to_string ());
+ log.log ("CurrentTrack", this.controller.track.to_string ());
log.log ("CurrentTrackDuration", this.player.duration_as_str);
log.log ("CurrentMediaDuration", this.player.duration_as_str);
- log.log ("CurrentTrackMetaData", this.metadata);
- log.log ("AVTransportURIMetaData", this.metadata);
- log.log ("CurrentTrackURI", this.uri);
- log.log ("AVTransportURI", this.uri);
+ log.log ("CurrentTrackMetaData",
+ Markup.escape_text (this.track_metadata));
+ log.log ("AVTransportURIMetaData",
+ Markup.escape_text (this.controller.metadata));
+ log.log ("CurrentTrackURI", this.track_uri);
+ log.log ("AVTransportURI", this.controller.uri);
log.log ("NextAVTransportURI", "NOT_IMPLEMENTED");
log.log ("NextAVTransportURIMetaData", "NOT_IMPLEMENTED");
@@ -241,6 +223,8 @@ internal class Rygel.AVTransport : Service {
typeof (string),
out _metadata);
+ // remove current playlist handler
+ this.controller.set_playlist (null);
if (_uri.has_prefix ("http://") || _uri.has_prefix ("https://")) {
var message = new Message ("HEAD", _uri);
message.request_headers.append ("getContentFeatures.dlna.org",
@@ -256,40 +240,53 @@ internal class Rygel.AVTransport : Service {
return;
} else {
var mime = msg.response_headers.get_one ("Content-Type");
+ var features = msg.response_headers.get_one
+ ("contentFeatures.dlna.org");
+
if (mime != null &&
- !(mime in this.player.get_mime_types ())) {
+ !(mime in this.player.get_mime_types () || mime ==
+ "text/xml")) {
action.return_error (714, _("Illegal MIME-type"));
return;
}
- this.player.mime_type = mime;
- var features = msg.response_headers.get_one
- ("contentFeatures.dlna.org");
- if (features != null) {
- this.player.content_features = features;
+ this.controller.metadata = _metadata;
+ this.controller.uri = _uri;
+
+ if (mime == "text/xml" &&
+ features.has_prefix ("DLNA.ORG_PN=DIDL_S")) {
+ // Delay returning the action until we got some
+ this.handle_playlist.begin (action);
} else {
- this.player.content_features = "*";
+ // some other track
+ this.player.mime_type = mime;
+ if (features != null) {
+ this.player.content_features = features;
+ } else {
+ this.player.content_features = "*";
+ }
+
+ // Track == Media
+ this.track_metadata = _metadata;
+ this.track_uri = _uri;
+ this.controller.n_tracks = 1;
+ this.controller.track = 1;
+
+ action.return ();
}
-
- this.metadata = _metadata;
- this.uri = _uri;
- this.n_tracks = 1;
- this.track = 1;
-
- action.return ();
}
});
this.session.queue_message (message, null);
} else {
- this.metadata = _metadata;
- this.uri = _uri;
+ this.controller.metadata = _metadata;
+ this.controller.uri = _uri;
if (_uri == "") {
- this.n_tracks = 0;
- this.track = 0;
+ this.controller.n_tracks = 0;
+ this.controller.track = 0;
} else {
- this.n_tracks = 1;
- this.track = 1;
+ this.controller.n_tracks = 1;
+ this.controller.track = 1;
}
action.return ();
@@ -302,18 +299,27 @@ internal class Rygel.AVTransport : Service {
return;
}
+ string media_duration;
+ if (this.controller.n_tracks > 1) {
+ // We don't know the size of the playlist. Might need change if we
+ // support playlists whose size we know in advance
+ media_duration = "0:00:00";
+ } else {
+ media_duration = this.player.duration_as_str;
+ }
+
action.set ("NrTracks",
typeof (uint),
- this.n_tracks,
+ this.controller.n_tracks,
"MediaDuration",
typeof (string),
- this.player.duration_as_str,
+ media_duration,
"CurrentURI",
typeof (string),
- this.uri,
+ this.controller.uri,
"CurrentURIMetaData",
typeof (string),
- this.metadata,
+ this.controller.metadata,
"NextURI",
typeof (string),
"NOT_IMPLEMENTED",
@@ -339,21 +345,30 @@ internal class Rygel.AVTransport : Service {
return;
}
+ string media_duration;
+ if (this.controller.n_tracks > 1) {
+ // We don't know the size of the playlist. Might need change if we
+ // support playlists whose size we know in advance
+ media_duration = "0:00:00";
+ } else {
+ media_duration = this.player.duration_as_str;
+ }
+
action.set ("CurrentType",
typeof (string),
"NO_MEDIA",
"NrTracks",
typeof (uint),
- this.n_tracks,
+ this.controller.n_tracks,
"MediaDuration",
typeof (string),
- this.player.duration_as_str,
+ media_duration,
"CurrentURI",
typeof (string),
- this.uri,
+ this.controller.uri,
"CurrentURIMetaData",
typeof (string),
- this.metadata,
+ this.controller.metadata,
"NextURI",
typeof (string),
"NOT_IMPLEMENTED",
@@ -401,16 +416,16 @@ internal class Rygel.AVTransport : Service {
action.set ("Track",
typeof (uint),
- this.track,
+ this.controller.track,
"TrackDuration",
typeof (string),
this.player.duration_as_str,
"TrackMetaData",
typeof (string),
- this.metadata,
+ this.track_metadata,
"TrackURI",
typeof (string),
- this.uri,
+ this.track_uri,
"RelTime",
typeof (string),
this.player.position_as_str,
@@ -534,6 +549,21 @@ internal class Rygel.AVTransport : Service {
action.return ();
return;
+ case "TRACK_NR":
+ debug ("Setting track to %s.", target);
+ var track = int.parse (target);
+
+ if (track < 1 || track > this.controller.n_tracks) {
+ action.return_error (711, _("Illegal seek target"));
+
+ return;
+ }
+
+ this.controller.track = track;
+
+ action.return();
+
+ break;
default:
action.return_error (710, _("Seek mode not supported"));
@@ -542,31 +572,96 @@ internal class Rygel.AVTransport : Service {
}
private void next_cb (Service service, ServiceAction action) {
- action.return_error (701, _("Transition not available"));
+ if (this.controller.next ()) {
+ action.return ();
+ } else {
+ action.return_error (711, _("Illegal seek target"));
+ }
}
private void previous_cb (Service service, ServiceAction action) {
- action.return_error (701, _("Transition not available"));
+ if (this.controller.previous ()) {
+ action.return ();
+ } else {
+ action.return_error (711, _("Illegal seek target"));
+ }
}
private void notify_state_cb (Object player, ParamSpec p) {
- this.changelog.log ("TransportState", this.player.playback_state);
+ var state = this.player.playback_state;
+ this.changelog.log ("TransportState", state);
+ }
+
+ private void notify_n_tracks_cb (Object player, ParamSpec p) {
+ this.changelog.log ("NumberOfTracks",
+ this.controller.n_tracks.to_string ());
}
- private void notify_duration_cb (Object player, ParamSpec p) {
+ private void notify_track_cb (Object player, ParamSpec p) {
+ this.changelog.log ("CurrentTrack",
+ this.controller.track.to_string ());
+ }
+
+ private void notify_duration_cb (Object player, ParamSpec p) {
this.changelog.log ("CurrentTrackDuration",
this.player.duration_as_str);
this.changelog.log ("CurrentMediaDuration",
this.player.duration_as_str);
}
+ private void notify_track_uri_cb (Object player, ParamSpec p) {
+ this.changelog.log ("CurrentTrackURI", this.track_uri);
+ }
+
private void notify_uri_cb (Object player, ParamSpec p) {
- this.changelog.log ("CurrentTrackURI", this.uri);
- this.changelog.log ("AVTransportURI", this.uri);
+ this.changelog.log ("AVTransportURI", this.controller.uri);
+ }
+
+ private void notify_track_meta_data_cb (Object player, ParamSpec p) {
+ this.changelog.log ("CurrentTrackMetaData",
+ Markup.escape_text (this.track_metadata));
}
private void notify_meta_data_cb (Object player, ParamSpec p) {
- this._metadata = this.player.metadata;
- this.changelog.log ("CurrentTrackMetadata", this.metadata);
+ this.changelog.log ("AVTransportURIMetaData",
+ Markup.escape_text (this.controller.metadata));
+ }
+
+ private async void handle_playlist (ServiceAction action) {
+ var message = new Message ("GET", this.controller.uri);
+ this.session.queue_message (message, () => {
+ handle_playlist.callback ();
+ });
+ yield;
+
+ if (message.status_code != 200) {
+ action.return_error (716, _("Resource not found"));
+
+ return;
+ }
+
+ unowned string xml_string = (string) message.response_body.data;
+
+ var collection = new MediaCollection.from_string (xml_string);
+ if (collection.get_items ().length () == 0) {
+ // FIXME: Return a more sensible error here.
+ action.return_error (716, _("Resource not found"));
+
+ return;
+ }
+
+ this.controller.set_playlist (collection);
+
+ action.return ();
+ }
+
+ private string unescape (string input) {
+ var result = input.replace (""", "\"");
+ result = result.replace ("<", "<");
+ result = result.replace (">", ">");
+ result = result.replace ("'", "'");
+ result = result.replace ("&", "&");
+
+ return result;
}
}
diff --git a/src/librygel-renderer/rygel-media-renderer-plugin.vala b/src/librygel-renderer/rygel-media-renderer-plugin.vala
index 872963b..a9e4ca1 100644
--- a/src/librygel-renderer/rygel-media-renderer-plugin.vala
+++ b/src/librygel-renderer/rygel-media-renderer-plugin.vala
@@ -35,6 +35,9 @@ public class Rygel.MediaRendererPlugin : Rygel.Plugin {
BuildConfig.DATA_DIR +
"/xml/MediaRenderer2.xml";
+ private string sink_protocol_info;
+ private PlayerController controller;
+
/**
* Create an instance of the plugin.
*
@@ -66,9 +69,47 @@ public class Rygel.MediaRendererPlugin : Rygel.Plugin {
RenderingControl.DESCRIPTION_PATH,
typeof (RenderingControl));
this.add_resource (resource);
+
+ this.controller = new PlayerController (this.get_player (),
+ this.get_protocol_info ());
}
public virtual MediaPlayer? get_player () {
return null;
}
+
+ internal PlayerController get_controller () {
+ return this.controller;
+ }
+
+ public string get_protocol_info () {
+ var player = this.get_player ();
+ if (player == null) {
+ return "";
+ }
+
+ if (this.sink_protocol_info == null) {
+ this.sink_protocol_info = "";
+ var protocols = player.get_protocols ();
+
+ this.sink_protocol_info += "http-get:*:text/xml:DLNA.ORG_PN=DIDL_S,";
+
+ var mime_types = player.get_mime_types ();
+ foreach (var protocol in protocols) {
+ if (protocols[0] != protocol) {
+ this.sink_protocol_info += ",";
+ }
+
+ foreach (var mime_type in mime_types) {
+ if (mime_types[0] != mime_type) {
+ this.sink_protocol_info += ",";
+ }
+
+ this.sink_protocol_info += protocol + ":*:" + mime_type + ":*";
+ }
+ }
+ }
+
+ return this.sink_protocol_info;
+ }
}
diff --git a/src/librygel-renderer/rygel-player-controller.vala b/src/librygel-renderer/rygel-player-controller.vala
new file mode 100644
index 0000000..d1d0a25
--- /dev/null
+++ b/src/librygel-renderer/rygel-player-controller.vala
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2012 Intel Corporation.
+ *
+ * Author: Jens Georg <jensg openismus com>
+ *
+ * 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.
+ *
+ * Rygel is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * 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.
+ */
+
+using GUPnP;
+
+/**
+ * This class keeps track of global states that are not dependant on the
+ * RygelMediaPlayer.
+ *
+ * These states are:
+ * # URI
+ * # MetaData
+ * # Number of tracks
+ * # Current track
+ * # Playback state
+ *
+ * In case of playlists this class will also control the player. It needs to
+ * proxy the playback state to react on end of item to be able to switch to
+ * the next item.
+ */
+internal class Rygel.PlayerController : Object {
+ private const int DEFAULT_IMAGE_TIMEOUT = 15;
+ private const string CONFIG_SECTION = "Renderer";
+ private const string TIMEOUT_KEY = "image-timeout";
+ private const string DIDL_FRAME_TEMPLATE = "<DIDL-Lite " +
+ "xmlns:dc=\"http://purl.org/dc/elements/1.1/\" " +
+ "xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\" " +
+ "xmlns:dlna=\"urn:schemas-dlna-org:metadata-1-0/\" " +
+ "xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\">" +
+ "%s</DIDL-Lite>";
+
+ /* private (construction) properties */
+ public MediaPlayer player { construct; private get; }
+ public string protocol_info { construct; private get; }
+
+ /* public properties */
+ public string playback_state { get; set; default = "NO_MEDIA_PRESENT"; }
+ public uint n_tracks { get; set; default = 0; }
+ public uint track {
+ get { return this._track; }
+ set { this._track = value; this.apply_track (); }
+ default = 0;
+ }
+ public string uri { get; set; default = ""; }
+ public string metadata {
+ owned get { return this._metadata ?? ""; }
+ set { this._metadata = this.unescape (value); }
+ default = "";
+ }
+
+ private MediaCollection collection;
+ private List<DIDLLiteItem> collection_items;
+ private uint timeout_id;
+ private uint default_image_timeout;
+ private Configuration config;
+
+ // Private property variables
+ private string _metadata;
+ private uint _track;
+
+ public PlayerController (MediaPlayer player, string protocol_info) {
+ Object (player : player, protocol_info : protocol_info);
+ }
+
+ public override void constructed () {
+ this.player.notify["playback-state"].connect (this.notify_state_cb);
+
+ this.config = MetaConfig.get_default ();
+ this.config.setting_changed.connect (this.on_setting_changed);
+ this.default_image_timeout = DEFAULT_IMAGE_TIMEOUT;
+ this.on_setting_changed (CONFIG_SECTION, TIMEOUT_KEY);
+ }
+
+ public bool next () {
+ if (this.track + 1 > this.n_tracks) {
+ return false;
+ }
+
+ this.track++;
+
+ return true;
+ }
+
+ public bool previous () {
+ if (this.track <= 1) {
+ return false;
+ }
+
+ this.track--;
+
+ return true;
+ }
+
+ public void set_playlist (MediaCollection? collection) {
+ this.collection = collection;
+ if (this.timeout_id != 0) {
+ this.timeout_id = 0;
+ Source.remove (this.timeout_id);
+ }
+
+ if (this.collection != null) {
+ this.collection_items = collection.get_items ();
+ this.n_tracks = this.collection_items.length ();
+ this.track = 1;
+ } else {
+ this.collection_items = null;
+ }
+ }
+
+ private void notify_state_cb (Object player, ParamSpec p) {
+ var state = this.player.playback_state;
+ if (state == "EOS") {
+ if (this.collection == null) {
+ // Just move to stop
+ Idle.add (() => {
+ this.player.playback_state = "STOPPED";
+
+ return false;
+ });
+
+ return;
+ } else {
+ // Set next playlist item
+ if (!this.next ()) {
+ // We were at the end of the list; as per DLNA, move to
+ // STOPPED and let current track be 1.
+ this.reset ();
+ }
+ }
+ } else {
+ // just forward
+ this.playback_state = state;
+ }
+ }
+
+ private void apply_track () {
+ // We only have something to do here if we have collection items
+ if (this.collection_items != null) {
+ var item = this.collection_items.nth (this.track - 1).data;
+
+ var res = item.get_compat_resource (this.protocol_info, true);
+ this.player.metadata = DIDL_FRAME_TEMPLATE.printf
+ (item.get_xml_string ());
+ this.player.uri = res.get_uri ();
+ if (item.upnp_class.has_prefix ("object.item.image") &&
+ this.collection != null) {
+ this.setup_image_timeouts (item.lifetime);
+ }
+ }
+ }
+
+ private void reset () {
+ this.player.playback_state = "STOPPED";
+ this.track = 1;
+ }
+
+ private void setup_image_timeouts (long lifetime) {
+ // For images, we handle the timeout here. Either the item carries a
+ // dlna:lifetime tag, then we use that or we use a default timeout of
+ // 5 minutes.
+ var timeout = this.default_image_timeout;
+ if (lifetime > 0) {
+ timeout = (uint) lifetime;
+ }
+
+ debug ("Item is image, setup timer: %ld", timeout);
+
+ if (this.timeout_id != 0) {
+ Source.remove (this.timeout_id);
+ }
+
+ this.timeout_id = Timeout.add_seconds ((uint) timeout, () => {
+ this.timeout_id = 0;
+ if (!this.next ()) {
+ this.reset ();
+ }
+
+ return false;
+ });
+ }
+
+ private void on_setting_changed (string section, string key) {
+ if (section != CONFIG_SECTION && key != TIMEOUT_KEY) {
+ return;
+ }
+
+ try {
+ this.default_image_timeout = config.get_int (CONFIG_SECTION,
+ TIMEOUT_KEY,
+ 0,
+ int.MAX);
+ } catch (Error error) {
+ this.default_image_timeout = DEFAULT_IMAGE_TIMEOUT;
+ }
+
+ debug ("New image timeout: %lu", this.default_image_timeout);
+ }
+
+ private string unescape (string input) {
+ var result = input.replace (""", "\"");
+ result = result.replace ("<", "<");
+ result = result.replace (">", ">");
+ result = result.replace ("'", "'");
+ result = result.replace ("&", "&");
+
+ return result;
+ }
+}
diff --git a/src/librygel-renderer/rygel-sink-connection-manager.vala b/src/librygel-renderer/rygel-sink-connection-manager.vala
index cea868c..beb30c0 100644
--- a/src/librygel-renderer/rygel-sink-connection-manager.vala
+++ b/src/librygel-renderer/rygel-sink-connection-manager.vala
@@ -24,8 +24,6 @@
using GUPnP;
internal class Rygel.SinkConnectionManager : Rygel.ConnectionManager {
- private MediaPlayer player;
-
public override void constructed () {
base.constructed ();
@@ -34,22 +32,6 @@ internal class Rygel.SinkConnectionManager : Rygel.ConnectionManager {
this.direction = "Input";
var plugin = this.root_device.resource_factory as MediaRendererPlugin;
- this.player = plugin.get_player ();
- var protocols = this.player.get_protocols ();
-
- foreach (var protocol in protocols) {
- if (protocols[0] != protocol) {
- this.sink_protocol_info += ",";
- }
- var mime_types = this.player.get_mime_types ();
-
- foreach (var mime_type in mime_types) {
- if (mime_types[0] != mime_type) {
- this.sink_protocol_info += ",";
- }
-
- this.sink_protocol_info += protocol + ":*:" + mime_type + ":*";
- }
- }
+ this.sink_protocol_info = plugin.get_protocol_info ();
}
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]