merged_view.dart 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. import 'package:fis_lib_media_rt/implementations/nt_rtmp/channel/channel.dart';
  2. import 'package:fis_lib_media_rt/implementations/nt_rtmp/widgets/remote_view/remote_view.dart';
  3. import 'package:fis_lib_media_rt/implementations/nt_rtmp/widgets/sub_view/sub_view.dart';
  4. import 'package:fis_lib_media_rt/implementations/trtc/channel.dart';
  5. import 'package:fis_lib_media_rt/implementations/trtc/widgets/remote_merged_view_main.dart';
  6. import 'package:fis_lib_media_rt/implementations/trtc/widgets/remote_merged_view_sub.dart';
  7. import 'package:flutter/foundation.dart';
  8. import 'package:flutter/material.dart';
  9. import '../config.dart';
  10. import '../interface/channel.dart';
  11. /// 实时画面视图(合图)
  12. class FRTMergedChannelView extends StatelessWidget {
  13. /// 播放路源
  14. final IPlayChannel channel;
  15. /// 指定尺寸
  16. final Size? size;
  17. /// 总视频帧尺寸
  18. final Size videoSize;
  19. /// 视图尺寸
  20. final Size viewSize;
  21. /// 视图对齐方式
  22. final AlignmentGeometry viewAlignment;
  23. /// 是否是子视图
  24. final bool isSubView;
  25. /// 主视图外部容器最大尺寸
  26. final Size? mainViewContainerMaxSize;
  27. const FRTMergedChannelView(
  28. this.channel, {
  29. super.key,
  30. this.size,
  31. this.mainViewContainerMaxSize,
  32. required this.isSubView,
  33. required this.videoSize,
  34. required this.viewSize,
  35. required this.viewAlignment,
  36. });
  37. @override
  38. Widget build(BuildContext context) {
  39. if (FRTMediaConfig.serviceType == FRealTimeMediaServiceType.ntRtmp) {
  40. if (isSubView) {
  41. return NTRemoteSubView(channel: channel as NTPlayChannel);
  42. } else {
  43. return NTRemoteView(channel: channel as NTPlayChannel);
  44. }
  45. }
  46. return LayoutBuilder(builder: (_, constraints) {
  47. // 为了使得移动端上的Texture纹理渲染正常,此处的主画面容器需要写死为1280x720
  48. Size displayViewSize =
  49. (kIsWeb || isSubView) ? viewSize : const Size(1280, 720);
  50. // 是否使用高度缩放
  51. final bool isHeightConstraint =
  52. constraints.maxWidth / constraints.maxHeight >
  53. displayViewSize.width / displayViewSize.height;
  54. double scale = isHeightConstraint
  55. ? videoSize.height / displayViewSize.height
  56. : videoSize.width / displayViewSize.width;
  57. /// 视图宽高比
  58. final double viewRatio = displayViewSize.width / displayViewSize.height;
  59. /// PC 端主视图是否依赖高度缩放
  60. final bool isMainViewHeightConstraint = mainViewContainerMaxSize ==
  61. null ||
  62. mainViewContainerMaxSize!.width / mainViewContainerMaxSize!.height >
  63. displayViewSize.width / displayViewSize.height;
  64. /// Container 的尺寸需要缩小,来满足视图的宽高比,要么宽度缩小,要么高度缩小
  65. final Size containerSize = viewRatio < 1
  66. ? Size(
  67. constraints.maxHeight * viewRatio,
  68. constraints.maxHeight,
  69. )
  70. : Size(
  71. constraints.maxWidth,
  72. constraints.maxWidth / viewRatio,
  73. );
  74. Offset extraOffset = Offset.zero;
  75. /// 如果推流的超声机画面小于1280x720,那么需要在移动端上进行额外缩放和位移
  76. if (!isSubView && !kIsWeb) {
  77. double extraScale = 1;
  78. // 判断是否是需要缩放
  79. const canvasAspectRatio = 1280 / 720;
  80. final imageAspectRatio = viewSize.width / viewSize.height;
  81. if (canvasAspectRatio < imageAspectRatio) {
  82. // 画布较宽,需要调整图像宽度以适应画布宽度
  83. extraScale = 1280 / viewSize.width;
  84. extraOffset = Offset(
  85. 0,
  86. (720 - viewSize.height * extraScale) / 2 / 720,
  87. );
  88. } else {
  89. // 画布较高,需要调整图像高度以适应画布高度
  90. extraScale = 720 / viewSize.height;
  91. extraOffset = Offset(
  92. (1280 - viewSize.width * extraScale) / 2 / 1280,
  93. 0,
  94. );
  95. }
  96. scale = scale * extraScale;
  97. }
  98. return kIsWeb
  99. ? _buildWebView(containerSize, scale,
  100. !isMainViewHeightConstraint) // TODO: 可能会存在问题,之前是使用 (!isMainViewHeightConstraint) 来判断的
  101. : _buildMobileView(containerSize, scale, extraOffset);
  102. });
  103. }
  104. // Widget _buildView() {
  105. // final type = FRTMediaConfig.serviceType;
  106. // if (type == FRealTimeMediaServiceType.ntRtmp) {
  107. // return NTPreviewView(channel: channel as NTPublishChannel);
  108. // } else if (type == FRealTimeMediaServiceType.vrtc) {
  109. // //
  110. // }
  111. // return TrtcPreviewView(channel as TrtcPublishChannel);
  112. // }
  113. Widget _buildMobileView(
  114. Size containerSize, double scale, Offset extraOffset) {
  115. if (isSubView && videoSize.width == 1280 && videoSize.height == 720) {
  116. return _buildMergedMobileCameraView(containerSize, scale, extraOffset);
  117. }
  118. return Align(
  119. alignment: Alignment.center,
  120. child: Container(
  121. color: Colors.black,
  122. width: containerSize.width,
  123. height: containerSize.height,
  124. child: ClipRect(
  125. child: FractionalTranslation(
  126. translation: extraOffset,
  127. child: Transform.scale(
  128. alignment: viewAlignment,
  129. scale: scale,
  130. child: isSubView
  131. ? TrtcRemoteMergedViewSub(channel as TrtcPlayChannel)
  132. : TrtcRemoteMergedViewMain(
  133. channel as TrtcPlayChannel,
  134. ),
  135. ),
  136. ),
  137. ),
  138. ),
  139. );
  140. }
  141. Widget _buildWebView(Size containerSize, double scale, bool isKeepCover) {
  142. print("✨ containerSize: $containerSize");
  143. if (isSubView && videoSize.width == 1280 && videoSize.height == 720) {
  144. return _buildMergedWebCameraView(containerSize, scale);
  145. }
  146. return Container(
  147. color: Colors.black,
  148. width: containerSize.width,
  149. height: containerSize.height,
  150. child: ClipRect(
  151. child: FractionalTranslation(
  152. translation: const Offset(0, 0),
  153. child: Transform.scale(
  154. alignment: viewAlignment,
  155. scale: scale,
  156. child: isSubView
  157. ? TrtcRemoteMergedViewSub(channel as TrtcPlayChannel)
  158. : TrtcRemoteMergedViewMain(
  159. channel as TrtcPlayChannel,
  160. isKeepCover: isKeepCover,
  161. ),
  162. ),
  163. ),
  164. ),
  165. );
  166. }
  167. Widget _buildMergedMobileCameraView(
  168. Size containerSize, double scale, Offset extraOffset) {
  169. return Align(
  170. alignment: Alignment.center,
  171. child: SizedBox(
  172. width: containerSize.width,
  173. height: containerSize.height,
  174. child: ClipRect(
  175. child: Stack(
  176. children: [
  177. FractionalTranslation(
  178. translation: Offset.zero,
  179. child: Transform.scale(
  180. alignment: Alignment.bottomRight,
  181. scale: 2,
  182. child: Container(
  183. margin: const EdgeInsets.only(left: 0.5),
  184. child:
  185. TrtcRemoteMergedViewSub(channel as TrtcPlayChannel)),
  186. ),
  187. ),
  188. FractionalTranslation(
  189. translation: const Offset(0, -0.496),
  190. child: Transform.scale(
  191. alignment: Alignment.bottomLeft,
  192. scale: 2,
  193. child: Container(
  194. margin: const EdgeInsets.only(right: 0.5),
  195. child:
  196. TrtcRemoteMergedViewSub(channel as TrtcPlayChannel)),
  197. ),
  198. ),
  199. ],
  200. ),
  201. ),
  202. ),
  203. );
  204. }
  205. Widget _buildMergedWebCameraView(Size containerSize, double scale) {
  206. return Container(
  207. color: Colors.black,
  208. width: containerSize.width,
  209. height: containerSize.height,
  210. child: ClipRect(
  211. child: Stack(
  212. children: [
  213. FractionalTranslation(
  214. translation: const Offset(0, 0),
  215. child: Transform.scale(
  216. alignment: Alignment.bottomRight,
  217. scale: 2,
  218. child: TrtcRemoteMergedViewSub(channel as TrtcPlayChannel),
  219. ),
  220. ),
  221. FractionalTranslation(
  222. translation: const Offset(0, -0.496),
  223. child: Transform.scale(
  224. alignment: Alignment.bottomLeft,
  225. scale: 2,
  226. child: TrtcRemoteMergedViewSub(channel as TrtcPlayChannel),
  227. ),
  228. ),
  229. ],
  230. ),
  231. ),
  232. );
  233. }
  234. }