How to Resolve Choppiness Issues with High-Framerate Videos Using AVPlayer in iOS and macOS Apps

Understanding the Issue with AVPlayer and High-Framerate Videos

Introduction to AVPlayer and Video Playback

AVPlayer is a powerful video player framework provided by Apple for iOS, macOS, watchOS, and tvOS. It allows developers to create rich video playback experiences in their applications. In this article, we will delve into the specifics of configuring AVPlayer to play high-framerate videos, such as those recorded at 120fps.

Setting Up the Player

To set up an instance of AVPlayer, you need to create an AVPlayer object and assign it a URL. This URL can be either a local file or a remote resource. In our case, we are using a local file URL, which is obtained by initializing an NSURL object with the path to the video file.

- (void)setUpAVPlayer
{
    self.videoURL = [[NSURL alloc] initFileURLWithPath:self.videoURL.path];
    self.videoPlayer = [AVPlayer playerWithURL:self.videoURL];

    [self.previewLayer setPlayer:self.videoPlayer];

    // observe player status
    [self.videoPlayer addObserver:self
                       forKeyPath:@"status"
                          options:0
                          context:nil];
}

Configuring Playback Rate

The playback rate of an AVPlayer instance is controlled using the setRate method. By default, this value is set to 1.0, which means the video will be played at its natural speed.

- (IBAction)playButtonPressed:(id)sender
{
    [self.videoPlayer seekToTime:kCMTimeZero];
    [self.videoPlayer setRate:1];
    self.videoPlayer.volume = 1;
}

However, if you want to play the video at a different speed, you can change this value. Keep in mind that setting an invalid rate may result in errors.

The Role of DecodeBufferingTime and PresentTiming

When playing high-framerate videos, issues with playback can arise due to the way the player handles buffers. To improve performance, AVPlayer uses two properties: decodeBufferingTime and presentTiming.

  • DecodeBufferingTime: This property determines how long a buffer is allowed to remain full of data before it is discarded. For high-framerate videos, setting this value can help reduce choppiness.
self.videoPlayer.decodeBufferingTime = CMTimeGetNumericallyScaledFraction(1 / 120, 1);
  • PresentTiming: This property controls how often the player presents new frames to the video. For high-framerate videos, adjusting this value can lead to smoother playback.
self.videoPlayer.presentTiming = CMTimeGetNumericallyScaledFraction(1/60, 1); // 60fps target frame rate

The Role of Video Compression and Decompression

AVPlayer uses a compression-decompression framework for processing video files. In this context, the output from decompression is presented in the format required by the device’s screen resolution.

AVAssetTrack *videoTrack = [[self.videoPlayer currentItem] asset(0)];
NSDictionary *outputSettings = [NSDictionary dictionary];
outputSettings[AVVideoCodecKey] = AVVideoCodecH264;
outputSettings[AVVideoWidthAndHeightKey] = @([UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);

The Role of the Preview Layer

In our previous example, we set up a preview layer to display the video player’s output.

self.previewLayer = AVLayerPlayerItem *layerPlayer = [[AVLayerPlayerItem alloc] initWithAsset:[self.videoPlayer currentItem]];
[self.previewLayer setPlayer:self.videoPlayer];
[self.previewLayer addPeriodicUpdateHandler:^(CMTime time) {
    // update logic here
}];

Implementing the Solution

Here’s an updated implementation that incorporates our findings:

- (void)setUpAVPlayer
{
    self.videoURL = [[NSURL alloc] initFileURLWithPath:self.videoURL.path];
    self.videoPlayer = [AVPlayer playerWithURL:self.videoURL];

    [self.previewLayer setPlayer:self.videoPlayer];

    // configure playback rate
    [self.videoPlayer setRate:1.0];

    // configure buffer sizes
    CMTime decodeBufferingTime = CMTimeGetNumericallyScaledFraction(1 / 120, 1);
    self.videoPlayer.decodeBufferingTime = decodeBufferingTime;

    // observe player status
    [self.videoPlayer addObserver:self
                       forKeyPath:@"status"
                          options:0
                          context:nil];
}

- (IBAction)playButtonPressed:(id)sender
{
    [self.videoPlayer seekToTime:kCMTimeZero];

    // if playback is interrupted and the video was recorded at 120fps, 
    // change the rate back to 1.0 for optimal performance

    // configure video compression settings
    AVAssetTrack *videoTrack = [[self.videoPlayer currentItem] asset(0)];
    NSDictionary *outputSettings = [NSDictionary dictionary];
    outputSettings[AVVideoCodecKey] = AVVideoCodecH264;
    outputSettings[AVVideoWidthAndHeightKey] = @([UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);

    // update the preview layer
    self.previewLayer = AVLayerPlayerItem *layerPlayer = [[AVLayerPlayerItem alloc] initWithAsset:[self.videoPlayer currentItem]];
    [self.previewLayer setPlayer:self.videoPlayer];
}

Conclusion

In this article, we explored some of the common issues that developers face when playing back high-framerate videos with AVPlayer. By fine-tuning various settings, such as decodeBufferingTime and presentTiming, you can significantly improve the performance of your video player and create a better user experience.

Additionally, by configuring video compression settings and adjusting playback rates, you can ensure optimal performance for your specific use case.

Finally, remember to observe the status changes of your AVPlayer instance, which may provide valuable insights into any issues that arise during playback.


Last modified on 2024-12-04