core: encodebin-based MPEG TS transcoder
authorLuis de Bethencourt <luis@debethencourt.com>
Mon, 14 Mar 2011 16:35:30 +0000 (17:35 +0100)
committerZeeshan Ali (Khattak) <zeeshanak@gnome.org>
Sun, 10 Apr 2011 15:19:45 +0000 (18:19 +0300)
Video and audio encoders and muxer are correctly auto-selected by
encodebin for us, however we don't set the bitrate currently as we don't
have a reliable way to do so.

Co-author: Zeeshan Ali (Khattak) <zeeshanak@gnome.org>

po/POTFILES.in
po/POTFILES.skip
src/rygel/Makefile.am
src/rygel/rygel-mp2ts-transcoder-bin.vala [deleted file]
src/rygel/rygel-mp2ts-transcoder.vala

index d7fec05..75ee862 100644 (file)
@@ -114,7 +114,6 @@ src/rygel/rygel-visual-item.vala
 src/rygel/rygel-media-object.vala
 src/rygel/rygel-media-receiver-registrar.vala
 src/rygel/rygel-meta-config.vala
-src/rygel/rygel-mp2ts-transcoder-bin.vala
 src/rygel/rygel-mp2ts-transcoder.vala
 src/rygel/rygel-mp3-transcoder-bin.vala
 src/rygel/rygel-mp3-transcoder.vala
index 1cac003..b781977 100644 (file)
@@ -72,7 +72,6 @@ src/rygel/rygel-video-item.c
 src/rygel/rygel-visual-item.c
 src/rygel/rygel-meta-config.c
 src/rygel/rygel-metadata-extractor.c
-src/rygel/rygel-mp2ts-transcoder-bin.c
 src/rygel/rygel-mp3-transcoder-bin.c
 src/rygel/rygel-plugin-loader.c
 src/rygel/rygel-root-device-factory.c
index ef7ad08..c0fb6b7 100644 (file)
@@ -95,7 +95,6 @@ VAPI_SOURCE_FILES = \
        rygel-mp2ts-transcoder.vala \
        rygel-mp3-transcoder.vala \
        rygel-l16-transcoder.vala \
-       rygel-mp2ts-transcoder-bin.vala \
        rygel-mp3-transcoder-bin.vala \
        rygel-l16-transcoder-bin.vala \
        rygel-wma-transcoder.vala \
diff --git a/src/rygel/rygel-mp2ts-transcoder-bin.vala b/src/rygel/rygel-mp2ts-transcoder-bin.vala
deleted file mode 100644 (file)
index b4c4f3a..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2009 Nokia Corporation.
- *
- * Author: Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
- *                               <zeeshan.ali@nokia.com>
- *
- * This file is part of Rygel.
- *
- * Rygel is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * Rygel is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-using 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.MP2TSTranscoderBin : Gst.Bin {
-    private const string DECODEBIN = "decodebin2";
-    private const string MUXER = "mpegtsmux";
-
-    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 MP2TSTranscoderBin (MediaItem       item,
-                               Element         src,
-                               MP2TSTranscoder transcoder)
-                               throws Error {
-        dynamic Element decodebin = GstUtils.create_element (DECODEBIN,
-                                                             DECODEBIN);
-        var mp3_transcoder = new MP3Transcoder (MP3Layer.TWO);
-        this.audio_enc = mp3_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.connect (this.decodebin_pad_added);
-        decodebin.autoplug_continue.connect (this.autoplug_continue);
-    }
-
-    private bool autoplug_continue (Element decodebin,
-                                    Pad     new_pad,
-                                    Caps    caps) {
-        var muxer_pad = this.muxer.get_compatible_pad (new_pad, null);
-
-        if (muxer_pad == null) {
-            return true;
-        } else {
-            return new_pad.link (muxer_pad) != PadLinkReturn.OK;
-        }
-    }
-
-    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) {
-            var error = new GstError.LINK (_("Failed to link pad %s to %s"),
-                                           new_pad.name,
-                                           enc_pad.name);
-            GstUtils.post_error (this, error);
-        }
-    }
-}
index 79f4eaa..f077a48 100644 (file)
@@ -31,10 +31,10 @@ internal enum Rygel.MP2TSProfile {
 
 /**
  * Transcoder for mpeg transport stream containing mpeg 2 video and mp2 audio.
- * This element uses MP2TSTrancoderBin for actual transcoding.
  */
 internal class Rygel.MP2TSTranscoder : Rygel.Transcoder {
     private const int VIDEO_BITRATE = 3000;
+    private const int AUDIO_BITRATE = 256;
 
     // HD
     private const int[] WIDTH = {720, 1280};
@@ -43,10 +43,8 @@ internal class Rygel.MP2TSTranscoder : Rygel.Transcoder {
     private const string[] PROFILES = {"MPEG_TS_SD_EU_ISO", "MPEG_TS_HD_NA_ISO"};
     private const int BITRATE = 3000000;
 
-    private const string VIDEO_ENCODER = "ffenc_mpeg2video";
-    private const string COLORSPACE_CONVERT = "ffmpegcolorspace";
-    private const string VIDEO_RATE = "videorate";
-    private const string VIDEO_SCALE = "videoscale";
+    private const string DECODE_BIN = "decodebin2";
+    private const string ENCODE_BIN = "encodebin";
 
     private MP2TSProfile profile;
 
@@ -59,7 +57,25 @@ internal class Rygel.MP2TSTranscoder : Rygel.Transcoder {
     public override Element create_source (MediaItem item,
                                            Element   src)
                                            throws Error {
-        return new MP2TSTranscoderBin (item, src, this);
+        dynamic Element decoder = GstUtils.create_element (DECODE_BIN,
+                                                           DECODE_BIN);
+        dynamic Element encoder = GstUtils.create_element (ENCODE_BIN,
+                                                           ENCODE_BIN);
+
+        encoder.profile = this.get_encoding_profile ();
+
+        var bin = new Bin ("mp2-ts-transcoder-bin");
+        bin.add_many (src, decoder, encoder);
+
+        src.link (decoder);
+
+        decoder.pad_added.connect (this.on_decoder_pad_added);
+
+        var pad = encoder.get_static_pad ("src");
+        var ghost = new GhostPad (null, pad);
+        bin.add_pad (ghost);
+
+        return bin;
     }
 
     public override DIDLLiteResource? add_resource (DIDLLiteItem     didl_item,
@@ -72,7 +88,7 @@ internal class Rygel.MP2TSTranscoder : Rygel.Transcoder {
 
         resource.width = WIDTH[profile];
         resource.height = HEIGHT[profile];
-        resource.bitrate = (VIDEO_BITRATE + MP3Transcoder.BITRATE) * 1000 / 8;
+        resource.bitrate = (VIDEO_BITRATE + AUDIO_BITRATE) * 1000 / 8;
 
         return resource;
     }
@@ -100,67 +116,72 @@ internal class Rygel.MP2TSTranscoder : Rygel.Transcoder {
         return distance;
     }
 
-    public Element create_encoder (MediaItem item,
-                                   string?   src_pad_name,
-                                   string?   sink_pad_name)
-                                   throws Error {
-        var videorate = GstUtils.create_element (VIDEO_RATE, VIDEO_RATE);
-        var videoscale = GstUtils.create_element (VIDEO_SCALE, VIDEO_SCALE);
-        var convert = GstUtils.create_element (COLORSPACE_CONVERT,
-                                               COLORSPACE_CONVERT);
-        dynamic Element encoder = GstUtils.create_element (VIDEO_ENCODER,
-                                                           VIDEO_ENCODER);
+    private void on_decoder_pad_added (Element decodebin, Pad new_pad) {
+        var bin = decodebin.get_parent () as Bin;
+        assert (bin != null);
 
-        encoder.bitrate = (int) VIDEO_BITRATE * 1000;
+        var encoder = bin.get_by_name (ENCODE_BIN);
+        assert (encoder != null);
 
-        var bin = new Bin ("video-encoder-bin");
-        bin.add_many (videorate, videoscale, convert, encoder);
+        var encoder_pad = encoder.get_compatible_pad (new_pad, null);
+        if (encoder_pad == null) {
+            debug ("No compatible encodebin pad found for pad '%s', ignoring..",
+                   new_pad.name);
+            return;
+        } else {
+            debug ("pad '%s' with caps '%s' is compatible with '%s'",
+                   new_pad.name,
+                   new_pad.get_caps ().to_string (),
+                   encoder_pad.name);
+        }
 
-        convert.link_many (videoscale, videorate);
+        if (new_pad.link (encoder_pad) != PadLinkReturn.OK) {
+            var error = new GstError.LINK (_("Failed to link pad %s to %s"),
+                                           new_pad.name,
+                                           encoder_pad.name);
+            GstUtils.post_error (bin, error);
+        }
+    }
 
-        int pixel_w;
-        int pixel_h;
+    private EncodingContainerProfile get_encoding_profile () {
+        var container_format = Caps.from_string ("video/mpegts," +
+                                                 "systemstream=true," +
+                                                 "packetsize=188");
 
-        var video_item = item as VideoItem;
+        var enc_container_profile = new EncodingContainerProfile
+                                        ("mpeg-ts-profile",
+                                         null,
+                                         container_format,
+                                         null);
 
-        if (video_item.pixel_width > 0 && video_item.pixel_height > 0) {
-            pixel_w = video_item.width *
-                      HEIGHT[this.profile] *
-                      video_item.pixel_width;
-            pixel_h = video_item.height *
-                      WIDTH[this.profile] *
-                      video_item.pixel_height;
-        } else {
-            // Original pixel-ratio not provided, lets just use 1:1
-            pixel_w = 1;
-            pixel_h = 1;
-        }
+        enc_container_profile.add_profile (this.get_video_profile ());
+        enc_container_profile.add_profile (this.get_audio_profile ());
 
-        var caps = new Caps.simple ("video/x-raw-yuv",
-                                    "width",
-                                        typeof (int),
-                                        WIDTH[this.profile],
-                                    "height",
-                                        typeof (int),
-                                        HEIGHT[this.profile],
-                                    "framerate",
-                                        typeof (Fraction),
-                                        FRAME_RATE[this.profile],
-                                        1,
-                                    "pixel-aspect-ratio",
-                                        typeof (Fraction),
-                                        pixel_w,
-                                        pixel_h);
-        videorate.link_filtered (encoder, caps);
-
-        var pad = convert.get_static_pad ("sink");
-        var ghost = new GhostPad (sink_pad_name, pad);
-        bin.add_pad (ghost);
+        return enc_container_profile;
+    }
 
-        pad = encoder.get_static_pad ("src");
-        ghost = new GhostPad (src_pad_name, pad);
-        bin.add_pad (ghost);
+    private EncodingVideoProfile get_video_profile () {
+        var format = Caps.from_string ("video/mpeg," +
+                                       "mpegversion=2," +
+                                       "systemstream=false," +
+                                       "framerate=(fraction)25/1");
+        var restriction = Caps.from_string
+                                        ("video/x-raw-yuv,width=" +
+                                         WIDTH[this.profile].to_string () +
+                                         ",height=" +
+                                         HEIGHT[this.profile].to_string () +
+                                         ",framerate=(fraction)" +
+                                         FRAME_RATE[this.profile].to_string () +
+                                         "/1");
+
+        // FIXME: We should use the preset to set bitrate
+        return new EncodingVideoProfile (format, null, restriction, 1);
+    }
 
-        return bin;
+    private EncodingAudioProfile get_audio_profile () {
+        var format = Caps.from_string ("audio/mpeg,mpegversion=4");
+
+        // FIXME: We should use the preset to set bitrate
+        return new EncodingAudioProfile (format, null, null, 1);
     }
 }