[longomatch] Add a new tool to merge and re-encode files
- From: Andoni Morales Alastruey <amorales src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [longomatch] Add a new tool to merge and re-encode files
- Date: Mon, 18 Mar 2013 10:20:18 +0000 (UTC)
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 <b> Game Units </b> 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]