mpeg4视频中,I帧、p帧、B帧的判定

李国帅 2012.9 应用中验证过的代码



mpeg4视频帧的帧类型判断

mpeg4的每一帧开头是固定的:00 00 01 b6, 那么我们如何判断当前帧属于什么帧呢?在接下来的2bit,将会告诉我们答案。

注意:是2bit,不是byte,下面是各类型帧与2bit的对应关系:

00: I Frame

01: P Frame

10: B Frame

为了更好地说明,我们举几个例子,以下是16进制显示的视频编码:

00 00 01 b6 10 34 78 97 09 87 06 57 87 …… I帧

00 00 01 b6 50 78 34 20 cc 66 b3 89 …… P帧

00 00 01 b6 96 88 99 06 54 34 78 90 98 …… B帧

下面我们来分析一下为什么他们分别是I、P、B帧

0x10 = 0001 0000

0x50 = 0101 0000

0x96 = 1001 0100

大家看红色的2bit,再对照开头说的帧与2bit的对应关系。

0x56 = 0101 0110

00 00 01 B0是视频对象序列开始标志(VISOBJSEQ_START_CODE),其后的数据只有一位F4,表示此视频对象编码序列编码的Profile与Level类型是XVID_PROFILE_AS_L4, Profile类型数值定义于xvid.h。

00 00 01 B5是视频对象开始标志(VISOBJ_START_CODE),其后只有一位16进制数据09,从这一位数据可以获得视频对象版本号、视频类型和视频信号类型信息,详细介绍见图5-11。

00 00 01 00是视频对象开始标志 (VIDOBJ_START_CODE),00 00 01 20是视频对象层开始标志(VIDOBJLAY_START_CODE),其后的11位16进制数据比较重要,解码所需要的VOP纵横比、视频对象形状和图像分辨率等数据都是从这11位数据获得的,详细介绍见图5-11,图中未标记用途的数据表示未使用。

00 00 01 B2是用户数据开始标志(USERDATA_START_CODE),十六进制用户数据共有8位。

00 00 01 B6是VOP开始标志(VOP_START_CODE),每个VOP编码数据都以VOP标识头开始,VOP标识头比较简单,如果用16进制数据表示其数值为00 00 01 B6。解码的时候,以00 00 01 B6来判定是不是一个VOP的开始,并从VOP标识头后的数据读取编码类型,量化值等参数后完成一帧VOP的解码。



例子

播放mpeg4文件的帧数据情况

0x05FC7C40 00 00 01 b6 56 c0 47 ff ff f5 b1 9d 4f 95 6a 81 ...?V?G..???O?j?

0x05FC7C50 e3 3f b1 bd 8c 67 fd 6f 95 67 88 fc 6f 34 1e 12 ?????g?o?g??o4..

0x05FC7C60 00 f1 fa a8 aa 27 56 5d 82 5f 26 02 88 0d 70 46 .????'V]?_&.?.pF

0x05FC7C70 a0 16 b0 3c 47 fe 62 5e 4f 82 9a 17 09 19 00 a0 ?.?<G?b^O??....?

0x05FC7C80 f7 da 0c 46 a0 18 47 57 01 e1 a0 1f 04 1f d0 24 ??.F?.GW.??...?$

0x05FC7C90 5e 24 15 9f f6 03 37 b4 1e 13 fe 95 7e 97 67 01 ^$.??.7?..??~?g.

播放axis的关键帧的情况

0x05ED7EB0 00 00 01 b0 f5 00 00 01 b5 09 00 00 01 00 00 00 ...??...?.......

0x05ED7EC0 01 20 00 88 40 19 28 b0 22 40 a2 1f 00 00 01 b3 . .?@.(?"@?....?

0x05ED7ED0 be d0 27 00 00 01 b6 1a 18 2b 30 bc 1c 0a 5e af ??'...?..+0?..^?

0x05ED7EE0 0b 91 a2 06 5c da f0 1f 12 00 b6 0e 05 18 3c 34 .??.\??...?...<4

或者

0x06137F98 00 00 01 b3 01 f3 67 00 00 01 b6 1a 38 2b 31 08 ...?.?g...?.8+1.

0x06137FA8 0a 32 af 03 c1 40 2a 83 01 80 a9 24 58 1f 0e 00 .2?.?@*?.€?$X...

0x06137FB8 bc 0e 05 18 3c 34 02 68 28 30 15 07 ce 80 2c 78 ?...<4.h(0..?€,x



mpeg4帧类型

video_object_start_code 00 through 1F

video_object_layer_start_code 20 through 2F

reserved 30 through AF

visual_object_sequence_start_code B0

visual_object_sequence_end_code B1

user_data_start_code B2

group_of_vop_start_code B3

video_session_error_code B4

visual_object_start_code B5

vop_start_code B6

reserved B7-B9

fba_object_start_code BA

fba_object_plane_start_code BB

mesh_object_start_code BC

mesh_object_plane_start_code BD

still_texture_object_start_code BE

texture_spatial_layer_start_code BF

texture_snr_layer_start_code C0

texture_tile_start_code C1

texture_shape_layer_start_code C2

stuffing_start_code C3

reserved C4-C5

System start codes (see note) C6 through FF



帧类型判断代码

现有程序的计算方法是错误的,并且没有考虑I帧的情况。


BYTE CRTPClientSession::GetVideoFrameType(LONG nVideoFormat,char* pFrameData, UINT uLength)
...
    UINT nTag =((UINT*)pData)[0];
    if(uLength<8)return 0x05;
    if((nTag & 0xF0FFFFFF) != 0xB0010000)return 0x04;//并非分帧模式第一个包//帧头,MP4的包标记
    BYTE nTagFrame =((BYTE*)pData)[4+3];// skip to the right position*/
    
    // vop_coding type: // 000001b6之后的两位,是00表示 I frame, 01 表示 P frame, 10 表示 B frame
        switch(nTagFrame & 0xC0)// vop_coding_type coding method
        {
        case 0:
            //avFlag = 0x40;//认为是配置帧+I帧
            break;
        case 0x40:
            //P帧//MP4的包标记
            break;
        case 0x80:
            //B frame/ 10 bidirectionally-predictive-coded(B)// Ignore JPEG frame, fragment frame...
            break;
        default:
            ATLASSERT(0);
            // unknown, invalid frame// 11 sprite(S) // assume vop 's position less than 128//无效帧
            break;
        }


修改为计算出VOP位置,然后找到第一个字节,得到视频帧类型,以前使用的方式是瞎猫碰到死耗子,没有出错而已。

把上面的代码BYTE nTagFrame =((BYTE*)pData)[4+3];替换为下面的代码


BYTE nTagFrame = 0xFF;// skip to the right position*/
        if (nTag == 0xB6010000)
        {
            nTagFrame = pData[4];
        }
        else
        {
            uint32_t Buffer_Offset = 0;
            int nGet = GetVop_Offset(pData,uLength,Buffer_Offset);
            if (nGet == 0)
                return 0x05; //没有得到VOP

            nTagFrame = pData[Buffer_Offset];
        }

int GetVop_Offset( uint8_t* Buffer, uint32_t Buffer_Size, uint32_t& Buffer_Offset )
{
    Buffer_Offset = 0;

    BitStream_Fast* BS=new BitStream_Fast;
    BS->Attach(Buffer, Buffer_Size);
    if (BS->Remain() < 5)
    {
        BS->Attach(NULL, 0);//不申请内存只是引用
        delete BS; //BS=NULL;
        return 0;
    }

    uint32_t nTag0 = 0;
    uint32_t nTag12 = 0;
    uint32_t nTag3 = 0;

    int nGetBO = 0;
    //查找00000100 00000120
    while (nGetBO == 0)
    {
        nTag0 = BS->Get8(8);
        if (nTag0 == 0x00)
        {
            Buffer_Offset++;
            nTag12 = BS->Get8(16);
            if (nTag12 == 0x0001)
            {
                Buffer_Offset += 2;
                nTag3 = BS->Get8(8);
                if (nTag3 == 0xB6)
                {
                    nGetBO = 1;
                    Buffer_Offset++;
                    break;
                }
                else
                {
                    Buffer_Offset++;
                }
            }
            else
            {
                Buffer_Offset += 2;
            }
        }
        else
        {
            Buffer_Offset++;
        }
        if (BS->Remain() < 5)
        {
            Buffer_Offset = 0;
            break;
        }
    }

    BS->Attach(NULL, 0);//不申请内存只是引用
    delete BS; //BS=NULL;

    return nGetBO;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

微澜-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值