diff --git a/BizHawk.MultiClient/BizHawk.MultiClient.csproj b/BizHawk.MultiClient/BizHawk.MultiClient.csproj
index e967543f74..fe940cacfc 100644
--- a/BizHawk.MultiClient/BizHawk.MultiClient.csproj
+++ b/BizHawk.MultiClient/BizHawk.MultiClient.csproj
@@ -164,6 +164,7 @@
+
Form
diff --git a/BizHawk.MultiClient/FFmpegWriter.cs b/BizHawk.MultiClient/FFmpegWriter.cs
new file mode 100644
index 0000000000..4464f670ef
--- /dev/null
+++ b/BizHawk.MultiClient/FFmpegWriter.cs
@@ -0,0 +1,229 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Net.Sockets;
+using System.Net;
+using System.Diagnostics;
+
+namespace BizHawk.MultiClient
+{
+ ///
+ /// uses tcp sockets to launch an external ffmpeg process and encode
+ ///
+ class FFmpegWriter : WavWriterV, IVideoWriter
+ {
+ ///
+ /// handle to external ffmpeg process
+ ///
+ Process ffmpeg;
+
+ ///
+ /// the commandline actually sent to ffmpeg; for informative purposes
+ ///
+ string commandline;
+
+ ///
+ /// current file segment (for multires)
+ ///
+ int segment;
+
+ ///
+ /// base filename before segment number is attached
+ ///
+ string baseName;
+
+ ///
+ /// recent lines in ffmpeg's stderr, for informative purposes
+ ///
+ Queue stderr;
+
+ ///
+ /// number of lines of stderr to buffer
+ ///
+ const int consolebuffer = 5;
+
+ public new void OpenFile(string baseName)
+ {
+ string s = System.IO.Path.GetFileNameWithoutExtension(baseName);
+
+ base.OpenFile(s + ".wav");
+
+ this.baseName = s;
+
+ segment = 0;
+ OpenFileSegment();
+ }
+
+ ///
+ /// starts an ffmpeg process and sets up associated sockets
+ ///
+ void OpenFileSegment()
+ {
+ ffmpeg = new Process();
+ ffmpeg.StartInfo.FileName = "ffmpeg";
+
+ string filename = String.Format("{0}_{1,4:D4}", baseName, segment);
+
+ ffmpeg.StartInfo.Arguments = String.Format
+ (
+ "-y -f rawvideo -pix_fmt bgra -s {0}x{1} -r {2}/{3} -i - -vcodec libx264rgb -crf 0 \"{4}.mkv\"",
+ width,
+ height,
+ fpsnum,
+ fpsden,
+ filename
+ );
+
+ ffmpeg.StartInfo.CreateNoWindow = true;
+
+ // ffmpeg sends informative display to stderr, and nothing to stdout
+ ffmpeg.StartInfo.RedirectStandardError = true;
+ ffmpeg.StartInfo.RedirectStandardInput = true;
+ ffmpeg.StartInfo.UseShellExecute = false;
+
+ commandline = "ffmpeg " + ffmpeg.StartInfo.Arguments;
+
+ ffmpeg.ErrorDataReceived += new DataReceivedEventHandler(StderrHandler);
+
+ stderr = new Queue(consolebuffer);
+
+ ffmpeg.Start();
+ ffmpeg.BeginErrorReadLine();
+ }
+
+ ///
+ /// saves stderr lines from ffmpeg in a short queue
+ ///
+ ///
+ ///
+ void StderrHandler(object p, DataReceivedEventArgs line)
+ {
+ if (!String.IsNullOrEmpty(line.Data))
+ {
+ if (stderr.Count == consolebuffer)
+ stderr.Dequeue();
+ stderr.Enqueue(line.Data + "\n");
+ }
+ }
+
+ ///
+ /// finishes an ffmpeg process
+ ///
+ void CloseFileSegment()
+ {
+ ffmpeg.StandardInput.Close();
+
+ // how long should we wait here?
+ ffmpeg.WaitForExit(20000);
+ ffmpeg = null;
+ stderr = null;
+ commandline = null;
+ }
+
+
+ public new void CloseFile()
+ {
+ CloseFileSegment();
+ baseName = null;
+ base.CloseFile();
+ }
+
+ ///
+ /// returns a string containing the commandline sent to ffmpeg and recent console (stderr) output
+ ///
+ ///
+ string ffmpeg_geterror()
+ {
+ if (ffmpeg.StartInfo.RedirectStandardError)
+ {
+ ffmpeg.CancelErrorRead();
+ }
+ StringBuilder s = new StringBuilder();
+ s.Append(commandline);
+ s.Append('\n');
+ while (stderr.Count > 0)
+ {
+ var foo = stderr.Dequeue();
+ System.Windows.Forms.MessageBox.Show(foo);
+ s.Append(foo);
+ }
+ return s.ToString();
+ }
+
+
+ public new void AddFrame(IVideoProvider source)
+ {
+ if (source.BufferWidth != width || source.BufferHeight != height)
+ SetVideoParameters(source.BufferWidth, source.BufferHeight);
+
+ if (ffmpeg.HasExited)
+ throw new Exception("unexpected ffmpeg death:\n" + ffmpeg_geterror());
+ var a = source.GetVideoBuffer();
+ var b = new byte[a.Length * sizeof (int)];
+ Buffer.BlockCopy(a, 0, b, 0, b.Length);
+ // have to do binary write!
+ ffmpeg.StandardInput.BaseStream.Write(b, 0, b.Length);
+ }
+
+
+ ///
+ /// codec token for FFmpegWriter
+ ///
+ class FFmpegWriterToken : IDisposable
+ {
+ public void Dispose()
+ {
+ }
+ }
+
+ public new IDisposable AcquireVideoCodecToken(IntPtr hwnd)
+ {
+ return new FFmpegWriterToken();
+ }
+
+ public new void SetVideoCodecToken(IDisposable token)
+ {
+ // nyi
+ }
+
+ ///
+ /// video params
+ ///
+ int fpsnum, fpsden, width, height;
+
+ public new void SetMovieParameters(int fpsnum, int fpsden)
+ {
+ this.fpsnum = fpsnum;
+ this.fpsden = fpsden;
+ }
+
+ public new void SetVideoParameters(int width, int height)
+ {
+ this.width = width;
+ this.height = height;
+
+ /* ffmpeg theoretically supports variable resolution videos, but there's no way to
+ * signal that metadata in a raw pipe. so if we're currently in a segment,
+ * start a new one */
+ if (ffmpeg != null)
+ {
+ CloseFileSegment();
+ segment++;
+ OpenFileSegment();
+ }
+ }
+
+
+ public new void SetMetaData(string gameName, string authors, ulong lengthMS, ulong rerecords)
+ {
+ // can be implemented with ffmpeg "-metadata" parameter???
+ // nyi
+ }
+
+ public new void Dispose()
+ {
+ base.Dispose();
+ }
+ }
+}
diff --git a/BizHawk.MultiClient/MainForm.cs b/BizHawk.MultiClient/MainForm.cs
index 1ab04f3388..92f20ec0a8 100644
--- a/BizHawk.MultiClient/MainForm.cs
+++ b/BizHawk.MultiClient/MainForm.cs
@@ -2748,7 +2748,7 @@ namespace BizHawk.MultiClient
sfd.FileName = "NULL";
sfd.InitialDirectory = PathManager.MakeAbsolutePath(Global.Config.AVIPath, "");
}
- sfd.Filter = "AVI (*.avi)|*.avi|JMD (*.jmd)|*.jmd|WAV (*.wav)|*.wav|All Files|*.*";
+ sfd.Filter = "AVI (*.avi)|*.avi|JMD (*.jmd)|*.jmd|WAV (*.wav)|*.wav|Matroska (*.mkv)|*.mkv|All Files|*.*";
Global.Sound.StopSound();
var result = sfd.ShowDialog();
Global.Sound.StartSound();
@@ -2768,6 +2768,8 @@ namespace BizHawk.MultiClient
aw = new AviWriter();
else if (ext == ".wav")
aw = new WavWriterV();
+ else if (ext == ".mkv")
+ aw = new FFmpegWriter();
else // hmm?
aw = new AviWriter();
try
diff --git a/BizHawk.MultiClient/WavWriter.cs b/BizHawk.MultiClient/WavWriter.cs
index 22ff286ec2..f915bd7ed5 100644
--- a/BizHawk.MultiClient/WavWriter.cs
+++ b/BizHawk.MultiClient/WavWriter.cs
@@ -254,7 +254,7 @@ namespace BizHawk.MultiClient
public void OpenFile(string baseName)
{
- wavwriter = new WavWriter(CreateStreamIterator (baseName), sampleRate, channels);
+ wavwriter = new WavWriter(CreateStreamIterator(baseName), sampleRate, channels);
}
public void CloseFile()