diff --git a/data/rygel.conf b/data/rygel.conf
index 307da12..3e77847 100644
--- a/data/rygel.conf
+++ b/data/rygel.conf
@@ -46,10 +46,13 @@ port=0
 log-level=*:4
 
 # Allow upload of media files?
-allow-upload=true
+allow-upload=false
+# Possible values - image-upload;av-upload;audio-upload
+# This will take effect only if allow-upload is set to true
+upload-option=image-upload;av-upload;audio-upload
 
 # Allow deletion of media folders and files?
-allow-deletion=true
+allow-deletion=false
 
 # Semicolon-separated list of device user-agents (or parts thereof) that need
 # a downgrade in the UPnP device versions
@@ -91,7 +112,7 @@ transcoders=mp3;lpcm;mp2ts;wmv;aac;avc
 image-timeout = 15
 
 [Tracker]
-enabled=true
+enabled=false
 share-pictures=true
 share-videos=true
 share-music=true
@@ -108,7 +129,8 @@ title=@REALNAME@'s media
 #       * @VIDEOS@: The standard videos folder (typically ${HOME}/Videos).
 #       * @PICTURES@: The standard pictures folder (typically ${HOME}/Pictures).
 #
-uris=@MUSIC@;@VIDEOS@;@PICTURES@
+# Since we are disabling non av-upload disabling music and audio folders from harvesting.
+uris=@VIDEOS@
 extract-metadata=true
 monitor-changes=true
 virtual-folders=true
diff --git a/src/librygel-core/rygel-connection-manager.vala b/src/librygel-core/rygel-connection-manager.vala
index b9a4108..ec99446 100644
--- a/src/librygel-core/rygel-connection-manager.vala
+++ b/src/librygel-core/rygel-connection-manager.vala
@@ -22,6 +22,14 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Parthiban Balasubramanian <P.Balasubramanian-contractor@cablelabs.com>
+ */
+
 using GLib;
 using GUPnP;
 
@@ -154,4 +162,8 @@ public class Rygel.ConnectionManager : Service {
 
         action.return ();
     }
+
+    public void set_source_protocol_info(string protocol_info) {
+		source_protocol_info = protocol_info;
+	}
 }
diff --git a/src/librygel-core/rygel-description-file.vala b/src/librygel-core/rygel-description-file.vala
index 37978de..03e2ab7 100644
--- a/src/librygel-core/rygel-description-file.vala
+++ b/src/librygel-core/rygel-description-file.vala
@@ -20,6 +20,14 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Parthiban Balasubramanian <P.Balasubramanian-contractor@cablelabs.com>
+ */
+
 using GUPnP;
 using Xml;
 
@@ -192,43 +200,43 @@ public class Rygel.DescriptionFile : Object {
             // handled by Rygel transparently.
             var allow_upload = true;
             var allow_delete = false;
-
+            var upload_options = new Gee.ArrayList<string>();
             try {
                 var config = MetaConfig.get_default ();
                 allow_upload = config.get_allow_upload ();
                 allow_delete = config.get_allow_deletion ();
+                upload_options = config.get_string_list
+                                        ("general", "upload-option");
             } catch (GLib.Error error) { }
 
             if (allow_upload) {
-                if (PluginCapabilities.IMAGE_UPLOAD in capabilities) {
-                    flags += "image-upload";
+                foreach (var option in upload_options) {
+                    if (option == "image-upload" ||
+                        option == "av-upload"    ||
+                        option == "audio-upload") {
+                        flags += option;
+                    }
                 }
-
-                if (PluginCapabilities.VIDEO_UPLOAD in capabilities) {
-                    flags += "av-upload";
+                // If DMS implements Content synchronization option,
+                // shall support upload of mandatory DLNA formats.(DLNA 7.4.1.7.8.4)
+                if (PluginCapabilities.TRACK_CHANGES in capabilities) {
+                    flags += "content-synchronization";
+                    flags += "create-child-container";
                 }
 
-                if (PluginCapabilities.AUDIO_UPLOAD in capabilities) {
-                    flags += "audio-upload";
+                // Might be that the plugin only supports create-child-container but
+                // not change tracking, so we need to add this capability separately
+                if (PluginCapabilities.CREATE_CONTAINERS in capabilities &&
+                    !(PluginCapabilities.TRACK_CHANGES in capabilities)) {
+                    flags += "create-child-container";
+                }
+                // This shall coexist with atleast one of audio-upload,
+                // av-upload or image-upload. (DLNA 7.4.1.7.4.1 Table 26)
+                if (allow_delete) {
+                    flags += "create-item-with-OCM-destroy-item";
                 }
             }
 
-            if (allow_delete) {
-                flags += "create-item-with-OCM-destroy-item";
-            }
-
-        }
-
-        if (PluginCapabilities.TRACK_CHANGES in capabilities) {
-            flags += "content-synchronization";
-            flags += "create-child-container";
-        }
-
-        // Might be that the plugin only supports create-child-container but
-        // not change tracking, so we need to add this capability separately
-        if (PluginCapabilities.CREATE_CONTAINERS in capabilities &&
-            !(PluginCapabilities.TRACK_CHANGES in capabilities)) {
-            flags += "create-child-container";
         }
 
         // Set the flags we found; otherwise remove whatever is in the
diff --git a/src/librygel-server/filelist.am b/src/librygel-server/filelist.am
index fcc0bbc..18ddc15 100644
--- a/src/librygel-server/filelist.am
+++ b/src/librygel-server/filelist.am
@@ -15,6 +15,7 @@ LIBRYGEL_SERVER_VAPI_SOURCE_FILES = \
 	rygel-video-item.vala \
 	rygel-media-container.vala \
 	rygel-media-item.vala \
+	rygel-media-resource.vala \
 	rygel-media-object.vala \
 	rygel-media-server-plugin.vala \
 	rygel-search-expression.vala \
@@ -47,9 +48,11 @@ LIBRYGEL_SERVER_NONVAPI_SOURCE_FILES = \
 	rygel-http-post.vala \
 	rygel-http-request.vala \
 	rygel-http-response.vala \
+	rygel-http-response-element.vala \
 	rygel-http-server.vala \
 	rygel-http-time-seek.vala \
 	rygel-http-transcode-handler.vala \
+	rygel-http-resource-handler.vala \
 	rygel-import-resource.vala \
 	rygel-object-creator.vala \
 	rygel-reference-creator.vala \
@@ -72,9 +75,12 @@ LIBRYGEL_SERVER_NONVAPI_SOURCE_FILES = \
 	rygel-search.vala \
 	rygel-serializer.vala \
 	rygel-source-connection-manager.vala \
+	rygel-connection-manager-protocolinfo.vala \
 	rygel-subtitle-manager.vala \
 	rygel-thumbnailer.vala \
 	rygel-wmp-hacks.vala \
 	rygel-xbmc-hacks.vala \
 	rygel-xbox-hacks.vala \
-	rygel-data-sink.vala
+	rygel-data-sink.vala \
+	rygel-dlna-playspeed.vala \
+	rygel-dtcp-cleartext-byte-seek.vala
diff --git a/src/librygel-server/rygel-audio-item.vala b/src/librygel-server/rygel-audio-item.vala
index 5882ea5..4b29cb8 100644
--- a/src/librygel-server/rygel-audio-item.vala
+++ b/src/librygel-server/rygel-audio-item.vala
@@ -22,6 +22,14 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Doug Galligan <doug@sentosatech.com>>
+ */
+
 using GUPnP;
 
 /**
@@ -30,6 +38,8 @@ using GUPnP;
 public class Rygel.AudioItem : MediaItem {
     public new const string UPNP_CLASS = "object.item.audioItem";
 
+    // TODO: These are not properties of Items, but of Resources, correct?
+    //       Everything that uses these fields should be audited.
     public long duration { get; set; default = -1; }  // Duration in seconds
     public int bitrate { get; set; default = -1; }    // Bytes/second
 
@@ -55,9 +65,10 @@ public class Rygel.AudioItem : MediaItem {
                                         (DIDLLiteObject didl_object,
                                          string?        uri,
                                          string         protocol,
+                                         MediaResource  resource,
                                          string?        import_uri = null)
                                          throws Error {
-        var res = base.add_resource (didl_object, uri, protocol, import_uri);
+        var res = base.add_resource (didl_object, uri, protocol, resource, import_uri);
 
         res.duration = this.duration;
         res.bitrate = this.bitrate;
diff --git a/src/librygel-server/rygel-connection-manager-protocolinfo.vala b/src/librygel-server/rygel-connection-manager-protocolinfo.vala
new file mode 100644
index 0000000..14c1c53
--- /dev/null
+++ b/src/librygel-server/rygel-connection-manager-protocolinfo.vala
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 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 APPLE 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.
+ */
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Parthiban Balasubramanian <P.Balasubramanian-contractor@cablelabs.com>
+ */
+
+using Gee;
+using GLib;
+using GUPnP;
+
+public class Rygel.ConnectionManagerProtocolInfo : GLib.Object {
+
+    private static ConnectionManagerProtocolInfo cm_protocol_info = null;
+
+    internal class CMSProtocolInfo {
+        public string name;
+        public GUPnP.DLNAOperation op_param;
+        public string[] ps_flag;
+        public GUPnP.DLNAFlags flags;
+
+        public CMSProtocolInfo(string name, GUPnP.DLNAOperation op_param, string[] ps_flag, GUPnP.DLNAFlags flags) {
+            this.name = name;
+            this.op_param = op_param;
+            this.ps_flag = ps_flag;
+            this.flags = flags;
+        }
+
+        public void combine_fourth_field_values (GUPnP.DLNAOperation op_param, string[] ps_flag, GUPnP.DLNAFlags flags) {
+            this.op_param |= op_param;
+            this.flags |= flags;
+
+            add_missing_speeds (this.ps_flag, ps_flag);
+        }
+
+        private void add_missing_speeds (string[] old_ps, string[] new_ps) {
+            Gee.List<string> combined_speed = new Gee.ArrayList<string>();
+
+            foreach (var old_ps_str in old_ps) {
+                combined_speed.add (old_ps_str);
+            }
+
+            foreach (var new_ps_str in new_ps) {
+                if (!combined_speed.contains (new_ps_str)) {
+                    combined_speed.add (new_ps_str);
+                }
+            }
+            this.ps_flag = combined_speed.to_array();
+        }
+
+        private string append_reserved_zeros (string flag_str) {
+            return flag_str + "00000000" + "00000000" + "00000000";
+        }
+
+        public string to_string() {
+              return this.name +
+                     (this.op_param == DLNAOperation.NONE ? "" : "DLNA.ORG_OP=" + "%0.2x".printf (this.op_param) + ";")+
+                     (ps_flag != null ? "DLNA.ORG_PS=" + string.joinv("\\,", this.ps_flag) + ";" : "") +
+                     (this.flags == DLNAFlags.NONE ? "" :  append_reserved_zeros ("DLNA.ORG_FLAGS=" + "%0.8x".printf (this.flags)));
+        }
+    }
+
+    private HashMap<string,CMSProtocolInfo> union_protocol_string
+                        = new HashMap< string,CMSProtocolInfo >();
+
+    private ConnectionManagerProtocolInfo() {
+
+    }
+
+    public static ConnectionManagerProtocolInfo get_default() {
+        if (cm_protocol_info == null) {
+            cm_protocol_info = new ConnectionManagerProtocolInfo();
+        }
+        return cm_protocol_info;
+    }
+
+    public void update_source_protocol_info (Rygel.RootDevice root_device, MediaObjects media_objects) {
+
+        union_protocol_string.clear();
+        // Iterate through all media objects and extract protocolinfo
+        foreach (var media_item in media_objects) {
+            if (media_item is MediaItem) {
+                MediaItem t_item = (media_item as MediaItem);
+                if (t_item.media_resources == null) {
+                    continue;
+                }
+
+                foreach (var media_resource in t_item.media_resources) {
+                    MediaResource t_resource = (media_resource as MediaResource);
+                    string protocolInfo = t_resource.protocol_info.to_string();
+
+                    debug ("Got ProtocolInfo : %s ", protocolInfo);
+                    string[] key = protocolInfo.split (":");
+
+                    // Fourth field must start with DLNA.ORG_PN
+                    if (key[3].index_of ("DLNA.ORG_PN=",0) != -1) {
+                        string hash_key = t_resource.protocol_info.protocol + ":" +
+                                     t_resource.protocol_info.network + ":" +
+                                     t_resource.protocol_info.mime_type + ":" +
+                                     "DLNA.ORG_PN=" + t_resource.protocol_info.dlna_profile+";";
+
+                        if (union_protocol_string.has_key (hash_key)) {
+                            CMSProtocolInfo cms_info = union_protocol_string.get (hash_key);
+
+                            // TODO: Check if the update is needed.
+
+                            // Call to combine the fourth field values if any..
+                            cms_info.combine_fourth_field_values
+                                     (t_resource.protocol_info.get_dlna_operation(),
+                                      t_resource.protocol_info.get_play_speeds(),
+                                      t_resource.protocol_info.get_dlna_flags());
+                        } else {
+                            union_protocol_string.set(hash_key,
+                                                      new CMSProtocolInfo
+                                                      (hash_key,
+                                                       t_resource.protocol_info.get_dlna_operation(),
+                                                       t_resource.protocol_info.get_play_speeds(),
+                                                       t_resource.protocol_info.get_dlna_flags()));
+                        }
+                    }
+                }
+            }
+        }
+
+        // Update the variable to publish the source protocol info
+        publish_protocol_info (root_device);
+    }
+
+    private void publish_protocol_info (Rygel.RootDevice root_device) {
+        var new_protocol_string = new StringBuilder();
+        int index = 0;
+        int protocolinfo_length = (this.union_protocol_string != null) ? this.union_protocol_string.size : 0;
+
+        foreach (var str_protocol_info in this.union_protocol_string.entries) {
+            string temp_str = str_protocol_info.value.to_string();
+            message ("ProtocolInfo String added : %s ",temp_str);
+            new_protocol_string.append (temp_str);
+            if (++index < protocolinfo_length)
+                new_protocol_string.append (",");
+        }
+
+        debug ("New SourceProtocolinfo : %s",new_protocol_string.str);
+        // Find the ConnectionManager Service and update SourceProtocolInfo variable
+        foreach (var service in root_device.services) {
+            if (service.get_type().is_a (typeof (Rygel.ConnectionManager))) {
+                var connection_manager = (Rygel.ConnectionManager) service;
+                connection_manager.set_source_protocol_info(new_protocol_string.str);
+            }
+        }
+    }
+
+}
diff --git a/src/librygel-server/rygel-content-directory.vala b/src/librygel-server/rygel-content-directory.vala
index 1812a96..0fc73c7 100644
--- a/src/librygel-server/rygel-content-directory.vala
+++ b/src/librygel-server/rygel-content-directory.vala
@@ -23,6 +23,14 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Parthiban Balasubramanian <P.Balasubramanian-contractor@cablelabs.com>
+ */
+
 using GUPnP;
 using Gee;
 
@@ -54,7 +62,7 @@ internal errordomain Rygel.ContentDirectoryError {
  * plugins will provide a child of this class. The inheriting classes should
  * override create_root_container method.
  */
-internal class Rygel.ContentDirectory: Service {
+public class Rygel.ContentDirectory: Service {
     public const string UPNP_ID = "urn:upnp-org:serviceId:ContentDirectory";
     public const string UPNP_TYPE =
                     "urn:schemas-upnp-org:service:ContentDirectory:3";
@@ -120,6 +128,16 @@ internal class Rygel.ContentDirectory: Service {
 
         this.search_caps = RelationalExpression.CAPS;
 
+        bool allow_upload = false;
+        try {
+            var config = MetaConfig.get_default ();
+            allow_upload = config.get_allow_upload ();
+        } catch (GLib.Error error) { }
+
+        if (allow_upload) {
+            this.search_caps += ",upnp:createClass";
+        }
+
         if (PluginCapabilities.TRACK_CHANGES in plugin.capabilities) {
             this.search_caps += ",upnp:objectUpdateID,upnp:containerUpdateID";
         }
@@ -452,6 +470,23 @@ internal class Rygel.ContentDirectory: Service {
         return false;
     }
 
+    private async void get_all_items () {
+        uint total_matches;
+        var s_container = (this.root_container as SearchableContainer);
+        MediaObjects media_objects = null;
+
+        try {
+            debug ("Updating SourceProtocolInfo using CDS list.");
+            media_objects = yield s_container.search (null , 0 , -1, out total_matches,
+                                             s_container.sort_criteria,
+                                             this.cancellable);
+            ConnectionManagerProtocolInfo cms_info = ConnectionManagerProtocolInfo.get_default();
+            cms_info.update_source_protocol_info ((Rygel.RootDevice)this.root_device, media_objects);
+        } catch (Error err) {
+            warning ("Updating SourceProtocolInfo using CDS list failed.");
+        }
+    }
+
     private void handle_last_change (MediaContainer updated_container,
                                      MediaObject object,
                                      ObjectEventType event_type,
@@ -541,6 +576,17 @@ internal class Rygel.ContentDirectory: Service {
                                      object);
 
         this.ensure_timeout ();
+
+        if ((event_type == ObjectEventType.ADDED ||
+            event_type == ObjectEventType.DELETED ||
+            event_type == ObjectEventType.MODIFIED) &&
+            object is MediaItem) {
+            debug ("Forward call to update SourceProtocolInfo");
+            if (this.root_container is SearchableContainer) {
+                 ("Calling searchablecontainer");
+                 this.get_all_items.begin ();
+             }
+        }
     }
 
     private void on_sub_tree_updates_finished (MediaContainer root_container,
diff --git a/src/librygel-server/rygel-data-sink.vala b/src/librygel-server/rygel-data-sink.vala
index 9ff7d51..697beb3 100644
--- a/src/librygel-server/rygel-data-sink.vala
+++ b/src/librygel-server/rygel-data-sink.vala
@@ -20,6 +20,14 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Craig Pratt <craig@ecaspia.com>
+ */
+
 using Soup;
 
 /**
@@ -40,7 +48,7 @@ internal class Rygel.DataSink : Object {
     public DataSink (DataSource source,
                      Server     server,
                      Message    message,
-                     HTTPSeek?  offsets) {
+                     HTTPSeekRequest?  offsets) {
         this.source = source;
         this.server = server;
         this.message = message;
@@ -49,8 +57,8 @@ internal class Rygel.DataSink : Object {
         this.bytes_sent = 0;
         this.max_bytes = int64.MAX;
         if (offsets != null &&
-            offsets is HTTPByteSeek) {
-            this.max_bytes = offsets.length;
+            offsets is HTTPByteSeekRequest) {
+            this.max_bytes = (offsets as HTTPByteSeekRequest).range_length;
         }
 
         this.source.data_available.connect (this.on_data_available);
diff --git a/src/librygel-server/rygel-data-source.vala b/src/librygel-server/rygel-data-source.vala
index 437f550..ffa83df 100644
--- a/src/librygel-server/rygel-data-source.vala
+++ b/src/librygel-server/rygel-data-source.vala
@@ -20,9 +20,18 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Craig Pratt <craig@ecaspia.com>
+ */
+
 public errordomain Rygel.DataSourceError {
     GENERAL,
-    SEEK_FAILED
+    SEEK_FAILED,
+    PLAYSPEED_FAILED
 }
 
 /**
@@ -36,7 +45,8 @@ public errordomain Rygel.DataSourceError {
  * to Rygel which adds them to the response it sends to the original HTTP
  * request received from the client.
  *
- * The data source is responsible for providing the streamable byte-stream
+ * The data source is responsible for providing response header information
+ * describing the content being produced and a streamable byte-stream
  * via its data_available signal. End-of-stream is signalled by the 
  * done signal, while errors are signalled by the error signal.
  *
@@ -58,14 +68,31 @@ public errordomain Rygel.DataSourceError {
  */
 public interface Rygel.DataSource : GLib.Object {
     /**
-     * Start producing the data.
+     * Preroll the data with the given seek and playspeed.
      *
-     * @param offsets optional limits of the stream for partial streaming
-     * @throws Error if anything goes wrong while starting the stream. Throws
-     * DataSourceError.SEEK_FAILED if a seek method is not supported or the
-     * range is not fulfillable.
+     * @param seek    optional seek/range specifier
+     * @param playspeed optional playback rate specifier
+     *
+     * @return List of HTTPResponseElements appropriate for the content request and
+     *         optional seek/playspeed (e.g. Content-Range, TimeSeekRange.dlna.org,
+     *         etc) or null/empty list if none are appropriate. Note: the list will
+     *         be processed in-order by the caller.
+     * 
+     * @throws Error if anything goes wrong while prerolling the stream.
+     *         Throws DataSourceError.SEEK_FAILED if a seek method is not supported or the
+     *         range is not fulfillable.
+     *         Throws PLAYSPEED_FAILED if the rate is not supported or fulfillable.
+     */
+    public abstract Gee.List<HTTPResponseElement> ? preroll ( HTTPSeekRequest? seek,
+                                                              DLNAPlaySpeedRequest? playspeed)
+       throws Error;
+    
+    /**
+     * Start producing the data.
+     * 
+     * @throws Error if anything goes wrong while starting the stream. 
      */
-    public abstract void start (HTTPSeek? offsets) throws Error;
+    public abstract void start () throws Error;
 
     /**
      * Temporarily stop data generation.
diff --git a/src/librygel-server/rygel-dlna-playspeed.vala b/src/librygel-server/rygel-dlna-playspeed.vala
new file mode 100644
index 0000000..6bcd9e2
--- /dev/null
+++ b/src/librygel-server/rygel-dlna-playspeed.vala
@@ -0,0 +1,231 @@
+/* 
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 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 APPLE 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.
+ *
+ * Author: Craig Pratt <craig@ecaspia.com>
+ *
+ * This file is part of Rygel.
+ */
+
+public errordomain Rygel.DLNAPlaySpeedError {
+    INVALID_SPEED_FORMAT,
+    SPEED_NOT_PRESENT
+}
+
+public static const string PLAYSPEED_HEADER = "PlaySpeed.dlna.org";
+public static const string FRAMERATE_HEADER = "FrameRateInTrickMode.dlna.org";
+
+/**
+ * This class represents a DLNA PlaySpeed request (PlaySpeed.dlna.org)
+ *
+ */
+public class Rygel.DLNAPlaySpeedRequest : GLib.Object {
+    public DLNAPlaySpeed speed { get; private set; }
+    
+    internal static bool requested (HTTPGet request) {
+        return request.msg.request_headers.get_one (PLAYSPEED_HEADER) != null;
+    }
+    
+    public DLNAPlaySpeedRequest (int numerator, uint denominator) {
+        base();
+        this.speed = new DLNAPlaySpeed(numerator, denominator);
+    }
+
+    public DLNAPlaySpeedRequest.from_string (string speed) throws DLNAPlaySpeedError {
+        base();
+        this.speed = new DLNAPlaySpeed.from_string(speed);
+    }
+
+    internal DLNAPlaySpeedRequest.from_request (Rygel.HTTPGet request) throws DLNAPlaySpeedError {
+        base();
+        // Format: PlaySpeed.dlna.org: speed=<rate>
+        string speed_string = request.msg.request_headers.get_one (PLAYSPEED_HEADER);
+
+        if (speed_string == null) {
+            throw new DLNAPlaySpeedError.SPEED_NOT_PRESENT("Could not find header " + PLAYSPEED_HEADER);
+        }
+        
+        var elements = speed_string.split("=");
+
+        if ((elements.length != 2) || (elements[0] != "speed")) {
+            throw new DLNAPlaySpeedError.INVALID_SPEED_FORMAT( "ill-formed value for "
+                                                               + PLAYSPEED_HEADER + ": "
+                                                               + speed_string );
+        }
+
+        speed = new DLNAPlaySpeed.from_string(elements[1]);
+
+        // Validate if playspeed is listed in the protocolInfo
+        if (request.handler is HTTPMediaResourceHandler) {
+            MediaResource resource = (request.handler as HTTPMediaResourceHandler)
+                                              .media_resource;
+            string[] speeds = resource.protocol_info.get_play_speeds();
+            bool found_speed = false;
+            foreach (var speed in speeds) {
+                var cur_speed = new DLNAPlaySpeedRequest.from_string (speed);
+                if (this.equals(cur_speed)) {
+                    found_speed = true;
+                    break;
+                }
+            }
+
+            if (!found_speed) {
+                throw new DLNAPlaySpeedError.SPEED_NOT_PRESENT("Unknown playspeed requested.");
+            }
+        }
+    }
+
+    public bool equals(DLNAPlaySpeedRequest that) {
+        if (that == null) return false;
+
+        return (this.speed.equals(that.speed));
+    }
+}
+
+/**
+ * This class represents a DLNA PlaySpeed response (PlaySpeed.dlna.org)
+ */
+public class Rygel.DLNAPlaySpeedResponse : Rygel.HTTPResponseElement {
+    DLNAPlaySpeed speed;
+    public static const int NO_FRAMERATE = -1;
+
+    /**
+     * The framerate supported for the given rate, in frames per second
+     */
+    public int framerate;
+
+    public DLNAPlaySpeedResponse (int numerator, uint denominator, int framerate) {
+        base();
+        this.speed = new DLNAPlaySpeed(numerator, denominator);
+        this.framerate = NO_FRAMERATE;
+    }
+
+    public DLNAPlaySpeedResponse.from_speed (DLNAPlaySpeed speed, int framerate)
+       throws DLNAPlaySpeedError {
+        base();
+        this.speed = speed;
+        this.framerate = framerate;
+    }
+
+    public DLNAPlaySpeedResponse.from_string (string speed, int framerate)
+       throws DLNAPlaySpeedError {
+        base();
+        this.speed = new DLNAPlaySpeed.from_string(speed);
+        this.framerate = NO_FRAMERATE;
+    }
+
+    public bool equals(DLNAPlaySpeedRequest that) {
+        if (that == null) return false;
+
+        return (this.speed.equals(that.speed));
+    }
+
+    public override void add_response_headers (Rygel.HTTPRequest request) {
+        // Format: PlaySpeed.dlna.org: speed=<rate>
+        request.msg.response_headers.append (PLAYSPEED_HEADER, "speed=" + speed.to_string());
+        if (this.framerate > 0) {
+            // Format: FrameRateInTrickMode.dlna.org: rate=<framerate>
+            request.msg.response_headers.append ( FRAMERATE_HEADER,
+                                                  "rate=" + this.framerate.to_string() );
+        }
+    }
+}
+
+/**
+ * This is a container for a PlaySpeed value.
+ * 
+ * A Playspeed can be positive or negative whole numbers or fractions.
+ * e.g. "2". "1/2", "-1/4"
+ */
+public class Rygel.DLNAPlaySpeed {
+    public int numerator; // Sign of the speed will be attached to the numerator
+    public uint denominator;
+    
+    public DLNAPlaySpeed (int numerator, uint denominator) {
+        this.numerator = numerator;
+        this.denominator = denominator;
+    }
+
+    public DLNAPlaySpeed.from_string (string speed) throws DLNAPlaySpeedError {
+        parse(speed);
+    }
+
+    public bool equals(DLNAPlaySpeed that) {
+        if (that == null) return false;
+
+        return ( (this.numerator == that.numerator)
+                 && (this.denominator == that.denominator) );
+    }
+
+    public bool is_positive() {
+        return (this.numerator > 0);
+    }
+
+    public bool is_negative() {
+        return (this.numerator < 0);
+    }
+
+    public bool is_normal_rate() {
+        return (this.numerator == 1) && (this.denominator == 1);
+    }
+
+    public bool is_trick_rate() {
+        return !is_normal_rate();
+    }
+
+    public string to_string() {
+        if (this.denominator == 1) {
+            return numerator.to_string();
+        } else {
+            return this.numerator.to_string() + "/" + this.denominator.to_string();
+        }
+    }
+
+    public float to_float() {
+        return (float)numerator/denominator;
+    }
+
+    private void parse(string speed) throws DLNAPlaySpeedError {
+        if (! ("/" in speed)) {
+            this.numerator = int.parse(speed);
+            this.denominator = 1;
+        } else {
+            var elements = speed.split("/");
+            if (elements.length != 2) {
+                throw new DLNAPlaySpeedError.INVALID_SPEED_FORMAT("Missing/extra numerator/denominator");
+            }
+            this.numerator = int.parse(elements[0]);
+            this.denominator = int.parse(elements[1]);
+        }
+        // "0" isn't a valid numerator or denominator (and parse returns "0" on parse error)
+        if (this.numerator == 0) {
+            throw new DLNAPlaySpeedError.INVALID_SPEED_FORMAT("Invalid numerator in speed: " + speed);
+        }
+        if (this.denominator <= 0) {
+            throw new DLNAPlaySpeedError.INVALID_SPEED_FORMAT("Invalid denominator in speed: " + speed);
+        }
+    }
+}
+
diff --git a/src/librygel-server/rygel-dtcp-cleartext-byte-seek.vala b/src/librygel-server/rygel-dtcp-cleartext-byte-seek.vala
new file mode 100644
index 0000000..0f4c735
--- /dev/null
+++ b/src/librygel-server/rygel-dtcp-cleartext-byte-seek.vala
@@ -0,0 +1,214 @@
+/* 
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 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 APPLE 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.
+ *
+ * Author: Craig Pratt <craig@ecaspia.com>
+ *
+ * This file is part of Rygel.
+ */
+
+using GUPnP;
+
+public static const string DTCP_CLEARTEXT_RANGE_REQUEST_HEADER = "Range.dtcp.com";
+
+public class Rygel.DTCPCleartextByteSeekRequest : Rygel.HTTPSeekRequest {
+    /**
+     * The start of the cleartext range in bytes 
+     */
+    public int64 start_byte { get; private set; }
+
+    /**
+     * The end of the cleartext range in bytes (inclusive). May be HTTPSeekRequest.UNSPECIFIED
+     */
+    public int64 end_byte { get; private set; }
+
+    /**
+     * The length of the cleartext range in bytes. May be HTTPSeekRequest.UNSPECIFIED
+     */
+    public int64 range_length { get; private set; }
+
+    /**
+     * The length of the cleartext resource in bytes. May be HTTPSeekRequest.UNSPECIFIED
+     */
+    public int64 total_size { get; private set; }
+
+    
+    public DTCPCleartextByteSeekRequest (HTTPGet request) throws HTTPSeekRequestError,
+                                                                 HTTPRequestError {
+        base ();
+
+        int64 start, end, total_size;
+        
+        // It's only possible to get the cleartext size from a MediaResource
+        //  (and only if it is link protected)
+        if (request.handler is HTTPMediaResourceHandler) {
+            MediaResource resource = (request.handler as HTTPMediaResourceHandler)
+                                     .media_resource;
+            total_size = resource.cleartext_size;
+            if (total_size <= 0) {
+                // Even if it's a resource and the content is link-protected, it may have an
+                // unknown cleartext size (e.g. if it's live/in-progress content). This doesn't
+                // mean the request is invalid, it just means the total size is non-static
+                total_size = UNSPECIFIED;
+            }
+        } else {
+            total_size = UNSPECIFIED;
+        }
+
+        unowned string range = request.msg.request_headers.get_one (DTCP_CLEARTEXT_RANGE_REQUEST_HEADER);
+        
+        if (range == null) {
+            throw new HTTPSeekRequestError.INVALID_RANGE ( "%s request header not present",
+                                                           DTCP_CLEARTEXT_RANGE_REQUEST_HEADER );
+        }
+        
+        if (!range.has_prefix ("bytes")) {
+            throw new HTTPSeekRequestError.INVALID_RANGE ( "Invalid %s value (missing bytes field): '%s'",
+                                                           DTCP_CLEARTEXT_RANGE_REQUEST_HEADER,
+                                                           range );
+        }
+
+        var range_tokens = range.substring (6).split ("-", 2); // skip "bytes="
+        if (range_tokens[0].length == 0) {
+            throw new HTTPSeekRequestError.INVALID_RANGE ( "No range start specified: '%s'",
+                                                           DTCP_CLEARTEXT_RANGE_REQUEST_HEADER,
+                                                           range );
+        }
+
+        if (!int64.try_parse(range_tokens[0], out start) || (start < 0)) {
+            throw new HTTPSeekRequestError.INVALID_RANGE ( "Invalid %s range start: '%s'",
+                                                           DTCP_CLEARTEXT_RANGE_REQUEST_HEADER,
+                                                           range );
+        }
+        // valid range start specified
+
+        // Look for a range end...
+        if (range_tokens[1].length == 0) {
+            end = UNSPECIFIED;
+        } else {
+            if (!int64.try_parse(range_tokens[1], out end) || (end <= 0)) {
+                throw new HTTPSeekRequestError.INVALID_RANGE ( "Invalid %s range end: '%s'",
+                                                               DTCP_CLEARTEXT_RANGE_REQUEST_HEADER,
+                                                               range );
+            }
+            // valid end range specified
+        }
+
+        if ((end != UNSPECIFIED) && (start > end)) {
+            throw new HTTPSeekRequestError.INVALID_RANGE ( "Invalid %s range - start > end: '%s'",
+                                                           DTCP_CLEARTEXT_RANGE_REQUEST_HEADER,
+                                                           range );
+        }
+
+        if ((total_size != UNSPECIFIED) && (start > total_size-1)) {
+            throw new HTTPSeekRequestError.OUT_OF_RANGE ( "Invalid %s range - start > length: '%s'",
+                                                           DTCP_CLEARTEXT_RANGE_REQUEST_HEADER,
+                                                           range );
+        }
+
+        if ((total_size != UNSPECIFIED) && (end > total_size-1)) {
+            // It's not clear from the DLNA link protection spec if the range end can be beyond
+            //  the total length. We'll assume RFC 2616 14.35.1 semantics. But note that having
+            //  an end with an unspecified size will be normal for live/in-progress content 
+            end = total_size-1;
+        }
+
+        this.start_byte = start;
+        this.end_byte = end;
+        this.range_length = (end == UNSPECIFIED) ? UNSPECIFIED
+                                                : end-start+1; // +1, since range is inclusive
+        this.total_size = total_size;
+    }
+
+    public static bool supported (HTTPGet request) {
+        return (request.handler is HTTPMediaResourceHandler)
+               && (request.handler as HTTPMediaResourceHandler)
+                  .media_resource.is_cleartext_range_support_enabled();
+    }
+
+    public static bool requested (HTTPGet request) {
+        return (request.msg.request_headers.get_one (DTCP_CLEARTEXT_RANGE_REQUEST_HEADER) != null);
+    }
+}
+
+public static const string DTCP_CLEARTEXT_RANGE_RESPONSE_HEADER = "Content-Range.dtcp.com";
+
+public class Rygel.DTCPCleartextByteSeekResponse : Rygel.HTTPResponseElement {
+    /**
+     * The start of the response range in bytes 
+     */
+    public int64 start_byte { get; private set; }
+
+    /**
+     * The end of the range in bytes (inclusive)
+     */
+    public int64 end_byte { get; private set; }
+
+    /**
+     * The length of the range in bytes
+     */
+    public int64 range_length { get; private set; }
+
+    /**
+     * The length of the resource in bytes. May be HTTPSeekRequest.UNSPECIFIED
+     */
+    public int64 total_size { get; private set; }
+
+    /**
+     * The encrypted length of the response
+     */
+    public int64 encrypted_length { get; public set;}
+    
+    public DTCPCleartextByteSeekResponse(int64 start_byte, int64 end_byte, int64 total_size) {
+        this.start_byte = start_byte;
+        this.end_byte = end_byte;
+        this.range_length = end_byte - start_byte + 1; // +1, since range is inclusive
+        this.total_size = total_size;
+        this.encrypted_length = UNSPECIFIED;
+    }
+
+    public DTCPCleartextByteSeekResponse.from_request(DTCPCleartextByteSeekRequest request) {
+        this.start_byte = request.start_byte;
+        this.end_byte = request.end_byte;
+        this.range_length = request.range_length;
+        this.total_size = request.total_size;
+        this.encrypted_length = UNSPECIFIED;
+    }
+
+    public override void add_response_headers (Rygel.HTTPRequest request) {
+        // Content-Range.dtcp.com: bytes START_BYTE-END_BYTE/TOTAL_LENGTH (or "*")
+        if (this.start_byte != UNSPECIFIED) {
+            string response = "bytes " + this.start_byte.to_string()
+                              + "-" + this.end_byte.to_string() + "/"
+                              + ( (this.total_size == UNSPECIFIED) ? "*"
+                                  : this.total_size.to_string() );
+
+            request.msg.response_headers.append (DTCP_CLEARTEXT_RANGE_RESPONSE_HEADER, response);
+        }
+        if (this.encrypted_length != UNSPECIFIED) {
+            request.msg.response_headers.set_content_length (this.encrypted_length);
+        }
+    }
+}
diff --git a/src/librygel-server/rygel-http-byte-seek.vala b/src/librygel-server/rygel-http-byte-seek.vala
index 590a1ae..5d7a2dc 100644
--- a/src/librygel-server/rygel-http-byte-seek.vala
+++ b/src/librygel-server/rygel-http-byte-seek.vala
@@ -22,45 +22,104 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Craig Pratt <craig@ecaspia.com>
+ *
+ * This file is part of Rygel.
+ */
+
+using GUPnP;
 
-internal class Rygel.HTTPByteSeek : Rygel.HTTPSeek {
-    public HTTPByteSeek (HTTPGet request) throws HTTPSeekError {
+public class Rygel.HTTPByteSeekRequest : Rygel.HTTPSeekRequest {
+    /**
+     * The start of the range in bytes 
+     */
+    public int64 start_byte { get; set; }
+
+    /**
+     * The end of the range in bytes (inclusive)
+     */
+    public int64 end_byte { get; set; }
+
+    /**
+     * The length of the range in bytes
+     */
+    public int64 range_length { get; private set; }
+
+    /**
+     * The length of the resource in bytes
+     */
+    public int64 total_size { get; set; }
+
+    
+    public HTTPByteSeekRequest (HTTPGet request) throws HTTPSeekRequestError,
+                                                 HTTPRequestError {
+        base ();
         Soup.Range[] ranges;
-        int64 start = 0, total_length;
+        int64 start, stop, total_size;
         unowned string range = request.msg.request_headers.get_one ("Range");
+        string range_header_str = null;
 
         if (request.thumbnail != null) {
-            total_length = request.thumbnail.size;
+            total_size = request.thumbnail.size;
         } else if (request.subtitle != null) {
-            total_length = request.subtitle.size;
+            total_size = request.subtitle.size;
+        } else if (request.handler is HTTPMediaResourceHandler) {
+            MediaResource resource = (request.handler as HTTPMediaResourceHandler)
+                                     .media_resource;
+            total_size = resource.size;
         } else {
-            total_length = (request.object as MediaItem).size;
+            total_size = (request.object as MediaItem).size;
         }
-        var stop = total_length - 1;
+        // TODO: Deal with resources that support Range but don't have fixed/known sizes
 
-        if (range != null) {
-            if (request.msg.request_headers.get_ranges (total_length,
+        // Check if Range is present in the header
+        if (range == null) {
+            start = 0;
+            stop = total_size -1;
+        } else {
+            range_header_str = range;
+            if (request.msg.request_headers.get_ranges (total_size,
                                                         out ranges)) {
                 // TODO: Somehow deal with multipart/byterange properly
+                //       (not legal in DLNA per 7.5.4.3.2.22.3)
                 start = ranges[0].start;
                 stop = ranges[0].end;
+                // TODO: For live/in-progress sources, we need to differentiate between "x-" and
+                //       "x-y" (and cases where y>total_size) (see DLNA 7.5.4.3.2.19.2)
+                //       We can't tell the difference with get_ranges()...
             } else {
-                // Range header was present but invalid
-                throw new HTTPSeekError.INVALID_RANGE (_("Invalid Range '%s'"),
-                                                       range);
+                throw new HTTPSeekRequestError.INVALID_RANGE (_("Invalid Range '%s'"),
+                                                              range_header_str);
             }
+        }
 
-            if (start > stop) {
-                throw new HTTPSeekError.INVALID_RANGE (_("Invalid Range '%s'"),
-                                                       range);
-            }
+        if (start > total_size-1) {
+            throw new HTTPSeekRequestError.OUT_OF_RANGE (_("Invalid Range '%s'"),
+                                                         range_header_str);
+        }
+        
+        if (stop < start) {
+            throw new HTTPSeekRequestError.INVALID_RANGE (_("Invalid Range '%s'"),
+                                                          range_header_str);
         }
 
-        base (request.msg, start, stop, 1, total_length);
-        this.seek_type = HTTPSeekType.BYTE;
+        if ((total_size > 0) && (stop > total_size-1)) {
+            // Per RFC 2616, the range end can be beyond the total length. And Soup doesn't clamp...
+            stop = total_size-1;
+        }
+            
+        this.start_byte = start;
+        this.end_byte = stop;
+        this.range_length = stop-start+1; // +1, since range is inclusive
+        this.total_size = total_size;
     }
 
-    public static bool needed (HTTPGet request) {
+    public static bool supported (HTTPGet request) {
         bool force_seek = false;
 
         try {
@@ -68,31 +127,63 @@ internal class Rygel.HTTPByteSeek : Rygel.HTTPSeek {
             force_seek = hack.force_seek ();
         } catch (Error error) { }
 
-        return force_seek || (!(request.object is MediaContainer) && ((request.object as MediaItem).size > 0 &&
-                request.handler is HTTPIdentityHandler) ||
-               (request.thumbnail != null &&
-                request.thumbnail.size > 0) ||
-               (request.subtitle != null && request.subtitle.size > 0));
+        bool resource_with_byte_seek
+                = (request.handler is HTTPMediaResourceHandler)
+                  && (request.handler as HTTPMediaResourceHandler)
+                     .media_resource.supports_arbitrary_byte_seek();
+
+        return force_seek
+               || (!(request.object is MediaContainer) && (request.object as MediaItem).size > 0)
+               || resource_with_byte_seek
+               || (request.thumbnail != null && request.thumbnail.size > 0)
+               || (request.subtitle != null && request.subtitle.size > 0);
     }
 
     public static bool requested (HTTPGet request) {
-        return request.msg.request_headers.get_one ("Range") != null;
+        return (request.msg.request_headers.get_one ("Range") != null);
     }
+}
 
-    public override void add_response_headers () {
-        // Content-Range: bytes START_BYTE-STOP_BYTE/TOTAL_LENGTH
-        var range = "bytes ";
-        unowned Soup.MessageHeaders headers = this.msg.response_headers;
+public class Rygel.HTTPByteSeekResponse : Rygel.HTTPResponseElement {
+    /**
+     * The start of the range in bytes 
+     */
+    public int64 start_byte { get; set; }
 
-        if (this.msg.request_headers.get_one ("Range") != null) {
-            headers.append ("Accept-Ranges", "bytes");
+    /**
+     * The end of the range in bytes (inclusive)
+     */
+    public int64 end_byte { get; set; }
 
-            range += this.start.to_string () + "-" +
-                     this.stop.to_string () + "/" +
-                     this.total_length.to_string ();
-            headers.append ("Content-Range", range);
-        }
+    /**
+     * The length of the range in bytes
+     */
+    public int64 range_length { get; private set; }
+
+    /**
+     * The length of the resource in bytes
+     */
+    public int64 total_size { get; set; }
+
+    public HTTPByteSeekResponse(int64 start_byte, int64 end_byte, int64 total_size) {
+        this.start_byte = start_byte;
+        this.end_byte = end_byte;
+        this.range_length = end_byte-start_byte+1; // +1, since range is inclusive
+        this.total_size = total_size;
+    }
+
+    public HTTPByteSeekResponse.from_request(HTTPByteSeekRequest request) {
+        this.start_byte = request.start_byte;
+        this.end_byte = request.end_byte;
+        this.range_length = request.range_length;
+        this.total_size = request.total_size;
+    }
 
-        headers.set_content_length (this.length);
+    public override void add_response_headers (Rygel.HTTPRequest request) {
+        // Content-Range: bytes START_BYTE-END_BYTE/TOTAL_LENGTH (or "*")
+        request.msg.response_headers.set_content_range ( this.start_byte, this.end_byte,
+                                                         this.total_size );
+        request.msg.response_headers.append ("Accept-Ranges", "bytes");
+        request.msg.response_headers.set_content_length (range_length);
     }
 }
diff --git a/src/librygel-server/rygel-http-get-handler.vala b/src/librygel-server/rygel-http-get-handler.vala
index df0603f..e098bb8 100644
--- a/src/librygel-server/rygel-http-get-handler.vala
+++ b/src/librygel-server/rygel-http-get-handler.vala
@@ -22,12 +22,20 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Craig Pratt <craig@ecaspia.com>
+ */
+
 using GUPnP;
 
 /**
  * HTTP GET request handler interface.
  */
-internal abstract class Rygel.HTTPGetHandler: GLib.Object {
+public abstract class Rygel.HTTPGetHandler: GLib.Object {
     private const string TRANSFER_MODE_HEADER = "transferMode.dlna.org";
 
     public Cancellable cancellable { get; set; }
@@ -70,17 +78,19 @@ internal abstract class Rygel.HTTPGetHandler: GLib.Object {
                                          -1,
                                          0, // FIXME: offer first subtitle only?
                                          null,
+                                         null,
                                          null);
 
                 request.msg.response_headers.append ("CaptionInfo.sec",
                                                      caption_uri);
         }
-
-        request.msg.response_headers.append ("Connection", "close");
     }
 
     public virtual bool knows_size (HTTPGet request) {
-        return false;
+        if ((request.object as MediaItem).size != -1)
+            return true;
+        else
+            return false;
     }
 
     // Create an HTTPResponse object that will render the body.
diff --git a/src/librygel-server/rygel-http-get.vala b/src/librygel-server/rygel-http-get.vala
index 95be210..aa0c1bc 100644
--- a/src/librygel-server/rygel-http-get.vala
+++ b/src/librygel-server/rygel-http-get.vala
--- a/src/librygel-server/rygel-http-get.vala
+++ b/src/librygel-server/rygel-http-get.vala
@@ -25,22 +25,33 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Craig Pratt <craig@ecaspia.com>
+ * Author: Parthiban Balasubramanian <P.Balasubramanian-contractor@cablelabs.com>
+ */
+
 /**
  * Responsible for handling HTTP GET & HEAD client requests.
  */
-internal class Rygel.HTTPGet : HTTPRequest {
+public class Rygel.HTTPGet : HTTPRequest {
     private const string TRANSFER_MODE_HEADER = "transferMode.dlna.org";
+    private const string SERVER_NAME = "CVP2-RI-DMS";
 
     public Thumbnail thumbnail;
     public Subtitle subtitle;
-    public HTTPSeek seek;
+    public HTTPSeekRequest seek;
+    public DLNAPlaySpeedRequest speed_request;
 
     private int thumbnail_index;
     private int subtitle_index;
 
     public HTTPGetHandler handler;
 
-    public HTTPGet (HTTPServer   http_server,
+    internal HTTPGet (HTTPServer   http_server,
                     Soup.Server  server,
                     Soup.Message msg) {
         base (http_server, server, msg);
@@ -66,6 +76,12 @@ internal class Rygel.HTTPGet : HTTPRequest {
                                                      this.cancellable);
         }
 
+        if (uri.media_resource_name != null) {
+            this.handler = new HTTPMediaResourceHandler (this.object as MediaItem,
+                                                         uri.media_resource_name,
+                                                         this.cancellable);
+        }
+
         if (uri.playlist_format != null &&
             HTTPPlaylistHandler.is_supported (uri.playlist_format)) {
             this.handler = new HTTPPlaylistHandler (uri.playlist_format,
@@ -135,28 +151,89 @@ internal class Rygel.HTTPGet : HTTPRequest {
     }
 
     private async void handle_item_request () throws Error {
-        var need_time_seek = HTTPTimeSeek.needed (this);
-        var requested_time_seek = HTTPTimeSeek.requested (this);
-        var need_byte_seek = HTTPByteSeek.needed (this);
-        var requested_byte_seek = HTTPByteSeek.requested (this);
+        var supports_time_seek = HTTPTimeSeekRequest.supported (this);
+        var requested_time_seek = HTTPTimeSeekRequest.requested (this);
+        var supports_byte_seek = HTTPByteSeekRequest.supported (this);
+        var requested_byte_seek = HTTPByteSeekRequest.requested (this);
+        var supports_cleartext_seek = DTCPCleartextByteSeekRequest.supported (this);
+        var requested_cleartext_seek = DTCPCleartextByteSeekRequest.requested (this);
+
+        // Order is significant here when the request has more than one seek header
+        if (requested_cleartext_seek) {
+            if (!supports_cleartext_seek) {
+                throw new HTTPRequestError.UNACCEPTABLE ( "Cleartext seek not supported for "
+                                                          + this.uri.to_string() );
+            }
+        } else if (requested_byte_seek) {
+            if (!supports_byte_seek) {
+                throw new HTTPRequestError.UNACCEPTABLE ( "Byte seek not supported for "
+                                                          + this.uri.to_string() );
+            }
+        } else if (requested_time_seek) {
+            if (!supports_time_seek) {
+                throw new HTTPRequestError.UNACCEPTABLE ( "Time seek not supported for "
+                                                          + this.uri.to_string() );
+            }
+        }
 
-        if ((requested_time_seek && !need_time_seek) ||
-            (requested_byte_seek && !need_byte_seek)) {
-            throw new HTTPRequestError.UNACCEPTABLE ("Invalid seek request");
+        // Check for DLNA PlaySpeed request only if Range or Range.dtcp.com is not
+        // in the request. DLNA 7.5.4.3.3.19.2, DLNA Link Protection : 7.6.4.4.2.12
+        // (is 7.5.4.3.3.19.2 compatible with the use case in 7.5.4.3.2.24.5?)
+        // Note: We need to check the speed since direction factors into validating
+        //       the time-seek request
+        try {
+            if ( !(requested_byte_seek || requested_cleartext_seek)
+                 && DLNAPlaySpeedRequest.requested(this) ) {
+                this.speed_request = new DLNAPlaySpeedRequest.from_request(this);
+                debug("Processing playspeed %s", speed_request.speed.to_string());
+            } else {
+                this.speed_request = null;
+            }
+        } catch (DLNAPlaySpeedError error) {
+            this.server.unpause_message (this.msg);
+            if (error is DLNAPlaySpeedError.INVALID_SPEED_FORMAT) {
+                // TODO: log something?
+                this.end (Soup.Status.BAD_REQUEST);
+                // Per DLNA 7.5.4.3.3.16.3
+            } else if (error is DLNAPlaySpeedError.SPEED_NOT_PRESENT) {
+                // TODO: log something?
+                this.end (Soup.Status.NOT_ACCEPTABLE);
+                 // Per DLNA 7.5.4.3.3.16.5
+            } else {
+                throw error;
+            }
+            return;
         }
 
         try {
-            if (need_time_seek && requested_time_seek) {
-                this.seek = new HTTPTimeSeek (this);
-            } else if (need_byte_seek && requested_byte_seek) {
-                this.seek = new HTTPByteSeek (this);
+            // Order is intentional here
+            if (supports_cleartext_seek && requested_cleartext_seek) {
+                var cleartext_seek = new DTCPCleartextByteSeekRequest (this);
+                debug ("Processing DTCP cleartext byte range request (bytes %lld to %lld)",
+                         cleartext_seek.start_byte, cleartext_seek.end_byte);
+                this.seek = cleartext_seek;
+            } else if (supports_byte_seek && requested_byte_seek) {
+                var byte_seek = new HTTPByteSeekRequest (this);
+                debug ("Processing byte range request (bytes %lld to %lld)",
+                       byte_seek.start_byte, byte_seek.end_byte);
+                this.seek = byte_seek;
+            } else if (supports_time_seek && requested_time_seek) {
+                // Assert: speed_request has been checked/processed
+                var time_seek = new HTTPTimeSeekRequest (this, ((this.speed_request == null) ? null
+                                                                : this.speed_request.speed) );
+                debug ("Processing time seek request (time %lldns to %lldns)",
+                       time_seek.start_time, time_seek.end_time);
+                this.seek = time_seek;
+            } else {
+                this.seek = null;
             }
-        } catch (HTTPSeekError error) {
+        } catch (HTTPSeekRequestError error) {
+            warning("Caught HTTPSeekRequestError: " + error.message);
             this.server.unpause_message (this.msg);
 
-            if (error is HTTPSeekError.INVALID_RANGE) {
+            if (error is HTTPSeekRequestError.INVALID_RANGE) {
                 this.end (Soup.Status.BAD_REQUEST);
-            } else if (error is HTTPSeekError.OUT_OF_RANGE) {
+            } else if (error is HTTPSeekRequestError.OUT_OF_RANGE) {
                 this.end (Soup.Status.REQUESTED_RANGE_NOT_SATISFIABLE);
             } else {
                 throw error;
@@ -166,24 +243,88 @@ internal class Rygel.HTTPGet : HTTPRequest {
         }
 
         // Add headers
+        // TODO: Should we have this after preroll()?
+        // TODO: How much of the logic in this method should be in the base HTTPGetHandler?
         this.handler.add_response_headers (this);
 
-        // Add general headers
-        if (this.msg.request_headers.get_one ("Range") != null) {
-            this.msg.set_status (Soup.Status.PARTIAL_CONTENT);
-        } else {
-            this.msg.set_status (Soup.Status.OK);
+        this.msg.response_headers.append ("Server",SERVER_NAME);
+
+        HTTPResponse response = this.handler.render_body (this);
+
+        // Have the response process the seek/speed request
+        var responses = response.preroll();
+
+        // Incorporate the prerolled responses
+        if (responses != null) {
+            foreach (var response_elem in responses) {
+                response_elem.add_response_headers(this);
+            }
+        }
+        
+        //
+        // All response header generation below here depends upon the seek range/speed response
+        //  fields being set (preroll() will have touched our seek/speed response-related
+        //  parameters)
+
+        // Determine the size value
+        int64 response_size;
+        {
+            if (this.seek != null) {
+                // The response element for seeks is responsible for setting the appropriate
+                //  length if/when appropriate. So it should already be set (if it can be set)
+                response_size = this.msg.response_headers.get_content_length();
+            } else if (this.handler is HTTPMediaResourceHandler) {
+                // If a seek isn't included, we'll be returning the entire resource binary
+                response_size = (this.handler as HTTPMediaResourceHandler)
+                                .media_resource.size;
+                this.msg.response_headers.set_content_length (response_size);
+            } else if (this.handler.knows_size (this)) {
+                // Still supporting the concept of MediaItem size (for now)
+                response_size = (this.object as MediaItem).size;
+                this.msg.response_headers.set_content_length (response_size);
+            } else {
+                response_size = 0;
+            }
+            // size will factor into other logic below...
+        }
+
+        // Determine the transfer mode encoding
+        {
+            Soup.Encoding response_body_encoding;
+            // See DLNA 7.5.4.3.2.15 for requirements
+            if ( (this.speed_request != null) && (this.msg.get_http_version() != Soup.HTTPVersion.@1_0) ) {
+                // We'll want the option to insert PlaySpeed position information
+                //  whether or not we know the length (see DLNA 7.5.4.3.3.17)
+                response_body_encoding = Soup.Encoding.CHUNKED;
+            } else if (response_size > 0) {
+                // TODO: Incorporate ChunkEncodingMode.dlna.org request into this block
+                response_body_encoding = Soup.Encoding.CONTENT_LENGTH;
+            } else { // Response size is 0
+                if (this.msg.get_http_version() == Soup.HTTPVersion.@1_0) {
+                    // Can't sent the length and can't send chunked (in HTTP 1.0)...
+                    // this.msg.response_headers.append ("Connection", "close");
+                    response_body_encoding = Soup.Encoding.EOF;
+                } else {
+                    response_body_encoding = Soup.Encoding.CHUNKED;
+                }
+            }
+            this.msg.response_headers.set_encoding (response_body_encoding);
         }
 
-        if (this.handler.knows_size (this)) {
-            this.msg.response_headers.set_encoding (Soup.Encoding.CONTENT_LENGTH);
-        } else {
-            this.msg.response_headers.set_encoding (Soup.Encoding.EOF);
+        // Determine the status code
+        {
+            int response_code;
+            if (this.msg.response_headers.get_one ("Content-Range") != null) {
+                response_code = Soup.Status.PARTIAL_CONTENT;
+            } else {
+                response_code = Soup.Status.OK;
+            }
+            this.msg.set_status (response_code);
         }
 
         debug ("Following HTTP headers appended to response:");
         this.msg.response_headers.foreach ((name, value) => {
-            debug ("%s : %s", name, value);
+            debug ("    %s : %s", name, value);
         });
 
         if (this.msg.method == "HEAD") {
@@ -193,8 +334,6 @@ internal class Rygel.HTTPGet : HTTPRequest {
             return;
         }
 
-        var response = this.handler.render_body (this);
-
         yield response.run ();
 
         this.end (Soup.Status.NONE);
diff --git a/src/librygel-server/rygel-http-identity-handler.vala b/src/librygel-server/rygel-http-identity-handler.vala
index 215a6ea..4577e79 100644
--- a/src/librygel-server/rygel-http-identity-handler.vala
+++ b/src/librygel-server/rygel-http-identity-handler.vala
@@ -23,6 +23,14 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Craig Pratt <craig@ecaspia.com>
+ */
+
 using GUPnP;
 
 // An HTTP request handler that passes the item content through as is.
@@ -45,16 +53,6 @@ internal class Rygel.HTTPIdentityHandler : Rygel.HTTPGetHandler {
                                                  (request.object as MediaItem).mime_type);
         }
 
-        if (request.seek != null) {
-            request.seek.add_response_headers ();
-        } else {
-            var size = this.get_size (request);
-
-            if (size > 0) {
-                request.msg.response_headers.set_content_length (size);
-            }
-        }
-
         // Chain-up
         base.add_response_headers (request);
     }
@@ -71,7 +69,7 @@ internal class Rygel.HTTPIdentityHandler : Rygel.HTTPGetHandler {
     public override bool knows_size (HTTPGet request) {
         var size = this.get_size (request);
 
-        return (request.seek != null && request.seek is HTTPByteSeek) ||
+        return (request.seek != null && request.seek is HTTPByteSeekRequest) ||
                     size > 0;
     }
 
@@ -85,7 +83,7 @@ internal class Rygel.HTTPIdentityHandler : Rygel.HTTPGetHandler {
             return request.thumbnail.add_resource (didl_object as DIDLLiteItem,
                                                    protocol);
         } else {
-            return request.object.add_resource (didl_object, null, protocol);
+            return request.object.add_resource (didl_object, null, protocol, new MediaResource("dummy"));
         }
     }
 
@@ -94,12 +92,12 @@ internal class Rygel.HTTPIdentityHandler : Rygel.HTTPGetHandler {
         var engine = MediaEngine.get_default ();
 
         if (request.subtitle != null) {
-            src = engine.create_data_source (request.subtitle.uri);
+            src = engine.create_data_source_for_resource (request.subtitle.uri, null);
         } else if (request.thumbnail != null) {
-            src = engine.create_data_source (request.thumbnail.uri);
+            src = engine.create_data_source_for_resource (request.thumbnail.uri, null);
         } else {
-            src = (request.object as MediaItem).create_stream_source
-                                        (request.http_server.context.host_ip);
+            src = (request.object as MediaItem).create_stream_source_for_resource
+                                        (request.http_server.context.host_ip, null);
         }
 
         if (src == null) {
diff --git a/src/librygel-server/rygel-http-item-uri.vala b/src/librygel-server/rygel-http-item-uri.vala
index ab764e1..d0c0950 100644
--- a/src/librygel-server/rygel-http-item-uri.vala
+++ b/src/librygel-server/rygel-http-item-uri.vala
@@ -23,14 +23,23 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Craig Pratt <craig@ecaspia.com>
+ */
+
 using Gee;
 
-internal class Rygel.HTTPItemURI : Object {
+public class Rygel.HTTPItemURI : Object {
     public string item_id { get; set; }
     public int thumbnail_index { get; set; default = -1; }
     public int subtitle_index { get; set; default = -1; }
     public string? transcode_target { get; set; default = null; }
     public string? playlist_format { get; set; default = null; }
+    public string? media_resource_name { get; set; default = null; }
     public unowned HTTPServer http_server { get; set; }
 
     private string real_extension;
@@ -53,7 +62,8 @@ internal class Rygel.HTTPItemURI : Object {
                         int        thumbnail_index = -1,
                         int        subtitle_index = -1,
                         string?    transcode_target = null,
-                        string?    playlist_format = null) {
+                        string?    playlist_format = null,
+                        MediaResource?    media_resource = null) {
         this.item_id = object.id;
         this.thumbnail_index = thumbnail_index;
         this.subtitle_index = subtitle_index;
@@ -61,6 +71,7 @@ internal class Rygel.HTTPItemURI : Object {
         this.http_server = http_server;
         this.playlist_format = playlist_format;
         this.extension = "";
+        this.media_resource_name = null;
 
         if (!(object is MediaItem)) {
             return;
@@ -89,6 +100,9 @@ internal class Rygel.HTTPItemURI : Object {
                     this.extension = subtitles[subtitle_index].caption_type;
                 }
             }
+        } else if (media_resource != null) {
+            this.media_resource_name = media_resource.get_name();
+            this.extension = media_resource.extension;
         } else if (transcode_target != null) {
             try {
                 var tc = this.http_server.get_transcoder (transcode_target);
@@ -170,6 +184,10 @@ internal class Rygel.HTTPItemURI : Object {
                     this.playlist_format = Soup.URI.decode (parts[i + 1]);
 
                     break;
+                case "res":
+                    this.media_resource_name = Soup.URI.decode (parts[i + 1]);
+
+                    break;
                 default:
                     break;
             }
@@ -190,7 +208,10 @@ internal class Rygel.HTTPItemURI : Object {
                                          true);
         string path = "/i/" + escaped;
 
-        if (this.transcode_target != null) {
+        if (this.media_resource_name != null) {
+            escaped = Uri.escape_string (this.media_resource_name, "", true);
+            path += "/res/" + escaped;
+        } else if (this.transcode_target != null) {
             escaped = Uri.escape_string (this.transcode_target, "", true);
             path += "/tr/" + escaped;
         } else if (this.thumbnail_index >= 0) {
diff --git a/src/librygel-server/rygel-http-playlist-handler.vala b/src/librygel-server/rygel-http-playlist-handler.vala
index 6a8d5aa..5839ce9 100644
--- a/src/librygel-server/rygel-http-playlist-handler.vala
+++ b/src/librygel-server/rygel-http-playlist-handler.vala
@@ -19,6 +19,16 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
+
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Craig Pratt <craig@ecaspia.com>
+ * Author: Doug Galligan <doug@sentosatech.com>>
+ */
+
 using GUPnP;
 
 /**
@@ -44,16 +54,27 @@ internal class Rygel.PlaylistDatasource : Rygel.DataSource, Object {
 
     public signal void data_ready ();
 
-    public void start (HTTPSeek? offsets) throws Error {
-        if (offsets != null) {
+    public Gee.List<HTTPResponseElement> ? preroll ( HTTPSeekRequest? seek_request,
+                                                     DLNAPlaySpeedRequest? playspeed_request)
+       throws Error {
+        if (seek_request != null) {
             throw new DataSourceError.SEEK_FAILED
                                         (_("Seeking not supported"));
         }
 
+        if (playspeed_request != null) {
+            throw new DataSourceError.PLAYSPEED_FAILED
+                                    (_("Speed not supported"));
+        }
+
+        return null;
+    }
+
+    public void start () throws Error {
         if (this.data == null) {
             this.data_ready.connect ( () => {
                 try {
-                    this.start (offsets);
+                    this.start ();
                 } catch (Error error) { }
             });
 
@@ -165,7 +186,8 @@ internal class Rygel.HTTPPlaylistHandler : Rygel.HTTPGetHandler {
         var protocol = request.http_server.get_protocol ();
 
         try {
-            var res = request.object.add_resource (didl_object, null, protocol);
+            var res = request.object.add_resource ( didl_object, null, protocol,
+                                                    new MediaResource("dummy") );
 
             // set DLNA profile to get proper contentFeatures header
             if (this.playlist_type == SerializerType.DIDL_S) {
diff --git a/src/librygel-server/rygel-http-request.vala b/src/librygel-server/rygel-http-request.vala
index 9b77f04..f6fcc92 100644
--- a/src/librygel-server/rygel-http-request.vala
+++ b/src/librygel-server/rygel-http-request.vala
@@ -23,7 +23,15 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
-internal errordomain Rygel.HTTPRequestError {
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Craig Pratt <craig@ecaspia.com>
+ */
+
+public errordomain Rygel.HTTPRequestError {
     UNACCEPTABLE = Soup.Status.NOT_ACCEPTABLE,
     BAD_REQUEST = Soup.Status.BAD_REQUEST,
     NOT_FOUND = Soup.Status.NOT_FOUND,
@@ -33,7 +41,7 @@ internal errordomain Rygel.HTTPRequestError {
 /**
  * Base class for HTTP client requests.
  */
-internal abstract class Rygel.HTTPRequest : GLib.Object, Rygel.StateMachine {
+public abstract class Rygel.HTTPRequest : GLib.Object, Rygel.StateMachine {
     public unowned HTTPServer http_server;
     private MediaContainer root_container;
     public unowned Soup.Server server;
@@ -41,8 +49,11 @@ internal abstract class Rygel.HTTPRequest : GLib.Object, Rygel.StateMachine {
 
     public Cancellable cancellable { get; set; }
 
-    protected HTTPItemURI uri;
+    public HTTPItemURI uri;
+    // TODO: rename this to "media_resource
     public MediaObject object;
+    // TODO: add: public MediaResource media_resource;
+    // Or - to take this further - maybe only the GetHandler should have item/resource references?
 
     internal ClientHacks hack;
 
diff --git a/src/librygel-server/rygel-http-resource-handler.vala b/src/librygel-server/rygel-http-resource-handler.vala
new file mode 100644
index 0000000..2f42db7
--- /dev/null
+++ b/src/librygel-server/rygel-http-resource-handler.vala
@@ -0,0 +1,110 @@
+/* 
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 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 APPLE 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.
+ *
+ * Author: Craig Pratt <craig@ecaspia.com>
+ *
+ * This file is part of Rygel.
+ */
+
+using GUPnP;
+
+/**
+ * The HTTP handler for HTTP ContentResource requests.
+ */
+internal class Rygel.HTTPMediaResourceHandler : HTTPGetHandler {
+    private MediaItem media_item;
+    private string media_resource_name;
+    public MediaResource media_resource;
+
+    public HTTPMediaResourceHandler (MediaItem media_item,
+                                     string media_resource_name,
+                                     Cancellable? cancellable)
+       throws HTTPRequestError {
+        this.media_item = media_item;
+        this.cancellable = cancellable;
+        this.media_resource_name = media_resource_name;
+        if (media_item.media_resources != null) {
+            foreach (var resource in media_item.media_resources) {
+                if (resource.get_name() == media_resource_name) {
+                    this.media_resource = resource;
+                }
+            }
+        } else {
+            warning("MediaItem without resources (%s)", media_item.uris[0]);
+            // TODO: Determine if/how we're getting here
+        }
+        if (this.media_resource == null) {
+            throw new HTTPRequestError.NOT_FOUND ("MediaResource %s not found", media_resource_name);
+        }
+    }
+
+    public override void add_response_headers (HTTPGet request)
+                                               throws HTTPRequestError {
+        request.msg.response_headers.append ("Content-Type",
+                                             this.media_resource.protocol_info.mime_type);
+        // Determine cache control
+        if (media_resource.is_link_protection_enabled())
+        {
+            if (request.msg.get_http_version() == Soup.HTTPVersion.@1_1) {
+                request.msg.response_headers.append ("Cache-control","no-cache");
+            }
+            request.msg.response_headers.append ("Pragma","no-cache");
+        }
+
+        // Chain-up
+        base.add_response_headers (request);
+    }
+
+    public override HTTPResponse render_body (HTTPGet request)
+                                              throws HTTPRequestError {
+        try {
+            DataSource src;
+
+            src = (request.object as MediaItem).create_stream_source_for_resource
+                                        (request.http_server.context.host_ip,
+                                        this.media_resource);
+
+            if (src == null) {
+                throw new HTTPRequestError.NOT_FOUND (_("Not found"));
+            }
+
+            return new HTTPResponse (request, this, src);
+        } catch (Error err) {
+            throw new HTTPRequestError.NOT_FOUND (err.message);
+        }
+    }
+
+    protected override DIDLLiteResource add_resource
+                                        (DIDLLiteObject didl_object,
+                                         HTTPGet      request)
+                                        throws Error {
+        DIDLLiteItem didl_item = didl_object as DIDLLiteItem;
+
+        DIDLLiteResource didl_resource = didl_item.add_resource();
+        media_resource.write_didl_lite(didl_resource);
+        return didl_resource;
+    }
+}
diff --git a/src/librygel-server/rygel-http-response-element.vala b/src/librygel-server/rygel-http-response-element.vala
new file mode 100644
index 0000000..2e5a069
--- /dev/null
+++ b/src/librygel-server/rygel-http-response-element.vala
@@ -0,0 +1,44 @@
+/* 
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 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 APPLE 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.
+ *
+ * Author: Craig Pratt <craig@ecaspia.com>
+ *
+ * This file is part of Rygel.
+ */
+
+/**
+ * This abstract class represents an entity that can contribute response headers to a
+ * HTTP request.
+ */
+public abstract class Rygel.HTTPResponseElement : GLib.Object {
+    // For designating fields that are unset
+    public static const int64 UNSPECIFIED = -1;
+    
+    /**
+     * Set the type-appropriate headers on the associated HTTP Message 
+     */
+    public abstract void add_response_headers (Rygel.HTTPRequest request);
+}
diff --git a/src/librygel-server/rygel-http-response.vala b/src/librygel-server/rygel-http-response.vala
index 7cfa7bf..d6742fe 100644
--- a/src/librygel-server/rygel-http-response.vala
+++ b/src/librygel-server/rygel-http-response.vala
@@ -23,15 +23,24 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Craig Pratt <craig@ecaspia.com>
+ */
+
 using Soup;
 
-internal class Rygel.HTTPResponse : GLib.Object, Rygel.StateMachine {
+public class Rygel.HTTPResponse : GLib.Object, Rygel.StateMachine {
     public unowned Soup.Server server { get; private set; }
     public Soup.Message msg;
 
     public Cancellable cancellable { get; set; }
 
-    public HTTPSeek seek;
+    public HTTPSeekRequest seek;
+    public DLNAPlaySpeedRequest speed;
 
     private SourceFunc run_continue;
     private int _priority = -1;
@@ -69,6 +78,7 @@ internal class Rygel.HTTPResponse : GLib.Object, Rygel.StateMachine {
         this.msg = request.msg;
         this.cancellable = request_handler.cancellable;
         this.seek = request.seek;
+        this.speed = request.speed_request;
         this.src = src;
         this.sink = new DataSink (this.src, this.server, this.msg, this.seek);
         this.src.done.connect ( () => {
@@ -99,10 +109,14 @@ internal class Rygel.HTTPResponse : GLib.Object, Rygel.StateMachine {
         }
     }
 
+    public Gee.List<HTTPResponseElement> ? preroll () throws Error {
+        return this.src.preroll (seek, speed);
+    }
+
     public async void run () {
         this.run_continue = run.callback;
         try {
-            this.src.start (this.seek);
+            this.src.start ();
         } catch (Error error) {
             Idle.add (() => {
                 this.end (false, Status.NONE);
diff --git a/src/librygel-server/rygel-http-seek.vala b/src/librygel-server/rygel-http-seek.vala
index a7c66ea..2396313 100644
--- a/src/librygel-server/rygel-http-seek.vala
+++ b/src/librygel-server/rygel-http-seek.vala
@@ -23,89 +23,25 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
-public errordomain Rygel.HTTPSeekError {
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Craig Pratt <craig@ecaspia.com>
+ */
+
+public errordomain Rygel.HTTPSeekRequestError {
     INVALID_RANGE = Soup.Status.BAD_REQUEST,
     OUT_OF_RANGE = Soup.Status.REQUESTED_RANGE_NOT_SATISFIABLE,
 }
 
-public enum Rygel.HTTPSeekType {
-    BYTE,
-    TIME
-}
-
 /**
- * HTTPSeek is an abstract representation of a ranged HTTP request.
- *
- * It can be one of:
+ * HTTPSeekRequest is an abstract base for a variety of seek request types.
  *
- *  - The classic Range request (seek_type == HTTPSeekType.BYTE), with start and stop in bytes.
- *  - The DLNA-Specific "TimeSeekRange.dlna.org" request (seek_type == HTTPSeekType.TIME) with start and stop in microseconds.
  */
-public abstract class Rygel.HTTPSeek : GLib.Object {
-
-    /**
-     * Identifies whether this is a class Range request or a DLNA-specific
-     * "TimeSeekRange.dlna.org" request.
-     */
-    public HTTPSeekType seek_type { get; protected set; }
-    public Soup.Message msg { get; private set; }
-
-    /**
-     * The start of the range as a number of bytes (classic) or as microseconds 
-     * (DLNA-specific). See seek_type.
-     */
-    public int64 start { get; private set; }
-
-    /**
-     * The end of the range as a number of bytes (classic) or as microseconds 
-     * (DLNA-specific). See seek_type.
-     */
-    public int64 stop { get; private set; }
-
-    /**
-     * Either 1 byte (classic) or as 1000 G_TIME_SPAN_MILLISECOND microseconds 
-     * (DLNA-specific). See seek_type.
-     */
-    public int64 step { get; private set; }
-
-    /**
-     * The length of the range as a number of bytes (classic) or as microseconds 
-     * (DLNA-specific). See seek_type.
-     */
-    public int64 length { get; private set; }
-
-    /**
-     * The length of the media file as a number of bytes (classic) or as microseconds 
-     * (DLNA-specific). See seek_type.
-     */
-    public int64 total_length { get; private set; }
-
-    public HTTPSeek (Soup.Message msg,
-                     int64        start,
-                     int64        stop,
-                     int64        step,
-                     int64        total_length) throws HTTPSeekError {
-        this.msg = msg;
-        this.start = start;
-        this.stop = stop;
-        this.length = length;
-        this.total_length = total_length;
-
-        if (start < 0 || start >= total_length) {
-            throw new HTTPSeekError.OUT_OF_RANGE (_("Out Of Range Start '%ld'"),
-                                                  start);
-        }
-        if (stop < 0 || stop >= total_length) {
-            throw new HTTPSeekError.OUT_OF_RANGE (_("Out Of Range Stop '%ld'"),
-                                                  stop);
-        }
-
-        if (length > 0) {
-            this.stop = stop.clamp (start + 1, length - 1);
-        }
-
-        this.length = stop + step - start;
-    }
-
-    public abstract void add_response_headers ();
+public abstract class Rygel.HTTPSeekRequest : GLib.Object {
+    // For designating fields that are unset
+    public static const int64 UNSPECIFIED = -1;
+    // Note: -1 is significant in that libsoup also uses it to designate an "unknown" value
 }
diff --git a/src/librygel-server/rygel-http-server.vala b/src/librygel-server/rygel-http-server.vala
index f6aa08c..3dd9fbe 100644
--- a/src/librygel-server/rygel-http-server.vala
+++ b/src/librygel-server/rygel-http-server.vala
@@ -22,11 +22,19 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Doug Galligan <doug@sentosatech.com>>
+ * Author: Craig Pratt <craig@ecaspia.com>
+ */
 
 using GUPnP;
 using Gee;
 
-internal class Rygel.HTTPServer : Rygel.TranscodeManager, Rygel.StateMachine {
+public class Rygel.HTTPServer : Rygel.TranscodeManager, Rygel.StateMachine {
     public string path_root { get; private set; }
 
     // Reference to root container of associated ContentDirectory
@@ -58,36 +66,10 @@ internal 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 ();
@@ -101,13 +83,15 @@ internal class Rygel.HTTPServer : Rygel.TranscodeManager, Rygel.StateMachine {
                                                   int       thumbnail_index,
                                                   int       subtitle_index,
                                                   string?   transcode_target,
-                                                  string?   playlist_target) {
+                                                  string?   playlist_target,
+                                                  MediaResource ? media_resource) {
         var uri = new HTTPItemURI (item,
                                    this,
                                    thumbnail_index,
                                    subtitle_index,
                                    transcode_target,
-                                   playlist_target);
+                                   playlist_target,
+                                   media_resource );
 
         return uri.to_string ();
     }
diff --git a/src/librygel-server/rygel-http-time-seek.vala b/src/librygel-server/rygel-http-time-seek.vala
index 675d4e1..6ca3656 100644
--- a/src/librygel-server/rygel-http-time-seek.vala
+++ b/src/librygel-server/rygel-http-time-seek.vala
@@ -23,73 +23,157 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
-internal class Rygel.HTTPTimeSeek : Rygel.HTTPSeek {
-    public HTTPTimeSeek (HTTPGet request) throws HTTPSeekError {
-        string range;
-        string[] range_tokens;
-        int64 start = 0;
-        int64 duration = (request.object as AudioItem).duration * TimeSpan.SECOND;
-        int64 stop = duration - TimeSpan.MILLISECOND;
-        int64 parsed_value = 0;
-        bool parsing_start = true;
-
-        range = request.msg.request_headers.get_one ("TimeSeekRange.dlna.org");
-
-        if (range != null) {
-            if (!range.has_prefix ("npt=")) {
-                throw new HTTPSeekError.INVALID_RANGE ("Invalid Range '%s'",
-                                                       range);
-            }
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Craig Pratt <craig@ecaspia.com>
+ */
 
-            range_tokens = range.substring (4).split ("-", 2);
-            if (range_tokens[0] == null ||
-                // Start token of the range must be provided
-                range_tokens[0] == "" ||
-                range_tokens[1] == null) {
-                throw new HTTPSeekError.INVALID_RANGE (_("Invalid Range '%s'"),
-                                                       range);
-            }
+// Note: For TimeSeekRange, the request and response header name is the same,
+//       but different forms are allowed for request and response
+public static const string TIMESEEKRANGE_HEADER = "TimeSeekRange.dlna.org";
 
-            foreach (string range_token in range_tokens) {
-                if (range_token == "") {
-                    continue;
-                }
+/**
+ * This class represents a DLNA TimeSeekRange request and response.
+ *
+ * A TimeSeekRange request can only have a time range ("npt=start-end"). But a
+ * TimeSeekRange response may have a time range ("npt=start-end/duration" and
+ * a byte range ("bytes=") corresponding with the actual time/data range.
+ *
+ * Note that DLNA requires both "npt=" and "bytes=" to be set if both
+ * data- and time-based seek are supported. (see DLNA 7.5.4.3.2.24.5)
+ */
+public class Rygel.HTTPTimeSeekRequest : Rygel.HTTPSeekRequest {
+    /**
+     * Requested range start time, in microseconds 
+     */
+    public int64 start_time;
+
+    /**
+     * Requested range end time, in microseconds 
+     */
+    public int64 end_time;
+
+    /**
+     * Requested range duration, in microseconds
+     */
+    public int64 range_duration;
+
+    /**
+     * The total duration of the resource, in microseconds
+     */
+    public int64 total_duration;
+
+    /**
+     * Create a HTTPTimeSeekRequest corresponding with a HTTPGet that contains a TimeSeekRange.dlna.org
+     * header value.
+     *
+     * @param request The HTTP GET/HEAD request
+     * @param positive_rate Indicates if playback is in the positive or negative direction
+     */
+    internal HTTPTimeSeekRequest (HTTPGet request, DLNAPlaySpeed ? speed) throws HTTPSeekRequestError {
+        base ();
+
+        bool positive_rate = (speed == null) || speed.is_positive();
+        bool trick_mode = (speed != null) && speed.is_trick_rate();
+
+        if (request.handler is HTTPMediaResourceHandler) {
+            MediaResource resource = (request.handler as HTTPMediaResourceHandler)
+                                                                  .media_resource;
+            this.total_duration = resource.duration * TimeSpan.SECOND;
+        } else {
+            this.total_duration = (request.object as AudioItem).duration * TimeSpan.SECOND;
+        }
 
-                if (range_token.index_of (":") == -1) {
-                    if (!parse_seconds (range_token, ref parsed_value)) {
-                        throw new HTTPSeekError.INVALID_RANGE
-                                            (_("Invalid Range '%s'"),
-                                               range);
+        var range = request.msg.request_headers.get_one (TIMESEEKRANGE_HEADER);
+
+        if (range == null) {
+            throw new HTTPSeekRequestError.INVALID_RANGE ("%s not present", TIMESEEKRANGE_HEADER);
+        }
+        
+        if (!range.has_prefix ("npt=")) {
+            throw new HTTPSeekRequestError.INVALID_RANGE ("Invalid %s value (missing npt field): '%s'",
+                                                   TIMESEEKRANGE_HEADER, range);
+        }
+
+        var range_tokens = range.substring (4).split ("-", 2);
+
+        int64 start = UNSPECIFIED;
+        if (!parse_npt_time (range_tokens[0], ref start)) {
+            throw new HTTPSeekRequestError.INVALID_RANGE("Invalid %s value (no start): '%s'",
+                                                  TIMESEEKRANGE_HEADER, range);
+        }
+
+        // Check for out-of-bounds range start and clamp it in if in trick/scan mode
+        if ((this.total_duration != UNSPECIFIED) && (start > this.total_duration)) {
+            if (trick_mode && !positive_rate) { // Per DLNA 7.5.4.3.2.24.4
+                this.start_time = this.total_duration;
+            } else { // See DLNA 7.5.4.3.2.24.8
+                throw new HTTPSeekRequestError.OUT_OF_RANGE( "Invalid %s start time %lldns is beyond the content duration of %lldns",
+                                                      TIMESEEKRANGE_HEADER, start,
+                                                      this.total_duration );
+            }
+        } else { // Nothing to check it against - just store it
+            this.start_time = start;
+        }
+
+        // Look for an end time
+        int64 end = UNSPECIFIED;
+        if (parse_npt_time (range_tokens[1], ref end)) {
+            // The end time was specified in the npt ("start-end")
+            // Check for valid range
+            if (positive_rate) {
+                // Check for out-of-bounds range end or fence it in
+                if ((this.total_duration != UNSPECIFIED) && (end > this.total_duration)) {
+                    if (trick_mode) { // Per DLNA 7.5.4.3.2.24.4
+                        this.end_time = this.total_duration;
+                    } else { // Per DLNA 7.5.4.3.2.24.8
+                        throw new HTTPSeekRequestError.OUT_OF_RANGE( "Invalid %s end time %lldns is beyond the content duration of %lldns",
+                                                              TIMESEEKRANGE_HEADER, end,
+                                                              this.total_duration );
                     }
                 } else {
-                    if (!parse_time (range_token,
-                                     ref parsed_value)) {
-                        throw new HTTPSeekError.INVALID_RANGE
-                                            (_("Invalid Range '%s'"),
-                                               range);
-                    }
+                    this.end_time = end;
                 }
-
-                if (parsing_start) {
-                    parsing_start = false;
-                    start = parsed_value;
-                } else {
-                    stop = parsed_value;
+                
+                this.range_duration =  this.end_time - this.start_time;
+                // At positive rate, start < end
+                if (this.range_duration <= 0) { // See DLNA 7.5.4.3.2.24.12
+                    throw new HTTPSeekRequestError.INVALID_RANGE (
+                        "Invalid %s value (start time after end time - forward scan): '%s'",
+                        TIMESEEKRANGE_HEADER, range );
+                }
+            } else { // Negative rate
+                // Note: start_time has already been checked/clamped
+                this.end_time = end;
+                this.range_duration = this.start_time - this.end_time;
+                // At negative rate, start > end
+                if (this.range_duration <= 0) { // See DLNA 7.5.4.3.2.24.12
+                    throw new HTTPSeekRequestError.INVALID_RANGE (
+                        "Invalid %s value (start time before end time - reverse scan): '%s'",
+                        TIMESEEKRANGE_HEADER, range );
                 }
             }
-
-            if (start > stop) {
-                throw new HTTPSeekError.INVALID_RANGE
-                                    (_("Invalid Range '%s'"),
-                                       range);
+        } else { // End time not specified in the npt field ("start-")
+            // See DLNA 7.5.4.3.2.24.4
+            this.end_time = UNSPECIFIED; // Will indicate "end/beginning of binary"
+            if (positive_rate) {
+                this.range_duration = this.total_duration - this.start_time;
+            } else { // Negative rate
+                this.range_duration = this.start_time; // Going backward from start to 0
             }
         }
-
-        base (request.msg, start, stop - 1, TimeSpan.MILLISECOND, duration);
-        this.seek_type = HTTPSeekType.TIME;
     }
 
-    public static bool needed (HTTPGet request) {
+    /**
+     * Return true if time-seek is supported.
+     *
+     * This method utilizes elements associated with the request to determine if a
+     * TimeSeekRange request is supported for the given request/resource.
+     */
+    public static bool supported (HTTPGet request) {
         bool force_seek = false;
 
         try {
@@ -97,39 +181,32 @@ internal class Rygel.HTTPTimeSeek : Rygel.HTTPSeek {
             force_seek = hack.force_seek ();
         } catch (Error error) { }
 
-        return force_seek || (request.object is AudioItem &&
-               (request.object as AudioItem).duration > 0 &&
-               (request.handler is HTTPTranscodeHandler ||
-                (request.thumbnail == null &&
-                 request.subtitle == null &&
-                 (request.object as MediaItem).is_live_stream ())));
+        // TODO: This needs to incorporate some delegation or maybe not even exist here.
+        //       (e.g. if there's a "TimeSeekRange supported" query it should be on a
+        //       ContentResource, since it owns the ProtocolInfo which indicates
+        //       time-seek-ability (a-val of ORG_OP or the LOP-time indicator))
+        return force_seek
+               || ( request.object is AudioItem
+                    && ( request.object as AudioItem).duration > 0
+                         && ( request.handler is HTTPTranscodeHandler
+                              || ( request.thumbnail == null
+                                   && request.subtitle == null
+                                   && (request.object as MediaItem).is_live_stream () ) ) )
+               || ( request.handler is HTTPMediaResourceHandler
+                    && (request.handler as HTTPMediaResourceHandler)
+                        .media_resource.supports_arbitrary_time_seek() );
     }
 
+    /**
+     * Return true of the HTTPGet contains a TimeSeekRange request.
+     */
     public static bool requested (HTTPGet request) {
-        return request.msg.request_headers.get_one ("TimeSeekRange.dlna.org") !=
-               null;
+        return (request.msg.request_headers.get_one (TIMESEEKRANGE_HEADER) != null);
     }
 
-    public override void add_response_headers () {
-        // TimeSeekRange.dlna.org: npt=START_TIME-END_TIME/DURATION
-        double start = (double) this.start / TimeSpan.SECOND;
-        double stop = (double) this.stop / TimeSpan.SECOND;
-        double total = (double) this.total_length / TimeSpan.SECOND;
-
-        var start_str = new char[double.DTOSTR_BUF_SIZE];
-        var stop_str = new char[double.DTOSTR_BUF_SIZE];
-        var total_str = new char[double.DTOSTR_BUF_SIZE];
-
-        var range = "npt=" + start.format (start_str, "%.3f") + "-" +
-                             stop.format (stop_str, "%.3f") + "/" +
-                             total.format (total_str, "%.3f");
-
-        this.msg.response_headers.append ("TimeSeekRange.dlna.org", range);
-    }
-
-    // Parses npt times in the format of '417.33'
-    private static bool parse_seconds (string    range_token,
-                                       ref int64 value) {
+    // Parses npt times in the format of '417.33' and returns the time in microseconds
+    private static bool parse_npt_seconds (string range_token,
+                                           ref int64 value) {
         if (range_token[0].isdigit ()) {
             value = (int64) (double.parse (range_token) * TimeSpan.SECOND);
         } else {
@@ -138,9 +215,17 @@ internal class Rygel.HTTPTimeSeek : Rygel.HTTPSeek {
         return true;
     }
 
-    // Parses npt times in the format of '10:19:25.7'
-    private static bool parse_time (string    range_token,
-                                    ref int64 value) {
+    // Parses npt times in the format of '10:19:25.7' and returns the time in microseconds
+    private static bool parse_npt_time (string? range_token,
+                                        ref int64 value) {
+        if (range_token == null) {
+            return false;
+        }
+        
+        if (range_token.index_of (":") == -1) {
+            return parse_npt_seconds(range_token, ref value);
+        }
+        // parse_seconds has a ':' in it...
         int64 seconds_sum = 0;
         int time_factor = 0;
         string[] time_tokens;
@@ -157,8 +242,7 @@ internal class Rygel.HTTPTimeSeek : Rygel.HTTPSeek {
 
         foreach (string time in time_tokens) {
             if (time[0].isdigit ()) {
-                seconds_sum += (int64) ((double.parse (time) *
-                                         TimeSpan.SECOND) * time_factor);
+                seconds_sum += (int64) ((double.parse (time) * TimeSpan.SECOND) * time_factor);
             } else {
                 return false;
             }
@@ -169,3 +253,134 @@ internal class Rygel.HTTPTimeSeek : Rygel.HTTPSeek {
         return true;
     }
 }
+
+public class Rygel.HTTPTimeSeekResponse : Rygel.HTTPResponseElement {
+    /**
+     * Effective range start time, in microseconds 
+     */
+    public int64 start_time { get; private set; }
+
+    /**
+     * Effective range end time, in microseconds 
+     */
+    public int64 end_time { get; private set; }
+
+    /**
+     * Effective range duration, in microseconds
+     */
+    public int64 range_duration { get; private set; }
+
+    /**
+     * The total duration of the resource, in microseconds
+     */
+    public int64 total_duration { get; private set; }
+
+    /**
+     * The start of the range in bytes 
+     */
+    public int64 start_byte { get; private set; }
+
+    /**
+     * The end of the range in bytes (inclusive)
+     */
+    public int64 end_byte { get; private set; }
+
+    /**
+     * The length of the range in bytes
+     */
+    public int64 range_length { get; private set; }
+
+    /**
+     * The length of the resource in bytes
+     */
+    public int64 total_size { get; private set; }
+    
+    public HTTPTimeSeekResponse(int64 start_time, int64 end_time, int64 total_duration,
+                                int64 start_byte, int64 end_byte, int64 total_size ) {
+        base();
+        this.start_time = start_time;
+        this.end_time = end_time;
+        this.total_duration = total_duration;
+        
+        this.start_byte = start_byte;
+        this.end_byte = end_byte;
+        this.range_length = end_byte - start_byte + 1;
+        this.total_size = total_size;
+    }
+
+    /**
+     * Create a HTTPTimeSeekResponse from a HTTPTimeSeekRequest
+     *
+     * Note: This form is only valid when byte-seek is not supported, according to the
+     * associated resource's ProtocolInfo (see DLNA 7.5.4.3.2.24.5)
+     */
+    public HTTPTimeSeekResponse.time_only(int64 start_time, int64 end_time, int64 total_duration) {
+        base();
+        this.start_time = start_time;
+        this.end_time = end_time;
+        this.total_duration = total_duration;
+        
+        this.start_byte = UNSPECIFIED;
+        this.end_byte = UNSPECIFIED;
+        this.range_length = UNSPECIFIED;
+        this.total_size = UNSPECIFIED;
+    }
+    
+    /**
+     * Create a HTTPTimeSeekResponse from a HTTPTimeSeekRequest
+     *
+     * Note: This form is only valid when byte-seek is not supported, according to the
+     * associated resource's ProtocolInfo (see DLNA 7.5.4.3.2.24.5)
+     */
+    public HTTPTimeSeekResponse.from_request( HTTPTimeSeekRequest time_seek_request,
+                                              int64 total_duration ) {
+        HTTPTimeSeekResponse.time_only( time_seek_request.start_time,
+                                        time_seek_request.end_time,
+                                        total_duration );
+    }
+    
+    public override void add_response_headers (Rygel.HTTPRequest request) {
+        // The response form of TimeSeekRange:
+        //
+        // TimeSeekRange.dlna.org: npt=START_TIME-END_TIME/DURATION bytes=START_BYTE-END_BYTE/LENGTH
+        //
+        // The "bytes=" field can be ommitted in some cases. (e.g. ORG_OP a-val==1, b-val==0)
+        // The DURATION can be "*" in some cases (e.g. for limited-operation mode)
+        // The LENGTH can be "*" in some cases (e.g. for limited-operation mode)
+        // And the entire response header can be ommitted for HEAD requests (see DLNA 7.5.4.3.2.24.2)
+
+        // It's not our job at this level to enforce all the semantics of the TimeSeekRange
+        //  response, as we don't have enough context. Setting up the correct HTTPTimeSeekRequest
+        //  object is the responsibility of the object owner. To form the response, we just
+        //  use what is set.
+
+        if (start_time != UNSPECIFIED) {
+            var response = new StringBuilder();
+            response.append("npt=");
+            response.append_printf("%.3f-", (double) this.start_time / TimeSpan.SECOND);
+            response.append_printf("%.3f/", (double) this.end_time / TimeSpan.SECOND);
+            if (this.total_duration != UNSPECIFIED) {
+                response.append_printf("%.3f", (double) this.total_duration / TimeSpan.SECOND);
+            } else {
+                response.append("*");
+            }
+
+            if (this.start_byte != UNSPECIFIED) {
+                response.append(" bytes=");
+                response.append(this.start_byte.to_string());
+                response.append("-");
+                response.append(this.end_byte.to_string());
+                response.append("/");
+                if (this.total_size != UNSPECIFIED) {
+                    response.append(this.total_size.to_string());
+                } else {
+                    response.append("*");
+                }
+                // Note: Don't use set_content_range() here - we don't want a "Content-range" header
+                request.msg.response_headers.set_content_length (this.range_length);
+            }
+
+            request.msg.response_headers.append (TIMESEEKRANGE_HEADER, response.str);
+        }
+    }
+}
diff --git a/src/librygel-server/rygel-http-transcode-handler.vala b/src/librygel-server/rygel-http-transcode-handler.vala
index da21a06..40f9e2a 100644
--- a/src/librygel-server/rygel-http-transcode-handler.vala
+++ b/src/librygel-server/rygel-http-transcode-handler.vala
@@ -23,6 +23,14 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Craig Pratt <craig@ecaspia.com>
+ */
+
 using GUPnP;
 
 /**
@@ -41,9 +49,6 @@ internal class Rygel.HTTPTranscodeHandler : HTTPGetHandler {
                                                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);
@@ -52,8 +57,8 @@ internal class Rygel.HTTPTranscodeHandler : HTTPGetHandler {
     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);
+        var src = item.create_stream_source_for_resource
+                                        (request.http_server.context.host_ip, null);
         if (src == null) {
             throw new HTTPRequestError.NOT_FOUND (_("Not found"));
         }
diff --git a/src/librygel-server/rygel-image-item.vala b/src/librygel-server/rygel-image-item.vala
index 55503c6..322e7f6 100644
--- a/src/librygel-server/rygel-image-item.vala
+++ b/src/librygel-server/rygel-image-item.vala
@@ -23,6 +23,15 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Doug Galligan <doug@sentosatech.com>>
+ * Author: Craig Pratt <craig@ecaspia.com>
+ */
+
 using GUPnP;
 using Gee;
 
@@ -96,9 +105,10 @@ public class Rygel.ImageItem : MediaItem, VisualItem {
                                         (DIDLLiteObject didl_object,
                                          string?      uri,
                                          string       protocol,
+                                         MediaResource resource,
                                          string?      import_uri = null)
                                          throws Error {
-        var res = base.add_resource (didl_object, uri, protocol, import_uri);
+        var res = base.add_resource (didl_object, uri, protocol, resource, import_uri);
 
         this.add_visual_props (res);
 
@@ -117,8 +127,9 @@ public class Rygel.ImageItem : MediaItem, VisualItem {
     }
 
     protected override ProtocolInfo get_protocol_info (string? uri,
-                                                       string  protocol) {
-        var protocol_info = base.get_protocol_info (uri, protocol);
+                                                       string  protocol,
+                                                       MediaResource resource) {
+        var protocol_info = base.get_protocol_info (uri, protocol, resource);
 
         protocol_info.dlna_flags |= DLNAFlags.INTERACTIVE_TRANSFER_MODE;
 
diff --git a/src/librygel-server/rygel-item-updater.vala b/src/librygel-server/rygel-item-updater.vala
index 9f1870e..0ea1ecf 100644
--- a/src/librygel-server/rygel-item-updater.vala
+++ b/src/librygel-server/rygel-item-updater.vala
@@ -20,6 +20,14 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Doug Galligan <doug@sentosatech.com>>
+ */
+
 using GUPnP;
 using Gee;
 
@@ -27,6 +35,8 @@ using Gee;
  * UpdateObject action implementation.
  */
 internal class Rygel.ItemUpdater: GLib.Object, Rygel.StateMachine {
+    public const string QN_UPNP_DATE = "dc:date";
+
     private static Regex escape_regex;
 
     private string object_id;
@@ -98,6 +108,71 @@ internal class Rygel.ItemUpdater: GLib.Object, Rygel.StateMachine {
         }
     }
 
+    // Remove any leading or trailing spaces for the corresponding text node.
+    private static string formatTagValues (string tag_values){
+        if(tag_values.length > 0 && tag_values.get_char(0) == '<'){
+            var init_split = tag_values.split("</");
+            var tag_name = init_split[1].substring(0, init_split[1].length - 1)._strip();
+            var tag_value = init_split[0].split(">")[1]._strip();
+            debug ("Tag Name formatted :%s",tag_name);
+            debug ("Tag Value formatted :%s",tag_value);
+            return "<%s>%s</%s>".printf (tag_name, tag_value, tag_name);
+        }
+        return tag_values;
+    }
+
+    // Check if the date is not in the recommended format
+    private static void check_date_tag (string [] current_tag,
+                                        string [] new_tag) throws Error {
+        int date_index = -1;
+
+        // Find the current tag index and
+        // check the validity of the new tag in the same index
+        foreach (unowned string cur_str in current_tag) {
+            date_index++;
+            if (cur_str.index_of(QN_UPNP_DATE) != -1) {
+                var date_val = new_tag[date_index].split("</")[0]
+                                                  .split(">")[1]._strip();
+                check_date (date_val);
+                break;
+            }
+        }
+
+        date_index = -1;
+        // If the current tag does not then search new Tag for dc:date
+        foreach (unowned string new_str in new_tag) {
+            date_index++;
+            if (new_str.index_of(QN_UPNP_DATE) != -1) {
+                var date_val = new_tag[date_index].split("</")[0]
+                                                  .split(">")[1]._strip();
+                check_date (date_val);
+                break;
+            }
+        }
+    }
+
+    // Same logic used in object-creator class
+    private static void check_date (string date_value) throws Error {
+        int year = 0, month = 0, day = 0;
+        if (date_value.scanf ("%4d-%02d-%02d",
+                                  out year,
+                                  out month,
+                                  out day) != 3) {
+            throw new ContentDirectoryError.INVALID_NEW_TAG_VALUE
+                                    (_("Invalid date format: %s"),
+                                     date_value);
+        }
+
+        var date = GLib.Date ();
+        date.set_dmy ((DateDay) day, (DateMonth) month, (DateYear) year);
+
+        if (!date.valid ()) {
+            throw new ContentDirectoryError.INVALID_NEW_TAG_VALUE
+                                    (_("Invalid date: %s"),
+                                     date_value);
+        }
+    }
+
     private static LinkedList<string> csv_split (string? tag_values) {
         var list = new LinkedList<string> ();
 
@@ -125,6 +200,14 @@ internal class Rygel.ItemUpdater: GLib.Object, Rygel.StateMachine {
                 case ',':
                     var value = tag_values.substring (token_start,
                                                       token_length);
+                    //  Check if the start character is "<"
+                    if (value.length > 0 && value[0] == '<') {
+                         // then the end character must be > before the split"
+                         if (!(value[value.length-1] == '>')) {
+                             // Continue the loop again.
+                             break;
+                         }
+                    }
 
                     list.add (ItemUpdater.unescape (value));
                     token_start += token_length + 1;
@@ -139,7 +222,7 @@ internal class Rygel.ItemUpdater: GLib.Object, Rygel.StateMachine {
             }
         }
 
-        list.add (ItemUpdater.unescape (tag_values.substring (token_start)));
+        list.add (formatTagValues(ItemUpdater.unescape (tag_values.substring (token_start)._strip())));
 
         return list;
     }
@@ -148,6 +231,12 @@ internal class Rygel.ItemUpdater: GLib.Object, Rygel.StateMachine {
         var media_object = yield this.fetch_object ();
         var current_list = csv_split (this.current_tag_value);
         var new_list = csv_split (this.new_tag_value);
+
+        // If the size is not equal it will be handled downstream and different error will be thrown
+        if (current_list.size == new_list.size){
+            check_date_tag (current_list.to_array(), new_list.to_array());
+        }
+
         var result = yield media_object.apply_fragments
                                         (current_list,
                                          new_list,
diff --git a/src/librygel-server/rygel-media-container.vala b/src/librygel-server/rygel-media-container.vala
index c6e0b13..0dbb8a5 100644
--- a/src/librygel-server/rygel-media-container.vala
+++ b/src/librygel-server/rygel-media-container.vala
@@ -24,6 +24,14 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Doug Galligan <doug@sentosatech.com>>
+ */
+
 using GUPnP;
 using Gee;
 
@@ -334,7 +342,8 @@ public abstract class Rygel.MediaContainer : MediaObject {
 
         var res = this.add_resource (didl_container,
                                      uri.to_string (),
-                                     http_server.get_protocol ());
+                                     http_server.get_protocol (),
+                                     new MediaResource ("dummy2"));
         if (res != null) {
             res.protocol_info.mime_type = "text/xml";
             res.protocol_info.dlna_profile = "DIDL_S";
@@ -346,7 +355,8 @@ public abstract class Rygel.MediaContainer : MediaObject {
 
         res = this.add_resource (didl_container,
                                  uri.to_string (),
-                                 http_server.get_protocol ());
+                                 http_server.get_protocol (),
+                                 new MediaResource ("dummy3"));
         if (res != null) {
             res.protocol_info.mime_type = "audio/x-mpegurl";
         }
@@ -356,6 +366,7 @@ public abstract class Rygel.MediaContainer : MediaObject {
                                         (DIDLLiteObject didl_object,
                                          string?        uri,
                                          string         protocol,
+                                         MediaResource  resource,
                                          string?        import_uri = null)
                                          throws Error {
         if (this.child_count <= 0) {
@@ -365,6 +376,7 @@ public abstract class Rygel.MediaContainer : MediaObject {
         var res = base.add_resource (didl_object,
                                      uri,
                                      protocol,
+                                     resource,
                                      import_uri);
 
         if (uri != null) {
diff --git a/src/librygel-server/rygel-media-engine.vala b/src/librygel-server/rygel-media-engine.vala
index 28d61b1..26ffba4 100644
--- a/src/librygel-server/rygel-media-engine.vala
+++ b/src/librygel-server/rygel-media-engine.vala
@@ -20,6 +20,14 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Craig Pratt <craig@ecaspia.com>
+ */
+
 public errordomain Rygel.MediaEngineError {
     NOT_FOUND
 }
@@ -40,7 +48,7 @@ public errordomain Rygel.MediaEngineError {
  * media-engine=librygel-media-engine-gst.so
  *
  * Media engines should also derive their own #RygelDataSource,
- * returning an instance of it from create_data_source().
+ * returning an instance of it from create_data_source_for_resource().
  *
  * If this media engine supports transcoding then it will typically
  * implement a set of transcoding classes, typically with one 
@@ -84,21 +92,37 @@ public abstract class Rygel.MediaEngine : GLib.Object {
     }
 
     /**
-     * Get a list of the DLNA profiles that are supported by this media
-     * engine when calling rygel_media_engine_create_data_source().
-     *
-     * Other DLNA profiles may be supported as transcoding targets -
+     * Get a list of the DLNA profiles that the media engine can stream.
      *
      * This information is needed to implement DLNA's
      * ConnectionManager.GetProtocolInfo call and to determine whether Rygel
      * can accept an uploaded file.
      *
      * @return A list of #RygelDLNAProfile<!-- -->s
-     * @see rygel_media_engine_get_transcoders().
      */
     public abstract unowned List<DLNAProfile> get_dlna_profiles ();
 
     /**
+     * Get the supported MediaResources for the given content uri.
+     *
+     * The MediaResources returned may include formats/profiles that don't match the
+     * raw source content byte-for-byte. 
+     * 
+     * Each MediaResource returned in the List must have a unique "name" field
+     * (containing only alphanumeric characters). The order of resources in
+     * the List should be from most-preferred to least-preferred. And some fields
+     * related to the delivery protocol will be over-written (e.g. the host address
+     * portion of the URI and the protocol field/deliver flags of the protocolInfo).
+     *
+     * Note: To reduce overhead, this call will only be made when source content is
+     * added or changed (the results will be cached).
+     *
+     * @return A list of #MediaResources<!-- -->s or null if no resources are supported
+     *         for the item.
+     */
+    public abstract Gee.List<MediaResource>? get_resources_for_uri(string uri);
+
+    /**
      * Get a list of the transcoders that are provided by this media engine.
      *
      * @return A list of #RygelTranscoder<!-- -->s or null if not supported.
@@ -106,10 +130,18 @@ public abstract class Rygel.MediaEngine : GLib.Object {
     public abstract unowned List<Transcoder>? get_transcoders ();
 
     /**
-     * Get a data source for the URI.
+     * Get a data source for the URI which renders the content specified by the uri
+     * according to the MediaResource-specified parameters.
+     *
+     * Note that the provided #resource will be field-wise equivalent with a MediaResource
+     * obtained from #get_resources_for_uri, but subclasses should not expect objects
+     * references from #get_resources_for_uri to be provided to this method. If #resource
+     * is null, then the DataSource returned should render the raw (unmodified) content.
      *
      * @param uri to create the data source for.
-     * @return A data source representing the uri
+     * @param resource format to render for the data source.
+     * @return A data source representing the uri rendered according to resource parameters
      */
-    public abstract DataSource? create_data_source (string uri);
+    public abstract DataSource? create_data_source_for_resource
+                                (string uri, MediaResource ? resource);
 }
diff --git a/src/librygel-server/rygel-media-item.vala b/src/librygel-server/rygel-media-item.vala
index f175c79..7929acf 100644
--- a/src/librygel-server/rygel-media-item.vala
+++ b/src/librygel-server/rygel-media-item.vala
@@ -23,6 +23,15 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Doug Galligan <doug@sentosatech.com>>
+ * Author: Craig Pratt <craig@ecaspia.com>
+ */
+
 using GUPnP;
 
 private errordomain Rygel.MediaItemError {
@@ -35,15 +44,13 @@ private errordomain Rygel.MediaItemError {
  * These objects correspond to items in the UPnP ContentDirectory's DIDL-Lite XML.
  */
 public abstract class Rygel.MediaItem : MediaObject {
-    public string date { get; set; }
-
-    public string creator { get; set; }
-
     // Resource info
+    // TODO: Remove me (this will be in MediaResource)
     public string mime_type { get; set; }
     public string dlna_profile { get; set; }
 
     // Size in bytes
+    // TODO: Remove me (this will be in MediaResource)
     private int64 _size = -1;
     public int64 size {
         get {
@@ -63,6 +70,9 @@ public abstract class Rygel.MediaItem : MediaObject {
 
     public string description { get; set; default = null; }
 
+    // Note: MediaResources are ordered from most- to least-preferred
+    public Gee.List<MediaResource> media_resources { get; set; default = null; }
+
     public override OCMFlags ocm_flags {
         get {
             var flags = OCMFlags.NONE;
@@ -113,7 +123,8 @@ public abstract class Rygel.MediaItem : MediaObject {
 
     // Live media items need to provide a nice working implementation of this
     // method if they can/do not provide a valid URI
-    public virtual DataSource? create_stream_source (string? host_ip = null) {
+    public virtual DataSource? create_stream_source_for_resource (string? host_ip = null,
+                                                                  MediaResource? resource = null) {
         if (this.uris.size == 0) {
             return null;
         }
@@ -128,7 +139,7 @@ public abstract class Rygel.MediaItem : MediaObject {
             }
         }
 
-        return MediaEngine.get_default ().create_data_source (translated_uri);
+        return MediaEngine.get_default ().create_data_source_for_resource (translated_uri, resource);
     }
 
     public bool is_live_stream () {
@@ -151,11 +162,13 @@ public abstract class Rygel.MediaItem : MediaObject {
                                         (DIDLLiteObject didl_object,
                                          string?        uri,
                                          string         protocol,
+                                         MediaResource  resource,
                                          string?        import_uri = null)
                                          throws Error {
         var res = base.add_resource (didl_object,
                                      uri,
                                      protocol,
+                                     resource,
                                      import_uri);
 
         if (uri != null && !this.place_holder) {
@@ -178,11 +191,6 @@ public abstract class Rygel.MediaItem : MediaObject {
             res.update_count = 0;
         }
 
-        res.size64 = this.size;
-
-        /* Protocol info */
-        res.protocol_info = this.get_protocol_info (uri, protocol);
-
         return res;
     }
 
@@ -261,8 +269,8 @@ public abstract class Rygel.MediaItem : MediaObject {
          * 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) {
+            this.write_didl_lite_for_resources (http_server, didl_item);
             var host_ip = http_server.context.host_ip;
 
             // then original URIs
@@ -285,22 +293,46 @@ public abstract class Rygel.MediaItem : MediaObject {
     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);
+    internal void write_didl_lite_for_resources(HTTPServer server, DIDLLiteItem didl_item) {
+        foreach (var resource in media_resources) {
+            // We create URIs here, arguably, so that the URI address can be written to
+            //  incorporate the interface address the request was received on (but if a
+            //  MediaItem subclass is created for each browse, perhaps it would be more
+            //  appropriate to have this done at initialization instead of using lazy
+            //  initialization?)
+            if (resource.uri == null || resource.uri.length == 0) {
+                var uri = server.create_uri_for_item (this,
+                                                       -1,
+                                                       -1,
+                                                       this.dlna_profile,
+                                                       // TODO: resource.protocol_info.dlna_profile?
+                                                       null,
+                                                       resource);
+                resource.uri = uri;
+            } else {
+                // We'll be here if the MediaEngine supplied a URI. This is the temporary
+                //  way to support external URIs (e.g. MPEG-DASH MPDs). Eventually external
+                //  resources/URIs should be setup by the MediaServer.
+                debug("Found MediaResource with pre-existing URI: " + resource.uri);
+            }
+
+            DIDLLiteResource didl_resource = didl_item.add_resource();
+            resource.write_didl_lite(didl_resource);
         }
     }
 
     protected virtual ProtocolInfo get_protocol_info (string? uri,
-                                                      string  protocol) {
+                                                      string  protocol,
+                                                      MediaResource resource) {
+        // TODO: REMOVE ME - This should be on MediaResource
         var protocol_info = new ProtocolInfo ();
 
-        protocol_info.mime_type = this.mime_type;
-        protocol_info.dlna_profile = this.dlna_profile;
+        protocol_info.mime_type = resource.protocol_info.mime_type;
+        protocol_info.dlna_profile = resource.protocol_info.dlna_profile;
         protocol_info.protocol = protocol;
+        // Seems like these are properties of the MediaServer
         protocol_info.dlna_flags = DLNAFlags.DLNA_V15 |
                                    DLNAFlags.CONNECTION_STALL |
                                    DLNAFlags.BACKGROUND_TRANSFER_MODE;
@@ -342,11 +374,11 @@ public abstract class Rygel.MediaItem : MediaObject {
     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);
+        foreach (MediaResource resource in this.media_resources) {
+            var protocol = this.get_protocol_for_uri (this.uris[0]);
 
             if (allow_internal || protocol != "internal") {
-                this.add_resource (didl_item, uri, protocol);
+                this.add_resource (didl_item, this.uris[0], protocol, resource);
             }
         }
     }
diff --git a/src/librygel-server/rygel-media-object.vala b/src/librygel-server/rygel-media-object.vala
index f5914c9..dbe9e7e 100644
--- a/src/librygel-server/rygel-media-object.vala
+++ b/src/librygel-server/rygel-media-object.vala
@@ -21,6 +21,14 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Doug Galligan <doug@sentosatech.com>>
+ */
+
 using GUPnP;
 using Gee;
 
@@ -41,6 +49,8 @@ public abstract class Rygel.MediaObject : GLib.Object {
     public string id { get; set construct; }
     public string ref_id { get; set; }
     public string upnp_class { get; construct set; }
+    public string date { get; set; }
+    public string creator { get; set; }
     public uint64 modified { get; set; }
     public uint object_update_id { get; set; }
 
@@ -280,6 +290,7 @@ public abstract class Rygel.MediaObject : GLib.Object {
                                         (DIDLLiteObject object,
                                          string?        uri,
                                          string         protocol,
+                                         MediaResource  resource,                                       
                                          string?        import_uri = null)
                                          throws Error {
         var res = object.add_resource ();
diff --git a/src/librygel-server/rygel-media-query-action.vala b/src/librygel-server/rygel-media-query-action.vala
index b3f52aa..914497b 100644
--- a/src/librygel-server/rygel-media-query-action.vala
+++ b/src/librygel-server/rygel-media-query-action.vala
@@ -20,6 +20,14 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Doug Galligan <doug@sentosatech.com>>
+ */
+
 using GUPnP;
 using Gee;
 
diff --git a/src/librygel-server/rygel-media-resource.vala b/src/librygel-server/rygel-media-resource.vala
new file mode 100644
index 0000000..b19321a
--- /dev/null
+++ b/src/librygel-server/rygel-media-resource.vala
@@ -0,0 +1,162 @@
+/* 
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 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 APPLE 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.
+ *
+ * Author: Craig Pratt <craig@ecaspia.com>
+ *
+ * This file is part of Rygel.
+ */
+
+using GUPnP;
+using Gee;
+
+/**
+ * Represents a media resource (Music, Video, Image, etc).
+ */
+public class Rygel.MediaResource : GLib.Object {
+    
+    private string name;
+    public string uri { get; set; }
+    public ProtocolInfo protocol_info { get; set; default = null; }
+    public string extension { get; set; default = null; }
+    public int64 size { get; set; default = -1; }
+    public int64 cleartext_size { get; set; default = -1; }
+    public long duration { get; set; default = -1; }
+    public int bitrate { get; set; default = -1; }
+    public int bits_per_sample { get; set; default = -1; }
+    public int color_depth { get; set; default = -1; }
+    public int width { get; set; default = -1; }
+    public int height { get; set; default = -1; }
+    public int audio_channels { get; set; default = -1; }
+    public int sample_freq { get; set; default = -1; }
+    
+    public MediaResource(string name) {
+        this.name = name;
+    }
+
+    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.cleartextSize;
+        this.protocol_info = didl_resource.protocol_info;
+        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;
+    }
+    
+    public DIDLLiteResource write_didl_lite (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.cleartextSize = this.cleartext_size;
+        didl_resource.protocol_info = this.protocol_info;
+        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;
+        
+        return didl_resource;
+    }
+
+    public bool supports_arbitrary_byte_seek() {
+        bool supported = ((this.protocol_info.dlna_operation & DLNAOperation.RANGE) != 0);
+        return supported;
+    }
+
+    public bool supports_arbitrary_time_seek() {
+        bool supported = ((this.protocol_info.dlna_operation & DLNAOperation.TIMESEEK) != 0);
+        return supported;
+    }
+    
+    public bool supports_limited_byte_seek() {
+        return check_flag (this.protocol_info,DLNAFlags.BYTE_BASED_SEEK);
+    }
+    
+    public bool supports_limited_time_seek() {
+        return check_flag (this.protocol_info,DLNAFlags.TIME_BASED_SEEK);
+    }
+
+    public bool supports_limited_cleartext_byte_seek() {
+        return check_flag (this.protocol_info,DLNAFlags.LOP_CLEARTEXT_BYTESEEK);
+    }
+
+    public bool supports_full_cleartext_byte_seek() {
+        return check_flag (this.protocol_info,DLNAFlags.CLEARTEXT_BYTESEEK_FULL);
+    }
+
+    public bool is_link_protection_enabled() {
+        return check_flag (this.protocol_info,DLNAFlags.LINK_PROTECTED_CONTENT);
+	}
+
+    // This is to check if any of the cleartext byte seek operation is supported.
+    public bool is_cleartext_range_support_enabled() {
+        return (check_flag (this.protocol_info, DLNAFlags.CLEARTEXT_BYTESEEK_FULL) ||
+                check_flag (this.protocol_info, DLNAFlags.LOP_CLEARTEXT_BYTESEEK));
+	}
+
+    private bool check_flag (ProtocolInfo protocol_info, int flag) {
+        long flag_value = long.parse ("%0.8d".printf (protocol_info.dlna_flags));
+        return ((flag_value & flag) == flag);
+    }
+
+    public bool supports_playspeed() {
+        return (this.protocol_info.play_speeds.length > 0);
+    }
+    
+    public string to_string() {
+        // TODO: incorporate all set fields
+        return name + ":{" + ((protocol_info == null) ? "null" : protocol_info.to_string());
+    }
+}
diff --git a/src/librygel-server/rygel-music-item.vala b/src/librygel-server/rygel-music-item.vala
index 73b5a5b..f5aa6dd 100644
--- a/src/librygel-server/rygel-music-item.vala
+++ b/src/librygel-server/rygel-music-item.vala
@@ -24,6 +24,14 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Doug Galligan <doug@sentosatech.com>>
+ */
+
 using GUPnP;
 
 /**
@@ -154,6 +162,7 @@ public class Rygel.MusicItem : AudioItem {
                                                               0,
                                                               -1,
                                                               null,
+                                                              null,
                                                               null);
         }
     }
diff --git a/src/librygel-server/rygel-relational-expression.vala b/src/librygel-server/rygel-relational-expression.vala
index 119eed3..e050718 100644
--- a/src/librygel-server/rygel-relational-expression.vala
+++ b/src/librygel-server/rygel-relational-expression.vala
@@ -21,6 +21,14 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Parthiban Balasubramanian <P.Balasubramanian-contractor@cablelabs.com>
+ */
+
 using GUPnP;
 
 /**
@@ -31,7 +39,7 @@ public class Rygel.RelationalExpression :
              Rygel.SearchExpression<SearchCriteriaOp,string,string> {
     internal const string CAPS = "@id,@parentID,@refID,upnp:class," +
                                  "dc:title,upnp:artist,upnp:album," +
-                                 "dc:creator,upnp:createClass,@childCount";
+                                 "dc:creator,@childCount";
 
     public override bool satisfied_by (MediaObject media_object) {
         switch (this.operand1) {
diff --git a/src/librygel-server/rygel-search.vala b/src/librygel-server/rygel-search.vala
index b97e991..494e533 100644
--- a/src/librygel-server/rygel-search.vala
+++ b/src/librygel-server/rygel-search.vala
@@ -21,6 +21,14 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Parthiban Balasubramanian <P.Balasubramanian-contractor@cablelabs.com>
+ */
+
 using GUPnP;
 using Gee;
 using Soup;
diff --git a/src/librygel-server/rygel-source-connection-manager.vala b/src/librygel-server/rygel-source-connection-manager.vala
index 4c23884..ee8ebe1 100644
--- a/src/librygel-server/rygel-source-connection-manager.vala
+++ b/src/librygel-server/rygel-source-connection-manager.vala
@@ -23,6 +23,14 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Parthiban Balasubramanian <P.Balasubramanian-contractor@cablelabs.com>
+ */
+
 using GUPnP;
 using Gee;
 
@@ -36,54 +44,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-transcode-manager.vala b/src/librygel-server/rygel-transcode-manager.vala
index 8df32d3..efcac28 100644
--- a/src/librygel-server/rygel-transcode-manager.vala
+++ b/src/librygel-server/rygel-transcode-manager.vala
@@ -23,6 +23,14 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Craig Pratt <craig@ecaspia.com>
+ */
+
 using GUPnP;
 using Gee;
 
@@ -45,7 +53,8 @@ public abstract class Rygel.TranscodeManager : GLib.Object {
                                                 int        thumbnail_index,
                                                 int        subtitle_index,
                                                 string?    transcode_target,
-                                                string?    playlist_target);
+                                                string?    playlist_target,
+                                                MediaResource ? media_resource);
 
     public void add_resources (DIDLLiteItem didl_item, MediaItem item)
                                throws Error {
@@ -96,7 +105,8 @@ public abstract class Rygel.TranscodeManager : GLib.Object {
             protocol_info.protocol = this.get_protocol ();
             protocol_info.mime_type = transcoder.mime_type;
             protocol_info.dlna_profile = transcoder.dlna_profile;
-
+            protocol_info.dlna_operation = transcoder.operation;
+            protocol_info.dlna_flags = transcoder.flags;
             protocol_infos.add (protocol_info);
         }
 
diff --git a/src/librygel-server/rygel-transcoder.vala b/src/librygel-server/rygel-transcoder.vala
index fcaab7b..eb406b2 100644
--- a/src/librygel-server/rygel-transcoder.vala
+++ b/src/librygel-server/rygel-transcoder.vala
@@ -23,6 +23,16 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Craig Pratt <craig@ecaspia.com>
+ */
+
+// TODO: Fix or eliminate in next patch
+
 using GUPnP;
 
 /**
@@ -37,6 +47,10 @@ public abstract class Rygel.Transcoder : GLib.Object {
     public string mime_type { get; construct; }
     public string dlna_profile { get; construct; }
     public string extension { get; construct; }
+    // DLNA operation specific to the transcoder
+    public GUPnP.DLNAOperation operation { get; set; }
+    // DLNA flags specific to the transcoder
+    public GUPnP.DLNAFlags flags { get; set; }
 
     /**
      * Creates a transcoding source.
@@ -73,35 +87,7 @@ public abstract class Rygel.Transcoder : GLib.Object {
                                                    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;
+        return null;
     }
 
     /**
diff --git a/src/librygel-server/rygel-video-item.vala b/src/librygel-server/rygel-video-item.vala
index b63b308..d9f7783 100644
--- a/src/librygel-server/rygel-video-item.vala
+++ b/src/librygel-server/rygel-video-item.vala
@@ -24,6 +24,15 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Craig Pratt <craig@ecaspia.com>
+ * Author: Doug Galligan <doug@sentosatech.com>>
+ */
+
 using GUPnP;
 using Gee;
 
@@ -87,6 +96,8 @@ public class Rygel.VideoItem : AudioItem, VisualItem {
     public override void add_uri (string uri) {
         base.add_uri (uri);
 
+        // TODO: This may need to be coordinated with the setting of MediaResources (since
+        //       the mime type(s) aren't known until the MRs are established) 
         this.add_thumbnail_for_uri (uri, this.mime_type);
 
         var subtitle_manager = SubtitleManager.get_default ();
@@ -119,9 +130,10 @@ public class Rygel.VideoItem : AudioItem, VisualItem {
                                         (DIDLLiteObject didl_object,
                                          string?        uri,
                                          string         protocol,
+                                         MediaResource  resource,
                                          string?        import_uri = null)
                                          throws Error {
-        var res = base.add_resource (didl_object, uri, protocol, import_uri);
+        var res = base.add_resource (didl_object, uri, protocol, resource, import_uri);
 
         this.add_visual_props (res);
 
@@ -188,6 +200,7 @@ public class Rygel.VideoItem : AudioItem, VisualItem {
                                                            -1,
                                                            index,
                                                            null,
+                                                           null,
                                                            null);
                 subtitle.add_didl_node (didl_item);
 
diff --git a/src/librygel-server/rygel-visual-item.vala b/src/librygel-server/rygel-visual-item.vala
index 82a792e..e1bfbec 100644
--- a/src/librygel-server/rygel-visual-item.vala
+++ b/src/librygel-server/rygel-visual-item.vala
@@ -24,6 +24,14 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Doug Galligan <doug@sentosatech.com>>
+ */
+
 using GUPnP;
 using Gee;
 
@@ -99,6 +107,7 @@ public interface Rygel.VisualItem : MediaItem {
                                                             index,
                                                             -1,
                                                             null,
+                                                            null,
                                                             null);
                 thumbnail.add_resource (didl_item, server.get_protocol ());
 
diff --git a/src/media-engines/gstreamer/rygel-gst-data-source.vala b/src/media-engines/gstreamer/rygel-gst-data-source.vala
index 1e536f6..5a9ef5e 100644
--- a/src/media-engines/gstreamer/rygel-gst-data-source.vala
+++ b/src/media-engines/gstreamer/rygel-gst-data-source.vala
@@ -20,6 +20,14 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Craig Pratt <craig@ecaspia.com>
+ */
+
 using Gst;
 
 internal errordomain Rygel.GstDataSourceError {
@@ -28,12 +36,14 @@ internal errordomain Rygel.GstDataSourceError {
 
 internal class Rygel.GstDataSource : Rygel.DataSource, GLib.Object {
     internal dynamic Element src;
+    internal MediaResource res;
     private Pipeline pipeline;
-    private HTTPSeek seek = null;
+    private HTTPSeekRequest seek = null;
     private GstSink sink;
     private uint bus_watch_id;
 
-    public GstDataSource (string uri) throws Error {
+    public GstDataSource (string uri, MediaResource ? resource) throws Error {
+        this.res = resource;
         this.src = GstUtils.create_source_for_uri (uri);
         if (this.src == null) {
             var msg = _("Could not create GstElement for URI %s");
@@ -56,8 +66,44 @@ internal class Rygel.GstDataSource : Rygel.DataSource, GLib.Object {
         this.src = element;
     }
 
-    public void start (HTTPSeek? offsets) throws Error {
-        this.seek = offsets;
+    public Gee.List<HTTPResponseElement> ? preroll ( HTTPSeekRequest? seek_request,
+                                                     DLNAPlaySpeedRequest? playspeed_request)
+       throws Error {
+        var response_list = new Gee.ArrayList<HTTPResponseElement>();
+
+        if (playspeed_request != null) {
+            throw new DataSourceError.PLAYSPEED_FAILED
+                                    (_("Playspeed not supported"));
+        }
+
+        if (seek_request == null) {
+            debug("No seek requested - sending entire binary");
+        } else if (seek_request is HTTPByteSeekRequest) {
+            var seek_response = new HTTPByteSeekResponse.from_request( seek_request
+                                                                       as HTTPByteSeekRequest );
+            debug("Processing byte seek request for bytes %lld-%lld",
+                    seek_response.start_byte, seek_response.end_byte);
+            response_list.add(seek_response);
+            // Supported - and no reponse values required...
+        } else if (seek is HTTPTimeSeekRequest) {
+            var time_seek = seek_request as HTTPTimeSeekRequest;
+            // Set the effective TimeSeekRange response range to the requested range
+            // TODO: Align this with actual time range being returned
+            var seek_response = new HTTPTimeSeekResponse.from_request(time_seek, res.duration);
+            debug("Processing time seek request for %lldns-%lldns",
+                    seek_response.start_time, seek_response.end_time);
+            response_list.add(seek_response);
+        } else {
+            // Unknown/unsupported seek type
+            throw new DataSourceError.SEEK_FAILED
+                                    (_("HTTPSeekRequest type unsupported"));
+        }
+            
+        this.seek = seek_request;
+        return response_list;
+    }
+    
+    public void start () throws Error {
         this.prepare_pipeline ("RygelGstDataSource", this.src);
         if (this.seek != null) {
             this.pipeline.set_state (State.PAUSED);
@@ -215,29 +261,35 @@ internal class Rygel.GstDataSource : Rygel.DataSource, GLib.Object {
     }
 
     private bool perform_seek () {
-        if (this.seek != null &&
-            this.seek.length >= this.seek.total_length) {
-            return true;
-        }
-
         var stop_type = Gst.SeekType.NONE;
         Format format;
         var flags = SeekFlags.FLUSH;
         int64 start, stop;
 
-        if (this.seek.seek_type == HTTPSeekType.TIME) {
+        if (this.seek is HTTPTimeSeekRequest) {
+            var time_seek = this.seek as HTTPTimeSeekRequest;
             format = Format.TIME;
             flags |= SeekFlags.KEY_UNIT;
-            start = (this.seek.start) * Gst.USECOND;
-            stop = (this.seek.stop) * Gst.USECOND;
-        } else {
+            start = time_seek.start_time * Gst.USECOND;
+            stop = time_seek.end_time * Gst.USECOND;
+            debug("Performing time-range seek: %lldns to %lldns", start, stop);
+        } else if (this.seek is HTTPByteSeekRequest) {
+            var byte_seek = this.seek as HTTPByteSeekRequest;
+            if (byte_seek.range_length >= byte_seek.total_size) {
+                // How/why would this happen? 
+                return true;
+            }
             format = Format.BYTES;
             flags |= SeekFlags.ACCURATE;
-            start = this.seek.start;
-            stop = this.seek.stop;
+            start = byte_seek.start_byte;
+            stop = byte_seek.end_byte;
+            debug("Performing byte-range seek: bytes %lld to %lld", start, stop);
+        } else {
+            this.error (new DataSourceError.SEEK_FAILED (_("Unsupported seek type")));
+            return false;
         }
 
-        if (this.seek.stop > 0) {
+        if (stop > 0) {
             stop_type = Gst.SeekType.SET;
         }
 
@@ -249,8 +301,7 @@ internal class Rygel.GstDataSource : Rygel.DataSource, GLib.Object {
                                  stop_type,
                                  stop + 1)) {
             warning (_("Failed to seek to offsets %lld:%lld"),
-                     this.seek.start,
-                     this.seek.stop);
+                     start, stop);
 
             this.error (new DataSourceError.SEEK_FAILED (_("Failed to seek")));
 
diff --git a/src/media-engines/gstreamer/rygel-gst-media-engine.vala b/src/media-engines/gstreamer/rygel-gst-media-engine.vala
index 1486737..32fd0ad 100644
--- a/src/media-engines/gstreamer/rygel-gst-media-engine.vala
+++ b/src/media-engines/gstreamer/rygel-gst-media-engine.vala
@@ -20,6 +20,14 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Craig Pratt <craig@ecaspia.com>
+ */
+
 using Gst;
 using Gee;
 
@@ -108,17 +116,27 @@ public class Rygel.GstMediaEngine : Rygel.MediaEngine {
         }
     }
 
-    public override unowned GLib.List<DLNAProfile> get_dlna_profiles () {
+    public override unowned GLib.List<DLNAProfile> get_dlna_profiles() {
         return this.dlna_profiles;
     }
 
+    public override Gee.List<MediaResource>? get_resources_for_uri(string uri) {
+        // TODO: Implement me
+        return null;
+    }
+
     public override unowned GLib.List<Transcoder>? get_transcoders () {
         return this.transcoders;
     }
 
-    public override DataSource? create_data_source (string uri) {
+    public override DataSource? create_data_source_for_resource
+                                (string uri, MediaResource ? resource) {
         try {
-            return new GstDataSource (uri);
+            DataSource ds = new GstDataSource (uri, resource);
+            debug("creating data source for %s", uri);
+            debug("MediaResource %s, profile %s, mime_type %s", resource.get_name(),
+                  resource.protocol_info.dlna_profile, resource.protocol_info.mime_type);
+            return ds;
         } catch (Error error) {
             warning (_("Failed to create GStreamer data source for %s: %s"),
                      uri,
diff --git a/src/media-engines/gstreamer/rygel-gst-sink.vala b/src/media-engines/gstreamer/rygel-gst-sink.vala
index acdaefe..e672b6b 100644
--- a/src/media-engines/gstreamer/rygel-gst-sink.vala
+++ b/src/media-engines/gstreamer/rygel-gst-sink.vala
@@ -23,6 +23,14 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Craig Pratt <craig@ecaspia.com>
+ */
+
 using Gst;
 using Gst.Base;
 
@@ -43,7 +51,7 @@ internal class Rygel.GstSink : Sink {
     private Mutex buffer_mutex = Mutex ();
     private Cond buffer_condition = Cond ();
     private unowned DataSource source;
-    private HTTPSeek offsets;
+    private HTTPSeekRequest offsets;
 
     private bool frozen;
 
@@ -56,7 +64,7 @@ internal class Rygel.GstSink : Sink {
         add_pad_template (template);
     }
 
-    public GstSink (DataSource source, HTTPSeek? offsets) {
+    public GstSink (DataSource source, HTTPSeekRequest? offsets) {
         this.bytes_sent = 0;
         this.max_bytes = int64.MAX;
         this.source = source;
@@ -69,8 +77,8 @@ internal class Rygel.GstSink : Sink {
         this.frozen = false;
 
         if (this.offsets != null) {
-            if (this.offsets.seek_type == HTTPSeekType.BYTE) {
-                this.max_bytes = this.offsets.length;
+            if (this.offsets is HTTPByteSeekRequest) {
+                this.max_bytes = (this.offsets as HTTPByteSeekRequest).total_size;
             }
         }
 
diff --git a/src/media-engines/simple/rygel-simple-data-source.vala b/src/media-engines/simple/rygel-simple-data-source.vala
index d0ccbd4..b68bbcc 100644
--- a/src/media-engines/simple/rygel-simple-data-source.vala
+++ b/src/media-engines/simple/rygel-simple-data-source.vala
@@ -20,6 +20,14 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Craig Pratt <craig@ecaspia.com>
+ */
+
 /**
  * A simple data source for use with the simple media engine (RygelSimpleMediaEngine).
  *
@@ -38,7 +46,6 @@ internal class Rygel.SimpleDataSource : DataSource, Object {
     private uint64 last_byte = 0;
     private bool frozen = false;
     private bool stop_thread = false;
-    private HTTPSeek offsets = null;
 
     public SimpleDataSource (string uri) {
         debug ("Creating new data source for %s", uri);
@@ -49,17 +56,40 @@ internal class Rygel.SimpleDataSource : DataSource, Object {
         this.stop ();
     }
 
-    public void start (HTTPSeek? offsets) throws Error {
-        if (offsets != null) {
-            if (offsets.seek_type == HTTPSeekType.TIME) {
+    public Gee.List<HTTPResponseElement> ? preroll ( HTTPSeekRequest? seek_request,
+                                                     DLNAPlaySpeedRequest? playspeed_request)
+       throws Error {
+        var response_list = new Gee.ArrayList<HTTPResponseElement>();
+
+        if (seek_request != null) {
+            if (!(seek_request is HTTPByteSeekRequest)) {
                 throw new DataSourceError.SEEK_FAILED
-                                        (_("Time-based seek not supported"));
+                                        (_("Only byte-based seek supported"));
 
             }
+
+            var byte_seek = seek_request as HTTPByteSeekRequest;
+            this.first_byte = byte_seek.start_byte;
+            this.last_byte = byte_seek.end_byte + 1;
+            debug("Processing byte seek request for bytes %lld-%lld of %s",
+                    byte_seek.start_byte, byte_seek.end_byte, this.uri);
+            var seek_response = new HTTPByteSeekResponse.from_request(byte_seek);
+            // Response will just return what was in the request
+            response_list.add(seek_response);
+        } else {
+            this.first_byte = 0;
+            this.last_byte = 0; // Indicates the entire file
         }
 
-        this.offsets = offsets;
+        if (playspeed_request != null) {
+            throw new DataSourceError.PLAYSPEED_FAILED
+                                    (_("Playspeed not supported"));
+        }
 
+        return response_list;
+    }
+
+    public void start () throws Error {
         debug ("Starting data source for uri %s", this.uri);
 
         // TODO: Convert to use a thread pool
@@ -102,16 +132,14 @@ internal class Rygel.SimpleDataSource : DataSource, Object {
 
     private void* thread_func () {
         var file = File.new_for_commandline_arg (this.uri);
-        debug ("Spawning new thread for streaming file %s", this.uri);
+        debug ("Thread started - sending bytes %lld-%lld (%lld bytes) of %s",
+                 this.first_byte, this.last_byte,this.last_byte-this.first_byte, this.uri);
         try {
             var mapped = new MappedFile (file.get_path (), false);
-            if (this.offsets != null) {
-                this.first_byte = this.offsets.start;
-                this.last_byte = this.offsets.stop + 1;
-            } else {
+            if (this.last_byte == 0) {
                 this.last_byte = mapped.get_length ();
             }
-
+            
             while (true) {
                 bool exit;
                 this.mutex.lock ();
diff --git a/src/media-engines/simple/rygel-simple-media-engine.vala b/src/media-engines/simple/rygel-simple-media-engine.vala
index 512a9f2..9661726 100644
--- a/src/media-engines/simple/rygel-simple-media-engine.vala
+++ b/src/media-engines/simple/rygel-simple-media-engine.vala
@@ -20,6 +20,14 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Craig Pratt <craig@ecaspia.com>
+ */
+
 /**
  * The simple media engine does not use GStreamer or any other
  * multimedia framework. Therefore its capabilities are limited.
@@ -32,19 +40,25 @@ internal class Rygel.SimpleMediaEngine : MediaEngine {
 
     public SimpleMediaEngine () { }
 
-    public override unowned List<DLNAProfile> get_dlna_profiles () {
+    public override unowned List<DLNAProfile> get_dlna_profiles() {
         return this.profiles;
     }
 
+    public override Gee.List<MediaResource>? get_resources_for_uri(string uri) {
+        // TODO: Implement me
+        return null;
+    }
+
     public override unowned List<Transcoder>? get_transcoders () {
         return null;
     }
 
-    public override DataSource? create_data_source (string uri) {
+    public override DataSource? create_data_source_for_resource
+                                (string uri, MediaResource ? resource) {
         if (!uri.has_prefix ("file://")) {
             return null;
         }
-
+        debug("creating data source for %s", uri);
         return new SimpleDataSource (uri);
     }
 }
diff --git a/src/plugins/gst-launch/rygel-gst-launch-audio-item.vala b/src/plugins/gst-launch/rygel-gst-launch-audio-item.vala
index ed696f8..dcfeacb 100644
--- a/src/plugins/gst-launch/rygel-gst-launch-audio-item.vala
+++ b/src/plugins/gst-launch/rygel-gst-launch-audio-item.vala
@@ -25,6 +25,14 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Craig Pratt <craig@ecaspia.com>
+ */
+
 /**
  * Audio item that serves data from a gst-launch commandline.
  */
@@ -42,7 +50,8 @@ public class Rygel.GstLaunch.AudioItem : Rygel.AudioItem, Item {
         this.launch_line = launch_line;
     }
 
-    public override DataSource? create_stream_source (string? host_ip) {
+    public override DataSource? create_stream_source_for_resource (string? host_ip = null,
+                                                                  MediaResource? resource = null) {
         return this.create_source ();
     }
 }
diff --git a/src/plugins/gst-launch/rygel-gst-launch-item.vala b/src/plugins/gst-launch/rygel-gst-launch-item.vala
index 965ef92..8b7ee8d 100644
--- a/src/plugins/gst-launch/rygel-gst-launch-item.vala
+++ b/src/plugins/gst-launch/rygel-gst-launch-item.vala
@@ -23,6 +23,14 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Craig Pratt <craig@ecaspia.com>
+ */
+
 /**
  * Item that serves data from a gst-launch commandline.
  */
@@ -32,7 +40,7 @@ public interface Rygel.GstLaunch.Item : Rygel.MediaItem {
     protected DataSource? create_source () {
         var engine = MediaEngine.get_default ();
 
-        return engine.create_data_source ("gst-launch://" + this.launch_line);
+        return engine.create_data_source_for_resource ("gst-launch://" + this.launch_line, null);
     }
 }
 
diff --git a/src/plugins/gst-launch/rygel-gst-launch-video-item.vala b/src/plugins/gst-launch/rygel-gst-launch-video-item.vala
index be29c61..35163a6 100644
--- a/src/plugins/gst-launch/rygel-gst-launch-video-item.vala
+++ b/src/plugins/gst-launch/rygel-gst-launch-video-item.vala
@@ -25,6 +25,14 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Craig Pratt <craig@ecaspia.com>
+ */
+
 /**
  * Video item that serves data from a gst-launch commandline.
  */
@@ -42,7 +50,8 @@ public class Rygel.GstLaunch.VideoItem : Rygel.VideoItem, Item {
         this.launch_line = launch_line;
     }
 
-    public override DataSource? create_stream_source (string? host_ip) {
+    public override DataSource? create_stream_source_for_resource (string? host_ip = null,
+                                                                  MediaResource? resource = null) {
         return this.create_source ();
     }
 }
diff --git a/src/plugins/media-export/rygel-media-export-media-cache.vala b/src/plugins/media-export/rygel-media-export-media-cache.vala
index e4c4eb7..0ff7435 100644
--- a/src/plugins/media-export/rygel-media-export-media-cache.vala
+++ b/src/plugins/media-export/rygel-media-export-media-cache.vala
@@ -21,6 +21,14 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+/*
+ * Modifications made by Cable Television Laboratories, Inc.
+ * Copyright (C) 2013  Cable Television Laboratories, Inc.
+ * Contact: http://www.cablelabs.com/
+ *
+ * Author: Doug Galligan <doug@sentosatech.com>>
+ * Author: Craig Pratt <craig@ecaspia.com>
+ */
 
 using Gee;
 using GUPnP;
@@ -930,21 +938,55 @@ public class Rygel.MediaExport.MediaCache : Object {
     private void fill_item (Statement statement, MediaItem item) {
         // Fill common properties
         item.date = statement.column_text (DetailColumn.DATE);
-        item.mime_type = statement.column_text (DetailColumn.MIME_TYPE);
-        item.dlna_profile = statement.column_text (DetailColumn.DLNA_PROFILE);
-        item.size = statement.column_int64 (DetailColumn.SIZE);
         item.creator = statement.column_text (DetailColumn.CREATOR);
 
+        // Fill primary MediaResource on the MediaItem.
+        item.media_resources = new Gee.ArrayList<MediaResource>();
+        // TODO: Get the resources from a MediaResource table (e.g. the "Resource" table in ODID
+        //       MediaServer) (Will be included in next patch). Resource name, mime_type, size,
+        //       operational modes, protocol, etc. will all come from this table and contained
+        //       in MediaResource objects.
+        MediaResource res = new MediaResource("primary");
+        
+        res.protocol_info = new ProtocolInfo();
+        item.mime_type = statement.column_text (DetailColumn.MIME_TYPE);
+        res.protocol_info.mime_type = statement.column_text (DetailColumn.MIME_TYPE);
+        if (res.protocol_info.mime_type == null) {
+            message("Found null mime-type for %s", statement.column_text (DetailColumn.URI));
+            res.protocol_info.mime_type = "application/octet-stream";
+        }
+        res.protocol_info.dlna_profile = statement.column_text (DetailColumn.DLNA_PROFILE);
+        res.size = statement.column_int64 (DetailColumn.SIZE);
+        res.uri = ""; // To avoid assertion errors.
+        res.protocol_info.dlna_operation = DLNAOperation.RANGE;
+        
+        // TODO: Some protocolInfo elements speak to the capabilities of the MediaServer/DataSink.
+        // Should these be set by interrogating the HTTPServer? 
+        res.protocol_info.protocol = "http-get";
+        res.protocol_info.dlna_flags |= DLNAFlags.DLNA_V15
+                                        | DLNAFlags.STREAMING_TRANSFER_MODE
+                                        | DLNAFlags.BACKGROUND_TRANSFER_MODE
+                                        | DLNAFlags.CONNECTION_STALL;
         if (item is AudioItem) {
-            var audio_item = item as AudioItem;
-            audio_item.duration = (long) statement.column_int64
-                                        (DetailColumn.DURATION);
-            audio_item.bitrate = statement.column_int (DetailColumn.BITRATE);
-            audio_item.sample_freq = statement.column_int
-                                        (DetailColumn.SAMPLE_FREQ);
-            audio_item.bits_per_sample = statement.column_int
-                                        (DetailColumn.BITS_PER_SAMPLE);
-            audio_item.channels = statement.column_int (DetailColumn.CHANNELS);
+            res.duration = (long) statement.column_int64 (DetailColumn.DURATION);
+            res.bitrate = statement.column_int (DetailColumn.BITRATE);
+            res.sample_freq = statement.column_int (DetailColumn.SAMPLE_FREQ);
+            res.bits_per_sample = statement.column_int (DetailColumn.BITS_PER_SAMPLE);
+            res.audio_channels = statement.column_int (DetailColumn.CHANNELS);
+
+            // Find extension from URI in DB.
+            string uri_extension = "";
+            string basename = Path.get_basename (statement.column_text (DetailColumn.URI));
+            int dot_index = -1;
+            if (basename != null)   
+            {
+                dot_index = basename.last_index_of(".");
+                if (dot_index > -1) {
+                    uri_extension = basename.substring (dot_index + 1);
+                }
+            }
+            res.extension = uri_extension;
+
             if (item is MusicItem) {
                 var music_item = item as MusicItem;
                 music_item.artist = statement.column_text (DetailColumn.AUTHOR);
@@ -957,12 +999,12 @@ public class Rygel.MediaExport.MediaCache : Object {
         }
 
         if (item is VisualItem) {
-            var visual_item = item as VisualItem;
-            visual_item.width = statement.column_int (DetailColumn.WIDTH);
-            visual_item.height = statement.column_int (DetailColumn.HEIGHT);
-            visual_item.color_depth = statement.column_int
-                                        (DetailColumn.COLOR_DEPTH);
+            res.width = statement.column_int (DetailColumn.WIDTH);
+            res.height = statement.column_int (DetailColumn.HEIGHT);
+            res.color_depth = statement.column_int (DetailColumn.COLOR_DEPTH);
         }
+        // Add the single/primary resource to the item
+        item.media_resources.add(res);
     }
 
     private static string translate_search_expression
