C#:使用海康SDK解碼回撥函式DecCallbackFUN()
阿新 • • 發佈:2018-11-29
簡述
使用海康攝像頭採集影象時,需要在影象上新增圖示、文字等額外資料。可選擇使用海康SDK繪圖回撥函式疊加字元、影象等(請參考上一篇文章);也可使用海康SDK的解碼回撥函式,對視訊流資料進行解碼後處理。該方法流程為:呼叫視訊預覽函式NET_DVR_RealPlay_V40()時將第三個引數設定為實時資料回撥函式RealDataCallBack()的函式指標,然後在RealDataCallBack()回撥函式中註冊視訊流資料解碼函式DecCallbackFUN(),最後在DecCallbackFUN()函式中對資料解析解碼、疊加字元等處理。
本文只調用解碼回撥函式將YV12格式視訊流資料轉換為RGB32格式資料,然後將RGB32格式資料轉換為Image,並使用PictrueBox顯示該Image資料,暫不做疊加字元、影象等處理。
程式碼
1、攝像頭操作程式碼
struct CameraInfo { public string strIP; public short nPort; public string strUserName; public string strPassword; } class IDeviceCamera { public Image m_img = null; public virtual bool InitCamera( CameraInfo stInfo ) { return true; } } class DeviceCamera : IDeviceCamera { private CameraInfo m_stCameraInfo; private bool m_bInitSDK = false; private Int32 m_lUserID = -1; private Int32 m_lRealHandle = -1; private Int32 m_lPort = -1; CHCNetSDK.REALDATACALLBACK RealData = null; //必須得定義為成員變數 public override bool InitCamera( CameraInfo stInfo ) { m_stCameraInfo = stInfo; m_bInitSDK = CHCNetSDK.NET_DVR_Init(); if ( !m_bInitSDK ) { uint nError = CHCNetSDK.NET_DVR_GetLastError(); } CHCNetSDK.NET_DVR_SetConnectTime( 5000, 1 ); CHCNetSDK.NET_DVR_SetReconnect( 10000, 1 ); if ( m_bInitSDK == false ) { MessageBox.Show( "NET_DVR_Init error!" ); return false; } else { //儲存SDK日誌 To save the SDK log CHCNetSDK.NET_DVR_SetLogToFile( 3, "C:\\SdkLog\\", true ); } string DVRIPAddress = stInfo.strIP; //裝置IP地址或者域名 Device IP Int16 DVRPortNumber = stInfo.nPort; //裝置服務埠號 Device Port string DVRUserName = stInfo.strUserName;//裝置登入使用者名稱 User name to login string DVRPassword = stInfo.strPassword;//裝置登入密碼 Password to login CHCNetSDK.NET_DVR_DEVICEINFO_V30 DeviceInfo = new CHCNetSDK.NET_DVR_DEVICEINFO_V30(); m_lUserID = CHCNetSDK.NET_DVR_Login_V30( DVRIPAddress, DVRPortNumber, DVRUserName, DVRPassword, ref DeviceInfo ); if ( m_lUserID < 0 ) { MessageBox.Show( "登入失敗!" ); CHCNetSDK.NET_DVR_Cleanup(); return false; } // CHCNetSDK.NET_DVR_PREVIEWINFO lpPreviewInfo = new CHCNetSDK.NET_DVR_PREVIEWINFO(); lpPreviewInfo.hPlayWnd = (IntPtr)null; lpPreviewInfo.lChannel = 1; lpPreviewInfo.dwStreamType = 0; //碼流型別:0-主碼流,1-子碼流,2-碼流3,3-碼流4,以此類推 lpPreviewInfo.dwLinkMode = 0; //連線方式:0- TCP方式,1- UDP方式,2- 多播方式,3- RTP方式,4-RTP/RTSP,5-RSTP/HTTP lpPreviewInfo.bBlocked = true; //0- 非阻塞取流,1- 阻塞取流 lpPreviewInfo.dwDisplayBufNum = 15; //播放庫播放緩衝區最大緩衝幀數 //使用回撥函式取攝像頭資料 RealData = new CHCNetSDK.REALDATACALLBACK( RealDataCallBack );//預覽實時流回調函式 IntPtr pUser = new IntPtr();//使用者資料 m_lRealHandle = CHCNetSDK.NET_DVR_RealPlay_V40( m_lUserID, ref lpPreviewInfo, RealData, pUser ); CHCNetSDK.NET_DVR_RigisterDrawFun( m_lRealHandle, new CHCNetSDK.DRAWFUN( cbDrawFun ), 0 );//回撥函式:繪製圖標 return true; } private uint nLastErr = 0; private static PlayCtrl.DECCBFUN m_fDisplayFun = null; private IntPtr m_ptrRealHandle; public void RealDataCallBack( Int32 lRealHandle, UInt32 dwDataType, IntPtr pBuffer, UInt32 dwBufSize, IntPtr pUser ) { //下面資料處理建議使用委託的方式 switch ( dwDataType ) { case CHCNetSDK.NET_DVR_SYSHEAD: // sys head if ( dwBufSize > 0 ) { if ( m_lPort >= 0 ) return; //同一路碼流不需要多次呼叫開流介面 //獲取播放控制代碼 Get the port to play if ( !PlayCtrl.PlayM4_GetPort( ref m_lPort ) ) { nLastErr = PlayCtrl.PlayM4_GetLastError( m_lPort ); break; } //設定流播放模式 Set the stream mode: real-time stream mode if ( !PlayCtrl.PlayM4_SetStreamOpenMode( m_lPort, PlayCtrl.STREAME_REALTIME ) ) { nLastErr = PlayCtrl.PlayM4_GetLastError( m_lPort ); //str = "Set STREAME_REALTIME mode failed, error code= " + nLastErr; //this.BeginInvoke( AlarmInfo, str ); } //開啟碼流,送入頭資料 Open stream if ( !PlayCtrl.PlayM4_OpenStream( m_lPort, pBuffer, dwBufSize, 2 * 1024 * 1024 ) ) { nLastErr = PlayCtrl.PlayM4_GetLastError( m_lPort ); //str = "PlayM4_OpenStream failed, error code= " + nLastErr; //this.BeginInvoke( AlarmInfo, str ); break; } //設定顯示緩衝區個數 Set the display buffer number if ( !PlayCtrl.PlayM4_SetDisplayBuf( m_lPort, 15 ) ) { nLastErr = PlayCtrl.PlayM4_GetLastError( m_lPort ); //str = "PlayM4_SetDisplayBuf failed, error code= " + nLastErr; //this.BeginInvoke( AlarmInfo, str ); } //設定顯示模式 Set the display mode //if ( !PlayCtrl.PlayM4_SetOverlayMode( m_lPort, 0, 0) ) //play off screen //{ // nLastErr = PlayCtrl.PlayM4_GetLastError( m_lPort ); // //str = "PlayM4_SetOverlayMode failed, error code= " + nLastErr; // //this.BeginInvoke( AlarmInfo, str ); //} //設定解碼回撥函式,獲取解碼後音視訊原始資料 Set callback function of decoded data m_fDisplayFun = new PlayCtrl.DECCBFUN( DecCallbackFUN ); if ( !PlayCtrl.PlayM4_SetDecCallBackEx( m_lPort, m_fDisplayFun, IntPtr.Zero, 0 ) ) { //this.BeginInvoke( AlarmInfo, "PlayM4_SetDisplayCallBack fail" ); } //開始解碼 Start to play if ( !PlayCtrl.PlayM4_Play( m_lPort, m_ptrRealHandle ) ) { nLastErr = PlayCtrl.PlayM4_GetLastError( m_lPort ); //str = "PlayM4_Play failed, error code= " + nLastErr; //this.BeginInvoke( AlarmInfo, str ); break; } } break; case CHCNetSDK.NET_DVR_STREAMDATA: // video stream data default: //the other data if ( dwBufSize > 0 && m_lPort != -1 ) { for ( int i = 0; i < 999; i++ ) { //送入碼流資料進行解碼 Input the stream data to decode if ( !PlayCtrl.PlayM4_InputData( m_lPort, pBuffer, dwBufSize ) ) { nLastErr = PlayCtrl.PlayM4_GetLastError( m_lPort ); //str = "PlayM4_InputData failed, error code= " + nLastErr; Thread.Sleep( 2 ); } else break; } } break; } } //解碼回撥函式 private void DecCallbackFUN( int nPort, IntPtr pBuf, int nSize, ref PlayCtrl.FRAME_INFO pFrameInfo, int nReserved1, int nReserved2 ) { if ( pFrameInfo.nType == 3 ) //#define T_YV12 3 { byte[] byteBuffYV12 = new byte[ nSize ]; Marshal.Copy( pBuf, byteBuffYV12, 0, nSize ); long lRGBSize = (long)pFrameInfo.nWidth * pFrameInfo.nHeight * 4; byte[] bufferRGB32 = new byte[ lRGBSize ]; CommonFun.YV12_to_RGB32( byteBuffYV12, bufferRGB32, pFrameInfo.nWidth, pFrameInfo.nHeight ); byteBuffYV12 = null; Bitmap bmpFromGRB32 = CommonFun.RGB32_to_Image( bufferRGB32, pFrameInfo.nWidth, pFrameInfo.nHeight ); bufferRGB32 = null; if ( null == bmpFromGRB32 ) return; m_img = bmpFromGRB32; } } }
注:得到的m_img用於PictureBox進行顯示。
2、YV12轉換為RGB32、RGB32轉換為Image程式碼
class CommonFun { public static bool YV12_to_RGB32( byte[] buffYV12, byte[] bufferRGB32, int nWidth, int nHeight ) { if( buffYV12.Length < 0 || bufferRGB32.Length < 0 ) return false; long nYLen = (long)nWidth * nHeight; int nHfWidth = ( nWidth >> 1 ); if ( nYLen < 1 || nHfWidth < 1 ) return false; byte[] byteYData = buffYV12.Skip( 0 ).Take( nWidth*nHeight ).ToArray(); byte[] byteUData = buffYV12.Skip( nWidth*nHeight ).Take( (nHeight/2)*(nWidth/2) ).ToArray(); byte[] byteVData = buffYV12.Skip( nWidth*nHeight + (nHeight/2)*(nWidth/2) ).Take( (nHeight/2)*(nWidth/2) ).ToArray(); if ( byteYData.Length < 0 || byteVData.Length < 0 || byteUData.Length < 0 ) return false; int[] nRgb = new int[4]; for( int nRow = 0; nRow < nHeight; nRow++ ) { for( int nCol = 0; nCol < nWidth; nCol++ ) { int Y = byteYData[nRow*nWidth + nCol]; int U = byteUData[(nRow / 2)*(nWidth / 2) + (nCol / 2)]; int V = byteVData[(nRow / 2)*(nWidth / 2) + (nCol / 2)]; int R = Y + (U - 128) + (((U - 128) * 103) >> 8); int G = Y - (((V - 128) * 88) >> 8) - (((U - 128) * 183) >> 8); int B = Y + (V - 128) + (((V - 128) * 198) >> 8); // r分量值 R = R<0 ? 0 : R; nRgb[2] = R > 255 ? 255 : R; // g分量值 G = G<0 ? 0 : G; nRgb[1] = G > 255 ? 255 : G; // b分量值 B = B<0 ? 0 : B; nRgb[0] = B > 255 ? 255 : B; //A分量值 nRgb[ 3 ] = 255; //Out RGB Buffer bufferRGB32[4 * (nRow*nWidth + nCol) + 0] = (byte)nRgb[0]; bufferRGB32[4 * (nRow*nWidth + nCol) + 1] = (byte)nRgb[1]; bufferRGB32[4 * (nRow*nWidth + nCol) + 2] = (byte)nRgb[2]; bufferRGB32[4 * (nRow*nWidth + nCol) + 3] = (byte)nRgb[3]; } } return true; } public static Bitmap RGB32_to_Image( byte[] byteBuff, int nWidth, int nHeight ) { if ( byteBuff.Length <= 0 || byteBuff.Length < nWidth*nHeight ) return null; Bitmap bmp = new Bitmap( nWidth, nHeight, PixelFormat.Format32bppArgb ); //鎖定記憶體資料 BitmapData bmpData = bmp.LockBits( new Rectangle( 0, 0, nWidth, nHeight ), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb ); //輸入顏色資料 System.Runtime.InteropServices.Marshal.Copy( byteBuff, 0, bmpData.Scan0, byteBuff.Length ); //解鎖 bmp.UnlockBits( bmpData ); return bmp; } }