using CScript.Net; using CScript.Utilities; using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; using System.Threading; using UnityEngine; namespace CScript.Quick { public class QuickAudioVideoManager : MonoSingleton { public PackageHandler packageHandler = new PackageHandler(null); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int OnVideoFrameCallBackDelegate(IntPtr frame, int frameLen, int timestamp); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int OnAudioFrameCallBackDelegate(IntPtr frame, int frameLen, int timestamp); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int OnLogCallBackDelegate(int state, string msg); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate IntPtr OnWriteCallBackDelegete(); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int OnCloseCallBackDelegate(); /** * * @param channel 传输方式 支持tcp或quic * @param ip 服务端 IP * @param port 服务端 Port * @param isW 是否开启 socket写入回调 1:开启写入 0:不开启写入 * @param wInter 开启socket写入回调的循环间隔,单位毫秒 1-30000 * @param retryCount 重连服务器的次数 0:一直重连 ,1:重连一次 2:重连2次,以此类推 * @param retryInter 重连服务器的间隔,单位毫秒 1-30000 * @param onVideoFrameCb 视频帧回调 * @param onAudioFrameCb 音频帧回调 * @param onLogCb 日志回调 * @param onWriteCb socket写入数据回调 * @param onCloseCb 是否关闭连接回调 * @return */ [DllImport("AvClient")] public static extern IntPtr AvClientConnect(string channel, string ip, int port, int isW, int wInter, int retryCount, int retryInter, OnVideoFrameCallBackDelegate onVideoFrameCb, OnAudioFrameCallBackDelegate onAudioFrameCb, OnLogCallBackDelegate onLogCb, OnWriteCallBackDelegete onWriteCb, OnCloseCallBackDelegate onCloseCb ); private string _quickServerIP; private int _quickServerPort; public AudioSource audio; private int buffPosAudio = 0; int positionAudio = 0; private byte[] databuff; private MemoryStream _receiveAudioBuffer = new MemoryStream(44100 * 12); private Thread _quickThread; bool isPlay = false; private MemoryStream _audioBuffer = new MemoryStream(4096); public readonly int sampleLength = 44100 * 12; private int closeSign = 0; const int DEF_RECV_BUFFER_SIZE = 409600 * 4; private MemoryStream _receiveVideoBuffer = new MemoryStream(DEF_RECV_BUFFER_SIZE); private string serverType = "tcp"; public void Init(string ip, int port) { _quickServerIP = ip; _quickServerPort = port; positionAudio = 0; closeSign = 0; switch (App.AppManager.Instance.connectType) { case App.ConnectType.None: break; case App.ConnectType.TCPServer: serverType = "tcp"; break; case App.ConnectType.Quic: serverType = "quic"; break; default: break; } Debug.Log("serverType:"+serverType); if (_quickThread == null || !_quickThread.IsAlive) { _quickThread = new Thread(StartQuick); _quickThread.Start(); } NetDistribute.Instance.OnConnect(true, "success"); } void StartQuick() { //AvClientConnect(serverType, _quickServerIP, _quickServerPort, // OnNewConnCallBack, OnConnClosedCallBack, // OnVideoFrameCallBack, OnAudioFrameCallBack, // OnLogCallBack // ); int isW = 0; int wInter = 1000; int retryCount = 0; int retryInter = 1000; AvClientConnect(serverType, _quickServerIP, _quickServerPort, isW, wInter, retryCount, retryInter, OnVideoFrameCallBack, OnAudioFrameCallBack, OnLogCallBack, OnWriteCallBack, OnCloseCallBack); } private int OnCloseCallBack() { //检测应用层是否关闭连接 /** * 返回值说明: * 返回值类型为 c#的Int * 返回 0 表示继续保持当前连接 * 返回 100 表示关闭当前连接 */ return closeSign; } private IntPtr OnWriteCallBack() { //动态库回调应用层写入数据 /** * 返回值说明: * 返回值类型为 c#的IntPtr * 返回值的前4个字节表示(发送字节流的长度) * 返回值从第4个字节之后的所有字节表示发送的内容字节流 * 如果当前无数据,请确保前4个字节表示的值的长度为0 */ //int writeBufLen = 1003;// 发送字节流的长度 //byte[] writeBufLenBytes = BitConverter.GetBytes(writeBufLen);// 内容长度转为4个字节 byte[] writeBuf = new byte[4 + 1003]; //for (int i = 0; i < 4; i++) //{ // writeBuf[i] = writeBufLenBytes[i]; //} //int size = writeBuf.Length; IntPtr p = Marshal.AllocHGlobal(0); Marshal.Copy(writeBuf, 0, p, 0); return p; } private int OnLogCallBack(int state, string msg) { //日志回调 Console.WriteLine("c# OnLogCallBack state=" + state.ToString() + ",msg=" + msg); if (state == 100) { NetDistribute.Instance.OnConnect(true, "success"); } /** * state=99 :开始连接 * state=100 :连接成功 * state=101 :连接已经断开 */ return 0; } private int OnVideoFrameCallBack(IntPtr frame, int frameLen, int timestamp) { Debug.Log("c# OnVideoFrameCallBack frameLen=" + frameLen.ToString() + ",timestamp=" + timestamp.ToString()); Marshal.Copy(frame, this._receiveVideoBuffer.GetBuffer(), 0, frameLen); this.packageHandler.ReceiveQuicData(this._receiveVideoBuffer.GetBuffer(), 0, frameLen); return 0; } private int OnAudioFrameCallBack(IntPtr frame, int frameLen, int timestamp) { byte[] buf = new byte[frameLen]; Marshal.Copy(frame, _audioBuffer.GetBuffer(), 0, frameLen); _receiveAudioBuffer.Write(_audioBuffer.GetBuffer(), 0, frameLen); if (!isPlay) { if (_receiveAudioBuffer.Length > 4096 * 4) { isPlay = true; positionAudio = 0; AudioClip clip = AudioClip.Create("MySinusoid1", sampleLength, 1, 44100, true, OnAudioRead);//307200/2(audioBuffer.GetBuffer().Length - buffPos) / 4 audio.clip = clip; audio.Play(); } } Debug.Log("c# OnAudioFrameCallBack frameLen=" + frameLen.ToString() + ",timestamp=" + timestamp.ToString() + "$$$ " + _receiveAudioBuffer.Length); return 0; } void OnAudioRead(float[] data) { int count = 0; float[] floatValue = null; // Debug.Log("开始 isPlay:" + isPlay + "buffPos:" + buffPos + " position :"+ position + " sampleLength:"+ sampleLength); if (!isPlay) { if (positionAudio < sampleLength) { isPlay = true; } } if (isPlay) { if (buffPosAudio < _receiveAudioBuffer.Length) { // Debug.LogWarning("播放中 buffPos:"+ buffPos + " data.Length: "+ data.Length); floatValue = GetByte2Float(_receiveAudioBuffer.GetBuffer(), buffPosAudio, data.Length * 4); buffPosAudio += data.Length * 4; } } while (count < data.Length) { // data[count] = floatValue[count];//11 data[count] = floatValue == null ? 0 : floatValue[count]*3; count++; } positionAudio += data.Length; if (positionAudio >= sampleLength) { isPlay = false; long size = _receiveAudioBuffer.Position - buffPosAudio; if (buffPosAudio < _receiveAudioBuffer.Position) { Array.Copy(_receiveAudioBuffer.GetBuffer(), this.buffPosAudio, _receiveAudioBuffer.GetBuffer(), 0, _receiveAudioBuffer.Position - this.buffPosAudio); } _receiveAudioBuffer.Position = size; _receiveAudioBuffer.SetLength(size); buffPosAudio = 0; } //Debug.LogWarning("播放结束:"+ position + " ^^^ " + buffPos + " () " + isPlay + " " + sampleLength); } public void Close() { closeSign = 100; } private float[] GetByte2Float(byte[] rawData) { float[] samples = new float[rawData.Length / 4]; float rescaleFactor = 32767; short st = 0; float ft = 0; for (int i = 0; i < rawData.Length; i += 4) { st = BitConverter.ToInt16(rawData, i); ft = st / rescaleFactor; samples[i / 4] = ft; } return samples; } private float[] GetByte2Float(byte[] buffer, int start, int count) { byte[] realBuffer = new byte[count]; Array.Copy(buffer, start, realBuffer, 0, count); return GetByte2Float(realBuffer); } } }