Anonymous avatar Anonymous committed 6dfb9d7

Vastly improved version that can record audio and dump into PNG files. Unfortunately some encoder setups are still incredible unstable :(

Comments (0)

Files changed (18)

-/(Debug|Release)/bin
+bin/(Debug|Release)
 librecordscreen/.*\.[od]$

GNOME-Record-Screen.userprefs

 <UserCombinePreferences version="1.0" filename="/home/mitsuhiko/Development/GNOME-Record-Screen/GNOME-Record-Screen.mds">
   <Files>
-    <File name="Welcome" />
-    <File name="GNOME-Record-Screen/src/UI/MainWindow.cs" line="172" column="76" />
-    <File name="GNOME-Record-Screen/src/Utils.cs" line="39" column="58" />
+    <File name="GNOME-Record-Screen/src/UI/MainWindow.cs" line="33" column="30" />
+    <File name="GNOME-Record-Screen/src/Utils.cs" line="141" column="28" />
     <File name="GNOME-Record-Screen/src/UI/SaveDialog.cs" line="50" column="2" />
-    <File name="GNOME-Record-Screen/src/UI/RecordingAreaSelector.cs" line="78" column="55" />
-    <File name="GNOME-Record-Screen/src/VideoEncoders.cs" line="139" column="41" />
-    <File name="GNOME-Record-Screen/src/UI/H264ConfigurationPane.cs" line="17" column="4" />
+    <File name="GNOME-Record-Screen/src/UI/H264ConfigurationPane.cs" line="18" column="13" />
+    <File name="GNOME-Record-Screen/src/ScreenRecorder.cs" line="264" column="67" />
+    <File name="GNOME-Record-Screen/src/VideoEncoders.cs" line="75" column="31" />
+    <File name="GNOME-Record-Screen/src/UI/TheoraConfigurationPane.cs" line="19" column="3" />
+    <File name="GNOME-Record-Screen/src/UI/MpegConfigurationPane.cs" line="19" column="34" />
   </Files>
   <Views>
     <ViewMemento id="ProjectPad">
       <Node expanded="True">
         <Node name="GNOME-Record-Screen" expanded="True">
-          <Node name="UserInterface" expanded="True" />
+          <Node name="References" expanded="True" />
           <Node name="src" expanded="True">
-            <Node name="UI" expanded="True">
-              <Node name="MainWindow.cs" selected="True" />
-            </Node>
+            <Node name="UI" expanded="True" />
+            <Node name="VideoEncoders.cs" selected="True" />
           </Node>
         </Node>
         <Node name="librecordscreen" expanded="True" />
       </Node>
     </ViewMemento>
     <ViewMemento id="ClassPad">
-      <Node expanded="True" />
+      <Node expanded="True">
+        <Node name="GNOME-Record-Screen" expanded="True">
+          <Node name="Gnome" expanded="True">
+            <Node name="RecordScreen" expanded="True">
+              <Node name="VideoContainer" selected="True" />
+            </Node>
+          </Node>
+        </Node>
+      </Node>
     </ViewMemento>
   </Views>
   <Properties>
-    <Property key="ActiveWindow" value="/home/mitsuhiko/Development/GNOME-Record-Screen/GNOME-Record-Screen/src/UI/MainWindow.cs" />
+    <Property key="ActiveWindow" value="/home/mitsuhiko/Development/GNOME-Record-Screen/GNOME-Record-Screen/src/VideoEncoders.cs" />
     <Property key="ActiveConfiguration" value="Debug" />
   </Properties>
 </UserCombinePreferences>

GNOME-Record-Screen/GNOME-Record-Screen.mdp

     <File name="src/Utils.cs" subtype="Code" buildaction="Compile" />
     <File name="src/UI/RecordingAreaSelector.cs" subtype="Code" buildaction="Compile" />
     <File name="src/UI/SaveDialog.cs" subtype="Code" buildaction="Compile" />
-    <File name="src/VideoEncoders.cs" subtype="Code" buildaction="Compile" />
+    <File name="src/VideoEncoder.cs" subtype="Code" buildaction="Compile" />
     <File name="src/UI/H264ConfigurationPane.cs" subtype="Code" buildaction="Compile" />
     <File name="gtk-gui/objects.xml" subtype="Code" buildaction="EmbedAsResource" />
     <File name="gtk-gui/Gnome.RecordScreen.UI.H264ConfigurationPane.cs" subtype="Code" buildaction="Compile" />
     <File name="gtk-gui/Gnome.RecordScreen.UI.TheoraConfigurationPane.cs" subtype="Code" buildaction="Compile" />
     <File name="src/UI/MpegConfigurationPane.cs" subtype="Code" buildaction="Compile" />
     <File name="gtk-gui/Gnome.RecordScreen.UI.MpegConfigurationPane.cs" subtype="Code" buildaction="Compile" />
+    <File name="src/AudioEncoder.cs" subtype="Code" buildaction="Compile" />
+    <File name="src/VideoEncoderImplementations.cs" subtype="Code" buildaction="Compile" />
+    <File name="src/AudioEncoderImplementations.cs" subtype="Code" buildaction="Compile" />
   </Contents>
   <References>
     <ProjectReference type="Gac" localcopy="True" refto="gtk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
     <ProjectReference type="Gac" localcopy="True" refto="System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
     <ProjectReference type="Gac" localcopy="True" refto="Mono.Posix, Version=2.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756" />
     <ProjectReference type="Gac" localcopy="True" refto="atk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
-    <ProjectReference type="Assembly" localcopy="True" refto="../../../../../usr/lib/monodevelop/AddIns/MonoDevelop.GtkCore/libstetic.dll" />
+    <ProjectReference type="Gac" localcopy="True" refto="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
   </References>
   <GtkDesignInfo gtkVersion="2.12.1">
     <ExportedWidgets>
Add a comment to this file

GNOME-Record-Screen/GNOME-Record-Screen.pidb

Binary file modified.

GNOME-Record-Screen/gtk-gui/Gnome.RecordScreen.UI.MainWindow.cs

         protected virtual void Build() {
             Stetic.Gui.Initialize(this);
             // Widget Gnome.RecordScreen.UI.MainWindow
-            this.WidthRequest = 580;
+            this.WidthRequest = 600;
             this.HeightRequest = 400;
             this.Name = "Gnome.RecordScreen.UI.MainWindow";
             this.Title = Mono.Unix.Catalog.GetString("GNOME Record Screen");
             if ((this.Child != null)) {
                 this.Child.ShowAll();
             }
-            this.DefaultWidth = 588;
+            this.DefaultWidth = 608;
             this.DefaultHeight = 488;
             this.videoFormatConfigurationLabel.Hide();
             this.stopButton.Hide();

GNOME-Record-Screen/gtk-gui/gui.stetic

   </configuration>
   <import>
     <widget-library name="../bin/Debug/GNOME-Record-Screen.exe" internal="true" />
-    <widget-library name="../../../../../../usr/lib/monodevelop/AddIns/MonoDevelop.GtkCore/libstetic.dll" />
   </import>
-  <widget class="Gtk.Window" id="Gnome.RecordScreen.UI.MainWindow" design-size="588 488">
+  <widget class="Gtk.Window" id="Gnome.RecordScreen.UI.MainWindow" design-size="608 488">
     <property name="MemberName" />
-    <property name="WidthRequest">580</property>
+    <property name="WidthRequest">600</property>
     <property name="HeightRequest">400</property>
     <property name="Title" translatable="yes">GNOME Record Screen</property>
     <property name="WindowPosition">CenterOnParent</property>

GNOME-Record-Screen/src/AudioEncoder.cs

+/**
+ * AudioEncoders.cs
+ *
+ * :copyright: 2008 mitsuhiko.
+ * :license: BSD
+ */
+
+using System;
+using Gnome.RecordScreen.Implementations;
+
+namespace Gnome.RecordScreen {
+	
+	public enum AudioQuality {
+		Low,
+		Medium,
+		High
+	}
+	
+	public class AudioEncoder : IEncoder {
+		private string gstKey;
+		
+		public AudioEncoder (string gstKey)
+		{
+			this.gstKey = gstKey;
+		}
+		
+		public virtual string GetEncoderArguments (AudioQuality quality) {
+			return null;
+		}
+		
+		public string GstKey {
+			get { return gstKey; }
+		}
+		
+		public virtual bool IsAvailable {
+			get { return GStreamerInterface.SupportsObject (this); }
+		}
+		
+		private static AudioEncoder vorbis = new VorbisAudioEncoder ();
+		public static AudioEncoder Vorbis {
+			get { return vorbis; }
+		}
+		
+		private static AudioEncoder mp3 = new Mp3AudioEncoder ();
+		public static AudioEncoder Mp3 {
+			get { return mp3; }
+		}
+		
+		public static AudioEncoder nullenc = new NullAudioEncoder ();
+		public static AudioEncoder Null {
+			get { return nullenc; }
+		}
+	}
+}

GNOME-Record-Screen/src/AudioEncoderImplementations.cs

+/**
+ * AudioEncoderImplementations.cs
+ *
+ * :copyright: 2008 mitsuhiko.
+ * :license: BSD
+ */
+
+using System;
+
+namespace Gnome.RecordScreen.Implementations {
+	
+	internal class VorbisAudioEncoder : AudioEncoder {
+		
+		public VorbisAudioEncoder ()
+			: base ("vorbisenc")
+		{}
+		
+		public override string GetEncoderArguments (AudioQuality quality)
+		{
+			switch (quality) {
+			case AudioQuality.High:
+				return "quality=0.7";
+			case AudioQuality.Medium:
+				return "quality=0.5";
+			case AudioQuality.Low:
+				return "quality=0.25";
+			}
+			return null;
+		}
+	}
+	
+	internal class Mp3AudioEncoder : AudioEncoder {
+
+		public Mp3AudioEncoder ()
+			: base ("lame")
+		{}
+		
+		public override string GetEncoderArguments (AudioQuality quality)
+		{
+			switch (quality) {
+			case AudioQuality.High:
+				return "quality=2";
+			case AudioQuality.Medium:
+				return "quality=5";
+			case AudioQuality.Low:
+				return "quality=8";
+			}
+			return null;
+		}
+	}
+	
+	internal class NullAudioEncoder : AudioEncoder {
+		
+		public NullAudioEncoder ()
+			: base (null)
+		{}
+		
+		public override bool IsAvailable {
+			get { return false; }
+		}
+	}
+}

GNOME-Record-Screen/src/Main.cs

 		public static void Main (string[] args)
 		{
 			Utils.SetProcessName ("gnome-record-screen");
-			Application.Init ();
+			Application.Init ("gnome-record-screen", ref args);
 			MainWindow win = new MainWindow ();
 			win.Show ();
 			Application.Run ();

GNOME-Record-Screen/src/ScreenRecorder.cs

  */
 
 using System;
+using System.IO;
 using System.Text;
+using System.Collections.Generic;
 using System.Runtime.InteropServices;
 
 using Gnome.RecordScreen;
 
 namespace Gnome.RecordScreen {
 	
+	public class RecorderException : ApplicationException {
+		
+		public RecorderException (string message)
+			: base (message)
+		{}
+	}
+	
+	public class RecordErrorEventArgs : EventArgs {
+		private string error;
+		
+		public RecordErrorEventArgs (string error)
+			: base ()
+		{
+			this.error = error;
+		}
+		
+		public string Error {
+			get { return error; }
+		}
+	}
+	
 	class GStreamerInterface : IDisposable {
 		private IntPtr element = IntPtr.Zero;
 		public delegate void MessageCallbackDelegate (IntPtr bus, IntPtr message);
 		public delegate void FinishRecordDelegate ();
-		private FinishRecordDelegate finishRecord;
+		public delegate void OnErrorDelegate (string error);
+		private FinishRecordDelegate onFinish;
+		private OnErrorDelegate onError;
 		
 		static GStreamerInterface ()
 		{
 			grc_initialize ();
 		}
 		
-		public GStreamerInterface (ScreenRecorder recorder, FinishRecordDelegate func)
+		public GStreamerInterface (ScreenRecorder recorder, FinishRecordDelegate onFinish,
+		                           OnErrorDelegate onError)
 		{
 			element = grc_new_interface (recorder.Pipeline, OnMessageReceived);
 			if (element == IntPtr.Zero)
-				throw new ExternalException ("GStreamer was unable to initialize interface.");			
-			finishRecord = func;
+				throw new RecorderException ("GStreamer was unable to initialize interface.");			
+			this.onFinish = onFinish;
+			this.onError = onError;
 		}
 		
 		~GStreamerInterface ()
 			Dispose ();
 		}
 		
-		public static bool HasEncoder (VideoEncoder encoder)
+		public static bool SupportsObject (IGstObject obj)
 		{
-			return (grc_test_pipeline (encoder.GstKey) &&
-			        grc_test_pipeline (encoder.Container.GstKey));
+			return grc_test_pipeline (obj.GstKey);
 		}
 		
 		public void Dispose ()
 			FinishRecord ();
 		}
 		
+		public void Kill ()
+		{
+			grc_interface_set_status (element, 0);
+		}
+		
 		protected void OnMessageReceived (IntPtr bus, IntPtr message)
 		{
 			IntPtr error = IntPtr.Zero;
 			if (error != IntPtr.Zero) {
 				string error_message = Marshal.PtrToStringAnsi (error);
 				grc_free_message_error (error);
-				throw new ApplicationException (error_message);
+				onError (error_message);
 			}
 			if (finished)
 				FinishRecord ();
 		
 		protected void FinishRecord ()
 		{
-			if (finishRecord != null) {
-				FinishRecordDelegate func = finishRecord;
-				finishRecord = null;
+			if (onFinish != null) {
+				FinishRecordDelegate func = onFinish;
+				onFinish = null;
 				func ();
 			}
 		}
 		public delegate void RecordFinishedDelegate (object sender, EventArgs args);
 		public event RecordFinishedDelegate RecordFinished;
 		
+		public delegate void ErrorOccurredDelegate (object sender, RecordErrorEventArgs args);
+		public event ErrorOccurredDelegate ErrorOccurred;
+		
 		private GStreamerInterface gst;
 		private bool recording;
 		private bool exhausted;
+		private bool recordAudio;
+		private bool hideMousePointer;
 		private string videoEncoderArguments;
+		private string audioEncoderArguments;
+		public int outputWidth = -1;
+		public int outputHeight = -1;
 		
 		private VideoEncoder videoEncoder;
 		private Gdk.Rectangle rectangle;
 		private double framerate = 15.0;
+		private string[] createdTemporaryFiles;
 		
 		private string temporaryFilename;
 		
 				if (gst == null)
 					gst = new GStreamerInterface (this, delegate () {
 						RecordFinished (this, new EventArgs ());
+					}, delegate (string error) {
+						ErrorOccurred (this, new RecordErrorEventArgs (error));
 					});
 				return gst;
 			}
 		}
 		
 		/**
+		 * Kills the recorder and cleans up.
+		 */
+		public void Kill ()
+		{
+			exhausted = true;
+			recording = false;
+			Gst.Kill ();
+			DeleteTemporaryFiles ();
+		}
+		
+		/**
+		 * Delete all created temporary files.
+		 */
+		public void DeleteTemporaryFiles ()
+		{
+			foreach (string filename in CreatedTemporaryFiles)
+				if (File.Exists (filename))
+					File.Delete (filename);
+		}
+		
+		/**
 		 * true if the recorder is currently recording.
 		 */
 		public bool IsRecording {
 		}
 		
 		/**
+		 * true if the recorder is already initialized.  If that's the
+		 * case no modifications on the record affecting properties are
+		 * allowed.
+		 * 
+		 * This is usually the case after recording started.
+		 */
+		public bool IsInitialized {
+			get { return gst != null; }
+		}
+		
+		/**
 		 * true if the full screen is captured.
 		 */
 		public bool FullscreenCapture {
 			get { return rectangle == Gdk.Rectangle.Zero; }
 			set {
+				AssertUninitialized ();
 				if (!value)
 					throw new ArgumentException ("To disable fullscreen " +
 					                             "capture assign a rectangle.");
 		}
 		
 		/**
-		 * true if the recorder is already initialized.  If that's the
-		 * case no modifications on the record affecting properties are
-		 * allowed.
-		 * 
-		 * This is usually the case after recording started.
+		 * true if the mouse pointer should be hidden.
 		 */
-		public bool IsInitialized {
-			get { return gst != null; }
+		public bool HideMousePointer {
+			get { return hideMousePointer; }
+			set {
+				AssertUninitialized ();
+				hideMousePointer = value;
+			}
 		}
 		
 		public Gdk.Rectangle Rectangle {
 			}
 		}
 		
+		public int OutputWidth {
+			get { return outputWidth; }
+			set {
+				AssertUninitialized ();
+				outputWidth = value;
+			}
+		}
+		
+		public int OutputHeight {
+			get { return outputHeight; }
+			set {
+				AssertUninitialized ();
+				outputHeight = value;
+			}
+		}
+		
+		public bool ResizeOutput {
+			get { return OutputWidth >= 0 && OutputHeight >= 0; }
+		}
+		
 		public string VideoEncoderArguments {
 			get { return videoEncoderArguments; }
 			set {
 			}
 		}
 		
+		public string AudioEncoderArguments {
+			get { return audioEncoderArguments; }
+			set {
+				AssertUninitialized ();
+				audioEncoderArguments = (value == "") ? null : value;
+			}
+		}
+		
+		public bool RecordAudio {
+			get { return recordAudio; }
+			set {
+				AssertUninitialized ();
+				recordAudio = value;
+			}
+		}
+		
+		public AudioEncoder AudioEncoder {
+			get { return VideoEncoder.Container.AudioCodec; }
+		}
+		
 		public string Pipeline {
 			get {
-				StringBuilder b = new StringBuilder ("ximagesrc name=screeninput");
+				if (RecordAudio)
+					return String.Format ("{0} ! {1} ! {2} ! queue ! mux. {3} ! queue ! mux.",
+					                      ContainerPipeline, VideoCapturePipeline,
+					                      VideoEncodePipeline, AudioPipeline);
+				return String.Format ("{0} ! queue ! {1} ! {2}", VideoCapturePipeline,
+				                      VideoEncodePipeline, ContainerPipeline);
+			}
+		}
+		
+		public string VideoEncodePipeline {
+			get {
+				string result = VideoEncoder.GstKey;
+				if (VideoEncoderArguments != null)
+					result += " " + VideoEncoderArguments;
+				
+				if (ResizeOutput)
+					result = String.Format ("video/x-raw-rgb,width={0},height={1} ! {2}",
+					                        OutputWidth, OutputHeight, result);
+				
+				return result;
+			}
+		}
+		
+		public string ContainerPipeline {
+			get {
+				string result = "";
+				if (VideoEncoder.Container.GstKey != null)
+					result += VideoEncoder.Container.GstKey + " name=mux ! ";
+				if (VideoEncoder.Container.RequiresMultiFileSink)
+					result += "multifilesink location=\"" + TemporaryFilename + "-%06d\"";
+				else
+					result += "filesink location=\"" + TemporaryFilename + "\"";
+				return result;
+			}
+		}
+		
+		public string VideoCapturePipeline {
+			get {
+				StringBuilder b = new StringBuilder ("ximagesrc");
 				
 				// offsets and dimensions if given
 				if (!FullscreenCapture) {
 					b.Append (" endy=" + (Rectangle.Y + Rectangle.Height));
 				}
 				
+				// disable mouse pointer if wanted
+				if (HideMousePointer)
+					b.Append(" show-pointer=false");				
+				
 				// Video Format
 				b.Append (" ! video/x-raw-rgb,framerate=" + Framerate + "/1");
 				
 				// Regular processing
-				b.Append (" ! videorate ! ffmpegcolorspace ! queue");
+				b.Append (" ! videorate ! ffmpegcolorspace");
 				
-				// Encodinging
-				b.Append (" ! " + VideoEncoder.GstKey);
-				if (VideoEncoderArguments != null)
-					b.Append(" " + VideoEncoderArguments);
-				b.Append (" ! " + VideoEncoder.Container.GstKey);
-
-				// Output Filename
-				b.Append (" ! filesink location=\"" + TemporaryFilename + "\"");
-				
-				return b.ToString();
+				return b.ToString ();
+			}
+		}
+		
+		public string AudioPipeline {
+			get {
+				// XXX: switch from gconfaudiosrc to the selection from
+				// the configuration panel.
+				string result = "gconfaudiosrc ! audioconvert ! " +
+					AudioEncoder.GstKey;
+				if (AudioEncoderArguments != null)
+					result += " " + AudioEncoderArguments;
+				return result;
 			}
 		}
 		
 			}
 		}
 		
+		public string[] CreatedTemporaryFiles {
+			get {
+				if (createdTemporaryFiles == null) {
+					if (!VideoEncoder.Container.RequiresMultiFileSink)
+						createdTemporaryFiles = new string[] { TemporaryFilename };
+					else {
+						createdTemporaryFiles =
+							Utils.FindPrefixedFiles (TemporaryFilename + "-");
+						Array.Sort (createdTemporaryFiles);
+					}
+				}
+				return createdTemporaryFiles;
+			}
+		}
+		
 		public VideoEncoder VideoEncoder {
 			get { return videoEncoder; }
 			set {

GNOME-Record-Screen/src/UI/MainWindow.cs

 			InitVideoFormats ();
 			
 			// disable not yet available features in the GUI
-			audioQuality.Sensitive = false;
-			recordSystemAudio.Sensitive = false;
-			recordMicrophoneAudio.Sensitive = false;
-			microphoneInput.Sensitive = false;
 			addPresetButton.Sensitive = false;
 			presets.Sensitive = false;
+			recordSystemAudio.Sensitive = false;
+			microphoneInput.Sensitive = false;
 		}
 
 		/**
 			foreach (VideoEncoder v in Utils.AvailableVideoEncoders)
 				videoFormat.AppendText (v.Name);
 			
-			if (Utils.AvailableVideoEncoders.Count == 0)
+			if (Utils.AvailableVideoEncoders.Length == 0)
 				Utils.ShowError ("No encoders available", "You do not have " +
 				                 "the appropriate gstreamer encoder plugins " +
 				                 "installed.\n\nInstall at least one " +
 			recorder.Framerate = Convert.ToDouble(regularFrameRate.ActiveText);
 			recorder.VideoEncoderArguments =
 				ActiveVideoEncoder.GetEncoderArguments (VideoEncoderConfigurationPane);
-			recorder.Rectangle = selectedRectangle;			
+			recorder.Rectangle = selectedRectangle;
+			
+			if (RecordAudio) {
+				recorder.RecordAudio = true;
+				recorder.AudioEncoderArguments =
+					ActiveAudioEncoder.GetEncoderArguments (AudioQuality);
+			}
+			
 			recorder.RecordFinished += OnRecordFinished;
+			recorder.ErrorOccurred += OnRecordError;
 			recorder.Record ();
 		}
 		
 				try {
 					return Utils.AvailableVideoEncoders[videoFormat.Active];
 				}
-				catch (ArgumentOutOfRangeException) {
+				catch (IndexOutOfRangeException) {
 					return null;
 				}
 			}
 				throw new ArgumentException ("Unavailable encoder");
 			}
 		}
+			
+		/**
+		 * The audio encoder is linked to the active video container
+		 * thus this property is read only.
+		 */
+		public AudioEncoder ActiveAudioEncoder {
+			get {
+				VideoEncoder enc = ActiveVideoEncoder;
+				return (enc != null) ? enc.Container.AudioCodec : null;
+			}
+		}
+		
+		/**
+		 * Disable or enable the audio controls in one go.
+		 */
+		public bool EnableAudioControls {
+			get { return audioQuality.Sensitive; }
+			set {
+				audioQuality.Sensitive = value;
+				recordMicrophoneAudio.Sensitive = value;
+			}
+		}
+		
+		/**
+		 * The audio quality.
+		 */
+		public AudioQuality AudioQuality {
+			get { return (AudioQuality)audioQuality.Active; }
+		}
+		
+		/**
+		 * True if audio recording is enabled.
+		 */
+		public bool RecordAudio {
+			get {
+				return ActiveAudioEncoder.IsAvailable && (recordSystemAudio.Active ||
+				                                          recordMicrophoneAudio.Active);
+			}
+		}
 		
 		/**
 		 * The configuration pane for the current active video encoder.
 				oldVideoEncoderPane = null;
 			}
 			videoFormatConfigurationLabel.Visible = false;
+						
+			// if the audio codec for the selected container is available
+			// enable the audio components, otherwise disable
+			EnableAudioControls = (ActiveAudioEncoder != null &&
+			                       ActiveAudioEncoder.IsAvailable);
 			
 			// load the configuration pane for the active encoder
 			Gtk.Widget widget = VideoEncoderConfigurationPane;
 			dialog.MoveOrDelete ();
 		}
 		
+		protected virtual void OnRecordError (object sender, RecordErrorEventArgs e)
+		{
+			stopButton.Visible = false;
+			recordButton.Visible = true;
+			Console.Error.WriteLine ("Error in Pipeline: " + recorder.Pipeline);
+			recorder.Kill ();
+			Utils.ShowError ("Record Error", "An error in the recording pipeline " +
+			                 "happened:\n\n" + e.Error + "\n\nThis could be caused" +
+			                 "by a bug in a codec or unfortunate Video settings.");
+		}
+		
 #endregion
 	}
 }

GNOME-Record-Screen/src/UI/MpegConfigurationPane.cs

 		{
 			Build ();
 			
-			foreach (string name in MpegVideoEncoder.MpegFormats)
+			foreach (string name in Utils.MpegFormats)
 				format.AppendText (name);
 			format.Active = 0;
 		}

GNOME-Record-Screen/src/UI/SaveDialog.cs

  */
 
 using System;
+using System.Collections.Generic;
 using System.IO;
 using Gtk;
 
 			filter.Name = "All files";
 			AddFilter (filter);
 			
-			DoOverwriteConfirmation = true;
-			
-			CurrentName = Utils.AddDateToFilename ("Recorded Desktop") +
-				enc.Container.Extensions[0];
+			string filename;
+			if (MultiFileMode)
+				filename = "recorded-desktop-###";
+			else {
+				DoOverwriteConfirmation = true;
+				filename = Utils.AddDateToFilename ("Recorded Desktop");
+			}			
+				
+			CurrentName = filename + enc.Container.Extensions[0];
+		}
+		
+		public bool MultiFileMode {
+			get { return recorder.VideoEncoder.Container.RequiresMultiFileSink; }
+		}
+		
+		public void MoveRecordedFiles ()
+		{			
+			if (MultiFileMode) {
+				string pattern = Filename;
+				if (pattern.IndexOf ('#') < 0)
+					pattern = Utils.AddToFilename (pattern, "#");
+				int index = 0;
+				foreach (string src in recorder.CreatedTemporaryFiles) {
+					if (!File.Exists (src))
+						continue;
+					string dst = Utils.AddFormatedNumber (pattern, index++);
+					if (File.Exists (dst))
+						File.Delete (dst);
+					File.Move (src, dst);
+				}
+			}
+			else {
+				if (File.Exists (Filename))
+					File.Delete (Filename);
+				File.Move (recorder.TemporaryFilename, Filename);
+			}
+		}
+		
+		public void DeleteRecordedFiles ()
+		{
+			recorder.DeleteTemporaryFiles ();
 		}
 		
 		public void MoveOrDelete ()
 		{
 			if ((ResponseType)Run() == ResponseType.Accept) {
-				if (File.Exists (Filename))
-					File.Delete (Filename);
-				File.Move (recorder.TemporaryFilename, Filename);
+				MoveRecordedFiles ();
 			}
 			else
-				File.Delete (recorder.TemporaryFilename);
+				DeleteRecordedFiles ();
 			Destroy ();
 		}
 	}

GNOME-Record-Screen/src/Utils.cs

  */
 
 using System;
+using System.IO;
 using System.Text;
 using System.Collections.Generic;
 using System.Runtime.InteropServices;
+using System.Linq;
 
 using Gtk;
 using Mono.Unix;
 		
 	}
 	
+	public interface IGstObject {
+		
+		bool IsAvailable { get; }
+		string GstKey { get; }
+	}
+	
+	public interface IEncoder : IGstObject {
+	}
+	
 	public class Utils {
-		private static List<VideoEncoder> availableVideoEncoders;
-		
-		/**
-		 * A list of available video encoders.
-		 */
-		public static List<VideoEncoder> AvailableVideoEncoders {
-			get {
-				if (availableVideoEncoders == null) {
-					availableVideoEncoders = new List<VideoEncoder> ();
-					if (GStreamerInterface.HasEncoder (VideoEncoder.H264))
-						availableVideoEncoders.Add (VideoEncoder.H264);
-					if (GStreamerInterface.HasEncoder (VideoEncoder.Theora))
-						availableVideoEncoders.Add (VideoEncoder.Theora);
-					if (GStreamerInterface.HasEncoder (VideoEncoder.Mpeg))
-						availableVideoEncoders.Add (VideoEncoder.Mpeg);
-				}
-				return availableVideoEncoders;
-			}
-		}
+		private static VideoEncoder[] availableVideoEncoders;
+		private static string[] mpegFormats = new string[] {
+			"Generic MPEG1",
+			"Standard VCD",
+			"User VCD",
+			"Generic Mpeg",
+			"Standard SVCD",
+			"User SVCD",
+			"VCD stills sequences",
+			"SVCD stills sequences",
+			"DVD MPEG-2 for author",
+			"DVD MPEG-2"
+		};
 		
 		/**
 		 * Shows an error message as dialog.
 		 */
 		public static string AddDateToFilename (string filename)
 		{
+			DateTime now = DateTime.Now;
+			string text = String.Format(" ({0}-{1}-{2})",
+			                            now.Year, now.Month, now.Day);			
+			return AddToFilename (filename, text);
+		}
+		
+		/**
+		 * Add something to a filename.
+		 */
+		public static string AddToFilename (string filename, string text)
+		{
 			string extension = "";
 			List<string> parts = new List<string> (filename.Split ('.'));
 			if (parts.Count > 1) {
 				parts.RemoveAt(parts.Count - 1);
 				filename = String.Join(".", parts.ToArray ());
 			}
-			DateTime now = DateTime.Now;
-			return String.Format("{0} ({1}-{2}-{3}){4}", filename,
-			                     now.Year, now.Month, now.Day,
-			                     extension);
+			return filename + text + extension;
+		}
+		
+		/**
+		 * Find all the files in a directory that start with the
+		 * given prefix.
+		 */
+		public static string[] FindPrefixedFiles (string path, string prefix)
+		{
+			string real_prefix = Path.Combine (path, prefix);
+			List<string> found = new List<string> ();
+			foreach (string filename in Directory.GetFiles (path))
+				if (filename.StartsWith (real_prefix))
+					found.Add (Path.Combine (path, filename));
+			return found.ToArray ();
+		}
+		
+		public static string[] FindPrefixedFiles (string filename)
+		{
+			return FindPrefixedFiles (Path.GetDirectoryName (filename),
+			                          Path.GetFileName (filename));
+		}
+		
+		/**
+		 * Inserts a number into the string where the pound signs are.
+		 */
+		public static string AddFormatedNumber (string template, int number)
+		{
+			string pattern = "";
+			int length = 0;
+			int first_pos = template.IndexOf ('#');
+			if (first_pos < 0)
+				throw new ArgumentException ("No pound signs in string.");
+
+			for (int i = first_pos; i < template.Length; i++) {
+				if (template[i] == '#') {
+					length++;
+					pattern += "0";
+				}
+			}
+			
+			return (template.Substring (0, first_pos) +
+			        number.ToString (pattern) +
+			        template.Substring (first_pos + length));
 		}
 		
 		/**
 			}
 		}
 		
+		/**
+		 * List of supported MPEG formats.
+		 */
+		public static string[] MpegFormats {
+			get { return mpegFormats; }
+		}		
+		
+		/**
+		 * A list of available video encoders.
+		 */
+		public static VideoEncoder[] AvailableVideoEncoders {
+			get {
+				if (availableVideoEncoders == null)
+					availableVideoEncoders = (new VideoEncoder[] {
+						VideoEncoder.H264,
+						VideoEncoder.Theora,
+						VideoEncoder.Mpeg,
+						VideoEncoder.Png
+					}).Where(x => x.IsAvailable).ToArray ();
+				return availableVideoEncoders;
+			}
+		}
+		
 #region Low-Level Functions
 		
 		[DllImport ("libc")] // Linux

GNOME-Record-Screen/src/VideoEncoder.cs

+/**
+ * VideoEncoder.cs
+ *
+ * :copyright: 2008 mitsuhiko.
+ * :license: BSD
+ */
+
+
+using System;
+using System.Collections.Generic;
+
+using Gnome.RecordScreen.UI;
+using Gnome.RecordScreen.Implementations;
+
+
+namespace Gnome.RecordScreen {
+	
+	public class RectangleTooSmallException : SystemException {
+		
+		public RectangleTooSmallException () :
+			base ("The rectangle is smaller than the supported size.")
+		{}
+	}
+	
+	public class VideoContainer : IGstObject {
+		private string name;
+		private string gstKey;
+		private string[] extensions;
+		private AudioEncoder audioCodec;
+		private bool requiresMultiFileSink;
+		
+		public VideoContainer (string name, string gstKey, string[] extensions,
+		                       AudioEncoder audioCodec, bool requiresMultiFileSink)
+		{
+			this.name = name;
+			this.gstKey = gstKey;
+			this.extensions = extensions;
+			this.audioCodec = audioCodec;
+			this.requiresMultiFileSink = requiresMultiFileSink;
+		}
+		
+		public string Name {
+			get { return name; }
+		}
+		
+		public string GstKey {
+			get { return gstKey; }
+		}
+		
+		public string[] Extensions {
+			get { return extensions; }
+		}
+		
+		public AudioEncoder AudioCodec {
+			get { return audioCodec; }
+		}
+		
+		public bool IsAvailable {
+			get {
+				// some containers are actually not containers
+				// but just dummies.  In that case such a thing
+				// always exists.
+				if (GstKey == null)
+					return true;
+				return GStreamerInterface.SupportsObject (this);
+			}
+		}
+
+		public bool RequiresMultiFileSink {
+			get { return requiresMultiFileSink; }
+		}
+				
+		private static VideoContainer avi =
+			new VideoContainer ("AVI", "avimux", new string[] { ".avi" },
+			                    AudioEncoder.Mp3, false);
+		public static VideoContainer Avi {
+			get { return avi; }
+		}
+		
+		private static VideoContainer ogg =
+			new VideoContainer ("Ogg", "oggmux", new string[] { ".ogg", ".ogv" },
+			                    AudioEncoder.Vorbis, false);
+		public static VideoContainer Ogg {
+			get { return ogg; }
+		}
+		
+		private static VideoContainer png =
+			new VideoContainer ("PNG", null, new string[] { ".png" },
+			                    AudioEncoder.Null, true);
+		public static VideoContainer Png {
+			get { return png; }
+		}
+	}
+	
+	
+	/**
+	 * Subclasses of this class represent video encoders.
+	 */
+	public class VideoEncoder : IEncoder {
+		private string name;
+		private string gstKey;
+		private VideoContainer container;
+
+		protected VideoEncoder (string name, string gstKey, VideoContainer container)
+		{
+			this.name = name;
+			this.gstKey = gstKey;
+			this.container = container;
+		}
+		
+		public string Name {
+			get { return name; }
+		}
+		
+		public string GstKey {
+			get { return gstKey; }
+		}
+		
+		public VideoContainer Container {
+			get { return container; }
+		}
+		
+		public virtual Gdk.Rectangle OptimizeRectangle (Gdk.Rectangle rectangle)
+		{
+			return rectangle;
+		}
+		
+		public virtual ConfigurationPane CreateConfigurationPane ()
+		{
+			return null;
+		}
+		
+		public virtual bool IsAvailable {
+			get { return (GStreamerInterface.SupportsObject (this) &&
+			              Container.IsAvailable); }
+		}
+		
+		/**
+		 * This method is passed a configuration pane and should return the
+		 * encoder arguments as string based on the values from that pane.  If
+		 * the pane is unsupported (pane from different encoder for example)
+		 * the return value must be null.
+		 */
+		public virtual string GetEncoderArguments (ConfigurationPane pane)
+		{
+			return null;
+		}
+		
+		private static VideoEncoder h264 = new H264VideoEncoder ();
+		public static VideoEncoder H264 {
+			get { return h264; }
+		}
+
+		private static VideoEncoder theora = new TheoraVideoEncoder ();
+		public static VideoEncoder Theora {
+			get { return theora; }
+		}
+		
+		private static VideoEncoder mpeg = new MpegVideoEncoder ();		
+		public static VideoEncoder Mpeg {
+			get { return mpeg; }
+		}
+		
+		private static VideoEncoder png = new PngVideoEncoder ();
+		public static VideoEncoder Png {
+			get { return png; }
+		}
+	}
+}

GNOME-Record-Screen/src/VideoEncoderImplementations.cs

+/**
+ * VideoEncoderImplementations.cs
+ * 
+ * This module implements the various encoder classes.
+ *
+ * :copyright: 2008 mitsuhiko.
+ * :license: BSD
+ */
+
+using Gnome.RecordScreen.UI;
+
+
+namespace Gnome.RecordScreen.Implementations {
+			
+	/**
+	 * Encodes videos with the H.264 encoder into an AVI container.
+	 */
+	class H264VideoEncoder : VideoEncoder {
+		
+		public H264VideoEncoder ()
+			: base ("H.264", "x264enc", VideoContainer.Avi)
+		{}
+		
+		public override Gdk.Rectangle OptimizeRectangle (Gdk.Rectangle rectangle)
+		{
+			int width = rectangle.Width;
+			int height = rectangle.Height;
+			
+			if (width % 16 != 0)
+				width = 16 * (width / 16 + 1);
+			if (height % 16 != 0)
+				height = 16 * (height / 16 + 1);
+			
+			if (height != rectangle.Height || width != rectangle.Width)
+				rectangle = new Gdk.Rectangle (rectangle.X, rectangle.Y,
+				                               width, height);
+			
+			return rectangle;
+		}
+		
+		public override ConfigurationPane CreateConfigurationPane ()
+		{
+			return new H264ConfigurationPane ();
+		}
+		
+		public override string GetEncoderArguments (ConfigurationPane pane)
+		{
+			H264ConfigurationPane hpane = pane as H264ConfigurationPane;
+			if (hpane == null)
+				return null;
+			return "bitrate=" + hpane.BitRate;
+		}
+	}
+	
+	/**
+	 * Encodes videos using the Theora encoder.
+	 */
+	class TheoraVideoEncoder : VideoEncoder {
+			
+		public TheoraVideoEncoder ()
+			: base ("Theora", "theoraenc", VideoContainer.Ogg)
+		{}
+		
+		public override ConfigurationPane CreateConfigurationPane ()
+		{
+			return new TheoraConfigurationPane ();
+		}
+		
+		public override string GetEncoderArguments (ConfigurationPane pane)
+		{
+			TheoraConfigurationPane tpane = pane as TheoraConfigurationPane;
+			if (tpane == null)
+				return null;
+			return "quality=" + tpane.VideoQuality;
+		}
+	}
+	
+	/**
+	 * Encodes videos using the MPEG encoder.
+	 */
+	class MpegVideoEncoder : VideoEncoder {
+					
+		public MpegVideoEncoder ()
+			: base ("MPEG", "mpeg2enc", VideoContainer.Avi)
+		{}
+		
+		public override ConfigurationPane CreateConfigurationPane ()
+		{
+			return new MpegConfigurationPane ();
+		}
+		
+		public override string GetEncoderArguments (ConfigurationPane pane)
+		{
+			MpegConfigurationPane mpane = pane as MpegConfigurationPane;
+			if (mpane == null)
+				return null;
+			return "format=" + mpane.Format + " bitrate=" + mpane.BitRate;
+		}
+	}
+	
+	/**
+	 * Encodes videos into multiple PNG files.
+	 */
+	class PngVideoEncoder : VideoEncoder {
+		
+		public PngVideoEncoder ()
+			: base ("PNG", "pngenc", VideoContainer.Png)
+		{}
+		
+		public override string GetEncoderArguments (ConfigurationPane pane)
+		{
+			return "snapshot=false";
+		}
+	}
+}

GNOME-Record-Screen/src/VideoEncoders.cs

-/**
- * VideoEncoders.cs
- *
- * :copyright: 2008 mitsuhiko.
- * :license: BSD
- */
-
-
-using System;
-using System.Collections.Generic;
-
-using Gnome.RecordScreen.UI;
-
-
-namespace Gnome.RecordScreen {
-	
-	public class RectangleTooSmallException : SystemException {
-		
-		public RectangleTooSmallException () :
-			base ("The rectangle is smaller than the supported size.")
-		{}
-	}
-		
-	public class VideoContainer {
-		private string name;
-		private string gstKey;
-		private string[] extensions;
-		
-		public static VideoContainer Avi;
-		public static VideoContainer Ogg;
-		public static VideoContainer Asf;
-		public static VideoContainer Flash;
-		
-		static VideoContainer ()
-		{
-			Avi = new VideoContainer ("AVI", "avimux", new string[] { ".avi" });
-			Ogg = new VideoContainer ("Ogg", "oggmux", new string[] { ".ogg", ".ogv" });
-			Asf = new VideoContainer ("ASF", "ffmux_asf", new string[] { ".wmv", ".asf" });
-			Flash = new VideoContainer ("Flash", "ffmux_flv", new string[] { ".flv" });
-		}
-		
-		public VideoContainer (string name, string gstKey, string[] extensions)
-		{
-			this.name = name;
-			this.gstKey = gstKey;
-			this.extensions = extensions;
-		}
-		
-		public string Name {
-			get { return name; }
-		}
-		
-		public string GstKey {
-			get { return gstKey; }
-		}
-		
-		public string[] Extensions {
-			get { return extensions; }
-		}		
-	}
-	
-	public class VideoEncoder {
-		private string name;
-		private string gstKey;
-		private VideoContainer container;
-
-		public static VideoEncoder H264 = new H264VideoEncoder ();
-		public static VideoEncoder Theora = new TheoraVideoEncoder ();
-		public static VideoEncoder Mpeg = new MpegVideoEncoder ();
-
-		public VideoEncoder (string name, string gstKey, VideoContainer container)
-		{
-			this.name = name;
-			this.gstKey = gstKey;
-			this.container = container;
-		}
-		
-		public string Name {
-			get { return name; }
-		}
-		
-		public string GstKey {
-			get { return gstKey; }
-		}
-		
-		public VideoContainer Container {
-			get { return container; }
-		}
-		
-		public virtual Gdk.Rectangle OptimizeRectangle (Gdk.Rectangle rectangle)
-		{
-			return rectangle;
-		}
-		
-		public virtual ConfigurationPane CreateConfigurationPane ()
-		{
-			return null;
-		}
-		
-		/**
-		 * This method is passed a configuration pane and should return the
-		 * encoder arguments as string based on the values from that pane.  If
-		 * the pane is unsupported (pane from different encoder for example)
-		 * the return value must be null.
-		 */
-		public virtual string GetEncoderArguments (ConfigurationPane pane)
-		{
-			return null;
-		}
-	}
-	
-	/**
-	 * Encodes videos with the H.264 encoder into an AVI container.
-	 */
-	class H264VideoEncoder : VideoEncoder {
-		
-		public H264VideoEncoder ()
-			: base ("H.264", "x264enc", VideoContainer.Avi)
-		{}
-		
-		public override Gdk.Rectangle OptimizeRectangle (Gdk.Rectangle rectangle)
-		{
-			int width = rectangle.Width;
-			int height = rectangle.Height;
-			
-			if (width % 16 != 0)
-				width = 16 * (width / 16 + 1);
-			if (height % 16 != 0)
-				height = 16 * (height / 16 + 1);
-			
-			if (height != rectangle.Height || width != rectangle.Width)
-				rectangle = new Gdk.Rectangle (rectangle.X, rectangle.Y,
-				                               width, height);
-			
-			return rectangle;
-		}
-		
-		public override ConfigurationPane CreateConfigurationPane ()
-		{
-			return new H264ConfigurationPane ();
-		}
-		
-		public override string GetEncoderArguments (ConfigurationPane pane)
-		{
-			H264ConfigurationPane hpane = pane as H264ConfigurationPane;
-			if (hpane == null)
-				return null;
-			return "bitrate=" + hpane.BitRate;
-		}
-	}
-	
-	/**
-	 * Encodes videos using the Theora encoder.
-	 */
-	public class TheoraVideoEncoder : VideoEncoder {
-			
-		public TheoraVideoEncoder ()
-			: base ("Theora", "theoraenc", VideoContainer.Ogg)
-		{}
-		
-		public override ConfigurationPane CreateConfigurationPane ()
-		{
-			return new TheoraConfigurationPane ();
-		}
-		
-		public override string GetEncoderArguments (ConfigurationPane pane)
-		{
-			TheoraConfigurationPane tpane = pane as TheoraConfigurationPane;
-			if (tpane == null)
-				return null;
-			return "quality=" + tpane.VideoQuality;
-		}
-	}
-	
-	/**
-	 * Encodes videos using the Mpeg encoder.
-	 */
-	public class MpegVideoEncoder : VideoEncoder {
-		
-		private static string[] mpegFormats = new string[] {
-			"Generic MPEG1",
-			"Standard VCD",
-			"User VCD",
-			"Generic Mpeg",
-			"Standard SVCD",
-			"User SVCD",
-			"VCD stills sequences",
-			"SVCD stills sequences",
-			"DVD MPEG-2 for author",
-			"DVD MPEG-2"
-		};
-					
-		public MpegVideoEncoder ()
-			: base ("MPEG", "mpeg2enc", VideoContainer.Ogg)
-		{}
-		
-		public override ConfigurationPane CreateConfigurationPane ()
-		{
-			return new MpegConfigurationPane ();
-		}
-		
-		public override string GetEncoderArguments (ConfigurationPane pane)
-		{
-			MpegConfigurationPane mpane = pane as MpegConfigurationPane;
-			if (mpane == null)
-				return null;
-			return "format=" + mpane.Format + " bitrate=" + mpane.BitRate;
-		}
-		
-		public static string[] MpegFormats {
-			get { return mpegFormats; }
-		}
-	}
-}

librecordscreen/main.c

 	case GST_MESSAGE_ERROR: {
 		GError *gerror = NULL;
 		gchar *debug;
-		gst_message_parse_info(message, &gerror, &debug);
+		gst_message_parse_error(message, &gerror, &debug);
 		*error = g_strdup(gerror->message);
 		g_free(debug);
 		g_error_free(gerror);
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.