/// <summary> /// 启动摄像头预览 /// </summary> /// <param name="cameraNo">摄像头编号</param> /// <returns></returns> public SmcErr StartReceiveVideo(string cameraNo) { NLogEx.LoggerEx logEx = new NLogEx.LoggerEx(log); logEx.Trace("Enter: IvsVideoMonitor.StartReceiveVideo({0}).", cameraNo); SmcErr err = new CgwError(); int handle; logEx.Trace("Call ivsSdkClient.StartRealPlayCBRaw({0}).", cameraNo); int result = this.ivsSdkClient.StartRealPlayCBRaw(cameraNo, out handle); //如果不为0,表示预览失败 if (result != CgwConst.IVS_SDK_SUCCESS_TAG) { err.SetErrorNo(CgwError.START_RECEIVE_VIDEO_FAILED); logEx.Error("Start Receive camera video data failed.Camera No:{0}.Ivs sdk error code:{1}", cameraNo, result); return(err); } else { logEx.Info("Start Receive camera video data success.Camera No:{0},Handle:{1}.", cameraNo, handle); } //预览成功,需要停止原来的预览,并将预览句柄添加到缓存 //需要停止的预览句柄 int needToStopHandel = CgwConst.START_RECEIVE_VIDEO_DATA_FAILED; if (this.handelOperateLock.TryEnterWriteLock(CgwConst.ENTER_LOCK_WAIT_TIME)) { try { //如果预览句柄已经存在,删除掉原来的句柄,用新的句柄替换 if (this.cameraVideoHandeDic.ContainsKey(cameraNo)) { needToStopHandel = this.cameraVideoHandeDic[cameraNo]; this.videoHandleCameraDic.Remove(needToStopHandel); this.cameraVideoHandeDic.Remove(cameraNo); } this.cameraVideoHandeDic.Add(cameraNo, handle); MediaDataSender mediaDataSender = new MediaDataSender(cameraNo, this.dataCallBack); this.videoHandleCameraDic.Add(handle, mediaDataSender); } finally { this.handelOperateLock.ExitWriteLock(); } } //重新预览后,更新了预览句柄,需要将原来的预览停止,放在handelOperateLock外面,防止长时间占用锁 if (needToStopHandel != CgwConst.START_RECEIVE_VIDEO_DATA_FAILED) { result = this.ivsSdkClient.StopRealPlay(needToStopHandel); //如果不为0,表示停止原来的预览失败,只记录日志,不返回错误,不设置错误码 if (result != CgwConst.IVS_SDK_SUCCESS_TAG) { logEx.Error("Get a new preview success. But stop old preview failed.CameraNo:{0},Ivs sdk error code:{0}", cameraNo, result); return(err); } } return(err); }
/// <summary> /// 启动摄像头预览 /// </summary> /// <param name="cameraNo">摄像头编号</param> /// <returns></returns> public SmcErr StartReceiveVideo(string cameraNo) { NLogEx.LoggerEx logEx = new NLogEx.LoggerEx(log); logEx.Trace("Enter: T28181VideoMonitor.StartReceiveVideo({0}).", cameraNo); SmcErr err = new CgwError(); //打开通道,开始接收实况RTP数据流 UInt32 channel = sipStack.StartRecvStream(cameraNo, domain, sipPort); //如果为0,表示预览失败 if (channel == CgwConst.T28181_ERROR_HANDLE) { err.SetErrorNo(CgwError.START_RECEIVE_VIDEO_FAILED); logEx.Error("Start Receive camera video data failed.Camera No:{0}.Handle:{1}.", cameraNo, channel); return(err); } else { logEx.Info("Start Receive camera video data success.Camera No:{0},Handle:{1}.", cameraNo, channel); } //设置rtp解析回调函数 rtpAdapter.ESDK_RTP_OpenChannel(frameDataCallBack, channel); //预览成功,需要停止原来的预览,并将预览句柄添加到缓存 //需要停止的预览句柄 UInt32 needToStopChannel = CgwConst.T28181_ERROR_HANDLE; if (this.handelOperateLock.TryEnterWriteLock(CgwConst.ENTER_LOCK_WAIT_TIME)) { try { //如果预览句柄已经存在,删除掉原来的句柄,用新的句柄替换 if (this.cameraVideoChannelDic.ContainsKey(cameraNo)) { needToStopChannel = this.cameraVideoChannelDic[cameraNo]; this.videoChannelDataSenderDic.Remove(needToStopChannel); this.cameraVideoChannelDic.Remove(cameraNo); //关闭rtp回调 rtpAdapter.ESDK_RTP_CloseChannel(needToStopChannel); } this.cameraVideoChannelDic.Add(cameraNo, channel); MediaDataSender mediaDataSender = new MediaDataSender(cameraNo, this.dataCallBack); this.videoChannelDataSenderDic.Add(channel, mediaDataSender); } finally { this.handelOperateLock.ExitWriteLock(); } } //重新预览后,更新了预览句柄,需要将原来的预览停止,放在handelOperateLock外面,防止长时间占用锁 if (needToStopChannel != CgwConst.T28181_ERROR_HANDLE) { int result = sipStack.StopRecvStream(needToStopChannel); //如果不为0,表示停止原来的预览失败,只记录日志,不返回错误,不设置错误码 if (result != CgwConst.IVS_SDK_SUCCESS_TAG) { err.SetErrorNo(CgwError.START_RECEIVE_VIDEO_FAILED); logEx.Error("Get a new preview success. But stop old preview failed.CameraNo:{0},Ivs sdk error code:{0}", cameraNo, result); return(err); } } return(err); }
/// <summary> /// 码流回调函数, /// 1、根据预览句柄handle从videoHandleCameraDic中获取摄像头编号, /// 2、根据摄像头编号,获取mic状态 /// 3、判断码流是否是音频,如果是音频,根据mic状态判断是否需要转发 /// </summary> /// <param name="handle">播放句柄</param> /// <param name="pRawFrameInfo">实况参数</param> /// <param name="pBuf">码流</param> /// <param name="uiBufSize">大小</param> /// <param name="cameraNo">摄像头编号</param> private void IvsRealPlayCallBackRaw(int handle, IvsRawFrameInfo pRawFrameInfo, byte[] pBuf, UInt32 uiBufSize, string pUserData) { NLogEx.LoggerEx logEx = new NLogEx.LoggerEx(log); //logEx.Trace("Enter:IvsRealPlayCallBackRaw(),{0}", pUserData); MediaDataSender mediaDataSender = null; try { if (this.handelOperateLock.TryEnterReadLock(CgwConst.ENTER_LOCK_WAIT_TIME)) { try { if (!this.videoHandleCameraDic.ContainsKey(handle)) { logEx.Error("The video data handle is not found.Handle:{0}", handle); return; } mediaDataSender = this.videoHandleCameraDic[handle]; } finally { this.handelOperateLock.ExitReadLock(); } } if (mediaDataSender == null) { return; } StreamType streamType = StreamType.VIDEO_H264; //判断码流类型,因为码流回调访问频频非常高,不单独抽成一个方法,减少方法访问开销 //对于支持的码流类型,用break退出switch,对于不支持的码流类型直接舍弃,用return返回 switch (pRawFrameInfo.StreamType) { //对于音频只接收G711A和G711U,其他舍弃 case (int)IvsStreamType.PAY_LOAD_TYPE_PCMU: streamType = StreamType.AUDIO_G711U; break; case (int)IvsStreamType.PAY_LOAD_TYPE_PCMA: streamType = StreamType.AUDIO_G711A; break; //只接收H264的视频码流 case (int)IvsStreamType.PAY_LOAD_TYPE_H264: //H264的标准视频流,作为视频流处理 streamType = StreamType.VIDEO_H264; break; default: //不支持的类型,直接舍弃,返回 //logEx.Warn("This stream type is not support. Chuck the data.StreamType:{0};Camera no:{1}", // Enum.GetName(typeof(IvsStreamType), pRawFrameInfo.StreamType), // cameraNo); return; } if (streamType == StreamType.AUDIO_G711A || streamType == StreamType.AUDIO_G711U) { //如果是音频流,需要判断mic的状态,开启时才发送音频流 if (this.micOperateLock.TryEnterReadLock(CgwConst.ENTER_LOCK_WAIT_TIME)) { try { if (this.cameraMicStatusDic.ContainsKey(mediaDataSender.CameraNo)) { //如果mic为非开启状态,则不发送音频码流, if (!this.cameraMicStatusDic[mediaDataSender.CameraNo]) { //logEx.Warn("This data is audio,but the mic is off.Chuck the data.Camera no:{0}", mediaDataSender.CameraNo); return; } } else { //默认为关闭状态,因此如果cameraMicStatusDic不包含该摄像头,则认为处于关闭状态,舍弃音频码流 //logEx.Warn("This data is audio,but the mic is off.Chuck the data.Camera no:{0}", mediaDataSender.CameraNo); return; } } finally { this.micOperateLock.ExitReadLock(); } } } MediaData mediaData = new MediaData(); mediaData.Data = pBuf; //IVS 目前为裸码流 mediaData.DataType = MediaDataType.FRAME_DATA; mediaData.StreamType = streamType; mediaData.Size = uiBufSize; //将Ivs帧类型转换成各融合网关统一的帧类型 string name = Enum.GetName(typeof(IvsH264NaluType), pRawFrameInfo.FrameType); //帧类型判断,偶现IVS返回的帧类型,在融合网关中未记录,返回值为null if (string.IsNullOrEmpty(name)) { logEx.Warn("IvsRealPlayCallBackRaw FrameType Is Not Defined ,FrameType:{0}", pRawFrameInfo.FrameType); mediaData.FrameType = FrameDataType.H264_NALU_TYPE_UNDEFINED; } else { if (Enum.IsDefined(typeof(FrameDataType), name)) { FrameDataType frameDataType = (FrameDataType)Enum.Parse(typeof(FrameDataType), name); mediaData.FrameType = frameDataType; } else { mediaData.FrameType = FrameDataType.H264_NALU_TYPE_UNDEFINED; } } //向回调函数转发码流 //this.videoDataCallBack(cameraNo, mediaData, this.sender); //logEx.Debug("FrameDataCallBackFun.mediaData.DataType={0},FrameType = {1},StreamType = {2},Size = {3}", Enum.GetName(typeof(MediaDataType), mediaData.DataType), // Enum.GetName(typeof(FrameDataType), mediaData.FrameType), Enum.GetName(typeof(StreamType), mediaData.StreamType), mediaData.Size); mediaDataSender.SendData(mediaData, this.sender); } catch (Exception e) { logEx.Error("Send the media data failed.Execption message:{0}", e.ToString()); } }
/// <summary> /// rtp码流回调处理 /// </summary> /// <param name="pBuf">帧数据字节数组</param> /// <param name="pFrameData">帧数据类型</param> /// <param name="uiChannel">通道</param> /// <param name="uiBufSize">帧数据字节数组长度</param> private void FrameDataCallBackFun(IntPtr pBuf, uint uiBufSize, ref ST_FRAME_DATA pFrameData, uint uiChannel) { NLogEx.LoggerEx logEx = new NLogEx.LoggerEx(log); ST_FRAME_DATA frameData = pFrameData; MediaDataSender mediaDataSender = null; if (this.handelOperateLock.TryEnterReadLock(CgwConst.ENTER_LOCK_WAIT_TIME)) { try { if (this.videoChannelDataSenderDic.ContainsKey(uiChannel)) { mediaDataSender = this.videoChannelDataSenderDic[uiChannel]; } } finally { this.handelOperateLock.ExitReadLock(); } } if (mediaDataSender == null) { logEx.Warn("FrameDataCallBackFun mediaDataSender = NULL"); return; } StreamType streamType = StreamType.VIDEO_H264; //对于支持的码流类型,用break退出switch,对于不支持的码流类型直接舍弃,用return返回 switch (frameData.iStreamType) { //对于音频只接收G711A和G711U,其他舍弃 case (int)IvsStreamType.PAY_LOAD_TYPE_PCMU: streamType = StreamType.AUDIO_G711U; break; case (int)IvsStreamType.PAY_LOAD_TYPE_PCMA: streamType = StreamType.AUDIO_G711A; break; //只接收H264的视频码流 case (int)IvsStreamType.PAY_LOAD_TYPE_H264: //H264的标准视频流,作为视频流处理 streamType = StreamType.VIDEO_H264; break; default: //不支持的类型,直接舍弃,返回 return; } if (streamType == StreamType.AUDIO_G711A || streamType == StreamType.AUDIO_G711U) { //如果是音频流,需要判断mic的状态,开启时才发送音频流 if (this.micOperateLock.TryEnterReadLock(CgwConst.ENTER_LOCK_WAIT_TIME)) { try { if (this.cameraMicStatusDic.ContainsKey(mediaDataSender.CameraNo)) { //如果mic为非开启状态,则不发送音频码流, if (!this.cameraMicStatusDic[mediaDataSender.CameraNo]) { logEx.Warn("This data is audio,but the mic is off.Chuck the data.Camera no:{0}", mediaDataSender.CameraNo); return; } } else { //默认为关闭状态,因此如果cameraMicStatusDic不包含该摄像头,则认为处于关闭状态,舍弃音频码流 logEx.Warn("This data is audio,but the mic is off.Chuck the data.Camera no:{0}", mediaDataSender.CameraNo); return; } } finally { this.micOperateLock.ExitReadLock(); } } } try { MediaData mediaData = new MediaData(); //获取非托管的数据 byte[] datagram = new byte[uiBufSize]; if (!(streamType == StreamType.AUDIO_G711A || streamType == StreamType.AUDIO_G711U)) { Marshal.Copy(pBuf, datagram, 0, (int)uiBufSize); //视频帧数据头部增加四个四节的开始表实0x000001 byte[] newDatagram = new byte[uiBufSize + 4]; datagram.CopyTo(newDatagram, 4); newDatagram[3] = 1; mediaData.Data = newDatagram; mediaData.Size = (uint)(uiBufSize + 4); } else { mediaData.Data = datagram; mediaData.Size = (uiBufSize); } //裸码流 mediaData.DataType = MediaDataType.FRAME_DATA; mediaData.StreamType = streamType; //将帧类型转换成各融合网关统一的帧类型 string name = Enum.GetName(typeof(IvsH264NaluType), frameData.iFrameDataType); if (Enum.IsDefined(typeof(FrameDataType), name)) { FrameDataType frameDataType = (FrameDataType)Enum.Parse(typeof(FrameDataType), name); mediaData.FrameType = frameDataType; } else { mediaData.FrameType = FrameDataType.H264_NALU_TYPE_UNDEFINED; } //向回调函数转发码流 mediaDataSender.SendData(mediaData, this.sender); } catch (System.Exception ex) { logEx.Error("FrameDataCallBackFun failed.Execption message:{0}", ex.Message); } }