C# 播放H264裸碼流


要播放H264裸碼流,可以分拆為以下三個工作:

1.解碼H264裸碼流獲取YUV數據

2.將YUV數據轉換為RGB數據填充圖片

3.將獲取的圖片進行顯示

要完成工作1,我們可以直接使用海思的解碼庫,由於海思的解碼庫是C++的動態庫,要完成在C#中進行調用可以參考海思h264解碼庫這篇文章,介紹的很詳細。但是對於該博文只介紹了一種幀解碼的方法,並沒有介紹真正實用的流式解碼方法。自己根據解碼庫的參考文檔寫了一份C#版的流式解碼算法。

             //初始化
            // 這是解碼器輸出圖像信息
            hiH264_DEC_FRAME_S _decodeFrame = new hiH264_DEC_FRAME_S();
            // 這是解碼器屬性信息
            hiH264_DEC_ATTR_S decAttr = new hiH264_DEC_ATTR_S();
            decAttr.uPictureFormat = 0;
            decAttr.uStreamInType = 0;
            /* 解碼器最大圖像寬高, D1圖像(1280x720) */
            decAttr.uPicWidthInMB = (uint)width / 16;
            decAttr.uPicHeightInMB = (uint)height / 16;
            /* 解碼器最大參考幀數: 16 */
            decAttr.uBufNum = 16;
            /* bit0 = 1: 標准輸出模式; bit0 = 0: 快速輸出模式 */
            /* bit4 = 1: 啟動內部Deinterlace; bit4 = 0: 不啟動內部Deinterlace */
            decAttr.uWorkMode = 0x10;
            //創建、初始化解碼器句柄
            IntPtr _decHandle = H264Dec.Hi264DecCreate(ref decAttr);
            //解碼結束
            bool isEnd = false;
            int bufferLen = 0x8000;
            //碼流段
            byte[] buf = new byte[bufferLen];
            while (!isEnd)
            {
                //獲取一段碼流,積累一定緩存量再解
                if (streamBuf.Count >= bufferLen || isStop == 1)
                {
                    byte tempByte;
                    int j = 0;
                    for (int i = 0; i < bufferLen; i++)
                    {
                        if (streamBuf.TryDequeue(out tempByte))
                            buf[j++] = tempByte;
                        else
                        {
                            break;
                        }
                    }
                    IntPtr pData = Marshal.AllocHGlobal(j);
                    Marshal.Copy(buf, 0, pData, j);
                    int result = 0;
                    result = H264Dec.Hi264DecFrame(_decHandle, pData, (UInt32)j, 0, ref _decodeFrame, (uint)isStop);
                    while (HI_H264DEC_NEED_MORE_BITS != result)
                    {
                        if (HI_H264DEC_NO_PICTURE == result)
                        {
                            isEnd = true;
                            break;
                        }
                        if (HI_H264DEC_OK == result)/* 輸出一幀圖像 */
                        {
                            //獲取yuv
                            UInt32 tempWid = _decodeFrame.uWidth;
                            UInt32 tempHeig = _decodeFrame.uHeight;
                            UInt32 yStride = _decodeFrame.uYStride;
                            UInt32 uvStride = _decodeFrame.uUVStride;
                            byte[] y = new byte[tempHeig * yStride];
                            byte[] u = new byte[tempHeig * uvStride / 2];
                            byte[] v = new byte[tempHeig * uvStride / 2];
                            Marshal.Copy(_decodeFrame.pY, y, 0, y.Length);
                            Marshal.Copy(_decodeFrame.pU, u, 0, u.Length);
                            Marshal.Copy(_decodeFrame.pV, v, 0, v.Length);

                            //轉為yv12格式
                            //byte[] yuvBytes = new byte[y.Length + u.Length + v.Length];
                            //Array.Copy(y, 0, yuvBytes, 0, y.Length);
                            //Array.Copy(v, 0, yuvBytes, y.Length , v.Length);
                            //Array.Copy(u, 0, yuvBytes, y.Length + v.Length, u.Length);
                            //更新顯示
                            this.d3dSource.Render(_decodeFrame.pY, _decodeFrame.pU, _decodeFrame.pV);
                        }
                        /* 繼續解碼剩余H.264碼流 */
                        result = H264Dec.Hi264DecFrame(_decHandle, IntPtr.Zero, 0, 0, ref _decodeFrame, (uint)isStop);
                    }
                }
                System.Threading.Thread.Sleep(5);
            }
            /* 銷毀解碼器 */
            H264Dec.Hi264DecDestroy(_decHandle);            

要完成工作2,有多種方式,一是自己實現轉換,二是使用ffmpeg的庫進行yuv和rgb的轉換,三是使用D3D進行轉換,效率最高的是第三種方式,因為它是利用顯卡來進行轉換的,更詳細的內容可以參考WPF下YUV播放的D3D解決方案

要完成工作3就非常簡單了,弄個ImageView進行顯示就好了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM