H265分辨率解码概述

1. 申明

本文章属于原创,其中参考的代码及文章在结尾处标明,侵删。

2. 目的

本文是为了解析H265中携带的视频分辨率而写的一个demo。

3. 背景知识

本文及文中所涉及代码,均以TS流作为媒介。

3.1 哥伦比亚指数编码无符号数的解码

(1)首先读取当前位置的bit位,记录连续0的个数N

(2)对于连续0的个数为0值为0,对于连续0的个数不为0 跳过第一个非0的bit位

(3)再读取N个数据,此数据以无符号形式解析num

(4)num=num-1+2的N次方

3.2 哥伦比亚指数编码有符号数的解码

(1)按照3.1的讲述先获取此段数据的哥伦比亚指数编码的无符号形式

(2)如果数据为奇数num=(num+1)/2;如果是偶数num=-(num/2)

3.3 解析参数序列集的过程

头部单元
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

   描述符一列表示此处数据的类型f和u表示的是无符号的数据类型,ue表示的是哥伦比亚指数编码的无符号数据,se表示的是哥伦比亚指数编码的有符号数据。

4. 代码demo


#define H265_SPS_HEAD_FLAG 0x42

uint8_t getbyte(uint32_t *start_bit,
    unsigned char *m_data, uint32_t m_len, uint32_t *m_zeros)
{
    if (*start_bit >= m_len)
        return 0;

    BYTE b = m_data[(*start_bit)++];
    // to avoid start-code emulation, a byte 0x03 is inserted
    // after any 00 00 pair. Discard that here.
    if (b == 0)
    {
        (*m_zeros)++;
        if ((*start_bit < m_len) && ((*m_zeros) == 2) && (m_data[*start_bit] == 0x03))
        {
            (*start_bit)++;
            (*m_zeros) = 0;
        }
    }
    else
    {
        (*m_zeros) = 0;
    }
    return b;
};

uint32_t getbit(int *m_bits, uint8_t *m_byte, uint32_t *start_bit, unsigned char *m_data,
     uint32_t m_len, uint32_t *m_zeros)
{
    if (*m_bits == 0)
    {
        *m_byte = getbyte(start_bit, m_data, m_len, m_zeros);
        *m_bits = 8;
    }
    *m_bits -= 1;
    return (*m_byte >> *m_bits) & 0x1;
};

uint32_t getword(int bits, int *m_bits, uint8_t *m_byte, uint32_t *start_bit, unsigned char *m_data,
     uint32_t m_len, uint32_t *m_zeros)
{
    uint32_t u = 0;
    while (bits > 0)
    {
        u <<= 1;
        u |= getbit(m_bits, m_byte, start_bit, m_data, m_len, m_zeros);
        bits--;
    }
    return u;
};

uint32_t getue(int *m_bits, uint8_t *m_byte, uint32_t *start_bit, unsigned char *m_data,
     uint32_t m_len, uint32_t *m_zeros)
{
    int zeros = 0;
    while (*start_bit < m_len && getbit(m_bits, m_byte, start_bit, m_data, m_len, m_zeros) == 0)
        zeros++;
    return getword(zeros, m_bits, m_byte, start_bit, m_data, m_len, m_zeros) + ((1 << zeros) - 1);
};

/*
  根据误差调整分辨率
  @param1 width:        原始宽数据的指针
  @param2 hight:        原始高数据的指针
  @param3 offset_width: 横向误差和
  @param4 offset_hight: 纵向误差和
*/
static inline BOOL adjust_resolution_by_offset(uint32_t *width, uint32_t *hight,
    uint8_t offset_width, uint8_t offset_hight)
{
    //左右存在对齐,分辨率宽度减去二倍的像素
    if (offset_width) {
        if ((offset_width * 2) <= *width)
            *width -= offset_width * 2;
        else
            return FALSE;
    }
    //上下存在对齐,分辨率高度减去二倍的像素
    if (offset_hight) {
        if ((offset_hight * 2) <= *hight)
            *hight -= offset_hight * 2;
        else
            return FALSE;
    }

    return TRUE;
}

/*
  查找H265 SPS数据起始部分
  @param1 buf:  原始数据buf的二级指针
  @param2 len:  原始数据大小的指针
  @param3 type: 要查找的视频类型起始字节:
                H264: 0x67
                H265: 0x42
*/
static inline BOOL find_sps_first_byte(unsigned char **buf, uint32_t *len, uint8_t type)
{
    unsigned char *pos = (unsigned char *)memchr(*buf, type, *len);
    if (NULL == pos)
        return FALSE;

    *len -= pos - *buf;
    *buf = pos;
    return TRUE;
}

/*
  解码H265
  @param1 pdata:    原始数据buf的指针
  @param2 len:      原始数据的大小
*/
static inline BOOL decode_h265(unsigned char *pdata, uint32_t len)
{
    uint32_t start_bit = 0;
    int m_bits = 0;
    uint8_t m_byte = 0;
    uint32_t m_zeros = 0;
    if (!find_sps_first_byte(&pdata, &len, H265_SPS_HEAD_FLAG))
        return FALSE;

    //解码 forbidden_zero_bit
    getword(1, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros);
    //解码 nal_unit_type
    getword(6, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros);
    //解码 nuh_layer_id
    getword(6, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros);
    //解码 nuh_temporal_id_plus1
    getword(3, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros);
    //解码 sps_video_parameter_set_id
    getword(4, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros);
    //解码 sps_max_sub_layers_minus1
    uint32_t sps_max_sub_layers_minus1 = getword(3, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros);
    if (sps_max_sub_layers_minus1 > 6)
        return FALSE;
    //解码 temporal_id_nesting_flag
    getword(1, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros);
    // profile_tier_level( sps_max_sub_layers_minus1 )
    {
        getword(2, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros);
        getword(1, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros);
        getword(5, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros);
        getword(32, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros);
        getword(1, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros);
        getword(1, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros);
        getword(1, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros);
        getword(1, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros);
        getword(44, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros);
        getword(8, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros);

        unsigned char sub_layer_profile_present_flag[6] = { 0 };
        unsigned char sub_layer_level_present_flag[6] = { 0 };
        int i;
        for (i = 0; i < sps_max_sub_layers_minus1; i++)
        {
            sub_layer_profile_present_flag[i] = getword(1, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros);
            sub_layer_level_present_flag[i] = getword(1, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros);
        }
        if (sps_max_sub_layers_minus1 > 0)
        {
            for (i = sps_max_sub_layers_minus1; i < 8; i++)
                getword(2, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros);
        }
        for (i = 0; i < sps_max_sub_layers_minus1; i++)
        {
            if (sub_layer_profile_present_flag[i])
            {
                getword(2, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros);
                getword(1, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros);
                getword(5, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros);
                getword(32, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros);
                getword(1, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros);
                getword(1, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros);
                getword(1, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros);
                getword(1, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros);
                getword(44, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros);
            }
            if (sub_layer_level_present_flag[i])
                getword(8, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros);
        }
    }

    // "The  value  of sps_seq_parameter_set_id shall be in the range of 0 to 15, inclusive."
    unsigned long sps_seq_parameter_set_id = getue(&m_bits, &m_byte, &start_bit, pdata,
        len, &m_zeros);
    if (sps_seq_parameter_set_id > 15)
        return FALSE;

    // "The value of chroma_format_idc shall be in the range of 0 to 3, inclusive."
    unsigned long chroma_format_idc = getue(&m_bits, &m_byte, &start_bit, pdata,
        len, &m_zeros);
    if (sps_seq_parameter_set_id > 3)
        return FALSE;

    if (chroma_format_idc == 3)
        getword(1, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros);

    uint32_t width = getue(&m_bits, &m_byte, &start_bit, pdata,
        len, &m_zeros); // pic_width_in_luma_samples
    uint32_t height = getue(&m_bits, &m_byte, &start_bit, pdata,
        len, &m_zeros); // pic_height_in_luma_samples
    if (getword(1, &m_bits, &m_byte, &start_bit, pdata, len, &m_zeros))
    {// conformance_window_flag
        // conf_win_left_offset
        uint32_t conf_win_left_offset = getue(&m_bits, &m_byte, &start_bit, pdata,
            len, &m_zeros);
        // conf_win_right_offset
        uint32_t conf_win_right_offset = getue(&m_bits, &m_byte, &start_bit, pdata,
            len, &m_zeros);
        // conf_win_top_offset
        uint32_t conf_win_top_offset = getue(&m_bits, &m_byte, &start_bit, pdata,
            len, &m_zeros);
        // conf_win_bottom_offset
        uint32_t conf_win_bottom_offset = getue(&m_bits, &m_byte, &start_bit, pdata,
            len, &m_zeros);

        if (!adjust_resolution_by_offset(&width, &height,
            conf_win_left_offset + conf_win_right_offset,
            conf_win_top_offset + conf_win_bottom_offset)) {
            ;//不够减
        }
    }
    return TRUE;
}

划重点: 为了解析分辨率,需要一层层进行解码,所以上面代码中,只调用getword(),getue()而未获取返回值的,可以暂时不用关注,有兴趣可以自己了解。需要强调的是,最后对于补齐的计算,如果不进行补齐的核对,会出现640*368这种分辨率,而实际应用中我们的宽高比为16:9, 所以分辨率应该为640:360, 那么这多出来的8,其实就是在传输过程中,必须以宏块(16的倍数)来传输,为了满足这个要求,高度向上进行了补齐,所以在计算真实值的时候,要根据conf_win_left_offset,conf_win_right_offset,conf_win_top_offset,conf_win_bottom_offset进行相应的调整。

5. 专栏知识链接

1. 协议知识概述
2. H264分辨率解码概述
3. 以太网Ethernet解码概述

6. 写在最后

本文引用了以下文章作者的代码或思路,
并结合了实际项目中的代码整理出的demo,如有问题欢迎指正。

https://blog.csdn.net/chinabinlang/article/details/89209609
https://blog.csdn.net/chinabinlang/article/details/93640746?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160742052619724848117078%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=160742052619724848117078&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_v2~rank_v29-6-93640746.nonecase&utm_term=H265%20%E8%A7%A3%E7%A0%81&spm=1018.2118.3001.4449

官方文档
https://www.doc88.com/p-8708471960665.html

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值