diff --git a/doc/reference/librygel-server/gtkdoc/librygel-server-docs.xml b/doc/reference/librygel-server/gtkdoc/librygel-server-docs.xml
index 91542bc..22dbb01 100644
--- a/doc/reference/librygel-server/gtkdoc/librygel-server-docs.xml
+++ b/doc/reference/librygel-server/gtkdoc/librygel-server-docs.xml
@@ -49,8 +49,6 @@
     <xi:include href="xml/rygel-subtitle.xml"/>
     <xi:include href="xml/rygel-thumbnail.xml"/>
     <xi:include href="xml/rygel-trackable-container.xml"/>
-    <xi:include href="xml/rygel-transcode-manager.xml"/>
-    <xi:include href="xml/rygel-transcoder.xml"/>
     <xi:include href="xml/rygel-video-item.xml"/>
     <xi:include href="xml/rygel-visual-item.xml"/>
     <xi:include href="xml/rygel-writable-container.xml"/>
diff --git a/src/librygel-server/filelist.am b/src/librygel-server/filelist.am
index 5f7d2a6..7589f35 100644
--- a/src/librygel-server/filelist.am
+++ b/src/librygel-server/filelist.am
@@ -16,13 +16,12 @@ LIBRYGEL_SERVER_VAPI_SOURCE_FILES = \
 	rygel-media-file-item.vala \
 	rygel-media-item.vala \
 	rygel-media-object.vala \
+	rygel-media-resource.vala \
 	rygel-media-server-plugin.vala \
 	rygel-search-expression.vala \
 	rygel-searchable-container.vala \
 	rygel-trackable-container.vala \
 	rygel-trackable-item.vala \
-	rygel-transcode-manager.vala \
-	rygel-transcoder.vala \
 	rygel-visual-item.vala \
 	rygel-writable-container.vala \
 	rygel-media-server.vala \
@@ -50,7 +49,6 @@ LIBRYGEL_SERVER_NONVAPI_SOURCE_FILES = \
 	rygel-http-response.vala \
 	rygel-http-server.vala \
 	rygel-http-time-seek.vala \
-	rygel-http-transcode-handler.vala \
 	rygel-import-resource.vala \
 	rygel-object-creator.vala \
 	rygel-reference-creator.vala \
diff --git a/src/librygel-server/rygel-audio-item.vala b/src/librygel-server/rygel-audio-item.vala
index 5882ea5..7eb0287 100644
--- a/src/librygel-server/rygel-audio-item.vala
+++ b/src/librygel-server/rygel-audio-item.vala
@@ -47,24 +73,20 @@ public class Rygel.AudioItem : MediaItem {
                 upnp_class : upnp_class);
     }
 
-    public override bool streamable () {
-        return true;
-    }
-
-    internal override DIDLLiteResource add_resource
-                                        (DIDLLiteObject didl_object,
-                                         string?        uri,
-                                         string         protocol,
-                                         string?        import_uri = null)
-                                         throws Error {
-        var res = base.add_resource (didl_object, uri, protocol, import_uri);
+    internal override MediaResource get_primary_resource () {
+        var res = base.get_primary_resource ();
 
         res.duration = this.duration;
         res.bitrate = this.bitrate;
         res.sample_freq = this.sample_freq;
         res.bits_per_sample = this.bits_per_sample;
         res.audio_channels = this.channels;
+        res.dlna_flags |= DLNAFlags.STREAMING_TRANSFER_MODE;
 
         return res;
     }
+
+    internal override void add_resources (HTTPServer server) {
+        // AudioItem doesn't add secondary resources
+    }
 }
diff --git a/src/librygel-server/rygel-http-server.vala b/src/librygel-server/rygel-http-server.vala
index 0472d2a..c92f6c2 100644
--- a/src/librygel-server/rygel-http-server.vala
+++ b/src/librygel-server/rygel-http-server.vala
@@ -1,10 +1,13 @@
 /*
  * Copyright (C) 2008, 2009 Nokia Corporation.
  * Copyright (C) 2012 Intel Corporation.
+ * Copyright (C) 2013 Cable Television Laboratories, Inc.
  *
  * Author: Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
  *                               <zeeshan.ali@nokia.com>
  *         Jens Georg <jensg@openismus.com>
+ *         Doug Galligan <doug@sentosatech.com>
+ *         Craig Pratt <craig@ecaspia.com>
  *
  * This file is part of Rygel.
  *
@@ -26,25 +29,26 @@
 using GUPnP;
 using Gee;
 
-public class Rygel.HTTPServer : Rygel.TranscodeManager, Rygel.StateMachine {
+public class Rygel.HTTPServer : Rygel.StateMachine, GLib.Object {
     public string path_root { get; private set; }
 
     // Reference to root container of associated ContentDirectory
     public MediaContainer root_container;
     public GUPnP.Context context;
     private ArrayList<HTTPRequest> requests;
+    private bool locally_hosted;
 
     public Cancellable cancellable { get; set; }
 
     public HTTPServer (ContentDirectory content_dir,
                        string           name) {
-        base ();
 
         this.root_container = content_dir.root_container;
         this.context = content_dir.context;
         this.requests = new ArrayList<HTTPRequest> ();
         this.cancellable = content_dir.cancellable;
-
+        this.locally_hosted = this.context.interface == "lo"
+                              || this.context.host_ip == "127.0.0.1";
         this.path_root = "/" + name;
     }
 
@@ -58,36 +62,10 @@ public class Rygel.HTTPServer : Rygel.TranscodeManager, Rygel.StateMachine {
         }
     }
 
-    internal void add_proxy_resource (DIDLLiteItem didl_item,
-                                      MediaItem    item)
-                                      throws Error {
-        if (this.http_uri_present (item)) {
-            return;
-        }
-
-        var uri = this.create_uri_for_item (item, -1, -1, null, null);
-
-        item.add_resource (didl_item, uri, this.get_protocol (), uri);
-    }
-
     public bool need_proxy (string uri) {
         return Uri.parse_scheme (uri) != "http";
     }
 
-    private bool http_uri_present (MediaItem item) {
-        bool present = false;
-
-        foreach (var uri in item.uris) {
-            if (!this.need_proxy (uri)) {
-                present = true;
-
-                break;
-            }
-        }
-
-        return present;
-    }
-
     private void on_cancelled (Cancellable cancellable) {
         // Cancel all state machines
         this.cancellable.cancel ();
@@ -97,35 +75,51 @@ public class Rygel.HTTPServer : Rygel.TranscodeManager, Rygel.StateMachine {
         this.completed ();
     }
 
-    internal override string create_uri_for_item (MediaItem item,
-                                                  int       thumbnail_index,
-                                                  int       subtitle_index,
-                                                  string?   transcode_target,
-                                                  string?   playlist_target) {
-        var uri = new HTTPItemURI (item,
+    public string create_uri_for_item (MediaObject object,
+                                         string extension,
+                                         int       thumbnail_index,
+                                         int       subtitle_index,
+                                         string?   resource_name) {
+        var uri = new HTTPItemURI (object,
                                    this,
+                                   extension,
                                    thumbnail_index,
                                    subtitle_index,
-                                   transcode_target,
-                                   playlist_target);
+                                   resource_name );
 
         return uri.to_string ();
     }
 
-    internal override string get_protocol () {
-        return "http-get";
+    public bool recognizes_uri (string uri) {
+        if (!uri.has_prefix ("http:")) {
+            return false;
+        }
+        try {
+            new HTTPItemURI.from_string (uri, this);
+            return true;
+        } catch (Error error) {
+            return false;
+        }
     }
 
-    internal override ArrayList<ProtocolInfo> get_protocol_info () {
-        var protocol_infos = base.get_protocol_info ();
-
-        var protocol_info = new ProtocolInfo ();
-        protocol_info.protocol = this.get_protocol ();
-        protocol_info.mime_type = "*";
+    public string get_protocol () {
+        return "http-get";
+    }
 
-        protocol_infos.add (protocol_info);
+    public bool is_local () {
+        return (this.locally_hosted);
+    }
 
-        return protocol_infos;
+    /**
+     * Set or unset options the server supports/doesn't support
+     *
+     * Resources should be setup assuming server supports all optional delivery modes
+     */
+    public void set_resource_delivery_options (MediaResource res) {
+        res.protocol = get_protocol ();
+        // Set this just to be safe
+        res.dlna_flags |= DLNAFlags.DLNA_V15 | DLNAFlags.CONNECTION_STALL;
+        // This server supports all DLNA delivery modes - so leave those flags alone
     }
 
     private void on_request_completed (StateMachine machine) {
diff --git a/src/librygel-server/rygel-image-item.vala b/src/librygel-server/rygel-image-item.vala
index 55503c6..6a8fe7f 100644
--- a/src/librygel-server/rygel-image-item.vala
+++ b/src/librygel-server/rygel-image-item.vala
@@ -74,54 +77,25 @@ public class Rygel.ImageItem : MediaItem, VisualItem {
         this.thumbnails = new ArrayList<Thumbnail> ();
     }
 
-    public override bool streamable () {
-        return false;
-    }
-
     public override void add_uri (string uri) {
         base.add_uri (uri);
 
         this.add_thumbnail_for_uri (uri, this.mime_type);
     }
 
-    internal override void add_resources (DIDLLiteItem didl_item,
-                                          bool         allow_internal)
-                                          throws Error {
-        base.add_resources (didl_item, allow_internal);
-
-        this.add_thumbnail_resources (didl_item, allow_internal);
-    }
+    internal override MediaResource get_primary_resource () {
+        var res = base.get_primary_resource ();
 
-    internal override DIDLLiteResource add_resource
-                                        (DIDLLiteObject didl_object,
-                                         string?      uri,
-                                         string       protocol,
-                                         string?      import_uri = null)
-                                         throws Error {
-        var res = base.add_resource (didl_object, uri, protocol, import_uri);
+        this.set_visual_resource_properties (res);
 
-        this.add_visual_props (res);
+        res.dlna_flags |= DLNAFlags.INTERACTIVE_TRANSFER_MODE;
 
         return res;
     }
 
-    internal override void add_proxy_resources (HTTPServer   server,
-                                                DIDLLiteItem didl_item)
-                                                throws Error {
-        base.add_proxy_resources (server, didl_item);
-
-        if (!this.place_holder) {
-            // Thumbnails comes in the end
-            this.add_thumbnail_proxy_resources (server, didl_item);
-        }
-    }
-
-    protected override ProtocolInfo get_protocol_info (string? uri,
-                                                       string  protocol) {
-        var protocol_info = base.get_protocol_info (uri, protocol);
-
-        protocol_info.dlna_flags |= DLNAFlags.INTERACTIVE_TRANSFER_MODE;
+    internal override void add_resources (HTTPServer http_server) {
+        base.add_resources (http_server);
 
-        return protocol_info;
+        this.add_thumbnail_resources (http_server);
     }
 }
diff --git a/src/librygel-server/rygel-media-container.vala b/src/librygel-server/rygel-media-container.vala
index 2c290a7..dec001f 100644
--- a/src/librygel-server/rygel-media-container.vala
+++ b/src/librygel-server/rygel-media-container.vala
@@ -204,9 +209,9 @@ public abstract class Rygel.MediaContainer : MediaObject {
         this.total_deleted_child_count = 0;
         this.upnp_class = UPNP_CLASS;
         this.create_mode_enabled = false;
-
         this.container_updated.connect (on_container_updated);
         this.sub_tree_updates_finished.connect (on_sub_tree_updates_finished);
+        add_playlist_resources ();
     }
 
     /**
@@ -267,6 +272,35 @@ public abstract class Rygel.MediaContainer : MediaObject {
                                 sub_tree_update);
     }
 
+    /**
+     * Add playlist resources to the MediaObject resource list
+     */
+    internal void add_playlist_resources () {
+        { // Create the DIDL_S playlist resource
+            var didl_s_res = new MediaResource (DIDL_S_PLAYLIST_RESNAME);
+            didl_s_res.extension = "xml";
+            didl_s_res.mime_type = "text/xml";
+            didl_s_res.dlna_profile = "DIDL_S";
+            didl_s_res.dlna_flags = DLNAFlags.CONNECTION_STALL |
+                                    DLNAFlags.BACKGROUND_TRANSFER_MODE |
+                                    DLNAFlags.INTERACTIVE_TRANSFER_MODE;
+            didl_s_res.uri = ""; // Established during serialization
+            this.get_resource_list ().add (didl_s_res);
+        }
+
+        { // Create the M3U playlist resource
+            var m3u_res = new MediaResource (M3U_PLAYLIST_RESNAME);
+            m3u_res.extension = "m3u";
+            m3u_res.mime_type = "audio/x-mpegurl";
+            m3u_res.dlna_profile = null;
+            m3u_res.dlna_flags = DLNAFlags.CONNECTION_STALL |
+                                 DLNAFlags.BACKGROUND_TRANSFER_MODE |
+                                 DLNAFlags.INTERACTIVE_TRANSFER_MODE;
+            m3u_res.uri = ""; // Established during serialization
+            this.get_resource_list ().add (m3u_res);
+        }
+    }
+
     public override DIDLLiteObject? serialize (Serializer serializer,
                                                HTTPServer http_server)
                                                throws Error {
@@ -315,72 +349,33 @@ public abstract class Rygel.MediaContainer : MediaObject {
             didl_container.restricted = true;
         }
 
-        this.add_resources (http_server, didl_container);
-
-        return didl_container;
-    }
-
-    internal void add_resources (Rygel.HTTPServer http_server,
-                                 DIDLLiteContainer didl_container)
-                                 throws Error {
-        // Add resource with container contents serialized to DIDL_S playlist
-        var uri = new HTTPItemURI (this,
-                                   http_server,
-                                   -1,
-                                   -1,
-                                   null,
-                                   "DIDL_S");
-        uri.extension = "xml";
-
-        var res = this.add_resource (didl_container,
-                                     uri.to_string (),
-                                     http_server.get_protocol ());
-        if (res != null) {
-            res.protocol_info.mime_type = "text/xml";
-            res.protocol_info.dlna_profile = "DIDL_S";
+        if (this.child_count > 0) {
+            serialize_resource_list (didl_container, http_server);
         }
 
-        // Add resource with container contents serialized to M3U playlist
-        uri = new HTTPItemURI (this, http_server, -1, -1, null, "M3U");
-        uri.extension = "m3u";
-
-        res = this.add_resource (didl_container,
-                                 uri.to_string (),
-                                 http_server.get_protocol ());
-        if (res != null) {
-            res.protocol_info.mime_type = "audio/x-mpegurl";
-        }
+        return didl_container;
     }
 
-    internal override DIDLLiteResource add_resource
-                                        (DIDLLiteObject didl_object,
-                                         string?        uri,
-                                         string         protocol,
-                                         string?        import_uri = null)
-                                         throws Error {
-        if (this.child_count <= 0) {
-            return null as DIDLLiteResource;
+    public override DataSource? create_stream_source_for_resource (HTTPRequest request,
+                                                                   MediaResource resource)
+        throws Error {
+        SerializerType playlist_type;
+
+        switch (resource.get_name ()) {
+            case DIDL_S_PLAYLIST_RESNAME:
+                playlist_type = SerializerType.DIDL_S;
+                break;
+            case M3U_PLAYLIST_RESNAME:
+                playlist_type = SerializerType.M3UEXT;
+                break;
+            default:
+                warning ("Unknown MediaContainer resource: %s", resource.get_name ());
+                return null;
         }
-
-        var res = base.add_resource (didl_object,
-                                     uri,
-                                     protocol,
-                                     import_uri);
-
-        if (uri != null) {
-            res.uri = uri;
-        }
-
-        var protocol_info = new ProtocolInfo ();
-        protocol_info.mime_type = "";
-        protocol_info.protocol = protocol;
-        protocol_info.dlna_flags = DLNAFlags.DLNA_V15 |
-                                   DLNAFlags.CONNECTION_STALL |
-                                   DLNAFlags.BACKGROUND_TRANSFER_MODE |
-                                   DLNAFlags.INTERACTIVE_TRANSFER_MODE;
-        res.protocol_info = protocol_info;
-
-        return res;
+        return new PlaylistDatasource (playlist_type,
+                                       this,
+                                       request.http_server,
+                                       request.hack);
     }
 
     /**
diff --git a/src/librygel-server/rygel-media-item.vala b/src/librygel-server/rygel-media-item.vala
index f175c79..88dc9ae 100644
--- a/src/librygel-server/rygel-media-item.vala
+++ b/src/librygel-server/rygel-media-item.vala
@@ -256,140 +149,6 @@ public abstract class Rygel.MediaItem : MediaObject {
             didl_item.update_id = this.object_update_id;
         }
 
-        /* We list proxy/transcoding resources first instead of original URIs
-         * because some crappy MediaRenderer/ControlPoint implemenation out
-         * there just choose the first one in the list instead of the one they
-         * can handle.
-         */
-        this.add_proxy_resources (http_server, didl_item);
-        if (!this.place_holder) {
-            var host_ip = http_server.context.host_ip;
-
-            // then original URIs
-            bool internal_allowed;
-            internal_allowed = http_server.context.interface == "lo" ||
-                               host_ip == "127.0.0.1";
-            this.add_resources (didl_item, internal_allowed);
-
-            foreach (var res in didl_item.get_resources ()) {
-                res.uri = MediaItem.address_regex.replace_literal (res.uri,
-                                                                   -1,
-                                                                   0,
-                                                                   host_ip);
-            }
-        }
-
         return didl_item;
     }
-
-    internal virtual void add_proxy_resources (HTTPServer   server,
-                                               DIDLLiteItem didl_item)
-                                               throws Error {
-        // Proxy resource for the original resources
-        server.add_proxy_resource (didl_item, this);
-
-        if (!this.place_holder) {
-            // Transcoding resources
-            server.add_resources (didl_item, this);
-        }
-    }
-
-    protected virtual ProtocolInfo get_protocol_info (string? uri,
-                                                      string  protocol) {
-        var protocol_info = new ProtocolInfo ();
-
-        protocol_info.mime_type = this.mime_type;
-        protocol_info.dlna_profile = this.dlna_profile;
-        protocol_info.protocol = protocol;
-        protocol_info.dlna_flags = DLNAFlags.DLNA_V15 |
-                                   DLNAFlags.CONNECTION_STALL |
-                                   DLNAFlags.BACKGROUND_TRANSFER_MODE;
-
-        if (this.size > 0) {
-            protocol_info.dlna_operation = DLNAOperation.RANGE;
-        }
-
-        if (this.streamable ()) {
-            protocol_info.dlna_flags |= DLNAFlags.STREAMING_TRANSFER_MODE;
-        }
-
-        return protocol_info;
-    }
-
-    internal string get_protocol_for_uri (string uri) throws Error {
-        var scheme = Uri.parse_scheme (uri);
-        if (scheme == null) {
-            throw new MediaItemError.BAD_URI (_("Bad URI: %s"), uri);
-        }
-
-        if (scheme == "http") {
-            return "http-get";
-        } else if (scheme == "file") {
-            return "internal";
-        } else if (scheme == "rtsp") {
-            // FIXME: Assuming that RTSP is always accompanied with RTP over UDP
-            return "rtsp-rtp-udp";
-        } else {
-            // Assume the protocol to be the scheme of the URI
-            warning (_("Failed to probe protocol for URI %s. Assuming '%s'"),
-                     uri,
-                     scheme);
-
-            return scheme;
-        }
-    }
-
-    protected virtual void add_resources (DIDLLiteItem didl_item,
-                                          bool         allow_internal)
-                                          throws Error {
-        foreach (var uri in this.uris) {
-            var protocol = this.get_protocol_for_uri (uri);
-
-            if (allow_internal || protocol != "internal") {
-                this.add_resource (didl_item, uri, protocol);
-            }
-        }
-    }
-
-    private int compare_by_date (MediaItem item) {
-        if (this.date == null) {
-            return -1;
-        } else if (item.date == null) {
-            return 1;
-        } else {
-            var our_date = this.date;
-            var other_date = item.date;
-
-            if (!our_date.contains ("T")) {
-                our_date += "T00:00:00Z";
-            }
-
-            if (!other_date.contains ("T")) {
-                other_date += "T00:00:00Z";
-            }
-
-            var tv1 = TimeVal ();
-            assert (tv1.from_iso8601 (this.date));
-
-            var tv2 = TimeVal ();
-            assert (tv2.from_iso8601 (item.date));
-
-            var ret = this.compare_long (tv1.tv_sec, tv2.tv_sec);
-            if (ret == 0) {
-                ret = this.compare_long (tv1.tv_usec, tv2.tv_usec);
-            }
-
-            return ret;
-        }
-    }
-
-    private int compare_long (long a, long b) {
-        if (a < b) {
-            return -1;
-        } else if (a > b) {
-            return 1;
-        } else {
-            return 0;
-        }
-    }
 }
diff --git a/src/librygel-server/rygel-media-object.vala b/src/librygel-server/rygel-media-object.vala
index 039e547..0defc03 100644
--- a/src/librygel-server/rygel-media-object.vala
+++ b/src/librygel-server/rygel-media-object.vala
@@ -50,6 +52,9 @@ public abstract class Rygel.MediaObject : GLib.Object {
     // and make the uri property single-value.
     public Gee.ArrayList<string> uris;
 
+    private Gee.List<MediaResource> media_resources
+                                    = new Gee.LinkedList<MediaResource> ();
+
     // You can keep both an unowned and owned ref to parent of this MediaObject.
     // In most cases, one will only need to keep an unowned ref to avoid cyclic
     // references since usually the parent container will keep refs to child items.
@@ -195,10 +200,119 @@ public abstract class Rygel.MediaObject : GLib.Object {
         return writables;
     }
 
+    /**
+     * Return the MediaResource list.
+     */
+    public Gee.List<MediaResource> get_resource_list () {
+        return media_resources;
+    }
+
+    /**
+     * Return a MediaResource list adapted for the HTTPServer
+     */
+    public Gee.List<MediaResource> get_resource_list_for_server (HTTPServer http_server) {
+        var new_list = new Gee.ArrayList<MediaResource> ();
+        foreach (var src_res in get_resource_list ()) {
+            var new_res = new MediaResource.from_resource (src_res.get_name (),
+                                                           src_res);
+            if (new_res.uri == null || new_res.uri == "") {
+                // Any resource without a URI will get a HTTP resource-based URI
+                new_res.uri = http_server.create_uri_for_item
+                                              (this,new_res.extension,-1,-1,
+                                               new_res.get_name ());
+                http_server.set_resource_delivery_options (new_res);
+                new_list.add (new_res);
+            } else { // URI doesn't refer to our HTTP server
+                string protocol;
+                try {
+                    protocol = this.get_protocol_for_uri (new_res.uri);
+                } catch (Error e) {
+                    warning ("Could not determine protocol for " + new_res.uri);
+                    continue;
+                }
+                if (protocol != "internal" || http_server.is_local ()) {
+                    new_list.add (new_res);
+                }
+            }
+        }
+
+        return new_list;
+    }
+
     public abstract DIDLLiteObject? serialize (Serializer serializer,
                                                HTTPServer http_server)
                                                throws Error;
 
+    /**
+     * Serialize the resource list
+     *
+     * Any resource with an empty URIs will get a resource-based HTTP URI and have its protocol
+     * and delivery options adjusted to the HTTPServer.
+     *
+     * Internal (e.g. "file:") resources will only be included when the http server
+     * is on the local host.
+     *
+     * Resources will be serialized in list order.
+     */
+    public void serialize_resource_list (DIDLLiteObject didl_object,
+                                         HTTPServer http_server)
+                                         throws Error {
+        // Note: Intentionally not using get_resource_list_for_server() to avoid a copy
+        foreach (var res in get_resource_list ()) {
+            if (res.uri == null || res.uri == "") {
+                // Any resource without a URI will get a HTTP resource-based URI
+                res.uri = http_server.create_uri_for_item
+                                        (this, res.extension,-1,-1,res.get_name ());
+                http_server.set_resource_delivery_options (res);
+                DIDLLiteResource didl_resource = didl_object.add_resource ();
+                res.serialize (didl_resource);
+                res.uri = "";
+            } else { // URI doesn't refer to our HTTP server
+                string protocol;
+                try {
+                    protocol = this.get_protocol_for_uri (res.uri);
+                } catch (Error e) {
+                    warning ("Could not determine protocol for " + res.uri);
+                    continue;
+                }
+                if (protocol != "internal" || http_server.is_local ()) {
+                    // Exclude internal resources when request is non-local
+                    DIDLLiteResource didl_resource = didl_object.add_resource ();
+                    res.serialize (didl_resource);
+                }
+            }
+        }
+    }
+
+    internal string get_protocol_for_uri (string uri) throws Error {
+        var scheme = Uri.parse_scheme (uri);
+        if (scheme == null) {
+            throw new MediaFileItemError.BAD_URI (_("Bad URI: %s"), uri);
+        }
+
+        if (scheme == "http") {
+            return "http-get";
+        } else if (scheme == "file") {
+            return "internal";
+        } else if (scheme == "rtsp") {
+            // FIXME: Assuming that RTSP is always accompanied with RTP over UDP
+            return "rtsp-rtp-udp";
+        } else {
+            // Assume the protocol to be the scheme of the URI
+            warning (_("Failed to probe protocol for URI %s. Assuming '%s'"),
+                     uri,
+                     scheme);
+
+            return scheme;
+        }
+    }
+
+    /**
+     * Create a stream source for the given resource
+     */
+    public abstract DataSource? create_stream_source_for_resource
+                                 (HTTPRequest request, MediaResource resource) throws Error;
+
     internal virtual void apply_didl_lite (DIDLLiteObject didl_object) {
         this.title = didl_object.title;
     }
@@ -285,17 +399,6 @@ public abstract class Rygel.MediaObject : GLib.Object {
         }
     }
 
-    internal virtual DIDLLiteResource add_resource
-                                        (DIDLLiteObject object,
-                                         string?        uri,
-                                         string         protocol,
-                                         string?        import_uri = null)
-                                         throws Error {
-        var res = object.add_resource ();
-
-        return res;
-    }
-
     protected int compare_int_props (int prop1, int prop2) {
         return (prop1 - prop2).clamp (-1, 1);
     }
diff --git a/src/librygel-server/rygel-media-resource.vala b/src/librygel-server/rygel-media-resource.vala
new file mode 100644
index 0000000..39a4661
--- /dev/null
+++ b/src/librygel-server/rygel-media-resource.vala
@@ -0,0 +1,360 @@
+/* 
+ * 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.
+ */
+
+using GUPnP;
+using Gee;
+
+/**
+ * Represents a media resource (Music, Video, Image, etc).
+ */
+public class Rygel.MediaResource : GLib.Object {
+    private string name;
+
+    // Res block fields
+    public string uri { get; set; }
+    public string import_uri { get; set; }
+    public string extension { get; set; default = null; }
+    public int64 size { get; set; default = -1; } // In bytes
+    public int64 cleartext_size { get; set; default = -1; } // In bytes
+    public long duration { get; set; default = -1; } // In seconds
+    public int bitrate { get; set; default = -1; } // In bits per second
+    public int bits_per_sample { get; set; default = -1; }
+    public int color_depth { get; set; default = -1; } // In bits
+    public int width { get; set; default = -1; } // In pixels
+    public int height { get; set; default = -1; } // In pixels
+    public int audio_channels { get; set; default = -1; }
+    public int sample_freq { get; set; default = -1; } // In Hz
+
+    // ProtocolInfo fields
+    public string protocol { get; set; default = null; }
+    public string mime_type  { get; set; default = null; }
+    public string dlna_profile  { get; set; default = null; }
+    public string network  { get; set; default = null; }
+    public string[] play_speeds = null;
+    public DLNAConversion dlna_conversion { get; set; default = DLNAConversion.NONE; }
+    public DLNAFlags dlna_flags { get; set; default = DLNAFlags.NONE; }
+    public DLNAOperation dlna_operation { get; set; default = DLNAOperation.NONE; }
+
+    // I know gupnp-av DIDLLiteResource and ProtocolInfo structures have the above fields.
+    //  But both proved to be problematic in their current form. This class can be
+    //  refactored if/when these classes are made more more flexible. For now, this class
+    //  needs to serve the needs of Rygel first and foremost...
+
+    public MediaResource (string name) {
+        this.name = name;
+    }
+
+    /**
+     * Copy constructor
+     */
+    public MediaResource.from_resource (string name, MediaResource that) {
+        this.name = name;
+        // res block
+        this.uri = that.uri;
+        this.import_uri = that.uri;
+        this.extension = that.extension;
+        this.size = that.size;
+        this.cleartext_size = that.cleartext_size;
+        this.duration = that.duration;
+        this.bitrate = that.bitrate;
+        this.bits_per_sample = that.bits_per_sample;
+        this.color_depth = that.color_depth;
+        this.width = that.width;
+        this.height = that.height;
+        this.audio_channels = that.audio_channels;
+        this.sample_freq = that.sample_freq;
+        // protocol info
+        this.protocol = that.protocol;
+        this.mime_type = that.mime_type;
+        this.dlna_profile = that.dlna_profile;
+        this.network = that.network;
+        this.play_speeds = copy_speeds (that.play_speeds);
+        this.dlna_conversion = that.dlna_conversion;
+        this.dlna_flags = that.dlna_flags;
+        this.dlna_operation = that.dlna_operation;
+    }
+
+    public static string []? copy_speeds (string? [] src) {
+        if (src == null) {
+            return null;
+        }
+        var new_speeds = new string[src.length];
+        int speed_index = 0;
+        foreach (var speed in src) {
+            new_speeds[speed_index++] = speed;
+        }
+
+        return new_speeds;
+    }
+
+    public string get_name () {
+        return this.name;
+    }
+
+    private HashMap<string,string> property_table = new HashMap<string,string> ();
+
+    public void set_custom_property (string ? name, string ? value) {
+        property_table.set (name,value);
+    }
+
+    public string get_custom_property (string ? name) {
+        return property_table.get (name);
+    }
+
+    public Set get_custom_property_names () {
+        return property_table.keys;
+    }
+
+    public void apply_didl_lite (DIDLLiteResource didl_resource) {
+        //  Populate the MediaResource from the given DIDLLiteResource
+        // Note: For a DIDLLiteResource, a value of -1/null also signals "not set"
+        this.uri = didl_resource.uri;
+        this.size = didl_resource.size64;
+        this.cleartext_size = didl_resource.cleartext_size;
+        this.duration = didl_resource.duration;
+        this.bitrate = didl_resource.bitrate;
+        this.bits_per_sample = didl_resource.bits_per_sample;
+        this.color_depth = didl_resource.color_depth;
+        this.width = didl_resource.width;
+        this.height = didl_resource.height;
+        this.audio_channels = didl_resource.audio_channels;
+        this.sample_freq = didl_resource.sample_freq;
+        if (didl_resource.protocol_info != null) {
+            this.protocol = didl_resource.protocol_info.protocol;
+            this.mime_type = didl_resource.protocol_info.mime_type;
+            this.dlna_profile = didl_resource.protocol_info.dlna_profile;
+            this.network = didl_resource.protocol_info.network;
+            this.play_speeds = copy_speeds (didl_resource.protocol_info.play_speeds);
+            this.dlna_conversion = didl_resource.protocol_info.dlna_conversion;
+            this.dlna_flags = didl_resource.protocol_info.dlna_flags;
+            this.dlna_operation = didl_resource.protocol_info.dlna_operation;
+        }
+    }
+
+    public DIDLLiteResource serialize (DIDLLiteResource didl_resource) {
+        // Note: For a DIDLLiteResource, a value of -1/null also signals "not set"
+        didl_resource.uri = this.uri;
+        didl_resource.size64 = this.size;
+        didl_resource.cleartext_size = this.cleartext_size;
+        didl_resource.duration = this.duration;
+        didl_resource.bitrate = this.bitrate;
+        didl_resource.bits_per_sample = this.bits_per_sample;
+        didl_resource.color_depth = this.color_depth;
+        didl_resource.width = this.width;
+        didl_resource.height = this.height;
+        didl_resource.audio_channels = this.audio_channels;
+        didl_resource.sample_freq = this.sample_freq;
+        didl_resource.protocol_info = get_protocol_info ();
+
+        return didl_resource;
+    }
+
+    public void set_protocol_info (ProtocolInfo pi) {
+        this.protocol = pi.protocol;
+        this.network = pi.network;
+        this.mime_type = pi.mime_type;
+        this.dlna_profile = pi.dlna_profile;
+        this.dlna_conversion = pi.dlna_conversion;
+        this.dlna_operation = pi.dlna_operation;
+        this.dlna_flags = pi.dlna_flags;
+        this.play_speeds = copy_speeds (pi.play_speeds);
+    }
+
+    public ProtocolInfo get_protocol_info () {
+        var new_pi = new ProtocolInfo ();
+
+        new_pi.protocol = this.protocol;
+        new_pi.network = this.network;
+        new_pi.mime_type = this.mime_type;
+        new_pi.dlna_profile = this.dlna_profile;
+        new_pi.dlna_conversion = this.dlna_conversion;
+        new_pi.dlna_operation = this.dlna_operation;
+        new_pi.dlna_flags = this.dlna_flags;
+        // ProtocolInfo doesn't like having play_speeds set to null
+        if (this.play_speeds != null) {
+            new_pi.play_speeds = copy_speeds (this.play_speeds);
+        }
+
+        return new_pi;
+    }
+
+    public bool supports_arbitrary_byte_seek () {
+        return is_dlna_operation_mode_set (DLNAOperation.RANGE);
+    }
+
+    public bool supports_arbitrary_time_seek () {
+        return is_dlna_operation_mode_set (DLNAOperation.TIMESEEK);
+    }
+
+    public bool supports_limited_byte_seek () {
+        return is_dlna_protocol_flag_set (DLNAFlags.BYTE_BASED_SEEK);
+    }
+
+    public bool supports_limited_time_seek () {
+        return is_dlna_protocol_flag_set (DLNAFlags.TIME_BASED_SEEK);
+    }
+
+    public bool supports_limited_cleartext_byte_seek () {
+        return is_dlna_protocol_flag_set (DLNAFlags.LOP_CLEARTEXT_BYTESEEK);
+    }
+
+    public bool supports_full_cleartext_byte_seek () {
+        return is_dlna_protocol_flag_set (DLNAFlags.CLEARTEXT_BYTESEEK_FULL);
+    }
+
+    public bool is_link_protection_enabled () {
+        return is_dlna_protocol_flag_set (DLNAFlags.LINK_PROTECTED_CONTENT);
+    }
+
+    public bool is_dlna_content () {
+        return (this.dlna_profile != null);
+    }
+
+    public string get_default_transfer_mode () {
+        if (is_dlna_protocol_flag_set (DLNAFlags.STREAMING_TRANSFER_MODE)) {
+            return "Streaming";
+        } else {
+            return "Interactive";
+        }
+    }
+
+    public bool supports_transfer_mode (string transfer_mode) {
+        if (!this.is_dlna_content ()) {
+            return true;
+        }
+
+        switch (transfer_mode) {
+            case "Streaming" :
+                return is_dlna_protocol_flag_set (DLNAFlags.STREAMING_TRANSFER_MODE);
+            case "Interactive" :
+                return is_dlna_protocol_flag_set (DLNAFlags.INTERACTIVE_TRANSFER_MODE);
+            case "Background" :
+                return is_dlna_protocol_flag_set (DLNAFlags.BACKGROUND_TRANSFER_MODE);
+            default:
+                return false;
+        }
+    }
+
+    public bool is_streamable () {
+        return is_dlna_protocol_flag_set (DLNAFlags.STREAMING_TRANSFER_MODE);
+    }
+
+    // This is to check if any of the cleartext byte seek operations are supported.
+    public bool is_cleartext_range_support_enabled () {
+        return ( is_dlna_protocol_flag_set (DLNAFlags.CLEARTEXT_BYTESEEK_FULL
+                                            | DLNAFlags.LOP_CLEARTEXT_BYTESEEK) );
+    }
+
+    public bool supports_playspeed () {
+        return (this.play_speeds.length > 0);
+    }
+
+    public bool is_dlna_protocol_flag_set (long flags) {
+        return ((this.dlna_flags & flags) != 0);
+    }
+
+    public bool is_dlna_operation_mode_set (long flags) {
+        return ((this.dlna_operation & flags) != 0);
+    }
+
+    public string to_string () {
+        var strbuf = new StringBuilder ();
+        strbuf.append (name).append_unichar ('(');
+        if (this.size >= 0) {
+            strbuf.append ("size ").append (this.size.to_string ())
+                  .append_unichar (',');
+        }
+        if (this.cleartext_size >= 0) {
+            strbuf.append ("cleartextsize ").append (this.cleartext_size.to_string ())
+                  .append_unichar (',');
+        }
+        if (this.duration >= 0) {
+            strbuf.append ("duration ").append (this.duration.to_string ())
+                  .append_unichar (',');
+        }
+        if (this.bitrate >= 0) {
+            strbuf.append ("bitrate ").append (this.bitrate.to_string ())
+                  .append_unichar (',');
+        }
+        if (this.bits_per_sample >= 0) {
+            strbuf.append ("bits_per_sample ").append (this.bits_per_sample.to_string ())
+                  .append_unichar (',');
+        }
+        if (this.width >= 0) {
+            strbuf.append ("width ").append (this.width.to_string ())
+                  .append_unichar (',');
+        }
+        if (this.height >= 0) {
+            strbuf.append ("height ").append (this.height.to_string ())
+                  .append_unichar (',');
+        }
+        if (this.color_depth >= 0) {
+            strbuf.append ("color_depth ").append (this.color_depth.to_string ())
+                  .append_unichar (',');
+        }
+        if (this.audio_channels >= 0) {
+            strbuf.append ("audio_channels ").append (this.audio_channels.to_string ())
+                  .append_unichar (',');
+        }
+        if (this.sample_freq >= 0) {
+            strbuf.append ("sample_freq ").append (this.sample_freq.to_string ())
+                  .append_unichar (',');
+        }
+        if (this.network != null) {
+            strbuf.append ("network ").append (this.network).append_unichar (',');
+        }
+        strbuf.append ("protocol ")
+              .append (this.protocol == null ? "null" : this.protocol)
+              .append_unichar (',');
+        strbuf.append ("mime_type ")
+              .append (this.mime_type == null ? "null" : this.mime_type)
+              .append_unichar (',');
+        strbuf.append ("dlna_profile ")
+              .append (this.dlna_profile == null ? "null" : this.dlna_profile)
+              .append_unichar (',');
+        strbuf.append_printf ("dlna_flags %.8X,", this.dlna_flags);
+        if (this.dlna_conversion != DLNAConversion.NONE) {
+            strbuf.append_printf ("dlna_conversion %1d,", this.dlna_conversion);
+        }
+        if (this.dlna_operation != DLNAOperation.NONE) {
+            strbuf.append_printf ("dlna_operation %.2X,", this.dlna_operation);
+        }
+        if (this.play_speeds != null) {
+            strbuf.append ("play_speeds [");
+            foreach (var speed in this.play_speeds) {
+                strbuf.append (speed).append_unichar (',');
+            }
+            strbuf.overwrite (strbuf.len-1,"]"); // Replace comma
+        }
+        strbuf.append (",uri ").append (this.uri == null ? "null" : this.uri);
+        strbuf.append_unichar (')');
+
+        return strbuf.str;
+    }
+}
diff --git a/src/librygel-server/rygel-music-item.vala b/src/librygel-server/rygel-music-item.vala
index 73b5a5b..700e346 100644
--- a/src/librygel-server/rygel-music-item.vala
+++ b/src/librygel-server/rygel-music-item.vala
@@ -62,20 +65,6 @@ public class Rygel.MusicItem : AudioItem {
         } catch (Error err) {};
     }
 
-    internal override void add_resources (DIDLLiteItem didl_item,
-                                          bool         allow_internal)
-                                          throws Error {
-        base.add_resources (didl_item, allow_internal);
-
-        if (this.album_art != null) {
-            var protocol = this.get_protocol_for_uri (this.album_art.uri);
-
-            if (allow_internal || protocol != "internal") {
-                didl_item.album_art = this.album_art.uri;
-            }
-        }
-    }
-
     internal override int compare_by_property (MediaObject media_object,
                                                string      property) {
         if (!(media_object is MusicItem)) {
@@ -130,34 +119,30 @@ public class Rygel.MusicItem : AudioItem {
             didl_item.genre = this.genre;
         }
 
-        if (didl_item.album_art != null) {
-            didl_item.album_art = MediaItem.address_regex.replace_literal
-                                        (didl_item.album_art,
-                                         -1,
-                                         0,
-                                         http_server.context.host_ip);
+        if (!this.place_holder && this.album_art != null) {
+            var protocol = this.get_protocol_for_uri (this.album_art.uri);
+
+            // Use the existing URI if the server is local or a non-internal/file uri is set
+            if (http_server.is_local () || protocol != "internal") {
+                didl_item.album_art = this.album_art.uri;
+            } else {
+                // Create a http uri for the album art that our server can process
+                string http_uri = http_server.create_uri_for_item (this,
+                                                                   this.album_art.file_extension,
+                                                                   0,
+                                                                   -1,
+                                                                   null);
+                didl_item.album_art = MediaFileItem.address_regex.replace_literal
+                                            (http_uri,
+                                             -1,
+                                             0,
+                                             http_server.context.host_ip);
+            }
         }
 
         return didl_item;
     }
 
-    internal override void add_proxy_resources (HTTPServer   server,
-                                                DIDLLiteItem didl_item)
-                                                throws Error {
-        base.add_proxy_resources (server, didl_item);
-
-        // Album-art URI comes in the end
-        if (!this.place_holder &&
-            this.album_art != null &&
-            server.need_proxy (this.album_art.uri)) {
-            didl_item.album_art = server.create_uri_for_item (this,
-                                                              0,
-                                                              -1,
-                                                              null,
-                                                              null);
-        }
-    }
-
     private string get_first (GLib.List<DIDLLiteContributor>? contributors) {
         if (contributors != null) {
             return contributors.data.name;
diff --git a/src/librygel-server/rygel-playlist-item.vala b/src/librygel-server/rygel-playlist-item.vala
index 4495941..1f5e415 100644
--- a/src/librygel-server/rygel-playlist-item.vala
+++ b/src/librygel-server/rygel-playlist-item.vala
@@ -40,10 +40,6 @@ public class Rygel.PlaylistItem : MediaItem {
                 upnp_class : upnp_class);
     }
 
-    public override bool streamable () {
-        return false;
-    }
-
     internal override DIDLLiteObject? serialize (Serializer serializer,
                                                  HTTPServer http_server)
                                                  throws Error {
@@ -51,4 +47,8 @@ public class Rygel.PlaylistItem : MediaItem {
 
         return didl_item;
     }
+
+    internal override void add_resources (HTTPServer server) {
+        // PlaylistItem doesn't add secondary resources
+    }
 }
diff --git a/src/librygel-server/rygel-source-connection-manager.vala b/src/librygel-server/rygel-source-connection-manager.vala
index 4c23884..2f8d7f2 100644
--- a/src/librygel-server/rygel-source-connection-manager.vala
+++ b/src/librygel-server/rygel-source-connection-manager.vala
@@ -2,9 +2,11 @@
  * Copyright (C) 2012 Intel Corporation.
  * Copyright (C) 2009-2011 Nokia Corporation.
  * Copyright (C) 2008 Zeeshan Ali (Khattak) <zeeshanak@gnome.org>.
+ * Copyright (C) 2013 Cable Television Laboratories, Inc.
  *
  * Author: Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
  *         Jens Georg <jensg@openismus.com>
+ *         Parthiban Balasubramanian <P.Balasubramanian-contractor@cablelabs.com>
  *
  * This file is part of Rygel.
  *
@@ -36,54 +38,5 @@ internal class Rygel.SourceConnectionManager : Rygel.ConnectionManager {
         this.rcs_id = -1;
         this.av_transport_id = -1;
         this.direction = "Output";
-
-        foreach (var protocol_info in this.get_protocol_info ()) {
-            if (this.source_protocol_info != "") {
-                // No comma before the first one
-                this.source_protocol_info += ",";
-            }
-
-            this.source_protocol_info += protocol_info.to_string ();
-        }
-    }
-
-    private ArrayList<ProtocolInfo> get_protocol_info () {
-        var server = this.get_http_server ();
-        var protocol_infos = server.get_protocol_info ();
-
-        var plugin = this.root_device.resource_factory as MediaServerPlugin;
-        unowned GLib.List<DLNAProfile> profiles = plugin.supported_profiles;
-
-        var protocol = server.get_protocol ();
-
-        foreach (var profile in profiles) {
-            var protocol_info = new ProtocolInfo ();
-
-            protocol_info.protocol = protocol;
-            protocol_info.mime_type = profile.mime;
-            protocol_info.dlna_profile = profile.name;
-
-            if (!(protocol_info in protocol_infos)) {
-                protocol_infos.insert (0, protocol_info);
-            }
-        }
-
-        return protocol_infos;
-    }
-
-    private HTTPServer get_http_server () {
-        HTTPServer server = null;
-
-        var root_device = (Rygel.RootDevice) this.root_device;
-
-        // Find the ContentDirectory service attached to this root device.
-        foreach (var service in root_device.services) {
-            if (service.get_type().is_a (typeof (Rygel.ContentDirectory))) {
-                var content_directory = (Rygel.ContentDirectory) service;
-                server = content_directory.http_server;
-            }
-        }
-
-        return server;
     }
 }
diff --git a/src/librygel-server/rygel-thumbnail.vala b/src/librygel-server/rygel-thumbnail.vala
index 42ed920..73423dc 100644
--- a/src/librygel-server/rygel-thumbnail.vala
+++ b/src/librygel-server/rygel-thumbnail.vala
@@ -1,7 +1,9 @@
 /*
  * Copyright (C) 2008 Zeeshan Ali <zeenix@gmail.com>.
+ * Copyright (C) 2013 Cable Television Laboratories, Inc.
  *
  * Author: Zeeshan Ali <zeenix@gmail.com>
+ *         Craig Pratt <craig@ecaspia.com>
  *
  * This file is part of Rygel.
  *
@@ -36,45 +38,24 @@ public class Rygel.Thumbnail : Rygel.IconInfo {
         this.dlna_profile = dlna_profile;
     }
 
-    internal virtual DIDLLiteResource? add_resource (DIDLLiteItem didl_item,
-                                                     string       protocol) {
-        var res = didl_item.add_resource ();
-
-        /* We check for NULL because 
-         * gupnp_didl_lite_resource_set_uri(),
-         * used by the generated code,
-         * complains, with a critical warning, if the URI is NULL.
-         * It's already the default.
-         */
-        if (this.uri != null) {
-            res.uri = this.uri;
-        }
-
-        res.size64 = this.size;
+    internal virtual MediaResource get_resource (string protocol) {
+        MediaResource res = new MediaResource (protocol + "_thumbnail");
 
+        res.size = this.size;
         res.width = this.width;
         res.height = this.height;
         res.color_depth = this.depth;
-
-        /* Protocol info */
-        res.protocol_info = this.get_protocol_info (protocol);
+        res.mime_type = this.mime_type;
+        res.dlna_profile = this.dlna_profile;
+        res.protocol = protocol;
+        // Note: These represent best-case. The MediaServer/HTTPServer can dial these back
+        res.dlna_flags |= DLNAFlags.INTERACTIVE_TRANSFER_MODE |
+                          DLNAFlags.BACKGROUND_TRANSFER_MODE |
+                          DLNAFlags.CONNECTION_STALL |
+                          DLNAFlags.DLNA_V15;
+        res.dlna_operation = DLNAOperation.RANGE;
+        res.dlna_conversion = DLNAConversion.TRANSCODED;
 
         return res;
     }
-
-    private ProtocolInfo get_protocol_info (string protocol) {
-        var protocol_info = new ProtocolInfo ();
-
-        protocol_info.mime_type = this.mime_type;
-        protocol_info.dlna_profile = this.dlna_profile;
-        protocol_info.protocol = protocol;
-        protocol_info.dlna_flags |= DLNAFlags.INTERACTIVE_TRANSFER_MODE |
-                                    DLNAFlags.BACKGROUND_TRANSFER_MODE |
-                                    DLNAFlags.CONNECTION_STALL |
-                                    DLNAFlags.DLNA_V15;
-        protocol_info.dlna_operation = DLNAOperation.RANGE;
-        protocol_info.dlna_conversion = DLNAConversion.TRANSCODED;
-
-        return protocol_info;
-    }
 }
diff --git a/src/librygel-server/rygel-transcode-manager.vala b/src/librygel-server/rygel-transcode-manager.vala
deleted file mode 100644
index 8df32d3..0000000
--- a/src/librygel-server/rygel-transcode-manager.vala
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2009-2012 Nokia Corporation.
- * Copyright (C) 2012 Intel Corporation.
- *
- * Author: Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
- *                               <zeeshan.ali@nokia.com>
- *         Jens Georg <jensg@openismus.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.
- *
- * 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;
-using Gee;
-
-/**
- * Responsible for management of all transcoders:
- * # Gets the appropriate transcoder given a transcoding target.
- * # Provide all possible transcoding resources for items.
- */
-public abstract class Rygel.TranscodeManager : GLib.Object {
-
-    private static bool protocol_equal_func (GUPnP.ProtocolInfo a,
-                                             GUPnP.ProtocolInfo b) {
-        return a.dlna_profile == b.dlna_profile &&
-               a.mime_type == b.mime_type;
-    }
-
-    public TranscodeManager () { }
-
-    public abstract string create_uri_for_item (MediaItem  item,
-                                                int        thumbnail_index,
-                                                int        subtitle_index,
-                                                string?    transcode_target,
-                                                string?    playlist_target);
-
-    public void add_resources (DIDLLiteItem didl_item, MediaItem item)
-                               throws Error {
-        var engine = MediaEngine.get_default ();
-        var list = new GLib.List<Transcoder> ();
-        unowned GLib.List<Transcoder> transcoders = engine.get_transcoders ();
-
-        foreach (var transcoder in transcoders) {
-            if (transcoder.get_distance (item) != uint.MAX) {
-                list.append (transcoder);
-            }
-        }
-
-        list.sort_with_data (item.compare_transcoders);
-        foreach (var transcoder in list) {
-            transcoder.add_resource (didl_item, item, this);
-        }
-    }
-
-    public Transcoder get_transcoder (string  target) throws Error {
-        Transcoder transcoder = null;
-
-        foreach (var iter in MediaEngine.get_default ().get_transcoders ()) {
-            if (iter.can_handle (target)) {
-                transcoder = iter;
-            }
-        }
-
-        if (transcoder == null) {
-            throw new HTTPRequestError.NOT_FOUND (
-                            _("No transcoder available for target format '%s'"),
-                            target);
-        }
-
-        return transcoder;
-    }
-
-    internal abstract string get_protocol ();
-
-    internal virtual ArrayList<ProtocolInfo> get_protocol_info () {
-        var engine = MediaEngine.get_default ();
-        var protocol_infos = new ArrayList<ProtocolInfo> (protocol_equal_func);
-        unowned GLib.List<Transcoder> transcoders = engine.get_transcoders ();
-
-        foreach (var transcoder in transcoders) {
-            var protocol_info = new ProtocolInfo ();
-
-            protocol_info.protocol = this.get_protocol ();
-            protocol_info.mime_type = transcoder.mime_type;
-            protocol_info.dlna_profile = transcoder.dlna_profile;
-
-            protocol_infos.add (protocol_info);
-        }
-
-        return protocol_infos;
-    }
-}
diff --git a/src/librygel-server/rygel-transcoder.vala b/src/librygel-server/rygel-transcoder.vala
deleted file mode 100644
index fcaab7b..0000000
--- a/src/librygel-server/rygel-transcoder.vala
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2009-2012 Nokia Corporation.
- * Copyright (C) 2012 Intel Corporation.
- *
- * Author: Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
- *                               <zeeshan.ali@nokia.com>
- *         Jens Georg <jensg@openismus.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.
- *
- * 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;
-
-/**
- * The base Transcoder class. Each implementation derives from it and must
- * implement create_source() and get_distance().
- *
- * Transcoders are obtained from rygel_media_engine_get_transcoders() and
- * are only expected to support the derived #RygelDataSource types provided
- * by the same media engine.
- */
-public abstract class Rygel.Transcoder : GLib.Object {
-    public string mime_type { get; construct; }
-    public string dlna_profile { get; construct; }
-    public string extension { get; construct; }
-
-    /**
-     * Creates a transcoding source.
-     *
-     * The provided original #RygelDataSource will have been implemented by the
-     * same media engine that provided the #RygelTranscoder,
-     * allowing the #RygelTranscoder to access specific resources of the
-     * underlying multimedia backend used by the media engine.
-     *
-     * @param src the media item to create the transcoding source for
-     * @param src the original (non-transcoding) source
-     *
-     * @return      the new transcoding source
-     */
-    public abstract DataSource create_source (MediaItem  item,
-                                              DataSource src) throws Error;
-
-    //TODO: It would be simpler for this to take a DIDLLiteResource to be filled,
-    //rather than taking a didl_item, requiring the call of the base class, and then
-    //returning the DIDLLiteResource.
-    /**
-     * Derived classes should implement this function to fill a GUPnPDIDLLiteResource,
-     * representing the transcoded content, with parameters specific to the transcoder,
-     * such as bitrate or resolution. The GUPnPDIDLLiteResource should be instantiated
-     * by calling this base class implementation, passing the provided didl_item, item
-     * and manager parameters.
-     *
-     * @param didl_item The DIDLLite item for which to create the resource, by calling the base class implementation.
-     * @param item The media item for which to create the DIDLiteResource, by calling the base class implementation.
-     * @param manager The transcoder manager to pass to the base class implemenetation.
-     * @return The new resource.
-     */
-    public virtual DIDLLiteResource? add_resource (DIDLLiteItem     didl_item,
-                                                   MediaItem        item,
-                                                   TranscodeManager manager)
-                                                   throws Error {
-        if (this.mime_type_is_a (item.mime_type, this.mime_type) &&
-            this.dlna_profile == item.dlna_profile) {
-            return null;
-        }
-
-        var protocol = manager.get_protocol ();
-        var uri = manager.create_uri_for_item (item,
-                                               -1,
-                                               -1,
-                                               this.dlna_profile,
-                                               null);
-        var res = item.add_resource (didl_item, uri, protocol);
-        res.size = -1;
-
-        var protocol_info = res.protocol_info;
-        protocol_info.mime_type = this.mime_type;
-        protocol_info.dlna_profile = this.dlna_profile;
-        protocol_info.dlna_conversion = DLNAConversion.TRANSCODED;
-        protocol_info.dlna_flags = DLNAFlags.STREAMING_TRANSFER_MODE |
-                                   DLNAFlags.BACKGROUND_TRANSFER_MODE |
-                                   DLNAFlags.CONNECTION_STALL |
-                                   DLNAFlags.DLNA_V15;
-        if (item is AudioItem && (item as AudioItem).duration > 0) {
-            protocol_info.dlna_operation = DLNAOperation.TIMESEEK;
-        } else {
-            protocol_info.dlna_operation = DLNAOperation.NONE;
-        }
-
-        return res;
-    }
-
-    /**
-     * Returns whether this trancoder can handle the specified DLNA profile.
-     * This is determined by the #RygelTranscodeManager, which checks
-     * the suitability of each #RygelTranscoder by calling
-     * rygel_transcoder_get_distance() with each #RygelMediaItem,
-     * choosing one DLNA profile for each transcoder to handle.
-     *
-     * @param target A DLNA profile name as obtained from rygel_media_item_get_dlna_profile().
-     *
-     * @return True if the transcoder can handle the specified DLNA profile.
-     */
-    public bool can_handle (string target) {
-        return target == this.dlna_profile;
-    }
-
-    /**
-     * Gets a numeric value that gives an gives an estimate of how hard
-     * it would be for this transcoder to trancode @item to the target profile of this transcoder.
-     *
-     * @param item the media item to calculate the distance for
-     *
-     * @return      the distance from the @item, uint.MIN if providing such a
-     *              value is impossible or uint.MAX if it doesn't make any
-     *              sense to use this transcoder for @item
-     */
-    public abstract uint get_distance (MediaItem item);
-
-    protected bool mime_type_is_a (string mime_type1, string mime_type2) {
-        string content_type1 = ContentType.get_mime_type (mime_type1);
-        string content_type2 = ContentType.get_mime_type (mime_type2);
-
-        return ContentType.is_a (content_type1, content_type2);
-    }
-}
diff --git a/src/librygel-server/rygel-http-transcode-handler.vala b/src/librygel-server/rygel-http-transcode-handler.vala
deleted file mode 100644
index da21a06..0000000
--- a/src/librygel-server/rygel-http-transcode-handler.vala
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2009 Nokia Corporation.
- * Copyright (C) 2012 Intel Corporation.
- *
- * Author: Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
- *                               <zeeshan.ali@nokia.com>
- *         Jens Georg <jensg@openismus.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.
- *
- * 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;
-
-/**
- * The handler for HTTP transcoding requests.
- */
-internal class Rygel.HTTPTranscodeHandler : HTTPGetHandler {
-    private Transcoder transcoder;
-
-    public HTTPTranscodeHandler (Transcoder   transcoder,
-                                 Cancellable? cancellable) {
-        this.transcoder = transcoder;
-        this.cancellable = cancellable;
-    }
-
-    public override void add_response_headers (HTTPGet request)
-                                               throws HTTPRequestError {
-        request.msg.response_headers.append ("Content-Type",
-                                             this.transcoder.mime_type);
-        if (request.seek != null) {
-            request.seek.add_response_headers ();
-        }
-
-        // Chain-up
-        base.add_response_headers (request);
-    }
-
-    public override HTTPResponse render_body (HTTPGet request)
-                                              throws HTTPRequestError {
-        var item = request.object as MediaItem;
-        var src = item.create_stream_source
-                                        (request.http_server.context.host_ip);
-        if (src == null) {
-            throw new HTTPRequestError.NOT_FOUND (_("Not found"));
-        }
-
-        try {
-            src = this.transcoder.create_source (item, src);
-
-            return new HTTPResponse (request, this, src);
-        } catch (GLib.Error err) {
-            throw new HTTPRequestError.NOT_FOUND (err.message);
-        }
-    }
-
-    protected override DIDLLiteResource add_resource
-                                        (DIDLLiteObject didl_object,
-                                         HTTPGet      request)
-                                        throws Error {
-        return this.transcoder.add_resource (didl_object as DIDLLiteItem,
-                                             request.object as MediaItem,
-                                             request.http_server);
-    }
-}
diff --git a/src/librygel-server/rygel-video-item.vala b/src/librygel-server/rygel-video-item.vala
index 488f6eb..e517b5e 100644
--- a/src/librygel-server/rygel-video-item.vala
+++ b/src/librygel-server/rygel-video-item.vala
@@ -80,10 +82,6 @@ public class Rygel.VideoItem : AudioItem, VisualItem {
         this.subtitles = new ArrayList<Subtitle> ();
     }
 
-    public override bool streamable () {
-        return true;
-    }
-
     public override void add_uri (string uri) {
         base.add_uri (uri);
 
@@ -99,35 +97,6 @@ public class Rygel.VideoItem : AudioItem, VisualItem {
         }
     }
 
-    internal override void add_resources (DIDLLiteItem didl_item,
-                                          bool         allow_internal)
-                                          throws Error {
-        foreach (var subtitle in this.subtitles) {
-            var protocol = this.get_protocol_for_uri (subtitle.uri);
-
-            if (allow_internal || protocol != "internal") {
-                subtitle.add_didl_node (didl_item);
-            }
-        }
-
-        base.add_resources (didl_item, allow_internal);
-
-        add_thumbnail_resources (didl_item, allow_internal);
-    }
-
-    internal override DIDLLiteResource add_resource
-                                        (DIDLLiteObject didl_object,
-                                         string?        uri,
-                                         string         protocol,
-                                         string?        import_uri = null)
-                                         throws Error {
-        var res = base.add_resource (didl_object, uri, protocol, import_uri);
-
-        this.add_visual_props (res);
-
-        return res;
-    }
-
     internal override int compare_by_property (MediaObject media_object,
                                                string      property) {
         if (!(media_object is VideoItem)) {
@@ -161,66 +130,84 @@ public class Rygel.VideoItem : AudioItem, VisualItem {
     internal override DIDLLiteObject? serialize (Serializer serializer,
                                                  HTTPServer  http_server)
                                                  throws Error {
-        var didl_item = base.serialize (serializer, http_server);
+        var didl_item = base.serialize (serializer, http_server) as DIDLLiteItem;
 
         if (this.author != null && this.author != "") {
             var contributor = didl_item.add_author ();
             contributor.name = this.author;
         }
 
-        return didl_item;
-    }
-
-    internal override void add_proxy_resources (HTTPServer   server,
-                                                DIDLLiteItem didl_item)
-                                                throws Error {
-        var main_subtitle = null as Subtitle;
         if (!this.place_holder) {
-            // Subtitles first
+            var main_subtitle = null as Subtitle;
             foreach (var subtitle in this.subtitles) {
-                if (!server.need_proxy (subtitle.uri)) {
-                    if (main_subtitle == null) {
-                        main_subtitle = subtitle;
-                    }
-
+                string protocol;
+                try {
+                    protocol = this.get_protocol_for_uri (subtitle.uri);
+                } catch (Error e) {
+                    message ("Could not determine protocol for " + subtitle.uri);
                     continue;
                 }
 
-                var uri = subtitle.uri; // Save the original URI
-                var index = this.subtitles.index_of (subtitle);
+                if (http_server.need_proxy (subtitle.uri)) {
+                    var uri = subtitle.uri; // Save the original URI
+                    var index = this.subtitles.index_of (subtitle);
 
-                subtitle.uri = server.create_uri_for_item (this,
-                                                           -1,
-                                                           index,
-                                                           null,
-                                                           null);
-                subtitle.add_didl_node (didl_item);
+                    subtitle.uri = http_server.create_uri_for_item (this,
+                                                                    subtitle.caption_type,
+                                                                    -1,
+                                                                    index,
+                                                                    null);
+                    subtitle.add_didl_node (didl_item);
+                    subtitle.uri = uri; // Now restore the original URI
 
-                if (main_subtitle == null) {
-                    main_subtitle = new Subtitle (subtitle.mime_type,
-                                                  subtitle.caption_type);
-                    main_subtitle.uri = subtitle.uri;
+                    if (main_subtitle == null) {
+                        main_subtitle = new Subtitle (subtitle.mime_type,
+                                                      subtitle.caption_type);
+                        main_subtitle.uri = uri;
+                    }
+                } else if (main_subtitle == null) {
+                    main_subtitle = subtitle;
                 }
 
-                // Now restore the original URI
-                subtitle.uri = uri;
+                if (http_server.is_local () || protocol != "internal") {
+                    subtitle.add_didl_node (didl_item);
+                }
+            }
+            if (main_subtitle != null) {
+                // Add resource-level subtitle metadata to all streamable video resources
+                // Note: All resources have already been serialized by the base
+                var resources = didl_item.get_resources ();
+                foreach (var resource in resources) {
+                    if ( (resource.protocol_info.dlna_flags
+                          & DLNAFlags.STREAMING_TRANSFER_MODE) != 0) {
+                        resource.subtitle_file_type =
+                            main_subtitle.caption_type.up ();
+                        resource.subtitle_file_uri = main_subtitle.uri;
+                    }
+                }
             }
         }
 
-        base.add_proxy_resources (server, didl_item);
+        return didl_item;
+    }
 
-        if (main_subtitle != null) {
-            var resources = didl_item.get_resources ();
-            foreach (var resource in resources) {
-                resource.subtitle_file_type =
-                    main_subtitle.caption_type.up ();
-                resource.subtitle_file_uri = main_subtitle.uri;
-            }
-        }
+    internal override MediaResource get_primary_resource () {
+        var res = base.get_primary_resource ();
 
-        if (!this.place_holder) {
-            // Thumbnails comes in the end
-            this.add_thumbnail_proxy_resources (server, didl_item);
-        }
+        res.width = this.width;
+        res.height = this.height;
+        res.color_depth = this.color_depth;
+        res.dlna_flags |= DLNAFlags.STREAMING_TRANSFER_MODE;
+
+        this.set_visual_resource_properties (res);
+
+        return res;
+    }
+
+    internal override void add_resources (HTTPServer http_server) {
+        base.add_resources (http_server);
+
+        // Add thumbnails (since VisualItem.serialize won't get called automatically)
+        this.add_thumbnail_resources (http_server);
     }
 }
diff --git a/src/librygel-server/rygel-visual-item.vala b/src/librygel-server/rygel-visual-item.vala
index 82a792e..a4d301c 100644
--- a/src/librygel-server/rygel-visual-item.vala
+++ b/src/librygel-server/rygel-visual-item.vala
@@ -69,41 +71,39 @@ public interface Rygel.VisualItem : MediaItem {
         }
     }
 
-    internal void add_thumbnail_resources (DIDLLiteItem didl_item,
-                                           bool         allow_internal)
-                                           throws Error {
-        foreach (var thumbnail in this.thumbnails) {
-            var protocol = this.get_protocol_for_uri (thumbnail.uri);
-
-            if (allow_internal || protocol != "internal") {
-                thumbnail.add_resource (didl_item, protocol);
-            }
-        }
-    }
-
-    internal void add_visual_props (DIDLLiteResource res) {
+    internal void set_visual_resource_properties (MediaResource res) {
         res.width = this.width;
         res.height = this.height;
         res.color_depth = this.color_depth;
     }
 
-    internal void add_thumbnail_proxy_resources (HTTPServer   server,
-                                                 DIDLLiteItem didl_item)
-                                                 throws Error {
+    internal void add_thumbnail_resources (HTTPServer http_server) {
         foreach (var thumbnail in this.thumbnails) {
-            if (server.need_proxy (thumbnail.uri)) {
-                var uri = thumbnail.uri; // Save the original URI
-                var index = this.thumbnails.index_of (thumbnail);
+            if (!this.place_holder) {
+                // Add the defined thumbnail uri unconditionally
+                //  (it will be filtered out if the request is remote)
+                string protocol;
+                try {
+                    protocol = this.get_protocol_for_uri (thumbnail.uri);
+                } catch (Error e) {
+                    message ("Could not determine protocol for " + thumbnail.uri);
+                    continue;
+                }
 
-                thumbnail.uri = server.create_uri_for_item (this,
-                                                            index,
-                                                            -1,
-                                                            null,
-                                                            null);
-                thumbnail.add_resource (didl_item, server.get_protocol ());
+                var thumb_res = thumbnail.get_resource (protocol);
+                this.get_resource_list ().add (thumb_res);
+                if (http_server.need_proxy (thumbnail.uri)) {
+                    var http_thumb_res = thumbnail.get_resource (http_server.get_protocol ());
 
-                // Now restore the original URI
-                thumbnail.uri = uri;
+                    var index = this.thumbnails.index_of (thumbnail);
+                    // Make a http uri for the thumbnail
+                    http_thumb_res.uri = http_server.create_uri_for_item (this,
+                                                                          thumbnail.file_extension,
+                                                                          index,
+                                                                          -1,
+                                                                          null);
+                    this.get_resource_list ().add (http_thumb_res);
+                }
             }
         }
     }
