youtube / youtube-ios-player-helper

Lightweight helper library that allows iOS developers to add inline playback of YouTube videos through a WebView
Other
1.65k stars 683 forks source link

Duration for live videos is always 0 #414

Open hipwelljo opened 4 years ago

hipwelljo commented 4 years ago

For some reason, duration is always returned as 0 when playing a live video, but it works with regular on demand videos.

With https://www.youtube.com/watch?v=BnloneYBBKc for example, the following code always prints time: 33.052174 duration: 0.0 where time increases over time but duration never changes:

func playerView(_ playerView: YTPlayerView, didPlayTime playTime: Float) {
    playerView.duration { (duration, error) in
        print("time: \(playTime) duration: \(duration)")
    }
}

This is reproducible in the sample project. Here progress turns out to be +Inf because of divide by 0:

- (void)playerView:(YTPlayerView *)playerView didPlayTime:(float)playTime {
  [self.playerView duration:^(double result, NSError * _Nullable error) {
    float progress = playTime/result;
    [self.slider setValue:progress];
  }];
}

I can see why - evaluating player.getDuration(); returns a NULL result but also NULL error, therefore completionHandler(0, nil); is always called.

- (void)duration:(_Nullable YTDoubleCompletionHandler)completionHandler {
  [self evaluateJavaScript:@"player.getDuration();"
         completionHandler:^(id  _Nullable result, NSError * _Nullable error) {
    if (!completionHandler) {
      return;
    }
    if (error) {
      completionHandler(-1, error);
      return;
    }
    if (!result || ![result isKindOfClass:[NSNumber class]]) {
      completionHandler(0, nil); //<----- always calls this
      return;
    }
    completionHandler([result doubleValue], nil);
  }];
}

I confirmed this behavior with version 1.0.2 (and 0.1.6) using iOS 14.

Interestingly, we are getting a duration with that same video on Android using a custom IFrame API implementation:

youtube_iframe.html.zip

I am wondering if it might be failing to get the metadata? The docs state

Note that getDuration() will return 0 until the video's metadata is loaded, which normally happens just after the video starts playing.

todd-patterson commented 4 years ago

Hi hipwelljo, This problem is due to an issue with live streaming on Apple devices. This impacts the underlying iFrame API library and is not a direct problem with the ios-player-helper library. As you correctly noted, the issue does not effect Android devices.

Unfortunately this is a long-standing problem and we don't currently have a solution. The only work-around available is a bit heavy, but should get the job done if you really need the functionality: 1) Retrieve "liveStreamingDetails.actualStartTime" from the streaming API https://developers.google.com/youtube/v3/docs/videos#properties 2) Use this value to compute duration by comparing to local system time on the device

Note that we haven't tested this, but in theory it should work.

For more information about "getDuration" with live videos, refer to the second paragraph of text ("If the currently playing video is a live event ...") in the iFrame API docs that you linked.

hipwelljo commented 4 years ago

Thank you for this information, that is insightful. This seems to impact the position as well, is that right? I'm seeing when I start playing a live video playerView:didPlayTime: is called with ~20 for playTime and it increases over time, but that seemingly doesn't indicate where they are in the live stream at all. Is that too a known issue?