فهرست منبع

基本实现音视频SDK TRTC 部分(不包含切图)

Jimmy 2 سال پیش
والد
کامیت
6ba5d76123
48فایلهای تغییر یافته به همراه1396 افزوده شده و 128 حذف شده
  1. 1 0
      demo/demo/BaseWindow.cs
  2. 39 0
      demo/fis.media/Library/Input/FisVideoMemberInput/ConsultationMemeber.cs
  3. 99 0
      demo/fis.media/Library/Input/FisVideoMemberInput/InputConverterHelper.cs
  4. 25 0
      demo/fis.media/Library/Input/FisVideoMemberInput/LiveConsultation.cs
  5. 17 0
      demo/fis.media/Library/Input/FisVideoMemberInput/LiveConsultationMemberStatus.cs
  6. 18 0
      demo/fis.media/Library/Input/FisVideoMemberInput/LiveData.cs
  7. 15 0
      demo/fis.media/Library/Input/FisVideoMemberInput/MemberRoleType.cs
  8. 18 0
      demo/fis.media/Library/Input/FisVideoMemberInput/VideoDeviceOutputInfo.cs
  9. 14 0
      demo/fis.media/Library/Input/FisVideoMemberInput/VideoDeviceSourceTypeEnum.cs
  10. 50 2
      demo/fis.media/Library/Media/Members/AbstractMember.cs
  11. 31 2
      demo/fis.media/Library/Media/Members/ActiveMember.cs
  12. 3 3
      demo/fis.media/Library/Media/Members/LocalVideoMember.cs
  13. 3 3
      demo/fis.media/Library/Media/Members/MainDeviceVideoMember .cs
  14. 26 0
      demo/fis.media/Library/Media/Members/MemberRoleTypeEnum.cs
  15. 0 16
      demo/fis.media/Library/Media/Members/Participent.cs
  16. 3 3
      demo/fis.media/Library/Media/Members/RemoteVideoMember.cs
  17. 0 41
      demo/fis.media/Library/Media/Members/RoleTypeEnum.cs
  18. 19 4
      demo/fis.media/Library/Media/Players/AbstractPlayer.cs
  19. 53 0
      demo/fis.media/Library/Media/Players/ChannelVideoFrame.cs
  20. 40 4
      demo/fis.media/Library/Media/Players/PlayerChannel.cs
  21. 36 10
      demo/fis.media/Library/Media/Players/RtcPlayer.cs
  22. 4 0
      demo/fis.media/Library/Media/Players/RtmpPlayer.cs
  23. 12 0
      demo/fis.media/Library/Media/Players/VideoFrameData.cs
  24. 24 0
      demo/fis.media/Library/Media/Publisher/ChannelNameEnum.cs
  25. 31 0
      demo/fis.media/Library/Media/Publisher/ChannelTypeEnum.cs
  26. 15 2
      demo/fis.media/Library/Media/Publisher/LayerType.cs
  27. 32 0
      demo/fis.media/Library/Media/Publisher/PositionEnum.cs
  28. 68 2
      demo/fis.media/Library/Media/Publisher/RegionInfo.cs
  29. 17 0
      demo/fis.media/Library/Media/Publisher/RegionModel.cs
  30. 7 1
      demo/fis.media/Library/Media/Rooms/IRoom.cs
  31. 2 1
      demo/fis.media/Library/Media/Rooms/IRtcRoom.cs
  32. 12 0
      demo/fis.media/Library/Media/Rooms/IVRTCRoom.cs
  33. 20 0
      demo/fis.media/Library/Media/Rooms/RTMPRoom.cs
  34. 52 0
      demo/fis.media/Library/Media/Rooms/Room.cs
  35. 26 0
      demo/fis.media/Library/Media/Rooms/RoomTypeEnum.cs
  36. 85 0
      demo/fis.media/Library/Media/Rooms/TRTCRoom.cs
  37. 18 0
      demo/fis.media/Library/Media/Rooms/VRTCRoom.cs
  38. 1 1
      demo/fis.media/Library/OutPut/FisVideoFrameOutput/Channelbase.cs
  39. 6 1
      demo/fis.media/Library/OutPut/FisVideoFrameOutput/LocalCameraChannel.cs
  40. 6 1
      demo/fis.media/Library/OutPut/FisVideoFrameOutput/MainChannel.cs
  41. 1 1
      demo/fis.media/Library/OutPut/FisVideoFrameOutput/RemoteCameraChannel.cs
  42. 6 1
      demo/fis.media/Library/OutPut/FisVideoFrameOutput/SideChannelOne.cs
  43. 6 1
      demo/fis.media/Library/OutPut/FisVideoFrameOutput/SideChannelTwo.cs
  44. 6 1
      demo/fis.media/Library/OutPut/FisVideoFrameOutput/VideoStreamStructre.cs
  45. 231 0
      demo/fis.media/Managers/ChatRoomManager.cs
  46. 74 16
      demo/fis.media/Managers/PlayerManager.cs
  47. 121 9
      demo/fis.media/Managers/PusherManager.cs
  48. 3 2
      demo/fis.media/ThirdPartLibrary/Tencent/TRTCChatRoom.cs

+ 1 - 0
demo/demo/BaseWindow.cs

@@ -11,3 +11,4 @@ namespace demo
     {
     }
 }
+.l 

+ 39 - 0
demo/fis.media/Library/Input/FisVideoMemberInput/ConsultationMemeber.cs

@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace fis.media.Library.Input.FisVideoMemberInput
+{
+    public class ConsultationMemeber
+    {
+        public string Id { get; set; }
+
+        public string Name { get; set; }
+
+        public MemberRoleType MemberRoleType { get; set; }
+
+        public bool IsOnline { get; set; }
+
+        public bool Mute { get; set; }
+
+        public bool VideoOpend { get; set; }
+
+        public bool IsInitiator { get; set; }
+
+        public bool IsBusy { get; set; }
+
+        public LiveConsultationMemberStatus Status { get; set; }
+
+        public LiveData? LiveData { get; set; }
+
+        public bool MergedChannel { get; set; }
+
+        public int MergedVideoOutputWidth { get; set; }
+
+        public int MergedVideoOutputHeight { get; set; }
+
+        public List<VideoDeviceOutputInfo> VideoDeviceInfos { get; set; }
+    }
+}

+ 99 - 0
demo/fis.media/Library/Input/FisVideoMemberInput/InputConverterHelper.cs

@@ -0,0 +1,99 @@
+using fis.media.Library.Media.Members;
+using fis.media.Library.Media.Publisher;
+using fis.media.Library.Media.Rooms;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace fis.media.Library.Input.FisVideoMemberInput
+{
+    public class InputConverterHelper
+    {
+        public static string[] GetChatRoomInitArgs(LiveConsultation liveConsultation)
+        { 
+            var chatRoomInitArgs = new List<string>();
+            chatRoomInitArgs.Add(liveConsultation.AppId.ToString());
+            chatRoomInitArgs.Add(liveConsultation.UserCode!.ToString());
+            chatRoomInitArgs.Add(liveConsultation.UserSign!.ToString());
+            chatRoomInitArgs.Add(liveConsultation.RoomNo.ToString());
+            return chatRoomInitArgs.ToArray();
+        }
+
+        public static List<AbstractMember> ConvetSDKMembers(List<ConsultationMemeber> consultationMemebers)
+        {
+            var members = new List<AbstractMember>();
+            foreach (var cm in consultationMemebers)
+            {
+                members.Add(ConvertBuisnessMember(cm));
+            }
+            return members;
+        }
+        public static AbstractMember ConvertBuisnessMember(ConsultationMemeber ConsultationMemeber)
+        {
+            if (ConsultationMemeber.MemberRoleType==MemberRoleType.Local)
+            {
+                var localMember= new LocalVideoMember(ConsultationMemeber.Id);
+                localMember.IsOnline = ConsultationMemeber.IsOnline;
+                localMember.IsMute = ConsultationMemeber.Mute;
+                localMember.IsCameraOn = ConsultationMemeber.VideoOpend;
+                localMember.IsSelf = true;
+                localMember.IsMergeChannel = ConsultationMemeber.MergedChannel;
+                return localMember;
+            }
+
+            if (ConsultationMemeber.MemberRoleType == MemberRoleType.Remote)
+            {
+                var remoteMember = new RemoteVideoMember(ConsultationMemeber.Id);
+                remoteMember.IsOnline = ConsultationMemeber.IsOnline;
+                remoteMember.IsMute = ConsultationMemeber.Mute;
+                remoteMember.IsCameraOn = ConsultationMemeber.VideoOpend;
+                remoteMember.IsSelf = false;
+                remoteMember.IsMergeChannel = ConsultationMemeber.MergedChannel;
+                return remoteMember;
+            }
+            if (ConsultationMemeber.MemberRoleType == MemberRoleType.Device)
+            {
+                var deviceMember = new RemoteVideoMember(ConsultationMemeber.Id);
+                deviceMember.IsOnline = ConsultationMemeber.IsOnline;
+                deviceMember.IsMute = ConsultationMemeber.Mute;
+                deviceMember.IsCameraOn = ConsultationMemeber.VideoOpend;
+                deviceMember.IsSelf = false;
+                deviceMember.IsMergeChannel = ConsultationMemeber.MergedChannel;
+                deviceMember.LayerConfig = GenerateLayerConfig(ConsultationMemeber.MergedChannel, ConsultationMemeber.VideoDeviceInfos, ConsultationMemeber.MergedVideoOutputWidth,
+                    ConsultationMemeber.MergedVideoOutputHeight);
+                return deviceMember;
+            }
+            return null;
+        }
+
+        private static LayerConfig GenerateLayerConfig(bool mergedChannel, List<VideoDeviceOutputInfo> videoDeviceInfos, int mergedVideoOutputWidth, int mergedVideoOutputHeight)
+        {
+            if (mergedChannel)
+            {
+                return null;
+            }
+            var layer = new LayerConfig();
+            var deskTop = videoDeviceInfos.FirstOrDefault(x => x.VideoDeviceSourceType == VideoDeviceSourceTypeEnum.DeskTop);
+            var camera= videoDeviceInfos.FirstOrDefault(x => x.VideoDeviceSourceType == VideoDeviceSourceTypeEnum.Camera);
+            layer.RegionInfo = new RegionInfo()
+            {
+                Width = mergedVideoOutputWidth,
+                Height = mergedVideoOutputHeight,
+                MainRegion = deskTop == null ? null : new RegionModel()
+                {
+                    Width = deskTop.OutputWidth,
+                    Height=deskTop.OutputHeight,  
+                    Position=PositionEnum.Center
+                },
+                SideRegionOne=camera==null?null:new RegionModel {
+                    Width = camera.OutputWidth,
+                    Height = camera.OutputHeight,
+                    Position = PositionEnum.TopRight
+                }
+            };
+            return layer;
+        }
+    }
+}

+ 25 - 0
demo/fis.media/Library/Input/FisVideoMemberInput/LiveConsultation.cs

@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace fis.media.Library.Input.FisVideoMemberInput
+{
+    public class LiveConsultation
+    {
+        public string? ConsultationCode { get; set; }
+
+        public string? InitiatorCode { get; set; }
+
+        public string? UserCode { get; set; }
+
+        public int RoomNo { get; set; }
+
+        public int AppId { get; set; }
+
+        public string? UserSign { get; set; }
+
+        public List<ConsultationMemeber>? MemberLiveDatas { get; set; }
+    }
+}

+ 17 - 0
demo/fis.media/Library/Input/FisVideoMemberInput/LiveConsultationMemberStatus.cs

@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace fis.media.Library.Input.FisVideoMemberInput
+{
+    public enum LiveConsultationMemberStatus
+    {
+        Default,
+        Accepted,
+        Rejcted,
+        Joined,
+        Left
+    }
+}

+ 18 - 0
demo/fis.media/Library/Input/FisVideoMemberInput/LiveData.cs

@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace fis.media.Library.Input.FisVideoMemberInput
+{
+    public class LiveData
+    {
+        public int Width { get; set; }
+        public int Height { get; set; }
+        public string RtmpPushUrl { get; set; }
+        public string RtmpPullUrl { get; set; }
+        public string HttpPullUrl { get; set; }
+        public string HlsPullUrl { get; set; }
+    }
+}

+ 15 - 0
demo/fis.media/Library/Input/FisVideoMemberInput/MemberRoleType.cs

@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace fis.media.Library.Input.FisVideoMemberInput
+{
+    public enum MemberRoleType
+    {
+        Local,
+        Remote,
+        Device
+    }
+}

+ 18 - 0
demo/fis.media/Library/Input/FisVideoMemberInput/VideoDeviceOutputInfo.cs

@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace fis.media.Library.Input.FisVideoMemberInput
+{
+    public class VideoDeviceOutputInfo
+    {
+        public string? VideoDeviceId { get; set; }
+        public VideoDeviceSourceTypeEnum VideoDeviceSourceType { get; set; }
+        public int OutputWidth { get; set; }
+        public int OutputHeight { get; set; }
+        public string? VideoDeviceSign { get; set; }
+
+    }
+}

+ 14 - 0
demo/fis.media/Library/Input/FisVideoMemberInput/VideoDeviceSourceTypeEnum.cs

@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace fis.media.Library.Input.FisVideoMemberInput
+{
+    public enum VideoDeviceSourceTypeEnum
+    {
+        DeskTop,
+        Camera
+    }
+}

+ 50 - 2
demo/fis.media/Library/Media/Members/AbstractMember.cs

@@ -1,4 +1,6 @@
-using System;
+using fis.media.Library.Media.Players;
+using fis.media.Library.Media.Rooms;
+using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
@@ -6,16 +8,62 @@ using System.Threading.Tasks;
 
 namespace fis.media.Library.Media.Members
 {
-    public abstract class AbstractMember
+    public abstract class AbstractMember:IDisposable
     {
+        private AbstractPlayer _player;
+        private bool _isOnline;
+
         public AbstractMember(string id)
         {
             Id = id;
         }
 
+        /// <summary>
+        /// Player 引用
+        /// </summary>
+        public AbstractPlayer Player => _player;
+
         /// <summary>
         /// 成员Id
         /// </summary>
         public string Id { get; protected set; }
+
+        /// <summary>
+        /// 是否在线
+        /// </summary>
+        public bool IsOnline
+        {
+            get { return _isOnline; }
+            set 
+            {   if (_isOnline != value)
+                {
+                    _isOnline = value;
+                }
+            }
+        }
+
+        public virtual bool AdaptMemberUpdate()
+        {
+            if (IsOnline)
+            {
+                if (Player == null)
+                {
+                    var activeMember = this as ActiveMember;
+                    ApplyPlayer(TRTCRoom.GenerateRtcPlayer(activeMember));
+                }
+            }
+            return true;
+        }
+
+        public bool ApplyPlayer(AbstractPlayer player)
+        {
+            _player = player;
+            return true;
+        }
+
+        public void Dispose()
+        {
+            Player.Dispose();
+        }
     }
 }

+ 31 - 2
demo/fis.media/Library/Media/Members/ActiveMember.cs

@@ -20,7 +20,7 @@ namespace fis.media.Library.Media.Members
         /// <summary>
         /// 成员角色
         /// </summary>
-        public abstract RoleTypeEnum RoleType { get; }
+        public abstract MemeberTypeEnum RoleType { get; }
 
         /// <summary>
         /// 音视频地址
@@ -46,6 +46,35 @@ namespace fis.media.Library.Media.Members
         /// 合流层级配置信息
         /// </summary>
         public LayerConfig LayerConfig { get; set; }
-        
+
+        /// <summary>
+        /// 是否是合图模式
+        /// </summary>
+        public bool IsMergeChannel { get; set; }
+
+        public override bool AdaptMemberUpdate()
+        {
+            if (IsMute)
+            {
+                //静音操作
+            }
+            else { 
+            
+            }
+            if (IsCameraOn)
+            {
+                //启动摄像头画面
+            }
+            else { 
+            }
+            if (IsMergeChannel)
+            {
+                //切图操作
+            }
+            else { 
+            }
+            return base.AdaptMemberUpdate();
+        }
+
     }
 }

+ 3 - 3
demo/fis.media/Library/Media/Members/MainParticipent.cs → demo/fis.media/Library/Media/Members/LocalVideoMember.cs

@@ -6,12 +6,12 @@ using System.Threading.Tasks;
 
 namespace fis.media.Library.Media.Members
 {
-    public class MainParticipent : ActiveMember
+    public class LocalVideoMember : ActiveMember
     {
-        public MainParticipent(string id) : base(id)
+        public LocalVideoMember(string id) : base(id)
         {
         }
-        public override RoleTypeEnum RoleType => RoleTypeEnum.MainParticipent;
+        public override MemeberTypeEnum RoleType => MemeberTypeEnum.LocalVideo;
     }
     
 }

+ 3 - 3
demo/fis.media/Library/Media/Members/Guest.cs → demo/fis.media/Library/Media/Members/MainDeviceVideoMember .cs

@@ -6,12 +6,12 @@ using System.Threading.Tasks;
 
 namespace fis.media.Library.Media.Members
 {
-    public class Guest : ActiveMember
+    public class MainDeviceVideoMember : ActiveMember
     {
-        public Guest(string id) : base(id)
+        public MainDeviceVideoMember(string id) : base(id)
         {
         }
-        public override RoleTypeEnum RoleType => RoleTypeEnum.Guest;
+        public override MemeberTypeEnum RoleType => MemeberTypeEnum.MainDeviceVideo;
     }
 
 }

+ 26 - 0
demo/fis.media/Library/Media/Members/MemberRoleTypeEnum.cs

@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace fis.media.Library.Media.Members
+{
+    public enum MemeberTypeEnum
+    {
+        /// <summary>
+        /// 本地摄像头
+        /// </summary>
+        LocalVideo,
+
+        /// <summary>
+        /// 远程摄像头
+        /// </summary>
+        RemoteVideo,
+
+        /// <summary>
+        /// 主要参与设备
+        /// </summary>
+        MainDeviceVideo,
+    }
+}

+ 0 - 16
demo/fis.media/Library/Media/Members/Participent.cs

@@ -1,16 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace fis.media.Library.Media.Members
-{
-    public class Participent : ActiveMember
-    {
-        public Participent(string id) : base(id)
-        {
-        }
-        public override RoleTypeEnum RoleType => RoleTypeEnum.Participent;
-    }
-}

+ 3 - 3
demo/fis.media/Library/Media/Members/Organizer.cs → demo/fis.media/Library/Media/Members/RemoteVideoMember.cs

@@ -6,11 +6,11 @@ using System.Threading.Tasks;
 
 namespace fis.media.Library.Media.Members
 {
-    public class Organizer : ActiveMember
+    public class RemoteVideoMember : ActiveMember
     {
-        public Organizer(string id):base(id)
+        public RemoteVideoMember(string id):base(id)
         { 
         }
-        public override RoleTypeEnum RoleType => RoleTypeEnum.Organizer;
+        public override MemeberTypeEnum RoleType => MemeberTypeEnum.RemoteVideo;
     }
 }

+ 0 - 41
demo/fis.media/Library/Media/Members/RoleTypeEnum.cs

@@ -1,41 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace fis.media.Library.Media.Members
-{
-    /// <summary>
-    /// 本枚举结合多方会诊业务
-    /// 对房间角色做了一定抽象
-    /// 不局限于会诊业务
-    /// </summary>
-    public enum RoleTypeEnum
-    {
-        /// <summary>
-        /// 组织者,主动发起和创建房间的人
-        /// 举例:会诊业务中的专家。
-        /// </summary>
-        Organizer,
-
-        /// <summary>
-        /// 主要参与者
-        /// 举例:会诊业务中的主要参与者是基层医生
-        /// </summary>
-        MainParticipent,
-
-
-        /// <summary>
-        /// 房间内受邀嘉宾
-        /// 举例:会诊业务中被邀请的第三方专家
-        /// </summary>
-        Guest,
-
-        /// <summary>
-        /// 房间内一般参与者
-        /// 举例:会诊中的助理、报告录入员之类
-        /// </summary>
-        Participent,
-    }
-}

+ 19 - 4
demo/fis.media/Library/Media/Players/AbstractPlayer.cs

@@ -7,17 +7,22 @@ using System.Threading.Tasks;
 
 namespace fis.media.Library.Media.Players
 {
-    public abstract class AbstractPlayer
+    public abstract class AbstractPlayer:IDisposable
     {
+        public AbstractPlayer(string id)
+        {
+            Id = id;
+        }
+
         /// <summary>
-        /// 当前播放器所在的房间
+        /// 播放器 Id
         /// </summary>
-        public IRoom Room { get; }
+        public string Id { get; set; }
 
         /// <summary>
         /// 是否正在播放
         /// </summary>
-        public bool IsPlaying { get; }
+        public virtual bool IsPlaying { get; }
 
         /// <summary>
         /// 播放器频道
@@ -39,6 +44,16 @@ namespace fis.media.Library.Media.Players
         /// </summary>
         protected abstract void Update();
 
+        protected virtual void DoDispose()
+        {
+            Stop();
+        }
+
+        public void Dispose()
+        {
+            DoDispose();
+        }
+
         /// <summary>
         /// 增加频道
         /// </summary>

+ 53 - 0
demo/fis.media/Library/Media/Players/ChannelVideoFrame.cs

@@ -0,0 +1,53 @@
+using fis.media.Library.Media.Members;
+using fis.media.Library.Media.Publisher;
+using fis.media.Library.Players;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace fis.media.Library.Media.Players
+{
+    /// <summary>
+    /// 频道视频帧定义
+    /// </summary>
+    public class ChannelVideoFrame
+    {
+       /// <summary>
+       /// 构造函数
+       /// </summary>
+       /// <param name="videoFrameData"></param>
+       /// <param name="channelOwnerId"></param>
+       /// <param name="channelNameEnum"></param>
+       /// <param name="ownerRoleType"></param>
+       public ChannelVideoFrame(VideoFrameData videoFrameData, string channelOwnerId, ChannelNameEnum channelNameEnum, MemeberTypeEnum ownerRoleType)
+       {
+            VideoFrameData = videoFrameData;
+            ChannelOwnerId = channelOwnerId;
+            ChannelNameEnum = channelNameEnum;
+            OwnerRoleType = ownerRoleType;
+        }
+
+
+        /// <summary>
+        /// 频道视频帧
+        /// </summary>
+        public VideoFrameData VideoFrameData { get; set; }
+
+        /// <summary>
+        /// 频道拥有者Id
+        /// </summary>
+        public string ChannelOwnerId { get;}
+
+        /// <summary>
+        /// 频道类型名称枚举
+        /// </summary>
+        public ChannelNameEnum ChannelNameEnum { get; }
+
+        /// <summary>
+        /// 成员角色
+        /// </summary>
+        public MemeberTypeEnum OwnerRoleType { get; }
+    }
+}

+ 40 - 4
demo/fis.media/Library/Media/Players/PlayerChannel.cs

@@ -1,4 +1,6 @@
 using fis.media.Library.Media.EventArgs;
+using fis.media.Library.Media.Members;
+using fis.media.Library.Media.Publisher;
 using fis.media.Library.Players;
 using System;
 using System.Collections.Generic;
@@ -10,10 +12,24 @@ namespace fis.media.Library.Media.Players
 {
     public class PlayerChannel
     {
+        private VideoFrameData _currentVideoFrame;
+        private Task _task;
+        private ChannelVideoFrame _channelVideoFrame;
+
+        /// <summary>
+        /// 频道名称
+        /// </summary>
+        public ChannelNameEnum ChannelName  {get;set;}
+
+        /// <summary>
+        /// 成员角色类型
+        /// </summary>
+        public MemeberTypeEnum ChannelMemberRoleType { get; set; }
+
         /// <summary>
-        /// 频道Id
+        /// 频道所属成员Id
         /// </summary>
-        public int Id { get; set; }
+        public string ChannelMemberId { get; set; }
 
         /// <summary>
         /// 是否正在播放
@@ -23,12 +39,12 @@ namespace fis.media.Library.Media.Players
         /// <summary>
         /// 当前视频帧
         /// </summary>
-        public VideoFrameData CurrentVideoFrame { get; }
+        public VideoFrameData CurrentVideoFrame => _currentVideoFrame;
 
         /// <summary>
         /// 收到画面
         /// </summary>
-        public event EventHandler<VideoFrameData> FrameRecieved;
+        public event EventHandler<ChannelVideoFrame> FrameRecieved;
 
         /// <summary>
         /// 错误事件
@@ -39,5 +55,25 @@ namespace fis.media.Library.Media.Players
         /// 状态事件
         /// </summary>
         public event EventHandler<StatusArgs> StatusChanged;
+
+        public void UpdateFrame(VideoFrameData videoFrameData)
+        {
+           _currentVideoFrame= videoFrameData;
+            IsPlaying = true;
+
+            if (_channelVideoFrame == null)
+            {
+                _channelVideoFrame = new ChannelVideoFrame(videoFrameData, ChannelMemberId, ChannelName,ChannelMemberRoleType);
+            }
+            else {
+                _channelVideoFrame.VideoFrameData = _currentVideoFrame;
+            }
+
+            if (_task == null)
+            {
+                _task = new Task(() => FrameRecieved.Invoke(this, _channelVideoFrame));
+            }
+            _task.Start();
+        }
     }
 }

+ 36 - 10
demo/fis.media/Library/Media/Players/RtcPlayer.cs

@@ -1,29 +1,55 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using fis.media.Library.Players;
+using fis.media.Managers;
 
 namespace fis.media.Library.Media.Players
 {
     internal class RtcPlayer : AbstractPlayer
     {
+        private bool _isPlaying;
+
+        public RtcPlayer(string id):base(id)
+        {
+           
+        }
+
+        public override bool IsPlaying => _isPlaying;
         public override void Start()
         {
-            throw new NotImplementedException();
+            PlayerManager.Instance.VideoFrameReceived += OnVideoFramReceived;
+            _isPlaying = true;
+        }
+
+        private void OnVideoFramReceived(object? sender, RemoteVideoFrameData e)
+        {
+            if (e.UserId != Id)
+            {
+                return;
+            }
+
+            if (PlayerChannels.Count == 1)
+            {
+                var channel = PlayerChannels[0];              
+                channel.UpdateFrame(e.Data);
+            }
+            else { 
+             //To do 根据channel信息执行裁图逻辑,生成对应图片,并更新至channel.
+            }
+            
         }
 
         public override void Stop()
         {
-            throw new NotImplementedException();
+            PlayerManager.Instance.VideoFrameReceived -= OnVideoFramReceived;
+            foreach (var c in PlayerChannels)
+            {
+                c.IsPlaying = false;
+            }
+            _isPlaying = false;
         }
 
         protected override void Update()
         {
             throw new NotImplementedException();
         }
-        private void BuildChannels()
-        { 
-        }
     }
 }

+ 4 - 0
demo/fis.media/Library/Media/Players/RtmpPlayer.cs

@@ -8,6 +8,10 @@ namespace fis.media.Library.Media.Players
 {
     internal class RtmpPlayer : AbstractPlayer
     {
+        public RtmpPlayer(string id) : base(id)
+        {
+        }
+
         public override void Start()
         {
             throw new NotImplementedException();

+ 12 - 0
demo/fis.media/Library/Media/Players/VideoFrameData.cs

@@ -99,4 +99,16 @@ namespace fis.media.Library.Players
             ImageDataBuffer = new ImageDataBuffer();
         }
     }
+
+    public class LocalPreviewVideoFrameData
+    {
+        public string UserId { get; }
+
+        public VideoFrameData? Data { get; set; }
+
+        public LocalPreviewVideoFrameData(string userId)
+        {
+            UserId = userId;
+        }
+    }
 }

+ 24 - 0
demo/fis.media/Library/Media/Publisher/ChannelNameEnum.cs

@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace fis.media.Library.Media.Publisher
+{
+    public enum ChannelNameEnum
+    {
+        /// <summary>
+        /// 主频道
+        /// </summary>
+        Main,
+        /// <summary>
+        /// 副频道1
+        /// </summary>
+        SideOne,
+        /// <summary>
+        /// 副频道2
+        /// </summary>
+        SideTwo,
+    }
+}

+ 31 - 0
demo/fis.media/Library/Media/Publisher/ChannelTypeEnum.cs

@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace fis.media.Library.Media.Publisher
+{
+    public enum ChannelTypeEnum
+    {
+        /// <summary>
+        /// 只有主区域
+        /// </summary>
+        OnlyMainRegion,
+
+        /// <summary>
+        /// 主区域带第一个副区域
+        /// </summary>
+        MainRegionWithSideOne,
+
+        /// <summary>
+        /// 主区域带第二个副区域
+        /// </summary>
+        MainRegionWithSideTwo,
+
+        /// <summary>
+        /// 主区域带第所有副区域
+        /// </summary>
+        MainRegionWithAllSide
+    }
+}

+ 15 - 2
demo/fis.media/Library/Media/Publisher/LayerType.cs

@@ -8,7 +8,20 @@ namespace fis.media.Library.Media.Publisher
 {
     public enum LayerType
     {
-        None,
-        //To add more
+        /// <summary>
+        /// 独立画面
+        /// </summary>
+        Single,
+
+        /// <summary>
+        /// 带有一个摄像头画面
+        /// </summary>
+        WithOneCamera,
+
+        /// <summary>
+        /// 带有两个摄像头画面
+        /// </summary>
+        WithTwoCamera,
+
     }
 }

+ 32 - 0
demo/fis.media/Library/Media/Publisher/PositionEnum.cs

@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace fis.media.Library.Media.Publisher
+{
+    public enum PositionEnum
+    {
+        /// <summary>
+        /// 中心
+        /// </summary>
+        Center,
+        /// <summary>
+        /// 右上
+        /// </summary>
+        TopRight,
+        /// <summary>
+        /// 左上
+        /// </summary>
+        TopLeft,
+        /// <summary>
+        /// 右下
+        /// </summary>
+        BottomRight,
+        /// <summary>
+        /// 左下
+        /// </summary>
+        BottomLeft,
+    }
+}

+ 68 - 2
demo/fis.media/Library/Media/Publisher/RegionInfo.cs

@@ -7,7 +7,73 @@ using System.Threading.Tasks;
 namespace fis.media.Library.Media.Publisher
 {
     public class RegionInfo
-    {
-        //To do
+    {  
+        /// <summary>
+        /// 合流图片总宽
+        /// </summary>
+        public double Width { get; set; }
+
+
+        /// <summary>
+        /// 合流图片总高
+        /// </summary>
+
+        public double Height { get; set; }
+
+        /// <summary>
+        /// 合流主图片区域
+        /// </summary>
+        public RegionModel MainRegion { get; set; }
+  
+        /// <summary>
+        /// 合流副图片区域
+        /// </summary>
+        public RegionModel SideRegionOne { get; set; }
+
+        /// <summary>
+        /// 合流副图片区域
+        /// </summary>
+        public RegionModel SideRegionTwo { get; set; }
+
+        public ChannelTypeEnum GetChannelTypeEnum()
+        {
+            if (OnlyMainRegion)
+            {
+                return ChannelTypeEnum.OnlyMainRegion;
+            }
+            if (MainRegionWithSideOne)
+            {
+                return ChannelTypeEnum.MainRegionWithSideOne;
+            }
+            if (MainRegionWithSideTwo)
+            {
+                return ChannelTypeEnum.MainRegionWithSideTwo;
+            }
+            if (MainRegionWithAllSide)
+            {
+                return ChannelTypeEnum.MainRegionWithAllSide;
+            }
+            return ChannelTypeEnum.OnlyMainRegion;
+        }
+
+        /// <summary>
+        /// 只有主区域
+        /// </summary>
+        private bool OnlyMainRegion => MainRegion != null && SideRegionOne==null&& SideRegionTwo == null;
+
+        /// <summary>
+        /// 主区域带副区域1
+        /// </summary>
+        private bool MainRegionWithSideOne => MainRegion != null && SideRegionOne != null && SideRegionTwo == null;
+
+        /// <summary>
+        /// 主区域带副区域2
+        /// </summary>
+        private bool MainRegionWithSideTwo => MainRegion != null && SideRegionOne == null && SideRegionTwo != null;
+
+        /// <summary>
+        /// 主区域带所有副区域
+        /// </summary>
+        private bool MainRegionWithAllSide => MainRegion != null && SideRegionOne != null && SideRegionTwo != null;
     }
 }

+ 17 - 0
demo/fis.media/Library/Media/Publisher/RegionModel.cs

@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace fis.media.Library.Media.Publisher
+{
+    public class RegionModel
+    {
+        public double Width { get; set; }
+
+        public double Height { get; set; }
+
+        public PositionEnum Position { get; set; }
+    }
+}

+ 7 - 1
demo/fis.media/Library/Media/Rooms/IRoom.cs

@@ -8,7 +8,7 @@ using System.Threading.Tasks;
 
 namespace fis.media.Library.Media.Rooms
 {
-    public interface IRoom
+    public interface IRoom:IDisposable
     {
         /// <summary>
         /// 房间ID
@@ -60,5 +60,11 @@ namespace fis.media.Library.Media.Rooms
         /// 成员离开事件
         /// </summary>
         public event EventHandler<AbstractMember> MemberLeaved;
+
+        /// <summary>
+        /// 房间类型枚举
+        /// </summary>
+        public RoomTypeEnum RoomTypeEnum { get; }
+
     }
 }

+ 2 - 1
demo/fis.media/Library/Media/Rooms/IRtcRoom.cs

@@ -11,6 +11,7 @@ namespace fis.media.Library.Media.Rooms
         /// <summary>
         /// Rtc房间id
         /// </summary>
-        public int RTCRoomId { get; set; }
+        public int RTCRoomId { get; }
+       
     }
 }

+ 12 - 0
demo/fis.media/Library/Media/Rooms/IVRTCRoom.cs

@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace fis.media.Library.Media.Rooms
+{
+    public interface IVRTCRoom:IRoom
+    {
+    }
+}

+ 20 - 0
demo/fis.media/Library/Media/Rooms/RTMPRoom.cs

@@ -0,0 +1,20 @@
+using fis.media.Library.Media.EventArgs;
+using fis.media.Library.Media.Members;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace fis.media.Library.Media.Rooms
+{
+    internal class RTMPRoom : Room, IRtmpRoom
+    {
+        private static IRoom _instance;
+        private RTMPRoom() : base(RoomTypeEnum.RTMPVirtualRoom)
+        {
+
+        }
+        public static IRoom Instance => _instance ?? (_instance = new RTMPRoom());      
+    }
+}

+ 52 - 0
demo/fis.media/Library/Media/Rooms/Room.cs

@@ -0,0 +1,52 @@
+using fis.media.Library.Media.EventArgs;
+using fis.media.Library.Media.Members;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace fis.media.Library.Media.Rooms
+{
+    public abstract class Room : IRoom
+    {
+        protected Room(RoomTypeEnum roomTypeEnum)
+        {
+            RoomTypeEnum= roomTypeEnum;
+        }
+
+        public string RoomId { get; set; }
+        public List<AbstractMember> Members { get; set; }
+        public RoomTypeEnum RoomTypeEnum { get; }
+
+        public event EventHandler<ErrorArgs> ErrorAccurred;
+        public event Action OnRoomClosed;
+        public event EventHandler<AbstractMember> MemberJoined;
+        public event EventHandler<AbstractMember> MemberLeaved;
+
+        public virtual void Close()
+        {
+            Dispose();
+        }
+
+        public void Dispose()
+        {
+            DoDispose();
+        }
+        protected virtual bool DoDispose()
+        {
+            return true;
+        }
+
+        public virtual int EnterRoom(AbstractMember member)
+        {
+            Members.Add(member);
+            return 1;
+        }
+
+        public virtual int KickMember(AbstractMember member)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}

+ 26 - 0
demo/fis.media/Library/Media/Rooms/RoomTypeEnum.cs

@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace fis.media.Library.Media.Rooms
+{
+    public enum RoomTypeEnum
+    {
+        /// <summary>
+        /// 腾讯RTC房间
+        /// </summary>
+        TRTCRoom,
+
+        /// <summary>
+        /// 飞依诺自研RTC房间
+        /// </summary>
+        VRTCRoom,
+
+        /// <summary>
+        /// 虚拟的RTMP房间
+        /// </summary>
+        RTMPVirtualRoom,
+    }
+}

+ 85 - 0
demo/fis.media/Library/Media/Rooms/TRTCRoom.cs

@@ -0,0 +1,85 @@
+using fis.media.Library.Media.EventArgs;
+using fis.media.Library.Media.Members;
+using fis.media.Library.Media.Players;
+using fis.media.Library.Media.Publisher;
+using fis.media.ThirdPartLibrary.Tencent;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace fis.media.Library.Media.Rooms
+{
+    internal class TRTCRoom : Room, IRtcRoom
+    {
+        private static IRoom _instance;
+        private TRTCChatRoom _trtcChatRoom;
+        private int _rtcRoomId;
+
+        private TRTCRoom():base(RoomTypeEnum.TRTCRoom)
+        {
+            _trtcChatRoom = TRTCChatRoom.Instance;
+        }
+        public static IRoom Instance => _instance ?? (_instance = new TRTCRoom());
+        public int RTCRoomId => _rtcRoomId;
+
+        public override int EnterRoom(AbstractMember member)
+        {
+            if (member is ActiveMember activeMember)
+            {
+                if (activeMember.IsOnline)
+                {
+                    activeMember.ApplyPlayer(GenerateRtcPlayer(activeMember));
+                }
+            }
+           
+            return base.EnterRoom(member);
+        }
+
+        public static AbstractPlayer GenerateRtcPlayer(ActiveMember activeMember)
+        {
+            var player = new RtcPlayer(activeMember.Id);
+            var regionInfo = activeMember.LayerConfig.RegionInfo;
+            var channels = player.PlayerChannels;
+
+            channels.Add(new PlayerChannel()
+            {
+                ChannelName = ChannelNameEnum.Main,
+                ChannelMemberId = activeMember.Id,
+                ChannelMemberRoleType = activeMember.RoleType,
+            });
+            switch (regionInfo.GetChannelTypeEnum())
+            {
+                case ChannelTypeEnum.OnlyMainRegion:                    
+                    break;
+
+                case ChannelTypeEnum.MainRegionWithSideOne:
+                    channels.Add(new PlayerChannel()
+                    {
+                        ChannelName = ChannelNameEnum.SideOne,
+                        ChannelMemberId = activeMember.Id,
+                        ChannelMemberRoleType = activeMember.RoleType,
+                    });
+                    break;
+                case ChannelTypeEnum.MainRegionWithSideTwo:
+                    channels.Add(new PlayerChannel()
+                    {
+                        ChannelName = ChannelNameEnum.SideTwo,
+                        ChannelMemberId = activeMember.Id,
+                        ChannelMemberRoleType = activeMember.RoleType,
+                    });
+                    break;
+                default:break;
+            }
+           
+            return player;
+        }
+
+        protected override bool DoDispose()
+        {
+            _trtcChatRoom.Exit();
+            return base.DoDispose();
+        }
+    }
+}

+ 18 - 0
demo/fis.media/Library/Media/Rooms/VRTCRoom.cs

@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace fis.media.Library.Media.Rooms
+{
+    public class VRTCRoom:Room, IVRTCRoom
+    {
+        private static IRoom _instance;
+        private VRTCRoom() : base(RoomTypeEnum.VRTCRoom)
+        {
+
+        }
+        public static IRoom Instance => _instance ?? (_instance = new VRTCRoom());
+    }
+}

+ 1 - 1
demo/fis.media/Library/OutPut/VideoFrameOutput/Channelbase.cs → demo/fis.media/Library/OutPut/FisVideoFrameOutput/Channelbase.cs

@@ -4,7 +4,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace fis.media.Library.OutPut.VideoFrameOutput
+namespace fis.media.Library.OutPut.FisVideoFrameOutput
 {
     /// <summary>
     /// 视频频道基类

+ 6 - 1
demo/fis.media/Library/OutPut/VideoFrameOutput/LocalCameraChannel.cs → demo/fis.media/Library/OutPut/FisVideoFrameOutput/LocalCameraChannel.cs

@@ -4,10 +4,15 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace fis.media.Library.OutPut.VideoFrameOutput
+namespace fis.media.Library.OutPut.FisVideoFrameOutput
 {
     public class LocalCameraChannel : Channelbase
     {
+        public LocalCameraChannel(string channelId, string channelName)
+        {
+            ChannelDisplayName = channelName;
+            Id = channelId;
+        }
         public override string ChannelDisplayName { get; set ; }
     }
 }

+ 6 - 1
demo/fis.media/Library/OutPut/VideoFrameOutput/MainChannel.cs → demo/fis.media/Library/OutPut/FisVideoFrameOutput/MainChannel.cs

@@ -4,10 +4,15 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace fis.media.Library.OutPut.VideoFrameOutput
+namespace fis.media.Library.OutPut.FisVideoFrameOutput
 {
     internal class MainChannel : Channelbase
     {
+        public MainChannel(string channelId, string channelName)
+        {
+            ChannelDisplayName = channelName;
+            Id = channelId;
+        }
         public override string ChannelDisplayName { get; set; }
     }
 }

+ 1 - 1
demo/fis.media/Library/OutPut/VideoFrameOutput/RemoteCameraChannel.cs → demo/fis.media/Library/OutPut/FisVideoFrameOutput/RemoteCameraChannel.cs

@@ -4,7 +4,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace fis.media.Library.OutPut.VideoFrameOutput
+namespace fis.media.Library.OutPut.FisVideoFrameOutput
 {
     /// <summary>
     /// 远程摄像头频道

+ 6 - 1
demo/fis.media/Library/OutPut/VideoFrameOutput/SideChannelOne.cs → demo/fis.media/Library/OutPut/FisVideoFrameOutput/SideChannelOne.cs

@@ -4,10 +4,15 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace fis.media.Library.OutPut.VideoFrameOutput
+namespace fis.media.Library.OutPut.FisVideoFrameOutput
 {
     internal class SideChannelOne : Channelbase
     {
+        public SideChannelOne(string channelId, string channelName)
+        {
+            ChannelDisplayName = channelName;
+            Id = channelId;
+        }
         public override string ChannelDisplayName { get; set ; }
     }
 }

+ 6 - 1
demo/fis.media/Library/OutPut/VideoFrameOutput/SideChannelTwo.cs → demo/fis.media/Library/OutPut/FisVideoFrameOutput/SideChannelTwo.cs

@@ -4,10 +4,15 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace fis.media.Library.OutPut.VideoFrameOutput
+namespace fis.media.Library.OutPut.FisVideoFrameOutput
 {
     internal class SideChannelTwo : Channelbase
     {
+        public SideChannelTwo(string channelId, string channelName)
+        {
+            ChannelDisplayName = channelName;
+            Id = channelId;
+        }
         public override string ChannelDisplayName { get; set; }
     }
 }

+ 6 - 1
demo/fis.media/Library/OutPut/VideoFrameOutput/VideoStreamStructre.cs → demo/fis.media/Library/OutPut/FisVideoFrameOutput/VideoStreamStructre.cs

@@ -4,10 +4,15 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace fis.media.Library.OutPut.VideoFrameOutput
+namespace fis.media.Library.OutPut.FisVideoFrameOutput
 {
     public class VideoStreamStructre
     {
+        public VideoStreamStructre()
+        {
+            VideoChannels = new List<Channelbase>();
+        }
+
         public List<Channelbase> VideoChannels { get; set; }
     }
 }

+ 231 - 0
demo/fis.media/Managers/ChatRoomManager.cs

@@ -0,0 +1,231 @@
+using fis.media.Library.Media.Members;
+using fis.media.Library.Media.Players;
+using fis.media.Library.Media.Publisher;
+using fis.media.Library.Media.Rooms;
+using fis.media.Library.OutPut.FisVideoFrameOutput;
+using fis.media.Library.Players;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace fis.media.Managers
+{
+    public class ChatRoomManager
+    {        
+        private ChatRoomManager()
+        {
+            Pusher =PusherManager.Instance;
+            Player = PlayerManager.Instance;
+            //默认为TRTC房间
+            RoomType = RoomTypeEnum.TRTCRoom;
+            _videoStreamStructre = new VideoStreamStructre();
+        }
+        private static ChatRoomManager _instance;
+        private RoomTypeEnum _roomType;
+        private VideoStreamStructre _videoStreamStructre;
+
+        public static ChatRoomManager Instance => _instance ?? (_instance = new ChatRoomManager());
+        public IRoom Room { get; private set; }
+        public PusherManager Pusher { get; set; }
+        public PlayerManager Player { get; set; }
+        
+        public event EventHandler<VideoStreamStructre> VideoStreamOutputInvoked;
+        
+      
+        /// <summary>
+        /// 进入房间
+        /// </summary>
+        /// <param name="roomType"></param>
+        /// <param name="members"></param>
+        /// <param name="args"></param>
+        /// <returns></returns>
+        public bool Enter(RoomTypeEnum roomType, List<AbstractMember> members,string[] args)
+        {
+            RoomType = roomType;
+            foreach (var c in members)
+            {
+                Room.EnterRoom(c);
+            }
+            var memebers = Room.Members;
+            foreach (var m in memebers)
+            {
+                var channels = m.Player.PlayerChannels;
+                foreach (var channel in channels)
+                {
+                    channel.FrameRecieved += OnChannelFrameRevieved;
+                }
+            }
+            var commonStartupArgs= new List<string> { $"{RoomType}" }; 
+         
+            foreach (var arg in args)
+            {
+                commonStartupArgs.Add(arg);
+            }
+            Pusher.Start(commonStartupArgs.ToArray());
+
+            Pusher.PreviewImageReceived += OnPreviewImageReceived;
+
+            Player.Start(commonStartupArgs.ToArray());
+
+            return true;
+        }
+
+        private void OnPreviewImageReceived(object? sender, LocalPreviewVideoFrameData e)
+        {
+            var selfMember = Room.Members.FirstOrDefault(x => x.Id == e.UserId);
+           var activeMember= selfMember as ActiveMember;
+            var channelVideoFrame = new ChannelVideoFrame(e.Data,activeMember.Id, ChannelNameEnum.Main,activeMember.RoleType);
+            OnChannelFrameRevieved(this, channelVideoFrame);
+        }
+
+        private void OnChannelFrameRevieved(object? sender, ChannelVideoFrame e)
+        {
+            var existOuput = _videoStreamStructre.VideoChannels.FirstOrDefault(x => x.Id == e.ChannelOwnerId
+                && x.ChannelDisplayName == e.ChannelNameEnum.ToString());
+            if (existOuput == null)
+            {
+                var outPut = GenerateOutput(e);
+                outPut.UpdataFrameData(e.VideoFrameData.ToBase64());
+                _videoStreamStructre.VideoChannels.Add(outPut);
+            }
+            else
+            {
+                existOuput.UpdataFrameData(e.VideoFrameData.ToBase64());
+            }
+            VideoStreamOutputInvoked?.Invoke(this,_videoStreamStructre);
+        }
+
+     
+        /// <summary>
+        /// 更新成员状态
+        /// </summary>
+        /// <param name="abstractMember"></param>
+        /// <returns></returns>
+        public bool UpdateMember(AbstractMember abstractMember)
+        {
+            var memeber = Room.Members.FirstOrDefault(x => x.Id == abstractMember.Id);
+            if (memeber == null)
+            {
+                return false;
+            }
+            var activeExistMemeber= memeber as ActiveMember;
+            var activeMemeber = abstractMember as ActiveMember;
+            if (activeMemeber != null && activeExistMemeber != null)
+            {
+                activeExistMemeber.IsOnline = activeMemeber.IsOnline;
+                activeExistMemeber.IsMute = activeMemeber.IsMute;
+                activeExistMemeber.IsCameraOn = activeExistMemeber.IsCameraOn;
+                activeExistMemeber.IsMergeChannel = activeExistMemeber.IsMergeChannel;
+                return activeExistMemeber.AdaptMemberUpdate();
+            }
+            return true;
+          
+        }
+
+        /// <summary>
+        /// 删除成员
+        /// </summary>
+        /// <param name="abstractMember"></param>
+        /// <returns></returns>
+        public bool RemoveMember(AbstractMember abstractMember)
+        {
+            var memeber = Room.Members.FirstOrDefault(x => x.Id == abstractMember.Id);
+            if (memeber == null)
+            {
+                return false;
+            }
+            memeber.Dispose();
+            Room.Members.Remove(memeber);
+            return true;
+
+        }
+
+        public bool Exit()
+        {
+            Pusher.Dispose();
+            Player.Dispose();
+          
+            var memebers = Room.Members;
+           
+            foreach (var m in memebers)
+            {
+                var channels = m.Player.PlayerChannels;
+                foreach (var channel in channels)
+                {
+                    channel.FrameRecieved -= OnChannelFrameRevieved;
+                }
+            }
+
+            Room.Dispose();
+            return true;
+        }
+
+        private RoomTypeEnum RoomType
+        {
+            get
+            {
+                return _roomType;
+            }
+            set
+            {
+                if (_roomType != value)
+                {
+                    _roomType = value;
+                    SwithRoom();
+                }
+            }
+        }
+
+
+        private void SwithRoom()
+        {
+            Room?.Dispose();
+            Room = null;
+            if (RoomType == RoomTypeEnum.TRTCRoom)
+            {
+                Room = TRTCRoom.Instance;
+            }
+            if (RoomType == RoomTypeEnum.RTMPVirtualRoom)
+            {
+                Room = RTMPRoom.Instance;
+            }
+            if (RoomType == RoomTypeEnum.VRTCRoom)
+            {
+                Room = VRTCRoom.Instance;
+            }
+        }
+
+        private Channelbase GenerateOutput(ChannelVideoFrame e)
+        {
+            if (e.OwnerRoleType == MemeberTypeEnum.LocalVideo)
+            {
+                return new LocalCameraChannel(e.ChannelOwnerId, e.ChannelNameEnum.ToString());
+
+            }
+            if (e.OwnerRoleType == MemeberTypeEnum.RemoteVideo)
+            {
+                return new RemoteCameraChannel(e.ChannelOwnerId, e.ChannelNameEnum.ToString());
+            }
+            if (e.OwnerRoleType == MemeberTypeEnum.MainDeviceVideo)
+            {
+                if (e.ChannelNameEnum == ChannelNameEnum.Main)
+                {
+                    return new MainChannel(e.ChannelOwnerId, e.ChannelNameEnum.ToString());
+                }
+                if (e.ChannelNameEnum == ChannelNameEnum.SideOne)
+                {
+                    return new SideChannelOne(e.ChannelOwnerId, e.ChannelNameEnum.ToString());
+                }
+                if (e.ChannelNameEnum == ChannelNameEnum.SideTwo)
+                {
+                    return new SideChannelTwo(e.ChannelOwnerId, e.ChannelNameEnum.ToString());
+                }
+            }
+            return null;
+        }
+
+
+    }
+}

+ 74 - 16
demo/fis.media/Managers/PlayerManager.cs

@@ -3,8 +3,11 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
+using fis.media.Library.Media.Players;
+using fis.media.Library.Media.Rooms;
 using fis.media.Library.Players;
 using fis.media.ThirdPartLibrary.Daniu.Player;
+using fis.media.ThirdPartLibrary.Tencent;
 
 namespace fis.media.Managers
 {
@@ -13,19 +16,15 @@ namespace fis.media.Managers
         private static PlayerManager? _instance;
 
         private SmartPlayer? _smartPlayer { get; set; }
-        public EventHandler<VideoFrameData>? VideoFrameReceived;
+        public EventHandler<RemoteVideoFrameData>? VideoFrameReceived;
+        private RoomTypeEnum _roomType;
+        private string _rtmpPullUrl;
 
-        private PlayerManager() { }
-
-        public static PlayerManager Instance()
-        {
-            if (_instance == null)
-            {
-                _instance = new PlayerManager();
-            }
-            return _instance;
+        private PlayerManager() {
+           
         }
-
+        public static PlayerManager Instance => _instance ?? (_instance = new PlayerManager());
+    
         /// <summary>
         /// 注册SmartPlayerSDK
         /// </summary>
@@ -35,17 +34,70 @@ namespace fis.media.Managers
         }
 
         /// <summary>
-        /// 开始拉流
+        /// 开始RTMP拉流
         /// </summary>
         /// <param name="url"></param>
-        public void Start(string url)
+        private void StartRTMP()
         {
             if (_smartPlayer == null)
             {
                 _smartPlayer = new SmartPlayer();
-                _smartPlayer.VideoFrameReceived += VideoFrameReceived;
+                //_smartPlayer.VideoFrameReceived += VideoFrameReceived;
             }
-            _smartPlayer.Play(url);
+            _smartPlayer.Play(_rtmpPullUrl);
+        }
+
+        /// <summary>
+        /// 开始TRTC拉流
+        /// </summary>
+        /// <param name="url"></param>
+        private void StartTRTC()
+        {
+            TRTCChatRoom.Instance.RemoteVideoFrameArrived += OnRemoteVideoFrameArrived;
+            TRTCChatRoom.Instance.RemoteVideoScreenFrameArrived += OnRemoteVideoScreenFrameArrived;
+        }
+
+        public void Start(string[] args)
+        {
+            if (SetRoomPara(args))
+            {
+                if (_roomType == RoomTypeEnum.TRTCRoom)
+                {
+                    StartTRTC();
+                }
+                if (_roomType == RoomTypeEnum.RTMPVirtualRoom)
+                {
+                    StartRTMP();
+                }
+            }
+
+        }
+
+        private bool SetRoomPara(string[] args)
+        {
+            if (args == null || args.Length < 1)
+            {
+                return false;
+            }
+            _roomType = (RoomTypeEnum)Enum.Parse(typeof(RoomTypeEnum), args[0], true);
+
+            if (_roomType == RoomTypeEnum.RTMPVirtualRoom && args.Length == 2)
+            {
+                var url = args[1];
+                _rtmpPullUrl = url;
+            }
+
+            return true;
+        }
+
+        private void OnRemoteVideoScreenFrameArrived(object? sender, RemoteVideoFrameData e)
+        {
+            throw new NotImplementedException();
+        }
+
+        private void OnRemoteVideoFrameArrived(object? sender, RemoteVideoFrameData e)
+        {
+            VideoFrameReceived?.Invoke(this,e);
         }
 
         /// <summary>
@@ -54,6 +106,9 @@ namespace fis.media.Managers
         public void Stop()
         {
             _smartPlayer?.Stop();
+
+            TRTCChatRoom.Instance.RemoteVideoFrameArrived -= OnRemoteVideoFrameArrived;
+            TRTCChatRoom.Instance.RemoteVideoScreenFrameArrived -= OnRemoteVideoScreenFrameArrived;
         }
 
         /// <summary>
@@ -91,6 +146,7 @@ namespace fis.media.Managers
         public void StopRecord()
         {
             _smartPlayer?.StopRecord();
+
         }
 
         /// <summary>
@@ -104,9 +160,11 @@ namespace fis.media.Managers
 
         public void Dispose()
         {
+            Stop();
+
             if (_smartPlayer != null)
             {
-                _smartPlayer.VideoFrameReceived -= VideoFrameReceived;
+                //_smartPlayer.VideoFrameReceived -= VideoFrameReceived;
             }
         }
     }

+ 121 - 9
demo/fis.media/Managers/PusherManager.cs

@@ -1,7 +1,9 @@
 using System.Runtime.Versioning;
+using fis.media.Library.Media.Rooms;
 using fis.media.Library.Players;
 using fis.media.ThirdPartLibrary.Daniu.Common;
 using fis.media.ThirdPartLibrary.Daniu.Publisher;
+using fis.media.ThirdPartLibrary.Tencent;
 
 namespace fis.media.Managers
 {
@@ -11,9 +13,18 @@ namespace fis.media.Managers
         private static PusherManager? _instance;
         public static PusherManager Instance => _instance ?? (_instance = new PusherManager());
 
-        public EventHandler<VideoFrameData>? PreviewImageReceived;
+        public EventHandler<LocalPreviewVideoFrameData> PreviewImageReceived;
 
         private SmartPublisher? _smartPublisher;
+        private string _userId;
+        private int _appId;
+        private string _userSign;
+        private int _rtcRoomId;
+        private RoomTypeEnum _roomType;
+        private string _rtmpurl;
+        private int _screenWidth;
+        private int _screenHeight;
+        private LocalPreviewVideoFrameData _localPreviewFrame;
 
         private PusherManager() { }
         /// <summary>
@@ -24,23 +35,114 @@ namespace fis.media.Managers
             SmartPublisher.RegistSmartPublisherSDK();
         }
 
+        public void Start(string[] args)
+        {
+            if (SetRoomPara(args))
+            {
+                if (_roomType == RoomTypeEnum.TRTCRoom)
+                {
+                    StartTRTC();
+                }
+                if (_roomType == RoomTypeEnum.RTMPVirtualRoom)
+                {
+                    StartRtmp();
+                }
+            }            
+        }
+
         /// <summary>
         /// 开始推流
         /// </summary>
         /// <param name="url">推流地址</param>
-        public void Start(string url, int screenWidth, int screenHeight, IntPtr windowHandle)
+        private void StartRtmp()
         {
-           
             if (_smartPublisher == null)
             {
                 _smartPublisher = new SmartPublisher(0, AudioMode.Mix);
-                SetScreenSize(screenWidth, screenHeight);
-                _smartPublisher.SetWindowHandle(windowHandle);
+                SetScreenSize(_screenWidth, _screenHeight);
                 _smartPublisher.InitLayers();
-                _smartPublisher.PreviewImageReceived += PreviewImageReceived;
+                _smartPublisher.PreviewImageReceived += OnRTMPFrameReceived;
             }
             _smartPublisher?.SetEnablePreview(true);
-            _smartPublisher?.Start(url);
+            _smartPublisher?.Start(_rtmpurl);
+
+        }
+
+        private void OnRTMPFrameReceived(object? sender, VideoFrameData e)
+        {
+            throw new NotImplementedException();
+        }
+
+        /// <summary>
+        /// 开始推流(TRTC进入房间即开始推流)
+        /// </summary>
+        /// <param name="url">推流地址</param>
+        private void StartTRTC()
+        {
+            TRTCChatRoom.Instance.Enter(_userId, _rtcRoomId, true, _userSign, _appId);
+            TRTCChatRoom.Instance.LocalVideoFrameArrived += OnLocalVideoFrameArrived;
+            TRTCChatRoom.Instance.LocalVideoScreenFrameArrived += OnLocalVideoScreenFrameArrived;
+        }
+
+        private void OnLocalVideoScreenFrameArrived(object? sender, VideoFrameData e)
+        {
+            ;
+        }
+
+        private void OnLocalVideoFrameArrived(object? sender, VideoFrameData e)
+        {
+            if (_localPreviewFrame == null)
+            {
+                _localPreviewFrame = new LocalPreviewVideoFrameData(_userId);
+            }
+            _localPreviewFrame.Data = e;
+            PreviewImageReceived?.Invoke(this, _localPreviewFrame);
+        }
+
+        private bool SetRoomPara(string[] args)
+        {
+            if (args == null||args.Length<1)
+            {
+                return false;
+            }
+            _roomType= (RoomTypeEnum)Enum.Parse(typeof(RoomTypeEnum), args[0], true);
+          
+
+            if (_roomType == RoomTypeEnum.TRTCRoom && args.Length == 5)
+            {
+                if (int.TryParse(args[1], out int appId))
+                {
+                    return false;
+                }
+                var userId=args[2];
+                var userSign = args[3];
+                if (int.TryParse(args[4], out int intergeRoomId))
+                {
+                    return false;
+                }
+                _userId = userId;
+                _appId = appId;
+                _userSign = userSign;
+                _rtcRoomId = intergeRoomId;
+            }
+
+            if (_roomType == RoomTypeEnum.TRTCRoom && args.Length == 4)
+            {
+                if (int.TryParse(args[1], out int screenWidth))
+                {
+                    return false;
+                }
+                if (int.TryParse(args[2], out int screenHeight))
+                {
+                    return false;
+                }
+                var url = args[3];
+                _rtmpurl = url;
+                _screenWidth = screenWidth;
+                _screenHeight = screenHeight;
+            }
+
+            return true;
         }
 
         public void EnableLayer(int index, bool isEnable)
@@ -81,14 +183,24 @@ namespace fis.media.Managers
         /// </summary>
         public void Stop()
         {
-            _smartPublisher?.Close();
+            if (_roomType == RoomTypeEnum.RTMPVirtualRoom)
+            {
+                _smartPublisher?.Close();
+            }
+            if (_roomType == RoomTypeEnum.TRTCRoom)
+            {
+                TRTCChatRoom.Instance.LocalVideoFrameArrived -= OnLocalVideoFrameArrived;
+                TRTCChatRoom.Instance.LocalVideoScreenFrameArrived -= OnLocalVideoScreenFrameArrived;
+                TRTCChatRoom.Instance.Exit();
+            }
         }
 
         public void Dispose()
         {
+            Stop();
             if (_smartPublisher != null)
             {
-                _smartPublisher.PreviewImageReceived -= PreviewImageReceived;
+                _smartPublisher.PreviewImageReceived -= OnRTMPFrameReceived;
                 _smartPublisher?.Dispose();
                 _smartPublisher = null;
             }

+ 3 - 2
demo/fis.media/ThirdPartLibrary/Tencent/TRTCChatRoom.cs

@@ -21,7 +21,7 @@ namespace fis.media.ThirdPartLibrary.Tencent
         public static TRTCChatRoom Instance => _instance ?? (_instance = new TRTCChatRoom());
 
         private ITRTCScreenCaptureSourceList? _mScreenList;
-
+        private string _localId;
         private int _roomId;
 
         private bool _chatUserEnterRoomSuccess = false;
@@ -110,7 +110,8 @@ namespace fis.media.ThirdPartLibrary.Tencent
             if (_roomId <= 0 || _tRTCCloud == null)
             {
                 _roomId = integerRoomId;
-                _tRTCCloud = ITRTCCloud.getTRTCShareInstance();
+                _localId = accountId;
+               _tRTCCloud = ITRTCCloud.getTRTCShareInstance();
                 SIZE thumbSize = new SIZE() { cx = 120, cy = 70 };
                 SIZE iconSize = new SIZE() { cx = 20, cy = 20 };
                 _mScreenList = _tRTCCloud.getScreenCaptureSources(ref thumbSize, ref iconSize);