using Android.Content; using Android.OS; using Com.Tencent.Trtc; using System; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Vinno.FIS.TRTCClient.Common.Log; using static Com.Tencent.Trtc.TRTCCloudDef; namespace Vinno.vCloud.FIS.CrossPlatform.Android.LiveVideo.RTC { public class TRTCLivePusher : TRTCCloudListener, IDisposable { private readonly ManualResetEvent _exitResetEvent = new ManualResetEvent(false); private TRTCCloud _tRtcCloud; private TRTCVideoFrame _videoFrame; private byte[] _tempData; private int _videoResolution; public event EventHandler FirstFrameSend; public TRTCLivePusher(string logPath, Context context) { TRTCCloud.SetLogCompressEnabled(false); TRTCCloud.SetLogDirPath(logPath); TRTCCloud.SetLogLevel(TrtcLogLevelError); _tRtcCloud = TRTCCloud.SharedInstance(context); } /// /// 进入房间 /// /// /// /// /// /// /// /// public void EnterRoom(string userId, int roomId, string userSign, int appid, int videoResolution, int videoFps, int videoBitrate, int minVideoBitrate, bool isMute, string micId) { Logger.WriteLineInfo($"Enter Room,UserId:{userId},RoomId:{roomId}"); _videoResolution = videoResolution; _tRtcCloud.MuteAllRemoteVideoStreams(true); _tRtcCloud.SetDefaultStreamRecvMode(false, false); _tRtcCloud.MuteAllRemoteAudio(true); _tRtcCloud.StartLocalAudio(TrtcAudioQualityMusic);//开启本地音频的采集和上行 _tRtcCloud.EnableCustomVideoCapture(TrtcVideoStreamTypeBig, true); _tRtcCloud.EnableCustomAudioCapture(false); _tRtcCloud.SetVideoEncoderMirror(false); InitVideoParam(videoFps, videoBitrate, minVideoBitrate); var renderParams = new TRTCRenderParams { FillMode = TrtcVideoRenderModeFit, MirrorType = TrtcVideoMirrorTypeDisable }; _tRtcCloud.SetLocalRenderParams(renderParams); TRTCParams trtcParams = new TRTCParams { SdkAppId = appid, RoomId = roomId, UserId = userId, UserSig = userSign, Role = TRTCRoleAnchor }; _tRtcCloud.SetListener(this); _tRtcCloud.MuteLocalAudio(isMute); _tRtcCloud.EnterRoom(trtcParams, TrtcAppSceneLive); } public void SetMute(bool isMute) { if (_tRtcCloud != null) { _tRtcCloud.MuteLocalAudio(isMute); } } public void SwitchMic(string micId) { } /// /// 离开房间 /// public void ExitRoom() { if (_tRtcCloud != null) { Logger.WriteLineInfo($"Exit room"); _tRtcCloud.StopAllRemoteView(); _tRtcCloud.StopLocalPreview(); _tRtcCloud.StopLocalAudio(); _tRtcCloud.MuteLocalAudio(true); _tRtcCloud.MuteLocalVideo(0, true); _exitResetEvent.Reset(); _tRtcCloud.ExitRoom(); _exitResetEvent.WaitOne(5000); _tRtcCloud.SetListener(null); _tRtcCloud = null; } } /// /// 发送要推流的数据 /// /// /// /// public void SendData(int width, int height, IntPtr frameBuffer) { var len = width * height * 3 / 2; if (_videoFrame == null) { _videoFrame = new TRTCVideoFrame { PixelFormat = TrtcVideoPixelFormatI420, BufferType = TrtcVideoBufferTypeByteArray, Width = width, Height = height, Timestamp = 0 }; } if (_tempData == null) { _tempData = new byte[len]; } else if (_tempData.Length != len) { Array.Resize(ref _tempData, len); } Marshal.Copy(frameBuffer, _tempData, 0, len); _videoFrame.Data = _tempData; _tRtcCloud?.SendCustomVideoData(TrtcVideoStreamTypeBig, _videoFrame); } /// /// 发送要推流的数据 /// /// /// /// public void SendData(int width, int height, byte[] frameBuffer) { var len = width * height * 3 / 2; if (_videoFrame == null) { _videoFrame = new TRTCVideoFrame { PixelFormat = TrtcVideoPixelFormatI420, BufferType = TrtcVideoBufferTypeByteArray, Width = width, Height = height, Timestamp = 0, }; } _videoFrame.Data = frameBuffer; _tRtcCloud?.SendCustomVideoData(TrtcVideoStreamTypeBig, _videoFrame); } private void InitVideoParam(int videoFps, int videoBitrate, int minVideoBitrate) { TRTCVideoEncParam videoEncParams = new TRTCVideoEncParam { VideoBitrate = videoBitrate, MinVideoBitrate = minVideoBitrate, VideoFps = videoFps, VideoResolution = _videoResolution, VideoResolutionMode = TrtcVideoResolutionModeLandscape, EnableAdjustRes = false }; _tRtcCloud.SetVideoEncoderParam(videoEncParams); var qosParams = new TRTCNetworkQosParam { Preference = TrtcVideoQosPreferenceClear, ControlMode = VideoQosControlServer, }; _tRtcCloud.SetNetworkQosParam(qosParams); } #region 回调 public override void OnError(int errCode, string errMsg, Bundle extInfo) { Logger.WriteLineError(string.Format("RtcPusher: Tecent errCode : {0}, errMsg : {1}, ExtInfo = {2}", errCode, errMsg, extInfo)); } public override void OnWarning(int warningCode, string warningMsg, Bundle extInfo) { if (warningCode == 1208) { return; } Logger.WriteLineWarn(string.Format("RtcPusher: Tecent warningCode : {0}, warningMsg : {1}, ExtInfo = {2}", warningCode, warningMsg, extInfo)); } public override void OnSendFirstLocalVideoFrame(int p0) { Logger.WriteLineInfo($"onSendFirstLocalVideoFrame successed{p0}"); Task.Run(() =>//Felix:如果不用Task,容易Crash,会SIGSEGV Error { FirstFrameSend?.Invoke(null, EventArgs.Empty); }); } public override void OnEnterRoom(long result) { if (result > 0) { Logger.WriteLineInfo($"Enter Room Success!:{result}"); } else { Logger.WriteLineInfo($"Enter Room Fail!:{result}"); } } public override void OnExitRoom(int reason) { Logger.WriteLineInfo($"Exit Room Success,{reason}"); _exitResetEvent.Set(); } public override void OnConnectionLost() { Logger.WriteLineWarn("RtcPusher Tx connection lost"); } public override void OnTryToReconnect() { Logger.WriteLineWarn("RtcPusher Tx try to reconnect"); } public override void OnConnectionRecovery() { Logger.WriteLineWarn("RtcPusher Tx connection recovery"); } public override void OnConnectOtherRoom(string userId, int errCode, string errMsg) { Logger.WriteLineInfo($"onConnectOtherRoom userId :{userId}, errCode: {errCode}, errMsg: {errMsg}"); } public override void OnFirstVideoFrame(string p0, int p1, int p2, int p3) { } public override void OnSpeedTestResult(TRTCSpeedTestResult currentResult) { } #endregion 回调 } }