import 'dart:collection'; import 'dart:typed_data'; import 'dart:ui'; import 'package:fis_measure/view/player/controller.dart'; import 'package:fis_measure/view/player/enums.dart'; import 'package:fis_measure/view/player/events.dart'; import 'package:flutter/material.dart' hide Image; import 'package:vid_player_demo/utils/utils.dart'; ///基于Scene组件的播放器 class VidScenePlayer extends StatefulWidget { const VidScenePlayer( this.controller, { Key? key, this.width, this.height, }) : super(key: key); final VidPlayerController controller; final double? width; final double? height; @override State createState() => _VidScenePlayerState(); } class _VidScenePlayerState extends State { Uint8List? frameBytes; Image? image; DateTime lastFrameTime = DateTime.now(); ListQueue frames = ListQueue()..addFirst(30); //预留一位防止分母为0 int maxFramesNum = 10; int curFPS = 30; @override void initState() { widget.controller.eventHandler.addListener(onControllerEvent); startFPSListener(); super.initState(); } @override void didUpdateWidget(covariant VidScenePlayer oldWidget) { if (oldWidget.controller != widget.controller) { throw UnsupportedError("[VidTestPlayer] unsupport replace controller."); } super.didUpdateWidget(oldWidget); } void onControllerEvent(Object sender, VidPlayerEvent e) { if (e is VidPlayerStatusChangeEvent) { if (!widget.controller.hasView) { setState(() {}); } } if (e is VidPlayerFrameIndexChangeEvent) { onFrameChanged(e); } if (e is VidPlayerBrightnessChangeEvent) { updateFrame(); } if (e is VidPlayerContrastChangeEvent) { updateFrame(); } } void onFrameChanged(VidPlayerFrameIndexChangeEvent e) { loadFrame(e.bytes); } void loadFrame(Uint8List bytes) async { // List? debugList = image?.debugGetOpenHandleStackTraces(); // print("${DateTime.now()} Load frame \n ${debugList.toString()}"); // image?.dispose(); image = await decodeImageFromList(bytes); drawUsImage(image); setState(() {}); countCurFPS(); } void updateFrame() { setState(() {}); } void countCurFPS() { int fps = 1000 ~/ DateTime.now().difference(lastFrameTime).inMilliseconds; lastFrameTime = DateTime.now(); frames.addFirst(fps); while (frames.length > maxFramesNum) { frames.removeLast(); } curFPS = frames.reduce((a, b) => a + b) ~/ frames.length; } @override Widget build(BuildContext context) { Widget? child; switch (widget.controller.status) { case VidPlayStatus.init: child = const Text("Loading"); break; case VidPlayStatus.ready: child = const Text("Ready"); break; case VidPlayStatus.loadFail: child = const Text("Load fail"); break; case VidPlayStatus.play: case VidPlayStatus.pause: child = buildFrameView(context); break; case VidPlayStatus.stop: case VidPlayStatus.dispose: child = const Text("Closed"); break; } return buildBox(context, child); } ///创建播放器容器,可以在此叠加信息 Widget buildBox(BuildContext context, Widget child) { return Stack( children: [ Text( "当前帧:${widget.controller.currentFrameIndex} \n估算平均帧率:$curFPS fps \n"), Container( alignment: Alignment.center, child: child, ), ], ); } Widget buildFrameView(BuildContext context) { if (image != null) { final size = MediaQuery.of(context).size; return SizedBox( width: size.width, height: size.height, child: Container()); // return CustomPaint( // painter: VidPainter(image: image!, contextSize: size), // isComplex: false, //是否为复杂图像(true会缓存) // size: size, // ); } else { return Container(); } } void drawUsImage(image) async { // ContainerLayer containerLayer = ContainerLayer(); // PaintingContext paintingContext = PaintingContext(containerLayer, Rect.zero); /// create Canvas & Recorder PictureRecorder recorder = PictureRecorder(); Canvas canvas = Canvas(recorder); /// set offset final offset = -Offset(image.width / 2, image.height / 2); /// draw image canvas.drawImage(image, offset, Paint()); /// draw circle // canvas.drawCircle(Offset.zero, 200, Paint()..color = Colors.red); /// get picture final picture = recorder.endRecording(); var centerX = window.physicalSize.width / 2.0; var centerY = window.physicalSize.height / 2.0; /// build scene final SceneBuilder sceneBuilder = SceneBuilder() ..pushOffset(centerX, centerY) ..addPicture(Offset.zero, picture, willChangeHint: true) ..pop(); /// get scene Scene scene = sceneBuilder.build(); window.render(scene); scene.dispose(); picture.dispose(); image.dispose(); } @override void dispose() { widget.controller.eventHandler.removeListener(onControllerEvent); stopFPSListener(); super.dispose(); } }