Unity 3D : C# 解析 H.264 SPS 幀

前言 ( 廢話 ):

嗯,別問我為啥用 Unity 搞 H.264,沒為什麼,純粹只是我喜歡 Unity 而已

不喜歡 FFMPEG 的人有福了,我們直接從 Bit 流解析 H.264,不用第三方插件,也能很好的跨平台,是不是很爽 ?

而且大部分的人都是用 C/C++ 來做解碼,用 C# 等物件導向語言做的人還挺少人做的,所以我來為世界貢獻一份心力,哈哈

SPS 是甚麼我就不詳細解釋了 ( 會看我文章的人估計都是有水準的人 ? ),簡單的說就是 影片的參數集 (例如影片的長與寬),沒有 SPS 會無法解析影片的。

你可以直接往下拉到最下面看輸出結果,你就可以知道整體的運作方式了。

C #:

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

public class SPSDecoder : MonoBehaviour
{
    unsafe void Start ()
    {
        byte[] strArray = GetSPS_ReadFile ("C:/D.h264");

//      // 如果你沒有上面的 h.264 檔案測試,你可以把下面註解解開,然後把上面的 GetSPS_ReadFile() 那行註解掉
//      // SPS Frame, 已去除開頭的 0x01 0x00 0x00 0x01 0x67
//      byte[] strArray = new byte[] {
//          0x64, 0x00, 0x28, 0xAC, 0xD9, 0x40, 0x78, 0x02, 0x27, 0xE5, 0xC0, 0x44, 0x00, 0x00, 0x0F, 0xA4, 0x00, 0x02, 0xEE, 0x00, 0x3C, 0x60, 0xC6, 0x58, 0x00
//      };

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

        int profile_idc = -1;
        int level_idc = -1;
        int seq_parameter_set_id = -1;
        int chroma_format_idc = -1;
        int bit_depth_luma_minus8 = -1;
        int bit_depth_chroma_minus8 = -1;
        int qpprime_y_zero_transform_bypass_flag = -1;
        int seq_scaling_matrix_present_flag = -1;
        int log2_max_frame_num_minus4 = -1;
        int pic_order_cnt_type = -1;
        int log2_max_pic_order_cnt_lsb_minus4 = -1;
        int max_num_ref_frames = -1;
        int gaps_in_frame_num_value_allowed_flag = -1;
        int pic_width_in_mbs_minus1 = -1;
        int pic_height_in_map_units_minus1 = -1;
        int frame_mbs_only_flag = -1;
        int mb_adaptive_frame_field_flag = -1;
        int direct_8x8_inference_flag = -1;
        int frame_cropping_flag = -1;
        int frame_crop_left_offset = -1;
        int frame_crop_right_offset = -1;
        int frame_crop_top_offset = -1;
        int frame_crop_bottom_offset = -1;
        int vui_parameters_present_flag = -1;

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

        int bytePosition = 3, bitPosition = 0;
        int dataLengthInBits = strArray.Length * 8;

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

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

        // ----------------------
        // Print

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

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

        print ("------------------------------------");

        print ("影片解析度:" + (pic_width_in_mbs_minus1 + 1) * 16 + " x " + (pic_height_in_map_units_minus1 + 1) * 16);
    }

    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;
    }

    byte [] GetSPS_ReadFile(string H264_File_Path){
        List<byte []> NALU_List = GetNALU_ReadFile (H264_File_Path);
        foreach (byte[] bArray in NALU_List) {
            if(bArray[0] == 0x67){
                byte [] b = new byte[bArray.Length-1];
                for(int i=0, k = 1; i < b.Length; i++, k++){
                    b[i] = bArray[k];
                }
                return b;
            }
        }
        return null;
    }

    List<byte []> GetNALU_ReadFile (string H264_File_Path)
    {
        byte[] b = System.IO.File.ReadAllBytes (H264_File_Path);
        List<int> index = new List<int> ();
        for (int i = 0; i < b.Length - 3; i++) {
            if (b [i] == 0x00 && b [i + 1] == 0x00 && b [i + 2] == 0x01) {              
                index.Add (i + 3);
            }
        }
        List<byte[]> NALU_List = new List<byte[]> ();

        for (int i = 0; i < index.Count - 1; i++) {
            int start = index [i];
            int end = index [i + 1];
            int size = end - start;

            byte[] nalu = new byte[size - 3];

            for (int x = 0, k = start; k < end - 3; x++, k++) {
                nalu [x] = b [k];
            }
            NALU_List.Add (nalu);
        }

        return NALU_List;
    }

}

輸出結果:

这里写图片描述

猜你喜欢

转载自blog.csdn.net/weixin_38884324/article/details/80115258
今日推荐