[rygel] core: Add WMV transcoder



commit 500c15dab6bc540c8718b23255930abcb817c746
Author: Jens Georg <mail jensge org>
Date:   Tue Dec 29 14:51:09 2009 +0100

    core: Add WMV transcoder

 src/rygel/Makefile.am                   |    4 +
 src/rygel/rygel-cmdline-config.vala     |    7 ++
 src/rygel/rygel-configuration.vala      |    2 +
 src/rygel/rygel-meta-config.vala        |   19 ++++++
 src/rygel/rygel-transcode-manager.vala  |    4 +
 src/rygel/rygel-user-config.vala        |    9 +++
 src/rygel/rygel-wma-transcoder-bin.vala |   66 +++++++++++++++++++
 src/rygel/rygel-wma-transcoder.vala     |  102 ++++++++++++++++++++++++++++++
 src/rygel/rygel-wmv-transcoder-bin.vala |   94 +++++++++++++++++++++++++++
 src/rygel/rygel-wmv-transcoder.vala     |  105 +++++++++++++++++++++++++++++++
 10 files changed, 412 insertions(+), 0 deletions(-)
---
diff --git a/src/rygel/Makefile.am b/src/rygel/Makefile.am
index 395a8a3..ccd19b0 100644
--- a/src/rygel/Makefile.am
+++ b/src/rygel/Makefile.am
@@ -91,6 +91,10 @@ VAPI_SOURCE_FILES = rygel-configuration.vala \
 		    rygel-mp2ts-transcoder-bin.vala \
 		    rygel-mp3-transcoder-bin.vala \
 		    rygel-l16-transcoder-bin.vala \
+		    rygel-wma-transcoder.vala \
+		    rygel-wma-transcoder-bin.vala \
+		    rygel-wmv-transcoder.vala \
+		    rygel-wmv-transcoder-bin.vala \
 		    rygel-gst-utils.vala \
 		    rygel-media-db.vala \
 		    rygel-metadata-extractor.vala \
diff --git a/src/rygel/rygel-cmdline-config.vala b/src/rygel/rygel-cmdline-config.vala
index 3da94fa..7ee7541 100644
--- a/src/rygel/rygel-cmdline-config.vala
+++ b/src/rygel/rygel-cmdline-config.vala
@@ -40,6 +40,7 @@ public class Rygel.CmdlineConfig : GLib.Object, Configuration {
     private static bool no_mp3_trans;
     private static bool no_mp2ts_trans;
     private static bool no_lpcm_trans;
+    private static bool no_wmv_trans;
 
     private static LogLevel log_level = LogLevel.INVALID;
 
@@ -75,6 +76,8 @@ public class Rygel.CmdlineConfig : GLib.Object, Configuration {
           "Disable mpeg2 transport stream transcoder", null },
         { "disable-lpcm-transcoder", 'l', 0, OptionArg.NONE, ref no_lpcm_trans,
           "Disable Linear PCM transcoder", null },
+        { "disable-wmv-transcoder", 'l', 0, OptionArg.NONE, ref no_wmv_trans,
+          "Disable WMV transcoder", null },
         { "log-level", 'g', 0, OptionArg.INT, ref log_level,
           "Log level. 1=critical,2=error,3=warning,4=message/info,5=debug",
           "N" },
@@ -165,6 +168,10 @@ public class Rygel.CmdlineConfig : GLib.Object, Configuration {
         }
     }
 
+    public bool get_wmv_transcoder () throws GLib.Error {
+        return !no_wmv_trans;
+    }
+
     public LogLevel get_log_level () throws GLib.Error {
         if (this.log_level == LogLevel.INVALID) {
             throw new ConfigurationError.NO_VALUE_SET ("No value available");
diff --git a/src/rygel/rygel-configuration.vala b/src/rygel/rygel-configuration.vala
index bb9937d..a27a7ab 100644
--- a/src/rygel/rygel-configuration.vala
+++ b/src/rygel/rygel-configuration.vala
@@ -47,6 +47,8 @@ public interface Rygel.Configuration : GLib.Object {
 
     public abstract bool get_lpcm_transcoder () throws GLib.Error;
 
+    public abstract bool get_wmv_transcoder () throws GLib.Error;
+
     public abstract LogLevel get_log_level () throws GLib.Error;
 
     public abstract bool get_enabled (string section) throws GLib.Error;
diff --git a/src/rygel/rygel-meta-config.vala b/src/rygel/rygel-meta-config.vala
index 90e2d4e..34390ee 100644
--- a/src/rygel/rygel-meta-config.vala
+++ b/src/rygel/rygel-meta-config.vala
@@ -191,6 +191,25 @@ public class Rygel.MetaConfig : GLib.Object, Configuration {
         return val;
     }
 
+    public bool get_wmv_transcoder () throws GLib.Error {
+        bool val = true;
+        bool unavailable = true;
+
+        foreach (var config in this.configs) {
+            try {
+                val = config.get_wmv_transcoder ();
+                unavailable = false;
+                break;
+            } catch (GLib.Error err) {}
+        }
+
+        if (unavailable) {
+            throw new ConfigurationError.NO_VALUE_SET ("No value available");
+        }
+
+        return val;
+    }
+
     public LogLevel get_log_level () throws GLib.Error {
         LogLevel val = LogLevel.DEFAULT;
         bool unavailable = true;
diff --git a/src/rygel/rygel-transcode-manager.vala b/src/rygel/rygel-transcode-manager.vala
index 055a875..cedecfb 100644
--- a/src/rygel/rygel-transcode-manager.vala
+++ b/src/rygel/rygel-transcode-manager.vala
@@ -49,6 +49,10 @@ internal abstract class Rygel.TranscodeManager : GLib.Object {
                 transcoders.add (new MP2TSTranscoder(MP2TSProfile.SD));
                 transcoders.add (new MP2TSTranscoder(MP2TSProfile.HD));
             }
+
+            if (config.get_wmv_transcoder ()) {
+                transcoders.add (new WMVTranscoder ());
+            }
         }
     }
 
diff --git a/src/rygel/rygel-user-config.vala b/src/rygel/rygel-user-config.vala
index d778182..11bef33 100644
--- a/src/rygel/rygel-user-config.vala
+++ b/src/rygel/rygel-user-config.vala
@@ -39,6 +39,7 @@ public class Rygel.UserConfig : GLib.Object, Configuration {
                                                     "enable-mp2ts-transcoder";
     protected static const string LPCM_TRANSCODER_KEY =
                                                     "enable-lpcm-transcoder";
+    protected static const string WMV_TRANSCODER_KEY = "enable-wmv-transcoder";
     protected static const string LOG_LEVEL_KEY = "log-level";
 
     private const string DBUS_SERVICE = "org.freedesktop.DBus";
@@ -122,6 +123,14 @@ public class Rygel.UserConfig : GLib.Object, Configuration {
         this.set_bool ("general", LPCM_TRANSCODER_KEY, value);
     }
 
+    public bool get_wmv_transcoder () throws GLib.Error {
+        return this.get_bool ("general", WMV_TRANSCODER_KEY);
+    }
+
+    public void set_wmv_transcoder (bool value) {
+        this.set_bool ("general", WMV_TRANSCODER_KEY, value);
+    }
+
     public LogLevel get_log_level () throws GLib.Error {
         return (LogLevel) this.get_int ("general",
                                         LOG_LEVEL_KEY,
diff --git a/src/rygel/rygel-wma-transcoder-bin.vala b/src/rygel/rygel-wma-transcoder-bin.vala
new file mode 100644
index 0000000..c4c3520
--- /dev/null
+++ b/src/rygel/rygel-wma-transcoder-bin.vala
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2009 Jens Georg <mail jensge org>.
+ *
+ * Author: Jens Georg <mail jensge 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 Gst;
+
+internal class Rygel.WMATranscoderBin : Gst.Bin {
+    private const string DECODEBIN = "decodebin2";
+
+    private const string AUDIO_SRC_PAD = "audio-src-pad";
+    private const string AUDIO_SINK_PAD = "audio-sink-pad";
+
+    private dynamic Element audio_enc;
+
+    public WMATranscoderBin (MediaItem     item,
+                             Element       src,
+                             WMATranscoder transcoder) throws Error {
+        Element decodebin = GstUtils.create_element (DECODEBIN, DECODEBIN);
+
+        this.audio_enc = transcoder.create_encoder (item,
+                                                    AUDIO_SRC_PAD,
+                                                    AUDIO_SINK_PAD);
+
+        this.add_many (src, decodebin, this.audio_enc);
+        src.link (decodebin);
+
+        var src_pad = this.audio_enc.get_static_pad (AUDIO_SRC_PAD);
+        var ghost = new GhostPad (null, src_pad);
+        this.add_pad (ghost);
+
+        decodebin.pad_added += this.decodebin_pad_added;
+    }
+
+    private void decodebin_pad_added (Element decodebin, Pad new_pad) {
+        Pad enc_pad = this.audio_enc.get_pad (AUDIO_SINK_PAD);
+        if (!new_pad.can_link (enc_pad)) {
+            return;
+        }
+
+        if (new_pad.link (enc_pad) != PadLinkReturn.OK) {
+            GstUtils.post_error (this,
+                                 new GstError.LINK (
+                                                "Failed to link pad %s to %s",
+                                                new_pad.name,
+                                                enc_pad.name));
+            return;
+        }
+    }
+}
diff --git a/src/rygel/rygel-wma-transcoder.vala b/src/rygel/rygel-wma-transcoder.vala
new file mode 100644
index 0000000..22869e9
--- /dev/null
+++ b/src/rygel/rygel-wma-transcoder.vala
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2009 Jens Georg <mail jensge org>.
+ *
+ * Author: Jens Georg <mail jensge 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 Gst;
+using GUPnP;
+
+internal class Rygel.WMATranscoder : Rygel.Transcoder {
+    private const int BITRATE = 64;
+
+    private const string CONVERT_SINK_PAD = "convert-sink-pad";
+
+    public WMATranscoder () {
+        base ("audio/x-wma", "WMA", MediaItem.AUDIO_CLASS);
+    }
+
+    public override Element create_source (MediaItem item,
+                                           Element   src)
+                                           throws Error {
+        return new WMATranscoderBin (item, src, this);
+    }
+
+    public override DIDLLiteResource? add_resource (DIDLLiteItem     didl_item,
+                                                    MediaItem        item,
+                                                    TranscodeManager manager)
+                                                    throws Error {
+        var resource = base.add_resource (didl_item, item, manager);
+        if (resource == null)
+            return null;
+
+        // Convert bitrate to bytes/second
+        resource.bitrate = BITRATE * 1000 / 8;
+
+        return resource;
+    }
+
+    public override uint get_distance (MediaItem item) {
+        if (item.upnp_class.has_prefix (MediaItem.IMAGE_CLASS)) {
+            return uint.MAX;
+        }
+
+        uint distance;
+
+        if (item.upnp_class.has_prefix (MediaItem.AUDIO_CLASS)) {
+            distance = uint.MIN;
+
+            if (item.bitrate > 0) {
+                distance += (item.bitrate - BITRATE).abs ();
+            }
+        } else {
+            distance = uint.MAX / 2;
+        }
+
+        return distance;
+    }
+
+    public Element create_encoder (MediaItem item,
+                                   string?   src_pad_name,
+                                   string?   sink_pad_name)
+                                   throws Error {
+        var l16_transcoder = new L16Transcoder (Endianness.LITTLE);
+        dynamic Element convert = l16_transcoder.create_encoder (
+                                       item,
+                                       null,
+                                       CONVERT_SINK_PAD);
+
+        dynamic Element encoder = GstUtils.create_element ("ffenc_wmav2",
+                                                           "ffenc_wmav2");
+        encoder.bitrate = BITRATE * 1000;
+
+        var bin = new Bin("wma-encoder-bin");
+        bin.add_many (convert, encoder);
+        convert.link (encoder);
+
+        var pad = convert.get_static_pad (CONVERT_SINK_PAD);
+        var ghost = new GhostPad (sink_pad_name, pad);
+        bin.add_pad (ghost);
+
+        pad = encoder.get_static_pad ("src");
+        ghost = new GhostPad (src_pad_name, pad);
+        bin.add_pad (ghost);
+
+        return bin;
+    }
+}
diff --git a/src/rygel/rygel-wmv-transcoder-bin.vala b/src/rygel/rygel-wmv-transcoder-bin.vala
new file mode 100644
index 0000000..3f3c386
--- /dev/null
+++ b/src/rygel/rygel-wmv-transcoder-bin.vala
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2009 Jens Georg <mail jensge org>.
+ *
+ * Author: Jens Georg <mail jensge 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 Gst;
+
+/**
+ * A Gst.Bin derivative that implements transcoding of any type of media (using
+ * decodebin2) to mpeg transport stream containing mpeg 2 video and mp2 audio.
+ */
+internal class Rygel.WMVTranscoderBin : Gst.Bin {
+    private const string DECODEBIN = "decodebin2";
+    private const string MUXER = "ffmux_asf";
+
+    private const string AUDIO_ENC_SINK = "audio-enc-sink-pad";
+    private const string VIDEO_ENC_SINK = "sink";
+
+    private dynamic Element audio_enc;
+    private dynamic Element video_enc;
+    private dynamic Element muxer;
+
+    public WMVTranscoderBin (MediaItem     item,
+                             Element       src,
+                             WMVTranscoder transcoder)
+                             throws Error {
+        Element decodebin = GstUtils.create_element (DECODEBIN, DECODEBIN);
+        var wma_transcoder = new WMATranscoder ();
+        this.audio_enc = wma_transcoder.create_encoder (item,
+                                                        null,
+                                                        AUDIO_ENC_SINK);
+        this.video_enc = transcoder.create_encoder (item, null, VIDEO_ENC_SINK);
+        this.muxer = GstUtils.create_element (MUXER, MUXER);
+
+        this.add_many (src,
+                       decodebin,
+                       this.audio_enc,
+                       this.video_enc,
+                       this.muxer);
+        src.link (decodebin);
+
+        var src_pad = muxer.get_static_pad ("src");
+        var ghost = new GhostPad (null, src_pad);
+        this.add_pad (ghost);
+
+        decodebin.pad_added += this.decodebin_pad_added;
+    }
+
+    private void decodebin_pad_added (Element decodebin, Pad new_pad) {
+        Element encoder;
+        Pad enc_pad;
+
+        var audio_enc_pad = this.audio_enc.get_pad (AUDIO_ENC_SINK);
+        var video_enc_pad = this.video_enc.get_pad (VIDEO_ENC_SINK);
+
+        // Check which encoder to use
+        if (new_pad.can_link (audio_enc_pad)) {
+            encoder = this.audio_enc;
+            enc_pad = audio_enc_pad;
+        } else if (new_pad.can_link (video_enc_pad)) {
+            encoder = this.video_enc;
+            enc_pad = video_enc_pad;
+        } else {
+            return;
+        }
+
+        encoder.link (this.muxer);
+
+        if (new_pad.link (enc_pad) != PadLinkReturn.OK) {
+            GstUtils.post_error (this,
+                                 new GstError.LINK (
+                                                "Failed to link pad %s to %s",
+                                                new_pad.name,
+                                                enc_pad.name));
+            return;
+        }
+    }
+}
diff --git a/src/rygel/rygel-wmv-transcoder.vala b/src/rygel/rygel-wmv-transcoder.vala
new file mode 100644
index 0000000..118e68f
--- /dev/null
+++ b/src/rygel/rygel-wmv-transcoder.vala
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2009 Jens Georg <mail jensge org>.
+ *
+ * Author: Jens Georg <mail jensge 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 Gst;
+using GUPnP;
+
+internal class Rygel.WMVTranscoder : Rygel.Transcoder {
+    private const int VIDEO_BITRATE = 1200;
+    private const int BITRATE = 1200000;
+
+    private const string VIDEO_ENCODER = "ffenc_wmv1";
+    private const string COLORSPACE_CONVERT = "ffmpegcolorspace";
+    private const string VIDEO_RATE = "videorate";
+    private const string VIDEO_SCALE = "videoscale";
+
+    public WMVTranscoder () {
+        base ("video/x-ms-wmv", "WMV", MediaItem.VIDEO_CLASS);
+    }
+
+    public override Element create_source (MediaItem item,
+                                           Element   src)
+                                           throws Error {
+        return new WMVTranscoderBin (item, src, this);
+    }
+
+    public override DIDLLiteResource? add_resource (DIDLLiteItem     didl_item,
+                                                    MediaItem        item,
+                                                    TranscodeManager manager)
+                                                    throws Error {
+        var resource = base.add_resource (didl_item, item, manager);
+        if (resource == null)
+            return null;
+
+        resource.width = item.width;
+        resource.height = item.height;
+        resource.bitrate = (VIDEO_BITRATE + WMATranscoder.BITRATE) * 1000 / 8;
+
+        return resource;
+    }
+
+    public override uint get_distance (MediaItem item) {
+        if (item.upnp_class.has_prefix (MediaItem.IMAGE_CLASS)) {
+            return uint.MAX;
+        }
+
+        uint distance;
+
+        if (item.upnp_class.has_prefix (MediaItem.VIDEO_CLASS)) {
+            distance = uint.MIN;
+
+            if (item.bitrate > 0) {
+                distance += (item.bitrate - BITRATE).abs ();
+            }
+        } else {
+            distance = uint.MAX / 2;
+        }
+
+        return distance;
+    }
+
+    public Element create_encoder (MediaItem item,
+                                   string?   src_pad_name,
+                                   string?   sink_pad_name)
+                                   throws Error {
+        var convert = GstUtils.create_element (COLORSPACE_CONVERT,
+                                               COLORSPACE_CONVERT);
+        dynamic Element encoder = GstUtils.create_element (VIDEO_ENCODER,
+                                                           VIDEO_ENCODER);
+
+        encoder.bitrate = (int) VIDEO_BITRATE * 1000;
+
+        var bin = new Bin ("video-encoder-bin");
+        bin.add_many (convert, encoder);
+        convert.link (encoder);
+
+        var pad = convert.get_static_pad ("sink");
+        var ghost = new GhostPad (sink_pad_name, pad);
+        bin.add_pad (ghost);
+
+        pad = encoder.get_static_pad ("src");
+        ghost = new GhostPad (src_pad_name, pad);
+        bin.add_pad (ghost);
+
+        return bin;
+    }
+
+}



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