Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions Codecs/SoundFlow.Codecs.FFMpeg/FFmpegDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<float> 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)
Expand Down
2 changes: 1 addition & 1 deletion Codecs/SoundFlow.Codecs.FFMpeg/Native/FFmpeg.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
11 changes: 10 additions & 1 deletion Native/ffmpeg-codec/soundflow-ffmpeg.c
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand All @@ -317,20 +317,29 @@ 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;
}
av_packet_unref(pkt);
}
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;
}

Expand Down