avi rez-change segmentation
This commit is contained in:
parent
a8c66418e7
commit
bff3957c7e
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
@ -9,14 +10,266 @@ using BizHawk;
|
||||||
|
|
||||||
namespace BizHawk.MultiClient
|
namespace BizHawk.MultiClient
|
||||||
{
|
{
|
||||||
unsafe class AviWriter : IDisposable
|
class AviWriter : IDisposable
|
||||||
{
|
{
|
||||||
static AviWriter()
|
CodecToken currVideoCodecToken = null;
|
||||||
|
AviWriterSegment currSegment;
|
||||||
|
IEnumerator<string> nameProvider;
|
||||||
|
|
||||||
|
bool IsOpen { get { return nameProvider != null; } }
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (currSegment != null)
|
||||||
|
currSegment.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// sets the codec token to be used for video compression
|
||||||
|
/// </summary>
|
||||||
|
public void SetVideoCodecToken(CodecToken token)
|
||||||
|
{
|
||||||
|
currVideoCodecToken = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerator<string> CreateBasicNameProvider(string template)
|
||||||
|
{
|
||||||
|
string dir = Path.GetDirectoryName(template);
|
||||||
|
string baseName = Path.GetFileNameWithoutExtension(template);
|
||||||
|
string ext = Path.GetExtension(template);
|
||||||
|
yield return template;
|
||||||
|
int counter = 1;
|
||||||
|
for (; ; )
|
||||||
|
{
|
||||||
|
yield return Path.Combine(dir, baseName) + "_" + counter + ext;
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// opens an avi file for recording with names based on the supplied template.
|
||||||
|
/// set a video codec token first.
|
||||||
|
/// </summary>
|
||||||
|
public void OpenFile(string baseName) { OpenFile(CreateBasicNameProvider(baseName)); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// opens an avi file for recording with the supplied enumerator used to name files.
|
||||||
|
/// set a video codec token first.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="nameProvider"></param>
|
||||||
|
public void OpenFile(IEnumerator<string> nameProvider)
|
||||||
|
{
|
||||||
|
this.nameProvider = nameProvider;
|
||||||
|
if (currVideoCodecToken == null)
|
||||||
|
throw new InvalidOperationException("Tried to start recording an AVI with no video codec token set");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CloseFile()
|
||||||
|
{
|
||||||
|
if (currSegment != null)
|
||||||
|
currSegment.Dispose();
|
||||||
|
currSegment = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddFrame(IVideoProvider source)
|
||||||
|
{
|
||||||
|
SetVideoParameters(source.BufferWidth, source.BufferHeight);
|
||||||
|
if (currSegment == null) Segment();
|
||||||
|
currSegment.AddFrame(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddSamples(short[] samples)
|
||||||
|
{
|
||||||
|
if (currSegment == null) Segment();
|
||||||
|
currSegment.AddSamples(samples);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StartRecording()
|
||||||
|
{
|
||||||
|
//i guess theres nothing to do here
|
||||||
|
}
|
||||||
|
|
||||||
|
void Segment()
|
||||||
|
{
|
||||||
|
if (!IsOpen) return;
|
||||||
|
|
||||||
|
if (currSegment == null)
|
||||||
|
StartRecording();
|
||||||
|
else
|
||||||
|
currSegment.Dispose();
|
||||||
|
currSegment = new AviWriterSegment();
|
||||||
|
nameProvider.MoveNext();
|
||||||
|
currSegment.OpenFile(nameProvider.Current, parameters, currVideoCodecToken);
|
||||||
|
currSegment.OpenStreams();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Acquires a video codec configuration from the user. you may save it for future use, but you must dispose of it when youre done with it.
|
||||||
|
/// returns null if the user canceled the dialog
|
||||||
|
/// </summary>
|
||||||
|
public static CodecToken AcquireVideoCodecToken(IntPtr hwnd, CodecToken lastToken)
|
||||||
|
{
|
||||||
|
var temp_params = new Parameters();
|
||||||
|
temp_params.height = 256;
|
||||||
|
temp_params.width = 256;
|
||||||
|
temp_params.fps = 60;
|
||||||
|
temp_params.fps_scale = 1;
|
||||||
|
temp_params.a_bits = 16;
|
||||||
|
temp_params.a_samplerate = 44100;
|
||||||
|
temp_params.a_channels = 2;
|
||||||
|
var temp = new AviWriterSegment();
|
||||||
|
string tempfile = Path.GetTempFileName();
|
||||||
|
File.Delete(tempfile);
|
||||||
|
tempfile = Path.ChangeExtension(tempfile, "avi");
|
||||||
|
temp.OpenFile(tempfile, temp_params, lastToken);
|
||||||
|
CodecToken token = temp.AcquireVideoCodecToken(hwnd);
|
||||||
|
temp.CloseFile();
|
||||||
|
File.Delete(tempfile);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
bool change = false;
|
||||||
|
|
||||||
|
change |= fps != parameters.fps;
|
||||||
|
parameters.fps = fps;
|
||||||
|
|
||||||
|
change |= parameters.fps_scale != fps_scale;
|
||||||
|
parameters.fps_scale = fps_scale;
|
||||||
|
|
||||||
|
if (change) Segment();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
bool change = false;
|
||||||
|
|
||||||
|
change |= parameters.width != width;
|
||||||
|
parameters.width = width;
|
||||||
|
|
||||||
|
change |= parameters.height != height;
|
||||||
|
parameters.height = height;
|
||||||
|
|
||||||
|
if (change) Segment();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
bool change = false;
|
||||||
|
|
||||||
|
change |= parameters.a_samplerate != sampleRate;
|
||||||
|
parameters.a_samplerate = sampleRate;
|
||||||
|
|
||||||
|
change |= parameters.a_channels != channels;
|
||||||
|
parameters.a_channels = channels;
|
||||||
|
|
||||||
|
change |= parameters.a_bits != bits;
|
||||||
|
parameters.a_bits = bits;
|
||||||
|
|
||||||
|
change |= parameters.has_audio != true;
|
||||||
|
parameters.has_audio = true;
|
||||||
|
|
||||||
|
if (change) Segment();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CodecToken : IDisposable
|
||||||
|
{
|
||||||
|
~CodecToken()
|
||||||
|
{
|
||||||
|
Dispose();
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe class AviWriterSegment : IDisposable
|
||||||
|
{
|
||||||
|
static AviWriterSegment()
|
||||||
{
|
{
|
||||||
Win32.AVIFileInit();
|
Win32.AVIFileInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public AviWriter()
|
public AviWriterSegment()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,120 +330,12 @@ namespace BizHawk.MultiClient
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Parameters parameters;
|
||||||
public class CodecToken : IDisposable
|
public void OpenFile(string destPath, Parameters parameters, CodecToken videoCodecToken)
|
||||||
{
|
{
|
||||||
public static CodecToken TakePossession(Win32.AVICOMPRESSOPTIONS comprOptions)
|
this.parameters = parameters;
|
||||||
{
|
this.currVideoCodecToken = videoCodecToken;
|
||||||
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?
|
//TODO - try creating the file once first before we let vfw botch it up?
|
||||||
|
|
||||||
//open the avi output file handle
|
//open the avi output file handle
|
||||||
|
@ -279,7 +424,7 @@ namespace BizHawk.MultiClient
|
||||||
//set audio stream input format
|
//set audio stream input format
|
||||||
Win32.WAVEFORMATEX wfex = new Win32.WAVEFORMATEX();
|
Win32.WAVEFORMATEX wfex = new Win32.WAVEFORMATEX();
|
||||||
parameters.PopulateWAVEFORMATEX(ref wfex);
|
parameters.PopulateWAVEFORMATEX(ref wfex);
|
||||||
if(Win32.FAILED(Win32.AVIStreamSetFormat(pAviRawAudioStream, 0, ref wfex, Marshal.SizeOf(wfex))))
|
if (Win32.FAILED(Win32.AVIStreamSetFormat(pAviRawAudioStream, 0, ref wfex, Marshal.SizeOf(wfex))))
|
||||||
{
|
{
|
||||||
CloseStreams();
|
CloseStreams();
|
||||||
throw new InvalidOperationException("Failed setting raw audio stream input format");
|
throw new InvalidOperationException("Failed setting raw audio stream input format");
|
||||||
|
@ -341,7 +486,7 @@ namespace BizHawk.MultiClient
|
||||||
outStatus.BufferedShorts[outStatus.audio_buffered_shorts++] = samples[idx++];
|
outStatus.BufferedShorts[outStatus.audio_buffered_shorts++] = samples[idx++];
|
||||||
todo -= chunk;
|
todo -= chunk;
|
||||||
|
|
||||||
if(outStatus.audio_buffered_shorts == OutputStatus.AUDIO_SEGMENT_SIZE)
|
if (outStatus.audio_buffered_shorts == OutputStatus.AUDIO_SEGMENT_SIZE)
|
||||||
FlushBufferedAudio();
|
FlushBufferedAudio();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -350,7 +495,7 @@ namespace BizHawk.MultiClient
|
||||||
{
|
{
|
||||||
int todo = outStatus.audio_buffered_shorts;
|
int todo = outStatus.audio_buffered_shorts;
|
||||||
int todo_realsamples = todo / 2;
|
int todo_realsamples = todo / 2;
|
||||||
IntPtr buf = GetStaticGlobalBuf(todo*2);
|
IntPtr buf = GetStaticGlobalBuf(todo * 2);
|
||||||
|
|
||||||
short* sptr = (short*)buf.ToPointer();
|
short* sptr = (short*)buf.ToPointer();
|
||||||
for (int i = 0; i < todo; i++)
|
for (int i = 0; i < todo; i++)
|
||||||
|
@ -366,7 +511,7 @@ namespace BizHawk.MultiClient
|
||||||
|
|
||||||
public unsafe void AddFrame(IVideoProvider source)
|
public unsafe void AddFrame(IVideoProvider source)
|
||||||
{
|
{
|
||||||
if(parameters.width != source.BufferWidth
|
if (parameters.width != source.BufferWidth
|
||||||
|| parameters.height != source.BufferHeight)
|
|| parameters.height != source.BufferHeight)
|
||||||
throw new InvalidOperationException("video buffer changed between start and now");
|
throw new InvalidOperationException("video buffer changed between start and now");
|
||||||
|
|
||||||
|
@ -406,17 +551,7 @@ namespace BizHawk.MultiClient
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// sets the codec token to be used for video compression
|
|
||||||
/// </summary>
|
|
||||||
public void SetVideoCodecToken(CodecToken token)
|
|
||||||
{
|
|
||||||
currVideoCodecToken = token;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2597,10 +2597,9 @@ namespace BizHawk.MultiClient
|
||||||
aw.SetMovieParameters(fps, 0x01000000);
|
aw.SetMovieParameters(fps, 0x01000000);
|
||||||
aw.SetVideoParameters(Global.Emulator.VideoProvider.BufferWidth, Global.Emulator.VideoProvider.BufferHeight);
|
aw.SetVideoParameters(Global.Emulator.VideoProvider.BufferWidth, Global.Emulator.VideoProvider.BufferHeight);
|
||||||
aw.SetAudioParameters(44100, 2, 16);
|
aw.SetAudioParameters(44100, 2, 16);
|
||||||
aw.OpenFile(sfd.FileName);
|
var token = AviWriter.AcquireVideoCodecToken(Global.MainForm.Handle, null);
|
||||||
var token = aw.AcquireVideoCodecToken(Global.MainForm.Handle);
|
|
||||||
aw.SetVideoCodecToken(token);
|
aw.SetVideoCodecToken(token);
|
||||||
aw.OpenStreams();
|
aw.OpenFile(sfd.FileName);
|
||||||
|
|
||||||
//commit the avi writing last, in case there were any errors earlier
|
//commit the avi writing last, in case there were any errors earlier
|
||||||
CurrAviWriter = aw;
|
CurrAviWriter = aw;
|
||||||
|
|
Loading…
Reference in New Issue