first draft of avi-writing: it works, unless you abuse it.
This commit is contained in:
parent
911b11a340
commit
52f27634eb
|
@ -0,0 +1,440 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using BizHawk;
|
||||||
|
|
||||||
|
//some helpful p/invoke from http://www.codeproject.com/KB/audio-video/Motion_Detection.aspx?msg=1142967
|
||||||
|
|
||||||
|
namespace BizHawk.MultiClient
|
||||||
|
{
|
||||||
|
unsafe class AviWriter
|
||||||
|
{
|
||||||
|
static AviWriter()
|
||||||
|
{
|
||||||
|
Win32.AVIFileInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AviWriter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CodecToken currVideoCodecToken = null;
|
||||||
|
bool IsOpen;
|
||||||
|
IntPtr pAviFile, pAviRawVideoStream, pAviRawAudioStream, pAviCompressedVideoStream;
|
||||||
|
IntPtr pGlobalBuf;
|
||||||
|
int pGlobalBuf_size;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// there is just ony global buf. this gets it and makes sure its big enough. don't get all re-entrant on it!
|
||||||
|
/// </summary>
|
||||||
|
IntPtr GetStaticGlobalBuf(int amount)
|
||||||
|
{
|
||||||
|
if (amount > pGlobalBuf_size)
|
||||||
|
{
|
||||||
|
if (pGlobalBuf != IntPtr.Zero)
|
||||||
|
Marshal.FreeHGlobal(pGlobalBuf);
|
||||||
|
pGlobalBuf_size = amount;
|
||||||
|
pGlobalBuf = Marshal.AllocHGlobal(pGlobalBuf_size);
|
||||||
|
}
|
||||||
|
return pGlobalBuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class OutputStatus
|
||||||
|
{
|
||||||
|
public int video_frames;
|
||||||
|
public int video_bytes;
|
||||||
|
public int audio_samples;
|
||||||
|
public int audio_buffered_shorts;
|
||||||
|
public const int AUDIO_SEGMENT_SIZE = 44100 * 2;
|
||||||
|
public short[] BufferedShorts = new short[AUDIO_SEGMENT_SIZE];
|
||||||
|
}
|
||||||
|
OutputStatus outStatus;
|
||||||
|
|
||||||
|
|
||||||
|
static int AVISaveOptions(IntPtr stream, ref Win32.AVICOMPRESSOPTIONS opts, IntPtr owner)
|
||||||
|
{
|
||||||
|
IntPtr mem = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Win32.AVICOMPRESSOPTIONS)));
|
||||||
|
|
||||||
|
Marshal.StructureToPtr(opts, mem, false);
|
||||||
|
|
||||||
|
IntPtr[] streams = new[] { stream };
|
||||||
|
IntPtr[] infPtrs = new[] { mem };
|
||||||
|
|
||||||
|
int ret = Win32.AVISaveOptions(owner, 0, 1, streams, infPtrs);
|
||||||
|
|
||||||
|
opts = (Win32.AVICOMPRESSOPTIONS)Marshal.PtrToStructure(mem, typeof(Win32.AVICOMPRESSOPTIONS));
|
||||||
|
|
||||||
|
Marshal.FreeHGlobal(mem);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class CodecToken : IDisposable
|
||||||
|
{
|
||||||
|
public static CodecToken TakePossession(Win32.AVICOMPRESSOPTIONS comprOptions)
|
||||||
|
{
|
||||||
|
CodecToken ret = new CodecToken();
|
||||||
|
ret.allocated = true;
|
||||||
|
ret.comprOptions = comprOptions;
|
||||||
|
ret.codec = Win32.decode_mmioFOURCC(comprOptions.fccHandler);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
private CodecToken() { }
|
||||||
|
public Win32.AVICOMPRESSOPTIONS comprOptions;
|
||||||
|
public string codec;
|
||||||
|
bool allocated = false;
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (!allocated) return;
|
||||||
|
|
||||||
|
IntPtr[] infPtrs = new IntPtr[1];
|
||||||
|
IntPtr mem;
|
||||||
|
|
||||||
|
// alloc unmanaged memory
|
||||||
|
mem = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Win32.AVICOMPRESSOPTIONS)));
|
||||||
|
infPtrs[0] = mem;
|
||||||
|
|
||||||
|
// copy from managed structure to unmanaged memory
|
||||||
|
Marshal.StructureToPtr(comprOptions, mem, false);
|
||||||
|
|
||||||
|
Win32.AVISaveOptionsFree(1, infPtrs);
|
||||||
|
Marshal.FreeHGlobal(mem);
|
||||||
|
|
||||||
|
codec = null;
|
||||||
|
comprOptions = new Win32.AVICOMPRESSOPTIONS();
|
||||||
|
allocated = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Parameters
|
||||||
|
{
|
||||||
|
public int width, height;
|
||||||
|
public void PopulateBITMAPINFOHEADER(ref Win32.BITMAPINFOHEADER bmih)
|
||||||
|
{
|
||||||
|
bmih.Init();
|
||||||
|
bmih.biPlanes = 1;
|
||||||
|
bmih.biBitCount = 24;
|
||||||
|
bmih.biHeight = -height;
|
||||||
|
bmih.biWidth = width;
|
||||||
|
bmih.biSizeImage = (uint)(3 * width * height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool has_audio;
|
||||||
|
public int a_samplerate, a_channels, a_bits;
|
||||||
|
public void PopulateWAVEFORMATEX(ref Win32.WAVEFORMATEX wfex)
|
||||||
|
{
|
||||||
|
int bytes = 0;
|
||||||
|
if(a_bits == 16) bytes=2;
|
||||||
|
else if(a_bits==8) bytes=1;
|
||||||
|
else throw new InvalidOperationException("only 8/16 bits audio are supported by AviWriter and you chose: " + a_bits);
|
||||||
|
if (a_channels == 1) { }
|
||||||
|
else if (a_channels == 2) { }
|
||||||
|
else throw new InvalidOperationException("only 1/2 channels audio are supported by AviWriter and you chose: " + a_channels);
|
||||||
|
|
||||||
|
wfex.Init();
|
||||||
|
wfex.nBlockAlign = (ushort)(bytes * a_channels);
|
||||||
|
wfex.nChannels = (ushort)a_channels;
|
||||||
|
wfex.wBitsPerSample = (ushort)a_bits;
|
||||||
|
wfex.wFormatTag = Win32.WAVE_FORMAT_PCM;
|
||||||
|
wfex.nSamplesPerSec = (uint)a_samplerate;
|
||||||
|
wfex.nAvgBytesPerSec = (uint)(wfex.nBlockAlign * a_samplerate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int fps, fps_scale;
|
||||||
|
}
|
||||||
|
Parameters parameters = new Parameters();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// set basic movie timing parameters for the avi file. must be set before the file is opened.
|
||||||
|
/// </summary>
|
||||||
|
public void SetMovieParameters(int fps, int fps_scale)
|
||||||
|
{
|
||||||
|
if (IsOpen) throw new InvalidOperationException("Can't set avi parameters while it is open");
|
||||||
|
parameters.fps = fps;
|
||||||
|
parameters.fps_scale = fps_scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// set basic video parameters for the avi file. must be set before the file is opened.
|
||||||
|
/// </summary>
|
||||||
|
public void SetVideoParameters(int width, int height)
|
||||||
|
{
|
||||||
|
if (IsOpen) throw new InvalidOperationException("Can't set avi parameters while it is open");
|
||||||
|
parameters.width = width;
|
||||||
|
parameters.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// set basic audio parameters for the avi file. must be set before the file isopened.
|
||||||
|
/// </summary>
|
||||||
|
public void SetAudioParameters(int sampleRate, int channels, int bits)
|
||||||
|
{
|
||||||
|
if (IsOpen) throw new InvalidOperationException("Can't set avi parameters while it is open");
|
||||||
|
parameters.a_samplerate = sampleRate;
|
||||||
|
parameters.a_channels = channels;
|
||||||
|
parameters.a_bits = bits;
|
||||||
|
parameters.has_audio = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// commits the movie parameters and opens the output file
|
||||||
|
/// </summary>
|
||||||
|
public void OpenFile(string destPath)
|
||||||
|
{
|
||||||
|
//TODO - try creating the file once first before we let vfw botch it up?
|
||||||
|
|
||||||
|
//open the avi output file handle
|
||||||
|
if (File.Exists(destPath))
|
||||||
|
File.Delete(destPath);
|
||||||
|
|
||||||
|
if (Win32.FAILED(Win32.AVIFileOpenW(ref pAviFile, destPath, Win32.OpenFileStyle.OF_CREATE | Win32.OpenFileStyle.OF_WRITE, 0)))
|
||||||
|
throw new InvalidOperationException("Couldnt open dest path for avi file: " + destPath);
|
||||||
|
|
||||||
|
//initialize the video stream
|
||||||
|
Win32.AVISTREAMINFOW vidstream_header = new Win32.AVISTREAMINFOW();
|
||||||
|
Win32.BITMAPINFOHEADER bmih = new Win32.BITMAPINFOHEADER();
|
||||||
|
parameters.PopulateBITMAPINFOHEADER(ref bmih);
|
||||||
|
vidstream_header.fccType = Win32.mmioFOURCC("vids");
|
||||||
|
vidstream_header.dwRate = parameters.fps;
|
||||||
|
vidstream_header.dwScale = parameters.fps_scale;
|
||||||
|
vidstream_header.dwSuggestedBufferSize = (int)bmih.biSizeImage;
|
||||||
|
if (Win32.FAILED(Win32.AVIFileCreateStreamW(pAviFile, out pAviRawVideoStream, ref vidstream_header)))
|
||||||
|
{
|
||||||
|
CloseFile();
|
||||||
|
throw new InvalidOperationException("Failed opening raw video stream. Not sure how this could happen");
|
||||||
|
}
|
||||||
|
|
||||||
|
//initialize audio stream
|
||||||
|
Win32.AVISTREAMINFOW audstream_header = new Win32.AVISTREAMINFOW();
|
||||||
|
Win32.WAVEFORMATEX wfex = new Win32.WAVEFORMATEX();
|
||||||
|
parameters.PopulateWAVEFORMATEX(ref wfex);
|
||||||
|
audstream_header.fccType = Win32.mmioFOURCC("auds");
|
||||||
|
audstream_header.dwQuality = -1;
|
||||||
|
audstream_header.dwScale = wfex.nBlockAlign;
|
||||||
|
audstream_header.dwRate = (int)wfex.nAvgBytesPerSec;
|
||||||
|
audstream_header.dwSampleSize = wfex.nBlockAlign;
|
||||||
|
audstream_header.dwInitialFrames = 1; // ??? optimal value?
|
||||||
|
if (Win32.FAILED(Win32.AVIFileCreateStreamW(pAviFile, out pAviRawAudioStream, ref audstream_header)))
|
||||||
|
{
|
||||||
|
CloseFile();
|
||||||
|
throw new InvalidOperationException("Failed opening raw audio stream. Not sure how this could happen");
|
||||||
|
}
|
||||||
|
|
||||||
|
outStatus = new OutputStatus();
|
||||||
|
IsOpen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Acquires a video codec configuration from the user
|
||||||
|
/// </summary>
|
||||||
|
public CodecToken AcquireVideoCodecToken(IntPtr hwnd)
|
||||||
|
{
|
||||||
|
if (!IsOpen) throw new InvalidOperationException("File must be opened before acquiring a codec token (or else the stream formats wouldnt be known)");
|
||||||
|
|
||||||
|
//encoder params
|
||||||
|
Win32.AVICOMPRESSOPTIONS comprOptions = new Win32.AVICOMPRESSOPTIONS();
|
||||||
|
if (currVideoCodecToken != null)
|
||||||
|
{
|
||||||
|
comprOptions = currVideoCodecToken.comprOptions;
|
||||||
|
}
|
||||||
|
if (AVISaveOptions(pAviRawVideoStream, ref comprOptions, hwnd) != 0)
|
||||||
|
return CodecToken.TakePossession(comprOptions);
|
||||||
|
else return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// begin recording
|
||||||
|
/// </summary>
|
||||||
|
public void OpenStreams()
|
||||||
|
{
|
||||||
|
if (currVideoCodecToken == null)
|
||||||
|
throw new InvalidOperationException("set a video codec token before opening the streams!");
|
||||||
|
|
||||||
|
//open compressed video stream
|
||||||
|
if (Win32.FAILED(Win32.AVIMakeCompressedStream(out pAviCompressedVideoStream, pAviRawVideoStream, ref currVideoCodecToken.comprOptions, IntPtr.Zero)))
|
||||||
|
{
|
||||||
|
CloseStreams();
|
||||||
|
throw new InvalidOperationException("Failed making compressed video stream");
|
||||||
|
}
|
||||||
|
|
||||||
|
//set the compressed video stream input format
|
||||||
|
Win32.BITMAPINFOHEADER bmih = new Win32.BITMAPINFOHEADER();
|
||||||
|
parameters.PopulateBITMAPINFOHEADER(ref bmih);
|
||||||
|
if (Win32.FAILED(Win32.AVIStreamSetFormat(pAviCompressedVideoStream, 0, ref bmih, Marshal.SizeOf(bmih))))
|
||||||
|
{
|
||||||
|
CloseStreams();
|
||||||
|
throw new InvalidOperationException("Failed setting compressed video stream input format");
|
||||||
|
}
|
||||||
|
|
||||||
|
//set audio stream input format
|
||||||
|
Win32.WAVEFORMATEX wfex = new Win32.WAVEFORMATEX();
|
||||||
|
parameters.PopulateWAVEFORMATEX(ref wfex);
|
||||||
|
if(Win32.FAILED(Win32.AVIStreamSetFormat(pAviRawAudioStream, 0, ref wfex, Marshal.SizeOf(wfex))))
|
||||||
|
{
|
||||||
|
CloseStreams();
|
||||||
|
throw new InvalidOperationException("Failed setting raw audio stream input format");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// wrap up the avi writing
|
||||||
|
/// </summary>
|
||||||
|
public void CloseFile()
|
||||||
|
{
|
||||||
|
CloseStreams();
|
||||||
|
if (pAviRawAudioStream != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Win32.AVIStreamRelease(pAviRawAudioStream);
|
||||||
|
pAviRawAudioStream = IntPtr.Zero;
|
||||||
|
}
|
||||||
|
if (pAviRawVideoStream != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Win32.AVIStreamRelease(pAviRawVideoStream);
|
||||||
|
pAviRawVideoStream = IntPtr.Zero;
|
||||||
|
}
|
||||||
|
if (pAviFile != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Win32.AVIFileRelease(pAviFile);
|
||||||
|
pAviFile = IntPtr.Zero;
|
||||||
|
}
|
||||||
|
if (pGlobalBuf != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Marshal.FreeHGlobal(pGlobalBuf);
|
||||||
|
pGlobalBuf = IntPtr.Zero;
|
||||||
|
pGlobalBuf_size = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// end recording
|
||||||
|
/// </summary>
|
||||||
|
public void CloseStreams()
|
||||||
|
{
|
||||||
|
FlushBufferedAudio();
|
||||||
|
if (pAviCompressedVideoStream != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Win32.AVIStreamRelease(pAviCompressedVideoStream);
|
||||||
|
pAviCompressedVideoStream = IntPtr.Zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//todo - why couldnt this take an ISoundProvider? it could do the timekeeping as well.. hmm
|
||||||
|
public unsafe void AddSamples(short[] samples)
|
||||||
|
{
|
||||||
|
int todo = samples.Length;
|
||||||
|
int idx = 0;
|
||||||
|
while (todo > 0)
|
||||||
|
{
|
||||||
|
int remain = OutputStatus.AUDIO_SEGMENT_SIZE - outStatus.audio_buffered_shorts;
|
||||||
|
int chunk = Math.Min(remain, todo);
|
||||||
|
for (int i = 0; i < chunk; i++)
|
||||||
|
outStatus.BufferedShorts[outStatus.audio_buffered_shorts++] = samples[idx++];
|
||||||
|
todo -= chunk;
|
||||||
|
|
||||||
|
if(outStatus.audio_buffered_shorts == OutputStatus.AUDIO_SEGMENT_SIZE)
|
||||||
|
FlushBufferedAudio();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe void FlushBufferedAudio()
|
||||||
|
{
|
||||||
|
int todo = outStatus.audio_buffered_shorts;
|
||||||
|
int todo_realsamples = todo / 2;
|
||||||
|
IntPtr buf = GetStaticGlobalBuf(todo*2);
|
||||||
|
|
||||||
|
short* sptr = (short*)buf.ToPointer();
|
||||||
|
for (int i = 0; i < todo; i++)
|
||||||
|
{
|
||||||
|
sptr[i] = outStatus.BufferedShorts[i];
|
||||||
|
}
|
||||||
|
//(TODO - inefficient- build directly in a buffer)
|
||||||
|
int bytes_written;
|
||||||
|
Win32.AVIStreamWrite(pAviRawAudioStream, outStatus.audio_samples, todo_realsamples, buf, todo_realsamples * 4, 0, IntPtr.Zero, out bytes_written);
|
||||||
|
outStatus.audio_samples += todo_realsamples;
|
||||||
|
outStatus.audio_buffered_shorts = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe void AddFrame(IVideoProvider source)
|
||||||
|
{
|
||||||
|
if(parameters.width != source.BufferWidth
|
||||||
|
|| parameters.height != source.BufferHeight)
|
||||||
|
throw new InvalidOperationException("video buffer changed between start and now");
|
||||||
|
|
||||||
|
int todo = source.BufferHeight * source.BufferWidth;
|
||||||
|
|
||||||
|
IntPtr buf = GetStaticGlobalBuf(todo * 3);
|
||||||
|
|
||||||
|
int[] buffer = source.GetVideoBuffer();
|
||||||
|
fixed (int* buffer_ptr = &buffer[0])
|
||||||
|
{
|
||||||
|
byte* bytes_ptr = (byte*)buf.ToPointer();
|
||||||
|
{
|
||||||
|
byte* bp = bytes_ptr;
|
||||||
|
|
||||||
|
for (int i = 0; i < todo; i++)
|
||||||
|
{
|
||||||
|
int r = (buffer[i ]>> 0) & 0xFF;
|
||||||
|
int g = (buffer[i] >> 8) & 0xFF;
|
||||||
|
int b = (buffer[i] >> 16) & 0xFF;
|
||||||
|
*bp++ = (byte)r;
|
||||||
|
*bp++ = (byte)g;
|
||||||
|
*bp++ = (byte)b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bytes_written;
|
||||||
|
int ret = Win32.AVIStreamWrite(pAviCompressedVideoStream, outStatus.video_frames, 1, new IntPtr(bytes_ptr), todo * 3, Win32.AVIIF_KEYFRAME, IntPtr.Zero, out bytes_written);
|
||||||
|
outStatus.video_bytes += bytes_written;
|
||||||
|
outStatus.video_frames++;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// sets the codec token to be used for video compression
|
||||||
|
/// </summary>
|
||||||
|
public void SetVideoCodecToken(CodecToken token)
|
||||||
|
{
|
||||||
|
currVideoCodecToken = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////TEST AVI
|
||||||
|
//AviWriter aw = new AviWriter();
|
||||||
|
//aw.SetVideoParameters(256, 256);
|
||||||
|
//aw.SetMovieParameters(60, 1);
|
||||||
|
//aw.OpenFile("d:\\bizhawk.avi");
|
||||||
|
//CreateHandle();
|
||||||
|
//var token = aw.AcquireVideoCodecToken(Handle);
|
||||||
|
//aw.SetVideoCodecToken(token);
|
||||||
|
//aw.OpenStreams();
|
||||||
|
|
||||||
|
//for (int i = 0; i < 100; i++)
|
||||||
|
//{
|
||||||
|
// TestVideoProvider video = new TestVideoProvider();
|
||||||
|
// Bitmap bmp = new Bitmap(256, 256, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
||||||
|
// using (Graphics g = Graphics.FromImage(bmp))
|
||||||
|
// {
|
||||||
|
// g.Clear(Color.Red);
|
||||||
|
// using (Font f = new Font(FontFamily.GenericMonospace, 10))
|
||||||
|
// g.DrawString(i.ToString(), f, Brushes.Black, 0, 0);
|
||||||
|
// }
|
||||||
|
// //bmp.Save(string.Format("c:\\dump\\{0}.bmp", i), ImageFormat.Bmp);
|
||||||
|
// for (int y = 0, idx = 0; y < 256; y++)
|
||||||
|
// for (int x = 0; x < 256; x++)
|
||||||
|
// video.buffer[idx++] = bmp.GetPixel(x, y).ToArgb();
|
||||||
|
// aw.AddFrame(video);
|
||||||
|
//}
|
||||||
|
//aw.CloseStreams();
|
||||||
|
//aw.CloseFile();
|
||||||
|
////-----
|
|
@ -97,6 +97,7 @@
|
||||||
<Compile Include="ArchiveChooser.Designer.cs">
|
<Compile Include="ArchiveChooser.Designer.cs">
|
||||||
<DependentUpon>ArchiveChooser.cs</DependentUpon>
|
<DependentUpon>ArchiveChooser.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="AviWriter.cs" />
|
||||||
<Compile Include="Config.cs" />
|
<Compile Include="Config.cs" />
|
||||||
<Compile Include="ConfigService.cs" />
|
<Compile Include="ConfigService.cs" />
|
||||||
<Compile Include="config\InputConfig.cs">
|
<Compile Include="config\InputConfig.cs">
|
||||||
|
|
|
@ -98,6 +98,9 @@
|
||||||
this.toolStripSeparator14 = new System.Windows.Forms.ToolStripSeparator();
|
this.toolStripSeparator14 = new System.Windows.Forms.ToolStripSeparator();
|
||||||
this.bindSavestatesToMoviesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.bindSavestatesToMoviesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.automaticallyBackupMoviesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.automaticallyBackupMoviesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
|
this.aVIWAVToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
|
this.recordAVIToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
|
this.stopAVIToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.screenshotToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.screenshotToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.screenshotF12ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.screenshotF12ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
this.screenshotAsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.screenshotAsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||||
|
@ -255,7 +258,7 @@
|
||||||
this.menuStrip1.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.Flow;
|
this.menuStrip1.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.Flow;
|
||||||
this.menuStrip1.Location = new System.Drawing.Point(0, 0);
|
this.menuStrip1.Location = new System.Drawing.Point(0, 0);
|
||||||
this.menuStrip1.Name = "menuStrip1";
|
this.menuStrip1.Name = "menuStrip1";
|
||||||
this.menuStrip1.Size = new System.Drawing.Size(470, 21);
|
this.menuStrip1.Size = new System.Drawing.Size(470, 40);
|
||||||
this.menuStrip1.TabIndex = 0;
|
this.menuStrip1.TabIndex = 0;
|
||||||
this.menuStrip1.Text = "menuStrip1";
|
this.menuStrip1.Text = "menuStrip1";
|
||||||
this.menuStrip1.MenuDeactivate += new System.EventHandler(this.menuStrip1_MenuDeactivate);
|
this.menuStrip1.MenuDeactivate += new System.EventHandler(this.menuStrip1_MenuDeactivate);
|
||||||
|
@ -273,6 +276,7 @@
|
||||||
this.saveSlotToolStripMenuItem,
|
this.saveSlotToolStripMenuItem,
|
||||||
this.toolStripMenuItem2,
|
this.toolStripMenuItem2,
|
||||||
this.movieToolStripMenuItem,
|
this.movieToolStripMenuItem,
|
||||||
|
this.aVIWAVToolStripMenuItem,
|
||||||
this.screenshotToolStripMenuItem,
|
this.screenshotToolStripMenuItem,
|
||||||
this.toolStripSeparator4,
|
this.toolStripSeparator4,
|
||||||
this.exitToolStripMenuItem});
|
this.exitToolStripMenuItem});
|
||||||
|
@ -285,7 +289,7 @@
|
||||||
//
|
//
|
||||||
this.openROMToolStripMenuItem.Image = global::BizHawk.MultiClient.Properties.Resources.OpenFile;
|
this.openROMToolStripMenuItem.Image = global::BizHawk.MultiClient.Properties.Resources.OpenFile;
|
||||||
this.openROMToolStripMenuItem.Name = "openROMToolStripMenuItem";
|
this.openROMToolStripMenuItem.Name = "openROMToolStripMenuItem";
|
||||||
this.openROMToolStripMenuItem.Size = new System.Drawing.Size(145, 22);
|
this.openROMToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
|
||||||
this.openROMToolStripMenuItem.Text = "Open ROM";
|
this.openROMToolStripMenuItem.Text = "Open ROM";
|
||||||
this.openROMToolStripMenuItem.Click += new System.EventHandler(this.openROMToolStripMenuItem_Click);
|
this.openROMToolStripMenuItem.Click += new System.EventHandler(this.openROMToolStripMenuItem_Click);
|
||||||
//
|
//
|
||||||
|
@ -298,7 +302,7 @@
|
||||||
this.autoloadMostRecentToolStripMenuItem});
|
this.autoloadMostRecentToolStripMenuItem});
|
||||||
this.recentROMToolStripMenuItem.Image = global::BizHawk.MultiClient.Properties.Resources.Recent;
|
this.recentROMToolStripMenuItem.Image = global::BizHawk.MultiClient.Properties.Resources.Recent;
|
||||||
this.recentROMToolStripMenuItem.Name = "recentROMToolStripMenuItem";
|
this.recentROMToolStripMenuItem.Name = "recentROMToolStripMenuItem";
|
||||||
this.recentROMToolStripMenuItem.Size = new System.Drawing.Size(145, 22);
|
this.recentROMToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
|
||||||
this.recentROMToolStripMenuItem.Text = "Recent ROM";
|
this.recentROMToolStripMenuItem.Text = "Recent ROM";
|
||||||
this.recentROMToolStripMenuItem.DropDownOpened += new System.EventHandler(this.recentROMToolStripMenuItem_DropDownOpened);
|
this.recentROMToolStripMenuItem.DropDownOpened += new System.EventHandler(this.recentROMToolStripMenuItem_DropDownOpened);
|
||||||
//
|
//
|
||||||
|
@ -331,14 +335,14 @@
|
||||||
//
|
//
|
||||||
this.closeROMToolStripMenuItem.Image = global::BizHawk.MultiClient.Properties.Resources.Close;
|
this.closeROMToolStripMenuItem.Image = global::BizHawk.MultiClient.Properties.Resources.Close;
|
||||||
this.closeROMToolStripMenuItem.Name = "closeROMToolStripMenuItem";
|
this.closeROMToolStripMenuItem.Name = "closeROMToolStripMenuItem";
|
||||||
this.closeROMToolStripMenuItem.Size = new System.Drawing.Size(145, 22);
|
this.closeROMToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
|
||||||
this.closeROMToolStripMenuItem.Text = "&Close ROM";
|
this.closeROMToolStripMenuItem.Text = "&Close ROM";
|
||||||
this.closeROMToolStripMenuItem.Click += new System.EventHandler(this.closeROMToolStripMenuItem_Click);
|
this.closeROMToolStripMenuItem.Click += new System.EventHandler(this.closeROMToolStripMenuItem_Click);
|
||||||
//
|
//
|
||||||
// toolStripMenuItem1
|
// toolStripMenuItem1
|
||||||
//
|
//
|
||||||
this.toolStripMenuItem1.Name = "toolStripMenuItem1";
|
this.toolStripMenuItem1.Name = "toolStripMenuItem1";
|
||||||
this.toolStripMenuItem1.Size = new System.Drawing.Size(142, 6);
|
this.toolStripMenuItem1.Size = new System.Drawing.Size(149, 6);
|
||||||
//
|
//
|
||||||
// saveStateToolStripMenuItem
|
// saveStateToolStripMenuItem
|
||||||
//
|
//
|
||||||
|
@ -356,7 +360,7 @@
|
||||||
this.toolStripSeparator6,
|
this.toolStripSeparator6,
|
||||||
this.saveNamedStateToolStripMenuItem});
|
this.saveNamedStateToolStripMenuItem});
|
||||||
this.saveStateToolStripMenuItem.Name = "saveStateToolStripMenuItem";
|
this.saveStateToolStripMenuItem.Name = "saveStateToolStripMenuItem";
|
||||||
this.saveStateToolStripMenuItem.Size = new System.Drawing.Size(145, 22);
|
this.saveStateToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
|
||||||
this.saveStateToolStripMenuItem.Text = "Save State";
|
this.saveStateToolStripMenuItem.Text = "Save State";
|
||||||
this.saveStateToolStripMenuItem.DropDownOpened += new System.EventHandler(this.saveStateToolStripMenuItem_DropDownOpened);
|
this.saveStateToolStripMenuItem.DropDownOpened += new System.EventHandler(this.saveStateToolStripMenuItem_DropDownOpened);
|
||||||
//
|
//
|
||||||
|
@ -458,7 +462,7 @@
|
||||||
this.toolStripSeparator7,
|
this.toolStripSeparator7,
|
||||||
this.loadNamedStateToolStripMenuItem});
|
this.loadNamedStateToolStripMenuItem});
|
||||||
this.loadStateToolStripMenuItem.Name = "loadStateToolStripMenuItem";
|
this.loadStateToolStripMenuItem.Name = "loadStateToolStripMenuItem";
|
||||||
this.loadStateToolStripMenuItem.Size = new System.Drawing.Size(145, 22);
|
this.loadStateToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
|
||||||
this.loadStateToolStripMenuItem.Text = "Load State";
|
this.loadStateToolStripMenuItem.Text = "Load State";
|
||||||
this.loadStateToolStripMenuItem.DropDownOpened += new System.EventHandler(this.loadStateToolStripMenuItem_DropDownOpened);
|
this.loadStateToolStripMenuItem.DropDownOpened += new System.EventHandler(this.loadStateToolStripMenuItem_DropDownOpened);
|
||||||
//
|
//
|
||||||
|
@ -563,7 +567,7 @@
|
||||||
this.saveToCurrentSlotToolStripMenuItem,
|
this.saveToCurrentSlotToolStripMenuItem,
|
||||||
this.loadCurrentSlotToolStripMenuItem});
|
this.loadCurrentSlotToolStripMenuItem});
|
||||||
this.saveSlotToolStripMenuItem.Name = "saveSlotToolStripMenuItem";
|
this.saveSlotToolStripMenuItem.Name = "saveSlotToolStripMenuItem";
|
||||||
this.saveSlotToolStripMenuItem.Size = new System.Drawing.Size(145, 22);
|
this.saveSlotToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
|
||||||
this.saveSlotToolStripMenuItem.Text = "SaveSlot";
|
this.saveSlotToolStripMenuItem.Text = "SaveSlot";
|
||||||
this.saveSlotToolStripMenuItem.DropDownOpened += new System.EventHandler(this.saveSlotToolStripMenuItem_DropDownOpened);
|
this.saveSlotToolStripMenuItem.DropDownOpened += new System.EventHandler(this.saveSlotToolStripMenuItem_DropDownOpened);
|
||||||
//
|
//
|
||||||
|
@ -673,7 +677,7 @@
|
||||||
// toolStripMenuItem2
|
// toolStripMenuItem2
|
||||||
//
|
//
|
||||||
this.toolStripMenuItem2.Name = "toolStripMenuItem2";
|
this.toolStripMenuItem2.Name = "toolStripMenuItem2";
|
||||||
this.toolStripMenuItem2.Size = new System.Drawing.Size(142, 6);
|
this.toolStripMenuItem2.Size = new System.Drawing.Size(149, 6);
|
||||||
//
|
//
|
||||||
// movieToolStripMenuItem
|
// movieToolStripMenuItem
|
||||||
//
|
//
|
||||||
|
@ -689,7 +693,7 @@
|
||||||
this.bindSavestatesToMoviesToolStripMenuItem,
|
this.bindSavestatesToMoviesToolStripMenuItem,
|
||||||
this.automaticallyBackupMoviesToolStripMenuItem});
|
this.automaticallyBackupMoviesToolStripMenuItem});
|
||||||
this.movieToolStripMenuItem.Name = "movieToolStripMenuItem";
|
this.movieToolStripMenuItem.Name = "movieToolStripMenuItem";
|
||||||
this.movieToolStripMenuItem.Size = new System.Drawing.Size(145, 22);
|
this.movieToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
|
||||||
this.movieToolStripMenuItem.Text = "Movie";
|
this.movieToolStripMenuItem.Text = "Movie";
|
||||||
this.movieToolStripMenuItem.DropDownOpened += new System.EventHandler(this.movieToolStripMenuItem_DropDownOpened);
|
this.movieToolStripMenuItem.DropDownOpened += new System.EventHandler(this.movieToolStripMenuItem_DropDownOpened);
|
||||||
//
|
//
|
||||||
|
@ -793,13 +797,36 @@
|
||||||
this.automaticallyBackupMoviesToolStripMenuItem.Text = "Automatically Backup Movies";
|
this.automaticallyBackupMoviesToolStripMenuItem.Text = "Automatically Backup Movies";
|
||||||
this.automaticallyBackupMoviesToolStripMenuItem.Click += new System.EventHandler(this.automaticallyBackupMoviesToolStripMenuItem_Click);
|
this.automaticallyBackupMoviesToolStripMenuItem.Click += new System.EventHandler(this.automaticallyBackupMoviesToolStripMenuItem_Click);
|
||||||
//
|
//
|
||||||
|
// aVIWAVToolStripMenuItem
|
||||||
|
//
|
||||||
|
this.aVIWAVToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
|
this.recordAVIToolStripMenuItem,
|
||||||
|
this.stopAVIToolStripMenuItem});
|
||||||
|
this.aVIWAVToolStripMenuItem.Name = "aVIWAVToolStripMenuItem";
|
||||||
|
this.aVIWAVToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
|
||||||
|
this.aVIWAVToolStripMenuItem.Text = "AVI/WAV";
|
||||||
|
//
|
||||||
|
// recordAVIToolStripMenuItem
|
||||||
|
//
|
||||||
|
this.recordAVIToolStripMenuItem.Name = "recordAVIToolStripMenuItem";
|
||||||
|
this.recordAVIToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
|
||||||
|
this.recordAVIToolStripMenuItem.Text = "Record AVI";
|
||||||
|
this.recordAVIToolStripMenuItem.Click += new System.EventHandler(this.recordAVIToolStripMenuItem_Click);
|
||||||
|
//
|
||||||
|
// stopAVIToolStripMenuItem
|
||||||
|
//
|
||||||
|
this.stopAVIToolStripMenuItem.Name = "stopAVIToolStripMenuItem";
|
||||||
|
this.stopAVIToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
|
||||||
|
this.stopAVIToolStripMenuItem.Text = "Stop AVI";
|
||||||
|
this.stopAVIToolStripMenuItem.Click += new System.EventHandler(this.stopAVIToolStripMenuItem_Click);
|
||||||
|
//
|
||||||
// screenshotToolStripMenuItem
|
// screenshotToolStripMenuItem
|
||||||
//
|
//
|
||||||
this.screenshotToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
this.screenshotToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
this.screenshotF12ToolStripMenuItem,
|
this.screenshotF12ToolStripMenuItem,
|
||||||
this.screenshotAsToolStripMenuItem});
|
this.screenshotAsToolStripMenuItem});
|
||||||
this.screenshotToolStripMenuItem.Name = "screenshotToolStripMenuItem";
|
this.screenshotToolStripMenuItem.Name = "screenshotToolStripMenuItem";
|
||||||
this.screenshotToolStripMenuItem.Size = new System.Drawing.Size(145, 22);
|
this.screenshotToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
|
||||||
this.screenshotToolStripMenuItem.Text = "Screenshot";
|
this.screenshotToolStripMenuItem.Text = "Screenshot";
|
||||||
//
|
//
|
||||||
// screenshotF12ToolStripMenuItem
|
// screenshotF12ToolStripMenuItem
|
||||||
|
@ -820,13 +847,13 @@
|
||||||
// toolStripSeparator4
|
// toolStripSeparator4
|
||||||
//
|
//
|
||||||
this.toolStripSeparator4.Name = "toolStripSeparator4";
|
this.toolStripSeparator4.Name = "toolStripSeparator4";
|
||||||
this.toolStripSeparator4.Size = new System.Drawing.Size(142, 6);
|
this.toolStripSeparator4.Size = new System.Drawing.Size(149, 6);
|
||||||
//
|
//
|
||||||
// exitToolStripMenuItem
|
// exitToolStripMenuItem
|
||||||
//
|
//
|
||||||
this.exitToolStripMenuItem.Name = "exitToolStripMenuItem";
|
this.exitToolStripMenuItem.Name = "exitToolStripMenuItem";
|
||||||
this.exitToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Alt | System.Windows.Forms.Keys.F4)));
|
this.exitToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Alt | System.Windows.Forms.Keys.F4)));
|
||||||
this.exitToolStripMenuItem.Size = new System.Drawing.Size(145, 22);
|
this.exitToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
|
||||||
this.exitToolStripMenuItem.Text = "Exit";
|
this.exitToolStripMenuItem.Text = "Exit";
|
||||||
this.exitToolStripMenuItem.Click += new System.EventHandler(this.exitToolStripMenuItem_Click);
|
this.exitToolStripMenuItem.Click += new System.EventHandler(this.exitToolStripMenuItem_Click);
|
||||||
//
|
//
|
||||||
|
@ -2125,6 +2152,9 @@
|
||||||
private System.Windows.Forms.ToolStripMenuItem viewCommentsToolStripMenuItem;
|
private System.Windows.Forms.ToolStripMenuItem viewCommentsToolStripMenuItem;
|
||||||
private System.Windows.Forms.ToolStripMenuItem displayLogWindowToolStripMenuItem;
|
private System.Windows.Forms.ToolStripMenuItem displayLogWindowToolStripMenuItem;
|
||||||
private System.Windows.Forms.ToolStripMenuItem displaySubtitlesToolStripMenuItem;
|
private System.Windows.Forms.ToolStripMenuItem displaySubtitlesToolStripMenuItem;
|
||||||
|
private System.Windows.Forms.ToolStripMenuItem aVIWAVToolStripMenuItem;
|
||||||
|
private System.Windows.Forms.ToolStripMenuItem recordAVIToolStripMenuItem;
|
||||||
|
private System.Windows.Forms.ToolStripMenuItem stopAVIToolStripMenuItem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,35 @@ namespace BizHawk.MultiClient
|
||||||
{
|
{
|
||||||
partial class MainForm
|
partial class MainForm
|
||||||
{
|
{
|
||||||
|
private void recordAVIToolStripMenuItem_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
var sfd = new SaveFileDialog();
|
||||||
|
//TODO adelikat (dunno how to do the paths correctly)
|
||||||
|
sfd.FileName = Path.Combine(Global.Config.AVIPath, "game.avi");
|
||||||
|
if (sfd.ShowDialog() == DialogResult.Cancel)
|
||||||
|
return;
|
||||||
|
|
||||||
|
//TODO - cores should be able to specify exact values for these instead of relying on this to calculate them
|
||||||
|
int fps = (int)(Global.Emulator.CoreOutputComm.VsyncRate * 0x01000000);
|
||||||
|
AviWriter aw = new AviWriter();
|
||||||
|
aw.SetMovieParameters(fps, 0x01000000);
|
||||||
|
aw.SetVideoParameters(Global.Emulator.VideoProvider.BufferWidth, Global.Emulator.VideoProvider.BufferHeight);
|
||||||
|
aw.SetAudioParameters(44100, 2, 16);
|
||||||
|
aw.OpenFile(sfd.FileName);
|
||||||
|
var token = aw.AcquireVideoCodecToken(Global.MainForm.Handle);
|
||||||
|
aw.SetVideoCodecToken(token);
|
||||||
|
aw.OpenStreams();
|
||||||
|
|
||||||
|
//commit the avi writing last, in case there were any errors earlier
|
||||||
|
CurrAviWriter = aw;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopAVIToolStripMenuItem_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
CurrAviWriter.CloseFile();
|
||||||
|
CurrAviWriter = null;
|
||||||
|
}
|
||||||
|
|
||||||
private void DumpStatus_Click(object sender, EventArgs e)
|
private void DumpStatus_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
string details = Global.Emulator.CoreOutputComm.RomStatusDetails;
|
string details = Global.Emulator.CoreOutputComm.RomStatusDetails;
|
||||||
|
|
|
@ -14,6 +14,7 @@ using BizHawk.Emulation.Consoles.Nintendo;
|
||||||
|
|
||||||
namespace BizHawk.MultiClient
|
namespace BizHawk.MultiClient
|
||||||
{
|
{
|
||||||
|
|
||||||
public partial class MainForm : Form
|
public partial class MainForm : Form
|
||||||
{
|
{
|
||||||
public const string EMUVERSION = "BizHawk v1.0.0";
|
public const string EMUVERSION = "BizHawk v1.0.0";
|
||||||
|
@ -31,6 +32,9 @@ namespace BizHawk.MultiClient
|
||||||
|
|
||||||
public bool ReadOnly = true; //Global Movie Read only setting
|
public bool ReadOnly = true; //Global Movie Read only setting
|
||||||
|
|
||||||
|
//avi/wav state
|
||||||
|
AviWriter CurrAviWriter = null;
|
||||||
|
|
||||||
//the currently selected savestate slot
|
//the currently selected savestate slot
|
||||||
private int SaveSlot = 0;
|
private int SaveSlot = 0;
|
||||||
|
|
||||||
|
@ -74,6 +78,7 @@ namespace BizHawk.MultiClient
|
||||||
displayLogWindowToolStripMenuItem.Checked = true;
|
displayLogWindowToolStripMenuItem.Checked = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
UpdateStatusSlots();
|
UpdateStatusSlots();
|
||||||
//in order to allow late construction of this database, we hook up a delegate here to dearchive the data and provide it on demand
|
//in order to allow late construction of this database, we hook up a delegate here to dearchive the data and provide it on demand
|
||||||
//we could background thread this later instead if we wanted to be real clever
|
//we could background thread this later instead if we wanted to be real clever
|
||||||
|
@ -330,6 +335,17 @@ namespace BizHawk.MultiClient
|
||||||
break;
|
break;
|
||||||
Thread.Sleep(0);
|
Thread.Sleep(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shutdown()
|
||||||
|
{
|
||||||
|
if (CurrAviWriter != null)
|
||||||
|
{
|
||||||
|
CurrAviWriter.CloseFile();
|
||||||
|
CurrAviWriter = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckMessages()
|
void CheckMessages()
|
||||||
|
@ -1423,6 +1439,18 @@ namespace BizHawk.MultiClient
|
||||||
Global.Emulator.FrameAdvance(!throttle.skipnextframe);
|
Global.Emulator.FrameAdvance(!throttle.skipnextframe);
|
||||||
//=======================================
|
//=======================================
|
||||||
|
|
||||||
|
if (CurrAviWriter != null)
|
||||||
|
{
|
||||||
|
//TODO - this will stray over time! have AviWriter keep an accumulation!
|
||||||
|
int samples = (int)(44100 / Global.Emulator.CoreOutputComm.VsyncRate);
|
||||||
|
short[] temp = new short[samples*2];
|
||||||
|
Global.Emulator.SoundProvider.GetSamples(temp);
|
||||||
|
genSound = false;
|
||||||
|
|
||||||
|
CurrAviWriter.AddFrame(Global.Emulator.VideoProvider);
|
||||||
|
CurrAviWriter.AddSamples(temp);
|
||||||
|
}
|
||||||
|
|
||||||
UpdateTools();
|
UpdateTools();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2534,5 +2562,7 @@ namespace BizHawk.MultiClient
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -109,6 +109,7 @@
|
||||||
<Compile Include="VirtualListView.cs">
|
<Compile Include="VirtualListView.cs">
|
||||||
<SubType>Component</SubType>
|
<SubType>Component</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Win32.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="7z\arch\Test.bzip2.7z" />
|
<EmbeddedResource Include="7z\arch\Test.bzip2.7z" />
|
||||||
|
|
|
@ -0,0 +1,380 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace BizHawk
|
||||||
|
{
|
||||||
|
public static class Win32
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public struct RECT
|
||||||
|
{
|
||||||
|
private int _Left;
|
||||||
|
private int _Top;
|
||||||
|
private int _Right;
|
||||||
|
private int _Bottom;
|
||||||
|
|
||||||
|
public RECT(RECT Rectangle)
|
||||||
|
: this(Rectangle.Left, Rectangle.Top, Rectangle.Right, Rectangle.Bottom)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public RECT(int Left, int Top, int Right, int Bottom)
|
||||||
|
{
|
||||||
|
_Left = Left;
|
||||||
|
_Top = Top;
|
||||||
|
_Right = Right;
|
||||||
|
_Bottom = Bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int X
|
||||||
|
{
|
||||||
|
get { return _Left; }
|
||||||
|
set { _Left = value; }
|
||||||
|
}
|
||||||
|
public int Y
|
||||||
|
{
|
||||||
|
get { return _Top; }
|
||||||
|
set { _Top = value; }
|
||||||
|
}
|
||||||
|
public int Left
|
||||||
|
{
|
||||||
|
get { return _Left; }
|
||||||
|
set { _Left = value; }
|
||||||
|
}
|
||||||
|
public int Top
|
||||||
|
{
|
||||||
|
get { return _Top; }
|
||||||
|
set { _Top = value; }
|
||||||
|
}
|
||||||
|
public int Right
|
||||||
|
{
|
||||||
|
get { return _Right; }
|
||||||
|
set { _Right = value; }
|
||||||
|
}
|
||||||
|
public int Bottom
|
||||||
|
{
|
||||||
|
get { return _Bottom; }
|
||||||
|
set { _Bottom = value; }
|
||||||
|
}
|
||||||
|
public int Height
|
||||||
|
{
|
||||||
|
get { return _Bottom - _Top; }
|
||||||
|
set { _Bottom = value - _Top; }
|
||||||
|
}
|
||||||
|
public int Width
|
||||||
|
{
|
||||||
|
get { return _Right - _Left; }
|
||||||
|
set { _Right = value + _Left; }
|
||||||
|
}
|
||||||
|
public Point Location
|
||||||
|
{
|
||||||
|
get { return new Point(Left, Top); }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_Left = value.X;
|
||||||
|
_Top = value.Y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public Size Size
|
||||||
|
{
|
||||||
|
get { return new Size(Width, Height); }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_Right = value.Width + _Left;
|
||||||
|
_Bottom = value.Height + _Top;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator Rectangle(RECT Rectangle)
|
||||||
|
{
|
||||||
|
return new Rectangle(Rectangle.Left, Rectangle.Top, Rectangle.Width, Rectangle.Height);
|
||||||
|
}
|
||||||
|
public static implicit operator RECT(Rectangle Rectangle)
|
||||||
|
{
|
||||||
|
return new RECT(Rectangle.Left, Rectangle.Top, Rectangle.Right, Rectangle.Bottom);
|
||||||
|
}
|
||||||
|
public static bool operator ==(RECT Rectangle1, RECT Rectangle2)
|
||||||
|
{
|
||||||
|
return Rectangle1.Equals(Rectangle2);
|
||||||
|
}
|
||||||
|
public static bool operator !=(RECT Rectangle1, RECT Rectangle2)
|
||||||
|
{
|
||||||
|
return !Rectangle1.Equals(Rectangle2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return "{Left: " + _Left + "; " + "Top: " + _Top + "; Right: " + _Right + "; Bottom: " + _Bottom + "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return ToString().GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(RECT Rectangle)
|
||||||
|
{
|
||||||
|
return Rectangle.Left == _Left && Rectangle.Top == _Top && Rectangle.Right == _Right && Rectangle.Bottom == _Bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object Object)
|
||||||
|
{
|
||||||
|
if (Object is RECT)
|
||||||
|
{
|
||||||
|
return Equals((RECT)Object);
|
||||||
|
}
|
||||||
|
else if (Object is Rectangle)
|
||||||
|
{
|
||||||
|
return Equals(new RECT((Rectangle)Object));
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public struct AVISTREAMINFOW
|
||||||
|
{
|
||||||
|
public Int32 fccType;
|
||||||
|
public Int32 fccHandler;
|
||||||
|
public Int32 dwFlags;
|
||||||
|
public Int32 dwCaps;
|
||||||
|
public Int16 wPriority;
|
||||||
|
public Int16 wLanguage;
|
||||||
|
public Int32 dwScale;
|
||||||
|
public Int32 dwRate;
|
||||||
|
public Int32 dwStart;
|
||||||
|
public Int32 dwLength;
|
||||||
|
public Int32 dwInitialFrames;
|
||||||
|
public Int32 dwSuggestedBufferSize;
|
||||||
|
public Int32 dwQuality;
|
||||||
|
public Int32 dwSampleSize;
|
||||||
|
public RECT rcFrame;
|
||||||
|
public Int32 dwEditCount;
|
||||||
|
public Int32 dwFormatChangeCount;
|
||||||
|
[MarshalAs(UnmanagedType.LPWStr, SizeConst=64)]
|
||||||
|
public string szName;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public struct BITMAPINFOHEADER
|
||||||
|
{
|
||||||
|
public uint biSize;
|
||||||
|
public int biWidth;
|
||||||
|
public int biHeight;
|
||||||
|
public ushort biPlanes;
|
||||||
|
public ushort biBitCount;
|
||||||
|
public uint biCompression;
|
||||||
|
public uint biSizeImage;
|
||||||
|
public int biXPelsPerMeter;
|
||||||
|
public int biYPelsPerMeter;
|
||||||
|
public uint biClrUsed;
|
||||||
|
public uint biClrImportant;
|
||||||
|
|
||||||
|
public void Init()
|
||||||
|
{
|
||||||
|
biSize = (uint)Marshal.SizeOf(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public struct WAVEFORMATEX
|
||||||
|
{
|
||||||
|
public ushort wFormatTag;
|
||||||
|
public ushort nChannels;
|
||||||
|
public uint nSamplesPerSec;
|
||||||
|
public uint nAvgBytesPerSec;
|
||||||
|
public ushort nBlockAlign;
|
||||||
|
public ushort wBitsPerSample;
|
||||||
|
public ushort cbSize;
|
||||||
|
|
||||||
|
public void Init()
|
||||||
|
{
|
||||||
|
cbSize = (ushort)Marshal.SizeOf(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public const int WAVE_FORMAT_PCM = 1;
|
||||||
|
public const int AVIIF_KEYFRAME = 0x00000010;
|
||||||
|
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum OpenFileStyle : uint
|
||||||
|
{
|
||||||
|
OF_CANCEL = 0x00000800, // Ignored. For a dialog box with a Cancel button, use OF_PROMPT.
|
||||||
|
OF_CREATE = 0x00001000, // Creates a new file. If file exists, it is truncated to zero (0) length.
|
||||||
|
OF_DELETE = 0x00000200, // Deletes a file.
|
||||||
|
OF_EXIST = 0x00004000, // Opens a file and then closes it. Used to test that a file exists
|
||||||
|
OF_PARSE = 0x00000100, // Fills the OFSTRUCT structure, but does not do anything else.
|
||||||
|
OF_PROMPT = 0x00002000, // Displays a dialog box if a requested file does not exist
|
||||||
|
OF_READ = 0x00000000, // Opens a file for reading only.
|
||||||
|
OF_READWRITE = 0x00000002, // Opens a file with read/write permissions.
|
||||||
|
OF_REOPEN = 0x00008000, // Opens a file by using information in the reopen buffer.
|
||||||
|
|
||||||
|
// For MS-DOS–based file systems, opens a file with compatibility mode, allows any process on a
|
||||||
|
// specified computer to open the file any number of times.
|
||||||
|
// Other efforts to open a file with other sharing modes fail. This flag is mapped to the
|
||||||
|
// FILE_SHARE_READ|FILE_SHARE_WRITE flags of the CreateFile function.
|
||||||
|
OF_SHARE_COMPAT = 0x00000000,
|
||||||
|
|
||||||
|
// Opens a file without denying read or write access to other processes.
|
||||||
|
// On MS-DOS-based file systems, if the file has been opened in compatibility mode
|
||||||
|
// by any other process, the function fails.
|
||||||
|
// This flag is mapped to the FILE_SHARE_READ|FILE_SHARE_WRITE flags of the CreateFile function.
|
||||||
|
OF_SHARE_DENY_NONE = 0x00000040,
|
||||||
|
|
||||||
|
// Opens a file and denies read access to other processes.
|
||||||
|
// On MS-DOS-based file systems, if the file has been opened in compatibility mode,
|
||||||
|
// or for read access by any other process, the function fails.
|
||||||
|
// This flag is mapped to the FILE_SHARE_WRITE flag of the CreateFile function.
|
||||||
|
OF_SHARE_DENY_READ = 0x00000030,
|
||||||
|
|
||||||
|
// Opens a file and denies write access to other processes.
|
||||||
|
// On MS-DOS-based file systems, if a file has been opened in compatibility mode,
|
||||||
|
// or for write access by any other process, the function fails.
|
||||||
|
// This flag is mapped to the FILE_SHARE_READ flag of the CreateFile function.
|
||||||
|
OF_SHARE_DENY_WRITE = 0x00000020,
|
||||||
|
|
||||||
|
// Opens a file with exclusive mode, and denies both read/write access to other processes.
|
||||||
|
// If a file has been opened in any other mode for read/write access, even by the current process,
|
||||||
|
// the function fails.
|
||||||
|
OF_SHARE_EXCLUSIVE = 0x00000010,
|
||||||
|
|
||||||
|
// Verifies that the date and time of a file are the same as when it was opened previously.
|
||||||
|
// This is useful as an extra check for read-only files.
|
||||||
|
OF_VERIFY = 0x00000400,
|
||||||
|
|
||||||
|
// Opens a file for write access only.
|
||||||
|
OF_WRITE = 0x00000001
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("avifil32.dll", SetLastError = true)]
|
||||||
|
public static extern int AVIFileOpenW(ref IntPtr pAviFile, [MarshalAs(UnmanagedType.LPWStr)] string szFile, OpenFileStyle uMode, int lpHandler);
|
||||||
|
|
||||||
|
[DllImport("avifil32.dll", SetLastError = true)]
|
||||||
|
public static extern void AVIFileInit();
|
||||||
|
|
||||||
|
// Create a new stream in an existing file and creates an interface to the new stream
|
||||||
|
[DllImport("avifil32.dll")]
|
||||||
|
public static extern int AVIFileCreateStreamW(
|
||||||
|
IntPtr pfile,
|
||||||
|
out IntPtr ppavi,
|
||||||
|
ref AVISTREAMINFOW psi);
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public struct AVICOMPRESSOPTIONS
|
||||||
|
{
|
||||||
|
public int fccType;
|
||||||
|
public int fccHandler;
|
||||||
|
public int dwKeyFrameEvery;
|
||||||
|
public int dwQuality;
|
||||||
|
public int dwBytesPerSecond;
|
||||||
|
public int dwFlags;
|
||||||
|
public int lpFormat;
|
||||||
|
public int cbFormat;
|
||||||
|
public int lpParms;
|
||||||
|
public int cbParms;
|
||||||
|
public int dwInterleaveEvery;
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("user32.dll", SetLastError = false)]
|
||||||
|
public static extern IntPtr GetDesktopWindow();
|
||||||
|
|
||||||
|
// Retrieve the save options for a file and returns them in a buffer
|
||||||
|
[DllImport("avifil32.dll")]
|
||||||
|
public static extern int AVISaveOptions(
|
||||||
|
IntPtr hwnd,
|
||||||
|
int flags,
|
||||||
|
int streams,
|
||||||
|
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] IntPtr[] ppavi,
|
||||||
|
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] IntPtr[] plpOptions);
|
||||||
|
|
||||||
|
// Free the resources allocated by the AVISaveOptions function
|
||||||
|
[DllImport("avifil32.dll")]
|
||||||
|
public static extern int AVISaveOptionsFree(
|
||||||
|
int streams,
|
||||||
|
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] IntPtr[] plpOptions);
|
||||||
|
|
||||||
|
// Create a compressed stream from an uncompressed stream and a
|
||||||
|
// compression filter, and returns the address of a pointer to
|
||||||
|
// the compressed stream
|
||||||
|
[DllImport("avifil32.dll")]
|
||||||
|
public static extern int AVIMakeCompressedStream(
|
||||||
|
out IntPtr ppsCompressed,
|
||||||
|
IntPtr psSource,
|
||||||
|
ref AVICOMPRESSOPTIONS lpOptions,
|
||||||
|
IntPtr pclsidHandler);
|
||||||
|
|
||||||
|
// Set the format of a stream at the specified position
|
||||||
|
[DllImport("avifil32.dll")]
|
||||||
|
public static extern int AVIStreamSetFormat(
|
||||||
|
IntPtr pavi,
|
||||||
|
int lPos,
|
||||||
|
ref BITMAPINFOHEADER lpFormat,
|
||||||
|
int cbFormat);
|
||||||
|
|
||||||
|
// Set the format of a stream at the specified position
|
||||||
|
[DllImport("avifil32.dll")]
|
||||||
|
public static extern int AVIStreamSetFormat(
|
||||||
|
IntPtr pavi,
|
||||||
|
int lPos,
|
||||||
|
ref WAVEFORMATEX lpFormat,
|
||||||
|
int cbFormat);
|
||||||
|
|
||||||
|
// Write data to a stream
|
||||||
|
[DllImport("avifil32.dll")]
|
||||||
|
public static extern int AVIStreamWrite(
|
||||||
|
IntPtr pavi,
|
||||||
|
int lStart,
|
||||||
|
int lSamples,
|
||||||
|
IntPtr lpBuffer,
|
||||||
|
int cbBuffer,
|
||||||
|
int dwFlags,
|
||||||
|
IntPtr plSampWritten,
|
||||||
|
out int plBytesWritten);
|
||||||
|
|
||||||
|
// Release an open AVI stream
|
||||||
|
[DllImport("avifil32.dll")]
|
||||||
|
public static extern int AVIStreamRelease(
|
||||||
|
IntPtr pavi);
|
||||||
|
|
||||||
|
// Release an open AVI stream
|
||||||
|
[DllImport("avifil32.dll")]
|
||||||
|
public static extern int AVIFileRelease(
|
||||||
|
IntPtr pfile);
|
||||||
|
|
||||||
|
|
||||||
|
// Replacement of mmioFOURCC macros
|
||||||
|
public static int mmioFOURCC(string str)
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
((int)(byte)(str[0])) |
|
||||||
|
((int)(byte)(str[1]) << 8) |
|
||||||
|
((int)(byte)(str[2]) << 16) |
|
||||||
|
((int)(byte)(str[3]) << 24));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool FAILED(int hr) { return hr < 0; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Inverse of mmioFOURCC
|
||||||
|
public static string decode_mmioFOURCC(int code)
|
||||||
|
{
|
||||||
|
char[] chs = new char[4];
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
chs[i] = (char)(byte)((code >> (i << 3)) & 0xFF);
|
||||||
|
if (!char.IsLetterOrDigit(chs[i]))
|
||||||
|
chs[i] = ' ';
|
||||||
|
}
|
||||||
|
return new string(chs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue