From c72b449a04fd990adc161a72b3b7379424b263d8 Mon Sep 17 00:00:00 2001 From: Frooxius Date: Tue, 28 Apr 2026 22:43:13 +0200 Subject: [PATCH 1/2] feat#: Report actual frame index after seeking --- Codecs/SoundFlow.Codecs.FFMpeg/FFmpegDecoder.cs | 2 +- Codecs/SoundFlow.Codecs.FFMpeg/Native/FFmpeg.cs | 2 +- Native/ffmpeg-codec/soundflow-ffmpeg.c | 11 ++++++++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Codecs/SoundFlow.Codecs.FFMpeg/FFmpegDecoder.cs b/Codecs/SoundFlow.Codecs.FFMpeg/FFmpegDecoder.cs index 954067cc..3bffe202 100644 --- a/Codecs/SoundFlow.Codecs.FFMpeg/FFmpegDecoder.cs +++ b/Codecs/SoundFlow.Codecs.FFMpeg/FFmpegDecoder.cs @@ -111,7 +111,7 @@ public bool Seek(int sampleOffset) if (IsDisposed || !_stream.CanSeek) return false; var frameIndex = sampleOffset / Channels; - var result = FFmpeg.SeekToPcmFrame(_handle, frameIndex); + var result = FFmpeg.SeekToPcmFrame(_handle, frameIndex, out long resultFrameIndex); return result == FFmpegResult.Success; } diff --git a/Codecs/SoundFlow.Codecs.FFMpeg/Native/FFmpeg.cs b/Codecs/SoundFlow.Codecs.FFMpeg/Native/FFmpeg.cs index fc61d02e..ac1bd1a7 100644 --- a/Codecs/SoundFlow.Codecs.FFMpeg/Native/FFmpeg.cs +++ b/Codecs/SoundFlow.Codecs.FFMpeg/Native/FFmpeg.cs @@ -194,7 +194,7 @@ public static partial FFmpegResult InitializeDecoder(SafeDecoderHandle decoder, public static partial FFmpegResult ReadPcmFrames(SafeDecoderHandle decoder, IntPtr pFramesOut, long frameCount, out long outFramesRead); [LibraryImport(LibraryName, EntryPoint = "sf_decoder_seek_to_pcm_frame")] - public static partial FFmpegResult SeekToPcmFrame(SafeDecoderHandle decoder, long frameIndex); + public static partial FFmpegResult SeekToPcmFrame(SafeDecoderHandle decoder, long frameIndex, out long resultFrameIndex); [LibraryImport(LibraryName, EntryPoint = "sf_decoder_free")] public static partial void FreeDecoder(IntPtr decoder); diff --git a/Native/ffmpeg-codec/soundflow-ffmpeg.c b/Native/ffmpeg-codec/soundflow-ffmpeg.c index 554a626f..dfb249bd 100644 --- a/Native/ffmpeg-codec/soundflow-ffmpeg.c +++ b/Native/ffmpeg-codec/soundflow-ffmpeg.c @@ -305,7 +305,7 @@ SF_FFMPEG_API SF_Result sf_decoder_read_pcm_frames(SF_Decoder* decoder, void* pF return SF_RESULT_SUCCESS; } -SF_FFMPEG_API SF_Result sf_decoder_seek_to_pcm_frame(SF_Decoder* decoder, int64_t frameIndex) { +SF_FFMPEG_API SF_Result sf_decoder_seek_to_pcm_frame(SF_Decoder* decoder, int64_t frameIndex, int64_t* resultFrameIndex) { if (!decoder || !decoder->format_ctx || decoder->stream_index < 0) return SF_RESULT_ERROR_INVALID_ARGS; AVStream* stream = decoder->format_ctx->streams[decoder->stream_index]; @@ -317,13 +317,17 @@ SF_FFMPEG_API SF_Result sf_decoder_seek_to_pcm_frame(SF_Decoder* decoder, int64_ int ret = av_seek_frame(decoder->format_ctx, decoder->stream_index, timestamp, AVSEEK_FLAG_BACKWARD); if (ret < 0) { + *resultFrameIndex = -1; return SF_RESULT_DECODER_ERROR_SEEK_FAILED; } // Read and discard packets until we reach the desired position + int64_t packetTimestamp = -1; + AVPacket* pkt = av_packet_alloc(); while (av_read_frame(decoder->format_ctx, pkt) >= 0) { if (pkt->stream_index == decoder->stream_index) { + packetTimestamp = pkt->pts; av_packet_unref(pkt); break; } @@ -331,6 +335,11 @@ SF_FFMPEG_API SF_Result sf_decoder_seek_to_pcm_frame(SF_Decoder* decoder, int64_ } av_packet_free(&pkt); + if (packetTimestamp >= 0) + resultFrameIndex = av_rescale_q(packetTimestamp, stream->time_base, (AVRational) { 1, stream->codecpar->sample_rate }); + else + resultFrameIndex = -1; + return SF_RESULT_SUCCESS; } From a90dd91d374db184791ac48384676aea3cec92db Mon Sep 17 00:00:00 2001 From: Frooxius Date: Wed, 29 Apr 2026 01:07:42 +0200 Subject: [PATCH 2/2] feat#: Dummy decode samples to skip to the exact location --- .../SoundFlow.Codecs.FFMpeg/FFmpegDecoder.cs | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/Codecs/SoundFlow.Codecs.FFMpeg/FFmpegDecoder.cs b/Codecs/SoundFlow.Codecs.FFMpeg/FFmpegDecoder.cs index 3bffe202..6b5520e0 100644 --- a/Codecs/SoundFlow.Codecs.FFMpeg/FFmpegDecoder.cs +++ b/Codecs/SoundFlow.Codecs.FFMpeg/FFmpegDecoder.cs @@ -112,7 +112,28 @@ public bool Seek(int sampleOffset) var frameIndex = sampleOffset / Channels; var result = FFmpeg.SeekToPcmFrame(_handle, frameIndex, out long resultFrameIndex); - return result == FFmpegResult.Success; + + if (result == FFmpegResult.Success) + { + if(resultFrameIndex >= 0) + { + // Compute how many samples to skip + var toSkip = (int)(frameIndex - resultFrameIndex); + + if(toSkip > 0) + { + // TODO!!! This could probably be optimized, but this is the quickest way to add this + Span dummySamples = stackalloc float[toSkip * Channels]; + + // Dummy decode samples to skip them so we are at exact location we want + Decode(dummySamples); + } + } + + return true; + } + else + return false; } private unsafe nuint OnRead(IntPtr pUserData, IntPtr pBuffer, nuint bytesToRead)