h264 raw stream parser-讀取H264裸流信息

 要分析h264裸流中的數據,H264BSAnalyzer這個工具就不錯,在這裏推薦一下。
 用H264BSAnalyzer讀取我編碼的一段h264視頻,截圖如下:
在這裏插入圖片描述
 閱讀或者修改就不太方便,依賴一些編解碼庫。昨天,我閱讀webrtc的代碼,發現chromium代碼裏有相關的部分,主要就兩個文件,短小精悍,於是就把它摘下來的。官方的測試例子[2]。
 我摘取的代碼,下載地址[4],編譯需要c++14。來段測試程序:

#include <iostream>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <stdlib.h>

#include <string>
#include "range.h"
#include "h264_bit_reader.h"
#include "h264_parser.h"
#include "logging.h"
using namespace std;
using namespace media;
//https://cs.chromium.org/chromium/src/media/video/h264_parser_unittest.cc
////https://zhuanlan.zhihu.com/p/27896239   sps pps
void print_sps_info(const H264SPS *sps,int len){
    int height=sps->pic_height_in_map_units_minus1;
    int width=sps->pic_width_in_mbs_minus1;
    std::cout<<"h "<<(height+1)*16<<" w "<<(width+1)*16<<std::endl;
    std::cout<<"ref "<<sps->max_num_ref_frames<<" "<<len<<std::endl; 
}
static std::string convert_slice_string(int type){
    switch(type%5){
        case H264SliceHeader::kPSlice:{
            return "PSlice";
        }
        case H264SliceHeader::kBSlice:{
            return "BSlice";
        }
        case H264SliceHeader::kISlice:{
            return "ISlice";
        }
        case H264SliceHeader::kSPSlice:{
            return "SPSlice";
        }
        case H264SliceHeader::kSISlice:{
            return "SISlice";
        }
        default:{
            return "none";
        }
    }
}
void printf_slice_info(media::H264SliceHeader &slice,int len){
    std::cout<<slice.frame_num<<" slice "<<convert_slice_string(slice.slice_type)<<" "<<len<<std::endl;
}
int main()
{

    int fd=0;
    if((fd=open("1280x720.h264",O_RDONLY,S_IRGRP))<0){
        printf("open failed \n");
        return 0;
    }
    struct stat file_stat;
    if((fstat(fd,&file_stat))<0){
        printf("fstat failed \n");
        return 0;        
    }
    void *start_fp;
    if((start_fp=mmap(NULL,file_stat.st_size,PROT_READ,MAP_SHARED,fd,0))==MAP_FAILED){
        printf("mmap failed \n");
        return 0;         
    }
    int length=file_stat.st_size;
    std::cout<<"size "<<length<<std::endl;
    H264Parser parser;
    parser.SetStream((uint8_t*)start_fp,length);
    int id;
    int f=0;
    int total_to_parse=5;
    int parse_len=0;
    while(f<total_to_parse){
        media::H264SliceHeader shdr;
        media::H264SEIMessage sei_msg;
        H264NALU nalu;
        H264Parser::Result res = parser.AdvanceToNextNALU(&nalu);
        if(res==H264Parser::kOk){
        switch (nalu.nal_unit_type){     
        case H264NALU::kIDRSlice:
        case H264NALU::kNonIDRSlice:{
            CHECK_EQ(parser.ParseSliceHeader(nalu, &shdr), H264Parser::kOk);
            int len=length-parser.get_offset()-parse_len;
            parse_len+=len;
            printf_slice_info(shdr,len);
            break;            
        }   
        case H264NALU::kSPS:{
            CHECK_EQ(parser.ParseSPS(&id), H264Parser::kOk);
            const H264SPS *sps=parser.GetSPS(id);
            int len=length-parser.get_offset()-parse_len;
            parse_len+=len;
            print_sps_info(sps,len);
            break;          
        }
        case H264NALU::kPPS:{
            CHECK_EQ(parser.ParsePPS(&id), H264Parser::kOk);
            int len=length-parser.get_offset()-parse_len;
            parse_len+=len;
            break;            
        }  
        case H264NALU::kSEIMessage:{
            int len=length-parser.get_offset()-parse_len;
            parse_len+=len;
            CHECK_EQ(parser.ParseSEI(&sei_msg), H264Parser::kOk);
            break;            
        }
        default:{
            // Skip unsupported NALU.
            DLOG(INFO)<< "Skipping unsupported NALU";
            int len=length-parser.get_offset()-parse_len;
            parse_len+=len;
            break;            
        }
        }   
        f++;
        }else{
            std::cout<<"h264 parser error"<<std::endl;
            break;
        }        
    }
    close(fd);
  return 0;
}

 輸出結果:

1 4 28 SPS SPS
2 4 8 PPS PPS
3 3 699 SEI SEI
4 3 27126 IDR ISlice
5 3 15373 IDR ISlice
6 4 11264 NonIDR PSlice
7 3 7752 NonIDR PSlice
8 4 10301 NonIDR PSlice
9 3 6717 NonIDR PSlice
10 4 9580 NonIDR PSlice

ref:
[1]H264BSAnalyzer
[2]h264_parser_unittest.cc
[3]h264bitstream
[4]h264-parser
[5]H264碼流解析及NALU
[6]視音頻數據處理入門:H.264視頻碼流解析
[7] 從零實現一個H.264碼流解析器

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章