radek-k / FFMediaToolkit

FFMediaToolkit is a cross-platform video decoder/encoder library for .NET that uses FFmpeg native libraries. It supports video frames extraction, reading stream metadata and creating videos from bitmaps in any format supported by FFmpeg.
MIT License
352 stars 56 forks source link

Reset MediaFile Stream position? #122

Open tottaka opened 2 years ago

tottaka commented 2 years ago

Hi, is it possible to reset the media file video and audio streams back to beginning once they reach the end? For example to make the video loop back to the beginning once all frames have been consumed.

I tried TryGetFrame with the timestamp of the beginning of the video, but that doesn't seem to "reset the internal stream" if there even is one.

Do I have to just call MediaFile.Open again or is there a better method of doing this?

IsaMorphic commented 1 year ago

Both VideoStream and AudioStream wrappers have a Seek function that you can pass a timestamp to in the form of a TimeSpan value. Simply pass TimeSpan.Zero and then call TryGetNextFrame as normal. Hope this helps :)

Saalvage commented 10 months ago

For anyone stumbling across this: I couldn't find the Seek function (might've been a change in the library, might've been me being stupid). What I found to work was simply calling GetFrame with TimeSpan.Zero. Subsequent calls to GetNextFrame were providing frames from the beginning again!

tottaka commented 10 months ago

For anyone stumbling across this: I couldn't find the Seek function (might've been a change in the library, might've been me being stupid). What I found to work was simply calling GetFrame with TimeSpan.Zero. Subsequent calls to GetNextFrame were providing frames from the beginning again!

I'm pretty sure you can save a reference to the video FileStream(or any stream) and directly call the seek function of the stream.

What I ended up doing was essentially wrapping a ConcurrentQueue for successful decoded frames, and reading/displaying the queue as needed.


System.IO.FileStream videoStream = System.IO.File.Open("video.mp4", System.IO.FileMode.Open);
FFMediaToolkit.Decoding.MediaFile VideoFile = MediaFile.Open(SourceStream);

// Seek in the "stream"...
videoStream.Seek(0, System.IO.SeekOrigin.Begin);
IsaMorphic commented 10 months ago

@tottaka @Saalvage glad you guys have found some workarounds, I'm surprised the built-in Seek method doesn't exist anymore? I am too lazy to verify so I will take your word for it xD

Regardless, I think that the former of those suggested would be considered the "best practice" for FFMediaToolkit. This is because FFmpeg uses its own internal caches and buffers to keep track of a container's various streams and how they flow through your application. I fear that manually seeking the FileStream via the managed handle could inadvertently invalidate FFmpeg's internal caches without explicitly notifying native code (dangerous in terms of memory management!!). You also are then missing out on the added benefit of passing a timestamp rather than a raw pointer value.

I'm sure FFmpeg's developer docs have some guidance about this as well, as seeking streams is a very common and well-defined operation. Traditionally I've seen it done via libavcodec wrappers instead of using the raw pointer, which is exactly what the GetFrame overload does.

Cheers!

Saalvage commented 10 months ago

Thank you very much for the insights!

The only mystery that remains is that of Seek, I looked through the commit history, couldn't find it! Closest is Seek on the internal AvioStream class. Internal InputContainer has SeekFile and MediaStream's internal GetFrame method is documented as "Seeks the stream". However, in the end it doesn't really matter, as I'm perfectly contempt with the GetFrame solution :)