AVOUT: FFMPEG and Nut writers: reduce memory thrashing issues substantially. should have no problem keeping up with extraordinarily large sizes now.
This commit is contained in:
parent
11d2216c98
commit
fcab1873b7
|
@ -182,13 +182,11 @@ namespace BizHawk.Client.EmuHawk
|
||||||
|
|
||||||
if (ffmpeg.HasExited)
|
if (ffmpeg.HasExited)
|
||||||
throw new Exception("unexpected ffmpeg death:\n" + ffmpeg_geterror());
|
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);
|
|
||||||
|
|
||||||
|
var video = source.GetVideoBuffer();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
muxer.writevideoframe(b);
|
muxer.WriteVideoFrame(video);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
@ -260,7 +258,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
throw new Exception("unexpected ffmpeg death:\n" + ffmpeg_geterror());
|
throw new Exception("unexpected ffmpeg death:\n" + ffmpeg_geterror());
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
muxer.writeaudioframe(samples);
|
muxer.WriteAudioFrame(samples);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,6 +15,67 @@ namespace BizHawk.Client.EmuHawk
|
||||||
{
|
{
|
||||||
// this code isn't really any good for general purpose nut creation
|
// this code isn't really any good for general purpose nut creation
|
||||||
|
|
||||||
|
#region simple buffer reuser
|
||||||
|
|
||||||
|
public class ReusableBufferPool<T>
|
||||||
|
{
|
||||||
|
private List<T[]> _available = new List<T[]>();
|
||||||
|
private List<T[]> _inuse = new List<T[]>();
|
||||||
|
|
||||||
|
private readonly int maxstored;
|
||||||
|
|
||||||
|
public ReusableBufferPool(int maxstored)
|
||||||
|
{
|
||||||
|
this.maxstored = maxstored;
|
||||||
|
}
|
||||||
|
|
||||||
|
private T[] GetBufferInternal(int length, bool zerofill, Func<T[], bool> criteria)
|
||||||
|
{
|
||||||
|
if (_inuse.Count == maxstored)
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
|
||||||
|
T[] candidate = _available.FirstOrDefault(criteria);
|
||||||
|
if (candidate == null)
|
||||||
|
{
|
||||||
|
if (_available.Count + _inuse.Count == maxstored)
|
||||||
|
{
|
||||||
|
// out of space! should not happen often
|
||||||
|
Console.WriteLine("Purging");
|
||||||
|
_available.Clear();
|
||||||
|
}
|
||||||
|
candidate = new T[length];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (zerofill)
|
||||||
|
Array.Clear(candidate, 0, candidate.Length);
|
||||||
|
_available.Remove(candidate);
|
||||||
|
}
|
||||||
|
_inuse.Add(candidate);
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T[] GetBuffer(int length, bool zerofill = false)
|
||||||
|
{
|
||||||
|
return GetBufferInternal(length, zerofill, a => a.Length == length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public T[] GetBufferAtLeast(int length, bool zerofill = false)
|
||||||
|
{
|
||||||
|
return GetBufferInternal(length, zerofill, a => a.Length >= length && a.Length / (float)length <= 2.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReleaseBuffer(T[] buffer)
|
||||||
|
{
|
||||||
|
if (!_inuse.Contains(buffer))
|
||||||
|
throw new ArgumentException();
|
||||||
|
_inuse.Remove(buffer);
|
||||||
|
_available.Add(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region binary write helpers
|
#region binary write helpers
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -328,6 +389,8 @@ namespace BizHawk.Client.EmuHawk
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Queue<NutFrame> audioqueue;
|
Queue<NutFrame> audioqueue;
|
||||||
|
|
||||||
|
ReusableBufferPool<byte> _bufferpool = new ReusableBufferPool<byte>(12);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region header writers
|
#region header writers
|
||||||
|
@ -433,6 +496,11 @@ namespace BizHawk.Client.EmuHawk
|
||||||
/// </summary>
|
/// </summary>
|
||||||
byte[] data;
|
byte[] data;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// valid length of the data
|
||||||
|
/// </summary>
|
||||||
|
int actual_length;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// presentation timestamp
|
/// presentation timestamp
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -443,21 +511,26 @@ namespace BizHawk.Client.EmuHawk
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ulong ptsnum, ptsden;
|
ulong ptsnum, ptsden;
|
||||||
|
|
||||||
|
ReusableBufferPool<byte> _pool;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="payload">frame data</param>
|
/// <param name="payload">frame data</param>
|
||||||
|
/// <param name="payloadlen">actual length of frame data</param>
|
||||||
/// <param name="pts">presentation timestamp</param>
|
/// <param name="pts">presentation timestamp</param>
|
||||||
/// <param name="ptsnum">numerator of timebase</param>
|
/// <param name="ptsnum">numerator of timebase</param>
|
||||||
/// <param name="ptsden">denominator of timebase</param>
|
/// <param name="ptsden">denominator of timebase</param>
|
||||||
/// <param name="ptsindex">which timestamp base is used, assumed to be also stream number</param>
|
/// <param name="ptsindex">which timestamp base is used, assumed to be also stream number</param>
|
||||||
public NutFrame(byte[] payload, ulong pts, ulong ptsnum, ulong ptsden, int ptsindex)
|
public NutFrame(byte[] payload, int payloadlen, ulong pts, ulong ptsnum, ulong ptsden, int ptsindex, ReusableBufferPool<byte> pool)
|
||||||
{
|
{
|
||||||
this.pts = pts;
|
this.pts = pts;
|
||||||
this.ptsnum = ptsnum;
|
this.ptsnum = ptsnum;
|
||||||
this.ptsden = ptsden;
|
this.ptsden = ptsden;
|
||||||
|
|
||||||
var frame = new MemoryStream();
|
this._pool = pool;
|
||||||
|
data = pool.GetBufferAtLeast(payloadlen + 2048);
|
||||||
|
var frame = new MemoryStream(data);
|
||||||
|
|
||||||
// create syncpoint
|
// create syncpoint
|
||||||
var sync = new NutPacket(NutPacket.StartCode.Syncpoint, frame);
|
var sync = new NutPacket(NutPacket.StartCode.Syncpoint, frame);
|
||||||
|
@ -471,7 +544,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
// frame_flags = FLAG_CODED, so:
|
// frame_flags = FLAG_CODED, so:
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
flags |= 1 << 0; // FLAG_KEY
|
flags |= 1 << 0; // FLAG_KEY
|
||||||
if (payload.Length == 0)
|
if (payloadlen == 0)
|
||||||
flags |= 1 << 1; // FLAG_EOR
|
flags |= 1 << 1; // FLAG_EOR
|
||||||
flags |= 1 << 3; // FLAG_CODED_PTS
|
flags |= 1 << 3; // FLAG_CODED_PTS
|
||||||
flags |= 1 << 4; // FLAG_STREAM_ID
|
flags |= 1 << 4; // FLAG_STREAM_ID
|
||||||
|
@ -480,14 +553,14 @@ namespace BizHawk.Client.EmuHawk
|
||||||
WriteVarU(flags, frameheader);
|
WriteVarU(flags, frameheader);
|
||||||
WriteVarU(ptsindex, frameheader); // stream_id
|
WriteVarU(ptsindex, frameheader); // stream_id
|
||||||
WriteVarU(pts + 256, frameheader); // coded_pts = pts + 1 << msb_pts_shift
|
WriteVarU(pts + 256, frameheader); // coded_pts = pts + 1 << msb_pts_shift
|
||||||
WriteVarU(payload.Length, frameheader); // data_size_msb
|
WriteVarU(payloadlen, frameheader); // data_size_msb
|
||||||
|
|
||||||
var frameheaderarr = frameheader.ToArray();
|
var frameheaderarr = frameheader.ToArray();
|
||||||
frame.Write(frameheaderarr, 0, frameheaderarr.Length);
|
frame.Write(frameheaderarr, 0, frameheaderarr.Length);
|
||||||
WriteBE32(NutCRC32(frameheaderarr), frame); // checksum
|
WriteBE32(NutCRC32(frameheaderarr), frame); // checksum
|
||||||
frame.Write(payload, 0, payload.Length);
|
frame.Write(payload, 0, payloadlen);
|
||||||
|
|
||||||
data = frame.ToArray();
|
actual_length = (int)frame.Position;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -521,7 +594,8 @@ namespace BizHawk.Client.EmuHawk
|
||||||
/// <param name="dest"></param>
|
/// <param name="dest"></param>
|
||||||
public void WriteData(Stream dest)
|
public void WriteData(Stream dest)
|
||||||
{
|
{
|
||||||
dest.Write(data, 0, data.Length);
|
dest.Write(data, 0, actual_length);
|
||||||
|
_pool.ReleaseBuffer(data);
|
||||||
//dbg.WriteLine(string.Format("{0},{1},{2}", pts, ptsnum, ptsden));
|
//dbg.WriteLine(string.Format("{0},{1},{2}", pts, ptsnum, ptsden));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -530,13 +604,19 @@ namespace BizHawk.Client.EmuHawk
|
||||||
/// write a video frame to the stream
|
/// write a video frame to the stream
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="data">raw video data; if length 0, write EOR</param>
|
/// <param name="data">raw video data; if length 0, write EOR</param>
|
||||||
public void writevideoframe(byte[] data)
|
public void WriteVideoFrame(int[] video)
|
||||||
{
|
{
|
||||||
if (videodone)
|
if (videodone)
|
||||||
throw new Exception("Can't write data after end of relevance!");
|
throw new InvalidOperationException("Can't write data after end of relevance!");
|
||||||
if (data.Length == 0)
|
if (audioqueue.Count > 5)
|
||||||
|
throw new Exception("A\\V Desync?");
|
||||||
|
int datalen = video.Length * sizeof(int);
|
||||||
|
byte[] data = _bufferpool.GetBufferAtLeast(datalen);
|
||||||
|
Buffer.BlockCopy(video, 0, data, 0, datalen);
|
||||||
|
if (datalen == 0)
|
||||||
videodone = true;
|
videodone = true;
|
||||||
var f = new NutFrame(data, videopts, (ulong) avparams.fpsden, (ulong) avparams.fpsnum, 0);
|
var f = new NutFrame(data, datalen, videopts, (ulong) avparams.fpsden, (ulong) avparams.fpsnum, 0, _bufferpool);
|
||||||
|
_bufferpool.ReleaseBuffer(data);
|
||||||
videopts++;
|
videopts++;
|
||||||
videoqueue.Enqueue(f);
|
videoqueue.Enqueue(f);
|
||||||
while (audioqueue.Count > 0 && f >= audioqueue.Peek())
|
while (audioqueue.Count > 0 && f >= audioqueue.Peek())
|
||||||
|
@ -547,16 +627,20 @@ namespace BizHawk.Client.EmuHawk
|
||||||
/// write an audio frame to the stream
|
/// write an audio frame to the stream
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="data">raw audio data; if length 0, write EOR</param>
|
/// <param name="data">raw audio data; if length 0, write EOR</param>
|
||||||
public void writeaudioframe(short[] samples)
|
public void WriteAudioFrame(short[] samples)
|
||||||
{
|
{
|
||||||
if (audiodone)
|
if (audiodone)
|
||||||
throw new Exception("Can't write audio after end of relevance!");
|
throw new Exception("Can't write audio after end of relevance!");
|
||||||
byte[] data = new byte[samples.Length * sizeof (short)];
|
if (videoqueue.Count > 5)
|
||||||
Buffer.BlockCopy(samples, 0, data, 0, data.Length);
|
throw new Exception("A\\V Desync?");
|
||||||
if (data.Length == 0)
|
int datalen = samples.Length * sizeof(short);
|
||||||
|
byte[] data = _bufferpool.GetBufferAtLeast(datalen);
|
||||||
|
Buffer.BlockCopy(samples, 0, data, 0, datalen);
|
||||||
|
if (datalen == 0)
|
||||||
audiodone = true;
|
audiodone = true;
|
||||||
|
|
||||||
var f = new NutFrame(data, audiopts, 1, (ulong)avparams.samplerate, 1);
|
var f = new NutFrame(data, datalen, audiopts, 1, (ulong)avparams.samplerate, 1, _bufferpool);
|
||||||
|
_bufferpool.ReleaseBuffer(data);
|
||||||
audiopts += (ulong)samples.Length / (ulong)avparams.channels;
|
audiopts += (ulong)samples.Length / (ulong)avparams.channels;
|
||||||
audioqueue.Enqueue(f);
|
audioqueue.Enqueue(f);
|
||||||
while (videoqueue.Count > 0 && f >= videoqueue.Peek())
|
while (videoqueue.Count > 0 && f >= videoqueue.Peek())
|
||||||
|
@ -606,9 +690,9 @@ namespace BizHawk.Client.EmuHawk
|
||||||
public void Finish()
|
public void Finish()
|
||||||
{
|
{
|
||||||
if (!videodone)
|
if (!videodone)
|
||||||
writevideoframe(new byte[0]);
|
WriteVideoFrame(new int[0]);
|
||||||
if (!audiodone)
|
if (!audiodone)
|
||||||
writeaudioframe(new short[0]);
|
WriteAudioFrame(new short[0]);
|
||||||
|
|
||||||
// flush any remaining queued packets
|
// flush any remaining queued packets
|
||||||
|
|
||||||
|
|
|
@ -75,15 +75,12 @@ namespace BizHawk.Client.EmuHawk
|
||||||
{
|
{
|
||||||
if (source.BufferHeight != height || source.BufferWidth != width)
|
if (source.BufferHeight != height || source.BufferWidth != width)
|
||||||
SetVideoParameters(source.BufferWidth, source.BufferHeight);
|
SetVideoParameters(source.BufferWidth, source.BufferHeight);
|
||||||
var a = source.GetVideoBuffer();
|
current.WriteVideoFrame(source.GetVideoBuffer());
|
||||||
var b = new byte[a.Length * sizeof(int)];
|
|
||||||
Buffer.BlockCopy(a, 0, b, 0, b.Length);
|
|
||||||
current.writevideoframe(b);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddSamples(short[] samples)
|
public void AddSamples(short[] samples)
|
||||||
{
|
{
|
||||||
current.writeaudioframe(samples);
|
current.WriteAudioFrame(samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue