You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

690 lines
25 KiB
Dart

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:convert';
import 'dart:ui';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/screen_util.dart';
import 'package:flutter_superplayer/flutter_superplayer.dart';
import 'package:flutterptcontrol/flutterptcontrol.dart';
import 'package:hyzp_ybqx/components/commonFun.dart';
import 'package:hyzp_ybqx/components/dioFun.dart';
import 'package:hyzp_ybqx/provider/player_ratio.dart';
import 'package:hyzp_ybqx/provider/player_region.dart';
import 'package:provider/provider.dart';
import 'package:fijkplayer/fijkplayer.dart';
import '../components/commonFun.dart';
import '../services/Storage.dart';
class SuperPlayerPage extends StatefulWidget {
SuperPlayerPage(
{@required this.url,
this.id = -2, // 播放点位视频的点位编号,-2 表示播放违章视频
this.dwip = '', // 点位IP用于点位视频控制球机方向
this.loop = 1,
this.title = 'Tencent Player',
Key key})
: super(key: key);
String dwip;
String url;
String title;
int loop; //设置播放循环默认播放器的循环次数是1 即不循环播放。如果设置循环次数0表示无限循环。
int id;
@override
_SuperPlayerPageState createState() => _SuperPlayerPageState();
}
class _SuperPlayerPageState extends State<SuperPlayerPage> with SuperPlayerListener {
final SuperPlayerController _playerController = SuperPlayerController();
final FijkPlayer _ijkPlayer = (){
var player = FijkPlayer();
player.setOption(FijkOption.playerCategory, "packet-buffering", 0);
player.setOption(FijkOption.formatCategory, "probesize", 1024);
return player;
}();
String _sdkVersion = 'Unknown';
List<String> _logs = [];
bool bFullScreen = false;
@override
void dispose() {
Playing = false;
// 云台控制代码1停止动作、3启动雨刷、11焦距变大、12焦距变小
setSphericalCameraDio(id: widget.id, dwip: widget.dwip, cmdCode: 1);
super.dispose();
_ijkPlayer.release();
_playerController.resetPlayer();
_playerController.release();
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
}
@override
void initState() {
super.initState();
init();
}
Future<void> init() async {
await _playerController.addListener(this);
await initPlatformState();
if (!mounted) return;
if (_useIJKPlayer) {
await _playerController.removeListener(this);
_ijkPlayer.addListener(_fijkValueListener);
_ijkPlayer.setDataSource(widget.url, autoPlay: true);
} else {
await _playerController.uiHideDanmu();
if (0 == widget.loop) {
await _playerController.setLoop(true);
}
await _playerController.playWithModel(testSuperPlayerModel);
}
}
bool get _useIJKPlayer {
return Platform.isIOS && widget.url.startsWith("rtmp");
}
SuperPlayerModel get testSuperPlayerModel {
return SuperPlayerModel(url: widget.url);
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
String sdkVersion;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
sdkVersion = await FlutterSuperPlayer.sdkVersion;
} on PlatformException {
sdkVersion = 'Failed to get platform version.';
}
print('sdkVersion = ${sdkVersion}');
print('mounted = ${mounted}');
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
_sdkVersion = sdkVersion;
});
}
void _addLog(String method, dynamic data) {
_logs.add('>>>$method');
if (data != null) {
_logs.add(data is Map ? json.encode(data) : data);
}
_logs.add(' ');
setState(() {});
}
PlayerRatioProvide playerRatioProvide;
@override
Widget build(BuildContext context) {
playerRegionProvide = Provider.of<PlayerRegionProvide>(context);
playerRatioProvide = Provider.of<PlayerRatioProvide>(context);
// List<Widget> listData = getDataListControl2();
double btnHeight1 = 70; //第一按钮行高度
double btnHeight2 = 160; //第二按钮行高度
int btnCount = 4; //每行按钮个数
// int btnCount3 = listData.length; //每行按钮个数
var mediaSize = MediaQuery.of(context).size;
// widget.id播放点位视频的点位编号-2 表示播放违章视频
double btn_left = -2 == widget.id ? 347 : 70; //第一按钮行高度
double btn_gap = -2 == widget.id ? 104 : 70; //第一按钮行高度
//远程控制球机方向按钮外半径和内半径
double _outerRadius = 270;
double _innerRadius = _outerRadius / 2;
return Scaffold(
appBar: bFullScreen
? null
: PreferredSize(
preferredSize: Size.fromHeight(ScreenUtil().setHeight(173)), // 设置appBar高度
// 设置appBar高度
child: AppBar(
automaticallyImplyLeading: false,
centerTitle: true,
titleSpacing: 0.0,
flexibleSpace: Container(
padding: EdgeInsets.only(top: ScreenUtil().statusBarHeight), //留出顶部状态栏高度
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
Color.fromRGBO(12, 186, 156, 1),
Color.fromRGBO(39, 127, 235, 1),
],
),
),
),
),
title: Padding(
padding: EdgeInsets.only(left: 0, right: 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
getIconAndTextButton(
iconColor: Colors.white,
iconData: Icons.chevron_left_outlined,
onPress: () {
getingDwVideo = false;
pop();
},
),
Expanded(
child: Text(widget.title,
style: TextStyle(color: Colors.white, fontSize: 18),
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis),
),
SizedBox(width: 50),
],
),
),
),
),
body: WillPopScope(
child: Container(
color: Color.fromRGBO(224, 224, 224, 1),
child: Column(
children: <Widget>[
//第2行组件视频播放区
Center(
child: Container(
alignment: Alignment(0, -1),
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.width * (9 / 16),
color: Colors.black,
child: Stack(
children: [
_playState <= 2 ? getMoreWidget(strokeWidth: 3.0) : SizedBox.shrink(),
_useIJKPlayer ? FijkView(player: _ijkPlayer, color: Colors.black) : SuperPlayerView(controller: _playerController)
],
),
),
),
//第3.1行组件,控制按钮区
bFullScreen ? SizedBox.shrink() : SizedBox(height: ScreenUtil().setHeight(69)),
bFullScreen
? SizedBox.shrink()
: Row(
children: [
SizedBox(width: ScreenUtil().setWidth(btn_left)),
getRoundButton(
text: playerRegionProvide.playerText,
icon: playerRegionProvide.playerIcon,
diameter: 130,
onPress: playOrPause,
),
SizedBox(width: ScreenUtil().setWidth(btn_gap)),
getRoundButton(
text: '刷新',
icon: Icons.autorenew,
diameter: 130,
onPress: () {
restartPlay(urlnew);
},
),
// widget.id播放点位视频的点位编号-2 表示播放违章视频
-2 == widget.id
? SizedBox.shrink()
: SizedBox(width: ScreenUtil().setWidth(btn_gap)),
-2 == widget.id
? SizedBox.shrink()
: getRoundButton_image(
text: '雨刷',
image_path: 'assets/images/wiper.png',
diameter: 130,
onPress: () {
// 云台控制代码1停止动作、3启动雨刷、11焦距变大、12焦距变小
setSphericalCameraDio(
id: widget.id, dwip: widget.dwip, cmdCode: 3);
},
),
-2 == widget.id
? SizedBox.shrink()
: SizedBox(width: ScreenUtil().setWidth(btn_gap)),
-2 == widget.id
? SizedBox.shrink()
: getRoundButton_image(
text: '放大',
image_path: 'assets/images/zoom_in.png',
imageSize: 72,
diameter: 130,
onPress: () {
// 云台控制代码1停止动作、3启动雨刷、11焦距变大、12焦距变小
setSphericalCameraDio(
id: widget.id, dwip: widget.dwip, cmdCode: 11);
},
),
-2 == widget.id
? SizedBox.shrink()
: SizedBox(width: ScreenUtil().setWidth(btn_gap)),
-2 == widget.id
? SizedBox.shrink()
: getRoundButton_image(
text: '缩小',
image_path: 'assets/images/zoom_out.png',
imageSize: 72,
diameter: 130,
onPress: () {
// 云台控制代码1停止动作、3启动雨刷、11焦距变大、12焦距变小
setSphericalCameraDio(
id: widget.id, dwip: widget.dwip, cmdCode: 12);
},
),
],
),
bFullScreen ? SizedBox.shrink() : SizedBox(height: ScreenUtil().setHeight(79)),
bFullScreen
? SizedBox.shrink()
: Container(
height: ScreenUtil().setHeight(2 * _outerRadius),
width: ScreenUtil().setWidth(2 * _outerRadius),
child: PtControlWidget(
// innerRadius: _innerRadius,
// outerRadius: _outerRadius,
// 进行像素单位转换,解决按钮响应错乱问题
innerRadius: _innerRadius / ScreenUtil().pixelRatio,
outerRadius: _outerRadius / ScreenUtil().pixelRatio,
callback: -2 == widget.id // 播放点位视频的点位编号,-2 表示播放违章视频
? null
: (status) {
///点击回调
print('status = $status');
switch (status) {
/// status == -1 超出范围
/// status == 0 右
case 0:
setSphericalCameraDio(
id: widget.id, dwip: widget.dwip, cmdCode: 24);
break;
/// status == 1 右上
case 1:
setSphericalCameraDio(
id: widget.id, dwip: widget.dwip, cmdCode: 26);
break;
///三、球机位移接口方向代码说明:
// 上8 下4 左2 右1 左上10 左下6 右上9 右下5
// 云台控制代码1停止动作、3启动雨刷、11焦距变大、12焦距变小
// 21上移
// 22下移
// 23左移
// 24右移
// 25左上移动
// 26右上移动
// 27左下移动
// 28右下移动
/// status == 2 上
case 2:
setSphericalCameraDio(
id: widget.id, dwip: widget.dwip, cmdCode: 21);
break;
/// status == 3 左上
case 3:
setSphericalCameraDio(
id: widget.id, dwip: widget.dwip, cmdCode: 25);
break;
/// status == 4 左
case 4:
setSphericalCameraDio(
id: widget.id, dwip: widget.dwip, cmdCode: 23);
break;
/// status == 5 左下
case 5:
setSphericalCameraDio(
id: widget.id, dwip: widget.dwip, cmdCode: 27);
break;
/// status == 6 下
case 6:
setSphericalCameraDio(
id: widget.id, dwip: widget.dwip, cmdCode: 22);
break;
/// status == 7 右下
case 7:
setSphericalCameraDio(
id: widget.id, dwip: widget.dwip, cmdCode: 28);
break;
/// status == 8 还原
case 8:
break;
default:
break;
}
},
),
),
],
),
),
onWillPop: () {
// 云台控制代码1停止动作、3启动雨刷、11焦距变大、12焦距变小
setSphericalCameraDio(id: widget.id, dwip: widget.dwip, cmdCode: 1);
pop();
},
),
);
}
pop() async {
Playing = false;
getingDwVideo = false;
_ijkPlayer.removeListener(() {});
await _playerController.removeListener(this);
Navigator.pop(context);
}
@override
void onClickFloatCloseBtn() {
_addLog('onClickFloatCloseBtn', {});
}
@override
void onClickSmallReturnBtn() {
_addLog('onClickSmallReturnBtn', {});
pop();
}
@override
void onFullScreenChange(bool isFullScreen) {
_addLog('onFullScreenChange', {'isFullScreen': isFullScreen});
if (bFullScreen == isFullScreen) return;
bFullScreen = isFullScreen;
setState(() {});
}
@override
void onPlayProgressChange(int current, int duration) {
_addLog('onPlayProgressChange', {'current': current, 'duration': duration});
}
int _playState = 4;
int i = 0;
@override
void onPlayStateChange(int playState) {
_playState = playState;
i++;
print('$i、playState = $playState');
_addLog('onPlayStateChange', {'playState': playState});
setPlayOrPauseIcon(playState);
}
void setPlayOrPauseIcon(int state) {
//state : 1 播放状态2 暂停状态
print('state = $state');
if (1 == state) {
bPlaying = true;
} else {
bPlaying = false;
}
playerRegionProvide.changePlayerState(bPlaying);
Storage.setString('bPlaying', bPlaying ? 'true' : 'false');
setState(() {});
}
@override
void onStartFloatWindowPlay() {
_addLog('onStartFloatWindowPlay', {});
}
void _fijkValueListener() {
FijkValue value = _ijkPlayer.value;
onPlayStateChange(value.state == FijkState.started ? 1 : 2);
onFullScreenChange(value.fullScreen);
}
//生成圆形按钮部件,基于图标
Widget getRoundButton(
{double diameter = 144,
double marginVer = 10,
String text,
IconData icon,
double fontSize = 16,
double iconSize = 90,
Color color = const Color.fromRGBO(52, 157, 237, 1),
var onPress}) {
return InkWell(
onTap: onPress,
child: Column(
children: [
Container(
width: ScreenUtil().setWidth(diameter),
height: ScreenUtil().setHeight(diameter),
alignment: Alignment.center,
child: Icon(
icon,
size: ScreenUtil().setWidth(iconSize),
color: Color.fromRGBO(52, 157, 237, 1),
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(200)),
border: Border.all(width: 0, style: BorderStyle.none),
),
),
SizedBox(height: ScreenUtil().setHeight(marginVer)),
Text(text, style: TextStyle(fontSize: fontSize, color: Color.fromRGBO(139, 139, 139, 1))),
],
),
);
}
//生成圆形按钮部件,基于图片
Widget getRoundButton_image(
{double diameter = 144,
double marginVer = 10,
String text,
String image_path,
double fontSize = 16,
double imageSize = 90,
Color color = const Color.fromRGBO(52, 157, 237, 1),
Color color_bkg = Colors.white,
var onPress}) {
return InkWell(
onTap: onPress,
child: Column(
children: [
Container(
width: ScreenUtil().setWidth(diameter),
height: ScreenUtil().setHeight(diameter),
alignment: Alignment.center,
child: Image.asset(image_path,
fit: BoxFit.fitWidth,
width: ScreenUtil().setWidth(imageSize),
color: Color.fromRGBO(52, 157, 237, 1)),
decoration: BoxDecoration(
color: color_bkg,
borderRadius: BorderRadius.all(Radius.circular(200)),
border: Border.all(width: 0, style: BorderStyle.none),
),
),
SizedBox(height: ScreenUtil().setHeight(marginVer)),
Text(text, style: TextStyle(fontSize: fontSize, color: Color.fromRGBO(139, 139, 139, 1))),
],
),
);
}
bool bPlaying = true;
Future<void> playOrPause() async {
//state : 1 播放状态2 暂停状态
int state;
if (_useIJKPlayer) {
state = _ijkPlayer.state == FijkState.started ? 1 : 2;
} else {
state = await _playerController.getPlayState();
}
if (1 == state) {
bPlaying = false;
_useIJKPlayer ? _ijkPlayer.pause() : _playerController.pause();
} else {
bPlaying = true;
_useIJKPlayer ? _ijkPlayer.start() : _playerController.resume();
}
playerRegionProvide.changePlayerState(bPlaying);
Storage.setString('bPlaying', bPlaying ? 'true' : 'false');
setState(() {});
}
void restartPlay(String url) async {
_playState = 4;
bPlaying = true;
if (_useIJKPlayer) {
await _ijkPlayer.reset();
_ijkPlayer.setDataSource(widget.url, autoPlay: true);
} else {
_playerController.resetPlayer();
_playerController.resume();
}
}
//生成播放控制区第2行按钮List
// List<Widget> getDataListControl2() {
// double _diameter = 100;
// double _iconSize = 70;
// double _fontSize = 14;
// double _marginVer = 8;
//
// List<Widget> list = [
// // getRoundButton(
// // diameter: _diameter,
// // iconSize: _iconSize,
// // text: '快退',
// // icon: Icons.fast_rewind,
// // onPress: () {
// // fastSeek(false);
// // },
// // ),
// // getRoundButton(
// // diameter: _diameter,
// // iconSize: _iconSize,
// // text: '快进',
// // icon: Icons.fast_forward,
// // onPress: () {
// // fastSeek(true);
// // },
// // ),
// getRoundButton(
// diameter: _diameter,
// iconSize: _iconSize,
// fontSize: _fontSize,
// marginVer: _marginVer,
// text: '放大',
// icon: Icons.zoom_in,
// onPress: () {
// //print('Icons.videocam');
// //_inputDialog(context2);
// if (10 >= playerRatioProvide.scale) {
// playerRatioProvide.changeScale(playerRatioProvide.scale + 0.5);
// }
// },
// ),
// getRoundButton(
// diameter: _diameter,
// iconSize: _iconSize,
// fontSize: _fontSize,
// marginVer: _marginVer,
// text: '缩小',
// icon: Icons.zoom_out,
// onPress: () {
// //print('Icons.videocam');
// //_getFileDialog(context2);
// if (1 < playerRatioProvide.scale) {
// playerRatioProvide.changeScale(playerRatioProvide.scale - 0.5);
// }
// },
// ),
// // _getIconAndTextButton(
// // '还原',
// // Icons.reply,
// // Colors.orange,
// // () {
// // //print('Icons.videocam');
// // //_getFileDialog(context2);
// // // 更新当前位置
// // playerRatioProvide.changeScale(1.0);
// // playerRatioProvide.changeOffset(Offset(0, 0));
// // playerRatioProvide.changeDeltaX(0.0);
// // playerRatioProvide.changeDeltaY(0.0);
// // },
// // ),
// getRoundButton(
// diameter: _diameter,
// iconSize: _iconSize,
// fontSize: _fontSize,
// marginVer: _marginVer,
// text: '截图',
// icon: Icons.camera_alt,
// onPress: () {
// //通过上面定义的key才能准确调用该类型的该对象的方法
// //_myFijkPanelWidgetBuilderStateKey.currentState.takeSnapshot();
// //_fijkPanelWidgetBuilder.currentState..takeSnapshot();
// takeSnapshot();
// },
// ),
// getRoundButton(
// diameter: _diameter,
// iconSize: _iconSize,
// fontSize: _fontSize,
// marginVer: _marginVer,
// text: '全屏',
// icon: Icons.fullscreen,
// onPress: () {
// //player.enterFullScreen();
// _playerController.toFullScreen();
// },
// ),
// ];
// return list;
// }
void takeSnapshot() {
// player.takeSnapShot().then((v) {
// var provider = MemoryImage(v);
// precacheImage(provider, context).then((_) {
// setState(() {
// myImageProvider = provider;
// });
// });
// FijkLog.d("get snapshot succeed");
//
// //Uint8List fileData;
// String fileFath;
// ImageSaver.save('extended_image_cropped_image.jpg', v).then((value) {
// fileFath = value;
// // var fileFath = await ImagePickerSaver.saveFile(fileData: fileData);
// print('my save fileFath : $fileFath');
// });
// }).catchError((e) {
// FijkLog.d("get snapshot failed");
// });
}
}