flutter_aliplayer/example/lib/page/player_page.dart

1020 lines
30 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_aliplayer/flutter_aliplayer.dart';
import 'package:flutter_aliplayer/flutter_aliplayer_factory.dart';
import 'package:flutter_aliplayer_example/config.dart';
import 'package:flutter_aliplayer_example/page/player_fragment/cache_config_fragment.dart';
import 'package:flutter_aliplayer_example/page/player_fragment/filter_fragment.dart';
import 'package:flutter_aliplayer_example/page/player_fragment/options_fragment.dart';
import 'package:flutter_aliplayer_example/page/player_fragment/play_config_fragment.dart';
import 'package:flutter_aliplayer_example/page/player_fragment/track_fragment.dart';
import 'package:flutter_aliplayer_example/util/formatter_utils.dart';
import 'package:flutter_aliplayer_example/widget/aliyun_marqueeview.dart';
import 'package:flutter_aliplayer_example/widget/aliyun_slider.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:path_provider/path_provider.dart';
import 'package:connectivity/connectivity.dart';
class PlayerPage extends StatefulWidget {
final ModeType playMode;
final Map<String, dynamic> dataSourceMap;
PlayerPage({Key key, this.playMode, this.dataSourceMap})
: assert(playMode != null),
super(key: key);
@override
_PlayerPageState createState() => _PlayerPageState();
}
class _PlayerPageState extends State<PlayerPage> with WidgetsBindingObserver {
FlutterAliplayer fAliplayer;
int bottomIndex;
List<Widget> mFramePage;
ModeType _playMode;
Map<String, dynamic> _dataSourceMap;
OptionsFragment mOptionsFragment;
//是否允许后台播放
bool _mEnablePlayBack = false;
//当前播放进度
int _currentPosition = 0;
//当前播放时间用于Text展示
int _currentPositionText = 0;
//当前buffer进度
int _bufferPosition = 0;
//是否展示loading
bool _showLoading = false;
//loading进度
int _loadingPercent = 0;
//视频时长
int _videoDuration = 1;
//截图保存路径
String _snapShotPath;
//提示内容
String _tipsContent;
//是否展示提示内容
bool _showTipsWidget = false;
//是否有缩略图
bool _thumbnailSuccess = false;
//缩略图
// Uint8List _thumbnailBitmap;
ImageProvider _imageProvider;
//当前网络状态
ConnectivityResult _currentConnectivityResult;
///seek中
bool _inSeek = false;
bool _isLock = false;
//网络状态
bool _isShowMobileNetWork = false;
//当前播放器状态
int _currentPlayerState = 0;
String extSubTitleText = '';
///封面图
bool _showCoverImg = true;
//网络状态监听
StreamSubscription _networkSubscription;
List<int> dotPositionList = [];
GlobalKey<TrackFragmentState> trackFragmentKey = GlobalKey();
int aliPlayerViewId;
@override
void initState() {
super.initState();
fAliplayer = FlutterAliPlayerFactory.createAliPlayer();
WidgetsBinding.instance.addObserver(this);
bottomIndex = 0;
_playMode = widget.playMode;
_dataSourceMap = widget.dataSourceMap;
//开启混音模式
if (Platform.isIOS) {
FlutterAliplayer.setAudioSessionTypeForIOS(
AliPlayerAudioSesstionType.mix);
}
//设置播放器
fAliplayer.setPreferPlayerName(GlobalSettings.mPlayerName);
fAliplayer.setEnableHardwareDecoder(GlobalSettings.mEnableHardwareDecoder);
if (Platform.isAndroid) {
getExternalStorageDirectories().then(
(value) {
if (value.length > 0) {
_snapShotPath = value[0].path;
return _snapShotPath;
}
},
);
}
mOptionsFragment = OptionsFragment(fAliplayer);
mFramePage = [
mOptionsFragment,
PlayConfigFragment(fAliplayer),
FilterFragment(fAliplayer),
CacheConfigFragment(fAliplayer),
TrackFragment(trackFragmentKey, fAliplayer),
];
mOptionsFragment.setOnEnablePlayBackChanged(
(mEnablePlayBack) {
this._mEnablePlayBack = mEnablePlayBack;
},
);
_initListener();
}
_initListener() {
fAliplayer.setOnEventReportParams(
(params, playerId) {
print("EventReportParams=${params}");
},
);
fAliplayer.setOnPrepared(
(playerId) {
Fluttertoast.showToast(msg: "OnPrepared ");
fAliplayer.getPlayerName().then(
(value) => print("getPlayerName==${value}"),
);
fAliplayer.getMediaInfo().then(
(value) {
_videoDuration = value['duration'];
setState(() {});
},
);
},
);
fAliplayer.setOnRenderingStart(
(playerId) {
Fluttertoast.showToast(msg: " OnFirstFrameShow ");
setState(
() {
if (mounted) {
_showCoverImg = false;
}
},
);
},
);
fAliplayer.setOnVideoSizeChanged((width, height, rotation, playerId) {});
fAliplayer.setOnStateChanged(
(newState, playerId) {
_currentPlayerState = newState;
switch (newState) {
case FlutterAvpdef.AVPStatus_AVPStatusStarted:
setState(
() {
_showTipsWidget = false;
_showLoading = false;
},
);
break;
case FlutterAvpdef.AVPStatus_AVPStatusPaused:
break;
default:
}
},
);
fAliplayer.setOnLoadingStatusListener(
loadingBegin: (playerId) {
setState(
() {
_loadingPercent = 0;
_showLoading = true;
},
);
},
loadingProgress: (percent, netSpeed, playerId) {
_loadingPercent = percent;
if (percent == 100) {
_showLoading = false;
}
setState(() {});
},
loadingEnd: (playerId) {
setState(
() {
_showLoading = false;
},
);
},
);
fAliplayer.setOnSeekComplete(
(playerId) {
_inSeek = false;
},
);
fAliplayer.setOnInfo(
(infoCode, extraValue, extraMsg, playerId) {
if (infoCode == FlutterAvpdef.CURRENTPOSITION) {
if (_videoDuration != 0 && extraValue <= _videoDuration) {
_currentPosition = extraValue;
}
if (!_inSeek) {
setState(
() {
_currentPositionText = extraValue;
},
);
}
} else if (infoCode == FlutterAvpdef.BUFFEREDPOSITION) {
_bufferPosition = extraValue;
if (mounted) {
setState(() {});
}
} else if (infoCode == FlutterAvpdef.AUTOPLAYSTART) {
Fluttertoast.showToast(msg: "AutoPlay");
} else if (infoCode == FlutterAvpdef.CACHESUCCESS) {
Fluttertoast.showToast(msg: "Cache Success");
} else if (infoCode == FlutterAvpdef.CACHEERROR) {
Fluttertoast.showToast(msg: "Cache Error $extraMsg");
} else if (infoCode == FlutterAvpdef.LOOPINGSTART) {
Fluttertoast.showToast(msg: "Looping Start");
} else if (infoCode == FlutterAvpdef.SWITCHTOSOFTWAREVIDEODECODER) {
Fluttertoast.showToast(msg: "change to soft ware decoder");
mOptionsFragment.switchHardwareDecoder();
}
},
);
fAliplayer.setOnCompletion(
(playerId) {
_showTipsWidget = true;
_showLoading = false;
_tipsContent = "播放完成";
setState(
() {
_currentPosition = _videoDuration;
},
);
},
);
fAliplayer.setOnTrackReady(
(playerId) {
fAliplayer.getMediaInfo().then(
(value) {
setState(() {});
List thumbnails = value['thumbnails'];
if (thumbnails != null && thumbnails.isNotEmpty) {
fAliplayer.createThumbnailHelper(thumbnails[0]['url']);
} else {
_thumbnailSuccess = false;
}
},
);
trackFragmentKey.currentState.loadData();
setState(() {});
},
);
fAliplayer.setOnSnapShot((path, playerId) {
Fluttertoast.showToast(msg: "SnapShot Save : $path");
});
fAliplayer.setOnError(
(errorCode, errorExtra, errorMsg, playerId) {
_showTipsWidget = true;
_showLoading = false;
_tipsContent = "$errorCode \n $errorMsg";
setState(() {});
},
);
fAliplayer.setOnTrackChanged(
(value, playerId) {
AVPTrackInfo info = AVPTrackInfo.fromJson(value);
if (info != null && info.trackDefinition.length > 0) {
trackFragmentKey.currentState.onTrackChanged(info);
Fluttertoast.showToast(msg: "${info.trackDefinition}切换成功");
}
},
);
fAliplayer.setOnThumbnailPreparedListener(
preparedSuccess: (playerId) {
_thumbnailSuccess = true;
setState(() {
/// 打点位置
dotPositionList.add(30000);
dotPositionList.add(65000);
dotPositionList.add(125000);
});
},
preparedFail: (playerId) {
_thumbnailSuccess = false;
},
);
fAliplayer.setOnThumbnailGetListener(
onThumbnailGetSuccess: (bitmap, range, playerId) {
// _thumbnailBitmap = bitmap;
var provider = MemoryImage(bitmap);
precacheImage(provider, context).then(
(_) {
setState(
() {
_imageProvider = provider;
},
);
},
);
},
onThumbnailGetFail: (playerId) {});
this.fAliplayer.setOnSubtitleHide(
(trackIndex, subtitleID, playerId) {
if (mounted) {
setState(
() {
extSubTitleText = '';
},
);
}
},
);
this.fAliplayer.setOnSubtitleShow(
(trackIndex, subtitleID, subtitle, playerId) {
if (mounted) {
setState(
() {
extSubTitleText = subtitle;
},
);
}
},
);
_setNetworkChangedListener();
}
_setNetworkChangedListener() {
_networkSubscription = Connectivity().onConnectivityChanged.listen(
(ConnectivityResult result) {
if (result == ConnectivityResult.mobile) {
fAliplayer.pause();
setState(
() {
_isShowMobileNetWork = true;
},
);
} else if (result == ConnectivityResult.wifi) {
//从4G网络或者无网络切换到wifi
if (_currentConnectivityResult == ConnectivityResult.mobile ||
_currentConnectivityResult == ConnectivityResult.none) {
fAliplayer.play();
}
setState(
() {
_isShowMobileNetWork = false;
},
);
}
_currentConnectivityResult = result;
},
);
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
switch (state) {
case AppLifecycleState.inactive:
break;
case AppLifecycleState.resumed:
if (_mEnablePlayBack && GlobalSettings.mEnabletPictureInPicture) {
FlutterAliPlayerFactory.hideFloatViewForAndroid();
}
setState(() {});
_setNetworkChangedListener();
break;
case AppLifecycleState.paused:
if (!_mEnablePlayBack) {
fAliplayer.pause();
}
if (_mEnablePlayBack && GlobalSettings.mEnabletPictureInPicture) {
FlutterAliPlayerFactory.showFloatViewForAndroid(aliPlayerViewId);
}
if (_networkSubscription != null) {
_networkSubscription.cancel();
}
break;
case AppLifecycleState.detached:
break;
}
}
@override
void dispose() {
SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
if (Platform.isIOS) {
FlutterAliplayer.setAudioSessionTypeForIOS(
AliPlayerAudioSesstionType.sdkDefault);
}
fAliplayer.stop();
fAliplayer.destroy();
super.dispose();
WidgetsBinding.instance.removeObserver(this);
if (_networkSubscription != null) {
_networkSubscription.cancel();
}
}
@override
Widget build(BuildContext context) {
var x = 0.0;
var y = 0.0;
Orientation orientation = MediaQuery.of(context).orientation;
var width = MediaQuery.of(context).size.width;
var height;
if (orientation == Orientation.portrait) {
height = width * 9.0 / 16.0;
} else {
height = MediaQuery.of(context).size.height;
}
AliPlayerView aliPlayerView = AliPlayerView(
onCreated: onViewPlayerCreated,
x: x,
y: y,
width: width,
height: height,
aliPlayerViewType: AliPlayerViewTypeForAndroid.surfaceview,
);
return OrientationBuilder(
builder: (BuildContext context, Orientation orientation) {
return Scaffold(
appBar: _buildAppBar(orientation),
body: Column(
children: [
Stack(
children: [
Container(
color: Colors.black,
child: aliPlayerView,
width: width,
height: height),
Container(
width: width,
height: height,
// padding: EdgeInsets.only(bottom: 25.0),
child: Offstage(
offstage: _isLock,
child: _buildContentWidget(orientation),
),
),
_buildProgressBar(width, height),
_buildTipsWidget(width, height),
_buildThumbnail(width, height),
_buildNetWorkTipsWidget(width, height),
Align(
alignment: Alignment.topCenter,
child: Text(
extSubTitleText,
style: TextStyle(color: Colors.red),
),
),
Positioned(
left: 30,
top: height / 2,
child: Offstage(
offstage: orientation == Orientation.portrait,
child: InkWell(
onTap: () {
setState(
() {
_isLock = !_isLock;
},
);
},
child: Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: Colors.black.withAlpha(150),
borderRadius: BorderRadius.circular(20),
),
child: Icon(
_isLock ? Icons.lock : Icons.lock_open,
color: Colors.white,
),
),
),
),
),
_buildCoverImg(),
_buildMarqueeView(),
],
),
_buildControlBtns(orientation),
_buildFragmentPage(orientation),
],
),
bottomNavigationBar: _buildBottomNavigationBar(orientation),
);
},
);
}
void onViewPlayerCreated(viewId) async {
this.aliPlayerViewId = viewId;
this.fAliplayer.setPlayerView(viewId);
_generatePlayConfigGen();
switch (_playMode) {
case ModeType.URL:
this.fAliplayer.setUrl(_dataSourceMap[DataSourceRelated.URL_KEY]);
break;
case ModeType.STS:
FlutterAliplayer.generatePlayerConfig().then((value) {
this.fAliplayer.setVidSts(
vid: _dataSourceMap[DataSourceRelated.VID_KEY],
region: _dataSourceMap[DataSourceRelated.REGION_KEY],
accessKeyId: _dataSourceMap[DataSourceRelated.ACCESSKEYID_KEY],
accessKeySecret:
_dataSourceMap[DataSourceRelated.ACCESSKEYSECRET_KEY],
securityToken:
_dataSourceMap[DataSourceRelated.SECURITYTOKEN_KEY],
definitionList: _dataSourceMap[DataSourceRelated.DEFINITION_LIST],
playConfig: value);
});
break;
case ModeType.AUTH:
FlutterAliplayer.generatePlayerConfig().then((value) {
this.fAliplayer.setVidAuth(
vid: _dataSourceMap[DataSourceRelated.VID_KEY],
region: _dataSourceMap[DataSourceRelated.REGION_KEY],
playAuth: _dataSourceMap[DataSourceRelated.PLAYAUTH_KEY],
definitionList: _dataSourceMap[DataSourceRelated.DEFINITION_LIST],
playConfig: value);
});
break;
case ModeType.MPS:
this.fAliplayer.setVidMps(_dataSourceMap);
break;
default:
}
}
_buildAppBar(Orientation orientation) {
if (orientation == Orientation.portrait) {
return AppBar(
title: const Text('Plugin for aliplayer'),
);
}
}
/// MARK: 私有方法
_buildControlBtns(Orientation orientation) {
return Offstage(
offstage: orientation == Orientation.landscape,
child: Padding(
padding: const EdgeInsets.only(top: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
InkWell(
child: Text('准备'),
onTap: () {
_showTipsWidget = false;
_showLoading = false;
trackFragmentKey.currentState.prepared();
setState(() {});
fAliplayer.prepare();
},
),
InkWell(
child: Text('播放'),
onTap: () {
fAliplayer.play();
},
),
InkWell(
child: Text('停止'),
onTap: () {
fAliplayer.stop();
},
),
InkWell(
child: Text('暂停'),
onTap: () {
fAliplayer.pause();
},
),
InkWell(
child: Text('截图'),
onTap: () {
if (Platform.isIOS) {
fAliplayer.snapshot(
DateTime.now().millisecondsSinceEpoch.toString() +
".png");
} else {
fAliplayer.snapshot(_snapShotPath +
"/snapshot_" +
new DateTime.now().millisecondsSinceEpoch.toString() +
".png");
}
},
),
],
),
),
);
}
_buildFragmentPage(Orientation orientation) {
return Expanded(
child: Offstage(
offstage: orientation == Orientation.landscape,
child: IndexedStack(index: bottomIndex, children: mFramePage),
)); //mFramePage
}
///缩略图
_buildThumbnail(double width, double height) {
if (_inSeek && _thumbnailSuccess) {
return Container(
alignment: Alignment.center,
width: width,
height: height,
child: Wrap(
direction: Axis.vertical,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Text("${FormatterUtils.getTimeformatByMs(_currentPosition)}",
style: TextStyle(color: Colors.white),
textAlign: TextAlign.center),
_imageProvider == null
? Container()
: Image(
width: width / 2,
height: height / 2,
image: _imageProvider,
),
],
),
);
} else {
return Container();
}
}
///提示Widget
_buildTipsWidget(double width, double height) {
if (_showTipsWidget) {
return Container(
alignment: Alignment.center,
width: width,
height: height,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(_tipsContent,
maxLines: 3,
style: TextStyle(color: Colors.red),
textAlign: TextAlign.center),
SizedBox(
height: 5.0,
),
OutlinedButton(
style: OutlinedButton.styleFrom(
shape: BeveledRectangleBorder(
side: BorderSide(
style: BorderStyle.solid,
color: Colors.blue,
width: 5,
),
borderRadius: BorderRadius.circular(5),
),
),
child: Text(
"Replay",
style: TextStyle(color: Colors.white),
),
onPressed: () {
setState(
() {
_showTipsWidget = false;
},
);
fAliplayer.prepare();
fAliplayer.play();
},
),
],
),
);
} else {
return Container();
}
}
//网络提示Widget
_buildNetWorkTipsWidget(double widgetWidth, double widgetHeight) {
return Offstage(
offstage: !_isShowMobileNetWork,
child: Container(
alignment: Alignment.center,
width: widgetWidth,
height: widgetHeight,
child: Wrap(
direction: Axis.vertical,
alignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Text("当前为移动网络",
style: TextStyle(color: Colors.white),
textAlign: TextAlign.center),
SizedBox(
height: 30.0,
),
Wrap(
direction: Axis.horizontal,
children: [
OutlinedButton(
style: OutlinedButton.styleFrom(
shape: BeveledRectangleBorder(
side: BorderSide(
style: BorderStyle.solid,
color: Colors.blue,
width: 5,
),
borderRadius: BorderRadius.circular(5),
),
),
child: Text("继续播放", style: TextStyle(color: Colors.white)),
onPressed: () {
setState(() {
_isShowMobileNetWork = false;
});
fAliplayer.play();
},
),
SizedBox(
width: 10.0,
),
OutlinedButton(
style: OutlinedButton.styleFrom(
shape: BeveledRectangleBorder(
side: BorderSide(
style: BorderStyle.solid,
color: Colors.blue,
width: 5,
),
borderRadius: BorderRadius.circular(5),
),
),
child: Text("退出播放", style: TextStyle(color: Colors.white)),
onPressed: () {
setState(
() {
_isShowMobileNetWork = false;
Navigator.pop(context);
},
);
},
),
],
),
],
),
),
);
}
///Loading
_buildProgressBar(double width, double height) {
if (_showLoading) {
return Positioned(
left: width / 2 - 20,
top: height / 2 - 20,
child: Column(
children: [
CircularProgressIndicator(
backgroundColor: Colors.white,
strokeWidth: 3.0,
),
SizedBox(
height: 10.0,
),
Text(
"$_loadingPercent%",
style: TextStyle(color: Colors.white),
),
],
),
);
} else {
return SizedBox();
}
}
///播放进度和buffer
_buildContentWidget(Orientation orientation) {
return SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(left: 5.0),
child: Text(
"buffer : ${FormatterUtils.getTimeformatByMs(_bufferPosition)}",
style: TextStyle(color: Colors.white, fontSize: 11),
),
),
Row(
children: [
SizedBox(
width: 5.0,
),
Text(
"${FormatterUtils.getTimeformatByMs(_currentPositionText)} / ${FormatterUtils.getTimeformatByMs(_videoDuration)}",
style: TextStyle(color: Colors.white, fontSize: 11),
),
Expanded(
child: AliyunSlider(
max: _videoDuration == 0 ? 1 : _videoDuration.toDouble(),
min: 0,
dotList: dotPositionList,
bufferColor: Colors.white,
bufferValue: _bufferPosition.toDouble(),
value: _currentPosition.toDouble(),
onChangeStart: (value) {
_inSeek = true;
_showLoading = false;
setState(() {});
},
onChangeEnd: (value) {
_inSeek = false;
setState(
() {
if (_currentPlayerState == FlutterAvpdef.completion &&
_showTipsWidget) {
setState(
() {
_showTipsWidget = false;
},
);
}
},
);
fAliplayer.seekTo(
value.ceil(),
GlobalSettings.mEnableAccurateSeek
? FlutterAvpdef.ACCURATE
: FlutterAvpdef.INACCURATE);
},
onTapDot: (value) {
if (_thumbnailSuccess) {
fAliplayer.requestBitmapAtPosition(value.ceil());
}
setState(
() async {
_inSeek = true;
await Future.delayed(
const Duration(milliseconds: 1500));
_inSeek = false;
},
);
},
onChanged: (value) {
if (_thumbnailSuccess) {
fAliplayer.requestBitmapAtPosition(value.ceil());
}
setState(
() {
_currentPosition = value.ceil();
},
);
},
),
),
IconButton(
icon: Icon(
orientation == Orientation.portrait
? Icons.fullscreen
: Icons.fullscreen_exit,
color: Colors.white,
),
onPressed: () {
if (orientation == Orientation.portrait) {
SystemChrome.setPreferredOrientations(
[
DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight
],
);
} else {
SystemChrome.setPreferredOrientations(
[
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown
],
);
}
},
),
],
),
],
),
);
}
//底部tab
_buildBottomNavigationBar(Orientation orientation) {
if (orientation == Orientation.portrait) {
return BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
label: "options", icon: Icon(Icons.control_point)),
BottomNavigationBarItem(
label: "config", icon: Icon(Icons.control_point)),
BottomNavigationBarItem(
label: "filter", icon: Icon(Icons.control_point)),
BottomNavigationBarItem(
label: "cache", icon: Icon(Icons.control_point)),
BottomNavigationBarItem(
label: "track", icon: Icon(Icons.control_point)),
],
currentIndex: bottomIndex,
onTap: (index) {
if (index != bottomIndex) {
setState(
() {
bottomIndex = index;
},
);
}
},
);
}
}
_buildCoverImg() {
if (_showCoverImg) {
return Image.asset("images/background_push.png");
} else {
return SizedBox();
}
}
_buildMarqueeView() {
return Container(
height: 36,
decoration: BoxDecoration(),
child: Row(
children: <Widget>[
Container(
decoration: const BoxDecoration(),
),
Expanded(
child: MarqueeView(
child: Text(
"阿里云播放器",
style: TextStyle(color: Colors.blue),
),
),
),
],
),
);
}
_generatePlayConfigGen() {
FlutterAliplayer.createVidPlayerConfigGenerator();
FlutterAliplayer.setPreviewTime(0);
}
}