unosquare / ffmediaelement

FFME: The Advanced WPF MediaElement (based on FFmpeg)
https://unosquare.github.io/ffmediaelement/
Other
1.17k stars 238 forks source link

Seek() Failure behaviour #561

Open simonkolomyjec opened 3 years ago

simonkolomyjec commented 3 years ago

Seek() Failure behaviour

We have built a multichannel video player that can play MP4 files either form a local disk or network share. Most of the time, it works well. However depending on either: Network conditions; or file issues, occasionally the Seek() command fails, which effectively blocks the processing queue. Sometimes if the user tries again it succeeds, or at worst case, close the file (to clear the processing queue), then opening the file again works.

I have traced it down to this code (in QueueSeekCommand):

            QueuedSeekTask = new Task<bool>(() =>
            {
                seekOperation.Wait();
                return true;
            });

The seekOperation.Wait(); call never returns in some cases.

Is there any chance there could be an overload provided allowing a CancellationToken to be used or some method to abort the wait/Seek operation after a period of time. I am happy to suggest something (when I get time) but if you think it's a worthwhile feature please advise! :)

Issue Categories

Version Information

Steps to Reproduce

  1. A little hard as it's intermittent, and due to file or network conditions.
  2. Step 2
  3. Step 3

Expected Results

Provide a Seek(TimeSpan desired, CancellationToken token) method.

mariodivece commented 3 years ago

Right... Let me see how this can be solved. The issue is that the ffmpeg API does not provide access to the input stream but I need to figure out a way to cancel the internal read without causing major inconsistencies in the state of the container for that to work. The cancellation token or the timeout is not really this issue -- it could be added very easily.

simonkolomyjec commented 3 years ago

Thanks Marcio,

I think I have a workaround for my specific use case. It involves reducing the amount of seek (and other) commands and periodically verifying the current playing position. Previously we were calling seek too often to try to "force" it to get to the correct position.

You are correct, the cancellation token did not help - tried implementing it but the command queue was still becoming blocked.

I didn't have a thorough look around all the command queue code so I'm probably missing exactly what is happening in there. Does a seek command "overwrite" any pending seek commands?

stale[bot] commented 3 years ago

This issue has been automatically marked as stale because it has not had recent activity. Thank you for your contributions.

simonkolomyjec commented 2 years ago

Hi Marcio,

I have investigated further on this. While this isn't an complete solution, it seems that the Configuration.GlobalOptions.SeekToAny flag does help in some circumstances.

Some of my test media have very sparse keyframes, and the MediaContainer.StreamSeek() method would effectively return a firstFrame that was way out of range for the desired seek position.

Specifically, changing this line seemed to assist (although would obviously display incomplete frames, which is understandable).

var seekFlags = Configuration.GlobalOptions.SeekToAny ? ffmpeg.AVSEEK_FLAG_ANY : ffmpeg.AVSEEK_FLAG_BACKWARD;

Without the SeekAny flag, the MediaFrame returned by the StreamSeek would effectively mean the TrySignalBlocksAvailable() method would take significant time to locate the desired position, and chew a lot of CPU.

It seemed to me that simply setting the InputContext->seek2any is not enough, the ffmpeg.av_seek_frame() still required the flag to be set in the method call.

Id like to propose this change for pure SeekAny behaviour. It can, in some circumstances, significantly assist in the time to seek, effectively meaning that the public MediaElement.Seek() command would return a lot quicker.

stale[bot] commented 2 years ago

This issue has been automatically marked as stale because it has not had recent activity. Thank you for your contributions.