[longomatch] Add a new tool to merge and re-encode files



commit 7c2c9f8e802b1412c682fd70118001cc6e72e703
Author: Andoni Morales Alastruey <ylatuya gmail com>
Date:   Fri Mar 8 16:19:54 2013 +0100

    Add a new tool to merge and re-encode files

 LongoMatch.Core/Common/Enums.cs                    |    5 +
 LongoMatch.Core/Common/Job.cs                      |   36 +-
 LongoMatch.Core/Handlers/Handlers.cs               |    3 +
 LongoMatch.Core/Interfaces/GUI/IGUIToolkit.cs      |    2 +-
 LongoMatch.Core/Interfaces/GUI/IMainWindow.cs      |    3 +
 .../Interfaces/Multimedia/IMultimediaToolkit.cs    |    2 +
 .../Interfaces/Multimedia/IVideoConverter.cs       |   45 ++
 LongoMatch.Core/LongoMatch.Core.mdp                |    1 +
 LongoMatch.Core/Makefile.am                        |    1 +
 LongoMatch.GUI/Gui/Dialog/RenderingJobsDialog.cs   |    1 -
 LongoMatch.GUI/Gui/Dialog/VideoConversionTool.cs   |  130 ++++
 LongoMatch.GUI/Gui/GUIToolkit.cs                   |   10 +-
 LongoMatch.GUI/Gui/MainWindow.cs                   |   18 +-
 LongoMatch.GUI/LongoMatch.GUI.mdp                  |    2 +
 LongoMatch.GUI/Makefile.am                         |    2 +
 .../LongoMatch.Gui.Dialog.VideoConversionTool.cs   |  226 ++++++
 .../gtk-gui/LongoMatch.Gui.MainWindow.cs           |    7 +-
 LongoMatch.GUI/gtk-gui/gui.stetic                  |  264 +++++++
 .../Converter/GstVideoConverter.cs                 |  260 +++++++
 LongoMatch.Multimedia/Converter/ObjectManager.cs   |   40 +
 LongoMatch.Multimedia/LongoMatch.Multimedia.mdp    |    3 +
 LongoMatch.Multimedia/Makefile.am                  |    2 +
 LongoMatch.Multimedia/MultimediaFactory.cs         |    5 +
 LongoMatch.Services/Services/PlaylistManager.cs    |    2 +-
 .../Services/RenderingJobsManager.cs               |   88 ++-
 libcesarplayer/Makefile.am                         |    5 +
 libcesarplayer/gst-video-encoder.c                 |  803 ++++++++++++++++++++
 libcesarplayer/gst-video-encoder.h                 |   93 +++
 libcesarplayer/liblongomatch.mdp                   |    2 +
 libcesarplayer/test-encoder.c                      |   93 +++
 30 files changed, 2117 insertions(+), 37 deletions(-)
---
diff --git a/LongoMatch.Core/Common/Enums.cs b/LongoMatch.Core/Common/Enums.cs
index d5d0597..3e0f291 100644
--- a/LongoMatch.Core/Common/Enums.cs
+++ b/LongoMatch.Core/Common/Enums.cs
@@ -126,4 +126,9 @@ namespace LongoMatch.Common
                CANCELED = -1,
                ERROR = -2
        }
+       
+       public enum JobType {
+               VideoEdition,
+               VideoConversion
+       }
 }
diff --git a/LongoMatch.Core/Common/Job.cs b/LongoMatch.Core/Common/Job.cs
index 28db723..352e695 100644
--- a/LongoMatch.Core/Common/Job.cs
+++ b/LongoMatch.Core/Common/Job.cs
@@ -16,22 +16,20 @@
 //  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 // 
 using System;
+using System.Collections.Generic;
 
 using LongoMatch.Common;
 using LongoMatch.Interfaces;
+using LongoMatch.Store;
 
 namespace LongoMatch.Common
 {
        [Serializable]
        public class Job
        {
-               public Job (IPlayList playlist, EncodingSettings encSettings,
-                           bool enableAudio, bool overlayTitle)
+               public Job (EncodingSettings encSettings)
                {
-                       Playlist = Cloner.Clone(playlist);
                        EncodingSettings = encSettings;
-                       EnableAudio = enableAudio;
-                       OverlayTitle = overlayTitle;
                        State = JobState.NotStarted;
                }
                
@@ -64,12 +62,23 @@ namespace LongoMatch.Common
                        }
                }
                
-               public IPlayList Playlist{
+               public EncodingSettings EncodingSettings {
                        get;
                        set;
                }
+       }
+       
+       public class EditionJob: Job
+       {
+               public EditionJob (IPlayList playlist, EncodingSettings encSettings,
+                                  bool enableAudio = false, bool overlayTitle = false): base (encSettings)
+               {
+                       Playlist = Cloner.Clone(playlist);
+                       EnableAudio = enableAudio;
+                       OverlayTitle = overlayTitle;
+               }
                
-               public EncodingSettings EncodingSettings {
+               public IPlayList Playlist{
                        get;
                        set;
                }
@@ -84,5 +93,18 @@ namespace LongoMatch.Common
                        set;
                }
        }
+       
+       public class ConversionJob: Job
+       {
+               public ConversionJob (List<MediaFile> files, EncodingSettings encSettings): base (encSettings)
+               {
+                       InputFiles = files;
+               }
+               
+               public List<MediaFile> InputFiles {
+                       get;
+                       set;
+               }
+       }
 }
 
diff --git a/LongoMatch.Core/Handlers/Handlers.cs b/LongoMatch.Core/Handlers/Handlers.cs
index 99d37cd..1f9de1a 100644
--- a/LongoMatch.Core/Handlers/Handlers.cs
+++ b/LongoMatch.Core/Handlers/Handlers.cs
@@ -102,6 +102,9 @@ namespace LongoMatch.Handlers
        /* Add a new rendering job */
        public delegate void RenderPlaylistHandler(IPlayList playlist);
         
+       /* Convert a video file */
+       public delegate void ConvertVideoFilesHandler (List<MediaFile> inputFiles, EncodingSettings 
encSettings);
+       
        /* A date was selected */
        public delegate void DateSelectedHandler(DateTime selectedDate);
        
diff --git a/LongoMatch.Core/Interfaces/GUI/IGUIToolkit.cs b/LongoMatch.Core/Interfaces/GUI/IGUIToolkit.cs
index 8da29f0..84349b4 100644
--- a/LongoMatch.Core/Interfaces/GUI/IGUIToolkit.cs
+++ b/LongoMatch.Core/Interfaces/GUI/IGUIToolkit.cs
@@ -46,7 +46,7 @@ namespace LongoMatch.Interfaces.GUI
                        
                IBusyDialog BusyDialog(string message);
                        
-               List<Job> ConfigureRenderingJob (IPlayList playlist);
+               List<EditionJob> ConfigureRenderingJob (IPlayList playlist);
                void ExportFrameSeries(Project openenedProject, Play play, string snapshotDir);
                
                ProjectDescription SelectProject(List<ProjectDescription> projects);
diff --git a/LongoMatch.Core/Interfaces/GUI/IMainWindow.cs b/LongoMatch.Core/Interfaces/GUI/IMainWindow.cs
index 29f4d69..b291c4c 100644
--- a/LongoMatch.Core/Interfaces/GUI/IMainWindow.cs
+++ b/LongoMatch.Core/Interfaces/GUI/IMainWindow.cs
@@ -73,6 +73,9 @@ namespace LongoMatch.Interfaces.GUI
                
                event KeyHandler KeyPressed;
                
+               /* Convert Video Files */
+               event ConvertVideoFilesHandler ConvertVideoFilesEvent;
+               
                void SetProject(Project project, ProjectType projectType, CaptureSettings props, PlaysFilter 
filter);
                void AddPlay(Play play);
                void UpdateSelectedPlay (Play play);
diff --git a/LongoMatch.Core/Interfaces/Multimedia/IMultimediaToolkit.cs 
b/LongoMatch.Core/Interfaces/Multimedia/IMultimediaToolkit.cs
index 9ccb5a2..49c9463 100644
--- a/LongoMatch.Core/Interfaces/Multimedia/IMultimediaToolkit.cs
+++ b/LongoMatch.Core/Interfaces/Multimedia/IMultimediaToolkit.cs
@@ -27,6 +27,8 @@ namespace LongoMatch.Interfaces.Multimedia
        {
                IVideoEditor GetVideoEditor();
                
+               IVideoConverter GetVideoConverter (string filename);
+               
                IFramesCapturer GetFramesCapturer();
                
                IRemuxer GetRemuxer(string inputFile, string outputFile, VideoMuxerType muxer);
diff --git a/LongoMatch.Core/Interfaces/Multimedia/IVideoConverter.cs 
b/LongoMatch.Core/Interfaces/Multimedia/IVideoConverter.cs
new file mode 100644
index 0000000..fb7abdb
--- /dev/null
+++ b/LongoMatch.Core/Interfaces/Multimedia/IVideoConverter.cs
@@ -0,0 +1,45 @@
+// ICapturer.cs
+//
+//  Copyright (C) 2007-2009 Andoni Morales Alastruey
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+//
+
+using System;
+using Gdk;
+
+using LongoMatch.Common;
+using LongoMatch.Handlers;
+
+namespace LongoMatch.Interfaces.Multimedia
+{
+
+
+       public interface IVideoConverter
+       {
+               event ErrorHandler Error;
+               event ProgressHandler Progress;
+
+               void Start();
+
+               void Cancel();
+               
+               void AddFile(string filename, long duration);
+               
+               EncodingSettings EncodingSettings {set;}
+               
+       }
+}
diff --git a/LongoMatch.Core/LongoMatch.Core.mdp b/LongoMatch.Core/LongoMatch.Core.mdp
index 0c4e5ba..4b2069f 100644
--- a/LongoMatch.Core/LongoMatch.Core.mdp
+++ b/LongoMatch.Core/LongoMatch.Core.mdp
@@ -101,6 +101,7 @@
     <File subtype="Code" buildaction="Compile" name="Common/PlaysFilter.cs" />
     <File subtype="Code" buildaction="Compile" name="Interfaces/Multimedia/IRemuxer.cs" />
     <File subtype="Code" buildaction="Compile" name="Common/Coordinates.cs" />
+    <File subtype="Code" buildaction="Compile" name="Interfaces/Multimedia/IVideoConverter.cs" />
   </Contents>
   <References>
     <ProjectReference type="Gac" localcopy="True" refto="System, Version=4.0.0.0, Culture=neutral, 
PublicKeyToken=b77a5c561934e089" />
diff --git a/LongoMatch.Core/Makefile.am b/LongoMatch.Core/Makefile.am
index c7b0a1c..a43c870 100644
--- a/LongoMatch.Core/Makefile.am
+++ b/LongoMatch.Core/Makefile.am
@@ -34,6 +34,7 @@ SOURCES = \
        Interfaces/Multimedia/IFramesCapturer.cs \
        Interfaces/Multimedia/IMultimediaToolkit.cs \
        Interfaces/Multimedia/IRemuxer.cs \
+       Interfaces/Multimedia/IVideoConverter.cs \
        Interfaces/Multimedia/IVideoEditor.cs \
        Interfaces/IDatabase.cs \
        Interfaces/IPlayList.cs \
diff --git a/LongoMatch.GUI/Gui/Dialog/RenderingJobsDialog.cs 
b/LongoMatch.GUI/Gui/Dialog/RenderingJobsDialog.cs
index f081ee0..554c4d6 100644
--- a/LongoMatch.GUI/Gui/Dialog/RenderingJobsDialog.cs
+++ b/LongoMatch.GUI/Gui/Dialog/RenderingJobsDialog.cs
@@ -28,7 +28,6 @@ namespace LongoMatch.Gui.Dialog
        public partial class RenderingJobsDialog : Gtk.Dialog
        {
                IRenderingJobsManager manager;
-               TreeStore model;
                
                public RenderingJobsDialog (IRenderingJobsManager manager)
                {
diff --git a/LongoMatch.GUI/Gui/Dialog/VideoConversionTool.cs 
b/LongoMatch.GUI/Gui/Dialog/VideoConversionTool.cs
new file mode 100644
index 0000000..0d4583e
--- /dev/null
+++ b/LongoMatch.GUI/Gui/Dialog/VideoConversionTool.cs
@@ -0,0 +1,130 @@
+// 
+//  Copyright (C) 2013 Andoni Morales Alastruey
+// 
+//  This program is free software; you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation; either version 2 of the License, or
+//  (at your option) any later version.
+// 
+//  This program 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 General Public License for more details.
+//  
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+// 
+using System;
+using System.Collections.Generic;
+using Gtk;
+using Mono.Unix;
+
+using LongoMatch.Common;
+using LongoMatch.Gui;
+using LongoMatch.Video.Utils;
+using LongoMatch.Store;
+using LongoMatch.Interfaces;
+
+namespace LongoMatch.Gui.Dialog
+{
+       public partial class VideoConversionTool : Gtk.Dialog
+       {
+               ListStore store;
+               string outputFile;
+               List<MediaFile> files;
+               
+               public VideoConversionTool ()
+               {
+                       this.Build ();
+                       SetTreeView ();
+                       buttonOk.Sensitive = false;
+                       Files = new List<MediaFile>();
+               }
+               
+               public List<MediaFile> Files {
+                       get;
+                       set;
+               }
+               
+               public EncodingSettings EncodingSettings {
+                       get;
+                       set;
+               }
+               
+               void CheckStatus () {
+                       buttonOk.Sensitive = outputFile != null && Files.Count != 0;
+               }
+               
+               void SetTreeView () {
+                       TreeViewColumn mediaFileCol = new TreeViewColumn();
+                       mediaFileCol.Title = Catalog.GetString("Input files");
+                       CellRendererText mediaFileCell = new CellRendererText();
+                       mediaFileCol.PackStart(mediaFileCell, true);
+                       mediaFileCol.SetCellDataFunc(mediaFileCell, new TreeCellDataFunc(RenderMediaFile));
+                       treeview1.AppendColumn(mediaFileCol);
+                       
+                       store = new ListStore(typeof(PreviewMediaFile));
+                       treeview1.Model = store;
+               }
+               
+               protected void OnAddbuttonClicked (object sender, System.EventArgs e)
+               {
+                       string path = GUIToolkit.Instance.OpenFile (Catalog.GetString("Add file"), null,
+                                                                   Config.HomeDir(), null, null);
+                       try {
+                               if (path == null)
+                                       return;
+                               MediaFile file = PreviewMediaFile.DiscoverFile(path);
+                               store.AppendValues (file);
+                               Files.Add (file);
+                       } catch (Exception ex) {
+                               GUIToolkit.Instance.ErrorMessage (ex.Message);
+                       }
+                       CheckStatus ();
+               }
+
+               protected void OnRemovebuttonClicked (object sender, System.EventArgs e)
+               {
+                       TreeIter iter;
+                       
+                       treeview1.Selection.GetSelected (out iter);
+                       Files.Remove (store.GetValue (iter, 0) as MediaFile);
+                       CheckStatus ();
+                       store.Remove(ref iter);
+               }
+
+               protected void OnOpenbuttonClicked (object sender, System.EventArgs e)
+               {
+                       string path = GUIToolkit.Instance.SaveFile (Catalog.GetString("Add file"),
+                                                                   "NewVideo.mp4",
+                                                                   Config.VideosDir(),
+                                                                   Catalog.GetString("MP4 file"),
+                                                                   "mp4");
+                       outputFile = System.IO.Path.ChangeExtension (path, "mp4");
+                       filelabel.Text = outputFile;
+                       CheckStatus ();
+               }
+               
+               private void RenderMediaFile(Gtk.TreeViewColumn column, Gtk.CellRenderer cell, Gtk.TreeModel 
model, Gtk.TreeIter iter)
+               {
+                       MediaFile file = (MediaFile) store.GetValue(iter, 0);
+
+                       (cell as Gtk.CellRendererText).Text = String.Format("{0} {1}x{2} (Video:'{3}' 
Audio:'{4}')",
+                                                                           System.IO.Path.GetFileName 
(file.FilePath),
+                                                                           file.VideoWidth, file.VideoHeight,
+                                                                           file.VideoCodec, file.AudioCodec);
+               }
+
+               protected void OnButtonOkClicked (object sender, System.EventArgs e)
+               {
+                       EncodingSettings encSettings;
+                       
+                       encSettings = new EncodingSettings(VideoStandards.P720, EncodingProfiles.MP4,
+                                                           25, 1, 4000, 128, outputFile, 0);
+                       EncodingSettings = encSettings;
+                       Respond (ResponseType.Ok);
+               }
+       }
+}
+
diff --git a/LongoMatch.GUI/Gui/GUIToolkit.cs b/LongoMatch.GUI/Gui/GUIToolkit.cs
index 11cb4c3..c900441 100644
--- a/LongoMatch.GUI/Gui/GUIToolkit.cs
+++ b/LongoMatch.GUI/Gui/GUIToolkit.cs
@@ -107,10 +107,10 @@ namespace LongoMatch.Gui
                                extensionFilter, FileChooserAction.Open);
                }
                
-               public List<Job> ConfigureRenderingJob (IPlayList playlist)
+               public List<EditionJob> ConfigureRenderingJob (IPlayList playlist)
                {
                        VideoEditionProperties vep;
-                       List<Job> jobs = new List<Job>();
+                       List<EditionJob> jobs = new List<EditionJob>();
                        int response;
                        
                        if (playlist.Count == 0) {
@@ -134,8 +134,8 @@ namespace LongoMatch.Gui
                        }
                        if(response ==(int)ResponseType.Ok) {
                                if (!vep.SplitFiles) {
-                                       jobs.Add(new Job(playlist, vep.EncodingSettings,
-                                                          vep.EnableAudio, vep.TitleOverlay));
+                                       jobs.Add(new EditionJob(playlist, vep.EncodingSettings,
+                                                               vep.EnableAudio, vep.TitleOverlay));
                                } else {
                                        int i = 0;
                                        foreach (PlayListPlay play in playlist) {
@@ -146,7 +146,7 @@ namespace LongoMatch.Gui
                                                
                                                pl.Add(play);
                                                settings.OutputFile = Path.Combine (vep.OutputDir, filename);
-                                               jobs.Add(new Job(pl, settings, vep.EnableAudio, 
vep.TitleOverlay));
+                                               jobs.Add(new EditionJob(pl, settings, vep.EnableAudio, 
vep.TitleOverlay));
                                                i++;
                                        }
                                }
diff --git a/LongoMatch.GUI/Gui/MainWindow.cs b/LongoMatch.GUI/Gui/MainWindow.cs
index 021e906..7420ed3 100644
--- a/LongoMatch.GUI/Gui/MainWindow.cs
+++ b/LongoMatch.GUI/Gui/MainWindow.cs
@@ -63,6 +63,9 @@ namespace LongoMatch.Gui
                public event NewPlaylistHandler NewPlaylistEvent;
                public event SavePlaylistHandler SavePlaylistEvent; 
                
+               /* Video Converter */
+               public event ConvertVideoFilesHandler ConvertVideoFilesEvent;
+               
                /* Snapshots */
                public event SnapshotSeriesHandler SnapshotSeriesEvent;
                
@@ -527,6 +530,20 @@ namespace LongoMatch.Gui
                }
                #endregion
                
+               #region Tool
+               protected void OnVideoConverterToolActionActivated (object sender, System.EventArgs e)
+               {
+                       int res;
+                       VideoConversionTool converter = new VideoConversionTool();
+                       res = converter.Run ();
+                       converter.Destroy();
+                       if (res == (int) ResponseType.Ok) {
+                               if (ConvertVideoFilesEvent != null)
+                                       ConvertVideoFilesEvent (converter.Files, converter.EncodingSettings);
+                       }
+               }
+               #endregion
+               
                #region View
                protected void OnTagSubcategoriesActionToggled (object sender, System.EventArgs e)
                {
@@ -901,7 +918,6 @@ namespace LongoMatch.Gui
                                KeyPressed(sender, key, modifier);
                }
 
-
                #endregion
        }
 }
diff --git a/LongoMatch.GUI/LongoMatch.GUI.mdp b/LongoMatch.GUI/LongoMatch.GUI.mdp
index 3c5aaed..6201931 100644
--- a/LongoMatch.GUI/LongoMatch.GUI.mdp
+++ b/LongoMatch.GUI/LongoMatch.GUI.mdp
@@ -157,6 +157,8 @@
     <File subtype="Code" buildaction="Compile" name="Gui/TreeView/CategoriesFilterTreeView.cs" />
     <File subtype="Code" buildaction="Compile" name="Gui/Dialog/ShortcutsHelpDialog.cs" />
     <File subtype="Code" buildaction="Compile" name="gtk-gui/LongoMatch.Gui.Dialog.ShortcutsHelpDialog.cs" />
+    <File subtype="Code" buildaction="Compile" name="Gui/Dialog/VideoConversionTool.cs" />
+    <File subtype="Code" buildaction="Compile" name="gtk-gui/LongoMatch.Gui.Dialog.VideoConversionTool.cs" />
   </Contents>
   <References>
     <ProjectReference type="Gac" localcopy="True" refto="atk-sharp, Version=2.12.0.0, Culture=neutral, 
PublicKeyToken=35e10195dab3c99f" />
diff --git a/LongoMatch.GUI/Makefile.am b/LongoMatch.GUI/Makefile.am
index 00edc91..c56f21c 100644
--- a/LongoMatch.GUI/Makefile.am
+++ b/LongoMatch.GUI/Makefile.am
@@ -48,6 +48,7 @@ SOURCES = \
        gtk-gui/LongoMatch.Gui.Dialog.TemplateEditorDialog.cs \
        gtk-gui/LongoMatch.Gui.Dialog.TemplatesManager.cs \
        gtk-gui/LongoMatch.Gui.Dialog.UpdateDialog.cs \
+       gtk-gui/LongoMatch.Gui.Dialog.VideoConversionTool.cs \
        gtk-gui/LongoMatch.Gui.Dialog.VideoEditionProperties.cs \
        gtk-gui/LongoMatch.Gui.Dialog.Win32CalendarDialog.cs \
        gtk-gui/LongoMatch.Gui.MainWindow.cs \
@@ -107,6 +108,7 @@ SOURCES = \
        Gui/Dialog/TemplatesManager.cs \
        Gui/Dialog/UpdateDialog.cs \
        Gui/Dialog/VideoEditionProperties.cs \
+       Gui/Dialog/VideoConversionTool.cs \
        Gui/Dialog/Win32CalendarDialog.cs \
        Gui/Popup/CalendarPopup.cs \
        Gui/Popup/MessagePopup.cs \
diff --git a/LongoMatch.GUI/gtk-gui/LongoMatch.Gui.Dialog.VideoConversionTool.cs 
b/LongoMatch.GUI/gtk-gui/LongoMatch.Gui.Dialog.VideoConversionTool.cs
new file mode 100644
index 0000000..2b8eb17
--- /dev/null
+++ b/LongoMatch.GUI/gtk-gui/LongoMatch.Gui.Dialog.VideoConversionTool.cs
@@ -0,0 +1,226 @@
+
+// This file has been generated by the GUI designer. Do not modify.
+namespace LongoMatch.Gui.Dialog
+{
+       public partial class VideoConversionTool
+       {
+               private global::Gtk.VBox vbox2;
+               private global::Gtk.HBox hbox1;
+               private global::Gtk.ScrolledWindow GtkScrolledWindow;
+               private global::Gtk.TreeView treeview1;
+               private global::Gtk.VBox vbox3;
+               private global::Gtk.Button addbutton;
+               private global::Gtk.Button removebutton;
+               private global::Gtk.VBox vbox4;
+               private global::Gtk.HBox hbox2;
+               private global::Gtk.Label label1;
+               private global::Gtk.ComboBox qualitycombobox;
+               private global::Gtk.HBox filebox;
+               private global::Gtk.Label filenamelabel;
+               private global::Gtk.HBox hbox3;
+               private global::Gtk.Label filelabel;
+               private global::Gtk.Button openbutton;
+               private global::Gtk.Button buttonCancel;
+               private global::Gtk.Button buttonOk;
+               
+               protected virtual void Build ()
+               {
+                       global::Stetic.Gui.Initialize (this);
+                       // Widget LongoMatch.Gui.Dialog.VideoConversionTool
+                       this.Name = "LongoMatch.Gui.Dialog.VideoConversionTool";
+                       this.Title = global::Mono.Unix.Catalog.GetString ("Video converter tool");
+                       this.Icon = global::Gdk.Pixbuf.LoadFromResource ("logo.svg");
+                       this.WindowPosition = ((global::Gtk.WindowPosition)(4));
+                       this.Modal = true;
+                       this.DestroyWithParent = true;
+                       this.Gravity = ((global::Gdk.Gravity)(5));
+                       this.SkipPagerHint = true;
+                       this.SkipTaskbarHint = true;
+                       // Internal child LongoMatch.Gui.Dialog.VideoConversionTool.VBox
+                       global::Gtk.VBox w1 = this.VBox;
+                       w1.Name = "dialog1_VBox";
+                       w1.BorderWidth = ((uint)(2));
+                       // Container child dialog1_VBox.Gtk.Box+BoxChild
+                       this.vbox2 = new global::Gtk.VBox ();
+                       this.vbox2.Name = "vbox2";
+                       this.vbox2.Spacing = 6;
+                       // Container child vbox2.Gtk.Box+BoxChild
+                       this.hbox1 = new global::Gtk.HBox ();
+                       this.hbox1.Name = "hbox1";
+                       this.hbox1.Spacing = 6;
+                       // Container child hbox1.Gtk.Box+BoxChild
+                       this.GtkScrolledWindow = new global::Gtk.ScrolledWindow ();
+                       this.GtkScrolledWindow.Name = "GtkScrolledWindow";
+                       this.GtkScrolledWindow.ShadowType = ((global::Gtk.ShadowType)(1));
+                       // Container child GtkScrolledWindow.Gtk.Container+ContainerChild
+                       this.treeview1 = new global::Gtk.TreeView ();
+                       this.treeview1.CanFocus = true;
+                       this.treeview1.Name = "treeview1";
+                       this.GtkScrolledWindow.Add (this.treeview1);
+                       this.hbox1.Add (this.GtkScrolledWindow);
+                       global::Gtk.Box.BoxChild w3 = ((global::Gtk.Box.BoxChild)(this.hbox1 
[this.GtkScrolledWindow]));
+                       w3.Position = 0;
+                       // Container child hbox1.Gtk.Box+BoxChild
+                       this.vbox3 = new global::Gtk.VBox ();
+                       this.vbox3.Name = "vbox3";
+                       this.vbox3.Spacing = 6;
+                       // Container child vbox3.Gtk.Box+BoxChild
+                       this.addbutton = new global::Gtk.Button ();
+                       this.addbutton.CanFocus = true;
+                       this.addbutton.Name = "addbutton";
+                       this.addbutton.UseStock = true;
+                       this.addbutton.UseUnderline = true;
+                       this.addbutton.Label = "gtk-add";
+                       this.vbox3.Add (this.addbutton);
+                       global::Gtk.Box.BoxChild w4 = ((global::Gtk.Box.BoxChild)(this.vbox3 
[this.addbutton]));
+                       w4.Position = 0;
+                       w4.Expand = false;
+                       w4.Fill = false;
+                       // Container child vbox3.Gtk.Box+BoxChild
+                       this.removebutton = new global::Gtk.Button ();
+                       this.removebutton.CanFocus = true;
+                       this.removebutton.Name = "removebutton";
+                       this.removebutton.UseStock = true;
+                       this.removebutton.UseUnderline = true;
+                       this.removebutton.Label = "gtk-remove";
+                       this.vbox3.Add (this.removebutton);
+                       global::Gtk.Box.BoxChild w5 = ((global::Gtk.Box.BoxChild)(this.vbox3 
[this.removebutton]));
+                       w5.Position = 1;
+                       w5.Expand = false;
+                       w5.Fill = false;
+                       this.hbox1.Add (this.vbox3);
+                       global::Gtk.Box.BoxChild w6 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.vbox3]));
+                       w6.Position = 1;
+                       w6.Expand = false;
+                       w6.Fill = false;
+                       this.vbox2.Add (this.hbox1);
+                       global::Gtk.Box.BoxChild w7 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.hbox1]));
+                       w7.Position = 0;
+                       // Container child vbox2.Gtk.Box+BoxChild
+                       this.vbox4 = new global::Gtk.VBox ();
+                       this.vbox4.Name = "vbox4";
+                       this.vbox4.Spacing = 6;
+                       // Container child vbox4.Gtk.Box+BoxChild
+                       this.hbox2 = new global::Gtk.HBox ();
+                       this.hbox2.Name = "hbox2";
+                       this.hbox2.Homogeneous = true;
+                       this.hbox2.Spacing = 6;
+                       // Container child hbox2.Gtk.Box+BoxChild
+                       this.label1 = new global::Gtk.Label ();
+                       this.label1.Name = "label1";
+                       this.label1.Xalign = 0F;
+                       this.label1.LabelProp = global::Mono.Unix.Catalog.GetString ("Video Quality:");
+                       this.hbox2.Add (this.label1);
+                       global::Gtk.Box.BoxChild w8 = ((global::Gtk.Box.BoxChild)(this.hbox2 [this.label1]));
+                       w8.Position = 0;
+                       // Container child hbox2.Gtk.Box+BoxChild
+                       this.qualitycombobox = global::Gtk.ComboBox.NewText ();
+                       this.qualitycombobox.AppendText (global::Mono.Unix.Catalog.GetString ("Low"));
+                       this.qualitycombobox.AppendText (global::Mono.Unix.Catalog.GetString ("Normal"));
+                       this.qualitycombobox.AppendText (global::Mono.Unix.Catalog.GetString ("Good"));
+                       this.qualitycombobox.AppendText (global::Mono.Unix.Catalog.GetString ("Extra"));
+                       this.qualitycombobox.Name = "qualitycombobox";
+                       this.qualitycombobox.Active = 1;
+                       this.hbox2.Add (this.qualitycombobox);
+                       global::Gtk.Box.BoxChild w9 = ((global::Gtk.Box.BoxChild)(this.hbox2 
[this.qualitycombobox]));
+                       w9.Position = 1;
+                       this.vbox4.Add (this.hbox2);
+                       global::Gtk.Box.BoxChild w10 = ((global::Gtk.Box.BoxChild)(this.vbox4 [this.hbox2]));
+                       w10.Position = 0;
+                       w10.Expand = false;
+                       w10.Fill = false;
+                       // Container child vbox4.Gtk.Box+BoxChild
+                       this.filebox = new global::Gtk.HBox ();
+                       this.filebox.Name = "filebox";
+                       this.filebox.Spacing = 6;
+                       // Container child filebox.Gtk.Box+BoxChild
+                       this.filenamelabel = new global::Gtk.Label ();
+                       this.filenamelabel.Name = "filenamelabel";
+                       this.filenamelabel.LabelProp = global::Mono.Unix.Catalog.GetString ("File name: ");
+                       this.filebox.Add (this.filenamelabel);
+                       global::Gtk.Box.BoxChild w11 = ((global::Gtk.Box.BoxChild)(this.filebox 
[this.filenamelabel]));
+                       w11.Position = 0;
+                       w11.Expand = false;
+                       w11.Fill = false;
+                       // Container child filebox.Gtk.Box+BoxChild
+                       this.hbox3 = new global::Gtk.HBox ();
+                       this.hbox3.Name = "hbox3";
+                       this.hbox3.Spacing = 6;
+                       // Container child hbox3.Gtk.Box+BoxChild
+                       this.filelabel = new global::Gtk.Label ();
+                       this.filelabel.Name = "filelabel";
+                       this.hbox3.Add (this.filelabel);
+                       global::Gtk.Box.BoxChild w12 = ((global::Gtk.Box.BoxChild)(this.hbox3 
[this.filelabel]));
+                       w12.Position = 0;
+                       // Container child hbox3.Gtk.Box+BoxChild
+                       this.openbutton = new global::Gtk.Button ();
+                       this.openbutton.CanFocus = true;
+                       this.openbutton.Name = "openbutton";
+                       this.openbutton.UseStock = true;
+                       this.openbutton.UseUnderline = true;
+                       this.openbutton.Label = "gtk-save-as";
+                       this.hbox3.Add (this.openbutton);
+                       global::Gtk.Box.BoxChild w13 = ((global::Gtk.Box.BoxChild)(this.hbox3 
[this.openbutton]));
+                       w13.Position = 1;
+                       w13.Expand = false;
+                       w13.Fill = false;
+                       this.filebox.Add (this.hbox3);
+                       global::Gtk.Box.BoxChild w14 = ((global::Gtk.Box.BoxChild)(this.filebox 
[this.hbox3]));
+                       w14.Position = 1;
+                       this.vbox4.Add (this.filebox);
+                       global::Gtk.Box.BoxChild w15 = ((global::Gtk.Box.BoxChild)(this.vbox4 
[this.filebox]));
+                       w15.Position = 1;
+                       w15.Expand = false;
+                       w15.Fill = false;
+                       this.vbox2.Add (this.vbox4);
+                       global::Gtk.Box.BoxChild w16 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.vbox4]));
+                       w16.Position = 1;
+                       w16.Expand = false;
+                       w16.Fill = false;
+                       w1.Add (this.vbox2);
+                       global::Gtk.Box.BoxChild w17 = ((global::Gtk.Box.BoxChild)(w1 [this.vbox2]));
+                       w17.Position = 0;
+                       // Internal child LongoMatch.Gui.Dialog.VideoConversionTool.ActionArea
+                       global::Gtk.HButtonBox w18 = this.ActionArea;
+                       w18.Name = "dialog1_ActionArea";
+                       w18.Spacing = 10;
+                       w18.BorderWidth = ((uint)(5));
+                       w18.LayoutStyle = ((global::Gtk.ButtonBoxStyle)(4));
+                       // Container child dialog1_ActionArea.Gtk.ButtonBox+ButtonBoxChild
+                       this.buttonCancel = new global::Gtk.Button ();
+                       this.buttonCancel.CanDefault = true;
+                       this.buttonCancel.CanFocus = true;
+                       this.buttonCancel.Name = "buttonCancel";
+                       this.buttonCancel.UseStock = true;
+                       this.buttonCancel.UseUnderline = true;
+                       this.buttonCancel.Label = "gtk-cancel";
+                       this.AddActionWidget (this.buttonCancel, -6);
+                       global::Gtk.ButtonBox.ButtonBoxChild w19 = 
((global::Gtk.ButtonBox.ButtonBoxChild)(w18 [this.buttonCancel]));
+                       w19.Expand = false;
+                       w19.Fill = false;
+                       // Container child dialog1_ActionArea.Gtk.ButtonBox+ButtonBoxChild
+                       this.buttonOk = new global::Gtk.Button ();
+                       this.buttonOk.CanDefault = true;
+                       this.buttonOk.CanFocus = true;
+                       this.buttonOk.Name = "buttonOk";
+                       this.buttonOk.UseStock = true;
+                       this.buttonOk.UseUnderline = true;
+                       this.buttonOk.Label = "gtk-ok";
+                       this.AddActionWidget (this.buttonOk, -5);
+                       global::Gtk.ButtonBox.ButtonBoxChild w20 = 
((global::Gtk.ButtonBox.ButtonBoxChild)(w18 [this.buttonOk]));
+                       w20.Position = 1;
+                       w20.Expand = false;
+                       w20.Fill = false;
+                       if ((this.Child != null)) {
+                               this.Child.ShowAll ();
+                       }
+                       this.DefaultWidth = 400;
+                       this.DefaultHeight = 300;
+                       this.Show ();
+                       this.addbutton.Clicked += new global::System.EventHandler (this.OnAddbuttonClicked);
+                       this.removebutton.Clicked += new global::System.EventHandler 
(this.OnRemovebuttonClicked);
+                       this.openbutton.Clicked += new global::System.EventHandler (this.OnOpenbuttonClicked);
+                       this.buttonOk.Clicked += new global::System.EventHandler (this.OnButtonOkClicked);
+               }
+       }
+}
diff --git a/LongoMatch.GUI/gtk-gui/LongoMatch.Gui.MainWindow.cs 
b/LongoMatch.GUI/gtk-gui/LongoMatch.Gui.MainWindow.cs
index 56b512b..e474c5b 100644
--- a/LongoMatch.GUI/gtk-gui/LongoMatch.Gui.MainWindow.cs
+++ b/LongoMatch.GUI/gtk-gui/LongoMatch.Gui.MainWindow.cs
@@ -36,6 +36,7 @@ namespace LongoMatch.Gui
                private global::Gtk.Action dialogInfoAction;
                private global::Gtk.Action ImportFromFileAction;
                private global::Gtk.ToggleAction TagSubcategoriesAction;
+               private global::Gtk.Action VideoConverterToolAction;
                private global::Gtk.VBox vbox1;
                private global::Gtk.VBox menubox;
                private global::Gtk.MenuBar menubar1;
@@ -168,6 +169,9 @@ namespace LongoMatch.Gui
                        this.TagSubcategoriesAction.Active = true;
                        this.TagSubcategoriesAction.ShortLabel = global::Mono.Unix.Catalog.GetString ("Tag 
subcategories");
                        w1.Add (this.TagSubcategoriesAction, null);
+                       this.VideoConverterToolAction = new global::Gtk.Action ("VideoConverterToolAction", 
global::Mono.Unix.Catalog.GetString ("Video Converter Tool"), null, null);
+                       this.VideoConverterToolAction.ShortLabel = global::Mono.Unix.Catalog.GetString 
("Video Converter Tool");
+                       w1.Add (this.VideoConverterToolAction, null);
                        this.UIManager.InsertActionGroup (w1, 0);
                        this.AddAccelGroup (this.UIManager.AccelGroup);
                        this.Name = "LongoMatch.Gui.MainWindow";
@@ -184,7 +188,7 @@ namespace LongoMatch.Gui
                        this.menubox.Name = "menubox";
                        this.menubox.Spacing = 6;
                        // Container child menubox.Gtk.Box+BoxChild
-                       this.UIManager.AddUiFromString ("<ui><menubar name='menubar1'><menu name='FileAction' 
action='FileAction'><menuitem name='NewPojectAction' action='NewPojectAction'/><menuitem name='openAction' 
action='openAction'/><menuitem name='SaveProjectAction' action='SaveProjectAction'/><menuitem 
name='CloseProjectAction' action='CloseProjectAction'/><separator/><menu name='ImportProjectAction' 
action='ImportProjectAction'/><separator/><menuitem name='QuitAction' action='QuitAction'/></menu><menu 
name='ToolsAction' action='ToolsAction'><menuitem name='ProjectsManagerAction' 
action='ProjectsManagerAction'/><menuitem name='CategoriesTemplatesManagerAction' 
action='CategoriesTemplatesManagerAction'/><menuitem name='TeamsTemplatesManagerAction' 
action='TeamsTemplatesManagerAction'/><menu name='ExportProjectAction1' 
action='ExportProjectAction1'><menuitem name='ExportToProjectFileAction' 
action='ExportToProjectFileAction'/></menu></menu><menu name='ViewAction' action='ViewAction'><menuit
 em name='FullScreenAction' action='FullScreenAction'/><menuitem name='HideAllWidgetsAction' 
action='HideAllWidgetsAction'/><separator/><menuitem name='PlaylistAction' 
action='PlaylistAction'/><separator/><menuitem name='TagSubcategoriesAction' 
action='TagSubcategoriesAction'/><menuitem name='TaggingViewAction' action='TaggingViewAction'/><menuitem 
name='ManualTaggingViewAction' action='ManualTaggingViewAction'/><menuitem name='TimelineViewAction' 
action='TimelineViewAction'/><menuitem name='GameUnitsViewAction' action='GameUnitsViewAction'/></menu><menu 
name='HelpAction' action='HelpAction'><menuitem name='AboutAction' action='AboutAction'/><menuitem 
name='HelpAction1' action='HelpAction1'/><menuitem name='dialogInfoAction' 
action='dialogInfoAction'/></menu></menubar></ui>");
+                       this.UIManager.AddUiFromString ("<ui><menubar name='menubar1'><menu name='FileAction' 
action='FileAction'><menuitem name='NewPojectAction' action='NewPojectAction'/><menuitem name='openAction' 
action='openAction'/><menuitem name='SaveProjectAction' action='SaveProjectAction'/><menuitem 
name='CloseProjectAction' action='CloseProjectAction'/><separator/><menu name='ImportProjectAction' 
action='ImportProjectAction'/><separator/><menuitem name='QuitAction' action='QuitAction'/></menu><menu 
name='ToolsAction' action='ToolsAction'><menuitem name='ProjectsManagerAction' 
action='ProjectsManagerAction'/><menuitem name='CategoriesTemplatesManagerAction' 
action='CategoriesTemplatesManagerAction'/><menuitem name='TeamsTemplatesManagerAction' 
action='TeamsTemplatesManagerAction'/><menu name='ExportProjectAction1' 
action='ExportProjectAction1'><menuitem name='ExportToProjectFileAction' 
action='ExportToProjectFileAction'/></menu><separator/><menuitem name='VideoConverterToolAction' acti
 on='VideoConverterToolAction'/></menu><menu name='ViewAction' action='ViewAction'><menuitem 
name='FullScreenAction' action='FullScreenAction'/><menuitem name='HideAllWidgetsAction' 
action='HideAllWidgetsAction'/><separator/><menuitem name='PlaylistAction' 
action='PlaylistAction'/><separator/><menuitem name='TagSubcategoriesAction' 
action='TagSubcategoriesAction'/><menuitem name='TaggingViewAction' action='TaggingViewAction'/><menuitem 
name='ManualTaggingViewAction' action='ManualTaggingViewAction'/><menuitem name='TimelineViewAction' 
action='TimelineViewAction'/><menuitem name='GameUnitsViewAction' action='GameUnitsViewAction'/></menu><menu 
name='HelpAction' action='HelpAction'><menuitem name='AboutAction' action='AboutAction'/><menuitem 
name='HelpAction1' action='HelpAction1'/><menuitem name='dialogInfoAction' 
action='dialogInfoAction'/></menu></menubar></ui>");
                        this.menubar1 = ((global::Gtk.MenuBar)(this.UIManager.GetWidget ("/menubar1")));
                        this.menubar1.Name = "menubar1";
                        this.menubox.Add (this.menubar1);
@@ -349,6 +353,7 @@ namespace LongoMatch.Gui
                        this.GameUnitsViewAction.Toggled += new global::System.EventHandler 
(this.OnViewToggled);
                        this.dialogInfoAction.Activated += new global::System.EventHandler 
(this.OnDialogInfoActionActivated);
                        this.TagSubcategoriesAction.Toggled += new global::System.EventHandler 
(this.OnTagSubcategoriesActionToggled);
+                       this.VideoConverterToolAction.Activated += new global::System.EventHandler 
(this.OnVideoConverterToolActionActivated);
                }
        }
 }
diff --git a/LongoMatch.GUI/gtk-gui/gui.stetic b/LongoMatch.GUI/gtk-gui/gui.stetic
index 34f21c9..41d7bfd 100644
--- a/LongoMatch.GUI/gtk-gui/gui.stetic
+++ b/LongoMatch.GUI/gtk-gui/gui.stetic
@@ -1859,6 +1859,12 @@
         <property name="Active">True</property>
         <signal name="Toggled" handler="OnTagSubcategoriesActionToggled" />
       </action>
+      <action id="VideoConverterToolAction">
+        <property name="Type">Action</property>
+        <property name="Label" translatable="yes">Video Converter Tool</property>
+        <property name="ShortLabel" translatable="yes">Video Converter Tool</property>
+        <signal name="Activated" handler="OnVideoConverterToolActionActivated" />
+      </action>
     </action-group>
     <property name="MemberName" />
     <property name="Title" translatable="yes">LongoMatch</property>
@@ -1894,6 +1900,8 @@
                     <node type="Menu" action="ExportProjectAction1">
                       <node type="Menuitem" action="ExportToProjectFileAction" />
                     </node>
+                    <node type="Separator" />
+                    <node type="Menuitem" action="VideoConverterToolAction" />
                   </node>
                   <node type="Menu" action="ViewAction">
                     <node type="Menuitem" action="FullScreenAction" />
@@ -7126,4 +7134,260 @@ Defining &lt;b&gt; Game Units &lt;/b&gt; will help you during the analysis to in
       </widget>
     </child>
   </widget>
+  <widget class="Gtk.Dialog" id="LongoMatch.Gui.Dialog.VideoConversionTool" design-size="400 300">
+    <property name="MemberName" />
+    <property name="Title" translatable="yes">Video converter tool</property>
+    <property name="Icon">resource:logo.svg</property>
+    <property name="WindowPosition">CenterOnParent</property>
+    <property name="Modal">True</property>
+    <property name="DestroyWithParent">True</property>
+    <property name="Gravity">Center</property>
+    <property name="SkipPagerHint">True</property>
+    <property name="SkipTaskbarHint">True</property>
+    <property name="Buttons">2</property>
+    <property name="HelpButton">False</property>
+    <child internal-child="VBox">
+      <widget class="Gtk.VBox" id="dialog1_VBox">
+        <property name="MemberName" />
+        <property name="BorderWidth">2</property>
+        <child>
+          <widget class="Gtk.VBox" id="vbox2">
+            <property name="MemberName" />
+            <property name="Spacing">6</property>
+            <child>
+              <widget class="Gtk.HBox" id="hbox1">
+                <property name="MemberName" />
+                <property name="Spacing">6</property>
+                <child>
+                  <widget class="Gtk.ScrolledWindow" id="GtkScrolledWindow">
+                    <property name="MemberName" />
+                    <property name="ShadowType">In</property>
+                    <child>
+                      <widget class="Gtk.TreeView" id="treeview1">
+                        <property name="MemberName" />
+                        <property name="CanFocus">True</property>
+                        <property name="ShowScrollbars">True</property>
+                      </widget>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="Position">0</property>
+                    <property name="AutoSize">True</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="Gtk.VBox" id="vbox3">
+                    <property name="MemberName" />
+                    <property name="Spacing">6</property>
+                    <child>
+                      <widget class="Gtk.Button" id="addbutton">
+                        <property name="MemberName" />
+                        <property name="CanFocus">True</property>
+                        <property name="UseStock">True</property>
+                        <property name="Type">StockItem</property>
+                        <property name="StockId">gtk-add</property>
+                        <signal name="Clicked" handler="OnAddbuttonClicked" />
+                        <property name="label">gtk-add</property>
+                      </widget>
+                      <packing>
+                        <property name="Position">0</property>
+                        <property name="AutoSize">True</property>
+                        <property name="Expand">False</property>
+                        <property name="Fill">False</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="Gtk.Button" id="removebutton">
+                        <property name="MemberName" />
+                        <property name="CanFocus">True</property>
+                        <property name="UseStock">True</property>
+                        <property name="Type">StockItem</property>
+                        <property name="StockId">gtk-remove</property>
+                        <signal name="Clicked" handler="OnRemovebuttonClicked" />
+                        <property name="label">gtk-remove</property>
+                      </widget>
+                      <packing>
+                        <property name="Position">1</property>
+                        <property name="AutoSize">True</property>
+                        <property name="Expand">False</property>
+                        <property name="Fill">False</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="Position">1</property>
+                    <property name="AutoSize">True</property>
+                    <property name="Expand">False</property>
+                    <property name="Fill">False</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="Position">0</property>
+                <property name="AutoSize">False</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="Gtk.VBox" id="vbox4">
+                <property name="MemberName" />
+                <property name="Spacing">6</property>
+                <child>
+                  <widget class="Gtk.HBox" id="hbox2">
+                    <property name="MemberName" />
+                    <property name="Homogeneous">True</property>
+                    <property name="Spacing">6</property>
+                    <child>
+                      <widget class="Gtk.Label" id="label1">
+                        <property name="MemberName" />
+                        <property name="Xalign">0</property>
+                        <property name="LabelProp" translatable="yes">Video Quality:</property>
+                      </widget>
+                      <packing>
+                        <property name="Position">0</property>
+                        <property name="AutoSize">False</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="Gtk.ComboBox" id="qualitycombobox">
+                        <property name="MemberName" />
+                        <property name="IsTextCombo">True</property>
+                        <property name="Items" translatable="yes">Low
+Normal
+Good
+Extra</property>
+                        <property name="Active">1</property>
+                      </widget>
+                      <packing>
+                        <property name="Position">1</property>
+                        <property name="AutoSize">False</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="Position">0</property>
+                    <property name="AutoSize">True</property>
+                    <property name="Expand">False</property>
+                    <property name="Fill">False</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="Gtk.HBox" id="filebox">
+                    <property name="MemberName" />
+                    <property name="Spacing">6</property>
+                    <child>
+                      <widget class="Gtk.Label" id="filenamelabel">
+                        <property name="MemberName" />
+                        <property name="LabelProp" translatable="yes">File name: </property>
+                      </widget>
+                      <packing>
+                        <property name="Position">0</property>
+                        <property name="AutoSize">False</property>
+                        <property name="Expand">False</property>
+                        <property name="Fill">False</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="Gtk.HBox" id="hbox3">
+                        <property name="MemberName" />
+                        <property name="Spacing">6</property>
+                        <child>
+                          <widget class="Gtk.Label" id="filelabel">
+                            <property name="MemberName" />
+                          </widget>
+                          <packing>
+                            <property name="Position">0</property>
+                            <property name="AutoSize">False</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="Gtk.Button" id="openbutton">
+                            <property name="MemberName" />
+                            <property name="CanFocus">True</property>
+                            <property name="UseStock">True</property>
+                            <property name="Type">StockItem</property>
+                            <property name="StockId">gtk-save-as</property>
+                            <signal name="Clicked" handler="OnOpenbuttonClicked" />
+                            <property name="label">gtk-save-as</property>
+                          </widget>
+                          <packing>
+                            <property name="Position">1</property>
+                            <property name="AutoSize">True</property>
+                            <property name="Expand">False</property>
+                            <property name="Fill">False</property>
+                          </packing>
+                        </child>
+                      </widget>
+                      <packing>
+                        <property name="Position">1</property>
+                        <property name="AutoSize">False</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="Position">1</property>
+                    <property name="AutoSize">True</property>
+                    <property name="Expand">False</property>
+                    <property name="Fill">False</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="Position">1</property>
+                <property name="AutoSize">True</property>
+                <property name="Expand">False</property>
+                <property name="Fill">False</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="Position">0</property>
+            <property name="AutoSize">False</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+    <child internal-child="ActionArea">
+      <widget class="Gtk.HButtonBox" id="dialog1_ActionArea">
+        <property name="MemberName" />
+        <property name="Spacing">10</property>
+        <property name="BorderWidth">5</property>
+        <property name="Size">2</property>
+        <property name="LayoutStyle">End</property>
+        <child>
+          <widget class="Gtk.Button" id="buttonCancel">
+            <property name="MemberName" />
+            <property name="CanDefault">True</property>
+            <property name="CanFocus">True</property>
+            <property name="UseStock">True</property>
+            <property name="Type">StockItem</property>
+            <property name="StockId">gtk-cancel</property>
+            <property name="ResponseId">-6</property>
+            <property name="label">gtk-cancel</property>
+          </widget>
+          <packing>
+            <property name="Expand">False</property>
+            <property name="Fill">False</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="Gtk.Button" id="buttonOk">
+            <property name="MemberName" />
+            <property name="CanDefault">True</property>
+            <property name="CanFocus">True</property>
+            <property name="UseStock">True</property>
+            <property name="Type">StockItem</property>
+            <property name="StockId">gtk-ok</property>
+            <property name="ResponseId">-5</property>
+            <signal name="Clicked" handler="OnButtonOkClicked" />
+            <property name="label">gtk-ok</property>
+          </widget>
+          <packing>
+            <property name="Position">1</property>
+            <property name="Expand">False</property>
+            <property name="Fill">False</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
 </stetic-interface>
\ No newline at end of file
diff --git a/LongoMatch.Multimedia/Converter/GstVideoConverter.cs 
b/LongoMatch.Multimedia/Converter/GstVideoConverter.cs
new file mode 100644
index 0000000..600ead6
--- /dev/null
+++ b/LongoMatch.Multimedia/Converter/GstVideoConverter.cs
@@ -0,0 +1,260 @@
+//  Copyright (C) 2007-2009 Andoni Morales Alastruey
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+//
+
+namespace LongoMatch.Video.Converter {
+
+       using System;
+       using System.Collections;
+       using System.Collections.Generic;
+       using System.Runtime.InteropServices;
+       using Mono.Unix;
+       
+       using LongoMatch.Interfaces;
+       using LongoMatch.Common;
+       using LongoMatch.Interfaces.Multimedia;
+       using LongoMatch.Video.Common;
+       
+
+       #region Autogenerated code
+       public  class GstVideoConverter: GLib.Object, IVideoConverter {
+       
+               public event LongoMatch.Handlers.ProgressHandler Progress;
+               public event LongoMatch.Handlers.ErrorHandler Error;
+
+               [DllImport("libcesarplayer.dll")]
+               static extern unsafe IntPtr gst_video_encoder_new(IntPtr filename, out IntPtr err);
+
+               public unsafe GstVideoConverter(string filename) : base(IntPtr.Zero)
+               {
+                       if(GetType() != typeof(GstVideoConverter)) {
+                               throw new InvalidOperationException("Can't override this constructor.");
+                       }
+                       IntPtr error = IntPtr.Zero;
+                       Raw = gst_video_encoder_new (GLib.Marshaller.StringToPtrGStrdup(filename), out error);
+                       if(error != IntPtr.Zero) throw new GLib.GException(error);
+                       
+                       PercentCompleted += delegate(object o, PercentCompletedArgs args) {
+                               if(Progress!= null)
+                                       Progress(args.Percent);
+                       };
+                       InternalError += delegate(object o, ErrorArgs args) {
+                               if (Error != null)
+                                       Error (o, args.Message);
+                       };
+               }
+
+#pragma warning disable 0169
+               [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+               delegate void ErrorSignalDelegate(IntPtr arg0, IntPtr arg1, IntPtr gch);
+
+               static void ErrorSignalCallback(IntPtr arg0, IntPtr arg1, IntPtr gch)
+               {
+                       ErrorArgs args = new ErrorArgs();
+                       try {
+                               GLib.Signal sig = ((GCHandle) gch).Target as GLib.Signal;
+                               if(sig == null)
+                                       throw new Exception("Unknown signal GC handle received " + gch);
+
+                               args.Args = new object[1];
+                               args.Args[0] = GLib.Marshaller.Utf8PtrToString(arg1);
+                               ErrorHandler handler = (ErrorHandler) sig.Handler;
+                               handler(GLib.Object.GetObject(arg0), args);
+                       } catch(Exception e) {
+                               GLib.ExceptionManager.RaiseUnhandledException(e, false);
+                       }
+               }
+
+               [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+               delegate void ErrorVMDelegate(IntPtr gcc, IntPtr message);
+
+               static ErrorVMDelegate ErrorVMCallback;
+
+               static void error_cb(IntPtr gcc, IntPtr message)
+               {
+                       try {
+                               GstVideoConverter gcc_managed = GLib.Object.GetObject(gcc, false) as 
GstVideoConverter;
+                               gcc_managed.OnError(GLib.Marshaller.Utf8PtrToString(message));
+                       } catch(Exception e) {
+                               GLib.ExceptionManager.RaiseUnhandledException(e, false);
+                       }
+               }
+
+               private static void OverrideError(GLib.GType gtype)
+               {
+                       if(ErrorVMCallback == null)
+                               ErrorVMCallback = new ErrorVMDelegate(error_cb);
+                       OverrideVirtualMethod(gtype, "error", ErrorVMCallback);
+               }
+
+               [GLib.DefaultSignalHandler(Type=typeof(LongoMatch.Video.Converter.GstVideoConverter), 
ConnectionMethod="OverrideError")]
+               protected virtual void OnError(string message)
+               {
+                       GLib.Value ret = GLib.Value.Empty;
+                       GLib.ValueArray inst_and_params = new GLib.ValueArray(2);
+                       GLib.Value[] vals = new GLib.Value [2];
+                       vals [0] = new GLib.Value(this);
+                       inst_and_params.Append(vals [0]);
+                       vals [1] = new GLib.Value(message);
+                       inst_and_params.Append(vals [1]);
+                       g_signal_chain_from_overridden(inst_and_params.ArrayPtr, ref ret);
+                       foreach(GLib.Value v in vals)
+                               v.Dispose();
+               }
+
+               [GLib.Signal("error")]
+               public event ErrorHandler InternalError {
+                       add {
+                               GLib.Signal sig = GLib.Signal.Lookup(this, "error", new 
ErrorSignalDelegate(ErrorSignalCallback));
+                               sig.AddDelegate(value);
+                       }
+                       remove {
+                               GLib.Signal sig = GLib.Signal.Lookup(this, "error", new 
ErrorSignalDelegate(ErrorSignalCallback));
+                               sig.RemoveDelegate(value);
+                       }
+               }
+
+               [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+               delegate void PercentCompletedVMDelegate(IntPtr gvc, float percent);
+
+               static PercentCompletedVMDelegate PercentCompletedVMCallback;
+
+               static void percentcompleted_cb(IntPtr gvc, float percent)
+               {
+                       try {
+                               GstVideoConverter gvc_managed = GLib.Object.GetObject(gvc, false) as 
GstVideoConverter;
+                               gvc_managed.OnPercentCompleted(percent);
+                       } catch(Exception e) {
+                               GLib.ExceptionManager.RaiseUnhandledException(e, false);
+                       }
+               }
+
+               private static void OverridePercentCompleted(GLib.GType gtype)
+               {
+                       if(PercentCompletedVMCallback == null)
+                               PercentCompletedVMCallback = new 
PercentCompletedVMDelegate(percentcompleted_cb);
+                       OverrideVirtualMethod(gtype, "percent_completed", PercentCompletedVMCallback);
+               }
+
+               [GLib.DefaultSignalHandler(Type=typeof(LongoMatch.Video.Converter.GstVideoConverter), 
ConnectionMethod="OverridePercentCompleted")]
+               protected virtual void OnPercentCompleted(float percent)
+               {
+                       GLib.Value ret = GLib.Value.Empty;
+                       GLib.ValueArray inst_and_params = new GLib.ValueArray(2);
+                       GLib.Value[] vals = new GLib.Value [2];
+                       vals [0] = new GLib.Value(this);
+                       inst_and_params.Append(vals [0]);
+                       vals [1] = new GLib.Value(percent);
+                       inst_and_params.Append(vals [1]);
+                       g_signal_chain_from_overridden(inst_and_params.ArrayPtr, ref ret);
+                       foreach(GLib.Value v in vals)
+                               v.Dispose();
+               }
+
+               [GLib.Signal("percent_completed")]
+               public event PercentCompletedHandler PercentCompleted {
+                       add {
+                               GLib.Signal sig = GLib.Signal.Lookup(this, "percent_completed", 
typeof(PercentCompletedArgs));
+                               sig.AddDelegate(value);
+                       }
+                       remove {
+                               GLib.Signal sig = GLib.Signal.Lookup(this, "percent_completed", 
typeof(PercentCompletedArgs));
+                               sig.RemoveDelegate(value);
+                       }
+               }
+#pragma warning restore 0169
+
+               [DllImport("libcesarplayer.dll")]
+               static extern void gst_video_encoder_init_backend(out int argc, IntPtr argv);
+
+               public static int InitBackend(string argv) {
+                       int argc;
+                       gst_video_encoder_init_backend(out argc, GLib.Marshaller.StringToPtrGStrdup(argv));
+                       return argc;
+               }
+
+               [DllImport("libcesarplayer.dll")]
+               static extern void gst_video_encoder_cancel(IntPtr raw);
+
+               public void Cancel() {
+                       gst_video_encoder_cancel(Handle);
+               }
+
+               [DllImport("libcesarplayer.dll")]
+               static extern void gst_video_encoder_start(IntPtr raw);
+
+               public void Start() {
+                       gst_video_encoder_start(Handle);
+               }
+
+               [DllImport("libcesarplayer.dll")]
+               static extern bool gst_video_encoder_set_encoding_format(IntPtr raw,
+                                                                        VideoEncoderType video_codec,
+                                                                        AudioEncoderType audio_codec,
+                                                                        VideoMuxerType muxer,
+                                                                        uint video_bitrate,
+                                                                        uint audio_bitrate,
+                                                                        uint height,
+                                                                        uint width,
+                                                                        uint fps_n,
+                                                                        uint fps_d);
+               
+               public EncodingSettings EncodingSettings {
+                       set {
+                               gst_video_encoder_set_encoding_format (Handle,
+                                                                      value.EncodingProfile.VideoEncoder,
+                                                                      value.EncodingProfile.AudioEncoder,
+                                                                      value.EncodingProfile.Muxer,
+                                                                      value.VideoBitrate,
+                                                                      value.AudioBitrate,
+                                                                      value.VideoStandard.Height,
+                                                                      value.VideoStandard.Width,
+                                                                      value.Framerate_n,
+                                                                      value.Framerate_d);
+                       }
+               }
+               
+               [DllImport("libcesarplayer.dll")]
+               static extern bool gst_video_encoder_add_file (IntPtr raw, IntPtr filename, long duration);
+               
+               public void AddFile (string filename, long duration) {
+                       if (!filename.StartsWith(Uri.UriSchemeFile)) {
+                               filename = "file://" + filename;
+                       }
+                       IntPtr file = GLib.Marshaller.StringToPtrGStrdup(filename);
+                       gst_video_encoder_add_file (Handle, file, duration);
+               }
+
+               [DllImport("libcesarplayer.dll")]
+               static extern IntPtr gst_video_encoder_get_type();
+
+               public static new GLib.GType GType {
+                       get {
+                               IntPtr raw_ret = gst_video_encoder_get_type();
+                               GLib.GType ret = new GLib.GType(raw_ret);
+                               return ret;
+                       }
+               }
+
+               static GstVideoConverter()
+               {
+                       LongoMatch.GtkSharp.Encoder.ObjectManager.Initialize();
+               }
+               #endregion
+               
+       }
+}
diff --git a/LongoMatch.Multimedia/Converter/ObjectManager.cs 
b/LongoMatch.Multimedia/Converter/ObjectManager.cs
new file mode 100644
index 0000000..8cc4ca1
--- /dev/null
+++ b/LongoMatch.Multimedia/Converter/ObjectManager.cs
@@ -0,0 +1,40 @@
+// ObjectManager.cs
+//
+//  Copyright (C) 2007-2009 Andoni Morales Alastruey
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+//
+// This file was generated by the Gtk# code generator.
+// Any changes made will be lost if regenerated.
+
+namespace LongoMatch.GtkSharp.Encoder {
+
+       public class ObjectManager {
+
+               static bool initialized = false;
+               // Call this method from the appropriate module init function.
+               public static void Initialize()
+               {
+                       if(initialized)
+                               return;
+
+                       initialized = true;
+
+                       GLib.GType.Register(LongoMatch.Video.Converter.GstVideoConverter.GType,
+                                           typeof(LongoMatch.Video.Converter.GstVideoConverter));
+               }
+       }
+}
diff --git a/LongoMatch.Multimedia/LongoMatch.Multimedia.mdp b/LongoMatch.Multimedia/LongoMatch.Multimedia.mdp
index e1d7fb3..ec9f164 100644
--- a/LongoMatch.Multimedia/LongoMatch.Multimedia.mdp
+++ b/LongoMatch.Multimedia/LongoMatch.Multimedia.mdp
@@ -59,6 +59,9 @@
     <File subtype="Code" buildaction="Compile" name="Remuxer/GstRemuxer.cs" />
     <File subtype="Code" buildaction="Compile" name="Remuxer/ObjectManager.cs" />
     <File subtype="Code" buildaction="Compile" name="Utils/Remuxer.cs" />
+    <File subtype="Directory" buildaction="Compile" name="Converter" />
+    <File subtype="Code" buildaction="Compile" name="Converter/GstVideoConverter.cs" />
+    <File subtype="Code" buildaction="Compile" name="Converter/ObjectManager.cs" />
   </Contents>
   <References>
     <ProjectReference type="Project" localcopy="True" refto="libcesarplayer" />
diff --git a/LongoMatch.Multimedia/Makefile.am b/LongoMatch.Multimedia/Makefile.am
index 075d19e..7019286 100644
--- a/LongoMatch.Multimedia/Makefile.am
+++ b/LongoMatch.Multimedia/Makefile.am
@@ -11,6 +11,8 @@ SOURCES = \
        Common/Constants.cs \
        Common/Enum.cs \
        Common/Handlers.cs \
+       Converter/GstVideoConverter.cs \
+       Converter/ObjectManager.cs \
        Editor/VideoSegment.cs \
        Editor/GstVideoSplitter.cs \
        Interfaces/ICapturer.cs \
diff --git a/LongoMatch.Multimedia/MultimediaFactory.cs b/LongoMatch.Multimedia/MultimediaFactory.cs
index b3dbed7..80413b1 100644
--- a/LongoMatch.Multimedia/MultimediaFactory.cs
+++ b/LongoMatch.Multimedia/MultimediaFactory.cs
@@ -30,6 +30,7 @@ using LongoMatch.Store;
 using LongoMatch.Video.Capturer;
 using LongoMatch.Video.Player;
 using LongoMatch.Video.Editor;
+using LongoMatch.Video.Converter;
 using LongoMatch.Video.Remuxer;
 using LongoMatch.Video.Utils;
 using LongoMatch.Video.Common;
@@ -101,6 +102,10 @@ namespace LongoMatch.Video
                        }
                }
 
+               public IVideoConverter GetVideoConverter(string filename) {
+                       return new GstVideoConverter (filename);
+               }
+
                public ICapturer GetCapturer(CapturerType type) {
                        switch(type) {
                        case CapturerType.Fake:
diff --git a/LongoMatch.Services/Services/PlaylistManager.cs b/LongoMatch.Services/Services/PlaylistManager.cs
index 6d244f4..4b82bde 100644
--- a/LongoMatch.Services/Services/PlaylistManager.cs
+++ b/LongoMatch.Services/Services/PlaylistManager.cs
@@ -171,7 +171,7 @@ namespace LongoMatch.Services
                
                protected virtual void OnRenderPlaylistEvent (IPlayList playlist)
                {
-                       List<Job> jobs = guiToolkit.ConfigureRenderingJob(playlist);
+                       List<EditionJob> jobs = guiToolkit.ConfigureRenderingJob(playlist);
                        foreach (Job job in jobs)
                                videoRenderer.AddJob(job);
                }
diff --git a/LongoMatch.Services/Services/RenderingJobsManager.cs 
b/LongoMatch.Services/Services/RenderingJobsManager.cs
index 7ec4620..15f7de4 100644
--- a/LongoMatch.Services/Services/RenderingJobsManager.cs
+++ b/LongoMatch.Services/Services/RenderingJobsManager.cs
@@ -32,6 +32,7 @@ namespace LongoMatch.Services
                /* List of pending jobs */
                List<Job> jobs, pendingJobs;
                IVideoEditor videoEditor;
+               IVideoConverter videoConverter;
                IFramesCapturer capturer;
                Job currentJob;
                IRenderingStateBar stateBar;
@@ -48,6 +49,10 @@ namespace LongoMatch.Services
                        pendingJobs = new List<Job>();
                        stateBar.Cancel += (sender, e) => CancelCurrentJob();
                        stateBar.ManageJobs += (sender, e) => ManageJobs();
+                       guiToolkit.MainWindow.ConvertVideoFilesEvent += delegate(List<MediaFile> inputFiles, 
EncodingSettings encSettings) {
+                               ConversionJob job = new ConversionJob(inputFiles, encSettings);
+                               AddJob (job);
+                       };; 
                }
                
                public List<Job> Jobs {
@@ -107,8 +112,14 @@ namespace LongoMatch.Services
                        if (currentJob != job) 
                                return;
                        
-                       videoEditor.Progress -= OnProgress;
-                       videoEditor.Cancel();
+                       if (job is EditionJob) {
+                               videoEditor.Progress -= OnProgress;
+                               videoEditor.Cancel();
+                       } else {
+                               videoConverter.Progress -= OnProgress;
+                               videoConverter.Error -= OnError;
+                               videoConverter.Cancel();
+                       }
                        job.State = JobState.Cancelled;
                        RemoveCurrentFromPending();
                        UpdateJobsStatus();
@@ -126,11 +137,56 @@ namespace LongoMatch.Services
                        guiToolkit.ManageJobs(this);
                }
                
-               private void LoadJob(Job job) {
+               private void LoadConversionJob (ConversionJob job) {
+                       videoConverter = multimediaToolkit.GetVideoConverter(job.EncodingSettings.OutputFile);
+                       videoConverter.Progress += OnProgress;
+                       videoConverter.EncodingSettings = job.EncodingSettings;
+                       videoConverter.Error += OnError;
+                       
+                       foreach(MediaFile file in job.InputFiles) {
+                               videoConverter.AddFile (file.FilePath, file.Length);
+                       }
+                       
+                       try {
+                               videoConverter.Start();
+                       } catch(Exception ex) {
+                               videoConverter.Cancel();
+                               job.State = JobState.Error;
+                               Log.Exception(ex);
+                               Log.Error("Error rendering job: ", job.Name);
+                               guiToolkit.ErrorMessage (Catalog.GetString("Error rendering job: ") + 
ex.Message);
+                       }
+               }
+
+               void OnError (object o, string message)
+               {
+                       Log.Error("Error rendering job: ", currentJob.Name);
+                       guiToolkit.ErrorMessage(Catalog.GetString("An error has occurred in the video 
renderer.")
+                                               + " " + Catalog.GetString("Please, try again."));
+               }
+               
+               private void LoadEditionJob(EditionJob job) {
+                       videoEditor = multimediaToolkit.GetVideoEditor();
+                       videoEditor.Progress += OnProgress;
+                       
                        foreach(PlayListPlay segment in job.Playlist) {
                                if (!ProcessSegment(segment))
                                        continue;
                        }
+                       
+                       try {
+                               videoEditor.EncodingSettings = job.EncodingSettings;
+                               videoEditor.EnableTitle = job.OverlayTitle;
+                               videoEditor.EnableAudio = job.EnableAudio;
+                               videoEditor.Start();
+                       }
+                       catch(Exception ex) {
+                               videoEditor.Cancel();
+                               job.State = JobState.Error;
+                               Log.Exception(ex);
+                               Log.Error("Error rendering job: ", job.Name);
+                               guiToolkit.ErrorMessage (Catalog.GetString("Error rendering job: ") + 
ex.Message);
+                       }
                }
                
                private bool ProcessSegment(PlayListPlay segment) {
@@ -198,23 +254,11 @@ namespace LongoMatch.Services
                                return;
                        }
                        
-                       videoEditor = multimediaToolkit.GetVideoEditor();
-                       videoEditor.Progress += OnProgress;
                        currentJob = pendingJobs[0];
-                       LoadJob(currentJob);
-                       
-                       try {
-                               videoEditor.EncodingSettings = currentJob.EncodingSettings;
-                               videoEditor.EnableTitle = currentJob.OverlayTitle;
-                               videoEditor.EnableAudio = currentJob.EnableAudio;
-                               videoEditor.Start();
-                       }
-                       catch(Exception ex) {
-                               videoEditor.Cancel();
-                               currentJob.State = JobState.Error;
-                               Log.Exception(ex);
-                               Log.Error("Error rendering job: ", currentJob.Name);
-                               guiToolkit.ErrorMessage (Catalog.GetString("Error rendering job: ") + 
ex.Message);
+                       if (currentJob is EditionJob) {
+                               LoadEditionJob(currentJob as EditionJob);
+                       } else {
+                               LoadConversionJob(currentJob as ConversionJob);
                        }
                }
                
@@ -256,7 +300,11 @@ namespace LongoMatch.Services
 
                        else if(progress == (float)EditorState.FINISHED) {
                                Log.Debug ("Job finished successfully");
-                               videoEditor.Progress -= OnProgress;
+                               if (currentJob is EditionJob) {
+                                       videoEditor.Progress -= OnProgress;
+                               } else {
+                                       videoConverter.Progress -= OnProgress;
+                               }
                                UpdateProgress(progress);
                                currentJob.State = JobState.Finished;
                                CloseAndNext();
diff --git a/libcesarplayer/Makefile.am b/libcesarplayer/Makefile.am
index 4ab9dfd..4c383c8 100644
--- a/libcesarplayer/Makefile.am
+++ b/libcesarplayer/Makefile.am
@@ -41,6 +41,8 @@ libcesarplayer_la_SOURCES = \
        gst-remuxer.h\
        gst-video-editor.c\
        gst-video-editor.h\
+       gst-video-encoder.c\
+       gst-video-encoder.h\
        video-utils.c\
        video-utils.h\
        macros.h
@@ -57,3 +59,6 @@ CLEANFILES = $(BUILT_SOURCES)
 EXTRA_DIST = \
        baconvideowidget-marshal.list
 
+
+test-encoder: test-encoder.c gst-video-encoder.c
+       ${CC} -o test-encoder test-encoder.c gst-video-encoder.c $(CESARPLAYER_CFLAGS) $(CESARPLAYER_LIBS) 
-O0 -g
diff --git a/libcesarplayer/gst-video-encoder.c b/libcesarplayer/gst-video-encoder.c
new file mode 100644
index 0000000..1bb33d1
--- /dev/null
+++ b/libcesarplayer/gst-video-encoder.c
@@ -0,0 +1,803 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+* Gstreamer Video Encoder
+* Copyright (C)  Andoni Morales Alastruey 2013 <ylatuya gmail com>
+*
+* You may redistribute it and/or modify it under the terms of the
+* GNU General Public License, as published by the Free Software
+* Foundation; either version 2 of the License, or (at your option)
+* any later version.
+*
+* Gstreamer DV 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 General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with foob.  If not, write to:
+*       The Free Software Foundation, Inc.,
+*       51 Franklin Street, Fifth Floor
+*       Boston, MA  02110-1301, USA.
+*/
+
+#include <gst/gst.h>
+#include <gtk/gtk.h>
+
+#include "gst-video-encoder.h"
+
+
+GST_DEBUG_CATEGORY (_video_encoder_gst_debug_cat);
+#define GST_CAT_DEFAULT _video_encoder_gst_debug_cat
+
+/* Signals */
+enum
+{
+  SIGNAL_ERROR,
+  SIGNAL_PERCENT_COMPLETED,
+  LAST_SIGNAL
+};
+
+struct GstVideoEncoderPrivate
+{
+  /*Encoding properties */
+  gchar *output_file;
+  GList *input_files;
+  GList *current_file;
+  guint output_height;
+  guint output_width;
+  guint audio_bitrate;
+  guint video_bitrate;
+  guint fps_n;
+  guint fps_d;
+  VideoEncoderType video_encoder_type;
+  AudioEncoderType audio_encoder_type;
+  VideoMuxerType video_muxer_type;
+
+  /*GStreamer elements */
+  GstElement *main_pipeline;
+  GstElement *source_bin;
+  GstElement *encoder_bin;
+  GstElement *video_enc;
+  GstElement *audio_enc;
+  GstElement *muxer;
+  GstElement *filesink;
+
+  /*GStreamer bus */
+  GstBus *bus;
+  gulong sig_bus_async;
+
+  gboolean drained;
+  GstClockTime total_duration;
+  guint update_id;
+};
+
+static GObjectClass *parent_class = NULL;
+
+static int gve_signals[LAST_SIGNAL] = { 0 };
+
+static void gve_error_msg (GstVideoEncoder * gcc, GstMessage * msg);
+static void gve_bus_message_cb (GstBus * bus, GstMessage * message,
+    gpointer data);
+static gboolean gst_video_encoder_select_next_file (GstVideoEncoder *gve);
+
+G_DEFINE_TYPE (GstVideoEncoder, gst_video_encoder, G_TYPE_OBJECT);
+
+/***********************************
+*
+*     Class, Object and Properties
+*
+************************************/
+
+static void
+gst_video_encoder_init (GstVideoEncoder * object)
+{
+  GstVideoEncoderPrivate *priv;
+  object->priv = priv =
+      G_TYPE_INSTANCE_GET_PRIVATE (object, GST_TYPE_VIDEO_ENCODER,
+      GstVideoEncoderPrivate);
+
+  priv->output_height = 480;
+  priv->output_width = 640;
+  priv->audio_bitrate = 128;
+  priv->video_bitrate = 5000;
+  priv->video_encoder_type = VIDEO_ENCODER_VP8;
+  priv->audio_encoder_type = AUDIO_ENCODER_VORBIS;
+  priv->video_muxer_type = VIDEO_MUXER_WEBM;
+}
+
+void
+gst_video_encoder_finalize (GObject * object)
+{
+  GstVideoEncoder *gve = (GstVideoEncoder *) object;
+
+  GST_DEBUG_OBJECT (gve, "Finalizing.");
+  if (gve->priv->bus) {
+    /* make bus drop all messages to make sure none of our callbacks is ever
+     * called again (main loop might be run again to display error dialog) */
+    gst_bus_set_flushing (gve->priv->bus, TRUE);
+
+    if (gve->priv->sig_bus_async)
+      g_signal_handler_disconnect (gve->priv->bus, gve->priv->sig_bus_async);
+
+    gst_object_unref (gve->priv->bus);
+    gve->priv->bus = NULL;
+  }
+
+  if (gve->priv->output_file) {
+    g_free (gve->priv->output_file);
+    gve->priv->output_file = NULL;
+  }
+
+  if (gve->priv->input_files) {
+    g_list_foreach (gve->priv->input_files, (GFunc) g_free, NULL);
+    g_free (gve->priv->input_files);
+    gve->priv->input_files = NULL;
+  }
+
+  if (gve->priv->main_pipeline != NULL
+      && GST_IS_ELEMENT (gve->priv->main_pipeline)) {
+    gst_element_set_state (gve->priv->main_pipeline, GST_STATE_NULL);
+    gst_object_unref (gve->priv->main_pipeline);
+    gve->priv->main_pipeline = NULL;
+  }
+
+  if (gve->priv->update_id != 0) {
+    g_source_remove (gve->priv->update_id);
+    gve->priv->update_id = 0;
+  }
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gst_video_encoder_class_init (GstVideoEncoderClass * klass)
+{
+  GObjectClass *object_class;
+
+  object_class = (GObjectClass *) klass;
+  parent_class = g_type_class_peek_parent (klass);
+
+  g_type_class_add_private (object_class, sizeof (GstVideoEncoderPrivate));
+
+  /* GObject */
+  object_class->finalize = gst_video_encoder_finalize;
+
+  /* Signals */
+  gve_signals[SIGNAL_ERROR] =
+      g_signal_new ("error",
+      G_TYPE_FROM_CLASS (object_class),
+      G_SIGNAL_RUN_LAST,
+      G_STRUCT_OFFSET (GstVideoEncoderClass, error),
+      NULL, NULL,
+      g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING);
+
+  gve_signals[SIGNAL_PERCENT_COMPLETED] =
+      g_signal_new ("percent_completed",
+      G_TYPE_FROM_CLASS (object_class),
+      G_SIGNAL_RUN_LAST,
+      G_STRUCT_OFFSET (GstVideoEncoderClass, percent_completed),
+      NULL, NULL, g_cclosure_marshal_VOID__FLOAT, G_TYPE_NONE, 1, G_TYPE_FLOAT);
+}
+
+/***********************************
+*
+*           GStreamer
+*
+************************************/
+
+void
+gst_video_encoder_init_backend (int *argc, char ***argv)
+{
+  gst_init (argc, argv);
+}
+
+GQuark
+gst_video_encoder_error_quark (void)
+{
+  static GQuark q;              /* 0 */
+
+  if (G_UNLIKELY (q == 0)) {
+    q = g_quark_from_static_string ("gve-error-quark");
+  }
+  return q;
+}
+
+static void
+gst_video_encoder_create_encoder_bin (GstVideoEncoder *gve)
+{
+  GstElement *colorspace1, *videoscale, *framerate, *deinterlace;
+  GstElement *colorspace2, *audioconvert, *audioresample;
+  GstElement *aqueue, *vqueue;
+  GstElement *v_identity, *a_identity;
+  GstCaps *video_caps, *audio_caps, *h264_caps;
+  GstPad *v_sink_pad, *a_sink_pad;
+
+  GST_INFO_OBJECT (gve, "Creating encoder bin");
+  gve->priv->encoder_bin = gst_bin_new ("encoder_bin");
+
+  colorspace1 = gst_element_factory_make("ffmpegcolorspace", NULL);
+  deinterlace = gst_element_factory_make("ffdeinterlace", NULL);
+  colorspace2 = gst_element_factory_make("ffmpegcolorspace", "colorspace2");
+  videoscale = gst_element_factory_make("videoscale", "gve_videoscale");
+  framerate = gst_element_factory_make("videorate", "gve_videorate");
+  audioconvert = gst_element_factory_make("audioconvert", NULL);
+  audioresample = gst_element_factory_make("audioresample", NULL);
+  gve->priv->filesink = gst_element_factory_make("filesink", NULL);
+  aqueue = gst_element_factory_make ("queue", "audio_queue");
+  vqueue = gst_element_factory_make ("queue", "video_queue");
+  a_identity = gst_element_factory_make ("identity", "audio_identity");
+  v_identity = gst_element_factory_make ("identity", "video_identity");
+
+
+  /* Increase audio queue size for h264 encoding as the encoder queues 2 seconds
+   * of video */
+  g_object_set (aqueue, "max-size-time", 5 * GST_SECOND, NULL);
+
+  /* Set caps for the encoding resolution */
+  video_caps = gst_caps_new_simple ("video/x-raw-yuv", NULL);
+  gst_caps_set_simple (video_caps, "format", GST_TYPE_FOURCC,
+      GST_STR_FOURCC ("I420"), NULL);
+  if (gve->priv->output_width != 0) {
+    gst_caps_set_simple (video_caps, "width", G_TYPE_INT, gve->priv->output_width,
+        NULL);
+  }
+  if (gve->priv->output_height != 0) {
+    gst_caps_set_simple (video_caps, "height", G_TYPE_INT, gve->priv->output_height,
+        NULL);
+  }
+  /* Set caps for the encoding framerate */
+  if (gve->priv->fps_n != 0 && gve->priv->fps_d != 0) {
+   gst_caps_set_simple (video_caps, "framerate", GST_TYPE_FRACTION,
+      gve->priv->fps_n, gve->priv->fps_d, NULL);
+  }
+
+  /* Audio caps to fixate the channels and sample rate */
+  audio_caps = gst_caps_from_string (
+      "audio/x-raw-int, channels=(int)2, rate=(int)48000;"
+      "audio/x-raw-float, channels=(int)2, rate=(int)48000");
+
+  /* Set caps for the h264 profile */
+  h264_caps = gst_caps_new_simple ("video/x-h264", NULL);
+  gst_caps_set_simple (h264_caps, "profile", G_TYPE_STRING,
+      "constrained-baseline", "stream-format", G_TYPE_STRING, "avc", NULL);
+
+  g_object_set (a_identity, "single-segment", TRUE, NULL);
+  g_object_set (v_identity, "single-segment", TRUE, NULL);
+
+  gst_bin_add_many(GST_BIN(gve->priv->encoder_bin), v_identity,  colorspace1,
+      deinterlace, videoscale, framerate, colorspace2,
+      vqueue, gve->priv->video_enc, gve->priv->muxer, gve->priv->filesink,
+      a_identity, audioconvert, audioresample, gve->priv->audio_enc, aqueue, NULL);
+
+  gst_element_link_many(v_identity, colorspace1, deinterlace, framerate,
+      videoscale, colorspace2, NULL);
+  gst_element_link_filtered (colorspace2, gve->priv->video_enc, video_caps);
+  gst_element_link_filtered (gve->priv->video_enc, vqueue, h264_caps);
+  gst_element_link (vqueue, gve->priv->muxer);
+  gst_element_link_many(a_identity, audioconvert, audioresample, NULL);
+  gst_element_link_filtered (audioresample, gve->priv->audio_enc, audio_caps);
+  gst_element_link_many (gve->priv->audio_enc, aqueue, gve->priv->muxer, NULL);
+  gst_element_link(gve->priv->muxer, gve->priv->filesink);
+
+  gst_caps_unref(video_caps);
+  gst_caps_unref(audio_caps);
+  gst_caps_unref (h264_caps);
+  g_object_set (gve->priv->filesink, "location", gve->priv->output_file, NULL);
+
+  /* Create ghost pads */
+  v_sink_pad = gst_element_get_static_pad (v_identity, "sink");
+  a_sink_pad = gst_element_get_static_pad (a_identity, "sink");
+  gst_element_add_pad (gve->priv->encoder_bin,
+      gst_ghost_pad_new ("video", v_sink_pad));
+  gst_element_add_pad (gve->priv->encoder_bin,
+      gst_ghost_pad_new ("audio", a_sink_pad));
+  gst_object_unref (GST_OBJECT (v_sink_pad));
+  gst_object_unref (GST_OBJECT (a_sink_pad));
+
+  gst_bin_add (GST_BIN (gve->priv->main_pipeline), gve->priv->encoder_bin);
+  GST_INFO_OBJECT (gve, "Encoder bin created successfully");
+}
+
+static gboolean
+cb_handle_eos (GstPad *pad, GstEvent *event, GstVideoEncoder *gve)
+{
+  if (event->type == GST_EVENT_EOS) {
+    GST_DEBUG_OBJECT (gve, "Dropping EOS on pad %s:%s",
+        GST_DEBUG_PAD_NAME (pad));
+    return FALSE;
+  }
+  return TRUE;
+}
+
+static void
+cb_new_pad (GstElement *decodebin, GstPad *pad, GstVideoEncoder *gve)
+{
+  GstPad *epad = NULL;
+  GstCaps *caps;
+  const GstStructure *s;
+  const gchar *mime;
+
+  caps = gst_pad_get_caps_reffed (pad);
+  s = gst_caps_get_structure (caps, 0);
+  mime = gst_structure_get_name (s);
+
+  if (g_strrstr (mime, "video")) {
+    epad = gst_element_get_static_pad (gve->priv->encoder_bin, "video");
+  } else if (g_strrstr (mime, "audio")) {
+    epad = gst_element_get_static_pad (gve->priv->encoder_bin, "audio");
+  }
+
+  if (epad && !gst_pad_is_linked (epad)) {
+    GST_INFO_OBJECT (gve, "Linking pad with caps %" GST_PTR_FORMAT, caps);
+    if (gst_pad_link (pad, epad)) {
+      g_signal_emit (gve, gve_signals[SIGNAL_ERROR], 0, "Error linking pads");
+    } else {
+      gst_pad_add_event_probe (pad, G_CALLBACK (cb_handle_eos), gve);
+    }
+  } else {
+    GST_INFO_OBJECT (gve, "Dropping pad with caps %" GST_PTR_FORMAT, caps);
+  }
+  gst_caps_unref (caps);
+}
+
+static void
+cb_drained (GstElement *decodebin, GstVideoEncoder *gve) {
+  if (!gve->priv->drained) {
+    g_idle_add ((GSourceFunc)gst_video_encoder_select_next_file, gve);
+  }
+  gve->priv->drained = TRUE;
+}
+
+static void
+gst_video_encoder_create_source (GstVideoEncoder *gve, gchar *location)
+{
+  GST_INFO_OBJECT (gve, "Creating source");
+
+  if (gve->priv->source_bin != NULL) {
+    gst_element_set_state (gve->priv->source_bin, GST_STATE_NULL);
+    gst_bin_remove (GST_BIN(gve->priv->main_pipeline), gve->priv->source_bin);
+  }
+  gve->priv->source_bin = gst_element_factory_make ("uridecodebin", NULL);
+  g_object_set (gve->priv->source_bin, "uri", location, NULL);
+  g_signal_connect (gve->priv->source_bin, "pad-added", G_CALLBACK (cb_new_pad), gve);
+  g_signal_connect (gve->priv->source_bin, "drained", G_CALLBACK (cb_drained), gve);
+  gst_bin_add (GST_BIN(gve->priv->main_pipeline), gve->priv->source_bin);
+  gst_element_sync_state_with_parent (gve->priv->source_bin);
+  gve->priv->drained = FALSE;
+}
+
+static gboolean
+gst_video_encoder_select_next_file (GstVideoEncoder *gve)
+{
+  GstPad *audio_pad, *video_pad;
+
+  audio_pad = gst_element_get_static_pad (gve->priv->encoder_bin, "audio");
+  video_pad = gst_element_get_static_pad (gve->priv->encoder_bin, "video");
+
+  if (gve->priv->current_file == NULL) {
+    gve->priv->current_file = gve->priv->input_files;
+  } else {
+    gve->priv->current_file = g_list_next (gve->priv->current_file);
+  }
+
+  if (gve->priv->current_file != NULL) {
+    GstPad *a_peer, *v_peer;
+
+    GST_INFO_OBJECT (gve, "Selecting next file: %s",
+        (gchar *) gve->priv->current_file->data);
+    a_peer = gst_pad_get_peer (audio_pad);
+    if (a_peer) {
+      gst_pad_unlink (a_peer, audio_pad);
+      gst_object_unref (a_peer);
+    }
+
+    v_peer = gst_pad_get_peer (video_pad);
+    if (v_peer) {
+      gst_pad_unlink (v_peer, video_pad);
+      gst_object_unref (v_peer);
+    }
+    gst_video_encoder_create_source (gve, (gchar *) gve->priv->current_file->data);
+  } else {
+    GST_INFO_OBJECT (gve, "No more files, sending EOS");
+    gst_pad_send_event (audio_pad, gst_event_new_eos());
+    gst_pad_send_event (video_pad, gst_event_new_eos());
+  }
+  return FALSE;
+}
+
+static gboolean
+gst_video_encoder_create_video_encoder (GstVideoEncoder * gve,
+    VideoEncoderType type, GError ** err)
+{
+  gchar *name = NULL;
+
+  g_return_val_if_fail (gve != NULL, FALSE);
+  g_return_val_if_fail (GST_IS_VIDEO_ENCODER (gve), FALSE);
+
+  switch (type) {
+    case VIDEO_ENCODER_MPEG4:
+      gve->priv->video_enc =
+          gst_element_factory_make ("ffenc_mpeg4", "video-encoder");
+      g_object_set (gve->priv->video_enc, "pass", 512,
+          "max-key-interval", -1, NULL);
+      name = "FFmpeg mpeg4 video encoder";
+      break;
+
+    case VIDEO_ENCODER_XVID:
+      gve->priv->video_enc =
+          gst_element_factory_make ("xvidenc", "video-encoder");
+      g_object_set (gve->priv->video_enc, "pass", 1,
+          "profile", 146, "max-key-interval", -1, NULL);
+      name = "Xvid video encoder";
+      break;
+
+    case VIDEO_ENCODER_H264:
+      gve->priv->video_enc =
+          gst_element_factory_make ("x264enc", "video-encoder");
+      g_object_set (gve->priv->video_enc, "key-int-max", 25, "pass", 17,
+          "speed-preset", 3, NULL);
+      name = "X264 video encoder";
+      break;
+
+    case VIDEO_ENCODER_THEORA:
+      gve->priv->video_enc =
+          gst_element_factory_make ("theoraenc", "video-encoder");
+      g_object_set (gve->priv->video_enc, "keyframe-auto", FALSE,
+          "keyframe-force", 25, NULL);
+      name = "Theora video encoder";
+      break;
+
+    case VIDEO_ENCODER_VP8:
+    default:
+      gve->priv->video_enc =
+          gst_element_factory_make ("vp8enc", "video-encoder");
+      g_object_set (gve->priv->video_enc, "speed", 2, "threads", 8,
+          "max-keyframe-distance", 25, NULL);
+      name = "VP8 video encoder";
+      break;
+
+  }
+  if (!gve->priv->video_enc) {
+    g_set_error (err,
+        GVE_ERROR,
+        GST_ERROR_PLUGIN_LOAD,
+        "Failed to create the %s element. "
+        "Please check your GStreamer installation.", name);
+    return FALSE;
+  }
+
+  if (gve->priv->video_encoder_type == VIDEO_ENCODER_MPEG4 ||
+      gve->priv->video_encoder_type == VIDEO_ENCODER_XVID)
+    g_object_set (gve->priv->video_enc, "bitrate", gve->priv->video_bitrate * 1000, NULL);
+  else
+    g_object_set (gve->priv->video_enc, "bitrate", gve->priv->video_bitrate,
+        NULL);
+
+  GST_INFO_OBJECT(gve, "Video encoder %s created", name);
+  gve->priv->video_encoder_type = type;
+  return TRUE;
+}
+
+static gboolean
+gst_video_encoder_create_audio_encoder (GstVideoEncoder * gve,
+    AudioEncoderType type, GError ** err)
+{
+  gchar *name = NULL;
+
+  g_return_val_if_fail (gve != NULL, FALSE);
+  g_return_val_if_fail (GST_IS_VIDEO_ENCODER (gve), FALSE);
+
+  switch (type) {
+    case AUDIO_ENCODER_MP3:
+      gve->priv->audio_enc =
+          gst_element_factory_make ("lamemp3enc", "audio-encoder");
+      g_object_set (gve->priv->audio_enc, "target", 0, NULL);
+      name = "Mp3 audio encoder";
+      break;
+
+    case AUDIO_ENCODER_AAC:
+      gve->priv->audio_enc = gst_element_factory_make ("faac", "audio-encoder");
+      name = "AAC audio encoder";
+      break;
+
+    case AUDIO_ENCODER_VORBIS:
+    default:
+      gve->priv->audio_enc =
+          gst_element_factory_make ("vorbisenc", "audio-encoder");
+      name = "Vorbis audio encoder";
+      break;
+  }
+
+  if (!gve->priv->audio_enc) {
+    g_set_error (err,
+        GVE_ERROR,
+        GST_ERROR_PLUGIN_LOAD,
+        "Failed to create the %s element. "
+        "Please check your GStreamer installation.", name);
+    return FALSE;
+  }
+
+  if (gve->priv->audio_encoder_type == AUDIO_ENCODER_MP3)
+    g_object_set (gve->priv->audio_enc, "bitrate", gve->priv->audio_bitrate, NULL);
+  else
+    g_object_set (gve->priv->audio_enc, "bitrate", 1000 * gve->priv->audio_bitrate, NULL);
+
+  GST_INFO_OBJECT(gve, "Audio encoder %s created", name);
+
+  gve->priv->audio_encoder_type = type;
+  return TRUE;
+}
+
+static gboolean
+gst_video_encoder_create_video_muxer (GstVideoEncoder * gve,
+    VideoMuxerType type, GError ** err)
+{
+  gchar *name = NULL;
+
+  g_return_val_if_fail (gve != NULL, FALSE);
+  g_return_val_if_fail (GST_IS_VIDEO_ENCODER (gve), FALSE);
+
+  switch (type) {
+    case VIDEO_MUXER_OGG:
+      name = "OGG muxer";
+      gve->priv->muxer = gst_element_factory_make ("oggmux", "video-muxer");
+      break;
+    case VIDEO_MUXER_AVI:
+      name = "AVI muxer";
+      gve->priv->muxer = gst_element_factory_make ("avimux", "video-muxer");
+      break;
+    case VIDEO_MUXER_MATROSKA:
+      name = "Matroska muxer";
+      gve->priv->muxer =
+          gst_element_factory_make ("matroskamux", "video-muxer");
+      break;
+    case VIDEO_MUXER_MP4:
+      name = "MP4 muxer";
+      gve->priv->muxer = gst_element_factory_make ("qtmux", "video-muxer");
+      break;
+    case VIDEO_MUXER_WEBM:
+    default:
+      name = "WebM muxer";
+      gve->priv->muxer = gst_element_factory_make ("webmmux", "video-muxer");
+      break;
+  }
+
+  if (!gve->priv->muxer) {
+    g_set_error (err,
+        GVE_ERROR,
+        GST_ERROR_PLUGIN_LOAD,
+        "Failed to create the %s element. "
+        "Please check your GStreamer installation.", name);
+  }
+
+  GST_INFO_OBJECT(gve, "Muxer %s created", name);
+  gve->priv->video_muxer_type = type;
+  return TRUE;
+}
+
+static void
+gst_video_encoder_initialize (GstVideoEncoder *gve)
+{
+  GError *err= NULL;
+
+  GST_INFO_OBJECT (gve, "Initializing encoders");
+  if (!gst_video_encoder_create_video_encoder(gve,
+        gve->priv->video_encoder_type, &err))
+    goto missing_plugin;
+  if (!gst_video_encoder_create_audio_encoder(gve,
+        gve->priv->audio_encoder_type, &err))
+    goto missing_plugin;
+  if (!gst_video_encoder_create_video_muxer(gve,
+        gve->priv->video_muxer_type, &err))
+    goto missing_plugin;
+
+  gst_video_encoder_create_encoder_bin (gve);
+  gst_video_encoder_select_next_file (gve);
+  gst_element_set_state (gve->priv->main_pipeline, GST_STATE_PLAYING);
+  return;
+
+missing_plugin:
+    g_signal_emit (gve, gve_signals[SIGNAL_ERROR], 0, err->message);
+    g_error_free (err);
+}
+
+static void
+gve_bus_message_cb (GstBus * bus, GstMessage * message, gpointer data)
+{
+  GstVideoEncoder *gve = (GstVideoEncoder *) data;
+  GstMessageType msg_type;
+
+  g_return_if_fail (gve != NULL);
+  g_return_if_fail (GST_IS_VIDEO_ENCODER (gve));
+
+  msg_type = GST_MESSAGE_TYPE (message);
+
+  switch (msg_type) {
+    case GST_MESSAGE_ERROR:
+    {
+      if (gve->priv->main_pipeline) {
+        gst_video_encoder_cancel (gve);
+        gst_element_set_state (gve->priv->main_pipeline, GST_STATE_NULL);
+      }
+      gve_error_msg (gve, message);
+      break;
+    }
+
+    case GST_MESSAGE_WARNING:
+    {
+      GST_WARNING ("Warning message: %" GST_PTR_FORMAT, message);
+      break;
+    }
+
+    case GST_MESSAGE_EOS:
+    {
+      GST_INFO_OBJECT (gve, "EOS message");
+      g_signal_emit (gve, gve_signals[SIGNAL_PERCENT_COMPLETED], 0, (gfloat) 1);
+      break;
+    }
+
+    default:
+      GST_LOG ("Unhandled message: %" GST_PTR_FORMAT, message);
+      break;
+  }
+}
+
+static void
+gve_error_msg (GstVideoEncoder * gve, GstMessage * msg)
+{
+  GError *err = NULL;
+  gchar *dbg = NULL;
+
+  gst_message_parse_error (msg, &err, &dbg);
+  if (err) {
+    GST_ERROR ("message = %s", GST_STR_NULL (err->message));
+    GST_ERROR ("domain  = %d (%s)", err->domain,
+        GST_STR_NULL (g_quark_to_string (err->domain)));
+    GST_ERROR ("code    = %d", err->code);
+    GST_ERROR ("debug   = %s", GST_STR_NULL (dbg));
+    GST_ERROR ("source  = %" GST_PTR_FORMAT, msg->src);
+
+
+    g_message ("Error: %s\n%s\n", GST_STR_NULL (err->message),
+        GST_STR_NULL (dbg));
+    g_signal_emit (gve, gve_signals[SIGNAL_ERROR], 0, err->message);
+    g_error_free (err);
+  }
+  g_free (dbg);
+}
+
+static gboolean
+gst_video_encoder_query_timeout (GstVideoEncoder * gve)
+{
+  GstFormat fmt;
+  gint64 pos;
+
+  fmt = GST_FORMAT_TIME;
+  pos = -1;
+
+  gst_element_query_position (gve->priv->main_pipeline, &fmt, &pos);
+
+  g_signal_emit (gve, gve_signals[SIGNAL_PERCENT_COMPLETED], 0,
+      MIN (0.99, (gfloat) pos / (gfloat) gve->priv->total_duration));
+
+  return TRUE;
+}
+
+/*******************************************
+ *
+ *         Public methods
+ *
+ * ****************************************/
+
+void
+gst_video_encoder_cancel (GstVideoEncoder * gve)
+{
+  g_return_if_fail (gve != NULL);
+  g_return_if_fail (GST_IS_VIDEO_ENCODER (gve));
+
+  g_signal_emit (gve, gve_signals[SIGNAL_PERCENT_COMPLETED], 0, (gfloat) -1);
+  gst_element_set_state (gve->priv->main_pipeline, GST_STATE_NULL);
+  gst_element_get_state (gve->priv->main_pipeline, NULL, NULL, -1);
+  gst_bin_remove (GST_BIN(gve->priv->main_pipeline), gve->priv->source_bin);
+  gst_bin_remove (GST_BIN(gve->priv->main_pipeline), gve->priv->encoder_bin);
+  gve->priv->total_duration = 0;
+  if (gve->priv->update_id != 0) {
+    g_source_remove (gve->priv->update_id);
+    gve->priv->update_id = 0;
+  }
+}
+
+void
+gst_video_encoder_start (GstVideoEncoder * gve)
+{
+  g_return_if_fail (gve != NULL);
+  g_return_if_fail (GST_IS_VIDEO_ENCODER (gve));
+
+  GST_INFO_OBJECT(gve, "Starting encoding");
+  g_signal_emit (gve, gve_signals[SIGNAL_PERCENT_COMPLETED], 0, (gfloat) 0);
+  gst_video_encoder_initialize (gve);
+  gve->priv->update_id =
+      g_timeout_add (100, (GSourceFunc) gst_video_encoder_query_timeout, gve);
+
+}
+
+void
+gst_video_encoder_add_file (GstVideoEncoder * gve, const gchar *file, guint64 duration)
+{
+  g_return_if_fail (gve != NULL);
+  g_return_if_fail (GST_IS_VIDEO_ENCODER (gve));
+
+  GST_INFO_OBJECT(gve, "Adding file %s", file);
+  gve->priv->input_files = g_list_append (gve->priv->input_files, g_strdup(file));
+  gve->priv->total_duration += duration * GST_MSECOND;
+}
+
+gboolean
+gst_video_encoder_dump_graph (GstVideoEncoder * gve)
+{
+  GST_DEBUG_BIN_TO_DOT_FILE (GST_BIN (gve->priv->main_pipeline),
+      GST_DEBUG_GRAPH_SHOW_ALL, "gst-video-encoder.dot");
+  return FALSE;
+}
+
+void
+gst_video_encoder_set_encoding_format (GstVideoEncoder * gve,
+    VideoEncoderType video_codec, AudioEncoderType audio_codec,
+    VideoMuxerType muxer, guint video_bitrate, guint audio_bitrate,
+    guint width, guint height, guint fps_n, guint fps_d)
+{
+  gve->priv->video_encoder_type = video_codec;
+  gve->priv->audio_encoder_type = audio_codec;
+  gve->priv->video_muxer_type = muxer;
+  gve->priv->video_bitrate = video_bitrate;
+  gve->priv->audio_bitrate = audio_bitrate;
+  gve->priv->output_width = width;
+  gve->priv->output_height = height;
+  gve->priv->fps_n = fps_n;
+  gve->priv->fps_d = fps_d;
+
+}
+GstVideoEncoder *
+gst_video_encoder_new (gchar * filename, GError ** err)
+{
+  GstVideoEncoder *gve = NULL;
+
+#ifndef GST_DISABLE_GST_INFO
+  if (_video_encoder_gst_debug_cat == NULL) {
+    GST_DEBUG_CATEGORY_INIT (_video_encoder_gst_debug_cat, "longomatch", 0,
+        "LongoMatch GStreamer Backend");
+  }
+#endif
+
+  gve = g_object_new (GST_TYPE_VIDEO_ENCODER, NULL);
+
+  gve->priv->output_file = g_strdup (filename);
+  gve->priv->main_pipeline = gst_pipeline_new ("main_pipeline");
+
+  if (!gve->priv->main_pipeline) {
+    g_set_error (err,
+        GVE_ERROR,
+        GST_ERROR_PLUGIN_LOAD,
+        "Failed to create the pipeline element. "
+        "Please check your GStreamer installation.");
+    goto missing_plugin;
+  }
+
+  /*Connect bus signals */
+  GST_INFO_OBJECT (gve, "Connecting bus signals");
+  gve->priv->bus = gst_element_get_bus (GST_ELEMENT (gve->priv->main_pipeline));
+  gst_bus_add_signal_watch (gve->priv->bus);
+  gve->priv->sig_bus_async =
+      g_signal_connect (gve->priv->bus, "message",
+      G_CALLBACK (gve_bus_message_cb), gve);
+
+  return gve;
+
+/* Missing plugin */
+missing_plugin:
+  {
+    g_object_ref_sink (gve);
+    g_object_unref (gve);
+    return NULL;
+  }
+}
diff --git a/libcesarplayer/gst-video-encoder.h b/libcesarplayer/gst-video-encoder.h
new file mode 100644
index 0000000..04b5420
--- /dev/null
+++ b/libcesarplayer/gst-video-encoder.h
@@ -0,0 +1,93 @@
+/*
+ * Gstreamer Multi File Source Bin
+ * Copyright (C) 2013 Andoni Morales Alastruey <ylatuya gmail com>
+ *
+ * You may redistribute it and/or modify it under the terms of the
+ * GNU General Public License, as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * foob 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with foob.  If not, write to:
+ *      The Free Software Foundation, Inc.,
+ *      51 Franklin Street, Fifth Floor
+ *      Boston, MA  02110-1301, USA.
+ */
+
+#ifndef _GST_VIDEO_ENCODER_H_
+#define _GST_VIDEO_ENCODER_H_
+
+#ifdef WIN32
+#define EXPORT __declspec (dllexport)
+#else
+#define EXPORT
+#endif
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+
+#include "common.h"
+
+G_BEGIN_DECLS
+#define GST_TYPE_VIDEO_ENCODER             (gst_video_encoder_get_type ())
+#define GST_VIDEO_ENCODER(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_VIDEO_ENCODER, 
GstVideoEncoder))
+#define GST_VIDEO_ENCODER_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_VIDEO_ENCODER, 
GstVideoEncoderClass))
+#define GST_IS_VIDEO_ENCODER(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_VIDEO_ENCODER))
+#define GST_IS_VIDEO_ENCODER_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_VIDEO_ENCODER))
+#define GST_VIDEO_ENCODER_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_VIDEO_ENCODER, 
GstVideoEncoderClass))
+#define GVE_ERROR gst_video_encoder_error_quark ()
+
+typedef struct _GstVideoEncoderClass GstVideoEncoderClass;
+typedef struct _GstVideoEncoder GstVideoEncoder;
+typedef struct GstVideoEncoderPrivate GstVideoEncoderPrivate;
+
+
+struct _GstVideoEncoderClass
+{
+  GObjectClass parent_class;
+
+  void (*error) (GstVideoEncoder * gve, const char *message);
+  void (*percent_completed) (GstVideoEncoder * gve, float percent);
+};
+
+struct _GstVideoEncoder
+{
+  GObject parent_instance;
+  GstVideoEncoderPrivate *priv;
+};
+
+EXPORT GType gst_video_encoder_get_type (void) G_GNUC_CONST;
+
+EXPORT void gst_video_encoder_init_backend                     (int *argc, char ***argv);
+
+EXPORT GstVideoEncoder *gst_video_encoder_new                  (gchar *output_file, GError ** err);
+
+EXPORT void gst_video_encoder_start                            (GstVideoEncoder * gve);
+
+EXPORT void gst_video_encoder_cancel                           (GstVideoEncoder * gve);
+
+EXPORT void gst_video_encoder_set_encoding_format              (GstVideoEncoder * gve,
+                                                                VideoEncoderType video_codec,
+                                                                AudioEncoderType audio_codec,
+                                                                VideoMuxerType muxer,
+                                                                guint video_bitrate,
+                                                                guint audio_bitrate,
+                                                                guint height,
+                                                                guint width,
+                                                                guint fps_n,
+                                                                guint fps_d);
+
+EXPORT void gst_video_encoder_add_file                         (GstVideoEncoder * gve,
+                                                                const gchar * file,
+                                                                guint64 duration);
+
+EXPORT gboolean gst_video_encoder_dump_graph                   (GstVideoEncoder *gve);
+
+G_END_DECLS
+#endif /* _GST_VIDEO_ENCODER_H_ */
diff --git a/libcesarplayer/liblongomatch.mdp b/libcesarplayer/liblongomatch.mdp
index 11bf0e1..1ca186d 100644
--- a/libcesarplayer/liblongomatch.mdp
+++ b/libcesarplayer/liblongomatch.mdp
@@ -35,6 +35,8 @@
     <File subtype="Code" buildaction="Nothing" name="baconvideowidget-marshal.h" />
     <File subtype="Code" buildaction="Compile" name="gst-remuxer.c" />
     <File subtype="Code" buildaction="Nothing" name="gst-remuxer.h" />
+    <File subtype="Code" buildaction="Compile" name="gst-video-encoder.c" />
+    <File subtype="Code" buildaction="Nothing" name="gst-video-encoder.h" />
   </Contents>
   <MonoDevelop.Autotools.MakefileInfo RelativeMakefileName="Makefile.am" IsAutotoolsProject="True" 
RelativeConfigureInPath=".">
     <BuildFilesVar />
diff --git a/libcesarplayer/test-encoder.c b/libcesarplayer/test-encoder.c
new file mode 100644
index 0000000..b55b25c
--- /dev/null
+++ b/libcesarplayer/test-encoder.c
@@ -0,0 +1,93 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * main.c
+ * Copyright (C) Andoni Morales Alastruey 2008 <ylatuya gmail com>
+ * 
+ * main.c is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * main.c 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include "gst-video-encoder.h"
+
+static GMainLoop *loop;
+
+static gboolean
+percent_done_cb (GstVideoEncoder *remuxer, gfloat percent, GstVideoEncoder *editor)
+{
+  if (percent == 1) {
+    g_print("SUCESS!\n");
+    g_main_loop_quit (loop);
+  } else {
+    g_print("----> %f%%\n", percent);
+  }
+  return TRUE;
+}
+
+static gboolean
+error_cb (GstVideoEncoder *remuxer, gchar *error, GstVideoEncoder *editor)
+{
+    g_print("ERROR: %s\n", error);
+    g_main_loop_quit (loop);
+}
+
+int
+main (int argc, char *argv[])
+{
+  GstVideoEncoder *encoder;
+  VideoEncoderType video_encoder;
+  VideoMuxerType video_muxer;
+  AudioEncoderType audio_encoder;
+  gchar *input_file, *output_file;
+  GError *err = NULL;
+  guint64 start, stop;
+  gint i;
+
+  gst_video_encoder_init_backend (&argc, &argv);
+
+  if (argc < 3) {
+    g_print("Usage: test-remuxer output_file input_files \n");
+    return 1;
+  }
+
+  output_file = argv[1];
+  input_file = argv[1];
+
+  video_encoder = VIDEO_ENCODER_H264;
+  video_muxer = VIDEO_MUXER_MP4;
+  audio_encoder = AUDIO_ENCODER_AAC;
+
+  encoder = gst_video_encoder_new (output_file, &err);
+  gst_video_encoder_set_encoding_format (encoder, video_encoder,
+      audio_encoder, video_muxer, 500, 128, 240, 180, 25, 1);
+
+  for (i=2; i < argc; i++) {
+    gst_video_encoder_add_file (encoder, argv[i], 1000);
+  }
+
+  loop = g_main_loop_new (NULL, FALSE);
+  g_signal_connect (encoder, "error", G_CALLBACK (error_cb), encoder);
+  g_signal_connect (encoder, "percent_completed", G_CALLBACK(percent_done_cb),
+      encoder);
+  gst_video_encoder_start (encoder);
+  g_main_loop_run (loop);
+
+  return 0;
+
+error:
+  g_print ("ERROR: %s", err->message);
+  return 1;
+
+}
+


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