Seeking / Playback Rate
Seeking
All time operations are done in seconds using doubles:
// After the video is loaded and metadata event fires you can use these:
// Get the media duration in seconds
double duration = mediaPlayer.Info.GetDuration();
// Get current time in seconds
double time = mediaPlayer.Control.GetCurrentTime();
// Get the ranges of time that can be seeked between
TimeRanges seekRanges = mediaPlayer.Control.GetSeekableTimes();
// Seek to 24 seconds
mediaPlayer.Control.Seek(24.0);
// Seek to nearest keyframe at 24 seconds
mediaPlayer.Control.SeekFast(24.0);
// Seek to closest keyframe allowing keyframe to be either ahead, behind or on both sides of the desired time
// This is only currently available on macOS, iOS, iPadOS and tvOS
mediaPlayer.Control.SeekWithTolerance(24.0, 5.0, 0.0);
// Seek to the current 'live' time for a live stream
TimeRange seekableRange = Helper.GetTimelineRange(mediaPlayer.Info.GetDuration(), mediaPlayer.Control.GetSeekableTimes());
mediaPlayer.Control.Seek(seekableRange.endTime);
Media that has a known constant frame rate can be seeked using frames:
// After the video is loaded and metadata event fires you can use these:
// Get the media duration in frames
int durationFrames = mediaPlayer.Info.GetDurationFrames();
// Get the highest frame number you can seek to (the same as durationFrames-1)
int maxFrame = mediaPlayer.Info.GetMaxFrameNumber();
// Seek to frame 60
mediaPlayer.Control.SeekToFrame(60);
// Seek back 10 frames
mediaPlayer.Control.SeekToFrameRelative(-10.0);
// Get current time in frames
int frame = mediaPlayer.Control.GetCurrentTimeFrames();
If the frame rate can not be determined (eg in some HLS media the frame rate returns zero) then you can still use the frame-based time methods by manually supplying the frame rate as an optional final parameter to the above methods:
// After the video is loaded and metadata event fires you can use these:
// Get the media duration in frames
int durationFrames = mediaPlayer.Info.GetDurationFrames(30f);
// Seek to frame 60
mediaPlayer.Control.SeekToFrame(60, 30f);
// Get current time in frames
int frame = mediaPlayer.Control.GetCurrentTimeFrames(30f);
There are some platform differences for seeking behaviour:
Fast Approximate Keyframe Seeking | Slow Accurate Frame Seeking | |
---|---|---|
Windows (WinRT / Media Foundation) | ✓ | ✓ |
Windows (DirectShow) | ✓ | Depends on the codec |
Android (ExoPlayer) | ✓ | ✓ |
Android (MediaPlayer) | ✓ | API 26 and above |
macOS | ✓ | ✓ |
iOS/iPadOS/tvOS | ✓ | ✓ |
WebGL | ✓ | Varies |
Playback Rate
Generally we recommend these rates:
0.25, 0.5, 1.0, 1.25, 1.5, 1.75, 2.0
Going up to 4.0 might be possible depending on your platform, machine specs and the codec used. Increasing playback rate usually places more demand on the video decoder and also on the disk/network source, so these limit how high you can set the playback rate.
Using negative values isn’t generally recommended as it isn’t as well supported, but if you do have to use a negative rate then also try keeping the numbers small such as:
-0.25, -0.5, -1.0
Audio also may or may not play when changing the playback rate - this depends on the platform (see table below).
One safe alternative to adjusting rate is to pause the video and fast seek to simulate a change in playback rate. This approach would work on all platforms.
Video encoding can also help the performance of a change in playback rate. Videos with more key-frames (or ideally all key-frames) and with less complex encoding (eg no B frames, CABAC disabled etc) will work better. Alternatively a fast key-frame-only codec could be used, such as Hap.
Scripting playback rate:
// After the video is loaded and metadata event fires you can use these:
// Get the current playback rate
float rate = mediaPlayer.PlaybackRate;
// Set the current playback rate
mediaPlayer.PlaybackRate = rate * 2f;
There are some platform differences for playback-rate behaviour:
Adjust Playback Rate | Negative Rates | Audio Plays | |
---|---|---|---|
Windows (WinRT / Media Foundation) | ✓ | ✓ | Depends on codec |
Windows (DirectShow) | ✓ | . | . |
Android (ExoPlayer) | ✓ | ? | ✓ |
Android (MediaPlayer) | API 23 and above | ? | ✓ |
macOS | ✓ | Depends on media source | ✓ |
iOS/iPadOS/tvOS | ✓ | Depends on media source | ✓ |
WebGL | ✓ | . | Depends on browser |
macOS/iOS/iPadOS/tvOS Playback Rate
By default playback rates higher than 2.0 causes the player to only show key-frames. If you need to play back a video at rates above 2.0 then you can adjust the "Max Playback Rate"
slider in the platform specific options to set a higher threshold. Should your playback rate exceed this new maximum rate then the player will drop back to showing just key-frames. This option must be configured prior to creating the player and cannot be changed after the media has been opened.
Optimal Encoding
Most videos are optimally encoded for the typical use case: normal forward playback with approximate seeking.
If you want to start changing the playback rate, play in reverse, allow fast scrubbing, or have fast frame accurate seeking then you may run into issues where the playback becomes extremely slow or the seeking is not accurate. There are several reasons for this, but it mostly is related to how the video is encoded and specifically the key-frame distribution. There are also some platform differences to consider.
Codecs such as H.264 and H.265 generally compress video frames so that they depend on data included with previously decoded frames. These are called P and B frames and seeking to one of these is computationally expensive, as in order to display them the decoder must first decode the other frames that they depend on. The other type of frame is called a key-frame or I-frame and these frames can be decoded immediately because they don’t depend on any other frames. Compressing using P And B frames is known as temporal compression and isn’t ideal for accurate random seeking or playback rate changes.
For the best results you would encode your video with only key-frames, as then you can seek accurately and quickly anywhere in the video. This is possible, but increases the file size dramatically. Using FFMPEG you can encode a video to use key-frames only using the “-g 1” option. Another option would be to use a codec that only supports key-frames, such as Hap or ProRes - but again these result in large file sizes and limited GPU decoding capabilities.
In most codec with temporal compression the key-frames are spaced every 250 frames. Some platforms can only seek to the key-frames (see table above), while others can do accurate seeking but this can be very slow if the distances between key-frames is too large. Try reducing the key-frame distance for faster seeking. You can also reduce the decoder complexity by encoding with a fastdecode
tuning option.
Here is an example FFMPEG command to encode using H.264 codec with all keyframes for very fast seeking:
ffmpeg -hide_banner -y -i input.mp4 -pix_fmt yuv420p -c:v libx264 -crf 18 -tune fastdecode -x264-params "keyint=1" output-h264.mp4
See the section about encoding for further information.