diff --git a/lib/my_fijkPanel_fix/my_fijkvalue.dart b/lib/my_fijkPanel_fix/my_fijkvalue.dart new file mode 100644 index 0000000..f9cc64c --- /dev/null +++ b/lib/my_fijkPanel_fix/my_fijkvalue.dart @@ -0,0 +1,50 @@ +//MIT License +// +//Copyright (c) [2019] [Befovy] +// +//Permission is hereby granted, free of charge, to any person obtaining a copy +//of this software and associated documentation files (the "Software"), to deal +//in the Software without restriction, including without limitation the rights +//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//copies of the Software, and to permit persons to whom the Software is +//furnished to do so, subject to the following conditions: +// +//The above copyright notice and this permission notice shall be included in all +//copies or substantial portions of the Software. +// +//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//SOFTWARE. + +//r:\Flutter\FlutterSDK\flutter\.pub-cache\hosted\pub.flutter-io.cn\fijkplayer-0.8.4\lib\core\MyFijkValue.dart +//part of fijkplayer; + +import 'dart:core'; + +//新增的,为变私有成员为公有成员 +class MyFijkData { + static String fijkViewPanelVolume = "__fijkview_panel_init_volume"; + static String fijkViewPanelBrightness = "__fijkview_panel_init_brightness"; + static String fijkViewPanelSeekto = "__fijkview_panel_sekto_position"; +} + +//新增的,为变私有成员为公有成员 +String mYduration2String(Duration duration) { + if (duration.inMilliseconds < 0) return "-: negtive"; + + String twoDigits(int n) { + if (n >= 10) return "$n"; + return "0$n"; + } + + String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60)); + String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60)); + int inHours = duration.inHours; + return inHours > 0 + ? "$inHours:$twoDigitMinutes:$twoDigitSeconds" + : "$twoDigitMinutes:$twoDigitSeconds"; +} diff --git a/lib/my_fijkPanel_fix/my_panel3.dart b/lib/my_fijkPanel_fix/my_panel3.dart new file mode 100644 index 0000000..c4225a3 --- /dev/null +++ b/lib/my_fijkPanel_fix/my_panel3.dart @@ -0,0 +1,828 @@ +import 'dart:async'; +import 'dart:core'; +import 'dart:math'; +import 'dart:ui'; +import 'dart:typed_data'; + +import 'package:fijkplayer/fijkplayer.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/widgets.dart'; +import 'my_fijkvalue.dart'; +import '../components/commonFun.dart'; +import 'package:provider/provider.dart'; +import '../provider/player_ratio.dart'; +import 'my_slider.dart'; +import '../my_extended_image/common/image_picker/_image_picker_io.dart'; +import '../services/Storage.dart'; + +// class MyFijkPanelWidgetBuilder extends StatefulWidget { +// const MyFijkPanelWidgetBuilder({Key key, this.path}) : super(key: key); +// final path; +// @override +// State createState() { +// return MyFijkPanelWidgetBuilderState(); +// } +// } + +// snapshot +ImageProvider myImageProvider; +//ImageProvider _imageProvider; +//Timer _snapshotTimer; + +FijkPanelWidgetBuilder fijkPanel2Builder3( + {Key key, + final FijkPlayer player, + final bool fill = false, + final int duration = 4000, + final bool doubleTap = true, + final bool snapShot = false, + final VoidCallback onBack}) { + return (FijkPlayer player, FijkData data, BuildContext context, Size viewSize, + Rect texturePos) { + return MyFijkPanelWidgetBuilder( + key: key, + player: player, + data: data, + onBack: onBack, + viewSize: viewSize, + texPos: texturePos, + fill: fill, + doubleTap: doubleTap, + snapShot: snapShot, + hideDuration: duration, + ); + }; +} + +class MyFijkPanelWidgetBuilder extends StatefulWidget { + final FijkPlayer player; + final FijkData data; + final VoidCallback onBack; + final Size viewSize; + final Rect texPos; + final bool fill; + final bool doubleTap; + final bool snapShot; + final int hideDuration; + + const MyFijkPanelWidgetBuilder( + {Key key, + @required this.player, + this.data, + this.fill, + this.onBack, + this.viewSize, + this.hideDuration, + this.doubleTap, + this.snapShot, + this.texPos}) + : assert(player != null), + assert( + hideDuration != null && hideDuration > 0 && hideDuration < 10000), + super(key: key); + + // @override + // FijkPanel2State createState() => FijkPanel2State(); + // @override + //State createState() { + MyFijkPanelWidgetBuilderState createState() { + return MyFijkPanelWidgetBuilderState(player); + } +} + +class MyFijkPanelWidgetBuilderState extends State + with SingleTickerProviderStateMixin { + FijkPlayer player; + + MyFijkPanelWidgetBuilderState(@required this.player) { + _oldFullScreen = player.value.fullScreen; + } + + PlayerRatioProvide playerRatioProvide; + AnimationController _controller; + Animation _animation; + Offset _normalizedOffset; + double _previousScale; + double _kMinFlingVelocity = 600.0; + + Offset _offset = Offset.zero; + double _scale = 1.0; + Offset _focalPoint = Offset.zero; + Offset _deltaPoint = Offset.zero; + + bool _oldFullScreen; + bool _bScaleing = false; + + //FijkPlayer get player => widget.player; + + Timer _hideTimer; + bool _hideStuff = true; + + Timer _statelessTimer; + bool _prepared = false; + bool _playing = false; + + //bool _dragLeft; + int _nDrag; //0 Left, 1 Center, 2 Right + double _volume; + double _brightness; + double _deltaX = 0; + double _deltaY = 0; + + double _seekPos = -1.0; + Duration _duration = Duration(); + Duration _currentPos = Duration(); + Duration _bufferPos = Duration(); + + StreamSubscription _currentPosSubs; + StreamSubscription _bufferPosSubs; + + StreamController _valController; + + // snapshot + //ImageProvider _imageProvider; + Timer _snapshotTimer; + + // Is it needed to clear seek data in FijkData (widget.data) + bool _needClearSeekData = true; + + static const MyFijkSliderColors sliderColors = MyFijkSliderColors(); + + @override + void initState() { + super.initState(); + + _controller = AnimationController(vsync: this); + _controller.addListener(() { + setState(() { + _offset = _animation.value; + }); + }); + + _valController = StreamController.broadcast(); + _prepared = player.state.index >= FijkState.prepared.index; + _playing = player.state == FijkState.started; + _duration = player.value.duration; + _currentPos = player.currentPos; + _bufferPos = player.bufferPos; + + _currentPosSubs = player.onCurrentPosUpdate.listen((v) { + if (_hideStuff == false) { + setState(() { + _currentPos = v; + }); + } else { + _currentPos = v; + } + if (_needClearSeekData) { + widget.data.clearValue(MyFijkData.fijkViewPanelSeekto); + } + _needClearSeekData = false; + }); + + if (widget.data.contains(MyFijkData.fijkViewPanelSeekto)) { + var pos = widget.data.getValue(MyFijkData.fijkViewPanelSeekto) as double; + _currentPos = Duration(milliseconds: pos.toInt()); + } + + _bufferPosSubs = player.onBufferPosUpdate.listen((v) { + if (_hideStuff == false) { + setState(() { + _bufferPos = v; + }); + } else { + _bufferPos = v; + } + }); + player.addListener(_playerValueChanged); + } + + @override + void dispose() { + super.dispose(); + _controller.dispose(); + _valController?.close(); + _hideTimer?.cancel(); + _statelessTimer?.cancel(); + _snapshotTimer?.cancel(); + _currentPosSubs?.cancel(); + _bufferPosSubs?.cancel(); + player.removeListener(_playerValueChanged); + } + + double dura2double(Duration d) { + return d != null ? d.inMilliseconds.toDouble() : 0.0; + } + + void _playerValueChanged() { + FijkValue value = player.value; + + if (value.duration != _duration) { + if (_hideStuff == false) { + setState(() { + _duration = value.duration; + }); + } else { + _duration = value.duration; + } + } + bool playing = (value.state == FijkState.started); + bool prepared = value.prepared; + if (playing != _playing || + prepared != _prepared || + value.state == FijkState.asyncPreparing) { + setState(() { + _playing = playing; + _prepared = prepared; + }); + } + } + + void _restartHideTimer() { + _hideTimer?.cancel(); + _hideTimer = Timer(Duration(milliseconds: widget.hideDuration), () { + setState(() { + _hideStuff = true; + }); + }); + } + + void onTapFun() { + if (_hideStuff == true) { + _restartHideTimer(); + } + setState(() { + _hideStuff = !_hideStuff; + }); + } + + void playOrPause() { + if (player.isPlayable() || player.state == FijkState.asyncPreparing) { + if (player.state == FijkState.started) { + //bPlaying = false; + playerRegionProvide.changePlayerState(false); + Storage.setString('bPlaying', 'false'); + player.pause(); + } else { + //bPlaying = true; + playerRegionProvide.changePlayerState(true); + Storage.setString('bPlaying', 'true'); + player.start(); + } + //setState(() {}); + } else { + FijkLog.w("Invalid state ${player.state} ,can't perform play or pause"); + } + } + + void onVerticalDragStartFun(DragStartDetails d) { + // if (_bScaleing) { + // return; + // } + if (d.localPosition.dx > panelWidth() * 3 / 4) { + // right, volume + //_dragLeft = false; + _nDrag = 2; //0 Left, 1 Center, 2 Right + + //https://fijkplayer.befovy.com/docs/zh/system-volume.html#gsc.tab=0 + /// never show system volume changed UI. + int neverShowUI = 2; + FijkVolume.setUIMode(neverShowUI); + + FijkVolume.getVol().then((v) { + if (widget.data != null && + !widget.data.contains(MyFijkData.fijkViewPanelVolume)) { + widget.data.setValue(MyFijkData.fijkViewPanelVolume, v); + } + setState(() { + _volume = v; + _valController.add(v); + }); + }); + } else if (d.localPosition.dx < panelWidth() / 4) { + // left, brightness + //_dragLeft = true; + _nDrag = 0; //0 Left, 1 Center, 2 Right + FijkPlugin.screenBrightness().then((v) { + if (widget.data != null && + !widget.data.contains(MyFijkData.fijkViewPanelBrightness)) { + widget.data.setValue(MyFijkData.fijkViewPanelBrightness, v); + } + setState(() { + _brightness = v; + _valController.add(v); + }); + }); + } else { + _nDrag = 1; //0 Left, 1 Center, 2 Right + // setState(() { + // if (_oldFullScreen != player.value.fullScreen) { + // //全屏和窗口之间切换,便初始化相关变量 + // _oldFullScreen = player.value.fullScreen; + // // _offset = Offset.zero; + // // _scale = 1.0; + // //_focalPoint = Offset.zero; + // //_deltaPoint = Offset.zero; + // } + // // _bScaleing = true; + // // _previousScale = _scale; + // // _normalizedOffset = (d.localPosition - _offset) / _scale; + // // // 计算图片放大后的位置 + // // _controller.stop(); + // }); + } + _statelessTimer?.cancel(); + _statelessTimer = Timer(const Duration(milliseconds: 2000), () { + setState(() {}); + }); + } + + void onVerticalDragUpdateFun(DragUpdateDetails d) { + // if (_bScaleing) { + // return; + // } + + double delta = d.primaryDelta / panelHeight(); + print("d.primaryDelta = ${d.primaryDelta}, delta = {$delta}"); + + delta = -delta.clamp(-1.0, 1.0); + //if (_dragLeft != null && _dragLeft == false) { + if (_nDrag != null && _nDrag == 2) { + if (_volume != null) { + _volume += delta; + _volume = _volume.clamp(0.0, 1.0); + FijkVolume.setVol(_volume); + setState(() { + _valController.add(_volume); + }); + } + //} else if (_dragLeft != null && _dragLeft == true) { + } else if (_nDrag != null && _nDrag == 0) { + if (_brightness != null) { + _brightness += delta; + _brightness = _brightness.clamp(0.0, 1.0); + FijkPlugin.setScreenBrightness(_brightness); + setState(() { + _valController.add(_brightness); + }); + } + } else { + setState(() { + //_scale = (_previousScale * details.scale).clamp(1.0, 10.0); + // 限制放大倍数 1~10倍 + // _offset = _clampOffset(d.localPosition - _normalizedOffset * _scale); + // playerRatioProvide.changeOffset(_offset); + + _deltaY -= 2 * d.primaryDelta / panelHeight(); + _deltaY = _deltaY.clamp(-1.0, 1.0); + playerRatioProvide.changeDeltaY(_deltaY); + // 更新当前位置 + }); + } + } + + void onVerticalDragEndFun(DragEndDetails e) { + _volume = null; + _brightness = null; + } + + Widget buildPlayButton(BuildContext context, double height) { + Icon icon = (player.state == FijkState.started) + ? Icon(Icons.pause) + : Icon(Icons.play_arrow); + bool fullScreen = player.value.fullScreen; + return IconButton( + padding: EdgeInsets.all(0), + iconSize: fullScreen ? height : height * 0.8, + color: Color(0xFFFFFFFF), + icon: icon, + onPressed: playOrPause, + ); + } + + Widget buildFullScreenButton(BuildContext context, double height) { + Icon icon = player.value.fullScreen + ? Icon(Icons.fullscreen_exit) + : Icon(Icons.fullscreen); + bool fullScreen = player.value.fullScreen; + return IconButton( + padding: EdgeInsets.all(0), + iconSize: fullScreen ? height : height * 0.8, + color: Color(0xFFFFFFFF), + icon: icon, + onPressed: () { + player.value.fullScreen + ? player.exitFullScreen() + : player.enterFullScreen(); + }, + ); + } + + Widget buildTimeText(BuildContext context, double height) { + String text = "${mYduration2String(_currentPos)}" + + "/${mYduration2String(_duration)}"; + return Text(text, style: TextStyle(fontSize: 12, color: Color(0xFFFFFFFF))); + } + + Widget buildSlider(BuildContext context) { + double duration = dura2double(_duration); + + double currentValue = _seekPos > 0 ? _seekPos : dura2double(_currentPos); + currentValue = currentValue.clamp(0.0, duration); + + double bufferPos = dura2double(_bufferPos); + bufferPos = bufferPos.clamp(0.0, duration); + + return Padding( + padding: EdgeInsets.only(left: 0), + child: MyFijkSlider( + colors: sliderColors, + value: currentValue, + cacheValue: bufferPos, + min: 0.0, + max: duration, + onChanged: (v) { + _restartHideTimer(); + setState(() { + _seekPos = v; + }); + }, + onChangeEnd: (v) { + setState(() { + player.seekTo(v.toInt()); + _currentPos = Duration(milliseconds: _seekPos.toInt()); + widget.data.setValue(MyFijkData.fijkViewPanelSeekto, _seekPos); + _needClearSeekData = true; + _seekPos = -1.0; + }); + }, + ), + ); + } + + Widget buildBottom(BuildContext context, double height) { + if (_duration != null && _duration.inMilliseconds > 0) { + return Row( + children: [ + buildPlayButton(context, height), + buildTimeText(context, height), + Expanded(child: buildSlider(context)), + buildFullScreenButton(context, height), + ], + ); + } else { + return Row( + children: [ + buildPlayButton(context, height), + Expanded(child: Container()), + buildFullScreenButton(context, height), + ], + ); + } + } + + 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"); + }); + } + + Widget buildPanel(BuildContext context) { + double height = panelHeight(); + + bool fullScreen = player.value.fullScreen; + Widget centerWidget = Container( + color: Color(0x00000000), + ); + + Widget centerChild = Container( + color: Color(0x00000000), + ); + + if (fullScreen && widget.snapShot) { + centerWidget = Row( + children: [ + Expanded(child: centerChild), + Padding( + padding: EdgeInsets.only(left: 10, right: 10, top: 8, bottom: 8), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + IconButton( + padding: EdgeInsets.all(0), + color: Color(0xFFFFFFFF), + icon: Icon(Icons.camera_alt), + onPressed: () { + takeSnapshot(); + }, + ), + ], + ), + ) + ], + ); + } + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Container( + height: height > 200 ? 80 : height / 5, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [Color(0x88000000), Color(0x00000000)], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + ), + Expanded( + child: centerWidget, + ), + Container( + height: height > 80 ? 80 : height / 2, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [Color(0x88000000), Color(0x00000000)], + end: Alignment.topCenter, + begin: Alignment.bottomCenter, + ), + ), + alignment: Alignment.bottomCenter, + child: Container( + height: height > 80 ? 45 : height / 2, + padding: EdgeInsets.only(left: 8, right: 8, bottom: 5), + child: buildBottom(context, height > 80 ? 40 : height / 2), + // child: CustomFijkWidgetBottom( + // player: player, + // buildContext: context, + // viewSize: + // Size(widget.viewSize.width, widget.viewSize.height - 50), + // texturePos: Rect.fromLTRB(widget.texPos.left, widget.texPos.top, + // widget.texPos.width, widget.texPos.height - 50), + //), + ), + ) + ], + ); + } + + Rect panelRect() { + Rect rect = player.value.fullScreen || (true == widget.fill) + ? Rect.fromLTWH(0, 0, widget.viewSize.width, widget.viewSize.height) + : Rect.fromLTRB( + max(0.0, widget.texPos.left), + max(0.0, widget.texPos.top), + min(widget.viewSize.width, widget.texPos.right), + min(widget.viewSize.height, widget.texPos.bottom)); + return rect; + } + + double panelHeight() { + if (player.value.fullScreen || (true == widget.fill)) { + return widget.viewSize.height; + } else { + return min(widget.viewSize.height, widget.texPos.bottom) - + max(0.0, widget.texPos.top); + } + } + + double panelWidth() { + if (player.value.fullScreen || (true == widget.fill)) { + return widget.viewSize.width; + } else { + return min(widget.viewSize.width, widget.texPos.right) - + max(0.0, widget.texPos.left); + } + } + + Widget buildBack(BuildContext context) { + return IconButton( + padding: EdgeInsets.only(left: 5), + icon: Icon( + Icons.arrow_back_ios, + color: Color(0xDDFFFFFF), + ), + onPressed: widget.onBack, + ); + } + + Widget buildStateless() { + if (_volume != null || _brightness != null) { + Widget toast = _volume == null + ? defaultFijkBrightnessToast(_brightness, _valController.stream) + : defaultFijkVolumeToast(_volume, _valController.stream); + return IgnorePointer( + child: AnimatedOpacity( + opacity: 1, + duration: Duration(milliseconds: 500), + child: toast, + ), + ); + } else if (player.state == FijkState.asyncPreparing) { + return Container( + alignment: Alignment.center, + child: SizedBox( + width: 30, + height: 30, + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation(Colors.white)), + ), + ); + } else if (player.state == FijkState.error) { + return Container( + alignment: Alignment.center, + child: Icon( + Icons.error, + size: 30, + color: Color(0x99FFFFFF), + ), + ); + } else if (myImageProvider != null) { + _snapshotTimer?.cancel(); + _snapshotTimer = Timer(Duration(milliseconds: 1500), () { + if (mounted) { + setState(() { + myImageProvider = null; + }); + } + }); + return Center( + child: IgnorePointer( + child: Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.yellowAccent, width: 3)), + child: + Image(height: 200, fit: BoxFit.contain, image: myImageProvider), + ), + ), + ); + } else { + return Container(); + } + } + + GestureDetector buildGestureDetector(BuildContext context) { + return GestureDetector( + onScaleStart: _handleOnScaleStart, + onScaleUpdate: _handleOnScaleUpdate, + onScaleEnd: _handleOnScaleEnd, + onDoubleTap: () { + //自定义 FijkView 的双击响应 + print('My onDoubleTap'); + if (1.0 == playerRatioProvide.scale) { + playOrPause(); + } else { + _handleScaleBack(); + } + }, + + onTap: onTapFun, + //onDoubleTap: widget.doubleTap ? onDoubleTapFun : null, + onVerticalDragUpdate: onVerticalDragUpdateFun, + onVerticalDragStart: onVerticalDragStartFun, + onVerticalDragEnd: onVerticalDragEndFun, + //onHorizontalDragUpdate: (d) {}, + child: AbsorbPointer( + absorbing: _hideStuff, + child: AnimatedOpacity( + opacity: _hideStuff ? 0 : 1, + duration: Duration(milliseconds: 300), + child: buildPanel(context), + ), + ), + ); + } + + void _handleOnScaleStart(ScaleStartDetails details) { + setState(() { + // if (_oldFullScreen != player.value.fullScreen) { + // //全屏和窗口之间切换,便初始化相关变量 + // _oldFullScreen = player.value.fullScreen; + // // _offset = Offset.zero; + // // _scale = 1.0; + // //_focalPoint = Offset.zero; + // //_deltaPoint = Offset.zero; + // } + + _bScaleing = true; + _previousScale = _scale; + _normalizedOffset = (details.focalPoint - _offset) / _scale; + // 计算图片放大后的位置 + _controller.stop(); + _focalPoint = details.focalPoint; + }); + } + + void _handleScaleBack() { + // 更新当前位置 + playerRatioProvide.changeScale(1.0); + playerRatioProvide.changeOffset(Offset(0, 0)); + playerRatioProvide.changeDeltaX(0.0); + playerRatioProvide.changeDeltaY(0.0); + } + + void _handleOnScaleUpdate(ScaleUpdateDetails details) { + setState(() { + print('details.scale = ${details.scale}'); + //print('_scale = $_scale, offset.dx = ${_offset.dx}, offset.dy = ${_offset.dy}'); + //print('details.focalPoint = (${details.focalPoint.dx}, ${details.focalPoint.dy})'); + //print('details.horizontalScale = ${details.horizontalScale}; details.verticalScale = ${details.verticalScale})'); + + if (1.0 != details.scale) { + _scale = (_previousScale * details.scale).clamp(1.0, 10.0); + playerRatioProvide.changeScale(_scale); + // 限制放大倍数 1~10倍 + _offset = _clampOffset(details.focalPoint - _normalizedOffset * _scale); + } else { + _deltaPoint = _clampdeltaPoint(details.focalPoint - _focalPoint); + _focalPoint = details.focalPoint; + print('_deltaPoint = (${_deltaPoint.dx}, ${_deltaPoint.dy})'); + playerRatioProvide.changeDeltaX(_deltaPoint.dx); + playerRatioProvide.changeDeltaY(_deltaPoint.dy); + } + + // 更新当前位置 + playerRatioProvide.changeOffset(_offset); + }); + } + + Offset _clampdeltaPoint(Offset delta) { + final Size size = context.size; // widget的屏幕尺寸 + double rate = 2.0; + Offset deltaPoint = + Offset((rate * delta.dx / size.width), (rate * delta.dy / size.height)); + _deltaPoint -= deltaPoint; + return Offset( + _deltaPoint.dx.clamp(-1.0, 1.0), _deltaPoint.dy.clamp(-1.0, 1.0)); + } + + Offset _clampOffset(Offset offset) { + final Size size = context.size; + // widget的屏幕宽度 + final Offset minOffset = Offset(size.width, size.height) * (1.0 - _scale); + // 限制他的最小尺寸 + return Offset( + offset.dx.clamp(minOffset.dx, 0.0), offset.dy.clamp(minOffset.dy, 0.0)); + } + + void _handleOnScaleEnd(ScaleEndDetails details) { + final double magnitude = details.velocity.pixelsPerSecond.distance; + if (magnitude < _kMinFlingVelocity) return; + final Offset direction = details.velocity.pixelsPerSecond / magnitude; + // 计算当前的方向 + final double distance = (Offset.zero & context.size).shortestSide; + // 计算放大倍速,并相应的放大宽和高,比如原来是600*480的图片,放大后倍数为1.25倍时,宽和高是同时变化的 + _animation = _controller.drive(Tween( + begin: _offset, end: _clampOffset(_offset + direction * distance))); + _controller + ..value = 0.0 + ..fling(velocity: magnitude / 1000.0); + _bScaleing = false; + } + + @override + Widget build(BuildContext context) { + playerRatioProvide = Provider.of(context); + Rect rect = panelRect(); + + List ws = []; + + if (_statelessTimer != null && _statelessTimer.isActive) { + ws.add(buildStateless()); + } else if (player.state == FijkState.asyncPreparing) { + ws.add(buildStateless()); + } else if (player.state == FijkState.error) { + ws.add(buildStateless()); + } else if (myImageProvider != null) { + ws.add(buildStateless()); + } + ws.add(buildGestureDetector(context)); + if (widget.onBack != null) { + ws.add(buildBack(context)); + } + return Positioned.fromRect( + rect: rect, + child: Stack(children: ws), + ); + } + +} diff --git a/lib/my_fijkPanel_fix/my_slider.dart b/lib/my_fijkPanel_fix/my_slider.dart new file mode 100644 index 0000000..46e4c2d --- /dev/null +++ b/lib/my_fijkPanel_fix/my_slider.dart @@ -0,0 +1,244 @@ +//MIT License +// +//Copyright (c) [2019] [Befovy] +// +//Permission is hereby granted, free of charge, to any person obtaining a copy +//of this software and associated documentation files (the "Software"), to deal +//in the Software without restriction, including without limitation the rights +//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//copies of the Software, and to permit persons to whom the Software is +//furnished to do so, subject to the following conditions: +// +//The above copyright notice and this permission notice shall be included in all +//copies or substantial portions of the Software. +// +//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//SOFTWARE. + +//part of fijkplayer; +import 'package:flutter/material.dart'; +import 'dart:core'; +import 'dart:math'; +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/widgets.dart'; + +/// MyFijkSlider is like Slider in Flutter SDK. +/// MyFijkSlider support [cacheValue] which can be used +/// to show the player's cached buffer. +/// The [colors] is used to make colorful painter to draw the line and circle. +class MyFijkSlider extends StatefulWidget { + final double value; + final double cacheValue; + + final ValueChanged onChanged; + final ValueChanged onChangeStart; + final ValueChanged onChangeEnd; + + final double min; + final double max; + + final MyFijkSliderColors colors; + + const MyFijkSlider({ + Key key, + @required this.value, + @required this.onChanged, + this.cacheValue = 0.0, + this.onChangeStart, + this.onChangeEnd, + this.min = 0.0, + this.max = 1.0, + this.colors = const MyFijkSliderColors(), + }) : assert(value != null), + assert(cacheValue != null), + assert(min != null), + assert(max != null), + assert(min <= max), + assert(value >= min && value <= max), + super(key: key); + + @override + State createState() { + return _MyFijkSliderState(); + } +} + +class _MyFijkSliderState extends State { + bool dragging = false; + + double dragValue; + + static const double margin = 2.0; + + @override + Widget build(BuildContext context) { + double v = widget.value / (widget.max - widget.min); + double cv = widget.cacheValue / (widget.max - widget.min); + + return GestureDetector( + child: Container( + margin: EdgeInsets.only(left: margin, right: margin), + height: double.infinity, + width: double.infinity, + color: Colors.transparent, + child: CustomPaint( + painter: _MySliderPainter(v, cv, dragging, colors: widget.colors), + ), + ), + onHorizontalDragStart: (DragStartDetails details) { + setState(() { + dragging = true; + }); + dragValue = widget.value; + if (widget.onChangeStart != null) { + widget.onChangeStart(dragValue); + } + }, + onHorizontalDragUpdate: (DragUpdateDetails details) { + final box = context.findRenderObject() as RenderBox; + final dx = details.localPosition.dx; + dragValue = (dx - margin) / (box.size.width - 2 * margin); + dragValue = max(0, min(1, dragValue)); + dragValue = dragValue * (widget.max - widget.min) + widget.min; + if (widget.onChanged != null) { + widget.onChanged(dragValue); + } + }, + onHorizontalDragEnd: (DragEndDetails details) { + setState(() { + dragging = false; + }); + if (widget.onChangeEnd != null) { + widget.onChangeEnd(dragValue); + } + }, + ); + } +} + +/// Colors for the MyFijkSlider +class MyFijkSliderColors { + const MyFijkSliderColors({ + this.playedColor = const Color.fromRGBO(255, 0, 0, 0.6), + this.bufferedColor = const Color.fromRGBO(50, 50, 100, 0.4), + this.cursorColor = const Color.fromRGBO(255, 0, 0, 0.8), + this.baselineColor = const Color.fromRGBO(200, 200, 200, 0.5), + }); + + final Color playedColor; + final Color bufferedColor; + final Color cursorColor; + final Color baselineColor; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is MyFijkSliderColors && + runtimeType == other.runtimeType && + hashCode == other.hashCode; + + @override + int get hashCode => + hashValues(playedColor, bufferedColor, cursorColor, baselineColor); +} + +class _MySliderPainter extends CustomPainter { + final double v; + final double cv; + + final bool dragging; + final Paint pt = Paint(); + + final MyFijkSliderColors colors; + + _MySliderPainter(this.v, this.cv, this.dragging, + {this.colors = const MyFijkSliderColors()}) + : assert(colors != null), + assert(v != null), + assert(cv != null); + + @override + void paint(Canvas canvas, Size size) { + double lineHeight = min(size.height / 2, 1); + pt.color = colors.baselineColor; + + double radius = min(size.height / 2, 4); + // draw background + canvas.drawRRect( + RRect.fromRectAndRadius( + Rect.fromPoints( + Offset(0, size.height / 2 - lineHeight), + Offset(size.width, size.height / 2 + lineHeight), + ), + Radius.circular(radius), + ), + pt, + ); + + final double value = v * size.width; + + // draw played part + pt.color = colors.playedColor; + canvas.drawRRect( + RRect.fromRectAndRadius( + Rect.fromPoints( + Offset(0, size.height / 2 - lineHeight), + Offset(value, size.height / 2 + lineHeight), + ), + Radius.circular(radius), + ), + pt, + ); + + // draw cached part + final double cacheValue = cv * size.width; + if (cacheValue > value && cacheValue > 0) { + pt.color = colors.bufferedColor; + canvas.drawRRect( + RRect.fromRectAndRadius( + Rect.fromPoints( + Offset(value, size.height / 2 - lineHeight), + Offset(cacheValue, size.height / 2 + lineHeight), + ), + Radius.circular(radius), + ), + pt, + ); + } + + //注意:改变拖动圆形光标,必须按Q退出App,再重新flutter run才能生效 + // draw circle cursor + // pt.color = colors.cursorColor; + // pt.color = pt.color.withAlpha(max(0, pt.color.alpha - 50)); + //radius = min(size.height / 2, dragging ? 10 : 5); + pt.color = Colors.red; + //radius = 10; + radius = min(size.height / 2, 10); + canvas.drawCircle(Offset(value, size.height / 2), radius, pt); + //pt.color = colors.cursorColor; + //radius = min(size.height / 2, dragging ? 6 : 3); + //canvas.drawCircle(Offset(value, size.height / 2), radius, pt); + } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is _MySliderPainter && hashCode == other.hashCode; + + @override + int get hashCode => hashValues(v, cv, dragging, colors); + + @override + bool shouldRepaint(_MySliderPainter oldDelegate) { + return hashCode != oldDelegate.hashCode; + } +} diff --git a/lib/widget/my_superplayer.dart b/lib/widget/my_superplayer.dart index 2971762..a4fa478 100644 --- a/lib/widget/my_superplayer.dart +++ b/lib/widget/my_superplayer.dart @@ -16,6 +16,7 @@ import 'package:provider/provider.dart'; import '../components/commonFun.dart'; import '../services/Storage.dart'; +import '../my_fijkPanel_fix/my_panel3.dart'; const _kControlViewTypes = [kControlViewTypeDefault, kControlViewTypeWithout]; @@ -40,16 +41,20 @@ class SuperPlayerPage extends StatefulWidget { _SuperPlayerPageState createState() => _SuperPlayerPageState(); } -class _SuperPlayerPageState extends State with SuperPlayerListener { +class _SuperPlayerPageState extends State + with WidgetsBindingObserver, AutomaticKeepAliveClientMixin { // SuperPlayerController _playerController = SuperPlayerController(); + + @override + // TODO: implement wantKeepAlive + bool get wantKeepAlive => true; + final FijkPlayer _playerController = FijkPlayer(); String _sdkVersion = 'Unknown'; List _logs = []; bool bFullScreen = false; - String _controlViewType = _kControlViewTypes.first; - @override void dispose() { Playing = false; @@ -69,6 +74,9 @@ class _SuperPlayerPageState extends State with SuperPlayerListe // }); // init(); + WidgetsBinding.instance.addObserver(this); + + _playerController.setOption(FijkOption.hostCategory, "enable-snapshot", 1); _playerController.setOption(FijkOption.playerCategory, "mediacodec-all-videos", 1); startPlay(); @@ -186,10 +194,14 @@ class _SuperPlayerPageState extends State with SuperPlayerListe PlayerRatioProvide playerRatioProvide; + FijkPanelWidgetBuilder _fijkPanelWidgetBuilder; + @override Widget build(BuildContext context) { + _fijkPanelWidgetBuilder = fijkPanel2Builder3(snapShot: true, player: _playerController); playerRegionProvide = Provider.of(context); playerRatioProvide = Provider.of(context); + // List listData = getDataListControl2(); double btnHeight1 = 70; //第一按钮行高度 double btnHeight2 = 160; //第二按钮行高度 @@ -294,7 +306,8 @@ class _SuperPlayerPageState extends State with SuperPlayerListe //设置视频区域背景颜色-OK color: Colors.black, player: _playerController, - panelBuilder: fijkPanel2Builder(snapShot: true), + // panelBuilder: fijkPanel2Builder(snapShot: true), + panelBuilder: _fijkPanelWidgetBuilder, fsFit: FijkFit.fill, // panelBuilder: simplestUI, // panelBuilder: (FijkPlayer player, BuildContext context,