[AvPlayer] improved video playback sync and stats
This commit is contained in:
parent
a9e1c25968
commit
c4543a0ed1
|
|
@ -351,6 +351,8 @@ public static class AvPlayerExports
|
|||
: VideoFrameReadResult.Pending;
|
||||
}
|
||||
|
||||
public int QueuedFrameCount => _frames.Reader.Count;
|
||||
|
||||
private void DecodeFrames()
|
||||
{
|
||||
try
|
||||
|
|
@ -456,6 +458,12 @@ public static class AvPlayerExports
|
|||
public ulong LastVideoTimestamp { get; set; }
|
||||
public ulong EventDataBuffer { get; set; }
|
||||
public long NextFrameIndex { get; set; }
|
||||
public long VideoCalls { get; set; }
|
||||
public long VideoDelivered { get; set; }
|
||||
public long VideoPending { get; set; }
|
||||
public long VideoSyncWait { get; set; }
|
||||
public long VideoWriteFailures { get; set; }
|
||||
public long VideoStatsSecond { get; set; } = -1;
|
||||
public ulong AudioBufferBase { get; set; }
|
||||
public int NextAudioBuffer { get; set; }
|
||||
public long NextAudioFrameIndex { get; set; }
|
||||
|
|
@ -498,6 +506,12 @@ public static class AvPlayerExports
|
|||
DisposePlaybackResources();
|
||||
PlaybackClock.Reset();
|
||||
NextFrameIndex = 0;
|
||||
VideoCalls = 0;
|
||||
VideoDelivered = 0;
|
||||
VideoPending = 0;
|
||||
VideoSyncWait = 0;
|
||||
VideoWriteFailures = 0;
|
||||
VideoStatsSecond = -1;
|
||||
LastGuestBuffer = 0;
|
||||
LastVideoTimestamp = 0;
|
||||
SeekVideoFramePending = false;
|
||||
|
|
@ -793,6 +807,7 @@ public static class AvPlayerExports
|
|||
player.PlaybackClock.Stop();
|
||||
player.FallbackPlayback?.Pause();
|
||||
Console.Error.WriteLine($"[AVPLAYER][INFO] pause handle=0x{player.Handle:X16}");
|
||||
TraceVideoStats(player);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1619,6 +1634,14 @@ public static class AvPlayerExports
|
|||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
player.VideoCalls++;
|
||||
var statsSecond = player.PlaybackClock.ElapsedMilliseconds / 1000;
|
||||
if (statsSecond > player.VideoStatsSecond)
|
||||
{
|
||||
player.VideoStatsSecond = statsSecond;
|
||||
TraceVideoStats(player);
|
||||
}
|
||||
|
||||
if (player.Paused)
|
||||
{
|
||||
if (player.SeekVideoFramePending &&
|
||||
|
|
@ -1658,47 +1681,26 @@ public static class AvPlayerExports
|
|||
}
|
||||
|
||||
var fps = Math.Max(1.0, player.FramesPerSecond);
|
||||
var expectedFrame = player.NextFrameIndex;
|
||||
if (player.AvSyncMode == AvSyncModeDefault)
|
||||
{
|
||||
var expectedFrame = CalculateExpectedDefaultSyncVideoFrame(
|
||||
expectedFrame = CalculateExpectedDefaultSyncVideoFrame(
|
||||
player.HasAudio,
|
||||
player.NextAudioFrameIndex,
|
||||
player.PlaybackClock.Elapsed.TotalSeconds,
|
||||
fps);
|
||||
if (player.NextFrameIndex > expectedFrame)
|
||||
{
|
||||
player.VideoSyncWait++;
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
var behind = expectedFrame - player.NextFrameIndex;
|
||||
if (behind > MaxCatchUpFrames)
|
||||
{
|
||||
// Limit this call's work without changing the shared playback clock.
|
||||
expectedFrame = player.NextFrameIndex + MaxCatchUpFrames;
|
||||
TraceOnce(
|
||||
"catch_up_capped",
|
||||
$"catch_up capped behind={behind} max={MaxCatchUpFrames} " +
|
||||
$"fps={fps:F3} {player.Width}x{player.Height}");
|
||||
}
|
||||
|
||||
while (player.NextFrameIndex < expectedFrame)
|
||||
{
|
||||
var skippedFrame = ReadFrame(player);
|
||||
if (skippedFrame == VideoFrameReadResult.Pending)
|
||||
{
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
if (skippedFrame == VideoFrameReadResult.End)
|
||||
{
|
||||
return FinishStream(ctx, player);
|
||||
}
|
||||
player.NextFrameIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
var frameResult = ReadFrame(player);
|
||||
if (frameResult == VideoFrameReadResult.Pending)
|
||||
{
|
||||
player.VideoPending++;
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
if (frameResult == VideoFrameReadResult.End)
|
||||
|
|
@ -1707,9 +1709,23 @@ public static class AvPlayerExports
|
|||
}
|
||||
|
||||
var frameIndex = player.NextFrameIndex;
|
||||
// Only discard old frames when a replacement is already decoded.
|
||||
for (var skipped = 0;
|
||||
skipped < MaxCatchUpFrames &&
|
||||
player.NextFrameIndex < expectedFrame &&
|
||||
player.VideoDecoder!.QueuedFrameCount > 1;
|
||||
skipped++)
|
||||
{
|
||||
player.NextFrameIndex++;
|
||||
if (ReadFrame(player) != VideoFrameReadResult.Ready)
|
||||
{
|
||||
break;
|
||||
}
|
||||
frameIndex = player.NextFrameIndex;
|
||||
}
|
||||
var timestamp = player.StartTimeMilliseconds +
|
||||
checked((ulong)Math.Round(frameIndex * 1000.0 / fps));
|
||||
player.NextFrameIndex++;
|
||||
player.NextFrameIndex = frameIndex + 1;
|
||||
if (!WriteVideoFrame(
|
||||
ctx,
|
||||
player,
|
||||
|
|
@ -1718,15 +1734,28 @@ public static class AvPlayerExports
|
|||
frameIndex,
|
||||
extended))
|
||||
{
|
||||
player.VideoWriteFailures++;
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
player.LastVideoTimestamp = timestamp;
|
||||
player.VideoDelivered++;
|
||||
|
||||
Trace($"video_frame handle=0x{player.Handle:X16} ex={extended} ts={timestamp} data=0x{player.LastGuestBuffer:X16}");
|
||||
return SetReturn(ctx, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private static void TraceVideoStats(PlayerState player)
|
||||
{
|
||||
Console.Error.WriteLine(
|
||||
$"[AVPLAYER][INFO] video_stats handle=0x{player.Handle:X16} " +
|
||||
$"clock_ms={player.PlaybackClock.ElapsedMilliseconds} audio_blocks={player.NextAudioFrameIndex} " +
|
||||
$"calls={player.VideoCalls} delivered={player.VideoDelivered} " +
|
||||
$"pending={player.VideoPending} sync_wait={player.VideoSyncWait} " +
|
||||
$"write_fail={player.VideoWriteFailures} next_frame={player.NextFrameIndex} " +
|
||||
$"queued={player.VideoDecoder?.QueuedFrameCount ?? 0} last_ts={player.LastVideoTimestamp}");
|
||||
}
|
||||
|
||||
private static int FinishStream(CpuContext ctx, PlayerState player)
|
||||
{
|
||||
TraceOnce(
|
||||
|
|
|
|||
Loading…
Reference in New Issue