diff --git a/Codecs/SoundFlow.Codecs.FFMpeg/FFmpegDecoder.cs b/Codecs/SoundFlow.Codecs.FFMpeg/FFmpegDecoder.cs index 954067cc..6b5520e0 100644 --- a/Codecs/SoundFlow.Codecs.FFMpeg/FFmpegDecoder.cs +++ b/Codecs/SoundFlow.Codecs.FFMpeg/FFmpegDecoder.cs @@ -111,8 +111,29 @@ public bool Seek(int sampleOffset) if (IsDisposed || !_stream.CanSeek) return false; var frameIndex = sampleOffset / Channels; - var result = FFmpeg.SeekToPcmFrame(_handle, frameIndex); - return result == FFmpegResult.Success; + var result = FFmpeg.SeekToPcmFrame(_handle, frameIndex, out long resultFrameIndex); + + 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) 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; }