sonopost_preview_helper.dart 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. import 'dart:convert';
  2. import 'dart:math' as math;
  3. import 'dart:typed_data';
  4. import 'package:fis_common/event/event_type.dart';
  5. import 'package:fis_common/logger/logger.dart';
  6. import 'package:flutter/material.dart';
  7. class ShellSonopostPlayController {
  8. static bool _playing = false;
  9. final int width;
  10. final int height;
  11. static final bufferFragmentReceived = FEventHandler<Uint8List>();
  12. ///截一张图
  13. static final onCaptureFrame = FEventHandler<bool>();
  14. ///录制,bool值代表开始or结束
  15. static final onRecord = FEventHandler<bool>();
  16. ShellSonopostPlayController({
  17. this.width = 1280,
  18. this.height = 720,
  19. });
  20. Future<void> init() {
  21. return Future(() => null);
  22. }
  23. void play() {
  24. if (_playing) return;
  25. _playing = true;
  26. logger.i("ShellSonopost start");
  27. }
  28. static void setPreviewImageData(String imageData) {
  29. try {
  30. if (_playing) {
  31. var data = base64Decode(imageData);
  32. bufferFragmentReceived.emit("shell_sonopost", data);
  33. } else {
  34. logger.i(
  35. "refresh sonopost preview failed since publish already stopped!");
  36. }
  37. } catch (e) {}
  38. }
  39. void stop() {
  40. _playing = false;
  41. logger.i("ShellSonopost stop");
  42. }
  43. }
  44. class ShellSonopostPlayer extends StatefulWidget {
  45. final ShellSonopostPlayController controller;
  46. const ShellSonopostPlayer({super.key, required this.controller});
  47. @override
  48. State<StatefulWidget> createState() => _ShellSonopostPlayer();
  49. }
  50. class _ShellSonopostPlayer extends State<ShellSonopostPlayer> {
  51. static const int _RETRY_LIMIT = 10;
  52. Uint8List? _frameBuffer;
  53. // late Uint8List _bufferArea;
  54. // int _bufferPosition = 0;
  55. bool _hasError = false;
  56. int _rgbaSize = 0;
  57. int _retryCount = 0;
  58. @override
  59. void initState() {
  60. super.initState();
  61. _rgbaSize = 4 * widget.controller.width * widget.controller.height;
  62. // _bufferArea = Uint8List(_rgbaSize);
  63. WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
  64. ShellSonopostPlayController.bufferFragmentReceived
  65. .addListener(_onBufferOnce);
  66. });
  67. }
  68. @override
  69. dispose() {
  70. ShellSonopostPlayController.bufferFragmentReceived
  71. .removeListener(_onBufferOnce);
  72. super.dispose();
  73. }
  74. @override
  75. Widget build(BuildContext context) {
  76. Widget child;
  77. if (_hasError) {
  78. child = const Center(
  79. child: Text("Error", style: TextStyle(color: Colors.white)),
  80. );
  81. } else if (_frameBuffer != null) {
  82. child = Image.memory(
  83. _frameBuffer!,
  84. fit: BoxFit.contain,
  85. gaplessPlayback: true,
  86. );
  87. // 水平翻转
  88. // child = Transform(
  89. // alignment: Alignment.center,
  90. // transform: Matrix4.rotationY(math.pi),
  91. // child: child,
  92. // );
  93. } else {
  94. child = const SizedBox();
  95. }
  96. return Container(
  97. color: Colors.black,
  98. child: child,
  99. );
  100. }
  101. void _onBufferOnce(Object sender, Uint8List e) {
  102. setState(() {
  103. _frameBuffer = e;
  104. });
  105. }
  106. void _onBuffer(Object sender, Uint8List e) {
  107. try {
  108. _onBufferOnce(sender, e);
  109. } catch (e) {
  110. _handleError();
  111. }
  112. }
  113. void _handleError() {
  114. widget.controller.stop();
  115. if (_retryCount > _RETRY_LIMIT) {
  116. setState(() {
  117. _hasError = true;
  118. });
  119. }
  120. Future.delayed(const Duration(milliseconds: 800), () {
  121. widget.controller.play();
  122. });
  123. _retryCount++;
  124. }
  125. static int readInt64(Uint8List data, [int index = 0]) {
  126. var intData = data.buffer.asByteData(index, 8);
  127. return intData.getInt64(0, Endian.little);
  128. }
  129. static int readInt64V2(Uint8List data, [int index = 0]) {
  130. int low = readInt(data, index);
  131. int high = readInt(data, index + 4);
  132. int value = high >> 32;
  133. value |= low;
  134. return value;
  135. }
  136. static int readInt(Uint8List data, [int index = 0]) {
  137. var intData = data.buffer.asByteData(index, 4);
  138. return intData.getInt32(0, Endian.little);
  139. }
  140. }