diff --git a/BizHawk.MultiClient/AviWriter.cs b/BizHawk.MultiClient/AviWriter.cs index 2b8d9cb9a9..33af053b2e 100644 --- a/BizHawk.MultiClient/AviWriter.cs +++ b/BizHawk.MultiClient/AviWriter.cs @@ -52,6 +52,58 @@ namespace BizHawk.MultiClient /// public void OpenFile(string baseName) { OpenFile(CreateBasicNameProvider(baseName)); } + // thread communication + // synchronized queue with custom messages + // it seems like there are 99999 ways to do everything in C#, so i'm sure this is not the best + System.Collections.Concurrent.BlockingCollection threadQ; + System.Threading.Thread workerT; + + void threadproc() + { + try + { + while (true) + { + Object o = threadQ.Take(); + if (o is IVideoProvider) + AddFrameEx((IVideoProvider)o); + else if (o is short[]) + AddSamplesEx((short[])o); + else + // anything else is assumed to be quit time + return; + } + } + catch (Exception) + { + return; + } + } + + // we can't pass the IVideoProvider we get to another thread, because it doesn't actually keep a local copy of its data, + // instead grabbing it from the emu as needed. this causes frame loss/dupping as a race condition + // instead we pass this + class VideoCopy : IVideoProvider + { + int[] vb; + int bw, bh, bc; + public int BufferWidth { get {return bw;} } + public int BufferHeight { get { return bh; } } + public int BackgroundColor { get { return bc; } } + public VideoCopy(IVideoProvider c) + { + vb = (int []) c.GetVideoBuffer().Clone (); + bw = c.BufferWidth; + bh = c.BufferHeight; + bc = c.BackgroundColor; + } + public int[] GetVideoBuffer() + { + return vb; + } + } + + /// /// opens an avi file for recording with the supplied enumerator used to name files. /// set a video codec token first. @@ -62,16 +114,29 @@ namespace BizHawk.MultiClient this.nameProvider = nameProvider; if (currVideoCodecToken == null) throw new InvalidOperationException("Tried to start recording an AVI with no video codec token set"); + + threadQ = new System.Collections.Concurrent.BlockingCollection(30); + workerT = new System.Threading.Thread(new System.Threading.ThreadStart(threadproc)); + workerT.Start(); } public void CloseFile() { + threadQ.Add(new Object ()); // acts as stop message + workerT.Join(); if (currSegment != null) currSegment.Dispose(); currSegment = null; } - public void AddFrame(IVideoProvider source) + public void AddFrame(IVideoProvider source) + { + if (!workerT.IsAlive) + // signal some sort of error? + return; + threadQ.Add(new VideoCopy (source)); + } + void AddFrameEx(IVideoProvider source) { SetVideoParameters(source.BufferWidth, source.BufferHeight); ConsiderLengthSegment(); @@ -79,7 +144,16 @@ namespace BizHawk.MultiClient currSegment.AddFrame(source); } - public void AddSamples(short[] samples) + public void AddSamples(short[] samples) + { + if (!workerT.IsAlive) + // signal some sort of error? + return; + // as MainForm.cs is written now, samples is all ours (nothing else will use it for anything) + // but that's a bad assumption to make and could change in the future, so copy it since we're passing to another thread + threadQ.Add((short []) samples.Clone ()); + } + void AddSamplesEx(short[] samples) { ConsiderLengthSegment(); if (currSegment == null) Segment();