【Android RTMP】RTMPDump 封裝 RTMPPacket 數據包 ( 封裝 SPS / PPS 數據包 )



Android 直播推流流程 : 手機採集視頻 / 音頻數據 , 視頻數據使用 H.264 編碼 , 音頻數據使用 AAC 編碼 , 最後將音視頻數據都打包到 RTMP 數據包中 , 使用 RTMP 協議上傳到 RTMP 服務器中 ;


Android 端中主要完成手機端採集視頻數據操作 , 並將視頻數據傳遞給 JNI , 在 NDK 中使用 x264 將圖像轉爲 H.264 格式的視頻 , 最後將 H.264 格式的視頻打包到 RTMP 數據包中 , 上傳到 RTMP 服務器中 ;


本篇博客中介紹如下內容 , Java 層將 Camera 採集的 NV21 格式的數據傳入 JNI 層 , 在 JNI 中使用 x264 編碼器將 NV21 圖像數據編碼爲 H.264 視頻數據 ;


本篇博客中主要封裝 AVC 序列頭數據 , 將 幀類型 , AVC 數據類型 , 合成時間 , 版本信息 , 編碼規格 , NALU 長度 , SPS 個數 , SPS 長度 , SPS 數據 , PPS 個數 , PPS 長度 , PPS 數據 , 封裝到 RTMP 包中 ;





一、 基本封裝數據格式說明



1 . 這是完整的視頻標籤數據內容 : 這是 FLV 中完整視頻標籤數據 ;

0x00000182	:   09 00 00 2E 00 00 00 00 
0x0000018a	:   00 00 00 17 00 00 00 00 
0x00000192	:   01 64 00 32 FF E1 00 19 
0x0000019a	:   67 64 00 32 AC D9 80 78 
0x000001a2	:   02 27 E5 84 00 00 03 00 
0x000001aa	:   04 00 00 1F 40 3C 60 C6 
0x000001b2	:   68 01 00 05 68 E9 7B 2C 
0x000001ba	:   8B 00 00 00 39

2 . 標籤頭 : 1111 個字節是標籤頭數據 , 存儲有 標籤類型 , 標籤數據大小 , 時間戳 , 時間戳擴展位 , 流編號 等 1111 字節信息 ;

0x00000182	:   09 00 00 2E 00 00 00 00 
0x0000018a	:   00 00 00 

3 . 標籤數據 ( 重點 ) : 這就是本篇博客要封裝的內容 , 基本上是封裝一個格式一模一樣的 RTMP 數據包 ,

                         17 00 00 00 00 
0x00000192	:   01 64 00 32 FF E1 00 19 
0x0000019a	:   67 64 00 32 AC D9 80 78 
0x000001a2	:   02 27 E5 84 00 00 03 00 
0x000001aa	:   04 00 00 1F 40 3C 60 C6 
0x000001b2	:   68 01 00 05 68 E9 7B 2C 
0x000001ba	:   8B 00 00 00 39


參考博客 : 參考之前的兩篇分析 RTMP 數據格式的博客 , 分析了與 RTMP 格式幾乎一致的 FLV 視頻數據格式 ;


這兩篇博客一定要 , 並且明白 FLV 視頻標籤數據格式 , 才能看懂今天寫的 RTMP 數據包封裝的內容 ;





二、 封裝 SPS PPS 數據總體說明



1 . 數據示例 :

                         17 00 00 00 00
0x00000192	:   01 64 00 32 FF E1 00 19
0x0000019a	:   67 64 00 32 AC D9 80 78
0x000001a2	:   02 27 E5 84 00 00 03 00
0x000001aa	:   04 00 00 1F 40 3C 60 C6
0x000001b2	:   68 01 00 05 68 E9 7B 2C
0x000001ba	:   8B 00 00 00 39
  • 17 幀類型, 1 字節
  • 00 數據類型, 1 字節
  • 00 00 00 合成時間, 3 字節
  • 01 版本信息, 1 字節
  • 64 00 32 編碼規則, 3 字節
  • FF NALU 長度, 1 字節
  • E1 SPS 個數, 1 字節
  • 00 19 SPS 長度, 2 字節

截止到當前位置有 13 字節數據

  • spsLen 字節數據, 這裏是 25 字節
                67 64 00 32 AC D9 80 78
0x000001a2	:   02 27 E5 84 00 00 03 00
0x000001aa	:   04 00 00 1F 40 3C 60 C6
0x000001b2	:   68
  • 01 PPS 個數, 1 字節
  • 00 05 PPS 長度, 2 字節
  • ppsLen 字節的 PPS 數據
                            68 E9 7B 2C
0x000001ba	:   8B
  • 後面的 00 00 00 39 是視頻標籤的總長度 , 這裏在 RTMP 標籤中可以不用封裝 ;


2 . 計算整個 SPS 和 PPS 數據的大小 :


① 封裝頭 : 幀類型 , 數據類型 , 合成時間 , 版本信息 , 編碼規則 , NALU 長度 , 總共有 1010 字節 ;

② 封裝 SPS 數據 : SPS 個數 , SPS 長度 , SPS 數據 , 分別有 1+2+spsLen1 + 2 + spsLen 字節 ;

③ 封裝 PPS 數據 : PPS 個數 , PPS 長度 , PPS 數據 , 分別有 1+2+ppsLen1 + 2 + ppsLen 字節 ;

int rtmpPackagesize = 10 + 3 + spsLen + 3 + ppsLen;




三、 封裝頭數據



向 RTMP 數據包中 , 封裝 幀類型 , 數據類型 , 合成時間 , 版本信息 , 編碼規則 , NALU 長度 , 總共有 1010 字節 ;

    // 幀類型數據 : 分爲兩部分;
    // 前 4 位表示幀類型, 1 表示關鍵幀, 2 表示普通幀
    // 後 4 位表示編碼類型, 7 表示 AVC 視頻編碼
    rtmpPacket->m_body[nextPosition++] = 0x17;

    // 數據類型, 00 表示 AVC 序列頭
    rtmpPacket->m_body[nextPosition++] = 0x00;

    // 合成時間, 一般設置 00 00 00
    rtmpPacket->m_body[nextPosition++] = 0x00;
    rtmpPacket->m_body[nextPosition++] = 0x00;
    rtmpPacket->m_body[nextPosition++] = 0x00;

    // 版本信息
    rtmpPacket->m_body[nextPosition++] = 0x01;

    // 編碼規格
    rtmpPacket->m_body[nextPosition++] = sps[1];
    rtmpPacket->m_body[nextPosition++] = sps[2];
    rtmpPacket->m_body[nextPosition++] = sps[3];

    // NALU 長度
    rtmpPacket->m_body[nextPosition++] = 0xFF;




四、 封裝 SPS 數據



將 SPS 數據封裝到 RTMP 數據包中 , 包含 SPS 個數 , SPS 長度 , SPS 數據 ;

    // SPS 個數
    rtmpPacket->m_body[nextPosition++] = 0xE1;

    // SPS 長度, 佔 2 字節
    // 設置長度的高位
    rtmpPacket->m_body[nextPosition++] = (spsLen >> 8) & 0xFF;
    // 設置長度的低位
    rtmpPacket->m_body[nextPosition++] = spsLen & 0xFF;

    // 拷貝 SPS 數據
    // 將 SPS 數據拷貝到 rtmpPacket->m_body[nextPosition] 地址中
    memcpy(&rtmpPacket->m_body[nextPosition], sps, spsLen);
    // 累加 SPS 長度信息
    nextPosition += spsLen;





五、 封裝 PPS 數據



將 PPS 數據封裝到 RTMP 數據包中 , 包含 PPS 個數 , PPS 長度 , PPS 數據 ;

    // PPS 個數
    rtmpPacket->m_body[nextPosition++] = 0x01;

    // PPS 數據的長度, 佔 2 字節
    // 設置長度的高位
    rtmpPacket->m_body[nextPosition++] = (ppsLen >> 8) & 0xFF;
    // 設置長度的低位
    rtmpPacket->m_body[nextPosition++] = (ppsLen) & 0xFF;
    // 拷貝 SPS 數據
    memcpy(&rtmpPacket->m_body[nextPosition], pps, ppsLen);




六、 設置 RTMP 數據包其它參數



設置 RTMP 包類型 , RTMP 包長度 , RTMP 通道 , 時間戳 等信息 ;

    // 設置 RTMP 包類型, 視頻類型數據
    rtmpPacket->m_packetType = RTMP_PACKET_TYPE_VIDEO;
    // 設置 RTMP 包長度
    rtmpPacket->m_nBodySize = rtmpPackagesize;
    // 分配 RTMP 通道, 隨意分配
    rtmpPacket->m_nChannel = 10;
    // 設置視頻時間戳, 如果是 SPP PPS 數據, 沒有時間戳
    rtmpPacket->m_nTimeStamp = 0;
    // 設置絕對時間, 對於 SPS PPS 賦值 0 即可
    rtmpPacket->m_hasAbsTimestamp = 0;
    // 設置頭類型, 隨意設置一個
    rtmpPacket->m_headerType = RTMP_PACKET_SIZE_MEDIUM;




七、 SPS PPS 數據封裝代碼示例



/**
 * 將 SPS / PPS 數據發送到 RTMP 服務器端
 * @param sps       SPS 數據
 * @param pps       PPS 數據
 * @param spsLen    SPS 長度
 * @param ppsLen    PPS 長度
 */
void VedioChannel::sendSpsPpsToRtmpServer(uint8_t *sps, uint8_t *pps, int spsLen, int ppsLen) {
    // 創建 RTMP 數據包, 將數據都存入該 RTMP 數據包中
    RTMPPacket *rtmpPacket = new RTMPPacket;

    /*
        計算整個 SPS 和 PPS 數據的大小
        數據示例 :
                                 17 00 00 00 00
        0x00000192	:   01 64 00 32 FF E1 00 19
        0x0000019a	:   67 64 00 32 AC D9 80 78
        0x000001a2	:   02 27 E5 84 00 00 03 00
        0x000001aa	:   04 00 00 1F 40 3C 60 C6
        0x000001b2	:   68 01 00 05 68 E9 7B 2C
        0x000001ba	:   8B 00 00 00 39

        17 幀類型, 1 字節
        00 數據類型, 1 字節
        00 00 00 合成時間, 3 字節
        01 版本信息, 1 字節
        64 00 32 編碼規則, 3 字節
        FF NALU 長度, 1 字節
        E1 SPS 個數, 1 字節
        00 19 SPS 長度, 2 字節

        截止到當前位置有 13 字節數據

        spsLen 字節數據, 這裏是 25 字節

                        67 64 00 32 AC D9 80 78
        0x000001a2	:   02 27 E5 84 00 00 03 00
        0x000001aa	:   04 00 00 1F 40 3C 60 C6
        0x000001b2	:   68

        01 PPS 個數, 1 字節
        00 05 PPS 長度, 2 字節

        ppsLen 字節的 PPS 數據
                                    68 E9 7B 2C
        0x000001ba	:   8B

        後面的 00 00 00 39 是視頻標籤的總長度
        這裏再 RTMP 標籤中可以不用封裝
     */
    int rtmpPackagesize = 10 + 3 + spsLen + 3 + ppsLen;

    // 爲 RTMP 數據包分配內存
    RTMPPacket_Alloc(rtmpPacket, rtmpPackagesize);

    // 記錄下一個要寫入數據的索引位置
    int nextPosition = 0;

    // 幀類型數據 : 分爲兩部分;
    // 前 4 位表示幀類型, 1 表示關鍵幀, 2 表示普通幀
    // 後 4 位表示編碼類型, 7 表示 AVC 視頻編碼
    rtmpPacket->m_body[nextPosition++] = 0x17;

    // 數據類型, 00 表示 AVC 序列頭
    rtmpPacket->m_body[nextPosition++] = 0x00;

    // 合成時間, 一般設置 00 00 00
    rtmpPacket->m_body[nextPosition++] = 0x00;
    rtmpPacket->m_body[nextPosition++] = 0x00;
    rtmpPacket->m_body[nextPosition++] = 0x00;

    // 版本信息
    rtmpPacket->m_body[nextPosition++] = 0x01;

    // 編碼規格
    rtmpPacket->m_body[nextPosition++] = sps[1];
    rtmpPacket->m_body[nextPosition++] = sps[2];
    rtmpPacket->m_body[nextPosition++] = sps[3];

    // NALU 長度
    rtmpPacket->m_body[nextPosition++] = 0xFF;

    // SPS 個數
    rtmpPacket->m_body[nextPosition++] = 0xE1;

    // SPS 長度, 佔 2 字節
    // 設置長度的高位
    rtmpPacket->m_body[nextPosition++] = (spsLen >> 8) & 0xFF;
    // 設置長度的低位
    rtmpPacket->m_body[nextPosition++] = spsLen & 0xFF;

    // 拷貝 SPS 數據
    // 將 SPS 數據拷貝到 rtmpPacket->m_body[nextPosition] 地址中
    memcpy(&rtmpPacket->m_body[nextPosition], sps, spsLen);
    // 累加 SPS 長度信息
    nextPosition += spsLen;

    // PPS 個數
    rtmpPacket->m_body[nextPosition++] = 0x01;

    // PPS 數據的長度, 佔 2 字節
    // 設置長度的高位
    rtmpPacket->m_body[nextPosition++] = (ppsLen >> 8) & 0xFF;
    // 設置長度的低位
    rtmpPacket->m_body[nextPosition++] = (ppsLen) & 0xFF;
    // 拷貝 SPS 數據
    memcpy(&rtmpPacket->m_body[nextPosition], pps, ppsLen);


    // 設置 RTMP 包類型, 視頻類型數據
    rtmpPacket->m_packetType = RTMP_PACKET_TYPE_VIDEO;
    // 設置 RTMP 包長度
    rtmpPacket->m_nBodySize = rtmpPackagesize;
    // 分配 RTMP 通道, 隨意分配
    rtmpPacket->m_nChannel = 10;
    // 設置視頻時間戳, 如果是 SPP PPS 數據, 沒有時間戳
    rtmpPacket->m_nTimeStamp = 0;
    // 設置絕對時間, 對於 SPS PPS 賦值 0 即可
    rtmpPacket->m_hasAbsTimestamp = 0;
    // 設置頭類型, 隨意設置一個
    rtmpPacket->m_headerType = RTMP_PACKET_SIZE_MEDIUM;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章