H264数据包格式

H264数据是通过连续的00 00 0100 00 00 01进行分隔的,分隔数据之后的一个字节包含三个方面,分别为

  1. 第1位禁止位,值为1表示语法出错
  2. 第2~3位为参考级别
  3. 第4~8为是nal单元类型

现给出一段代码,进行数据分析:

#include <stdio.h>
#include <windows.h>

typedef enum {
    NALU_TYPE_SLICE    = 1,
    NALU_TYPE_DPA      = 2,
    NALU_TYPE_DPB      = 3,
    NALU_TYPE_DPC      = 4,
    NALU_TYPE_IDR      = 5,
    NALU_TYPE_SEI      = 6,
    NALU_TYPE_SPS      = 7,
    NALU_TYPE_PPS      = 8,
    NALU_TYPE_AUD      = 9,
    NALU_TYPE_EOSEQ    = 10,
    NALU_TYPE_EOSTREAM = 11,
    NALU_TYPE_FILL     = 12,
} NaluType;

typedef enum {
    NALU_PRIORITY_DISPOSABLE = 0,
    NALU_PRIRITY_LOW         = 1,
    NALU_PRIORITY_HIGH       = 2,
    NALU_PRIORITY_HIGHEST    = 3
} NaluPriority;

void printfH264(int j, int i, int nLen, int nFrameType)
{
    printf("%d  %d", j, i);
    int nForbiddenBit = (nFrameType>>7) & 0x1;//第1位禁止位,值为1表示语法出错
    int nReference_idc = (nFrameType>>5) & 0x03;//第2~3位为参考级别
    int nType = nFrameType & 0x1f;//第4~8为是nal单元类型 0x1f=31


    printf("  ");
    switch(nReference_idc){
        case NALU_PRIORITY_DISPOSABLE://可丢弃
            printf("DISPOS");
            break;
        case NALU_PRIRITY_LOW:
            printf("LOW");
            break;
        case NALU_PRIORITY_HIGH:
            printf("HIGH");
            break;
        case NALU_PRIORITY_HIGHEST:
            printf("HIGHEST");
            break;
    }

    //4-8
    printf("  ");
    switch(nType)
    {
        case NALU_TYPE_SLICE:
            printf("不分区,非IDR图像的片");
            break;
        case NALU_TYPE_DPA://片分区A
            printf("DPA");
            break;
        case NALU_TYPE_DPB://片分区B
            printf("DPB");
            break;
        case NALU_TYPE_DPC://片分区C
            printf("DPC");
            break;
        case NALU_TYPE_IDR: // IDR图像中的片 I帧=5
            printf("IDR");
            break;
        case NALU_TYPE_SEI:// 补充增强信息单元(SEI)
            printf("SEI");
            break;
        case NALU_TYPE_SPS:
            printf("SPS");
            break;
        case NALU_TYPE_PPS:
            printf("PPS");
            break;
        case NALU_TYPE_AUD:// 序列结束
            printf("AUD");
            break;
        case NALU_TYPE_EOSEQ://序列结束
            printf("EOSEQ");
            break;
        case NALU_TYPE_EOSTREAM://码流借宿
            printf("EOSTREAM");
            break;
        case NALU_TYPE_FILL://填充
            printf("FILL");
            break;
        case 24:
            printf("STAP-A   单一时间的组合包");
            break;
        case 25:
            printf("STAP-B   单一时间的组合包");
            break;
        case 26:
            printf("MTAP16   多个时间的组合包");
            break;
        case 27:
            printf("MTAP24   多个时间的组合包");
            break;
        case 28:
            printf("FU-A     分片的单元");
            break;
        case 29:
            printf(" FU-B     分片的单元");
            break;
        default:
            printf("没有定义 %d", nType);
            break;

    }

    printf("  %d\r\n", nLen);
}

int GetLen(int nPos, int nTotalSize, BYTE* btData)
{
    int nStart = nPos;
    while (nStart < nTotalSize)
    {
        if (btData[nStart] == 0x00 && btData[nStart + 1] == 0x00 && btData[nStart + 2] == 0x01)
        {
            return nStart - nPos;
        }
        else if (btData[nStart] == 0x00 && btData[nStart + 1] == 0x00 && btData[nStart + 2] == 0x00 && btData[nStart + 3] == 0x01)
        {
            return nStart - nPos;
        }
        else
        {
            nStart++;
        }
    }
    return nTotalSize - nPos;//最后一帧。
}

int _tmain(int argc, _TCHAR* argv[])
{
    BYTE *btData = NULL;
    DWORD dwFileSize = 0;
    HANDLE hFile = CreateFile(L"D:\\video.h264", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile != INVALID_HANDLE_VALUE){
        dwFileSize = GetFileSize(hFile, NULL);
        btData = new BYTE[dwFileSize];
        memset(btData, 0, dwFileSize);

        DWORD dwRead = 0;
        ReadFile(hFile, btData, dwFileSize, &dwRead, NULL);
        CloseHandle(hFile);
    }

    int j = 0;//多少帧
    int i = 0;//偏移量
    while (i < dwFileSize - 4)
    {
        if (btData[i] == 0x00 && btData[i + 1] == 0x00 && btData[i + 2] == 0x01)
        {
            int nLen = GetLen(i + 3, dwFileSize, btData);
            printfH264(j, i, nLen, btData[i + 3]);
            j++;
            i += 3;
        }
        else if (btData[i] == 0x00 && btData[i + 1] == 0x00 && btData[i + 2] == 0x00 && btData[i + 3] == 0x01)
        {
            int nLen = GetLen(i + 4, dwFileSize, btData);
            printfH264(j, i, nLen, btData[i + 4]);
            j++;
            i += 4;
        }
        else{
            i++;
        }
    }

    if (btData){
        delete[] btData;
    }
    return 0;
}