-
Notifications
You must be signed in to change notification settings - Fork 3.6k
[video_player] #60048 ios picture in picture #3500
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
Changes from 1 commit
595d3df
4831f57
976b630
a7d5dd5
876dfe8
a4ca9e7
49f769a
5477f0b
0764e27
65fdf65
d4d9ff9
ff6d02f
ae7500a
4eeb6e1
9110d0f
211c6b3
5a8735b
4583229
70284aa
7289998
ee96f77
da13c09
4d83a52
ac920fe
8394840
19b1440
1963d27
f71a27a
b3e05a0
1325254
72ba79a
8c67ab8
f935a16
42a7815
fa93319
b0103d0
236ab51
1cf961d
5c18256
ddc7139
45494b9
0597f32
5c1f759
e94ccaa
9dc85a0
2b72605
601c506
4574fa9
1135695
cd7fede
e9aba08
30c0b7a
c0ccbfc
8ee1dcb
58db59a
16ae8cb
a15c3b7
87b92c2
5183028
002faa0
148cd55
d88cf33
45f96f5
9459bd5
d9f883b
c9993dc
3e401c1
b250e9c
01aaf8d
ee6db47
2e3196b
29c1d3b
6d8fb1e
114f5b8
5861e1d
114f9ac
3c1b4e6
3985a77
1649be4
bc56139
e1348c8
7e70da0
4abe6f1
2006494
58dabf4
881e80e
917bdd4
fba55da
b0f42d8
e0231d8
c58b722
376bcf5
5dd6e13
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| ## 2.7.0 | ||
|
|
||
| * Added support for picture in picture on iOS | ||
| * Adds support for picture-in-picture on iOS. | ||
|
|
||
| ## 2.6.1 | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -135,12 +135,12 @@ and so on. | |
|
|
||
| To learn about playback speed limitations, see the [`setPlaybackSpeed` method documentation](https://pub.dev/documentation/video_player/latest/video_player/VideoPlayerController/setPlaybackSpeed.html). | ||
|
|
||
| ### Picture in Picture | ||
| ### Picture-in-Picture | ||
|
|
||
| #### iOS | ||
| On iOS the picture in picture is linked to the AVPlayerController. | ||
| If you want to enable picture in picture make sure to enable the `audio` capability (in Xcode's UI it will say **Audio, AirPlay, and Picture in Picture**). | ||
| Not setting this capability but calling `setPictureInPictureOverlayRectMessage` and `setPictureInPicture` will not start the picture in picture. | ||
| On iOS the picture-in-picture is linked to the AVPlayerController. | ||
| If you want to enable picture-in-picture make sure to enable the `audio` capability (in Xcode's UI it will say **Audio, AirPlay, and Picture in Picture**). | ||
| Not setting this capability but calling `setPictureInPictureOverlayRectMessage` and `setPictureInPicture` will not start the picture-in-picture. | ||
|
|
||
| ```xml | ||
stuartmorgan-g marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| <key>UIBackgroundModes</key> | ||
|
|
@@ -150,14 +150,14 @@ Not setting this capability but calling `setPictureInPictureOverlayRectMessage` | |
| ``` | ||
|
|
||
| Example: | ||
|  | ||
|  | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. READMEs should never have references to non-pinned URLs, as it means old versions' READMEs can change or break at any time. E.g., renaming the plugin in the future would break all previous READMEs. This will need to be added in a follow-up PR, using a hash instead of |
||
|
|
||
| #### Android | ||
|
|
||
| On Android the implementation is different. There is no link to the video player. Your complete app will be minimized ([picture in picutre Android documentation](https://developer.android.com/guide/topics/ui/picture-in-picture)) | ||
| On Android there is no link to the video player. Your complete app will be minimized ([picture-in-picture Android documentation](https://developer.android.com/guide/topics/ui/picture-in-picture)) | ||
vanlooverenkoen marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
vanlooverenkoen marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| You have multiple options on Android: | ||
| - [simple_pip_mode](https://pub.dev/packages/simple_pip_mode) | ||
vanlooverenkoen marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| - Create your own plugin that follows the andorid documentation | ||
| - Create your own plugin that follows the android documentation | ||
|
|
||
| Furthermore, see the example app for an example playback speed implementation. | ||
vanlooverenkoen marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -253,15 +253,15 @@ class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> { | |
| future: _controller.isPictureInPictureSupported(), | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't most of the UI need to be conditional on this? It looks like the button below to trigger PiP is unconditionally enabled and will call a method that isn't implemented on most platforms, for instance. |
||
| builder: (BuildContext context, AsyncSnapshot<bool> snapshot) => | ||
| Text(snapshot.data ?? false | ||
| ? 'Picture in picture is supported' | ||
| : 'Picture in picture is not supported'), | ||
| ? 'Picture-in-picture is supported' | ||
| : 'Picture-in-picture is not supported'), | ||
| ), | ||
| Row( | ||
| children: <Widget>[ | ||
| const SizedBox(width: 16), | ||
| const Expanded( | ||
| child: Text( | ||
| 'Start picture in picture automatically when going to background'), | ||
| 'Start picture-in-picture automatically when going to background'), | ||
| ), | ||
| Switch( | ||
| value: _enableStartPictureInPictureAutomaticallyFromInline, | ||
|
|
@@ -296,7 +296,7 @@ class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> { | |
| ), | ||
| ); | ||
| }, | ||
| child: const Text('Set picture in picture overlay rect'), | ||
| child: const Text('Set picture-in-picture overlay rect'), | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this something that clients have to call manually? Shouldn't the controller be determining this information from the widget as needed? |
||
| ), | ||
| MaterialButton( | ||
| color: Colors.blue, | ||
|
|
@@ -308,8 +308,8 @@ class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> { | |
| } | ||
| }, | ||
| child: Text(_controller.value.isPictureInPictureActive | ||
| ? 'Stop picture in picture' | ||
| : 'Start picture in picture'), | ||
| ? 'Stop picture-in-picture' | ||
| : 'Start picture-in-picture'), | ||
| ), | ||
| Container( | ||
| padding: const EdgeInsets.all(20), | ||
|
|
@@ -330,7 +330,7 @@ class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> { | |
| children: const <Widget>[ | ||
| Icon(Icons.picture_in_picture), | ||
| SizedBox(height: 8), | ||
| Text('This video is playing in picture in picture.'), | ||
| Text('This video is playing in picture-in-picture.'), | ||
| ], | ||
| ), | ||
| ] else ...<Widget>[ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| ## 2.5.0 | ||
|
|
||
| * Added support for picture in picture on iOS | ||
| * Adds support for picture-in-picture on iOS. | ||
|
|
||
| ## 2.4.4 | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -275,7 +275,7 @@ - (void)testTransformFix { | |
| XCTAssertNil(error); | ||
| XCTAssertEqual(avPlayer.volume, 0.1f); | ||
|
|
||
| // Set Picture In Picture | ||
| // Set picture-in-picture | ||
|
||
| NSNumber *isPictureInPictureSupported = [videoPlayerPlugin isPictureInPictureSupported:&error]; | ||
| XCTAssertNil(error); | ||
| XCTAssertNotNil(isPictureInPictureSupported); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,21 +33,21 @@ - (void)testPlayVideo { | |
| [playButton tap]; | ||
|
|
||
| if ([AVPictureInPictureController isPictureInPictureSupported]) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be a separate test.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This still needs to be resolved. |
||
| XCUIElement *pipSupportedText = app.staticTexts[@"Picture in picture is supported"]; | ||
| XCUIElement *pipSupportedText = app.staticTexts[@"Picture-in-picture is supported"]; | ||
| XCTAssertTrue([pipSupportedText waitForExistenceWithTimeout:30.0]); | ||
|
|
||
| XCUIElement *pipPrepareButton = app.buttons[@"Set picture in picture overlay rect"]; | ||
| XCUIElement *pipPrepareButton = app.buttons[@"Set picture-in-picture overlay rect"]; | ||
| XCTAssertTrue([pipPrepareButton waitForExistenceWithTimeout:30.0]); | ||
| [pipPrepareButton tap]; | ||
|
|
||
| XCUIElement *pipStartButton = app.buttons[@"Start picture in picture"]; | ||
| XCUIElement *pipStartButton = app.buttons[@"Start picture-in-picture"]; | ||
| XCTAssertTrue([pipStartButton waitForExistenceWithTimeout:30.0]); | ||
| [pipStartButton tap]; | ||
|
|
||
| XCUIElement *pipUIView = app.otherElements[@"PIPUIView"]; | ||
| XCTAssertTrue([pipUIView waitForExistenceWithTimeout:30.0]); | ||
|
|
||
| XCUIElement *pipStopButton = app.buttons[@"Stop picture in picture"]; | ||
| XCUIElement *pipStopButton = app.buttons[@"Stop picture-in-picture"]; | ||
| XCTAssertTrue([pipStopButton waitForExistenceWithTimeout:30.0]); | ||
| [pipStopButton tap]; | ||
|
|
||
|
|
@@ -57,7 +57,7 @@ - (void)testPlayVideo { | |
| XCTAssertFalse([pipUIView exists]); | ||
| } else { | ||
| XCTAssertTrue( | ||
| [app.staticTexts[@"Picture in picture is not supported"] waitForExistenceWithTimeout:30.0]); | ||
| [app.staticTexts[@"Picture-in-picture is not supported"] waitForExistenceWithTimeout:30.0]); | ||
| } | ||
|
|
||
| NSPredicate *find1xButton = [NSPredicate predicateWithFormat:@"label CONTAINS '1.0x'"]; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -41,7 +41,7 @@ @interface FLTVideoPlayer | |
| // streams (not just iOS 16). (https://github.com/flutter/flutter/issues/109116). | ||
| // An invisible AVPlayerLayer is used to overwrite the protection of pixel buffers in those streams | ||
| // for issue #1, and restore the correct width and height for issue #2. | ||
| // It is also used to start picture in picture | ||
| // It is also used to start picture-in-picture | ||
| @property(readonly, nonatomic) AVPlayerLayer *playerLayer; | ||
| @property(readonly, nonatomic) CADisplayLink *displayLink; | ||
| @property(nonatomic) AVPictureInPictureController *pictureInPictureController; | ||
|
|
@@ -258,10 +258,10 @@ - (instancetype)initWithPlayerItem:(AVPlayerItem *)item | |
| // video streams (not just iOS 16). (https://github.com/flutter/flutter/issues/109116). An | ||
| // invisible AVPlayerLayer is used to overwrite the protection of pixel buffers in those streams | ||
| // for issue #1, and restore the correct width and height for issue #2. | ||
| // It is also used to start picture in picture | ||
| // It is also used to start picture-in-picture | ||
| _playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player]; | ||
| // We set the opacity to 0.001 because it is an overlay. | ||
| // Picture in picture will show a placeholder over other widgets when video_player is used in a | ||
| // Picture-in-picture will show a placeholder over other widgets when video_player is used in a | ||
| // ScrollView, PageView or in a widget that changes location. | ||
| _playerLayer.opacity = 0.001; | ||
|
||
| [rootViewController().view.layer addSublayer:_playerLayer]; | ||
|
|
@@ -311,8 +311,7 @@ - (void)startOrStopPictureInPicture:(BOOL)shouldPictureInPictureStart { | |
| if (self.pictureInPictureController && self.isPictureInPictureStarted && | ||
| ![self.pictureInPictureController isPictureInPictureActive]) { | ||
| if (_eventSink != nil) { | ||
| // The event is already send here to make sure that Flutter UI can be updates as soon as | ||
| // possible | ||
| // The event is sent here to make sure that the Flutter UI can be updated as soon as possible. | ||
| _eventSink(@{@"event" : @"startingPictureInPicture"}); | ||
| } | ||
| [self.pictureInPictureController startPictureInPicture]; | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.