diff options
Diffstat (limited to 'src/media-engines/gstreamer/rygel-gst-transcoder.vala')
-rw-r--r-- | src/media-engines/gstreamer/rygel-gst-transcoder.vala | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/src/media-engines/gstreamer/rygel-gst-transcoder.vala b/src/media-engines/gstreamer/rygel-gst-transcoder.vala new file mode 100644 index 00000000..73e68b56 --- /dev/null +++ b/src/media-engines/gstreamer/rygel-gst-transcoder.vala @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2009-2012 Nokia Corporation. + * Copyright (C) 2012 Intel Corporation. + * + * Author: Zeeshan Ali (Khattak) <zeeshanak@gnome.org> + * <zeeshan.ali@nokia.com> + * Jens Georg <jensg@openismus.com> + * + * This file is part of Rygel. + * + * Rygel is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Rygel is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using Gst; +using GUPnP; + +/** + * The base Transcoder class. Each implementation derives from it and must + * implement get_distance and get_encoding_profile methods. + */ +internal abstract class Rygel.GstTranscoder : Rygel.Transcoder { + public string preset { get; + protected set; + default = DEFAULT_ENCODING_PRESET; } + + private const string DECODE_BIN = "decodebin2"; + private const string ENCODE_BIN = "encodebin"; + private const string DEFAULT_ENCODING_PRESET = "Rygel DLNA preset"; + + dynamic Element decoder; + dynamic Element encoder; + + private bool link_failed; + + public GstTranscoder (string mime_type, + string dlna_profile, + string upnp_class, + string extension) { + this.mime_type = mime_type; + this.dlna_profile = dlna_profile; + this.link_failed = true; + this.extension = extension; + } + + /** + * Creates a transcoding source. + * + * @param src the media item to create the transcoding source for + * @param src the original (non-transcoding) source + * + * @return the new transcoding source + */ + public override DataSource create_source (MediaItem item, + DataSource src) throws Error { + // We can only link GStreamer data sources together + assert (src is GstDataSource); + + var orig_source = src as GstDataSource; + + this.decoder = GstUtils.create_element (DECODE_BIN, + DECODE_BIN); + this.encoder = GstUtils.create_element (ENCODE_BIN, + ENCODE_BIN); + + encoder.profile = this.get_encoding_profile (); + debug ("%s using the following encoding profile:", + this.get_class ().get_type ().name ()); + GstUtils.dump_encoding_profile (encoder.profile); + + var bin = new Bin ("transcoder-source"); + bin.add_many (orig_source.src, decoder, encoder); + + orig_source.src.link (decoder); + + decoder.pad_added.connect (this.on_decoder_pad_added); + decoder.autoplug_continue.connect (this.on_autoplug_continue); + decoder.no_more_pads.connect (this.on_no_more_pads); + + var pad = encoder.get_static_pad ("src"); + var ghost = new GhostPad (null, pad); + bin.add_pad (ghost); + + return new GstDataSource.from_element (bin); + } + + /** + * Gets the Gst.EncodingProfile for this transcoder. + * + * @return the Gst.EncodingProfile for this transcoder. + */ + protected abstract EncodingProfile get_encoding_profile (); + + private bool on_autoplug_continue (Element decodebin, + Pad new_pad, + Caps caps) { + Gst.Pad sinkpad = null; + + Signal.emit_by_name (this.encoder, "request-pad", caps, out sinkpad); + if (sinkpad == null) { + return true; + } + + return false; + } + + private void on_decoder_pad_added (Element decodebin, Pad new_pad) { + Gst.Pad sinkpad; + + sinkpad = this.encoder.get_compatible_pad (new_pad, null); + + if (sinkpad == null) { + var caps = new_pad.get_caps_reffed (); + Signal.emit_by_name (this.encoder, "request-pad", caps, out sinkpad); + } + + if (sinkpad == null) { + debug ("No compatible encodebin pad found for pad '%s', ignoring..", + new_pad.name); + + return; + } + + var pad_link_ok = (new_pad.link (sinkpad) == PadLinkReturn.OK); + if (!pad_link_ok) { + warning ("Failed to link pad '%s' to '%s'", + new_pad.name, + sinkpad.name); + } else { + this.link_failed = false; + } + + return; + } + + private const string description = "Encoder and decoder are not " + + "compatible"; + + private void on_no_more_pads (Element decodebin) { + // We haven't found any pads we could link + if (this.link_failed) { + // Signalize that error + var bin = this.encoder.get_parent () as Bin; + var error = new IOError.FAILED ("Could not link"); + var message = new Message.error (bin, + error, + description); + + + var bus = bin.get_bus (); + bus.post (message); + } + } +} |