[banshee] gst#: Add initial Transcoder feature



commit 4a64ae6513a873df7023b038215e01d0bfb120f3
Author: Olivier Dufour <olivier duff gmail com>
Date:   Thu May 26 22:29:43 2011 +0200

    gst#: Add initial Transcoder feature
    
    Signed-off-by: Bertrand Lorentz <bertrand lorentz gmail com>

 .../Banshee.GStreamerSharp.csproj                  |    1 +
 .../Banshee.GStreamerSharp/Transcoder.cs           |  314 ++++++++++++++++++++
 src/Backends/Banshee.GStreamerSharp/Makefile.am    |    3 +-
 3 files changed, 317 insertions(+), 1 deletions(-)
---
diff --git a/src/Backends/Banshee.GStreamerSharp/Banshee.GStreamerSharp.csproj b/src/Backends/Banshee.GStreamerSharp/Banshee.GStreamerSharp.csproj
index dc063bc..5eb1f97 100644
--- a/src/Backends/Banshee.GStreamerSharp/Banshee.GStreamerSharp.csproj
+++ b/src/Backends/Banshee.GStreamerSharp/Banshee.GStreamerSharp.csproj
@@ -74,6 +74,7 @@
     <Compile Include="Banshee.GStreamerSharp\BpmDetector.cs" />
     <Compile Include="Banshee.GStreamerSharp\CddaManager.cs" />
     <Compile Include="Banshee.GStreamerSharp\AudioCdRipper.cs" />
+    <Compile Include="Banshee.GStreamerSharp\Transcoder.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
 </Project>
diff --git a/src/Backends/Banshee.GStreamerSharp/Banshee.GStreamerSharp/Transcoder.cs b/src/Backends/Banshee.GStreamerSharp/Banshee.GStreamerSharp/Transcoder.cs
new file mode 100644
index 0000000..5ad02eb
--- /dev/null
+++ b/src/Backends/Banshee.GStreamerSharp/Banshee.GStreamerSharp/Transcoder.cs
@@ -0,0 +1,314 @@
+//
+// Transcoder.cs
+//
+// Author:
+//   Aaron Bockover <abockover novell com>
+//  Olivier Dufour <olivier duff gmail com>
+//
+// Copyright (C) 2008 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Runtime.InteropServices;
+using Mono.Unix;
+
+using Hyena;
+using Banshee.Base;
+using Banshee.Collection;
+using Banshee.ServiceStack;
+using Banshee.MediaEngine;
+using Banshee.MediaProfiles;
+using Banshee.Configuration.Schema;
+
+using Gst;
+
+namespace Banshee.GStreamerSharp
+{
+    public class Transcoder : ITranscoder
+    {
+        private class AudioSinkBin : Bin
+        {
+            public AudioSinkBin (IntPtr o) : base (o)
+            {}
+
+            public AudioSinkBin (string elementName, string encoder_pipeline, string output_uri) : base (elementName)
+            {
+                Element encoder_elem = null;
+                Element sink_elem;
+                Element conv_elem;
+                Element resample_elem;
+                Pad encoder_pad;
+
+                sink_elem = ElementFactory.MakeFromUri (URIType.Sink, output_uri, "sink");
+                if (sink_elem == null) {
+                    throw new Exception (Catalog.GetString ("Could not create sink element"));
+                }
+
+                conv_elem = ElementFactory.Make ("audioconvert", "audioconvert");
+                if (conv_elem == null) {
+                    throw new Exception (Catalog.GetString ("Could not create 'audioconvert' plugin"));
+                }
+
+                resample_elem = ElementFactory.Make ("audioresample", "audioresample");
+                if (resample_elem == null) {
+                    throw new Exception (Catalog.GetString ("Could not create 'audioresample' plugin"));
+                }
+
+                try {
+                    encoder_elem = Parse.BinFromDescription (encoder_pipeline, true);
+                } catch (Exception e) {
+                    string err = Catalog.GetString ("Could not create encoding pipeline : {0}");
+                    throw new Exception (String.Format (err, e.Message));
+                }
+
+                encoder_pad = conv_elem.GetStaticPad ("sink");
+                if (encoder_pad == null) {
+                    throw new Exception (Catalog.GetString ("Could not get sink pad from encoder"));
+                }
+
+                Add (conv_elem, resample_elem, encoder_elem, sink_elem);
+                Element.Link (conv_elem, resample_elem, encoder_elem, sink_elem);
+
+                AddPad (new GhostPad ("sink", encoder_pad));
+            }
+        }
+
+        public event TranscoderProgressHandler Progress;
+        public event TranscoderTrackFinishedHandler TrackFinished;
+        public event TranscoderErrorHandler Error;
+
+        private TrackInfo current_track;
+        private string error_message;
+        private SafeUri output_uri;
+
+        bool is_transcoding;
+        Gst.Pipeline pipeline;
+        AudioSinkBin sink_bin;
+        System.Timers.Timer timer;
+    
+        public Transcoder ()
+        {
+            timer = new System.Timers.Timer ();
+            timer.Interval = 200;
+            timer.AutoReset = true;
+            timer.Elapsed += OnTick;
+        }
+
+        void OnTick (object o, System.Timers.ElapsedEventArgs args)
+        {
+            Format format = Format.Time;
+            long position, duration;
+
+            if (!pipeline.QueryDuration (ref format, out duration) ||
+                !sink_bin.QueryPosition (ref format, out position)) {
+                return;
+            }
+
+            RaiseProgress (current_track, (double)position / (double)duration);
+        }
+
+        public void Finish ()
+        {
+            timer.Stop ();
+
+            if(pipeline != null) {
+                pipeline.SetState (State.Null);
+                pipeline = null;
+            }
+
+            if(output_uri != null) {
+                output_uri = null;
+            }
+        }
+
+        public void Cancel ()
+        {
+            timer.Stop ();
+            is_transcoding = false;
+    
+            if(pipeline != null) {
+                pipeline.SetState (State.Null);
+                pipeline = null;
+            }
+
+            output_uri = null;
+        }
+
+        public void TranscodeTrack (TrackInfo track, SafeUri outputUri, ProfileConfiguration config)
+        {
+            if(IsTranscoding) {
+                throw new ApplicationException("Transcoder is busy");
+            }
+
+            Log.DebugFormat ("Transcoding {0} to {1}", track.Uri, outputUri);
+            output_uri = outputUri;
+
+            error_message = null;
+
+            current_track = track;
+            Transcode (config.Profile.Pipeline.GetProcessById("gstreamer"));
+
+        }
+
+        private bool ConstructPipeline (string encoder_pipeline)
+        {
+            Element source_elem;
+            Element decoder_elem;
+
+            pipeline = new Gst.Pipeline ("pipeline");
+
+            source_elem = ElementFactory.MakeFromUri (URIType.Src, current_track.Uri.AbsoluteUri, "source");
+            if (source_elem == null) {
+                RaiseError (current_track, Catalog.GetString ("Could not create source element"));
+                return false;
+            }
+
+            decoder_elem = ElementFactory.Make ("decodebin2", "decodebin2");
+            if (decoder_elem == null) {
+                RaiseError (current_track, Catalog.GetString ("Could not create 'decodebin2' plugin"));
+                return false;
+            }
+
+            try {
+                sink_bin = new AudioSinkBin ("sinkbin", encoder_pipeline, output_uri.AbsoluteUri);
+            } catch (Exception e) {
+                RaiseError (current_track, e.Message);
+            }
+
+            if (sink_bin == null) {
+                RaiseError (current_track, Catalog.GetString ("Could not create 'sinkbin' plugin"));
+                return false;
+            }
+
+            pipeline.Add (source_elem, decoder_elem, sink_bin);
+
+            source_elem.Link (decoder_elem);
+
+            decoder_elem.PadAdded += OnPadAdded;
+
+            pipeline.Bus.AddWatch (OnBusMessage);
+
+            return true;
+        }
+
+        private void OnPadAdded (object sender, PadAddedArgs args)
+        {
+            Caps caps;
+            Structure str;
+            Pad audiopad;
+
+            audiopad = sink_bin.GetStaticPad ("sink");
+
+            if (audiopad.IsLinked) {
+                return;
+            }
+
+            caps = args.Pad.Caps;
+            str = caps [0];
+
+            if(!str.Name.Contains ("audio")) {
+                return;
+            }
+           
+            args.Pad.Link (audiopad);
+        }
+
+        private bool OnBusMessage (Bus bus, Message msg)
+        {
+            switch (msg.Type) {
+                case MessageType.Eos:
+                    pipeline.SetState (State.Null);
+                    pipeline = null;
+                    is_transcoding = false;
+                    timer.Stop ();
+                    RaiseTrackFinished (current_track, output_uri);
+                    break;
+
+                case MessageType.Error:
+                    Enum error_type;
+                    string err_msg, debug;
+                    is_transcoding = false;
+                    timer.Stop ();
+                    msg.ParseError (out error_type, out err_msg, out debug);
+                    RaiseError (current_track, String.Format ("{0} : {1}", err_msg, debug));
+                    timer.Stop ();
+                    break;
+
+                default:
+                    break;
+            }
+
+            return true;
+        }
+
+        private void Transcode (string encoder_pipeline)
+        {
+            if (IsTranscoding) {
+                return;
+            }
+            if(!ConstructPipeline (encoder_pipeline)) {
+                RaiseError (current_track, Catalog.GetString ("Could not construct pipeline")); 
+                return;
+            }
+
+            is_transcoding = true;
+            pipeline.SetState (State.Playing);
+            timer.Start ();
+        }
+
+        protected virtual void RaiseProgress (TrackInfo track, double fraction)
+        {
+            TranscoderProgressHandler handler = Progress;
+            if (handler != null) {
+                handler (this, new TranscoderProgressArgs (track, fraction, track.Duration));
+            }
+        }
+
+        protected virtual void RaiseTrackFinished (TrackInfo track, SafeUri outputUri)
+        {
+            TranscoderTrackFinishedHandler handler = TrackFinished;
+            if (handler != null) {
+                handler (this, new TranscoderTrackFinishedArgs (track, outputUri));
+            }
+        }
+
+        protected virtual void RaiseError (TrackInfo track, string message)
+        {
+            try {
+                Banshee.IO.File.Delete (output_uri);
+            } catch {}
+            
+            TranscoderErrorHandler handler = Error;
+            if (handler != null) {
+                handler (this, new TranscoderErrorArgs (track, message));
+            }
+        }
+
+        public bool IsTranscoding {
+            get { return is_transcoding; }
+        }
+
+        public string ErrorMessage {
+            get { return error_message; }
+        }
+    }
+}
diff --git a/src/Backends/Banshee.GStreamerSharp/Makefile.am b/src/Backends/Banshee.GStreamerSharp/Makefile.am
index 9405300..9c25355 100644
--- a/src/Backends/Banshee.GStreamerSharp/Makefile.am
+++ b/src/Backends/Banshee.GStreamerSharp/Makefile.am
@@ -7,7 +7,8 @@ SOURCES =  \
 	Banshee.GStreamerSharp/AudioCdRipper.cs \
 	Banshee.GStreamerSharp/BpmDetector.cs \
 	Banshee.GStreamerSharp/CddaManager.cs \
-	Banshee.GStreamerSharp/PlayerEngine.cs
+	Banshee.GStreamerSharp/PlayerEngine.cs \
+	Banshee.GStreamerSharp/Transcoder.cs
 
 RESOURCES = Banshee.GStreamerSharp.addin.xml
 



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