Search code examples
ioscocoa-touchmpmovieplayercontroller

MPMoviePlayerController: getting a reliable non-skipping currentPlaybackTime


I'm playing a live TV stream in an iOS app I'm developing. I'm using a MPMoviePlayerController and need to create custom controls (play, pause, a progress bar, etc).

While trying to get the currentPlaybackTime I'm noticing some very strange behaviour: it's skipping around like there's no tomorrow.

The duration property is stable at 7170 seconds, it never changes. The playableDuration property grows while the movie is playing, and currentPlaybackTime is constantly jumping around:

playableDuration: 7179.875398
currentPlaybackTime: 7170.181204
--------------
playableDuration: 7187.655396
currentPlaybackTime: 7171.079373
--------------
playableDuration: 7197.208128
currentPlaybackTime: 7172.131998
--------------
playableDuration: 7197.208128
currentPlaybackTime: 7173.139118
--------------
playableDuration: 7197.208128
currentPlaybackTime: 7174.139770
--------------
playableDuration: 7197.208128
currentPlaybackTime: 7175.139666
--------------
playableDuration: 7197.208128
currentPlaybackTime: 7176.140805
--------------
playableDuration: 7197.208128
currentPlaybackTime: 7177.139057
--------------
playableDuration: 7197.208128
currentPlaybackTime: 7178.139128
--------------
playableDuration: 7197.208128
currentPlaybackTime: 7179.139911
--------------
playableDuration: 7199.822063
currentPlaybackTime: 7170.095921
--------------
playableDuration: 7209.959997
currentPlaybackTime: 7171.140078
--------------
playableDuration: 7209.959997
currentPlaybackTime: 7172.140715
--------------
playableDuration: 7209.959997
currentPlaybackTime: 7173.076899
--------------
playableDuration: 7209.959997
currentPlaybackTime: 7174.139497
--------------
playableDuration: 7209.959997
currentPlaybackTime: 7175.139907
--------------
playableDuration: 7209.959997
currentPlaybackTime: 7176.138233
--------------
playableDuration: 7209.959997
currentPlaybackTime: 7177.139523
--------------
playableDuration: 7209.959997
currentPlaybackTime: 7178.135736
--------------
playableDuration: 7209.959997
currentPlaybackTime: 7179.138545
--------------
playableDuration: 7209.959997
currentPlaybackTime: 7180.111102
--------------
playableDuration: 7219.939398
currentPlaybackTime: 7171.132859
--------------
playableDuration: 7219.939398
currentPlaybackTime: 7172.128591
--------------
playableDuration: 7219.939398
currentPlaybackTime: 7173.136137
--------------
playableDuration: 7219.939398
currentPlaybackTime: 7174.135416
--------------
playableDuration: 7219.939398
currentPlaybackTime: 7175.137593
--------------
playableDuration: 7219.939398
currentPlaybackTime: 7176.131573
--------------
playableDuration: 7219.939398
currentPlaybackTime: 7177.137812
--------------
playableDuration: 7219.939398
currentPlaybackTime: 7178.131751
--------------
playableDuration: 7219.939398
currentPlaybackTime: 7179.135922
--------------
playableDuration: 7219.939398
currentPlaybackTime: 7180.139828
--------------
playableDuration: 7229.752731
currentPlaybackTime: 7171.129731
--------------
playableDuration: 7229.752731
currentPlaybackTime: 7172.136576
--------------
playableDuration: 7229.752731
currentPlaybackTime: 7173.132362
--------------
playableDuration: 7229.752731
currentPlaybackTime: 7174.139998

As you can see it jumps from 7179 to 7170, then from 7180 back to 7171, and again and again... In other words, there seems to be no way to get a stable incrementing currentPlaybackTime. This is causing my progress bar to jump around and the progress label (0:04:30 / 2:00:00) to behave very strangely as well.

Has anyone seen this behavior? Is there a workaround?


Solution

  • In the end I've solved this problem by using my own internal property to keep track of the playback time:

    @property (nonatomic) NSTimeInterval previousPlaybackTime;
    @property (nonatomic) NSTimeInterval currentPlaybackTime;
    @property (strong, nonatomic) NSTimer *timer;
    
    - (void)setCurrentPlaybackTime:(NSTimeInterval)currentPlaybackTime {
        BOOL force = (self.moviePlayer.playbackState == MPMoviePlaybackStatePaused);
        [self setCurrentPlaybackTime:currentPlaybackTime force:force];
    }
    
    - (void)setCurrentPlaybackTime:(NSTimeInterval)currentPlaybackTime force:(BOOL)force {
        if (currentPlaybackTime > self.moviePlayer.duration) {
            currentPlaybackTime = self.moviePlayer.duration;
        }
    
        if (!force && currentPlaybackTime < _previousPlaybackTime) {
            // Don't go backwards in time if it's less then 50 seconds (solves the 10 seconds going back problem)
            if (_previousPlaybackTime - currentPlaybackTime < 50) {
                return;
            }
        }
    
        if (!force && _previousPlaybackTime && currentPlaybackTime > _previousPlaybackTime) {
            // Don't go forward in time if it's more then 10 seconds (solves the huge jumps when using the progress bar)
            if (currentPlaybackTime - _previousPlaybackTime > 10) {
                return;
            }
        }
    
        _previousPlaybackTime = currentPlaybackTime;
        _currentPlaybackTime = currentPlaybackTime;
    }
    
    - (void)viewWillAppear:(BOOL)animated {
        [super viewWillAppear:animated];
    
        self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timedJob) userInfo:nil repeats:YES];
        [self.timer fire];
    }
    
    - (void)timedJob {
        if (self.moviePlayer.playbackState == MPMoviePlaybackStateSeekingBackward) return;
        if (self.moviePlayer.playbackState == MPMoviePlaybackStateSeekingForward) return;
    
        self.currentPlaybackTime = self.moviePlayer.currentPlaybackTime;
    }
    

    So in my interface I now use self.currentPlaybackTime which has jump protection built in, so to speak. When the user is using the slider to change the playback position, then you use the force:YES parameter.