-
Notifications
You must be signed in to change notification settings - Fork 9.7k
[video_player] Fixed HLS Streams on iOS #3360
Conversation
65e0999 to
ab6cd05
Compare
packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m
Outdated
Show resolved
Hide resolved
mvanbeusekom
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
|
The pubspec.yaml file needs to be updated to the version: |
cyanglaz
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you so much for the PR and fix. I think the idea is generally ok but I left some comments and questions :)
packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m
Outdated
Show resolved
Hide resolved
| } | ||
|
|
||
| - (int64_t)duration { | ||
| // When CMTIME_IS_INDEFINITE return a value that matches TIME_UNSET from ExoPlayer2 on Android. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the significants to make it matches android? It seems like a high maintenance code as we don't have a test to cover it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem here to me really stems from:
| int64_t FLTCMTimeToMillis(CMTime time) { |
The conversion to Milliseconds from CMTime removes some of the functionality that CMTime provides. As far as I can tell FLTCMTimeToMillis is already an attempt to mimic the timebase found in Exoplayer2 to make it easier to digest on the flutter side. But it is an incomplete implementation since the timebase used in Exoplayer2 uses constants to identify special cases. On iOS these special cases (CMTIME_IS_INDEFINITE being one of them) are part of the CMTime class.
I think there has to be a way to bubble up the complexity of CMTime to the flutter side and matching Exoplayer2's timebase for now is the best way to handle that. This way at least within the flutter code everything matches.
This PR heads in that direction but I tried to limit the scope as much as possible to fixing the issue - since it is a problem I have in a production app and I'd like to stop using dependency_overrides as soon as possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, in this case, can we refactor the if ([self isDurationIndefinite]) into FLTCMTimeToMillis?
int64_t FLTCMTimeToMillis(CMTime time) {
if (CMTIME_IS_INDEFINITE(time)) { return TIME_UNSET; }
if (time.timescale == 0) { return 0; }
return time.value * 1000 / time.timescale;
}
And I think FLTCMTimeToMillis should really be a static function as it is only accessed in this file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've done the refactor but I'm a bit at a loss at how to create a test for this. Should I create a XCTest to test passing variations of CMTime to FLTCMTimeToMillis and checking their output? Or should I make an integration test - in which case wouldn't I need an example HLS video hosted somewhere like https://flutter.github.io/assets-for-api-docs/assets/videos/?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added a test
cyanglaz
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the explanation. I have left a comment. Can we also add tests for this?
| } | ||
|
|
||
| - (int64_t)duration { | ||
| // When CMTIME_IS_INDEFINITE return a value that matches TIME_UNSET from ExoPlayer2 on Android. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, in this case, can we refactor the if ([self isDurationIndefinite]) into FLTCMTimeToMillis?
int64_t FLTCMTimeToMillis(CMTime time) {
if (CMTIME_IS_INDEFINITE(time)) { return TIME_UNSET; }
if (time.timescale == 0) { return 0; }
return time.value * 1000 / time.timescale;
}
And I think FLTCMTimeToMillis should really be a static function as it is only accessed in this file.
|
hey guys, any news about this? |
|
Any news on this? We are currently investigating Flutter as a framework for a new streaming app, but we are getting more and more unsure as to if this is feasible at all since a lot of the packages that are published as working actually have critial bugs like this that are not fixed even though they seem to have working PRs ready to merge... |
|
I'm landing this ASAP. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
@nathanaelneveux with the URL that you added (link), the I've used the Shaka demo to add both m3u8s, and they both seem to represent "live" video. Can you please take a look to find an explanation of why one live stream works but the other doesn't? Is this a quirk of m3u8s? a bug in the iOS implementation that needs to be patched? a little bit of both? If so, I'd keep both stream URLs in the tests, to ensure this doesn't break in the future. Thanks for your patience! |
@ditman I'm having trouble with consistent playback of the https://storage.googleapis.com/shaka-live-assets/player-source.m3u8 in safari on MacOS using both the built in safari video player and the Shaka demo. I'm getting a lot of 404 errors for the video and audio segments - I wonder if this is causing the test to not pass? |
|
@ditman I've made a minimal test project to compare playing back https://storage.googleapis.com/shaka-live-assets/player-source.m3u8 with this PR vs without it. With this minimal project I am able to confirm that without the changes in this PR https://storage.googleapis.com/shaka-live-assets/player-source.m3u8 does not playback. Overriding video_player with this PR (as outlined in a previous comment) does cause the live video to playback. Minimal Test Project pubspec.yaml - Toggle the "dependency_overrides" block to test stable video_player vs. this PR **All other files are Flutter default template |
|
There is something not quite right with https://storage.googleapis.com/shaka-live-assets/player-source.m3u8 - running the playlist through Apple's mediastreamvalidator (guide) produces quite a few errors. In contrast running https://cph-p2p-msl.akamaized.net/hls/live/2000341/test/master.m3u8 does not seem to produce errors. I'm not familiar enough with the HLS specification to provide much more info than that. |
|
@nathanaelneveux thanks for diving deeper. I'll rebase the branch and revert the URL so this is mergeable again. |
|
I think this 87da9ab was the stuff that was breaking our tests. @nathanaelneveux, can you confirm that my change is valid? (I tried to use the google-hosted m3u8 and I started getting errors consistent with what you mention here, lots of stream not found errors which were in fact breaking tests too. See this run.) |
Refactor `FLTCMTimeToMillis` to support indefinite streams. Co-authored-by: Mike Diarmid <mike.diarmid@gmail.com>
Refactor `FLTCMTimeToMillis` to support indefinite streams. Co-authored-by: Mike Diarmid <mike.diarmid@gmail.com>
Description
Many HLS streams on iOS were being treated as incomplete files because their duration has a CMTime of CMTIME_IS_INDEFINITE which was being simplified to '0'. This PR replaces durations of CMTIME_IS_INDEFINITE with a constant borrowed from Exoplayer2 on Android - TIME_UNSET - which is similarly used for an unknown duration time.
Future iterations may want to deal with TIME_UNSET to provide better UI behavior.
Related Issues
Fixes #48670
Checklist
Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes (
[x]). This will ensure a smooth and quick review process.///).flutter analyze) does not report any problems on my PR.Breaking Change
Does your PR require plugin users to manually update their apps to accommodate your change?