Unity 3D : C# 解析 H.264 ( SPS / PPS / IDR )

由於我前兩篇文章已經有寫解析 SPS 與 PPS ,所以這篇主要是解析 IDR ,不過要解析 IDR 之前,必須先取得 SPS 與 PPS 內的值才能進行解析。所以這篇文章同時解析三種 NALU,並且同時改進之前的解析方法。

C #:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class IDRDecoder : MonoBehaviour
{
    unsafe void Start ()
    {
        List<NALU> NALUs = GetNALU_ReadFile ("C:/AA.h264");

        NALU SPS_NALU = GetNALU (NALUs, 0x67);
        NALU PPS_NALU = GetNALU (NALUs, 0x68);
        NALU IDR_NALU = GetNALU (NALUs, 0x65);

        SPS sps = SPS_Decoder (SPS_NALU);
        PPS pps = PPS_Decoder (PPS_NALU);
        IDR idr = IDR_Decoder (sps, pps, IDR_NALU);

        Print_SPS (sps);
        Print_PPS (pps);
        Print_IDR (sps, pps, idr, IDR_NALU);
    }

    void Print_SPS (SPS sps)
    {
        print ("-------------------------------------------------------------------------------");
        print ("[ SPS ]");
        print ("        profile_idc = " + sps.profile_idc);
        print ("        level_idc = " + sps.level_idc);
        print ("        seq_parameter_set_id = " + sps.seq_parameter_set_id);
        print ("        chroma_format_idc = " + sps.chroma_format_idc);
        print ("        bit_depth_luma_minus8 = " + sps.bit_depth_luma_minus8);
        print ("        bit_depth_chroma_minus8 = " + sps.bit_depth_chroma_minus8);
        print ("        qpprime_y_zero_transform_bypass_flag = " + sps.qpprime_y_zero_transform_bypass_flag);
        print ("        seq_scaling_matrix_present_flag = " + sps.seq_scaling_matrix_present_flag);
        print ("        log2_max_frame_num_minus4 = " + sps.log2_max_frame_num_minus4);
        print ("        pic_order_cnt_type = " + sps.pic_order_cnt_type);
        print ("        log2_max_pic_order_cnt_lsb_minus4 = " + sps.log2_max_pic_order_cnt_lsb_minus4);
        print ("        max_num_ref_frames = " + sps.max_num_ref_frames);
        print ("        gaps_in_frame_num_value_allowed_flag = " + sps.gaps_in_frame_num_value_allowed_flag);
        print ("        pic_width_in_mbs_minus1 = " + sps.pic_width_in_mbs_minus1);
        print ("        pic_height_in_map_units_minus1 = " + sps.pic_height_in_map_units_minus1);
        print ("        frame_mbs_only_flag = " + sps.frame_mbs_only_flag);
        if (sps.frame_mbs_only_flag == 0) {
            print ("        mb_adaptive_frame_field_flag = " + sps.mb_adaptive_frame_field_flag);
        }
        print ("        direct_8x8_inference_flag = " + sps.direct_8x8_inference_flag);
        print ("        frame_cropping_flag = " + sps.frame_cropping_flag);
        if (sps.frame_cropping_flag == 1) {         
            print ("        frame_crop_left_offset = " + sps.frame_crop_left_offset);
            print ("        frame_crop_right_offset = " + sps.frame_crop_right_offset);
            print ("        frame_crop_top_offset = " + sps.frame_crop_top_offset);
            print ("        frame_crop_bottom_offset = " + sps.frame_crop_bottom_offset);   
        }
        print ("        vui_parameters_present_flag = " + sps.vui_parameters_present_flag);
    }

    void Print_PPS (PPS pps)
    {
        print ("-------------------------------------------------------------------------------");
        print ("[ PPS ]");
        print ("        pic_parameter_set_id = " + pps.pic_parameter_set_id);
        print ("        seq_parameter_set_id = " + pps.seq_parameter_set_id);
        print ("        entropy_coding_mode_flag = " + pps.entropy_coding_mode_flag);
        print ("        pic_order_present_flag = " + pps.pic_order_present_flag);
        print ("        num_slice_groups_minus1 = " + pps.num_slice_groups_minus1);
        print ("        num_ref_idx_l0_active_minus1 = " + pps.num_ref_idx_l0_active_minus1);
        print ("        num_ref_idx_l1_active_minus1 = " + pps.num_ref_idx_l1_active_minus1);
        print ("        weighted_pred_flag = " + pps.weighted_pred_flag);
        print ("        weighted_bipred_idc = " + pps.weighted_bipred_idc);
        print ("        pic_init_qp_minus26 = " + pps.pic_init_qp_minus26);
        print ("        pic_init_qs_minus26 = " + pps.pic_init_qs_minus26);
        print ("        chroma_qp_index_offset = " + pps.chroma_qp_index_offset);
        print ("        deblocking_filter_control_present_flag = " + pps.deblocking_filter_control_present_flag);
        print ("        constrained_intra_pred_flag = " + pps.constrained_intra_pred_flag);
        print ("        redundant_pic_cnt_present_flag = " + pps.redundant_pic_cnt_present_flag);
    }

    void Print_IDR (SPS sps, PPS pps, IDR idr, NALU nalu)
    {
        // --------------------------------------------
        // 參考 H.264 官方手冊 7.4.3 章節
        // 判斷 slice_type 是 I, P, B ... 哪一種
        string slice_type_format = "-";
        switch (idr.slice_type) {
        case 0:
        case 5:
            slice_type_format = "P Slice";
            break;
        case 1:
        case 6:
            slice_type_format = "B Slice";
            break;
        case 2:
        case 7:
            slice_type_format = "I Slice";
            break;
        case 3:
        case 8:
            slice_type_format = "SP Slice";
            break;
        case 4:
        case 9:
            slice_type_format = "SI Slice";
            break;
        }

        // --------------------------------------------


        print ("-------------------------------------------------------------------------------");
        print ("[ IDR ]");
        print ("        first_mb_in_slice : " + idr.first_mb_in_slice);
        print ("        slice_type : " + idr.slice_type + " ( " + slice_type_format + " )");
        print ("        pic_parameter_set_id : " + idr.pic_parameter_set_id);
        print ("        frame_num : " + idr.frame_num);
        if (sps.frame_mbs_only_flag == 0) {
            print ("        field_pic_flag : " + idr.field_pic_flag);
            if (idr.field_pic_flag == 1) {
                print ("        bottom_field_flag : " + idr.bottom_field_flag);
            }
        }
        if (nalu.nal_unit_type == 5) {
            print ("        idr_pic_id : " + idr.idr_pic_id);
        }
        if (sps.pic_order_cnt_type == 0) {
            print ("        pic_order_cnt_lsb : " + idr.pic_order_cnt_lsb);
            if (pps.pic_order_present_flag == 1 && idr.field_pic_flag == 0) {
                print ("        delta_pic_order_cnt_bottom : " + idr.delta_pic_order_cnt_bottom);
            }
        }

        print ("        dec_ref_pic_marking() {");
        print ("                no_output_of_prior_pics_flag : " + idr.dec_ref_pic_marking.no_output_of_prior_pics_flag);
        print ("                long_term_reference_flag     : " + idr.dec_ref_pic_marking.long_term_reference_flag);
        print ("        }");


        print ("        slice_qp_delta : " + idr.slice_qp_delta);
    }


    unsafe SPS SPS_Decoder (NALU nalu)
    {
        SPS sps = new SPS ();

        int bytePosition = 3, bitPosition = 0;

        byte[] strArray = nalu.RBSP.ToArray ();

        sps.profile_idc = strArray [0];
        sps.level_idc = strArray [2];
        sps.seq_parameter_set_id = get_uev_code_num (strArray, &bytePosition, &bitPosition);

        if (sps.profile_idc == 100 || sps.profile_idc == 110 || sps.profile_idc == 122 || sps.profile_idc == 244
            || sps.profile_idc == 44 || sps.profile_idc == 83 || sps.profile_idc == 86 || sps.profile_idc == 118 || sps.profile_idc == 128) {
            sps.chroma_format_idc = get_uev_code_num (strArray, &bytePosition, &bitPosition);
            if (sps.chroma_format_idc == 3) {
                sps.separate_colour_plane_flag = get_bit_at_position (strArray, &bytePosition, &bitPosition);
            }
            sps.bit_depth_luma_minus8 = get_uev_code_num (strArray, &bytePosition, &bitPosition);
            sps.bit_depth_chroma_minus8 = get_uev_code_num (strArray, &bytePosition, &bitPosition);
            sps.qpprime_y_zero_transform_bypass_flag = get_bit_at_position (strArray, &bytePosition, &bitPosition);
            sps.seq_scaling_matrix_present_flag = get_bit_at_position (strArray, &bytePosition, &bitPosition);
            if (sps.seq_scaling_matrix_present_flag == 1) {
                throw new UnityException ("不支援此格式的檔案");
            }
        }
        sps.log2_max_frame_num_minus4 = get_uev_code_num (strArray, &bytePosition, &bitPosition);
        sps.pic_order_cnt_type = get_uev_code_num (strArray, &bytePosition, &bitPosition);
        if (sps.pic_order_cnt_type == 0) {
            sps.log2_max_pic_order_cnt_lsb_minus4 = get_uev_code_num (strArray, &bytePosition, &bitPosition);
        } else if (sps.pic_order_cnt_type == 1) {
            throw new UnityException ("不支援此格式的檔案");
        }
        sps.max_num_ref_frames = get_uev_code_num (strArray, &bytePosition, &bitPosition);
        sps.gaps_in_frame_num_value_allowed_flag = get_bit_at_position (strArray, &bytePosition, &bitPosition);
        sps.pic_width_in_mbs_minus1 = get_uev_code_num (strArray, &bytePosition, &bitPosition);
        sps.pic_height_in_map_units_minus1 = get_uev_code_num (strArray, &bytePosition, &bitPosition);
        sps.frame_mbs_only_flag = get_bit_at_position (strArray, &bytePosition, &bitPosition);
        if (sps.frame_mbs_only_flag == 0) {
            sps.mb_adaptive_frame_field_flag = get_bit_at_position (strArray, &bytePosition, &bitPosition);
        }
        sps.direct_8x8_inference_flag = get_bit_at_position (strArray, &bytePosition, &bitPosition);
        sps.frame_cropping_flag = get_bit_at_position (strArray, &bytePosition, &bitPosition);
        if (sps.frame_cropping_flag == 1) {
            sps.frame_crop_left_offset = get_uev_code_num (strArray, &bytePosition, &bitPosition);
            sps.frame_crop_right_offset = get_uev_code_num (strArray, &bytePosition, &bitPosition);
            sps.frame_crop_top_offset = get_uev_code_num (strArray, &bytePosition, &bitPosition);
            sps.frame_crop_bottom_offset = get_uev_code_num (strArray, &bytePosition, &bitPosition);
        }
        sps.vui_parameters_present_flag = get_bit_at_position (strArray, &bytePosition, &bitPosition);
        if (sps.vui_parameters_present_flag == 1) {             
        }


        return sps;
    }

    unsafe PPS PPS_Decoder (NALU nalu)
    {
        PPS pps = new PPS ();

        int bytePosition = 0, bitPosition = 0;

        byte[] strArray = nalu.RBSP.ToArray ();

        pps.pic_parameter_set_id = get_uev_code_num (strArray, &bytePosition, &bitPosition);
        pps.seq_parameter_set_id = get_uev_code_num (strArray, &bytePosition, &bitPosition);
        pps.entropy_coding_mode_flag = get_bit_at_position (strArray, &bytePosition, &bitPosition);
        pps.pic_order_present_flag = get_bit_at_position (strArray, &bytePosition, &bitPosition);
        pps.num_slice_groups_minus1 = get_uev_code_num (strArray, &bytePosition, &bitPosition);
        if (pps.num_slice_groups_minus1 > 0) {
            throw new UnityException ("不支援此格式的檔案");
        }
        pps.num_ref_idx_l0_active_minus1 = get_uev_code_num (strArray, &bytePosition, &bitPosition);
        pps.num_ref_idx_l1_active_minus1 = get_uev_code_num (strArray, &bytePosition, &bitPosition);
        pps.weighted_pred_flag = get_bit_at_position (strArray, &bytePosition, &bitPosition);
        pps.weighted_bipred_idc = get_bit_at_position (strArray, &bytePosition, &bitPosition) << 1 + get_bit_at_position (strArray, &bytePosition, &bitPosition);
        pps.pic_init_qp_minus26 = get_sev_code_num (strArray, &bytePosition, &bitPosition);
        pps.pic_init_qs_minus26 = get_sev_code_num (strArray, &bytePosition, &bitPosition);
        pps.chroma_qp_index_offset = get_sev_code_num (strArray, &bytePosition, &bitPosition);
        pps.deblocking_filter_control_present_flag = get_bit_at_position (strArray, &bytePosition, &bitPosition);
        pps.constrained_intra_pred_flag = get_bit_at_position (strArray, &bytePosition, &bitPosition);
        pps.redundant_pic_cnt_present_flag = get_bit_at_position (strArray, &bytePosition, &bitPosition);

        return pps;
    }

    unsafe IDR IDR_Decoder (SPS sps, PPS pps, NALU nalu)
    {       
        IDR idr = new IDR ();

        int bytePosition = 0, bitPosition = 0;

        byte[] strArray = nalu.RBSP.ToArray ();

        idr.first_mb_in_slice = get_uev_code_num (strArray, &bytePosition, &bitPosition);
        idr.slice_type = get_uev_code_num (strArray, &bytePosition, &bitPosition);
        idr.pic_parameter_set_id = get_uev_code_num (strArray, &bytePosition, &bitPosition);

        idr.frame_num = get_uint_code_num (strArray, &bytePosition, &bitPosition, sps.log2_max_frame_num_minus4 + 4);  // 有問題

        if (sps.frame_mbs_only_flag == 0) {
            idr.field_pic_flag = get_bit_at_position (strArray, &bytePosition, &bitPosition);
            if (idr.field_pic_flag == 1) {
                idr.bottom_field_flag = get_bit_at_position (strArray, &bytePosition, &bitPosition);
            }
        }

        if (nalu.nal_unit_type == 5) {
            idr.idr_pic_id = get_uev_code_num (strArray, &bytePosition, &bitPosition);
        }

        if (sps.pic_order_cnt_type == 0) {          
            idr.pic_order_cnt_lsb = get_uint_code_num (strArray, &bytePosition, &bitPosition, sps.log2_max_pic_order_cnt_lsb_minus4 + 4); // 有問題
            if (pps.pic_order_present_flag == 1 && idr.field_pic_flag == 0) {
                idr.delta_pic_order_cnt_bottom = get_sev_code_num (strArray, &bytePosition, &bitPosition);
            }
        }

        if (nalu.nal_ref_idc != 0) {
            // 進入 dec_ref_pic_marking() H264 函式 ( 簡化版 )
            if (nalu.nal_unit_type == 5) {
                idr.dec_ref_pic_marking.no_output_of_prior_pics_flag = get_bit_at_position (strArray, &bytePosition, &bitPosition);
                idr.dec_ref_pic_marking.long_term_reference_flag = get_bit_at_position (strArray, &bytePosition, &bitPosition);
            }
        }

        idr.slice_qp_delta = get_sev_code_num (strArray, &bytePosition, &bitPosition);

        return idr;
    }

    // -------------------------------------------------
    // H.264 Function

    unsafe int get_bit_at_position (byte[] buf, int*bytePotion, int*bitPosition)
    {
        int mask = 0, val = 0;

        mask = 1 << (7 - *bitPosition);

        val = ((buf [*bytePotion] & mask) != 0) ? 1 : 0;

        if (++*bitPosition > 7) {
            *bytePotion = *bytePotion + 1;
            *bitPosition = 0;
        }

        return val;
    }

    unsafe int get_uev_code_num (byte[] buf, int*bytePotion, int*bitPosition)
    {
        int val = 0, prefixZeroCount = 0;
        int prefix = 0, surfix = 0;

        while (true) {
            val = get_bit_at_position (buf, bytePotion, bitPosition);
            if (val == 0) {
                prefixZeroCount++;
            } else {
                break;
            }
        }

        prefix = (1 << prefixZeroCount) - 1;
        for (int i = 0; i < prefixZeroCount; i++) {
            val = get_bit_at_position (buf, bytePotion, bitPosition);
            surfix += val * (1 << (prefixZeroCount - i - 1));
        }

        prefix += surfix;

        return prefix;
    }


    unsafe int get_sev_code_num (byte[] buf, int*bytePotion, int*bitPosition)
    {
        int uev = get_uev_code_num (buf, bytePotion, bitPosition);
        int sign = uev % 2 == 0 ? -1 : 1;
        int sev = sign * ((uev + 1) >> 1);
        return sev;
    }

    unsafe int get_uint_code_num (byte[] buf, int*bytePotion, int*bitPosition, int length)
    {
        int uVal = 0;
        for (int idx = 0; idx < length; idx++) {
            uVal += get_bit_at_position (buf, bytePotion, bitPosition) << (length - idx - 1);
        }
        return uVal;
    }

    // -------------------------------------------------
    // Tool :

    // 只會找到最近的 Header
    NALU GetNALU (List<NALU> NALU_List, int header)
    {
        NALU _nalu = new NALU ();
        bool isFind = false;
        foreach (NALU nalu in NALU_List) {
            if (nalu.NAL_Header == header) {                
                _nalu = nalu;
                isFind = true;
                break;
            }
        }
        if (isFind == false) {
            throw new UnityException ("沒找到指定的 NAL Header");
        }
        return _nalu;
    }

    List<NALU> GetNALU_ReadFile (string H264_File_Path)
    {
        byte[] b = System.IO.File.ReadAllBytes (H264_File_Path);
        List<NALU> NALU_List = new List<NALU> ();
        int i = 0;
        while (i < b.Length) {
            if (b [i] == 0x00 && b [i + 1] == 0x00 && b [i + 2] == 0x01) {
                i += 3;
                NALU nalu = new NALU ();
                nalu.NAL_Header = b [i];
                nalu.forbidden_zero_bit = nalu.NAL_Header & 0x80;
                nalu.nal_ref_idc = nalu.NAL_Header & 0x60;
                nalu.nal_unit_type = nalu.NAL_Header & 0x1F;
                i += 1;
                nalu.RBSP = new List<byte> ();
                do {
                    nalu.RBSP.Add (b [i]);
                    i += 1;
                    if (i == b.Length - 3) {
                        nalu.RBSP.Add (b [i + 1]);
                        nalu.RBSP.Add (b [i + 2]);
                        break;
                    }
                } while((b [i] == 0x00 && b [i + 1] == 0x00 && b [i + 2] == 0x01) == false);
                NALU_List.Add (nalu);
            } else {
                i += 1;
            }
        }
        return NALU_List;
    }
    // -------------------------------------------------
    // Struct :

    struct NALU
    {
        // +---------------+
        // |  NAL_Header   |
        // +---------------+
        // |F|NRI| Type    |
        // +-+-+-+-+-+-+-+-+
        // |0|1|2|3|4|5|6|7|
        // +---------------+
        //
        // F    = forbidden_zero_bit   : ( H264 規範變數 )
        // NRI  = nal_ref_idc          : ( H264 規範變數 )
        // Type = nal_unit_type        : ( H264 規範變數 )
        //
        // NAL_Header = F + NRI + Type

        public byte NAL_Header;

        public int forbidden_zero_bit;
        public int nal_ref_idc;
        public int nal_unit_type;

        public List <byte> RBSP;
    }

    struct SPS
    {
        public int profile_idc;
        public int level_idc;
        public int seq_parameter_set_id;
        public int chroma_format_idc;
        public int separate_colour_plane_flag;
        public int bit_depth_luma_minus8;
        public int bit_depth_chroma_minus8;
        public int qpprime_y_zero_transform_bypass_flag;
        public int seq_scaling_matrix_present_flag;
        public int log2_max_frame_num_minus4;
        public int pic_order_cnt_type;
        public int log2_max_pic_order_cnt_lsb_minus4;
        public int max_num_ref_frames;
        public int gaps_in_frame_num_value_allowed_flag;
        public int pic_width_in_mbs_minus1;
        public int pic_height_in_map_units_minus1;
        public int frame_mbs_only_flag;
        public int mb_adaptive_frame_field_flag;
        public int direct_8x8_inference_flag;
        public int frame_cropping_flag;
        public int frame_crop_left_offset;
        public int frame_crop_right_offset;
        public int frame_crop_top_offset;
        public int frame_crop_bottom_offset;
        public int vui_parameters_present_flag;
    }

    struct PPS
    {
        public int pic_parameter_set_id;
        public int seq_parameter_set_id;
        public int entropy_coding_mode_flag;
        public int pic_order_present_flag;
        public int num_slice_groups_minus1;
        public int num_ref_idx_l0_active_minus1;
        public int num_ref_idx_l1_active_minus1;
        public int weighted_pred_flag;
        public int weighted_bipred_idc;
        public int pic_init_qp_minus26;
        public int pic_init_qs_minus26;
        public int chroma_qp_index_offset;
        public int deblocking_filter_control_present_flag;
        public int constrained_intra_pred_flag;
        public int redundant_pic_cnt_present_flag;
    }

    struct IDR
    {
        public int first_mb_in_slice;
        public int slice_type;
        public int pic_parameter_set_id;
        public int frame_num;
        public int field_pic_flag;
        public int bottom_field_flag;
        public int idr_pic_id;
        public int pic_order_cnt_lsb;
        public int delta_pic_order_cnt_bottom;
        public int slice_qp_delta;

        public IDR_SubFunction__Dec_Ref_Pic_Marking dec_ref_pic_marking;
    }

    // IDR Sub
    struct IDR_SubFunction__Dec_Ref_Pic_Marking
    {
        public int no_output_of_prior_pics_flag;
        public int long_term_reference_flag;
    }
}

執行結果:

这里写图片描述

猜你喜欢

转载自blog.csdn.net/weixin_38884324/article/details/80217186