flutter_aliplayer/example/lib/page/player_page.dart

1020 lines
30 KiB
Dart
Raw Permalink Normal View History

2023-12-06 11:23:26 +08:00
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);
}
}