精华内容
下载资源
问答
  • 解析h264
    2022-02-20 17:43:23

    解析H264的SPS信息
    from: https://www.jianshu.com/p/304ba7e9db29

    //
    //  H264ParseSPS.h
    //
    //  Created by lzj<lizhijian_21@163.com> on 2018/7/6.
    //  Copyright © 2018年 LZJ. All rights reserved.
    //
    
    #ifndef H264ParseSPS_h
    #define H264ParseSPS_h
    #ifdef __cplusplus
    extern "C"
    {
    #endif
    #include <stdio.h>
    
    typedef struct
    {
        unsigned int profile_idc;
        unsigned int level_idc;
    
        unsigned int width;
        unsigned int height;
        unsigned int fps;       //SPS中可能不包含FPS信息
    } sps_info_struct;
    
    /**
     解析SPS数据信息
    
     @param data SPS数据内容,需要Nal类型为0x7数据的开始(比如:67 42 00 28 ab 40 22 01 e3 cb cd c0 80 80 a9 02)
     @param dataSize SPS数据的长度
     @param info SPS解析之后的信息数据结构体
     @return success:1,fail:0
    
     */
    int h264_parse_sps(const unsigned char *data, unsigned int dataSize, sps_info_struct *info);
    
    #ifdef __cplusplus
    }
    #endif
    #endif /* H264ParseSPS_h */
    
    
    //
    //  H264ParseSPS.c
    //
    //  Created by lzj<lizhijian_21@163.com> on 2018/7/6.
    //  Copyright © 2018年 LZJ. All rights reserved.
    //
    //  See https://www.itu.int/rec/T-REC-H.264-201610-S
    //
    
    #include "H264ParseSPS.h"
    #include <string.h>
    #include <stdlib.h>
    #include <math.h>
    
    typedef unsigned char BYTE;
    typedef int INT;
    typedef unsigned int UINT;
    
    typedef struct
    {
        const BYTE *data;   //sps数据
        UINT size;          //sps数据大小
        UINT index;         //当前计算位所在的位置标记
    } sps_bit_stream;
    
    /**
     移除H264的NAL防竞争码(0x03)
    
     @param data sps数据
     @param dataSize sps数据大小
     */
    static void del_emulation_prevention(BYTE *data, UINT *dataSize)
    {
        UINT dataSizeTemp = *dataSize;
        for (UINT i=0, j=0; i<(dataSizeTemp-2); i++) {
            int val = (data[i]^0x0) + (data[i+1]^0x0) + (data[i+2]^0x3);    //检测是否是竞争码
            if (val == 0) {
                for (j=i+2; j<dataSizeTemp-1; j++) {    //移除竞争码
                    data[j] = data[j+1];
                }
    
                (*dataSize)--;      //data size 减1
            }
        }
    }
    
    static void sps_bs_init(sps_bit_stream *bs, const BYTE *data, UINT size)
    {
        if (bs) {
            bs->data = data;
            bs->size = size;
            bs->index = 0;
        }
    }
    
    /**
     是否已经到数据流最后
    
     @param bs sps_bit_stream数据
     @return 1:yes,0:no
     */
    static INT eof(sps_bit_stream *bs)
    {
        return (bs->index >= bs->size * 8);    //位偏移已经超出数据
    }
    
    /**
     读取从起始位开始的BitCount个位所表示的值
    
     @param bs sps_bit_stream数据
     @param bitCount bit位个数(从低到高)
     @return value
     */
    static UINT u(sps_bit_stream *bs, BYTE bitCount)
    {
        UINT val = 0;
        for (BYTE i=0; i<bitCount; i++) {
            val <<= 1;
            if (eof(bs)) {
                val = 0;
                break;
            } else if (bs->data[bs->index / 8] & (0x80 >> (bs->index % 8))) {     //计算index所在的位是否为1
                val |= 1;
            }
            bs->index++;  //递增当前起始位(表示该位已经被计算,在后面的计算过程中不需要再次去计算所在的起始位索引,缺点:后面每个bit位都需要去位移)
        }
    
        return val;
    }
    
    /**
     读取无符号哥伦布编码值(UE)
     #2^LeadingZeroBits - 1 + (xxx)
    
     @param bs sps_bit_stream数据
     @return value
     */
    static UINT ue(sps_bit_stream *bs)
    {
        UINT zeroNum = 0;
        while (u(bs, 1) == 0 && !eof(bs) && zeroNum < 32) {
            zeroNum ++;
        }
    
        return (UINT)((1 << zeroNum) - 1 + u(bs, zeroNum));
    }
    
    /**
     读取有符号哥伦布编码值(SE)
     #(-1)^(k+1) * Ceil(k/2)
    
     @param bs sps_bit_stream数据
     @return value
     */
    INT se(sps_bit_stream *bs)
    {
        INT ueVal = (INT)ue(bs);
        double k = ueVal;
    
        INT seVal = (INT)ceil(k / 2);     //ceil:返回大于或者等于指定表达式的最小整数
        if (ueVal % 2 == 0) {       //偶数取反,即(-1)^(k+1)
            seVal = -seVal;
        }
    
        return seVal;
    }
    
    /**
     视频可用性信息(Video usability information)解析
    
     @param bs sps_bit_stream数据
     @param info sps解析之后的信息数据及结构体
     @see E.1.1 VUI parameters syntax
     */
    void vui_para_parse(sps_bit_stream *bs, sps_info_struct *info)
    {
        UINT aspect_ratio_info_present_flag = u(bs, 1);
        if (aspect_ratio_info_present_flag) {
            UINT aspect_ratio_idc = u(bs, 8);
            if (aspect_ratio_idc == 255) {      //Extended_SAR
                u(bs, 16);      //sar_width
                u(bs, 16);      //sar_height
            }
        }
    
        UINT overscan_info_present_flag = u(bs, 1);
        if (overscan_info_present_flag) {
            u(bs, 1);       //overscan_appropriate_flag
        }
    
        UINT video_signal_type_present_flag = u(bs, 1);
        if (video_signal_type_present_flag) {
            u(bs, 3);       //video_format
            u(bs, 1);       //video_full_range_flag
            UINT colour_description_present_flag = u(bs, 1);
            if (colour_description_present_flag) {
                u(bs, 8);       //colour_primaries
                u(bs, 8);       //transfer_characteristics
                u(bs, 8);       //matrix_coefficients
            }
        }
    
        UINT chroma_loc_info_present_flag = u(bs, 1);
        if (chroma_loc_info_present_flag) {
            ue(bs);     //chroma_sample_loc_type_top_field
            ue(bs);     //chroma_sample_loc_type_bottom_field
        }
    
        UINT timing_info_present_flag = u(bs, 1);
        if (timing_info_present_flag) {
            UINT num_units_in_tick = u(bs, 32);
            UINT time_scale = u(bs, 32);
            UINT fixed_frame_rate_flag = u(bs, 1);
    
            info->fps = (UINT)((float)time_scale / (float)num_units_in_tick);
            if (fixed_frame_rate_flag) {
                info->fps = info->fps/2;
            }
        }
    
        UINT nal_hrd_parameters_present_flag = u(bs, 1);
        if (nal_hrd_parameters_present_flag) {
            //hrd_parameters()  //see E.1.2 HRD parameters syntax
        }
    
        //后面代码需要hrd_parameters()函数接口实现才有用
        UINT vcl_hrd_parameters_present_flag = u(bs, 1);
        if (vcl_hrd_parameters_present_flag) {
            //hrd_parameters()
        }
        if (nal_hrd_parameters_present_flag || vcl_hrd_parameters_present_flag) {
            u(bs, 1);   //low_delay_hrd_flag
        }
    
        u(bs, 1);       //pic_struct_present_flag
        UINT bitstream_restriction_flag = u(bs, 1);
        if (bitstream_restriction_flag) {
            u(bs, 1);   //motion_vectors_over_pic_boundaries_flag
            ue(bs);     //max_bytes_per_pic_denom
            ue(bs);     //max_bits_per_mb_denom
            ue(bs);     //log2_max_mv_length_horizontal
            ue(bs);     //log2_max_mv_length_vertical
            ue(bs);     //max_num_reorder_frames
            ue(bs);     //max_dec_frame_buffering
        }
    }
    
    //See 7.3.1 NAL unit syntax
    //See 7.3.2.1.1 Sequence parameter set data syntax
    INT h264_parse_sps(const BYTE *data, UINT dataSize, sps_info_struct *info)
    {
        if (!data || dataSize <= 0 || !info) return 0;
        INT ret = 0;
    
        BYTE *dataBuf = malloc(dataSize);
        memcpy(dataBuf, data, dataSize);        //重新拷贝一份数据,防止移除竞争码时对原数据造成影响
        del_emulation_prevention(dataBuf, &dataSize);
    
        sps_bit_stream bs = {0};
        sps_bs_init(&bs, dataBuf, dataSize);   //初始化SPS数据流结构体
    
        u(&bs, 1);      //forbidden_zero_bit
        u(&bs, 2);      //nal_ref_idc
        UINT nal_unit_type = u(&bs, 5);
    
        if (nal_unit_type == 0x7) {     //Nal SPS Flag
            info->profile_idc = u(&bs, 8);
            u(&bs, 1);      //constraint_set0_flag
            u(&bs, 1);      //constraint_set1_flag
            u(&bs, 1);      //constraint_set2_flag
            u(&bs, 1);      //constraint_set3_flag
            u(&bs, 1);      //constraint_set4_flag
            u(&bs, 1);      //constraint_set4_flag
            u(&bs, 2);      //reserved_zero_2bits
            info->level_idc = u(&bs, 8);
    
            ue(&bs);    //seq_parameter_set_id
    
            UINT chroma_format_idc = 1;     //摄像机出图大部分格式是4:2:0
            if (info->profile_idc == 100 || info->profile_idc == 110 || info->profile_idc == 122 ||
                info->profile_idc == 244 || info->profile_idc == 44 || info->profile_idc == 83 ||
                info->profile_idc == 86 || info->profile_idc == 118 || info->profile_idc == 128 ||
                info->profile_idc == 138 || info->profile_idc == 139 || info->profile_idc == 134 || info->profile_idc == 135) {
                chroma_format_idc = ue(&bs);
                if (chroma_format_idc == 3) {
                    u(&bs, 1);      //separate_colour_plane_flag
                }
    
                ue(&bs);        //bit_depth_luma_minus8
                ue(&bs);        //bit_depth_chroma_minus8
                u(&bs, 1);      //qpprime_y_zero_transform_bypass_flag
                UINT seq_scaling_matrix_present_flag = u(&bs, 1);
                if (seq_scaling_matrix_present_flag) {
                    UINT seq_scaling_list_present_flag[8] = {0};
                    for (INT i=0; i<((chroma_format_idc != 3)?8:12); i++) {
                        seq_scaling_list_present_flag[i] = u(&bs, 1);
                        if (seq_scaling_list_present_flag[i]) {
                            if (i < 6) {    //scaling_list(ScalingList4x4[i], 16, UseDefaultScalingMatrix4x4Flag[I])
                            } else {    //scaling_list(ScalingList8x8[i − 6], 64, UseDefaultScalingMatrix8x8Flag[i − 6] )
                            }
                        }
                    }
                }
            }
    
            ue(&bs);        //log2_max_frame_num_minus4
            UINT pic_order_cnt_type = ue(&bs);
            if (pic_order_cnt_type == 0) {
                ue(&bs);        //log2_max_pic_order_cnt_lsb_minus4
            } else if (pic_order_cnt_type == 1) {
                u(&bs, 1);      //delta_pic_order_always_zero_flag
                se(&bs);        //offset_for_non_ref_pic
                se(&bs);        //offset_for_top_to_bottom_field
    
                UINT num_ref_frames_in_pic_order_cnt_cycle = ue(&bs);
                INT *offset_for_ref_frame = (INT *)malloc((UINT)num_ref_frames_in_pic_order_cnt_cycle * sizeof(INT));
                for (INT i = 0; i<num_ref_frames_in_pic_order_cnt_cycle; i++) {
                    offset_for_ref_frame[i] = se(&bs);
                }
                free(offset_for_ref_frame);
            }
    
            ue(&bs);      //max_num_ref_frames
            u(&bs, 1);      //gaps_in_frame_num_value_allowed_flag
    
            UINT pic_width_in_mbs_minus1 = ue(&bs);     //第36位开始
            UINT pic_height_in_map_units_minus1 = ue(&bs);      //47
            UINT frame_mbs_only_flag = u(&bs, 1);
    
            info->width = (INT)(pic_width_in_mbs_minus1 + 1) * 16;
            info->height = (INT)(2 - frame_mbs_only_flag) * (pic_height_in_map_units_minus1 + 1) * 16;
    
            if (!frame_mbs_only_flag) {
                u(&bs, 1);      //mb_adaptive_frame_field_flag
            }
    
            u(&bs, 1);     //direct_8x8_inference_flag
            UINT frame_cropping_flag = u(&bs, 1);
            if (frame_cropping_flag) {
                UINT frame_crop_left_offset = ue(&bs);
                UINT frame_crop_right_offset = ue(&bs);
                UINT frame_crop_top_offset = ue(&bs);
                UINT frame_crop_bottom_offset= ue(&bs);
    
                //See 6.2 Source, decoded, and output picture formats
                INT crop_unit_x = 1;
                INT crop_unit_y = 2 - frame_mbs_only_flag;      //monochrome or 4:4:4
                if (chroma_format_idc == 1) {   //4:2:0
                    crop_unit_x = 2;
                    crop_unit_y = 2 * (2 - frame_mbs_only_flag);
                } else if (chroma_format_idc == 2) {    //4:2:2
                    crop_unit_x = 2;
                    crop_unit_y = 2 - frame_mbs_only_flag;
                }
    
                info->width -= crop_unit_x * (frame_crop_left_offset + frame_crop_right_offset);
                info->height -= crop_unit_y * (frame_crop_top_offset + frame_crop_bottom_offset);
            }
    
            UINT vui_parameters_present_flag = u(&bs, 1);
            if (vui_parameters_present_flag) {
                vui_para_parse(&bs, info);
            }
    
            ret = 1;
        }
        free(dataBuf);
    
        return ret;
    }
    
    更多相关内容
  • 该资源为解析h264文件,保存为图片,ffmpeg编译注意版本问题以及链接库顺序,可直接使用,解决opencv无法解析h264文件问题。
  • gb28181-ps解析h264.rar

    2020-07-01 10:35:53
    基于jrtp,gb28181视频流,从ps流中解析提取h264保存成文件。
  • h264-解析解析 h264 比特流 nalu
  • C++ 解析H264文件

    2016-12-21 15:20:35
    C++ 解析H264文件.vs2012
  • 解析h264 sps c源码

    2019-03-15 10:38:16
    H264的sps帧完整解析代码,包含测试代码,实际环境测试通过
  • ps解析H264

    2018-05-26 16:19:32
    把示例采用VS2017开发,C#语言实现PS流解析H264数据 可以参考博客:https://blog.csdn.net/g0415shenw/article/details/80385088
  • H264解码器Python模块 该项目的目的是为Raspberry Pi摄像机捕获的视频提供一种简单的解码器。 在撰写本文时,我仅需要H264解码,因为RPi软件提供了H264流。 此外,希望以各种方式灵活地将解码器合并到较大的python...
  • flv解析h264

    2018-01-18 16:54:13
    将flv文件的的h264,aac数据提取出来,非常好用,适合了解flv的数据结构,
  • 此资源转载于pudn,经本学渣亲测可用。嘿嘿
  • 可以读取H264文件,并从中提取每一帧(是完整一帧,不是NAL)的数据,并能获得视频的分辨率。
  • H264的sps帧解析出宽高等信息包含c/c++两种语言,可以使用。
  • GB28181学习之路——PS流解析H264

    千次阅读 2020-07-01 10:31:00
    磕磕绊绊的做了出来,也算为自己留个资料吧。先讲理论再上代码。...2. 找到ps包之后就要从它的格式入手开始解析,ps荷载h264是把一帧帧的数据打包传过来,一个完整的ps包会包含一帧的数据。 而h264的帧分为 i 帧和

    磕磕绊绊的做了出来,也算为自己留个资料吧。先讲理论再上代码。挑些重点讲。

    1. 首先就是获取到 rtp 包,rtp包的结构是:rtp包头+payload,payload就是我们要的ps包,rtp包头的长度是12个字节,所以rtp包去掉前12字节就是ps包了。

    比如这个 rtp 包,跳过12个字节,从00 00 01 ba 开始就是ps包了。

    2. 找到ps包之后就要从它的格式入手开始解析,ps荷载h264是把一帧帧的数据打包传过来,一个完整的ps包会包含一帧的数据。

    而h264的帧分为 i 帧和 p 帧,i 帧的结构是 ps header + ps system header + ps system map + pes + h264 (+ pes + 音频)。

    p 帧的结构为 ps header + pes + h264 (+ pes + 音频)。

    3. 首先我们要找到第一个 i 帧,找到ps头 00 00 01 ba,ps头长度为14个字节,最后一个字节的后三位的数字标识扩展字节,

    这里最后一个字节是 fe ,所以跳过6个字节,到00 00 01 bb。

    4. 00 00 01 bb标识 ps system header,它后面的两个字节共同表示ps system header的长度,注意是表示这之后的数据长度,

    比如这里ps system header 表示长度的两个字节是 00 12,换算成十进制,就是后面还有18个字节,跳过这18个字节就到了 00 00 01 bc。

    5. 00 00 01 bc表示ps system map,它后面的两个字节代表ps system map 的长度。也是这之后的数据长度。

    比如这个长度是 00 5e,跳过这 94 个字节就到了 00 00 01 e0,

    6,00 00 01 e0就是我们要找的pes 数据包,它后面的两个字节表示pes包剩余数据的长度,跳过两个字节的下一个字节代码pes包的扩展长度。

    比如这里 00 22表示后面还有 34个字节,08表示有8个扩展字节,扩展字节之后就是h264的数据,h264数据的长度为pes的长度减去到扩展字节的长度再减去扩展字节。也就是34-3-8 = 23个字节。

    如图我们就取到了一个h264的数据了。看到后面又是00 00 01 e0开始的,所以又是一个视频帧。循环往复即可。

    下面上代码,这里我使用的是 jrtp,方便获取 rtp 包,测试过海康和大华的两款设备都是没问题的。

    代码已上传CSDN:https://download.csdn.net/download/qq_39805297/12566110

    MyRTPSession.h

    #ifndef MYRTPSESSION_H
    #define MYRTPSESSION_H
    
    #include <jrtplib3/rtpsession.h>
    #include <jrtplib3/rtppacket.h>
    #include <jrtplib3/rtpudpv4transmitter.h>
    #include <jrtplib3/rtpipv4address.h>
    #include <jrtplib3/rtpsessionparams.h>
    #include <jrtplib3/rtperrors.h>
    #include <jrtplib3/rtpsourcedata.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <iostream>
    #include <string>
    
    using namespace jrtplib;
    //
    // The new class routine
    //
    
    class MyRTPSession : public RTPSession
    {
    protected:
    	void OnNewSource(RTPSourceData *dat)
    	{
    		if (dat->IsOwnSSRC())
    			return;
    
    		uint32_t ip;
    		uint16_t port;
    
    		if (dat->GetRTPDataAddress() != 0)
    		{
    			const RTPIPv4Address *addr = (const RTPIPv4Address *)(dat->GetRTPDataAddress());
    			ip = addr->GetIP();
    			port = addr->GetPort();
    		}
    		else if (dat->GetRTCPDataAddress() != 0)
    		{
    			const RTPIPv4Address *addr = (const RTPIPv4Address *)(dat->GetRTCPDataAddress());
    			ip = addr->GetIP();
    			port = addr->GetPort() - 1;
    		}
    		else
    			return;
    
    		RTPIPv4Address dest(ip, port);
    		AddDestination(dest);
    
    		struct in_addr inaddr;
    		inaddr.s_addr = htonl(ip);
    		std::cout << "Adding destination " << std::string(inet_ntoa(inaddr)) << ":" << port << std::endl;
    	}
    
    	void OnBYEPacket(RTPSourceData *dat)
    	{
    		if (dat->IsOwnSSRC())
    			return;
    
    		uint32_t ip;
    		uint16_t port;
    
    		if (dat->GetRTPDataAddress() != 0)
    		{
    			const RTPIPv4Address *addr = (const RTPIPv4Address *)(dat->GetRTPDataAddress());
    			ip = addr->GetIP();
    			port = addr->GetPort();
    		}
    		else if (dat->GetRTCPDataAddress() != 0)
    		{
    			const RTPIPv4Address *addr = (const RTPIPv4Address *)(dat->GetRTCPDataAddress());
    			ip = addr->GetIP();
    			port = addr->GetPort() - 1;
    		}
    		else
    			return;
    
    		RTPIPv4Address dest(ip, port);
    		DeleteDestination(dest);
    
    		struct in_addr inaddr;
    		inaddr.s_addr = htonl(ip);
    		std::cout << "Deleting destination " << std::string(inet_ntoa(inaddr)) << ":" << port << std::endl;
    	}
    
    	void OnRemoveSource(RTPSourceData *dat)
    	{
    		if (dat->IsOwnSSRC())
    			return;
    		if (dat->ReceivedBYE())
    			return;
    
    		uint32_t ip;
    		uint16_t port;
    
    		if (dat->GetRTPDataAddress() != 0)
    		{
    			const RTPIPv4Address *addr = (const RTPIPv4Address *)(dat->GetRTPDataAddress());
    			ip = addr->GetIP();
    			port = addr->GetPort();
    		}
    		else if (dat->GetRTCPDataAddress() != 0)
    		{
    			const RTPIPv4Address *addr = (const RTPIPv4Address *)(dat->GetRTCPDataAddress());
    			ip = addr->GetIP();
    			port = addr->GetPort() - 1;
    		}
    		else
    			return;
    
    		RTPIPv4Address dest(ip, port);
    		DeleteDestination(dest);
    
    		struct in_addr inaddr;
    		inaddr.s_addr = htonl(ip);
    		std::cout << "Deleting destination " << std::string(inet_ntoa(inaddr)) << ":" << port << std::endl;
    	}
    };
    
    #endif //MYRTPSESSION_H

    main.cpp

    #include "myrtpsession.h"
    
    #define PS_BUFF_SIZE 4096000
    #define SAVE_PS_FILE 1
    #define SAVE_H264_FILE 1
    
    //
    // This function checks if there was a RTP error. If so, it displays an error
    // message and exists.
    //
    
    uint8_t *_frameBuff;
    int _frameSize;
    int _buffLen;
    FILE* fp_h264;
    
    void checkerror(int rtperr)
    {
    	if (rtperr < 0)
    	{
    		std::cout << "ERROR: " << RTPGetErrorString(rtperr) << std::endl;
    		exit(-1);
    	}
    }
    
    void writeH264Frame()
    {
    	printf("write frame size=%d\n", _buffLen);
    	if (_frameSize != _buffLen)
    		printf("error:frameSize=%d bufflen=%d\n", _frameSize, _buffLen);
    #if SAVE_H264_FILE
    	fwrite(_frameBuff, 1, _buffLen, fp_h264);
    #endif
    	memset(_frameBuff, 0, sizeof(_frameBuff));
    	_buffLen = 0;
    	_frameSize = 0;
    }
    
    void getH264Frame(uint8_t* payloadData, int payloadLength)
    {
    	int pos = 0;
    	memset(_frameBuff, 0, sizeof(_frameBuff));
    	_buffLen = 0;
    	 _frameSize = 0;
    	/******  统一 帧  ******/
    	while (payloadData[pos] == 0x00 && payloadData[pos+1] == 0x00 && 
            payloadData[pos+2] == 0x01 && payloadData[pos+3] == 0xe0)
    	{
    		uint16_t h264_size = payloadData[pos+4] << 8 | payloadData[pos+5];
    		uint8_t expand_size = payloadData[pos+8];
    		_frameSize = h264_size - 3 - expand_size;
    		pos += 9 + expand_size;
    		//全部写入并保存帧
    		if (_frameSize <= payloadLength - pos)
    		{
    			{
    				memcpy(_frameBuff, payloadData + pos, _frameSize);
    				_buffLen += _frameSize;
    				pos += _frameSize;
    				writeH264Frame();
    			}
    			if (pos >= payloadLength)
    				break;
    		}
    		else
    		{
    			memcpy(_frameBuff, payloadData + pos, payloadLength - pos);
    			_buffLen += payloadLength - pos;
    			printf("Frame size:%d\n", _frameSize);
    			break;
    		}
    	}
    }
    
    void getH264FromPacket(uint8_t* payloadData, int payloadLength)
    {
    	int pos = 0;
        uint8_t expand_size = payloadData[13] & 0x07;//扩展字节
        pos += 14 + expand_size;//ps包头14
        /******  i 帧  ******/
        if (payloadData[pos] == 0x00 && payloadData[pos + 1] == 0x00 && 
            payloadData[pos + 2] == 0x01 && payloadData[pos + 3] == 0xbb)
        {//ps system header
    	    uint16_t psh_size = payloadData[pos + 4] << 8 | payloadData[pos + 5];//psh长度
    	    pos += 6 + psh_size;
    	    if (payloadData[pos] == 0x00 && payloadData[pos + 1] == 0x00 && 
                payloadData[pos + 2] == 0x01 && payloadData[pos + 3] == 0xbc)
    	    {//ps system map
    		    uint16_t psm_size = payloadData[pos + 4] << 8 | payloadData[pos + 5];
    		    pos += 6 + psm_size;
    		}
    	    else
    	    {
    		    printf("no system map and no video stream\n");
    		    return;
    	    }
    	}
        getH264Frame(payloadData + pos, payloadLength - pos);
    }
    
    
    void executeProcess(int port, int secs)
    {
    #ifdef RTP_SOCKETTYPE_WINSOCK
    	WSADATA dat;
    	WSAStartup(MAKEWORD(2, 2), &dat);
    #endif // RTP_SOCKETTYPE_WINSOCK
    
    #if SAVE_PS_FILE
    	FILE* fp_ps = fopen("gb.ps", "w");
    #endif // SAVE_PS_FILE
    #if SAVE_H264_FILE
    	fp_h264 = fopen("gb.h264", "w");
    #endif // SAVE_H264_FILE
    
    	MyRTPSession sess;
    	std::string ipstr;
    	int status, j;
    
    	// Now, we'll create a RTP session, set the destination
    	// and poll for incoming data.
    
    	RTPUDPv4TransmissionParams transparams;
    	RTPSessionParams sessparams;
    
    	// IMPORTANT: The local timestamp unit MUST be set, otherwise
    	//            RTCP Sender Report info will be calculated wrong
    	// In this case, we'll be just use 8000 samples per second.
    	sessparams.SetOwnTimestampUnit(1.0 / 8000.0);
    
    	sessparams.SetAcceptOwnPackets(true);
    	transparams.SetPortbase(port);
    	status = sess.Create(sessparams, &transparams);
    	checkerror(status);
    	_frameBuff = new uint8_t[PS_BUFF_SIZE];
    	memset(_frameBuff, 0, sizeof(_frameBuff));
    	_frameSize = 0;
    	_buffLen = 0;
    
    	for (j = 1; j <= secs; j++)
    	{
    		sess.BeginDataAccess();
    		printf("secs gone %d\n", j);
    		// check incoming packets
    		if (sess.GotoFirstSourceWithData())
    		{
    			do
    			{
    				RTPPacket *pack;
    				while ((pack = sess.GetNextPacket()) != NULL)
    				{
    					printf("Got packet\n");
    					// You can examine the data here
    					if (pack->GetPayloadType() == 96)
    					{
    #if SAVE_PS_FILE
    						fwrite(pack->GetPayloadData(), 1, pack->GetPayloadLength(), fp_ps);
    #endif
    						//查找ps头 0x000001BA
    						if (pack->GetPayloadData()[0] == 0x00 && pack->GetPayloadData()[1] == 0x00 &&
    							pack->GetPayloadData()[2] == 0x01 && pack->GetPayloadData()[3] == 0xba)
    						{
    							getH264FromPacket(pack->GetPayloadData(), pack->GetPayloadLength());
    						}
                            else if (pack->GetPayloadData()[0] == 0x00 && pack->GetPayloadData()[1] == 0x00 &&
    							pack->GetPayloadData()[2] == 0x01 && pack->GetPayloadData()[3] == 0xe0)
                            {
                                getH264Frame(pack->GetPayloadData(), pack->GetPayloadLength());
                            }
    						else  //当然如果开头不是0x000001BA,默认为一个帧的中间部分,我们将这部分内存顺着帧的开头向后存储
    						{
    							//排除音频和私有数据
    							if (pack->GetPayloadData()[0] == 0x00 && pack->GetPayloadData()[1] == 0x00 &&
    								pack->GetPayloadData()[2] == 0x01 && pack->GetPayloadData()[3] == 0xc0)
    							{ 
    							}
    							else if (pack->GetPayloadData()[0] == 0x00 && pack->GetPayloadData()[1] == 0x00 &&
    								pack->GetPayloadData()[2] == 0x01 && pack->GetPayloadData()[3] == 0xbd)
    							{ 
    							}
    							else   //这是正常的帧数据,像贪吃蛇一样,将它放在帧开头的后边
    							{
    								if (pack->GetPayloadLength() + _buffLen >= _frameSize)
    								{
    									int len = _frameSize - _buffLen;
    									memcpy(_frameBuff + _buffLen, pack->GetPayloadData(), len);
    									_buffLen += len;
    									writeH264Frame();
    									if (pack->GetPayloadLength() > len)
    										getH264FromPacket(pack->GetPacketData() + len, pack->GetPacketLength() - len);
    								}
    								else
    								{
    									memcpy(_frameBuff + _buffLen, pack->GetPayloadData(), pack->GetPayloadLength());
    									_buffLen += pack->GetPayloadLength();
    								}
    							}
    						}
    					}
    					// we don't longer need the packet, so
    					// we'll delete it
    					sess.DeletePacket(pack);
    				}
    			} while (sess.GotoNextSourceWithData());
    		}
    
    		sess.EndDataAccess();
    
    #ifndef RTP_SUPPORT_THREAD
    		status = sess.Poll();
    		checkerror(status);
    #endif // RTP_SUPPORT_THREAD
    
    		RTPTime::Wait(RTPTime(1, 0));
    	}
    
    	sess.BYEDestroy(RTPTime(10, 0), 0, 0);
    
    #ifdef RTP_SOCKETTYPE_WINSOCK
    	WSACleanup();
    #endif // RTP_SOCKETTYPE_WINSOCK
    #if SAVE_PS_FILE
    	fclose(fp_ps);
    #endif
    #if SAVE_H264_FILE
    	fclose(fp_h264);
    #endif
    	printf("StreamReciever exits\n");
    }
    
    int main()
    {
        executeProcess(19444, 100);
        return 0;
    }

     

    展开全文
  • 3.提取FLV文件的H264视频流,保存到.h264文件中 4.提取FLV文件的AAC音频流,保存到.aac文件中 代码经过测试可以以上功能都可以实现。 使用暴风影音可以播放成功提取的aac文件 使用迅雷看看可以播放成功提取的H264...
  • 主要为大家详细介绍了Java解码H264格式视频流中的图片,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • H264解析工具

    2018-06-03 12:36:06
    win32控制台程序,H264裸流解析,丢包测试等功能,详情和使用说明移步:https://blog.csdn.net/u013752202
  • 该程序完成从XXX.h264编码文件中解析出视频的基本单元NALU,NALU是H.264码流解码的基本单元。NALU包括起始码和数据部分,对于起始码,程序可以解析出0x000001或者0x00000001开头的NALU。程序由main()、int parse_h...
  • 前文 《简单解析海康PS流获取H264》 针对海康摄像头的PS流解析做了简单处理,基本逻辑是正确的,但最近几个摄像头出现了一些奇怪的问题,需要针对相关现象进行查询。RTP传输层项目中为了可靠处理且因其他原因使用了...

    前文 《简单解析海康PS流获取H264》 针对海康摄像头的PS流解析做了简单处理,基本逻辑是正确的,但最近几个摄像头出现了一些奇怪的问题,需要针对相关现象进行查询。

    RTP传输层

    项目中为了可靠处理且因其他原因使用了TCP来传输28181的RTP数据,根据 rfc4571 记录,使用TCP传输的时候只需要在RTP包之前打上2个字节的长度,用来控制,毕竟TCP是流式传输,只能靠长度字段进行包的区分。

    因此使用了TCP方式的RTP看起来问题不大,简单处理下就可以提取出PS数据了,事实也基本如此。但是出问题的摄像头却经常在解析RTP的时候出错,即判断RTP标志和SSRC出错,证明不是RTP包或者说包错乱。经过调试,打印出RTP头的所有信息,确实是在某个包后就出错了。打开二进制进行查看,发现出错之后往后部分数据之后,还能发现新的RTP包,看来是丢数据了。

    将抓包数据写个测试程序测试,测试程序在解析RTP出错之后,往后查新的RTP包(主要靠SSRC),然后打印每个包的时间戳和序列号,发现确实是中间丢了几个包,TCP还能丢包?再看看日志发现,出现问题的摄像头都是上传速度非常慢的摄像头,即每秒仅能上传几帧视频的,而且丢包丢的都是I帧,毕竟I帧非常大。

    因此猜测摄像头本身逻辑可能有点问题,即内部编码数据后,可能累积了大量未发送的数据,然后进行了内部丢包,而且这个丢包应该并没有按照一个RTP包去丢,而是直接丢弃的缓存数据,因而造成了上述问题。当然这只是根据现象的猜测,至于具体嘛,咱也不知道,咱也没有问。

    还有一个现象是,这种丢包发生一会之后网络连接就被摄像头给断了。因为暂时不确定其他推流速度正常的摄像头是否会偶发这种现象,因此暂时任务可能偶发,针对性的类似测试程序一样,出现问题了通过查找下一个包来缓解挽救。但这由于丢包肯定会导致解析媒体数据出现错误,也就是下面的问题。

    PS解析的错误处理

    网络丢包造成的无法解析

    如上,在丢包后,肯定会造成数据无法解析,这个时候肯定不能再继续处理了,肯定要结束当前的解析状态。也就是要上面网络层的配合,即网络层发现丢包之后要反馈给PS解析,让PS解析器及时的停止和重置,具体说来,大概是如果当前已经解析了一些媒体数据了,则提交这些数据,等待下一个PS包的到来(0x000001ba)。

    PS数据格式错误?

    在没有丢包的情况下,在解析PS的时候依然出现了问题,其格式大概如下

    e00f4b21e71b792d61b55b2c1ead81dc.png

    如上 0x000372F0 偏移处是前一个PES包(大小65478),里面解析了一个I帧0x000372FC,然后下一个PES包是 0x0004C72C 偏移处。二者偏移远远超过了 0xFFFF 大小,而PES的大小就是两个字节表示的。那么这就意味着格式有误,同时在RTP层解析确认此处的RTP流是顺序的,是没有丢包发生的。

    虽然这个软件能解析出来后续的,但确实是对不上格式定义的,那这个也是一个坑。这个地方我也打算同上处理,解析的时候发生错误时候,查找下一个 0x000001XX 即可。同时也会兼顾RTP层的包及新的PS包 0x000001BA 出现。

    其他问题

    PS中媒体的真正时间戳

    一般一个PS中存在了一个时间戳的视频包和一个时间戳的音频包,但是这个音频包和视频包可能时间戳是不相同的,为了表示准确,解析的时候尽量是按照时间戳变化后提交一次数据,无论是音频还是视频,基本上时间戳就没问题了。

    展开全文
  • H264解析

    2022-05-28 10:31:03
    H264的结构: H264是由一个个NALU组成,NALU之间通过分隔符进行分割,一般为0x000001(3Byte)或者0x00000001(4Byte),一般在NALU开始的时候使用0x00000001(4Byte)。 NALU NALU NALU ...

    H264的结构

    H264是由一个个NALU组成,NALU之间通过分隔符进行分割,一般为0x000001(3Byte)或者0x00000001(4Byte),一般在NALU开始的时候使用0x00000001(4Byte)。

    NALU

    NALU

    NALU

    NALU

    NALU

    NALU

    ……

    SPS

    PPS

    SEI

    I Frame

    P Frame

    B Frame

    ……

    NALU详解

    NALU分为多种:SPS、PPS、SEI、IDR、P Frame、B Frame,其中:

    SPS:Sequence Paramater Set序列参数集,其nal_unit_type为7, SPS中保存了一组视频编码序列的全局参数,序列中每一帧编码后的数据所依赖的参数保存于图像参数集中,一般SPS和PPS位于整个码流的起始位置,但是在某些特殊情况下,在码流中间也会出现这2种结构,如:解码器需要在码流中间开始解码,编码器在编码的过程中改变了码流的参数(如分辨率)。

     

    PPS:Picture Paramater Set图像参数集,其nal_unit_type为8。

     

    SEI:主要起补充和增强的作用,其nal_unit_type为6。

     

    I Frame:关键帧,其nal_unit_type为5,slice_type为7,解码时只需要本帧数据就可以完成。

     

    P Frame:其nal_unit_type为1,slice_type为5,表示本帧与之前的一个关键帧的差别,解码时需要用之前缓存的画面叠加上本帧定义的差别,生成画面。

     

    B Frame:其nal_unit_type为1,slice_type为6,双向差别帧,解码时需要用到之前缓存的画面,同时还需要之后的画面,通过前后画面与本帧数据的叠加获得最终的画面。

     解析代码参考雷神写的即可https://blog.csdn.net/leixiaohua1020/article/details/50534369

     

    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
     
    typedef enum {
    	NALU_TYPE_SLICE    = 1,
    	NALU_TYPE_DPA      = 2,
    	NALU_TYPE_DPB      = 3,
    	NALU_TYPE_DPC      = 4,
    	NALU_TYPE_IDR      = 5,
    	NALU_TYPE_SEI      = 6,
    	NALU_TYPE_SPS      = 7,
    	NALU_TYPE_PPS      = 8,
    	NALU_TYPE_AUD      = 9,
    	NALU_TYPE_EOSEQ    = 10,
    	NALU_TYPE_EOSTREAM = 11,
    	NALU_TYPE_FILL     = 12,
    } NaluType;
     
    typedef enum {
    	NALU_PRIORITY_DISPOSABLE = 0,
    	NALU_PRIRITY_LOW         = 1,
    	NALU_PRIORITY_HIGH       = 2,
    	NALU_PRIORITY_HIGHEST    = 3
    } NaluPriority;
     
     
    typedef struct
    {
    	int startcodeprefix_len;      //! 4 for parameter sets and first slice in picture, 3 for everything else (suggested)
    	unsigned len;                 //! Length of the NAL unit (Excluding the start code, which does not belong to the NALU)
    	unsigned max_size;            //! Nal Unit Buffer size
    	int forbidden_bit;            //! should be always FALSE
    	int nal_reference_idc;        //! NALU_PRIORITY_xxxx
    	int nal_unit_type;            //! NALU_TYPE_xxxx    
    	char *buf;                    //! contains the first byte followed by the EBSP
    } NALU_t;
     
    FILE *h264bitstream = NULL;                //!< the bit stream file
     
    int info2=0, info3=0;
    
    /*
    * 判断是否为0x000001
    */
    static int FindStartCode2 (unsigned char *Buf){
    	if(Buf[0]!=0 || Buf[1]!=0 || Buf[2] !=1) return 0; //0x000001?
    	else return 1;
    }
    /*
    * 判断是否为0x00000001
    */
    static int FindStartCode3 (unsigned char *Buf){
    	if(Buf[0]!=0 || Buf[1]!=0 || Buf[2] !=0 || Buf[3] !=1) return 0;//0x00000001?
    	else return 1;
    }
     
     
    int GetAnnexbNALU (NALU_t *nalu){
    	int pos = 0;
    	int StartCodeFound, rewind;
    	unsigned char *Buf;
     
    	if ((Buf = (unsigned char*)calloc (nalu->max_size , sizeof(char))) == NULL) 
    		printf ("GetAnnexbNALU: Could not allocate Buf memory\n");
     
    	nalu->startcodeprefix_len=3;
     
    	if (3 != fread (Buf, 1, 3, h264bitstream)){
    		free(Buf);
    		return 0;
    	}
    	info2 = FindStartCode2 (Buf);
    	if(info2 != 1) {
    		if(1 != fread(Buf+3, 1, 1, h264bitstream)){
    			free(Buf);
    			return 0;
    		}
    		info3 = FindStartCode3 (Buf);
    		if (info3 != 1){ 
    			free(Buf);
    			return -1;
    		}
    		else {
    			pos = 4;
    			nalu->startcodeprefix_len = 4;
    		}
    	}
    	else{
    		nalu->startcodeprefix_len = 3;
    		pos = 3;
    	}
    	StartCodeFound = 0;
    	info2 = 0;
    	info3 = 0;
    
        // 找到下一个分界标记位
    	while (!StartCodeFound){
    		if (feof (h264bitstream)){
    			nalu->len = (pos-1)-nalu->startcodeprefix_len;
    			memcpy (nalu->buf, &Buf[nalu->startcodeprefix_len], nalu->len);     
    			nalu->forbidden_bit = nalu->buf[0] & 0x80; //1 bit
    			nalu->nal_reference_idc = nalu->buf[0] & 0x60; // 2 bit
    			nalu->nal_unit_type = (nalu->buf[0]) & 0x1f;// 5 bit
    			free(Buf);
    			return pos-1;
    		}
    		Buf[pos++] = fgetc (h264bitstream);
    		info3 = FindStartCode3(&Buf[pos-4]);
    		if(info3 != 1)
    			info2 = FindStartCode2(&Buf[pos-3]);
    		StartCodeFound = (info2 == 1 || info3 == 1);
    	}
     
    	// Here, we have found another start code (and read length of startcode bytes more than we should
    	// have.  Hence, go back in the file
    	rewind = (info3 == 1)? -4 : -3; // 回退Bytes若为0x00000001则需要回退4Bytes,若为0x000001则需要回退3位
     
    	if (0 != fseek (h264bitstream, rewind, SEEK_CUR)){ // 回退到上一个NALU的末尾
    		free(Buf);
    		printf("GetAnnexbNALU: Cannot fseek in the bit stream file");
    	}
     
    	// Here the Start code, the complete NALU, and the next start code is in the Buf.  
    	// The size of Buf is pos, pos+rewind are the number of bytes excluding the next
    	// start code, and (pos+rewind)-startcodeprefix_len is the size of the NALU excluding the start code
     
    	nalu->len = (pos+rewind)-nalu->startcodeprefix_len;
    	memcpy (nalu->buf, &Buf[nalu->startcodeprefix_len], nalu->len);//
    	nalu->forbidden_bit = nalu->buf[0] & 0x80; //1 bit
    	nalu->nal_reference_idc = nalu->buf[0] & 0x60; // 2 bit
    	nalu->nal_unit_type = (nalu->buf[0]) & 0x1f;// 5 bit
    	free(Buf);
     
    	return (pos+rewind);
    }
     
    /**
     * Analysis H.264 Bitstream
     * @param url    Location of input H.264 bitstream file.
     */
    int simplest_h264_parser(char *url){
     
    	NALU_t *n;
    	int buffersize=100000;
     
    	//FILE *myout=fopen("output_log.txt","wb+");
    	FILE *myout=stdout;
     
    	h264bitstream=fopen(url, "rb+");
    	if (h264bitstream==NULL){
    		printf("Open file error\n");
    		return 0;
    	}
     
    	n = (NALU_t*)calloc (1, sizeof (NALU_t));
    	if (n == NULL){
    		printf("Alloc NALU Error\n");
    		return 0;
    	}
     
    	n->max_size=buffersize;
    	n->buf = (char*)calloc (buffersize, sizeof (char));
    	if (n->buf == NULL){
    		free (n);
    		printf ("AllocNALU: n->buf");
    		return 0;
    	}
     
    	int data_offset=0;
    	int nal_num=0;
    	printf("-----+-------- NALU Table ------+---------+\n");
    	printf(" NUM |    POS  |    IDC |  TYPE |   LEN   |\n");
    	printf("-----+---------+--------+-------+---------+\n");
     
    	while(!feof(h264bitstream)) 
    	{
    		int data_lenth;
    		data_lenth=GetAnnexbNALU(n);
     
    		char type_str[20]={0};
    		switch(n->nal_unit_type){
    			case NALU_TYPE_SLICE:sprintf(type_str,"SLICE");break;
    			case NALU_TYPE_DPA:sprintf(type_str,"DPA");break;
    			case NALU_TYPE_DPB:sprintf(type_str,"DPB");break;
    			case NALU_TYPE_DPC:sprintf(type_str,"DPC");break;
    			case NALU_TYPE_IDR:sprintf(type_str,"IDR");break;
    			case NALU_TYPE_SEI:sprintf(type_str,"SEI");break;
    			case NALU_TYPE_SPS:sprintf(type_str,"SPS");break;
    			case NALU_TYPE_PPS:sprintf(type_str,"PPS");break;
    			case NALU_TYPE_AUD:sprintf(type_str,"AUD");break;
    			case NALU_TYPE_EOSEQ:sprintf(type_str,"EOSEQ");break;
    			case NALU_TYPE_EOSTREAM:sprintf(type_str,"EOSTREAM");break;
    			case NALU_TYPE_FILL:sprintf(type_str,"FILL");break;
    		}
    		char idc_str[20]={0};
    		switch(n->nal_reference_idc>>5){
    			case NALU_PRIORITY_DISPOSABLE:sprintf(idc_str,"DISPOS");break;
    			case NALU_PRIRITY_LOW:sprintf(idc_str,"LOW");break;
    			case NALU_PRIORITY_HIGH:sprintf(idc_str,"HIGH");break;
    			case NALU_PRIORITY_HIGHEST:sprintf(idc_str,"HIGHEST");break;
    		}
     
    		fprintf(myout,"%5d| %8d| %7s| %6s| %8d|\n",nal_num,data_offset,idc_str,type_str,n->len);
     
    		data_offset=data_offset+data_lenth;
     
    		nal_num++;
    	}
     
    	//Free
    	if (n){
    		if (n->buf){
    			free(n->buf);
    			n->buf=NULL;
    		}
    		free (n);
    	}
    	return 0;
    }

    展开全文
  • ffmpeg--解析h264

    千次阅读 2017-02-18 15:56:11
    ff_h264_parser是FFmpeg的H.264解析器对应的AVCodecParser结构体。它的定义位于libavcodec\h264_parser.c,如下所示。 AVCodecParser ff_h264_parser = {  .codec_ids = {AV_CODEC_ID_H264 },  .priv_data_size
  • c# VS2017代码 demo 模拟ehome从海康ipc获取的流中提取h264流。设备型号DS-2CD2T10D-I3
  • h264sps解析获取分辨率

    2019-05-17 17:21:47
    h264sps解析获取分辨率
  • Gb28181之Ps流解析H264

    万次阅读 2018-05-20 20:30:54
    本文详细描述如何通过ps流解析H264码流。先研究下PSM(节目流映射),PSM头定义如下:这里找了一个标准的PS流里面的PSM数据进行研究分析:packet_start_code_prefix—24bit :00 00 01map_stream_id-8bit:BCprogram...
  • C++ 读取h264中的nalu

    2022-05-08 13:23:52
    h264帧通常包含多个nalu,当我们...h264的打包格式有2种,一种是Annex-B,另一种是AVCC,本文提供Annex-B的解析方法。本资源为文章附件资源,原文链接:https://blog.csdn.net/u013113678/article/details/123112596
  • h264-工具mkv2mkv : Usage: mkv2mkv input-file output-fileconvert audio/video files with optional H.264 video rencoding and aac audio reencoding.3D H.264 input video (with MVC) can be reencoded to Side ...
  • 解析华为M2391相机RTSP协议中的H264数据流 单独存储视频流中的时间戳 代码: """ A demo python code that .. 1) Connects to an IP cam with RTSP 2) Draws RTP/NAL/H264 packets from the camera 3) Writes them...
  • h264 文件解析

    2022-02-18 21:46:57
    获得 h264 文件 ffmpeg -i test.mp4 -codec copy -bsf: h264_mp4toannexb -f h264 test.264 概述 h264 数据是保存在 NALU 中的,但是 NALU 之间如何进行分割,常用的有两种方式,一种是 annexb模式,其通过在每个 ...
  • h264解析工具

    2018-09-29 08:57:53
    一款可直接对h264码流进行分析的码流工具,有助于新手分析了解码流协议

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 22,171
精华内容 8,868
关键字:

解析h264