123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 |
- import 'package:fis_lib_media_rt/implementations/nt_rtmp/channel/channel.dart';
- import 'package:fis_lib_media_rt/implementations/nt_rtmp/widgets/remote_view/remote_view.dart';
- import 'package:fis_lib_media_rt/implementations/nt_rtmp/widgets/sub_view/sub_view.dart';
- import 'package:fis_lib_media_rt/implementations/trtc/channel.dart';
- import 'package:fis_lib_media_rt/implementations/trtc/widgets/remote_merged_view_main.dart';
- import 'package:fis_lib_media_rt/implementations/trtc/widgets/remote_merged_view_sub.dart';
- import 'package:flutter/foundation.dart';
- import 'package:flutter/material.dart';
- import '../config.dart';
- import '../interface/channel.dart';
- /// 实时画面视图(合图)
- class FRTMergedChannelView extends StatelessWidget {
- /// 播放路源
- final IPlayChannel channel;
- /// 指定尺寸
- final Size? size;
- /// 总视频帧尺寸
- final Size videoSize;
- /// 视图尺寸
- final Size viewSize;
- /// 视图对齐方式
- final AlignmentGeometry viewAlignment;
- /// 是否是子视图
- final bool isSubView;
- /// 主视图外部容器最大尺寸
- final Size? mainViewContainerMaxSize;
- const FRTMergedChannelView(
- this.channel, {
- super.key,
- this.size,
- this.mainViewContainerMaxSize,
- required this.isSubView,
- required this.videoSize,
- required this.viewSize,
- required this.viewAlignment,
- });
- @override
- Widget build(BuildContext context) {
- if (FRTMediaConfig.serviceType == FRealTimeMediaServiceType.ntRtmp) {
- if (isSubView) {
- return NTRemoteSubView(channel: channel as NTPlayChannel);
- } else {
- return NTRemoteView(channel: channel as NTPlayChannel);
- }
- }
- return LayoutBuilder(builder: (_, constraints) {
- // 为了使得移动端上的Texture纹理渲染正常,此处的主画面容器需要写死为1280x720
- Size displayViewSize =
- (kIsWeb || isSubView) ? viewSize : const Size(1280, 720);
- // 是否使用高度缩放
- final bool isHeightConstraint =
- constraints.maxWidth / constraints.maxHeight >
- displayViewSize.width / displayViewSize.height;
- double scale = isHeightConstraint
- ? videoSize.height / displayViewSize.height
- : videoSize.width / displayViewSize.width;
- /// 视图宽高比
- final double viewRatio = displayViewSize.width / displayViewSize.height;
- /// PC 端主视图是否依赖高度缩放
- final bool isMainViewHeightConstraint = mainViewContainerMaxSize ==
- null ||
- mainViewContainerMaxSize!.width / mainViewContainerMaxSize!.height >
- displayViewSize.width / displayViewSize.height;
- /// Container 的尺寸需要缩小,来满足视图的宽高比,要么宽度缩小,要么高度缩小
- final Size containerSize = viewRatio < 1
- ? Size(
- constraints.maxHeight * viewRatio,
- constraints.maxHeight,
- )
- : Size(
- constraints.maxWidth,
- constraints.maxWidth / viewRatio,
- );
- Offset extraOffset = Offset.zero;
- /// 如果推流的超声机画面小于1280x720,那么需要在移动端上进行额外缩放和位移
- if (!isSubView && !kIsWeb) {
- double extraScale = 1;
- // 判断是否是需要缩放
- const canvasAspectRatio = 1280 / 720;
- final imageAspectRatio = viewSize.width / viewSize.height;
- if (canvasAspectRatio < imageAspectRatio) {
- // 画布较宽,需要调整图像宽度以适应画布宽度
- extraScale = 1280 / viewSize.width;
- extraOffset = Offset(
- 0,
- (720 - viewSize.height * extraScale) / 2 / 720,
- );
- } else {
- // 画布较高,需要调整图像高度以适应画布高度
- extraScale = 720 / viewSize.height;
- extraOffset = Offset(
- (1280 - viewSize.width * extraScale) / 2 / 1280,
- 0,
- );
- }
- scale = scale * extraScale;
- }
- return kIsWeb
- ? _buildWebView(containerSize, scale,
- !isMainViewHeightConstraint) // TODO: 可能会存在问题,之前是使用 (!isMainViewHeightConstraint) 来判断的
- : _buildMobileView(containerSize, scale, extraOffset);
- });
- }
- // Widget _buildView() {
- // final type = FRTMediaConfig.serviceType;
- // if (type == FRealTimeMediaServiceType.ntRtmp) {
- // return NTPreviewView(channel: channel as NTPublishChannel);
- // } else if (type == FRealTimeMediaServiceType.vrtc) {
- // //
- // }
- // return TrtcPreviewView(channel as TrtcPublishChannel);
- // }
- Widget _buildMobileView(
- Size containerSize, double scale, Offset extraOffset) {
- if (isSubView && videoSize.width == 1280 && videoSize.height == 720) {
- return _buildMergedMobileCameraView(containerSize, scale, extraOffset);
- }
- return Align(
- alignment: Alignment.center,
- child: Container(
- color: Colors.black,
- width: containerSize.width,
- height: containerSize.height,
- child: ClipRect(
- child: FractionalTranslation(
- translation: extraOffset,
- child: Transform.scale(
- alignment: viewAlignment,
- scale: scale,
- child: isSubView
- ? TrtcRemoteMergedViewSub(channel as TrtcPlayChannel)
- : TrtcRemoteMergedViewMain(
- channel as TrtcPlayChannel,
- ),
- ),
- ),
- ),
- ),
- );
- }
- Widget _buildWebView(Size containerSize, double scale, bool isKeepCover) {
- print("✨ containerSize: $containerSize");
- if (isSubView && videoSize.width == 1280 && videoSize.height == 720) {
- return _buildMergedWebCameraView(containerSize, scale);
- }
- return Container(
- color: Colors.black,
- width: containerSize.width,
- height: containerSize.height,
- child: ClipRect(
- child: FractionalTranslation(
- translation: const Offset(0, 0),
- child: Transform.scale(
- alignment: viewAlignment,
- scale: scale,
- child: isSubView
- ? TrtcRemoteMergedViewSub(channel as TrtcPlayChannel)
- : TrtcRemoteMergedViewMain(
- channel as TrtcPlayChannel,
- isKeepCover: isKeepCover,
- ),
- ),
- ),
- ),
- );
- }
- Widget _buildMergedMobileCameraView(
- Size containerSize, double scale, Offset extraOffset) {
- return Align(
- alignment: Alignment.center,
- child: SizedBox(
- width: containerSize.width,
- height: containerSize.height,
- child: ClipRect(
- child: Stack(
- children: [
- FractionalTranslation(
- translation: Offset.zero,
- child: Transform.scale(
- alignment: Alignment.bottomRight,
- scale: 2,
- child: Container(
- margin: const EdgeInsets.only(left: 0.5),
- child:
- TrtcRemoteMergedViewSub(channel as TrtcPlayChannel)),
- ),
- ),
- FractionalTranslation(
- translation: const Offset(0, -0.496),
- child: Transform.scale(
- alignment: Alignment.bottomLeft,
- scale: 2,
- child: Container(
- margin: const EdgeInsets.only(right: 0.5),
- child:
- TrtcRemoteMergedViewSub(channel as TrtcPlayChannel)),
- ),
- ),
- ],
- ),
- ),
- ),
- );
- }
- Widget _buildMergedWebCameraView(Size containerSize, double scale) {
- return Container(
- color: Colors.black,
- width: containerSize.width,
- height: containerSize.height,
- child: ClipRect(
- child: Stack(
- children: [
- FractionalTranslation(
- translation: const Offset(0, 0),
- child: Transform.scale(
- alignment: Alignment.bottomRight,
- scale: 2,
- child: TrtcRemoteMergedViewSub(channel as TrtcPlayChannel),
- ),
- ),
- FractionalTranslation(
- translation: const Offset(0, -0.496),
- child: Transform.scale(
- alignment: Alignment.bottomLeft,
- scale: 2,
- child: TrtcRemoteMergedViewSub(channel as TrtcPlayChannel),
- ),
- ),
- ],
- ),
- ),
- );
- }
- }
|