-
Notifications
You must be signed in to change notification settings - Fork 3.3k
[video_player] fix: add missing isPlaybackLikelyToKeepUp check. #3826
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
It looks like this pull request may not have tests. Please make sure to add tests before merging. If you need an exemption to this rule, contact Hixie on the #hackers channel in Chat (don't just cc him here, he won't see it! He's on Discord!). If you are not sure if you need tests, consider this rule of thumb: the purpose of a test is to make sure someone doesn't accidentally revert the fix. Ask yourself, is there anything in your PR that you feel it is important we not accidentally revert back to how it was before your fix? Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing. |
58347f9
to
92a6dce
Compare
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.
Seems you're still working on this, but I'll just reiterate what the bot already said. You'll need to add some tests to this pr before we can merge it.
Hi @tarrinneal , I've added the necessary test case and all tests have passed, both for replay and livestream. |
d11eec0
to
20dc1c6
Compare
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.
Seems good to me, I'll let @hellohuanlin do final review.
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'm not sure if isPlaybackLikelyToKeepUp
is a good indicator for bufferingStart
and bufferingEnd
events. From the doc this flag is just a "prediction", which may not be correct.
@@ -319,11 +319,17 @@ - (void)observeValueForKeyPath:(NSString *)path | |||
} | |||
} else if (context == playbackBufferEmptyContext) { | |||
if (_eventSink != nil) { | |||
_eventSink(@{@"event" : @"bufferingStart"}); | |||
if ([[_player currentItem] isPlaybackLikelyToKeepUp]) { |
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.
did you hit this call? When playback buffer is empty, is it possible for isPlaybackLikelyToKeepUp
to be true at all?
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 have tested this with below code and through OBS livestreaming, and have found that it is possible for both the 'isPlaybackLikelyToKeepUp' flag to be true and for the playback buffer to be empty at the same time while watching a livestream.
import AVKit
import UIKit
class ViewController: UIViewController {
var isPlaybackBufferEmptyListener: NSKeyValueObservation?
var isPlaybackBufferFullListener: NSKeyValueObservation?
var isPlaybackLikelyToKeepUpListener: NSKeyValueObservation?
@IBAction func onClickPlay(_ sender: Any) {
/// Test with a live stream URL, it's m3u8 has no end tag
guard let url = URL(string: "Test with a live stream URL") else { return }
// guard let url = URL(string: "https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8") else { return }
let item = AVPlayerItem(url: url)
isPlaybackBufferEmptyListener?.invalidate()
isPlaybackBufferEmptyListener = item.observe(\.isPlaybackBufferEmpty, options: [.initial, .new]) { _, change in
guard let new = change.newValue else { return }
print("isPlaybackBufferEmpty ---> \(new)")
}
isPlaybackBufferFullListener?.invalidate()
isPlaybackBufferFullListener = item.observe(\.isPlaybackBufferFull, options: [.initial, .new]) { _, change in
guard let new = change.newValue else { return }
print("isPlaybackBufferFull ---> \(new)")
}
isPlaybackLikelyToKeepUpListener?.invalidate()
isPlaybackLikelyToKeepUpListener = item.observe(\.isPlaybackLikelyToKeepUp, options: [.initial, .new]) { _, change in
guard let new = change.newValue else { return }
print("isPlaybackLikelyToKeepUp ---> \(new)")
}
let player = AVPlayer(playerItem: item)
let controller = AVPlayerViewController()
controller.player = player
present(controller, animated: true) {
player.play()
}
}
}
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.
Could you post your log here? Thanks!
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.
Yes. the the log,
isPlaybackBufferFull ---> false
isPlaybackLikelyToKeepUp ---> false
isPlaybackBufferFull ---> true
isPlaybackBufferFull ---> false
isPlaybackLikelyToKeepUp ---> true
>>>>>>> SEEK BEGIN >>>>>>>
isPlaybackLikelyToKeepUp ---> false
<<<<<<<< SEEK END <<<<<<<<
isPlaybackLikelyToKeepUp ---> true
>>>>>>> SEEK BEGIN >>>>>>>
isPlaybackLikelyToKeepUp ---> false
<<<<<<<< SEEK END <<<<<<<<
isPlaybackLikelyToKeepUp ---> true
Based on the aforementioned logs, it is evident that the isPlaybackBufferFull
consistently remains false, while the isPlaybackLikelyToKeepUp
can be either true
or false
during seeking. I have uploaded the testing project to AVPlayer_livestream. Please feel free to run it and review the provided logs.
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 was asking the opposite situation - isPlaybackBufferEmpty
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.
Sorry, @hellohuanlin, I realized that I missed including the first line of the log. Here's the revised version:
isPlaybackBufferEmpty ---> true
isPlaybackBufferFull ---> false
isPlaybackLikelyToKeepUp ---> false
isPlaybackBufferFull ---> true
isPlaybackBufferFull ---> false
isPlaybackLikelyToKeepUp ---> true
>>>>>>> SEEK BEGIN >>>>>>>
isPlaybackLikelyToKeepUp ---> false
<<<<<<<< SEEK END <<<<<<<<
isPlaybackLikelyToKeepUp ---> true
Please note that the isPlaybackBufferEmpty
always remains true, while the isPlaybackLikelyToKeepUp
can be either true or false during seeking.
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.
this is surprising. i'd expect isPlaybackBufferEmpty to be false most of the time. Do you have some insight on this?
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.
It seems like there is a bug in AVFoundation
related to the isPlaybackBufferEmpty
key-value observing (KVO) not being triggered when its value changes. As a workaround for now, we may check the isPlaybackLikelyToKeepUp
property within the playbackBufferEmptyContext
and playbackBufferFullContext
. refer log:
[KVO] isPlaybackBufferEmpty ---> true
[KVO] isPlaybackBufferFull ---> false
[KVO] isPlaybackLikelyToKeepUp ---> false (crrent isPlaybackBufferEmpty ---> true)
2023-05-25 13:26:31.528310+0900 Lab_AVPlayer_livestream[1757:465625] Metal API Validation Enabled
[KVO] isPlaybackBufferFull ---> true
[KVO] isPlaybackBufferFull ---> false
[KVO] isPlaybackLikelyToKeepUp ---> true (crrent isPlaybackBufferEmpty ---> false)
I tested this behavior on iPadOS 16.5 using a real device. You can also check the provided demo AVPlayer_livestream for further reference.
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.
This behavior needs to be heavily documented in our code (since it's not mentioned in apple's doc), otherwise it's too hard to understand in the future. Could you add some comments explaining why we need this workaround?
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.
Hello @hellohuanlin Before I proceed with adding comments about the necessity of the isPlaybackLikelyToKeepUp
check when observing isPlaybackBufferFull
and isPlaybackBufferEmpty
using KVO, I would like to seek clarification on the exact meaning of isBuffering
within the video_player module. I have raised this question in detail here.
Based on my understanding, the evaluation of isBuffering
relies on the Key-Value Observing (KVO) of isPlaybackBufferFull
, isPlaybackBufferEmpty
, and isPlaybackLikelyToKeepUp
.
} | ||
} else if (context == playbackBufferFullContext) { | ||
if (_eventSink != nil) { | ||
_eventSink(@{@"event" : @"bufferingEnd"}); | ||
if ([[_player currentItem] isPlaybackLikelyToKeepUp]) { |
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.
from the doc, when buffer is full, it's still possible for isPlaybackLikelyToKeepUp
to return NO. Do we want to emit bufferingStart
then?
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.
Yes, that is correct. Since the KVO code related to 'playbackBufferEmptyContext' is already responsible for emitting the 'bufferingStart' event, there is no need to emit it here.
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 meant, do we need an else
statement here (similar to playbackBufferEmptyContext
)? I am having some trouble understanding why these 2 are different.
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.
As stated in the doc the |
Based on the code in |
The line may have changed. could you link here? Thanks! |
Hello @hellohuanlin, the link FLTVideoPlayerPlugin.m#LL332C4-L339C4 |
I'm a bit confused here. Simply observing |
@hellohuanlin, based on my testing log, it appears that simply observing isPlaybackLikelyToKeepUp is sufficient for checking if playback is stalling or not. However, the Key-Value Observing (KVO) for isPlaybackBufferEmpty or isPlaybackBufferFull does not always work correctly. It's a bug. There is a related issue: https://developer.apple.com/forums/thread/669541 Here is the log snippet from my test:
I conducted this test on iPadOS 16.5 using a real device. You can also refer to the provided demo AVPlayer_livestream for further clarification. |
// expected behavior of the value change for `playbackBufferEmpty` is not triggered. This | ||
// issue has been confirmed in iOS 16.5. Refer: | ||
// https://github.com/flutter/packages/pull/3826#discussion_r1204985454 Fortunately, the KVO | ||
// for `playbackBufferFull` is working correctly, so the bug won't affect the event passing |
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.
Fortunately, the KVO for
playbackBufferFull
is working correctly,
Sorry for the back forth, but it looks like this comment only explains why we need playbackBufferFull
, but it does not explain why we need to check isPlaybackLikelyToKeepUp
(i.e. your code change).
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.
Hi @hellohuanlin, thank you for your suggestion. I have added documentation in this commit.
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.
Can you also rephrase this and add to the comment? #3826 (comment)
I think it's important to know why we can't just listen to isPlaybackLikelyToKeepUp
and call it a day.
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.
@hellohuanlin It would be preferable for isBuffering
to serve a singular purpose, indicating whether the playback is likely to proceed without stalling. I believe that relying solely on isPlaybackLikelyToKeepUp
is sufficient, as mentioned in the #3826 (comment), the code listening to isPlaybackBufferEmpty
or isPlaybackBufferFull
should be removed. This change have a significant impact. I have pushed the commit.
Regarding the Android implementation in the video_player
code, the isBuffering
status is derived from the playbackState
, specifically by checking if it equals Player.STATE_BUFFERING
. In the context of Android's ExoPlayer, Player.STATE_BUFFERING
signifies that the player is unable to immediately start playing the media but is actively working towards that goal. This state commonly occurs when the player needs to buffer additional data before playback can commence. For further reference, please consult the documentation.
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.
This change have a significant impact.
Can you elaborate more on the impact? the new code makes more sense to me
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.
Hi @hellohuanlin, apologies for the unclear expression in my previous comment. The impact of the commit is significant as it removes a considerable amount of code, and all the test cases have passed successfully. Therefore, it functions as expected.
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.
Just wanna double check before stamping it - now it's just deleting the code. Does it still fix your issue for your project?
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.
Yes, I have tested the latest commit in my production app and deployed it to my production environment.
…elyToKeepUp` over `isPlaybackBufferFull` or `isPlaybackLikelyToKeepUp` for determining playback stall status
02f3102
to
39b3e5c
Compare
…aybackLikelyToKeepUp` for determining `isBuffering` status. `isBuffering` servea a singular purpose, indicating whether the playback is likely to proceed without stalling. Relying solely on `isPlaybackLikelyToKeepUp` is sufficient, as [discussion](flutter#3826 (comment)). Therefore, the code listening to `isPlaybackBufferEmpty` or `isPlaybackBufferFull` should be removed.
auto label is removed for flutter/packages, pr: 3826, due to - The status or check suite Linux repo_tools_tests has failed. Please fix the issues identified (or deflake) before re-applying this label. |
auto label is removed for flutter/packages, pr: 3826, due to - The status or check suite repo_checks has failed. Please fix the issues identified (or deflake) before re-applying this label. |
flutter/packages@6889cca...3e8b813 2023-07-18 [email protected] [video_player] Fix iOS crash with multiple players (flutter/packages#4202) 2023-07-17 [email protected] [pigeon] Enable Android emulator tests in CI (flutter/packages#4484) 2023-07-17 [email protected] [video_player] Add optional web options [Platform interface] (flutter/packages#4433) 2023-07-17 [email protected] [google_maps_flutter_platform_interface] Platform interface changes for #3258 (flutter/packages#4478) 2023-07-17 [email protected] [video_player] fix: add missing isPlaybackLikelyToKeepUp check. (flutter/packages#3826) 2023-07-17 [email protected] [camerax] Add flash configuration for image capture (flutter/packages#3800) 2023-07-17 [email protected] Remove `equatable` and `xml` allowances (flutter/packages#4489) 2023-07-17 [email protected] [ci] Switch Linux platform tests to LUCI (flutter/packages#4479) 2023-07-17 [email protected] [ci] Adjust bot configurations (flutter/packages#4485) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages-flutter-autoroll Please CC [email protected],[email protected] on the revert to ensure that a human is aware of the problem. To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
flutter/packages@6889cca...3e8b813 2023-07-18 [email protected] [video_player] Fix iOS crash with multiple players (flutter/packages#4202) 2023-07-17 [email protected] [pigeon] Enable Android emulator tests in CI (flutter/packages#4484) 2023-07-17 [email protected] [video_player] Add optional web options [Platform interface] (flutter/packages#4433) 2023-07-17 [email protected] [google_maps_flutter_platform_interface] Platform interface changes for flutter#3258 (flutter/packages#4478) 2023-07-17 [email protected] [video_player] fix: add missing isPlaybackLikelyToKeepUp check. (flutter/packages#3826) 2023-07-17 [email protected] [camerax] Add flash configuration for image capture (flutter/packages#3800) 2023-07-17 [email protected] Remove `equatable` and `xml` allowances (flutter/packages#4489) 2023-07-17 [email protected] [ci] Switch Linux platform tests to LUCI (flutter/packages#4479) 2023-07-17 [email protected] [ci] Adjust bot configurations (flutter/packages#4485) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages-flutter-autoroll Please CC [email protected],[email protected] on the revert to ensure that a human is aware of the problem. To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
When watching a live stream, if the playback buffers but the AVPlayerItem is likely to keep up, it is necessary to recheck AVPlayerItem.isPlaybackLikelyToKeepUp. If isPlaybackLikelyToKeepUp, a bufferingEnd event should be immediately triggered. I am encountering an issue with my product where, when watching a live stream, if I seek to the latest time, it continuously
bufferingStart
and does not come to anbufferingEnd
.dart format
.)[shared_preferences]
pubspec.yaml
with an appropriate new version according to the pub versioning philosophy, or this PR is exempt from version changes.CHANGELOG.md
to add a description of the change, following repository CHANGELOG style.///
).If you need help, consider asking for advice on the #hackers-new channel on Discord.