[rygel] core: Add basic support for subtitles
- From: Zeeshan Ali Khattak <zeeshanak src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [rygel] core: Add basic support for subtitles
- Date: Wed, 7 Apr 2010 23:29:23 +0000 (UTC)
commit ec9c6b677889a9cae11c066f260aa56a2086ea3a
Author: Andreas Henriksson <andreas fatal se>
Date: Thu Apr 8 02:19:47 2010 +0300
core: Add basic support for subtitles
For each video item, we check if there is a file in the same directory
with the same name but extension "srt" and if so, we serve it using
Samsung's custom DIDL-Lite and HTTP extensions.
Co-author & reviewer: Zeeshan Ali (Khattak) <zeeshanak gnome org>
src/rygel/Makefile.am | 2 +
src/rygel/rygel-http-byte-seek.vala | 5 ++-
src/rygel/rygel-http-get-handler.vala | 14 +++++
src/rygel/rygel-http-get.vala | 5 ++
src/rygel/rygel-http-identity-handler.vala | 14 ++++-
src/rygel/rygel-http-item-uri.vala | 10 ++++
src/rygel/rygel-http-server.vala | 26 +++++++++-
src/rygel/rygel-media-item.vala | 23 ++++++++
src/rygel/rygel-subtitle-manager.vala | 76 ++++++++++++++++++++++++++++
src/rygel/rygel-subtitle.vala | 54 ++++++++++++++++++++
src/rygel/rygel-transcode-manager.vala | 1 +
src/rygel/rygel-transcoder.vala | 1 +
12 files changed, 226 insertions(+), 5 deletions(-)
---
diff --git a/src/rygel/Makefile.am b/src/rygel/Makefile.am
index b093db1..28afb3e 100644
--- a/src/rygel/Makefile.am
+++ b/src/rygel/Makefile.am
@@ -75,6 +75,8 @@ VAPI_SOURCE_FILES = rygel-configuration.vala \
rygel-media-item.vala \
rygel-thumbnail.vala \
rygel-thumbnailer.vala \
+ rygel-subtitle.vala \
+ rygel-subtitle-manager.vala \
rygel-browse.vala \
rygel-search.vala \
rygel-xbox-hacks.vala \
diff --git a/src/rygel/rygel-http-byte-seek.vala b/src/rygel/rygel-http-byte-seek.vala
index 08257b8..51ea055 100644
--- a/src/rygel/rygel-http-byte-seek.vala
+++ b/src/rygel/rygel-http-byte-seek.vala
@@ -29,6 +29,8 @@ internal class Rygel.HTTPByteSeek : Rygel.HTTPSeek {
if (request.thumbnail != null) {
total_length = request.thumbnail.size;
+ } else if (request.subtitle != null) {
+ total_length = request.subtitle.size;
} else {
total_length = request.item.size;
}
@@ -80,7 +82,8 @@ internal class Rygel.HTTPByteSeek : Rygel.HTTPSeek {
public static bool needed (HTTPGet request) {
return (request.item.size > 0 &&
request.handler is HTTPIdentityHandler) ||
- (request.thumbnail != null && request.thumbnail.size > 0);
+ (request.thumbnail != null && request.thumbnail.size > 0) ||
+ (request.subtitle != null && request.subtitle.size > 0);
}
public override void add_response_headers () {
diff --git a/src/rygel/rygel-http-get-handler.vala b/src/rygel/rygel-http-get-handler.vala
index fe9c354..4beeea5 100644
--- a/src/rygel/rygel-http-get-handler.vala
+++ b/src/rygel/rygel-http-get-handler.vala
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2008-2010 Nokia Corporation.
+ * Copyright (C) 2010 Andreas Henriksson <andreas fatal se>
*
* Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
* <zeeshan ali nokia com>
@@ -56,6 +57,19 @@ internal abstract class Rygel.HTTPGetHandler: GLib.Object {
warning ("Received request for 'contentFeatures.dlna.org' but " +
"failed to provide the value in response headers");
}
+
+ // Handle Samsung DLNA TV proprietary subtitle headers
+ if (request.msg.request_headers.get ("getCaptionInfo.sec") != null &&
+ request.item.subtitles.size > 0) {
+ var caption_uri = request.http_server.create_uri_for_item (
+ request.item,
+ -1,
+ 0, // FIXME: offer first subtitle only?
+ null);
+
+ request.msg.response_headers.append ("CaptionInfo.sec",
+ caption_uri);
+ }
}
// Create an HTTPResponse object that will render the body.
diff --git a/src/rygel/rygel-http-get.vala b/src/rygel/rygel-http-get.vala
index ecd0a07..e3c5ee8 100644
--- a/src/rygel/rygel-http-get.vala
+++ b/src/rygel/rygel-http-get.vala
@@ -30,9 +30,11 @@ using Gst;
*/
internal class Rygel.HTTPGet : HTTPRequest {
public Thumbnail thumbnail;
+ public Subtitle subtitle;
public HTTPSeek seek;
private int thumbnail_index;
+ private int subtitle_index;
public HTTPGetHandler handler;
@@ -42,6 +44,7 @@ internal class Rygel.HTTPGet : HTTPRequest {
base (http_server, server, msg);
this.thumbnail_index = -1;
+ this.subtitle_index = -1;
}
protected override async void handle () {
@@ -84,6 +87,8 @@ internal class Rygel.HTTPGet : HTTPRequest {
if (this.uri.thumbnail_index >= 0) {
this.thumbnail = this.item.thumbnails.get (
this.uri.thumbnail_index);
+ } else if (this.uri.subtitle_index >= 0) {
+ this.subtitle = this.item.subtitles.get (this.uri.subtitle_index);
}
}
diff --git a/src/rygel/rygel-http-identity-handler.vala b/src/rygel/rygel-http-identity-handler.vala
index 9db8625..640b1f5 100644
--- a/src/rygel/rygel-http-identity-handler.vala
+++ b/src/rygel/rygel-http-identity-handler.vala
@@ -32,7 +32,10 @@ internal class Rygel.HTTPIdentityHandler : Rygel.HTTPGetHandler {
public override void add_response_headers (HTTPGet request)
throws HTTPRequestError {
- if (request.thumbnail != null) {
+ if (request.subtitle != null) {
+ request.msg.response_headers.append ("Content-Type",
+ request.subtitle.mime_type);
+ } else if (request.thumbnail != null) {
request.msg.response_headers.append ("Content-Type",
request.thumbnail.mime_type);
} else {
@@ -70,7 +73,14 @@ internal class Rygel.HTTPIdentityHandler : Rygel.HTTPGetHandler {
}
private HTTPResponse render_body_real (HTTPGet request) throws Error {
- if (request.thumbnail != null) {
+ if (request.subtitle != null) {
+ return new SeekableResponse (request.server,
+ request.msg,
+ request.subtitle.uri,
+ request.seek,
+ request.subtitle.size,
+ this.cancellable);
+ } else if (request.thumbnail != null) {
return new SeekableResponse (request.server,
request.msg,
request.thumbnail.uri,
diff --git a/src/rygel/rygel-http-item-uri.vala b/src/rygel/rygel-http-item-uri.vala
index ca92df4..7cf8980 100644
--- a/src/rygel/rygel-http-item-uri.vala
+++ b/src/rygel/rygel-http-item-uri.vala
@@ -26,15 +26,18 @@
internal class Rygel.HTTPItemURI : Object {
public string item_id;
public int thumbnail_index;
+ public int subtitle_index;
public string? transcode_target;
public HTTPServer http_server;
public HTTPItemURI (string item_id,
HTTPServer http_server,
int thumbnail_index = -1,
+ int subtitle_index = -1,
string? transcode_target = null) {
this.item_id = item_id;
this.thumbnail_index = thumbnail_index;
+ this.subtitle_index = subtitle_index;
this.transcode_target = transcode_target;
this.http_server = http_server;
}
@@ -44,6 +47,7 @@ internal class Rygel.HTTPItemURI : Object {
throws HTTPRequestError {
// do not decode the path here as it may contain encoded slashes
this.thumbnail_index = -1;
+ this.subtitle_index = -1;
this.transcode_target = null;
this.http_server = http_server;
@@ -72,6 +76,10 @@ internal class Rygel.HTTPItemURI : Object {
this.thumbnail_index = parts[i + 1].to_int ();
break;
+ case "subtitle":
+ this.subtitle_index = parts[i + 1].to_int ();
+
+ break;
default:
break;
}
@@ -97,6 +105,8 @@ internal class Rygel.HTTPItemURI : Object {
path += "/transcoded/" + escaped;
} else if (this.thumbnail_index >= 0) {
path += "/thumbnail/" + this.thumbnail_index.to_string ();
+ } else if (this.subtitle_index >= 0) {
+ path += "/subtitle/" + this.subtitle_index.to_string ();
}
return this.create_uri_for_path (path);
diff --git a/src/rygel/rygel-http-server.vala b/src/rygel/rygel-http-server.vala
index 9b46441..a16d904 100644
--- a/src/rygel/rygel-http-server.vala
+++ b/src/rygel/rygel-http-server.vala
@@ -66,6 +66,23 @@ internal class Rygel.HTTPServer : Rygel.TranscodeManager, Rygel.StateMachine {
internal override void add_resources (DIDLLiteItem didl_item,
MediaItem item)
throws Error {
+ // Subtitles first
+ foreach (var subtitle in item.subtitles) {
+ if (!is_http_uri (subtitle.uri)) {
+ var uri = subtitle.uri; // Save the original URI
+ var index = item.subtitles.index_of (subtitle);
+
+ subtitle.uri = this.create_uri_for_item (item,
+ -1,
+ index,
+ null);
+ subtitle.add_didl_node (didl_item);
+
+ // Now restore the original URI
+ subtitle.uri = uri;
+ }
+ }
+
if (!this.http_uri_present (item)) {
this.add_proxy_resource (didl_item, item);
}
@@ -78,7 +95,10 @@ internal class Rygel.HTTPServer : Rygel.TranscodeManager, Rygel.StateMachine {
var uri = thumbnail.uri; // Save the original URI
var index = item.thumbnails.index_of (thumbnail);
- thumbnail.uri = this.create_uri_for_item (item, index, null);
+ thumbnail.uri = this.create_uri_for_item (item,
+ index,
+ -1,
+ null);
thumbnail.add_resource (didl_item, this.get_protocol ());
// Now restore the original URI
@@ -90,7 +110,7 @@ internal class Rygel.HTTPServer : Rygel.TranscodeManager, Rygel.StateMachine {
internal void add_proxy_resource (DIDLLiteItem didl_item,
MediaItem item)
throws Error {
- var uri = this.create_uri_for_item (item, -1, null);
+ var uri = this.create_uri_for_item (item, -1, -1, null);
item.add_resource (didl_item,
uri.to_string (),
@@ -127,10 +147,12 @@ internal class Rygel.HTTPServer : Rygel.TranscodeManager, Rygel.StateMachine {
internal override string create_uri_for_item (MediaItem item,
int thumbnail_index,
+ int subtitle_index,
string? transcode_target) {
var uri = new HTTPItemURI (item.id,
this,
thumbnail_index,
+ subtitle_index,
transcode_target);
return uri.to_string ();
diff --git a/src/rygel/rygel-media-item.vala b/src/rygel/rygel-media-item.vala
index 68b9d70..85e0eac 100644
--- a/src/rygel/rygel-media-item.vala
+++ b/src/rygel/rygel-media-item.vala
@@ -64,6 +64,7 @@ public class Rygel.MediaItem : MediaObject {
public int color_depth = -1;
public ArrayList<Thumbnail> thumbnails;
+ public ArrayList<Subtitle> subtitles;
internal bool place_holder = false;
@@ -77,6 +78,7 @@ public class Rygel.MediaItem : MediaObject {
this.upnp_class = upnp_class;
this.thumbnails = new ArrayList<Thumbnail> ();
+ this.subtitles = new ArrayList<Subtitle> ();
}
// Live media items need to provide a nice working implementation of this
@@ -127,6 +129,19 @@ public class Rygel.MediaItem : MediaObject {
this.thumbnails.add (thumb);
} catch (Error err) {}
}
+
+ if (this.upnp_class.has_prefix (MediaItem.VIDEO_CLASS)) {
+ var subtitle_manager = SubtitleManager.get_default ();
+
+ if (subtitle_manager == null) {
+ return;
+ }
+
+ try {
+ var subtitle = subtitle_manager.get_subtitle (uri);
+ this.subtitles.add (subtitle);
+ } catch (Error err) {}
+ }
}
internal int compare_transcoders (void *a, void *b) {
@@ -140,6 +155,14 @@ public class Rygel.MediaItem : MediaObject {
internal void add_resources (DIDLLiteItem didl_item,
bool allow_internal)
throws Error {
+ foreach (var subtitle in this.subtitles) {
+ var protocol = this.get_protocol_for_uri (subtitle.uri);
+
+ if (allow_internal || protocol != "internal") {
+ subtitle.add_didl_node (didl_item);
+ }
+ }
+
foreach (var uri in this.uris) {
var protocol = this.get_protocol_for_uri (uri);
diff --git a/src/rygel/rygel-subtitle-manager.vala b/src/rygel/rygel-subtitle-manager.vala
new file mode 100644
index 0000000..7ecf86d
--- /dev/null
+++ b/src/rygel/rygel-subtitle-manager.vala
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2008 Zeeshan Ali <zeenix gmail com>.
+ * Copyright (C) 2010 Andreas Henriksson <andreas fatal se>.
+ *
+ * Authors: Andreas Henriksson <andreas fatal se>
+ * Zeeshan Ali (Khattak) <zeeshanak gnome org>
+ *
+ * This file is part of Rygel.
+ *
+ * Rygel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Rygel is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+internal errordomain SubtitleManagerError {
+ NO_SUBTITLE
+}
+
+/**
+ * Provides subtitles for vidoes.
+ */
+internal class Rygel.SubtitleManager : GLib.Object {
+ private static SubtitleManager manager; // Our singleton object
+
+ public static SubtitleManager? get_default () {
+ if (manager == null) {
+ manager = new SubtitleManager ();
+ }
+
+ return manager;
+ }
+
+ public Subtitle get_subtitle (string uri) throws Error {
+ var video_file = File.new_for_uri (uri);
+
+ var directory = video_file.get_parent ();
+ var filename = video_file.get_basename ();
+ var extension = filename.rchr (-1, '.');
+ if (extension != null) {
+ filename = filename.substring (0,
+ filename.length - extension.length);
+ }
+ // FIXME: foreach ".eng.srt", ".ger.srt", ".srt"...
+ // FIXME: case insensitive?
+ filename += ".srt";
+
+ var srt_file = directory.get_child (filename);
+
+ var info = srt_file.query_info (FILE_ATTRIBUTE_ACCESS_CAN_READ + "," +
+ FILE_ATTRIBUTE_STANDARD_SIZE,
+ FileQueryInfoFlags.NONE,
+ null);
+
+ if (!info.get_attribute_boolean (FILE_ATTRIBUTE_ACCESS_CAN_READ)) {
+ throw new SubtitleManagerError.NO_SUBTITLE (
+ "No subtitle available");
+ }
+
+ var subtitle = new Subtitle ();
+ subtitle.uri = srt_file.get_uri ();
+ subtitle.size = (long) info.get_attribute_uint64 (
+ FILE_ATTRIBUTE_STANDARD_SIZE);
+
+ return subtitle;
+ }
+}
diff --git a/src/rygel/rygel-subtitle.vala b/src/rygel/rygel-subtitle.vala
new file mode 100644
index 0000000..17d531f
--- /dev/null
+++ b/src/rygel/rygel-subtitle.vala
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2008 Zeeshan Ali <zeenix gmail com>.
+ * Copyright (C) 2010 Andreas Henriksson <andreas fatal se>
+ *
+ * Authors: Andreas Henriksson <andreas fatal se>
+ * Zeeshan Ali (Khattak) <zeeshanak gnome org>
+ *
+ * This file is part of Rygel.
+ *
+ * Rygel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Rygel is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+using GUPnP;
+
+/**
+ * Represents a subtitle for a video.
+ */
+public class Rygel.Subtitle {
+ public string uri;
+ public string mime_type;
+ public string caption_type;
+
+ public long size = -1; // Size in bytes
+
+ public Subtitle (string mime_type = "text/plain",
+ string caption_type = "srt") {
+ this.mime_type = mime_type;
+ this.caption_type = caption_type;
+ }
+
+ internal void add_didl_node (DIDLLiteItem didl_item) {
+ Xml.Node *item_node = didl_item.xml_node;
+ Xml.Node *root_node = item_node->doc->get_root_element ();
+
+ weak Xml.Ns sec_ns = root_node->new_ns ("http://www.sec.co.kr/", "sec");
+ Xml.Node *sec_node = item_node->new_child (sec_ns,
+ "CaptionInfoEx",
+ this.uri);
+
+ sec_node->new_prop ("sec:type", this.caption_type);
+ }
+}
diff --git a/src/rygel/rygel-transcode-manager.vala b/src/rygel/rygel-transcode-manager.vala
index cedecfb..4fed0fe 100644
--- a/src/rygel/rygel-transcode-manager.vala
+++ b/src/rygel/rygel-transcode-manager.vala
@@ -58,6 +58,7 @@ internal abstract class Rygel.TranscodeManager : GLib.Object {
public abstract string create_uri_for_item (MediaItem item,
int thumbnail_index,
+ int subtitle_index,
string? transcode_target);
public virtual void add_resources (DIDLLiteItem didl_item,
diff --git a/src/rygel/rygel-transcoder.vala b/src/rygel/rygel-transcoder.vala
index 16b6ca8..bc3e13a 100644
--- a/src/rygel/rygel-transcoder.vala
+++ b/src/rygel/rygel-transcoder.vala
@@ -67,6 +67,7 @@ internal abstract class Rygel.Transcoder : GLib.Object {
var protocol = manager.get_protocol ();
var uri = manager.create_uri_for_item (item,
-1,
+ -1,
this.dlna_profile);
var res = item.add_resource (didl_item, uri, protocol);
res.size = -1;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]