[rygel/wip/didl-s: 16/25] WIP



commit 9d97fee1b94545acad8b07a49f76885907a576d7
Author: Jens Georg <jensg openismus com>
Date:   Mon Nov 19 17:29:33 2012 +0100

    WIP

 data/xml/AVTransport2.xml.in                       |    1 +
 src/librygel-renderer/filelist.am                  |    1 +
 src/librygel-renderer/rygel-av-transport.vala      |  167 +++++++++++++++++---
 src/librygel-renderer/rygel-playlist-handler.vala  |   55 +++++++
 .../rygel-sink-connection-manager.vala             |   12 ++
 5 files changed, 214 insertions(+), 22 deletions(-)
---
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/filelist.am b/src/librygel-renderer/filelist.am
index 5048e79..352b8ce 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-playlist-handler.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 7d5ff18..c7a204d 100644
--- a/src/librygel-renderer/rygel-av-transport.vala
+++ b/src/librygel-renderer/rygel-av-transport.vala
@@ -35,6 +35,7 @@ internal class Rygel.AVTransport : Service {
                     "urn:schemas-upnp-org:metadata-1-0/AVT/";
 
     private Session session;
+    private PlaylistHandler playlist_handler;
 
     // The setters below update the LastChange message
     private uint _n_tracks = 0;
@@ -67,6 +68,9 @@ internal class Rygel.AVTransport : Service {
     public string metadata {
         owned get {
             if (this._metadata != null) {
+                if (this._metadata.has_prefix ("&lt;")) {
+                    return this._metadata;
+                }
                 return Markup.escape_text (this._metadata);
             } else {
                 return "";
@@ -75,11 +79,28 @@ internal class Rygel.AVTransport : Service {
 
         set {
             this._metadata = value;
+        }
+    }
+
+    public string track_metadata {
+        owned get {
+            if (this.player.metadata != null) {
+                if (this.player.metadata.has_prefix ("&lt;")) {
+                    return this.player.metadata;
+                }
+
+                return Markup.escape_text (this.player.metadata);
+            } else {
+                return "";
+            }
+        }
+
+        set {
             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);
@@ -93,6 +114,24 @@ internal class Rygel.AVTransport : Service {
         }
     }
 
+    private string _uri;
+    public string uri {
+        owned get {
+            if (this._uri == null) {
+                return "";
+            }
+
+            return this._uri;
+        }
+
+        set {
+            this._uri = value;
+            this.changelog.log ("AVTransportURI", this._uri);
+        }
+    }
+
+
+
     private string _status = "OK";
     public string status {
         get {
@@ -193,9 +232,9 @@ internal class Rygel.AVTransport : Service {
         log.log ("CurrentTrack",                 this.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 ("CurrentTrackMetaData",         this.track_metadata);
         log.log ("AVTransportURIMetaData",       this.metadata);
-        log.log ("CurrentTrackURI",              this.uri);
+        log.log ("CurrentTrackURI",              this.track_uri);
         log.log ("AVTransportURI",               this.uri);
         log.log ("NextAVTransportURI",           "NOT_IMPLEMENTED");
         log.log ("NextAVTransportURIMetaData",   "NOT_IMPLEMENTED");
@@ -256,25 +295,41 @@ 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;
-                    } else {
-                        this.player.content_features = "*";
-                    }
 
                     this.metadata = _metadata;
                     this.uri = _uri;
-                    this.n_tracks = 1;
+
+                    if (mime == "text/xml" &&
+                        features.has_prefix ("DLNA.ORG_PN=DIDL_S")) {
+                        // this is a playlist, we'll handle that internally
+                        // Return action, then handle playlist
+                        // first thing handle playlist does is defer to
+                        // mainloop so the action will be returned first.
+                        this.handle_playlist.begin ();
+                    } else {
+                        // 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.n_tracks = 1;
+                    }
 
                     action.return ();
                 }
@@ -299,12 +354,21 @@ internal class Rygel.AVTransport : Service {
             return;
         }
 
+        string media_duration;
+        if (this.playlist_handler != null) {
+            // 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,
                     "MediaDuration",
                         typeof (string),
-                        this.player.duration_as_str,
+                        media_duration,
                     "CurrentURI",
                         typeof (string),
                         this.uri,
@@ -336,6 +400,15 @@ internal class Rygel.AVTransport : Service {
             return;
         }
 
+        string media_duration;
+        if (this.playlist_handler != null) {
+            // 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",
@@ -344,7 +417,7 @@ internal class Rygel.AVTransport : Service {
                         this.n_tracks,
                     "MediaDuration",
                         typeof (string),
-                        this.player.duration_as_str,
+                        media_duration,
                     "CurrentURI",
                         typeof (string),
                         this.uri,
@@ -404,10 +477,10 @@ internal class Rygel.AVTransport : Service {
                         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,
@@ -531,6 +604,27 @@ internal class Rygel.AVTransport : Service {
             action.return ();
 
             return;
+        case "TRACK_NR":
+            debug ("Setting track to %s.", target);
+            if (this.playlist_handler == null) {
+                action.return_error (710, _("Seek mode not supported"));
+
+                return;
+            }
+
+            var track = int.parse (target);
+
+            if (track < 1 || track > this.n_tracks) {
+                action.return_error (711, _("Illegal seek target"));
+
+                return;
+            }
+            this.playlist_handler.current_track = track;
+            this.playlist_handler.set_track ();
+
+            action.return();
+
+            break;
         default:
             action.return_error (710, _("Seek mode not supported"));
 
@@ -539,11 +633,21 @@ internal class Rygel.AVTransport : Service {
     }
 
     private void next_cb (Service service, ServiceAction action) {
-        action.return_error (701, _("Transition not available"));
+        if (this.playlist_handler != null) {
+            this.playlist_handler.next ();
+            action.return ();
+        } else {
+            action.return_error (701, _("Transition not available"));
+        }
     }
 
     private void previous_cb (Service service, ServiceAction action) {
-        action.return_error (701, _("Transition not available"));
+        if (this.playlist_handler != null) {
+            this.playlist_handler.previous ();
+            action.return ();
+        } else {
+            action.return_error (701, _("Transition not available"));
+        }
     }
 
     private void notify_state_cb (Object player, ParamSpec p) {
@@ -558,12 +662,31 @@ internal class Rygel.AVTransport : Service {
     }
 
     private void notify_uri_cb (Object player, ParamSpec p) {
-        this.changelog.log ("CurrentTrackURI", this.uri);
-        this.changelog.log ("AVTransportURI", this.uri);
+        this.changelog.log ("CurrentTrackURI", this.track_uri);
     }
 
     private void notify_meta_data_cb (Object player, ParamSpec p) {
         this._metadata = this.player.metadata;
         this.changelog.log ("CurrentTrackMetadata", this.metadata);
     }
+
+    private async void handle_playlist () {
+        Idle.add ( () => { handle_playlist.callback (); return false;  });
+        yield;
+
+        var message = new Message ("GET", this.uri);
+        this.session.queue_message (message, () => {
+            handle_playlist.callback ();
+        });
+        yield;
+
+        if (message.status_code != 200) {
+            return;
+        }
+
+        unowned string xml_string = (string) message.response_body.data;
+
+        var collection = new MediaCollection.from_string (xml_string);
+        this.playlist_handler = new PlaylistHandler (collection, this);
+    }
 }
diff --git a/src/librygel-renderer/rygel-playlist-handler.vala b/src/librygel-renderer/rygel-playlist-handler.vala
new file mode 100644
index 0000000..23dedcb
--- /dev/null
+++ b/src/librygel-renderer/rygel-playlist-handler.vala
@@ -0,0 +1,55 @@
+using GUPnP;
+
+internal class Rygel.PlaylistHandler : GLib.Object {
+    //private const int DEFAULT_IMAGE_TIMEOUT = 300;
+    private const int DEFAULT_IMAGE_TIMEOUT = 30;
+    public MediaCollection collection { construct; private get; }
+    public unowned AVTransport transport { construct; private get; }
+    public uint current_track { construct set; get; default = 1; }
+
+    private List<DIDLLiteItem> items;
+
+    public PlaylistHandler (MediaCollection collection,
+                            AVTransport     transport) {
+        Object (collection : collection, transport : transport);
+    }
+
+    public override void constructed () {
+        debug ("Playlist handler constructed");
+        this.items = this.collection.get_items ();
+        this.transport.n_tracks = items.length ();
+        this.set_track ();
+    }
+
+    public void next () {
+        this.current_track++;
+        if (this.current_track > this.items.length ()) {
+            debug ("Ignoring next, end of list: %u", this.items.length ());
+            this.current_track = this.items.length ();
+
+            return;
+        }
+
+        this.set_track ();
+    }
+
+    public void previous () {
+        this.current_track--;
+        if (this.current_track < 1) {
+            debug ("Ignoring previous, beginning of list.");
+            this.current_track = 1;
+
+            return;
+        }
+
+        this.set_track ();
+    }
+
+    public void set_track () {
+        var item = items.nth (this.current_track - 1).data;
+        var res = item.get_resources().nth (0).data;
+        this.transport.track = this.current_track;
+        this.transport.track_uri = res.get_uri ();
+        debug ("Trying to set track uri to %s", res.get_uri ());
+    }
+}
diff --git a/src/librygel-renderer/rygel-sink-connection-manager.vala b/src/librygel-renderer/rygel-sink-connection-manager.vala
index 9f76c9d..ec26edf 100644
--- a/src/librygel-renderer/rygel-sink-connection-manager.vala
+++ b/src/librygel-renderer/rygel-sink-connection-manager.vala
@@ -37,8 +37,20 @@ internal class Rygel.SinkConnectionManager : Rygel.ConnectionManager {
         this.player = plugin.get_player ();
         var protocols = this.player.get_protocols ();
 
+        this.sink_protocol_info += "http-get:*:text/xml:DLNA.ORG_PN=DIDL_S,";
+        this.sink_protocol_info += "http-get:*:audio/mpeg:DLNA.ORG_PN=MP3,";
         this.sink_protocol_info +=
             "http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM,";
+        this.sink_protocol_info +=
+            "http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO_320,";
+        this.sink_protocol_info +=
+            "http-get:*:audio/vnd.dlna.adts:DLNA.ORG_PN=AAC_ADTS_320,";
+        this.sink_protocol_info += "http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM,";
+        this.sink_protocol_info += "http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED,";
+        this.sink_protocol_info += "http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG,";
+        this.sink_protocol_info +=
+            "http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_BL_CIF15_AAC_520,";
+
         foreach (var protocol in protocols) {
             if (protocols[0] != protocol) {
                 this.sink_protocol_info += ",";



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]