fix: audio silence (idempotent sync-delay configure), webcam gray block, truncated videos
- AudioSyncDelay.Configure reallocated/zeroed its buffer every ~10ms tick
(AudioMixer re-reads the UI setting each mix), so any non-zero sync offset
erased the just-written audio -> total silence. Now early-returns when the
delay samples are unchanged. Regression test proven both ways.
- SceneCompositor.BlitContentRaw defaulted cbW/cbH=0 when CropBounds is null
(regression from ed9d7c1) -> webcam blit to an empty rect = gray block.
Default to src.Width/Height. Regression test proven both ways.
- Truncated recordings: rawvideo mux stamps frames at declared 60fps by
arrival; a scene whose first layer is dynamic (hidden elements still count)
kills the bake cache -> full render ~35ms -> ~27fps submitted -> halved
file length. Static scenes bake once (246ms cold, then <1ms) -> 60fps,
full-length (probe + 13:53 take, 301/300 per 5s, 12.46s file from 12.3s
wall). FramePump.ProbeRender names the hot render path on slow frames.
This commit is contained in:
@@ -267,9 +267,6 @@ public sealed class MediaCaptureFrameSource : ICameraFrameSource
|
||||
private int _ringNext;
|
||||
private long _epoch;
|
||||
|
||||
// Alpha probe diagnostics (one-shot, throttled)
|
||||
private DateTime _lastAlphaProbe = DateTime.MinValue;
|
||||
|
||||
private byte[] RentRingBuffer(int size)
|
||||
{
|
||||
for (var tries = 0; tries < _frameRing.Length; tries++)
|
||||
@@ -308,25 +305,11 @@ public sealed class MediaCaptureFrameSource : ICameraFrameSource
|
||||
var pixels = RentRingBuffer((int)capacity);
|
||||
Marshal.Copy(pixelsPtr, pixels, 0, (int)capacity);
|
||||
|
||||
// Alpha probe diagnostics (throttled to once per 5s)
|
||||
var now = DateTime.UtcNow;
|
||||
if ((now - _lastAlphaProbe).TotalSeconds >= 5.0)
|
||||
{
|
||||
_lastAlphaProbe = now;
|
||||
ProbeAlphaStats(pixels, bitmap.PixelWidth, bitmap.PixelHeight);
|
||||
}
|
||||
|
||||
// MediaCapture video carries no alpha plane — the OS fills 255.
|
||||
var videoFrame = new VideoFrame(bitmap.PixelWidth, bitmap.PixelHeight, pixels)
|
||||
var outFrame = new VideoFrame(bitmap.PixelWidth, bitmap.PixelHeight, pixels)
|
||||
{ IsOpaque = true, Epoch = ++_epoch };
|
||||
|
||||
// Diagnostic: Log frame properties for compositor debugging
|
||||
if ((now - _lastAlphaProbe).TotalSeconds >= 5.0)
|
||||
{
|
||||
AppLog.Write($"Webcam frame emit: {videoFrame.Width}x{videoFrame.Height}, IsOpaque={videoFrame.IsOpaque}, Epoch={videoFrame.Epoch}");
|
||||
}
|
||||
|
||||
FrameAvailable?.Invoke(videoFrame);
|
||||
|
||||
FrameAvailable?.Invoke(outFrame);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -339,37 +322,4 @@ public sealed class MediaCaptureFrameSource : ICameraFrameSource
|
||||
bitmap.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void ProbeAlphaStats(byte[] pixels, int width, int height)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Sample 1000 alpha bytes (every 4th byte in BGRA format)
|
||||
var sampleCount = Math.Min(1000, pixels.Length / 4);
|
||||
var alphaMin = 255;
|
||||
var alphaMax = 0;
|
||||
var alphaSum = 0;
|
||||
|
||||
for (var i = 0; i < sampleCount; i++)
|
||||
{
|
||||
var alphaIndex = (i * 4) + 3; // Alpha is 4th byte in BGRA
|
||||
if (alphaIndex < pixels.Length)
|
||||
{
|
||||
var alpha = pixels[alphaIndex];
|
||||
alphaMin = Math.Min(alphaMin, alpha);
|
||||
alphaMax = Math.Max(alphaMax, alpha);
|
||||
alphaSum += alpha;
|
||||
}
|
||||
}
|
||||
|
||||
var alphaMean = sampleCount > 0 ? alphaSum / (double)sampleCount : 0;
|
||||
AppLog.Write($"Webcam alpha probe: {width}x{height}, epoch {_epoch}, " +
|
||||
$"alpha[min={alphaMin}, max={alphaMax}, mean={alphaMean:F1}], " +
|
||||
$"sampled {sampleCount} pixels");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AppLog.Write($"Webcam alpha probe failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user