From c18b29947535beed25957a0380f741b78cccc168 Mon Sep 17 00:00:00 2001 From: Mario Guggenberger Date: Tue, 30 Jan 2024 12:14:51 +0100 Subject: [PATCH] fix: FFmpeg end-of-stream handling breaks stream contract --- .../FFmpegSourceStreamTests.cs | 25 +++++++++++++++++++ src/Aurio.FFmpeg/FFmpegSourceStream.cs | 19 ++++++++++++-- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/Aurio.FFmpeg.UnitTest/FFmpegSourceStreamTests.cs b/src/Aurio.FFmpeg.UnitTest/FFmpegSourceStreamTests.cs index 8d77b66..8a1a59f 100644 --- a/src/Aurio.FFmpeg.UnitTest/FFmpegSourceStreamTests.cs +++ b/src/Aurio.FFmpeg.UnitTest/FFmpegSourceStreamTests.cs @@ -239,5 +239,30 @@ out It.Ref.IsAny Times.Exactly(3) ); } + + [Fact] + public void SeekBeyondEnd_PositionCanBeSet() + { + var fileInfo = new FileInfo("./Resources/sine440-44100-16-mono-200ms.ts"); + var s = new FFmpegSourceStream(fileInfo); + var position = 10000000000 * s.SampleBlockSize; + + s.Position = position; + + Assert.Equal(position, s.Position); + } + + [Fact] + public void SeekBeyondEnd_ReadIndicatesEndOfStream() + { + var fileInfo = new FileInfo("./Resources/sine440-44100-16-mono-200ms.ts"); + var s = new FFmpegSourceStream(fileInfo); + var position = 10000000000 * s.SampleBlockSize; + + s.Position = position; + var bytesRead = s.Read(new byte[1000], 0, 1000); + + Assert.Equal(0, bytesRead); + } } } diff --git a/src/Aurio.FFmpeg/FFmpegSourceStream.cs b/src/Aurio.FFmpeg/FFmpegSourceStream.cs index 69b7705..18a56e2 100644 --- a/src/Aurio.FFmpeg/FFmpegSourceStream.cs +++ b/src/Aurio.FFmpeg/FFmpegSourceStream.cs @@ -175,7 +175,12 @@ private void ForwardReadUntilTimestamp(long targetTimestamp) { ReadFrame(); - if (readerPosition == previousReaderPosition) + if (readerPosition == -1) + { + // "Timestamp not found (end of file reached)" + return; + } + else if (readerPosition == previousReaderPosition) { // Prevent an endless read-loop in case the reported position does not change. // I did not encounter this behavior, but who knows how FFmpeg acts on the myriad of supported formats. @@ -221,6 +226,16 @@ public long Position ForwardReadUntilTimestamp(seekTarget); } + if (sourceBufferLength == -1) + { + // End of stream handling: + // The Aurio stream interface allows seeking beyond the end, so we act + // like the seek was successful. `Read` won't return any samples though. + readerPosition = seekTarget; + sourceBufferPosition = 0; + return; + } + // check if seek ended up at seek target (or earlier because of frame size, depends on file format and stream codec) if (seekTarget == readerPosition) { @@ -284,7 +299,7 @@ public int Read(byte[] buffer, int offset, int count) out Type type ); - if (newPosition == -1 || sourceBufferLength == -1) + if (sourceBufferLength == -1) { return 0; // end of stream }