Skip to content
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

[video_player_avfoundation] send video load failure even when eventsink was initialized late #7194

Merged
merged 10 commits into from
Nov 20, 2024

Conversation

misos1
Copy link
Contributor

@misos1 misos1 commented Jul 22, 2024

Event initialized is sent from onListenWithArguments by calling setupEventSinkIfReadyToPlay if event sink was nil during observeValueForKeyPath. This change also sends failure in a similar way. Adds more details to the error message returned by VideoPlayerController.initialize().

Tests testSeekToWhilePausedStartsDisplayLinkTemporarily and testSeekToWhilePlayingDoesNotStopDisplayLink are failing on the main branch on ios. Seems position is correctly set to 1234 in seekTo completion handler but right after [mockDisplayLink setRunning:YES] is set again to 0 and after some time back to 1234 so those two tests sometimes fail (seems more often when running all tests).

Pre-launch Checklist

If you need help, consider asking for advice on the #hackers-new channel on Discord.

@misos1 misos1 force-pushed the complete_on_error branch from 756f3b9 to 16d3d7a Compare July 22, 2024 17:20
@misos1 misos1 marked this pull request as ready for review July 23, 2024 18:25
@misos1 misos1 requested a review from hellohuanlin as a code owner July 23, 2024 18:25
@hellohuanlin hellohuanlin requested a review from tarrinneal July 24, 2024 21:27
@@ -406,9 +400,46 @@ - (void)updatePlayingState {
_displayLink.running = _isPlaying || self.waitingForFrame;
}

- (void)sendFailedToLoadVideoEvent:(NSError *)error {
if (!NSThread.isMainThread) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it looks like sendFailedToLoadVideoEvent is only called from background queue. Can this function simply be:

- (void)sendFailedToLoadVideoEventOnMainQueue:(NSError *)error {
  dispatch_async(dispatch_get_main_queue(), ^{
    // all logic here
  }
}

Copy link
Contributor Author

@misos1 misos1 Jul 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is also called from setupEventSinkIfReadyToPlay and observeValueForKeyPath. I can make it without any "dispatch" and use dispatch_async just in trackCompletionHandler (or maybe performSelector like is already used in this function here performSelector:_cmd).

@hellohuanlin
Copy link
Contributor

I have a hard time understanding this in the PR description:

Sends also failure similarly as is sent event initialized from onListenWithArguments through setupEventSinkIfReadyToPlay if event sink was not initialized during observeValueForKeyPath.

Could you rephrase a bit? That'd be helpful for a review. Thanks!

stringByAppendingString:[item.error localizedDescription]]
details:nil]);
}
[self sendFailedToLoadVideoEvent:item.error];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: sendFailedToLoadVideoEventIfNeeded since you moved the check inside

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would not say "if needed" sounds right here. Meaning of that check is rather like "if can" or "if possible".

};
NSError *underlyingError = error.userInfo[NSUnderlyingErrorKey];
// add more details to error message
// https://github.com/flutter/flutter/issues/56665
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i dont think we need this comment

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can remove this link.

// if status is Failed here then this was called from onListenWithArguments which means
// _eventSink was nil when was called observeValueForKeyPath with updated Failed status
// and error was not sent and never will be so it is needed to send it here to have
// future returned by initialize resolved instead of in state of infinite waiting,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you separate these 4 lines into multiple sentences? I have a hard time understanding this.

return;
case AVKeyValueStatusFailed:
// if something failed then future should result in error
[self sendFailedToLoadVideoEvent:error];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you also specify which thread this is on

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On some AVFoundation background queue (this is also mentioned in a comment a few lines above):

https://developer.apple.com/documentation/avfoundation/avasynchronouskeyvalueloading/1387321-loadvaluesasynchronouslyforkeys

// https://github.com/flutter/flutter/issues/151475
// https://github.com/flutter/flutter/issues/147707
if (currentItem.status == AVPlayerItemStatusFailed) {
[self sendFailedToLoadVideoEvent:currentItem.error];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we move this to the caller, before setupEventSinkIfReadyToPlay is called?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or can you update the function name to setupEventSinkIfReadyToPlayOrReportFailure

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is also 4th place where this function is called masked by _cmd here self performSelector:_cmd, I would rather not put that check outside of that function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually that function does not set up _eventsink, I would say it is set up by onListenWithArguments. This function just sends an event through that event sink.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the other hand if status becomes failed on that second call it should be already caught in the key observer where the event sink is not nil and so it would send an error from there.

@misos1 misos1 requested a review from hellohuanlin August 27, 2024 20:18
case AVKeyValueStatusFailed: {
// if something failed then future should result in error
dispatch_async(dispatch_get_main_queue(), ^{
[self sendFailedToLoadVideoEvent:error];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this code the key change that fixed the bug?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems no, I think it is that change in onListenWithArguments. But without that change it seems also this change works as I now tested it with "Code sample 1" from flutter/flutter#151475.

@@ -587,6 +613,13 @@ - (FlutterError *_Nullable)onListenWithArguments:(id _Nullable)arguments
// This line ensures the 'initialized' event is sent when the event
// 'AVPlayerItemStatusReadyToPlay' fires before _eventSink is set (this function
// onListenWithArguments is called)
// and also send error in similar case with 'AVPlayerItemStatusFailed'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

by "similar case", do you mean AVPlayerItemStatusReadyToPlay fires before _eventSink is set? Can you be more explicit here in the comment? (Sorry I have some trouble understanding this bug fix)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I was following up with that comment above. But this time with AVPlayerItemStatusFailed instead of AVPlayerItemStatusReadyToPlay. If anything fires before is event sink set then it could not be sent. Would that comment be better if I added "in similar case as above"?

@hellohuanlin hellohuanlin requested review from stuartmorgan and removed request for tarrinneal September 24, 2024 18:58
@misos1 misos1 requested a review from hellohuanlin October 17, 2024 19:07
Copy link
Contributor

@hellohuanlin hellohuanlin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay.

// and also send error in similar case with 'AVPlayerItemStatusFailed'
// https://github.com/flutter/flutter/issues/151475
// https://github.com/flutter/flutter/issues/147707
if (self.player.currentItem.status == AVPlayerItemStatusFailed) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is the part that actually fix the bug? Can you add a comment explaining how we ended up with Failed state here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I did not manage to proceed with this. Yes when AVPlayerItemStatusFailed fires before _eventSink is set. AVPlayerItem is "started" to do its things and can asynchronously become ReadyToPlay or Failed before the dart side starts to listen where onListenWithArguments is called. My comment here follows up that comment before which tries to explain this but that previous fix connected with that comment omitted Failed state as possible state here which this line fixes (if player item can end up with success here it can also end up with failure, so that previous comment should explain it).

@stuartmorgan stuartmorgan added the triage-ios Should be looked at in iOS triage label Nov 8, 2024
@stuartmorgan stuartmorgan removed their request for review November 8, 2024 16:30
@stuartmorgan
Copy link
Contributor

@hellohuanlin Please add tarrinneal (the ecosystem-level CODEOWNER) as the secondary reviewer once this has passed iOS review; the iOS team review is primary since this is internal to the iOS implementation.

Copy link
Contributor

@hellohuanlin hellohuanlin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM after the nit on comment

@cbracken cbracken added the autosubmit Merge PR when tree becomes green via auto submit App label Nov 20, 2024
@auto-submit auto-submit bot merged commit 2703b67 into flutter:main Nov 20, 2024
76 checks passed
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Nov 21, 2024
github-merge-queue bot pushed a commit to flutter/flutter that referenced this pull request Nov 21, 2024
flutter/packages@e95f6d8...913b99e

2024-11-21 brackenavaron@gmail.com [vector_graphics] handle errors from
bytes loader (flutter/packages#8080)
2024-11-21 8276375+derdilla@users.noreply.github.com [flutter_svg] Fix
SvgNetworkLoader not closing internal http client
(flutter/packages#8126)
2024-11-20 30872003+misos1@users.noreply.github.com
[video_player_avfoundation] send video load failure even when eventsink
was initialized late (flutter/packages#7194)
2024-11-20 kevmoo@users.noreply.github.com [flutter_markdown] enable
Wasm support (flutter/packages#8120)
2024-11-20 98614782+auto-submit[bot]@users.noreply.github.com Reverts
"[url_launcher] Add Swift Package Manager integration to example app
(#8128)" (flutter/packages#8136)
2024-11-20 737941+loic-sharma@users.noreply.github.com [url_launcher]
Add Swift Package Manager integration to example app
(flutter/packages#8128)
2024-11-20 stuartmorgan@google.com [pigeon] Enable example app build in
CI (flutter/packages#8119)

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 flutter-ecosystem@google.com 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://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
sinyu1012 added a commit to sinyu1012/packages that referenced this pull request Nov 22, 2024
* main: (64 commits)
  [quick_actions_plaform_interface] add localizedSubtitle (flutter#8112)
  [tools] Don't check license of generated Swift package (flutter#8137)
  Roll Flutter from 8536b96ebb3e to 93d772c5cdd8 (37 revisions) (flutter#8147)
  [go_router] Fix: Consistent PopScope Handling on Root Routes issue #140869 (flutter#8045)
  [in_app_purchase_storekit] fix price displayed with wrong precision (flutter#8127)
  [pigeon] Removes the `@protected` annotation from the InstanceManager field of the   `PigeonInternalProxyApiBaseClass` (flutter#8125)
  [google_maps_flutter] Use structured Pigeon data on iOS (flutter#8142)
  [vector_graphics] handle errors from bytes loader (flutter#8080)
  [flutter_svg] Fix SvgNetworkLoader not closing internal http client (flutter#8126)
  [video_player_avfoundation] send video load failure even when eventsink was initialized late (flutter#7194)
  [flutter_markdown] enable Wasm support (flutter#8120)
  Reverts "[url_launcher] Add Swift Package Manager integration to example app (flutter#8128)" (flutter#8136)
  [url_launcher] Add Swift Package Manager integration to example app (flutter#8128)
  [pigeon] Enable example app build in CI (flutter#8119)
  [in_app_purchase_storekit] disallow ios versions lower than supported from enabling storekit (flutter#8110)
  [interactive_media_ads]: Bump com.google.ads.interactivemedia.v3:interactivemedia from 3.35.1 to 3.36.0 in /packages/interactive_media_ads/android (flutter#8046)
  [interactive_media_ads]: Bump androidx.annotation:annotation from 1.8.2 to 1.9.1 in /packages/interactive_media_ads/android (flutter#7980)
  [webview_flutter_android] Updates plugin to use `ProxyApis`s (flutter#7794)
  [interactive_media_ads] Adds support to define parameters that control the rendering of ads (flutter#8057)
  Roll Flutter from b3818f6b5979 to 8536b96ebb3e (22 revisions) (flutter#8124)
  ...

# Conflicts:
#	packages/quick_actions/quick_actions_platform_interface/CHANGELOG.md
@misos1
Copy link
Contributor Author

misos1 commented Nov 22, 2024

I forgot to prepend those 3 issues with fixes. They can probably be closed now (or at least those which this PR covers in full).

@stuartmorgan
Copy link
Contributor

Thanks, I closed one, and left a comment in another that is likely fixed but needs re-testing (unless you've already tested that one? If so we can close it). For the third it's not clear to me whether it's fully resolved.

@misos1
Copy link
Contributor Author

misos1 commented Nov 22, 2024

That first which you closed actually contained another behaviour which is not covered by this PR. But I think it is rather not a fault in the package or flutter sdk but rather with something how flutter runs apps on ios devices. Seems sometimes the app "disappears" after running but after closer inspection it seems it was just probably somehow "pushed" to background.

I probably tested the second with some text file but I am not sure and probably not with that original file and probably not on macos either. Anyway the best would be confirmation from the author.

That third is a mess. Seems there are several different groups who interpret that issue differently. Sticking to what author wrote in description and repeated in comments seems that the issue is just about better error message flutter/flutter#56665 (comment). So the question is whether issue should be interpreted differently if a lot of users expect that that issue should be closed by fixing underlying behaviour (which probably cannot be done from flutter or video_player side as it does not have anything to do with http communication here, if of course there is not some flag somewhere in AVFoundation). Maybe better to ask the author also here (or maybe wait and see if any new comments are added in next few months).

mchudy pushed a commit to leancodepl/packages that referenced this pull request Nov 25, 2024
…nk was initialized late (flutter#7194)

Event `initialized` is sent from `onListenWithArguments` by calling `setupEventSinkIfReadyToPlay` if event sink was `nil` during `observeValueForKeyPath`. This change also sends failure in a similar way. Adds more details to the error message returned by `VideoPlayerController.initialize()`.

- flutter/flutter#151475
- flutter/flutter#147707
- flutter/flutter#56665 - now error message may look like: `Failed to load video: Operation Stopped: The server is not correctly configured.: The operation couldn�t be completed. (CoreMediaErrorDomain error -12939 - byte range and no content length - error code is 200)`

Tests `testSeekToWhilePausedStartsDisplayLinkTemporarily` and `testSeekToWhilePlayingDoesNotStopDisplayLink` are failing on the main branch on ios. Seems position is correctly set to 1234 in `seekTo` completion handler but right after `[mockDisplayLink setRunning:YES]` is set again to 0 and after some time back to 1234 so those two tests sometimes fail (seems more often when running all tests).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
autosubmit Merge PR when tree becomes green via auto submit App p: video_player platform-ios platform-macos triage-ios Should be looked at in iOS triage
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants