Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

[video_player] ios picture in picture #6284

Closed
wants to merge 83 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
42ffe17
Don't force save the picked/taken image to the gallery
NicolaVerbeeck Nov 17, 2021
08f723d
Merge pull request #2 from flutter/main
ikbendewilliam Jul 13, 2022
e7921c2
Merge branch 'master' into source
jorre127 Jul 13, 2022
d319e04
Merge pull request #3 from icapps/source
jorre127 Jul 13, 2022
b54f049
Merge branch 'flutter:main' into master
vanlooverenkoen Aug 16, 2022
4a34420
Merge branch 'flutter:main' into master
vanlooverenkoen Aug 17, 2022
826b995
#60048 Added ios pip support
vanlooverenkoen Aug 17, 2022
8e15600
Updated pubspec
vanlooverenkoen Aug 17, 2022
78be5cf
Removed development team & updated bundle identifier
vanlooverenkoen Aug 17, 2022
5d91352
Updated the pr feedback
vanlooverenkoen Aug 18, 2022
5f7281a
Dependency overrides
vanlooverenkoen Aug 18, 2022
c38e3d3
Added iOS UI tests for the PiP
vanlooverenkoen Aug 18, 2022
f0e4820
Fixed some dart tests
vanlooverenkoen Aug 18, 2022
567e146
FIxed incorrect import
vanlooverenkoen Aug 18, 2022
77287c1
Fixed a dart test where the to string was updated but not fixed in test
vanlooverenkoen Aug 18, 2022
5ca97ca
Fixed formatting
vanlooverenkoen Aug 18, 2022
6309b2f
Revert "Fixed formatting"
vanlooverenkoen Aug 18, 2022
db3eb7d
Fixed the ios formatting
vanlooverenkoen Aug 18, 2022
3a4d91e
Updated android gradle plugin
vanlooverenkoen Aug 18, 2022
f8a3ed6
Check if the PIPUIView is availabel after starting PiP
vanlooverenkoen Aug 19, 2022
3328e52
Fixed pr comments
vanlooverenkoen Aug 19, 2022
17a580e
Fixed pr comments
vanlooverenkoen Aug 19, 2022
3cbfd50
Added extra tests to check if starting is set.
vanlooverenkoen Aug 19, 2022
a9317da
Update the player layer behaviour.
vanlooverenkoen Aug 19, 2022
6572321
Added support to enable or disable automaticly start pip when the app…
vanlooverenkoen Aug 19, 2022
82912b4
Moved to a oneliner
vanlooverenkoen Aug 19, 2022
e4582ec
Merge branch 'main' into feature/ios-pip
vanlooverenkoen Aug 25, 2022
2e2c31d
Fixed the README comments
vanlooverenkoen Sep 9, 2022
63b5bd8
Updated readme
vanlooverenkoen Sep 9, 2022
5c0da92
Fixed pr comments
vanlooverenkoen Sep 9, 2022
071893d
Update the project file
vanlooverenkoen Sep 9, 2022
fd0e9cd
Moved the api to a rect
vanlooverenkoen Sep 9, 2022
2af7230
Removed unwanted enabledVideo value
vanlooverenkoen Sep 9, 2022
f2ee1a4
Updated the isPictureInPicture supported method
vanlooverenkoen Sep 9, 2022
1047384
Fixed pr comments in the switch case
vanlooverenkoen Sep 9, 2022
f9ec3f5
Removed unwanted info.plist items
vanlooverenkoen Sep 9, 2022
5927554
Fixed test
vanlooverenkoen Sep 9, 2022
620c179
Make sure the tests compile
vanlooverenkoen Sep 9, 2022
de5d58a
Updated the boolean arguments
vanlooverenkoen Sep 9, 2022
5fd2bcb
Updated the UI tests to check if pip is supported or not
vanlooverenkoen Sep 9, 2022
3cc7213
Fixed a typo
vanlooverenkoen Sep 9, 2022
ff67016
Fixed typo & made it clear what the switch does
vanlooverenkoen Sep 9, 2022
f34b8b4
Pr feedback
vanlooverenkoen Sep 9, 2022
0c2d632
Added extra documentation
vanlooverenkoen Sep 9, 2022
16b2c0f
Removed the web package
vanlooverenkoen Sep 9, 2022
bab27ed
Fixed pr feedback
vanlooverenkoen Sep 9, 2022
a829a14
Fixed formatting
vanlooverenkoen Sep 9, 2022
85c77e7
Fixed pr comments
vanlooverenkoen Sep 9, 2022
640db39
Merge branch 'flutter:main' into master
vanlooverenkoen Sep 9, 2022
ed675e3
Merge branch 'master' into feature/ios-pip
vanlooverenkoen Sep 9, 2022
7eddf97
Fixed the merge conflicts
vanlooverenkoen Sep 9, 2022
0bc160a
Update ImagePickerDelegate.java
vanlooverenkoen Sep 9, 2022
db8a9b2
Fixed the web pubspec
vanlooverenkoen Sep 9, 2022
699ea6f
Revert "Update ImagePickerDelegate.java"
vanlooverenkoen Sep 9, 2022
2a217d5
Revert "Fixed the web pubspec"
vanlooverenkoen Sep 9, 2022
e621157
Removed link to web
vanlooverenkoen Sep 9, 2022
31b6a9f
Update ImagePickerDelegate.java
vanlooverenkoen Sep 9, 2022
0ba1b21
Trigger build
vanlooverenkoen Sep 9, 2022
c45a2c2
Merge branch 'feature/ios-pip' of github.com:icapps/plugins into feat…
vanlooverenkoen Sep 9, 2022
f188b5f
Fixed formatting
vanlooverenkoen Sep 9, 2022
892aac0
Merge branch 'flutter:main' into master
vanlooverenkoen Sep 26, 2022
c841ad4
Merge branch 'master' into feature/ios-pip
vanlooverenkoen Sep 26, 2022
36333f6
Refactorred some of the api's and fixed pr comments.
vanlooverenkoen Sep 26, 2022
d07cf9c
Cleanup pip to picture in picture
vanlooverenkoen Sep 26, 2022
abe022b
Fixed tests
vanlooverenkoen Sep 26, 2022
66b5ff1
Merge branch 'flutter:main' into master
vanlooverenkoen Oct 5, 2022
467b85e
Fixed merge conflicts
vanlooverenkoen Oct 5, 2022
e9bdfd5
Fixed a comment about the api start/stop
vanlooverenkoen Oct 5, 2022
faed7b8
Fixed comment about the pipUI view test that should be hidden
vanlooverenkoen Oct 5, 2022
3fb924b
Fixed check if buttons are hidden.
vanlooverenkoen Oct 5, 2022
90f993a
Fixed pr comments
vanlooverenkoen Oct 5, 2022
a901313
Merge branch 'main' into feature/ios-pip
vanlooverenkoen Jan 4, 2023
83edcef
Fixed comments
vanlooverenkoen Jan 5, 2023
2b8b565
Updated the gif
vanlooverenkoen Jan 5, 2023
c6f81b7
Fixed pr comments
vanlooverenkoen Jan 18, 2023
6f66373
Fixed formatting
vanlooverenkoen Jan 18, 2023
cc639b2
Fixed compile issue
vanlooverenkoen Jan 18, 2023
23defa3
Merge branch 'main' into feature/ios-pip
vanlooverenkoen Jan 28, 2023
eaaaac2
Merge branch 'flutter:main' into master
vanlooverenkoen Feb 10, 2023
de162e8
Merge branch 'master' into feature/ios-pip
vanlooverenkoen Feb 10, 2023
326c187
Updated the video_player_platfom_interface
vanlooverenkoen Feb 10, 2023
96c788a
Test if this works? It doesn't work on my machine
vanlooverenkoen Feb 10, 2023
565012b
Fixed pr build
vanlooverenkoen Feb 15, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objectVersion = 50;
objects = {

/* Begin PBXBuildFile section */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
Expand All @@ -27,6 +29,10 @@
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
Expand Down
54 changes: 52 additions & 2 deletions packages/video_player/video_player/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,10 @@ class _BumbleBeeRemoteVideo extends StatefulWidget {
class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> {
late VideoPlayerController _controller;

final GlobalKey<State<StatefulWidget>> _key =
GlobalKey<State<StatefulWidget>>();
final Key _pictureInPictureKey = UniqueKey();

Future<ClosedCaptionFile> _loadCaptions() async {
final String fileContents = await DefaultAssetBundle.of(context)
.loadString('assets/bumble_bee_captions.vtt');
Expand Down Expand Up @@ -243,17 +247,63 @@ class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> {
children: <Widget>[
Container(padding: const EdgeInsets.only(top: 20.0)),
const Text('With remote mp4'),
FutureBuilder<bool>(
key: _pictureInPictureKey,
future: _controller.isPictureInPictureSupported(),
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) =>
Text(snapshot.data ?? false
? 'Pip is suported'
: 'Pip is not supported'),
),
MaterialButton(
color: Colors.blue,
onPressed: () {
final RenderBox? box =
_key.currentContext?.findRenderObject() as RenderBox?;
if (box == null) {
return;
}
final Offset offset = box.localToGlobal(Offset.zero);
_controller.preparePictureInPicture(
top: offset.dy,
left: offset.dx,
width: box.size.width,
height: box.size.height,
);
},
child: const Text('Prepare'),
),
MaterialButton(
color: Colors.blue,
onPressed: () =>
_controller.setPictureInPicture(!_controller.value.isPipActive),
child:
Text(_controller.value.isPipActive ? 'Stop PiP' : 'Start PiP'),
),
Container(
padding: const EdgeInsets.all(20),
child: AspectRatio(
aspectRatio: _controller.value.aspectRatio,
child: Stack(
key: _key,
alignment: Alignment.bottomCenter,
children: <Widget>[
VideoPlayer(_controller),
ClosedCaption(text: _controller.value.caption.text),
_ControlsOverlay(controller: _controller),
VideoProgressIndicator(_controller, allowScrubbing: true),
if (_controller.value.isPipActive) ...<Widget>[
Container(color: Colors.white),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
Icon(Icons.picture_in_picture),
SizedBox(height: 8),
Text('This video is playing in picture in picture.'),
],
),
] else ...<Widget>[
VideoProgressIndicator(_controller, allowScrubbing: true),
_ControlsOverlay(controller: _controller),
],
],
),
),
Expand Down
60 changes: 58 additions & 2 deletions packages/video_player/video_player/lib/video_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class VideoPlayerValue {
this.isPlaying = false,
this.isLooping = false,
this.isBuffering = false,
this.isPipActive = false,
this.volume = 1.0,
this.playbackSpeed = 1.0,
this.rotationCorrection = 0,
Expand Down Expand Up @@ -105,6 +106,9 @@ class VideoPlayerValue {
/// The current speed of the playback.
final double playbackSpeed;

/// True if picture in picture is currently active.
final bool isPipActive;

/// A description of the error if present.
///
/// If [hasError] is false this is `null`.
Expand Down Expand Up @@ -153,6 +157,7 @@ class VideoPlayerValue {
bool? isPlaying,
bool? isLooping,
bool? isBuffering,
bool? isPipActive,
double? volume,
double? playbackSpeed,
int? rotationCorrection,
Expand All @@ -169,6 +174,7 @@ class VideoPlayerValue {
isPlaying: isPlaying ?? this.isPlaying,
isLooping: isLooping ?? this.isLooping,
isBuffering: isBuffering ?? this.isBuffering,
isPipActive: isPipActive ?? this.isPipActive,
volume: volume ?? this.volume,
playbackSpeed: playbackSpeed ?? this.playbackSpeed,
rotationCorrection: rotationCorrection ?? this.rotationCorrection,
Expand All @@ -191,6 +197,7 @@ class VideoPlayerValue {
'isPlaying: $isPlaying, '
'isLooping: $isLooping, '
'isBuffering: $isBuffering, '
'isPipActive: $isPipActive, '
'volume: $volume, '
'playbackSpeed: $playbackSpeed, '
'errorDescription: $errorDescription)';
Expand Down Expand Up @@ -400,6 +407,12 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
case VideoEventType.bufferingEnd:
value = value.copyWith(isBuffering: false);
break;
case VideoEventType.startingPiP:
value = value.copyWith(isPipActive: true);
break;
case VideoEventType.stoppedPiP:
value = value.copyWith(isPipActive: false);
break;
case VideoEventType.unknown:
break;
}
Expand Down Expand Up @@ -519,6 +532,40 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
await _videoPlayerPlatform.setVolume(_textureId, value.volume);
}

/// Returns true if picture in picture is supported on the device.
Future<bool> isPictureInPictureSupported() {
return _videoPlayerPlatform.isPictureInPictureSupported();
}

/// Prepare picture in picture by passing the location of the video player view
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comments here as in the platform interface about the division of API calls.

Future<void> preparePictureInPicture({
double top = 0,
double left = 0,
double width = 0,
double height = 0,
}) async {
if (!value.isInitialized || _isDisposed) {
return;
}
await _videoPlayerPlatform.preparePictureInPicture(
textureId: _textureId,
top: top,
left: left,
width: width,
height: height,
);
}

/// Start/stop picture in picture mode
Future<void> setPictureInPicture(
Copy link
Member

Choose a reason for hiding this comment

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

This "set" name isn't clear that this will actually start or stop the PiP. toggleOnOrOff? I can't think of a great name.

Copy link
Contributor Author

@vanlooverenkoen vanlooverenkoen Sep 9, 2022

Choose a reason for hiding this comment

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

Maybe I should switch to 2 methods.
startPictureInPicture and stopPictureInPicture?

bool enabled,
) async {
if (!value.isInitialized || _isDisposed) {
return;
}
await _videoPlayerPlatform.setPictureInPicture(_textureId, enabled);
}

Future<void> _applyPlaybackSpeed() async {
if (_isDisposedOrNotInitialized) {
return;
Expand Down Expand Up @@ -690,6 +737,7 @@ class _VideoAppLifeCycleObserver extends Object with WidgetsBindingObserver {
_VideoAppLifeCycleObserver(this._controller);

bool _wasPlayingBeforePause = false;
bool _showingPip = false;
final VideoPlayerController _controller;

void initialize() {
Expand All @@ -701,7 +749,10 @@ class _VideoAppLifeCycleObserver extends Object with WidgetsBindingObserver {
switch (state) {
case AppLifecycleState.paused:
_wasPlayingBeforePause = _controller.value.isPlaying;
_controller.pause();
_showingPip = _controller.value.isPipActive;
if (!_showingPip) {
_controller.pause();
}
break;
case AppLifecycleState.resumed:
if (_wasPlayingBeforePause) {
Expand Down Expand Up @@ -734,9 +785,11 @@ class _VideoPlayerState extends State<VideoPlayer> {
_VideoPlayerState() {
_listener = () {
final int newTextureId = widget.controller.textureId;
if (newTextureId != _textureId) {
final bool newEnabledVideo = !widget.controller.value.isPipActive;
if (newTextureId != _textureId || newEnabledVideo != _enabledVideo) {
setState(() {
_textureId = newTextureId;
// _enabledVideo = newEnabledVideo;
});
}
};
Expand All @@ -745,11 +798,13 @@ class _VideoPlayerState extends State<VideoPlayer> {
late VoidCallback _listener;

late int _textureId;
late bool _enabledVideo;

@override
void initState() {
super.initState();
_textureId = widget.controller.textureId;
_enabledVideo = !widget.controller.value.isPipActive;
// Need to listen for initialization events since the actual texture ID
// becomes available after asynchronous initialization finishes.
widget.controller.addListener(_listener);
Expand All @@ -760,6 +815,7 @@ class _VideoPlayerState extends State<VideoPlayer> {
super.didUpdateWidget(oldWidget);
oldWidget.controller.removeListener(_listener);
_textureId = widget.controller.textureId;
_enabledVideo = !widget.controller.value.isPipActive;
widget.controller.addListener(_listener);
}

Expand Down
2 changes: 1 addition & 1 deletion packages/video_player/video_player/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ description: Flutter plugin for displaying inline video with other Flutter
widgets on Android, iOS, and web.
repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22
version: 2.4.6
version: 2.5.0

environment:
sdk: ">=2.14.0 <3.0.0"
Expand Down
16 changes: 16 additions & 0 deletions packages/video_player/video_player/test/video_player_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,22 @@ class FakeController extends ValueNotifier<VideoPlayerValue>
Future<void> setClosedCaptionFile(
Future<ClosedCaptionFile>? closedCaptionFile,
) async {}

@override
Future<void> preparePictureInPicture({
double top = 0,
double left = 0,
double width = 0,
double height = 0,
}) async {}

@override
Future<void> setPictureInPicture(bool enabled) async {}

@override
Future<bool> isPictureInPictureSupported() async {
return true;
}
}

Future<ClosedCaptionFile> _loadClosedCaption() async =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,10 @@ class MiniController extends ValueNotifier<VideoPlayerValue> {
case VideoEventType.bufferingEnd:
value = value.copyWith(isBuffering: false);
break;
case VideoEventType.startingPiP:
break;
case VideoEventType.stoppedPiP:
break;
case VideoEventType.unknown:
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class VideoPlayerValue {
this.isInitialized = false,
this.isPlaying = false,
this.isBuffering = false,
this.isPipActive = false,
this.playbackSpeed = 1.0,
this.errorDescription,
});
Expand Down Expand Up @@ -70,6 +71,9 @@ class VideoPlayerValue {
/// The current speed of the playback.
final double playbackSpeed;

/// True if picture in picture is currently active.
final bool isPipActive;

/// A description of the error if present.
///
/// If [hasError] is false this is `null`.
Expand Down Expand Up @@ -112,6 +116,7 @@ class VideoPlayerValue {
bool? isInitialized,
bool? isPlaying,
bool? isBuffering,
bool? isPipActive,
double? playbackSpeed,
String? errorDescription,
}) {
Expand All @@ -123,6 +128,7 @@ class VideoPlayerValue {
isInitialized: isInitialized ?? this.isInitialized,
isPlaying: isPlaying ?? this.isPlaying,
isBuffering: isBuffering ?? this.isBuffering,
isPipActive: isPipActive ?? this.isPipActive,
playbackSpeed: playbackSpeed ?? this.playbackSpeed,
errorDescription: errorDescription ?? this.errorDescription,
);
Expand Down Expand Up @@ -243,6 +249,12 @@ class MiniController extends ValueNotifier<VideoPlayerValue> {
case VideoEventType.bufferingEnd:
value = value.copyWith(isBuffering: false);
break;
case VideoEventType.startingPiP:
value = value.copyWith(isPipActive: true);
break;
case VideoEventType.stoppedPiP:
value = value.copyWith(isPipActive: false);
break;
case VideoEventType.unknown:
break;
}
Expand Down
Loading