Skip to content

Commit d28c98c

Browse files
author
Chris Yang
authored
[video_player] Move [player dispose] to onUnregistered (flutter#2124)
1 parent fb47143 commit d28c98c

File tree

9 files changed

+216
-69
lines changed

9 files changed

+216
-69
lines changed

packages/video_player/CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## 0.10.3+1
2+
3+
* Dispose `FLTVideoPlayer` in `onTextureUnregistered` callback on iOS.
4+
* Add a temporary fix to dispose the `FLTVideoPlayer` with a delay to avoid race condition.
5+
* Updated the example app to include a new page that pop back after video is done playing.
6+
17
## 0.10.3
28

39
* Add support for the v2 Android embedding. This shouldn't impact existing

packages/video_player/example/lib/main.dart

+146-63
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import 'package:flutter/cupertino.dart';
99
import 'package:flutter/material.dart';
1010
import 'package:video_player/video_player.dart';
11+
import 'package:flutter/scheduler.dart';
1112

1213
/// Controls play and pause of [controller].
1314
///
@@ -28,7 +29,7 @@ class VideoPlayPause extends StatefulWidget {
2829
class _VideoPlayPauseState extends State<VideoPlayPause> {
2930
_VideoPlayPauseState() {
3031
listener = () {
31-
setState(() {});
32+
SchedulerBinding.instance.addPostFrameCallback((_) => setState(() {}));
3233
};
3334
}
3435

@@ -48,8 +49,11 @@ class _VideoPlayPauseState extends State<VideoPlayPause> {
4849

4950
@override
5051
void deactivate() {
51-
controller.setVolume(0.0);
52-
controller.removeListener(listener);
52+
SchedulerBinding.instance.addPostFrameCallback((_) {
53+
controller.setVolume(0.0);
54+
controller.removeListener(listener);
55+
});
56+
5357
super.deactivate();
5458
}
5559

@@ -360,73 +364,152 @@ class AspectRatioVideoState extends State<AspectRatioVideo> {
360364
}
361365
}
362366

363-
void main() {
364-
runApp(
365-
MaterialApp(
366-
home: DefaultTabController(
367-
length: 3,
368-
child: Scaffold(
369-
appBar: AppBar(
370-
title: const Text('Video player example'),
371-
bottom: const TabBar(
372-
isScrollable: true,
373-
tabs: <Widget>[
374-
Tab(
375-
icon: Icon(Icons.cloud),
376-
text: "Remote",
377-
),
378-
Tab(icon: Icon(Icons.insert_drive_file), text: "Asset"),
379-
Tab(icon: Icon(Icons.list), text: "List example"),
380-
],
381-
),
367+
class App extends StatelessWidget {
368+
@override
369+
Widget build(BuildContext context) {
370+
return DefaultTabController(
371+
length: 3,
372+
child: Scaffold(
373+
key: const ValueKey<String>('home_page'),
374+
appBar: AppBar(
375+
title: const Text('Video player example'),
376+
actions: <Widget>[
377+
IconButton(
378+
key: const ValueKey<String>('push_tab'),
379+
icon: const Icon(Icons.navigation),
380+
onPressed: () {
381+
Navigator.push<PlayerVideoAndPopPage>(
382+
context,
383+
MaterialPageRoute<PlayerVideoAndPopPage>(
384+
builder: (BuildContext context) =>
385+
PlayerVideoAndPopPage()),
386+
);
387+
},
388+
)
389+
],
390+
bottom: const TabBar(
391+
isScrollable: true,
392+
tabs: <Widget>[
393+
Tab(
394+
icon: Icon(Icons.cloud),
395+
text: "Remote",
396+
),
397+
Tab(icon: Icon(Icons.insert_drive_file), text: "Asset"),
398+
Tab(icon: Icon(Icons.list), text: "List example"),
399+
],
382400
),
383-
body: TabBarView(
384-
children: <Widget>[
385-
SingleChildScrollView(
386-
child: Column(
387-
children: <Widget>[
388-
Container(
389-
padding: const EdgeInsets.only(top: 20.0),
401+
),
402+
body: TabBarView(
403+
children: <Widget>[
404+
SingleChildScrollView(
405+
child: Column(
406+
children: <Widget>[
407+
Container(
408+
padding: const EdgeInsets.only(top: 20.0),
409+
),
410+
const Text('With remote mp4'),
411+
Container(
412+
padding: const EdgeInsets.all(20),
413+
child: NetworkPlayerLifeCycle(
414+
'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4',
415+
(BuildContext context,
416+
VideoPlayerController controller) =>
417+
AspectRatioVideo(controller),
390418
),
391-
const Text('With remote mp4'),
392-
Container(
393-
padding: const EdgeInsets.all(20),
394-
child: NetworkPlayerLifeCycle(
395-
'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4',
419+
),
420+
],
421+
),
422+
),
423+
SingleChildScrollView(
424+
child: Column(
425+
children: <Widget>[
426+
Container(
427+
padding: const EdgeInsets.only(top: 20.0),
428+
),
429+
const Text('With assets mp4'),
430+
Container(
431+
padding: const EdgeInsets.all(20),
432+
child: AssetPlayerLifeCycle(
433+
'assets/Butterfly-209.mp4',
396434
(BuildContext context,
397435
VideoPlayerController controller) =>
398-
AspectRatioVideo(controller),
399-
),
400-
),
401-
],
402-
),
436+
AspectRatioVideo(controller)),
437+
),
438+
],
403439
),
404-
SingleChildScrollView(
405-
child: Column(
406-
children: <Widget>[
407-
Container(
408-
padding: const EdgeInsets.only(top: 20.0),
409-
),
410-
const Text('With assets mp4'),
411-
Container(
412-
padding: const EdgeInsets.all(20),
413-
child: AssetPlayerLifeCycle(
414-
'assets/Butterfly-209.mp4',
415-
(BuildContext context,
416-
VideoPlayerController controller) =>
417-
AspectRatioVideo(controller)),
418-
),
419-
],
420-
),
421-
),
422-
AssetPlayerLifeCycle(
423-
'assets/Butterfly-209.mp4',
424-
(BuildContext context, VideoPlayerController controller) =>
425-
VideoInListOfCards(controller)),
426-
],
427-
),
440+
),
441+
AssetPlayerLifeCycle(
442+
'assets/Butterfly-209.mp4',
443+
(BuildContext context, VideoPlayerController controller) =>
444+
VideoInListOfCards(controller)),
445+
],
428446
),
429447
),
448+
);
449+
}
450+
}
451+
452+
void main() {
453+
runApp(
454+
MaterialApp(
455+
home: App(),
430456
),
431457
);
432458
}
459+
460+
class PlayerVideoAndPopPage extends StatefulWidget {
461+
@override
462+
_PlayerVideoAndPopPageState createState() => _PlayerVideoAndPopPageState();
463+
}
464+
465+
class _PlayerVideoAndPopPageState extends State<PlayerVideoAndPopPage> {
466+
VideoPlayerController _videoPlayerController;
467+
bool startedPlaying = false;
468+
469+
@override
470+
void initState() {
471+
super.initState();
472+
473+
_videoPlayerController =
474+
VideoPlayerController.asset('assets/Butterfly-209.mp4');
475+
_videoPlayerController.addListener(() {
476+
if (startedPlaying && !_videoPlayerController.value.isPlaying) {
477+
Navigator.pop(context);
478+
}
479+
});
480+
}
481+
482+
@override
483+
void dispose() {
484+
_videoPlayerController.dispose();
485+
super.dispose();
486+
}
487+
488+
Future<bool> started() async {
489+
await _videoPlayerController.initialize();
490+
await _videoPlayerController.play();
491+
startedPlaying = true;
492+
return true;
493+
}
494+
495+
@override
496+
Widget build(BuildContext context) {
497+
return Material(
498+
elevation: 0,
499+
child: Center(
500+
child: FutureBuilder<bool>(
501+
future: started(),
502+
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
503+
if (snapshot.data == true) {
504+
return AspectRatio(
505+
aspectRatio: _videoPlayerController.value.aspectRatio,
506+
child: VideoPlayer(_videoPlayerController));
507+
} else {
508+
return const Text('waiting for video to load');
509+
}
510+
},
511+
),
512+
),
513+
);
514+
}
515+
}

packages/video_player/example/pubspec.yaml

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@ description: Demonstrates how to use the video_player plugin.
44
dependencies:
55
flutter:
66
sdk: flutter
7+
video_player:
8+
path: ../
79

810
dev_dependencies:
911
flutter_test:
1012
sdk: flutter
1113
flutter_driver:
1214
sdk: flutter
1315
e2e: "^0.2.0"
14-
video_player:
15-
path: ../
16+
test: any
1617

1718
flutter:
1819
uses-material-design: true
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright 2019, the Chromium project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:flutter_driver/driver_extension.dart';
6+
import 'package:video_player_example/main.dart' as app;
7+
8+
void main() {
9+
enableFlutterDriverExtension();
10+
app.main();
11+
}

packages/video_player/example/test_driver/video_player_e2e.dart

-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ const Duration _playDuration = Duration(seconds: 1);
1212
void main() {
1313
E2EWidgetsFlutterBinding.ensureInitialized();
1414
VideoPlayerController _controller;
15-
1615
tearDown(() async => _controller.dispose());
1716

1817
group('asset videos', () {

packages/video_player/example/test_driver/video_player_e2e_test.dart

-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
import 'dart:async';
66
import 'dart:io';
7-
87
import 'package:flutter_driver/flutter_driver.dart';
98

109
Future<void> main() async {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2019, the Chromium project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:async';
6+
import 'package:flutter_driver/flutter_driver.dart';
7+
import 'package:test/test.dart';
8+
9+
Future<void> main() async {
10+
final FlutterDriver driver = await FlutterDriver.connect();
11+
tearDownAll(() async {
12+
driver.close();
13+
});
14+
15+
//TODO(cyanglaz): Use TabBar tabs to navigate between pages after https://github.com/flutter/flutter/issues/16991 is fixed.
16+
//TODO(cyanglaz): Un-skip the test after https://github.com/flutter/flutter/issues/43012 is fixed
17+
test('Push a page contains video and pop back, do not crash.', () async {
18+
final SerializableFinder pushTab = find.byValueKey('push_tab');
19+
await driver.waitFor(pushTab);
20+
await driver.tap(pushTab);
21+
await driver.waitForAbsent(pushTab);
22+
await driver.waitFor(find.byValueKey('home_page'));
23+
await driver.waitUntilNoTransientCallbacks();
24+
final Health health = await driver.checkHealth();
25+
expect(health.status, HealthStatus.ok);
26+
}, skip: 'Cirrus CI currently hangs while playing videos');
27+
}

packages/video_player/ios/Classes/VideoPlayerPlugin.m

+22-1
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,12 @@ - (CVPixelBufferRef)copyPixelBuffer {
363363
}
364364
}
365365

366+
- (void)onTextureUnregistered {
367+
dispatch_async(dispatch_get_main_queue(), ^{
368+
[self dispose];
369+
});
370+
}
371+
366372
- (FlutterError* _Nullable)onCancelWithArguments:(id _Nullable)arguments {
367373
_eventSink = nil;
368374
return nil;
@@ -487,7 +493,22 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
487493
if ([@"dispose" isEqualToString:call.method]) {
488494
[_registry unregisterTexture:textureId];
489495
[_players removeObjectForKey:@(textureId)];
490-
[player dispose];
496+
// If the Flutter contains https://github.com/flutter/engine/pull/12695,
497+
// the `player` is disposed via `onTextureUnregistered` at the right time.
498+
// Without https://github.com/flutter/engine/pull/12695, there is no guarantee that the
499+
// texture has completed the un-reregistration. It may leads a crash if we dispose the
500+
// `player` before the texture is unregistered. We add a dispatch_after hack to make sure the
501+
// texture is unregistered before we dispose the `player`.
502+
//
503+
// TODO(cyanglaz): Remove this dispatch block when
504+
// https://github.com/flutter/flutter/commit/8159a9906095efc9af8b223f5e232cb63542ad0b is in
505+
// stable And update the min flutter version of the plugin to the stable version.
506+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)),
507+
dispatch_get_main_queue(), ^{
508+
if (!player.disposed) {
509+
[player dispose];
510+
}
511+
});
491512
result(nil);
492513
} else if ([@"setLooping" isEqualToString:call.method]) {
493514
[player setIsLooping:[argsMap[@"looping"] boolValue]];

packages/video_player/pubspec.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: video_player
22
description: Flutter plugin for displaying inline video with other Flutter
33
widgets on Android and iOS.
44
author: Flutter Team <[email protected]>
5-
version: 0.10.3
5+
version: 0.10.3+1
66
homepage: https://github.com/flutter/plugins/tree/master/packages/video_player
77

88
flutter:

0 commit comments

Comments
 (0)