AVPlayer seekToTime only generates frame every second - ios

how can I make AVPlayerLayer display the exact video's frame in method seekToTime.
Now [AVPlayer seekToTime:CMTimeMakeWithSeconds(CMTimeGetSeconds(A, B))], the AVPlayerLayer only displays the frame at every 1-second change for example 1.50-2.50-3.50. I want it to display frame at 4.45 second for example. Is it possible?

You can use [AVPlayer seekToTime: toleranceBefore: toleranceAfter:] to get random media access with higher precision.
To get the highest precision possible, pass kCMTimeZero as argument for both tolerances. Note that this might add noticeable delay during seeks.

Related

AVPlayerItem canStepBackward/canPlayReverse Conditions

I'm trying to scrub through a video frame by frame. For this I found multiple options:
Encode the video for every frame to be a keyframe
[avplayer seekToTime:time toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero]
stepForward/stepBackward
Re-encoding the video is not an option, so I have to stick to the other two. I stumbled upon two different problems:
Jumping between keyframes
long loading times but precise
Both problems are a no-go.
In AVPlayerItem I've found canStepBackwardand canPlayReverse but I can't find anywhere the conditions for these methods to return true.
Does anyone know these conditions?
Thanks
Try these:
in .h file
AVPlayer *avPlayer;
in .m file
[avPlayer.currentItem canPlayReverse];
[avPlayer.currentItem canStepBackward];
These condition being the BOOL value are already set to 1.0 i.e. they are already true. You just have to provide these values to your AVplayerItem object.
You can also try setting the player rate:
avPlayer.rate=-1.0;(for negative playing)
avplayer.rate=1.0; (for positive playing)

iOS Change keyframes in video

I'm trying to scrub through videos in really small values (maybe even less than milliseconds). To get to the next frame I use [AVPlayer seekToTime:time toleranceBefore: kCMTimeZero toleranceAfter:kCMTimeZero] which gives me the correct position. The problem is, that it takes too long to scrub backward.
I know the reasons are the keyframes and the player has to start searching from the nearest keyframe to reach the position.
Is there any possibility to reencode the video to have more keyframes, or entirely exist out of keyframes?
Thanks
Yes, you can encode video to contain all keyframes, but the file will become MUCH larger. It will also take time/CPU to do it. In addition at 30 frames per second there is only one frame every 33 milliseconds, so sub millisecond resolution doesn't make any sense.

Synchronize multiple AVPlayers

I am trying to find a solution for a problem I got. I have 5 UIViews, which are all at the same position. Each UIView holds an AVPlayer with different videos. To be more precise they are all the same video, but encoded with different playback speed.
Video 1x speed
Video 4x speed
Video 8x speed
Video 16x speed
Video 24x speed
By default the video 1 is visible and playing, but I should be able to switch between the videos, but the switching shouldn't be visible for the user, therefore, I need to keep them synchronized. So If I am watching video 1 and switch to video 2, then video 2 should play exactly at the position, where video 1 stopped.
The sense is that it should look like, that the video is speeding up after an action, eg. a flick gesture.
I hope I described my issue good enough and I am very thankful for any suggestion.
I am using in the end an observer, which takes snapshots of the currentTime each 5 seconds, and calls all the other AVPLayer with the seekToTime method. This works fine to keep them synchronized. I just needed to adapt the CMTime for each player with different speed. As an example I got here 4x video:
videoPosition = player1.currentTime; //gets the video duration
videoPositionInSeconds = (Float64) videoPosition.value/videoPosition.timescale; //transfers the CMTime duration into seconds
[player2 seekToTime: CMTimeMakeWithSeconds(videoPositionInSeconds/4.0, player1.currentItem.asset.duration.timescale) toleranceBefore: kCMTimeZero toleranceAfter: kCMTimeZero];
Hope this helps.

Video plays very fast some times in avplayer

In Avplayer, we apply the rate for video slow motion and negative frame also. if video ends and apply the rate to avplayer the video plays very fastly instead of given rate. i was struggled more than 2 days. Thanks
Try to call [avPlayer setRate:0.3] and [avPlayer play] at the same time, so the second call override the first call, which sets the rate to be 0 (normal).
The method setRate: already set the rate for the player and play the video already

AVPlayerItem's stepByCount only smooth for forward, but choppy for backwards?

I have implemented AVPlayerItem's stepByCount method to manually go through a video frame by frame. Here's how it looks like for forward 1 step.
AVPlayer *player = [AVPlayer playerWithURL:url];
[player.currentItem stepByCount:1];
And backward 1 step
AVPlayer *player = [AVPlayer playerWithURL:url];
[player.currentItem stepByCount:-1];
The forward 1 step (going forward in time frame by frame) works well. However when I try to go backward frame by frame, it's not as smooth as the forward step. Am I missing something? Or is this because of the way videos are encoded--it's meant to be viewed forward but not backwards--inherently?
You can check to see if the AVPlayerItem supports stepping:
if (playerItem.canStepBackward)
{
[playerItem stepByCount:numberOfFrames];
}
else
{
// Do our best here...
[player seekToTime:CMTimeSubtract(player.currentTime, CMTimeMake(ABS(numberOfFrames)*1000, compositionFPS*1000)) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
}
Not sure what encoding format your video is in but compressed videos are encoded with keyframes. These occur either at regular intervals (like once a second) or when the scene changes (like when a cut is made from close up to wide shot). The data between keyframes just describes the cumulative changes since the last keyframe - only pixels that change are noted. As you have deduced, this means that a video in reverse is not how it is designed to be played. When you skip backwards the encoder needs to skip back to the previous keyframe (which could be hundreds or even thousands of frames before your current position) then recreate all the frames up to your required frame before actually presenting you with the composited frame. The only way around this in your situation is to either use an already flattened video format or to flatten it before scanning. You might want to take a look at http://opencv.org/ if you want to get into decompression.

Resources