Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scrubber (UISlider) in AVPlayer?

When you play remote video via AVPlayer and start rewinding, the scrubber is buggy.

I'm doing a player based on this Apple example.

How to implement it smoothly?

Code from my project follows - https://github.com/nullproduction/Player

- (void)initScrubberTimer
{
    double interval = .1f;

    CMTime playerDuration = [self playerItemDuration];
    if (CMTIME_IS_INVALID(playerDuration))
    {
        return;
    }
    double duration = CMTimeGetSeconds(playerDuration);
    if (isfinite(duration))
    {
        CGFloat width = CGRectGetWidth([scrubberSlider bounds]);
        interval = 0.5f * duration / width;
    }

    __weak id weakSelf = self;
    CMTime intervalSeconds = CMTimeMakeWithSeconds(interval, NSEC_PER_SEC);
    mTimeObserver = [self.player addPeriodicTimeObserverForInterval:intervalSeconds
                                                              queue:dispatch_get_main_queue()
                                                         usingBlock:^(CMTime time) {
                                                             [weakSelf syncScrubber];
                                                         }];

}


- (void)syncScrubber
{
    CMTime playerDuration = [self playerItemDuration];
    if (CMTIME_IS_INVALID(playerDuration))
    {
        scrubberSlider.minimumValue = 0.0;
        return;
    }

    double duration = CMTimeGetSeconds(playerDuration);
    if (isfinite(duration))
    {
        float minValue = [scrubberSlider minimumValue];
        float maxValue = [scrubberSlider maximumValue];
        double time = CMTimeGetSeconds([self.player currentTime]);

        [scrubberSlider setValue:(maxValue - minValue) * time / duration + minValue];
    }
}

- (IBAction)beginScrubbing:(id)sender
{
    mRestoreAfterScrubbingRate = [self.player rate];
    [self.player setRate:0.f];

    [self removePlayerTimeObserver];
}


- (IBAction)scrub:(id)sender
{
    if ([sender isKindOfClass:[UISlider class]])
    {
        UISlider* slider = sender;

        CMTime playerDuration = [self playerItemDuration];
        if (CMTIME_IS_INVALID(playerDuration))
        {
            return;
        }

        double duration = CMTimeGetSeconds(playerDuration);
        if (isfinite(duration))
        {
            float minValue = [slider minimumValue];
            float maxValue = [slider maximumValue];
            float value = [slider value];

            double time = duration * (value - minValue) / (maxValue - minValue);

            [self.player seekToTime:CMTimeMakeWithSeconds(time, NSEC_PER_SEC)];
        }
    }
}

- (IBAction)endScrubbing:(id)sender
{
    if (!mTimeObserver)
    {
        CMTime playerDuration = [self playerItemDuration];
        if (CMTIME_IS_INVALID(playerDuration))
        {
            return;
        }

        double duration = CMTimeGetSeconds(playerDuration);
        if (isfinite(duration))
        {
            CGFloat width = CGRectGetWidth([scrubberSlider bounds]);
            double tolerance = 0.5f * duration / width;

            __weak id weakSelf = self;
            CMTime intervalSeconds = CMTimeMakeWithSeconds(tolerance, NSEC_PER_SEC);
            mTimeObserver = [self.player addPeriodicTimeObserverForInterval:intervalSeconds
                                                                      queue:dispatch_get_main_queue()
                                                                 usingBlock: ^(CMTime time) {
                                                                     [weakSelf syncScrubber];
                                                                 }];
        }
    }

    if (mRestoreAfterScrubbingRate)
    {
        [self.player setRate:mRestoreAfterScrubbingRate];
        mRestoreAfterScrubbingRate = 0.f;
    }
}
like image 771
nullproduction Avatar asked Nov 18 '13 15:11

nullproduction


1 Answers

I guess the problem is, that your scrubber is still updating from the video, while you are using the seekbar. Implement it in a way, that you pause the player during the scrubbing, and you won't have this bug anymore. Checkout my solution:

The function to update your player:

- (IBAction)seekbarAction:(UISlider *)sender {
    CMTime videoLength = playerItem1.duration;  //gets the video duration
    float videoLengthInSeconds = videoLength.value/videoLength.timescale; //transfers the CMTime duration into seconds

    [player1 seekToTime:CMTimeMakeWithSeconds(videoLengthInSeconds*sender.value, 1)];
}

And another seekbar action with "Touch down" in order to pause the video:

- (IBAction)pauseSeek:(id)sender {
    [player1 pause];
}

And another seekbar action with "Touch up" in order to resume the video when you release the scrubber. Hope this helps.

like image 51
kchromik Avatar answered Oct 05 '22 18:10

kchromik



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!