【音视频】ffmpeg中AVpacket解析NALU总结

概述

项目中涉及到一些向国标平台推送视频流的逻辑,因之前没有接触过该方面的知识,所以开始走了很多错误的路线,本文主要总结H265 视频中解析出NALU方法

代码实现

输出

[tcp @ 0x55842b7e60] No default whitelist set
[tcp @ 0x55842b7e60] Original list of addresses:
[tcp @ 0x55842b7e60] Address 192.168.1.181 port 554
[tcp @ 0x55842b7e60] Interleaved list of addresses:
[tcp @ 0x55842b7e60] Address 192.168.1.181 port 554
[tcp @ 0x55842b7e60] Starting connection attempt to 192.168.1.181 port 554
[tcp @ 0x55842b7e60] Successfully connected to 192.168.1.181 port 554
[rtsp @ 0x55842ed950] SDP:
v=0
o=- 1741789310577832 1741789310577832 IN IP4 192.168.1.181
s=Media Presentation
e=NONE
b=AS:5050
t=0 0
a=control:rtsp://192.168.1.181:554/Streaming/Channels/101/
m=video 0 RTP/AVP 96
c=IN IP4 0.0.0.0
b=AS:5000
a=recvonly
a=x-dimensions:1920,1080
a=control:rtsp://192.168.1.181:554/Streaming/Channels/101/trackID=1
a=rtpmap:96 H265/90000
a=Media_header:MEDIAINFO=494D4B48010300000400050000000000000000000000000000000000000000000000000000000000;
a=appversion:1.0

[rtsp @ 0x55842ed950] video codec set to: hevc
[rtp @ 0x55842b7cc0] No default whitelist set
[udp @ 0x55842bb400] No default whitelist set
[udp @ 0x55842bb400] end receive buffer size reported is 131072
[udp @ 0x55843010e0] No default whitelist set
[udp @ 0x55843010e0] end receive buffer size reported is 131072
[rtsp @ 0x55842ed950] setting jitter buffer size to 500
[rtsp @ 0x55842ed950] hello state=0
[hevc @ 0x55842f07c0] nal_unit_type: 32(VPS), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] nal_unit_type: 33(SPS), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] nal_unit_type: 34(PPS), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] nal_unit_type: 39(SEI_PREFIX), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] nal_unit_type: 19(IDR_W_RADL), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] Decoding VPS
[hevc @ 0x55842f07c0] Main profile bitstream
[hevc @ 0x55842f07c0] Decoding SPS
[hevc @ 0x55842f07c0] Main profile bitstream
[hevc @ 0x55842f07c0] Decoding VUI
[hevc @ 0x55842f07c0] Decoding PPS
[hevc @ 0x55842f07c0] Decoding SEI
[hevc @ 0x55842f07c0] Skipped PREFIX SEI 229
[AVBSFContext @ 0x55842b7280] nal_unit_type: 32(VPS), nuh_layer_id: 0, temporal_id: 0
[AVBSFContext @ 0x55842b7280] nal_unit_type: 33(SPS), nuh_layer_id: 0, temporal_id: 0
[AVBSFContext @ 0x55842b7280] nal_unit_type: 34(PPS), nuh_layer_id: 0, temporal_id: 0
[AVBSFContext @ 0x55842b7280] nal_unit_type: 39(SEI_PREFIX), nuh_layer_id: 0, temporal_id: 0
[AVBSFContext @ 0x55842b7280] nal_unit_type: 19(IDR_W_RADL), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] nal_unit_type: 32(VPS), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] nal_unit_type: 33(SPS), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] nal_unit_type: 34(PPS), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] nal_unit_type: 39(SEI_PREFIX), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] nal_unit_type: 19(IDR_W_RADL), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] Decoding VPS
[hevc @ 0x55842f07c0] Main profile bitstream
[hevc @ 0x55842f07c0] Decoding SPS
[hevc @ 0x55842f07c0] Main profile bitstream
[hevc @ 0x55842f07c0] Decoding VUI
[hevc @ 0x55842f07c0] Decoding PPS
[hevc @ 0x55842f07c0] Decoding SEI
[hevc @ 0x55842f07c0] Skipped PREFIX SEI 229
[hevc @ 0x55842f07c0] Format yuvj420p chosen by get_format().
[hevc @ 0x55842f07c0] Output frame with POC 0.
[hevc @ 0x55842f07c0] Decoded frame with POC 0.
[hevc @ 0x55842f07c0] nal_unit_type: 32(VPS), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] nal_unit_type: 33(SPS), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] nal_unit_type: 34(PPS), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] Decoding VPS
[hevc @ 0x55842f07c0] Main profile bitstream
[hevc @ 0x55842f07c0] Decoding SPS
[hevc @ 0x55842f07c0] Main profile bitstream
[hevc @ 0x55842f07c0] Decoding VUI
[hevc @ 0x55842f07c0] Decoding PPS
[hevc @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
[rtsp @ 0x55842ed950] All info found
[rtsp @ 0x55842ed950] Setting avg frame rate based on r frame rate
NALU 类型: 32 (32), 大小: 23, PTS: -9223372036854775808, DTS: -9223372036854775808
NALU 类型: 33 (33), 大小: 34, PTS: -9223372036854775808, DTS: -9223372036854775808
NALU 类型: 34 (34), 大小: 7, PTS: -9223372036854775808, DTS: -9223372036854775808
NALU 类型: 39 (39), 大小: 9, PTS: -9223372036854775808, DTS: -9223372036854775808
NALU 类型: 19 (19), 大小: 263700, PTS: -9223372036854775808, DTS: -9223372036854775808
NALU 类型: 1 (1), 大小: 788, PTS: 3600, DTS: 3600
NALU 类型: 1 (1), 大小: 2978, PTS: 7200, DTS: 7200
NALU 类型: 1 (1), 大小: 2220, PTS: 10800, DTS: 10800
NALU 类型: 1 (1), 大小: 2947, PTS: 14400, DTS: 14400
NALU 类型: 1 (1), 大小: 3518, PTS: 18000, DTS: 18000
NALU 类型: 1 (1), 大小: 5597, PTS: 21600, DTS: 21600
NALU 类型: 1 (1), 大小: 8614, PTS: 25200, DTS: 25200
NALU 类型: 1 (1), 大小: 6374, PTS: 28800, DTS: 28800
NALU 类型: 1 (1), 大小: 3811, PTS: 32400, DTS: 32400
NALU 类型: 1 (1), 大小: 7767, PTS: 36000, DTS: 36000
NALU 类型: 1 (1), 大小: 9875, PTS: 39600, DTS: 39600
NALU 类型: 1 (1), 大小: 10074, PTS: 43200, DTS: 43200
NALU 类型: 1 (1), 大小: 10542, PTS: 46800, DTS: 46800
NALU 类型: 1 (1), 大小: 10142, PTS: 50400, DTS: 50400
NALU 类型: 1 (1), 大小: 10576, PTS: 54000, DTS: 54000
NALU 类型: 1 (1), 大小: 10192, PTS: 57600, DTS: 57600
NALU 类型: 1 (1), 大小: 9648, PTS: 61200, DTS: 61200
NALU 类型: 1 (1), 大小: 9878, PTS: 64800, DTS: 64800
NALU 类型: 1 (1), 大小: 9835, PTS: 68400, DTS: 68400
NALU 类型: 1 (1), 大小: 10402, PTS: 72000, DTS: 72000
NALU 类型: 1 (1), 大小: 9714, PTS: 75600, DTS: 75600
[NULL @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
NALU 类型: 1 (1), 大小: 8508, PTS: 79200, DTS: 79200
[NULL @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
NALU 类型: 1 (1), 大小: 8509, PTS: 82800, DTS: 82800
[NULL @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
NALU 类型: 1 (1), 大小: 12812, PTS: 86400, DTS: 86400
[NULL @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
NALU 类型: 1 (1), 大小: 12787, PTS: 90000, DTS: 90000
[NULL @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
NALU 类型: 1 (1), 大小: 17511, PTS: 93600, DTS: 93600
[NULL @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
NALU 类型: 1 (1), 大小: 12669, PTS: 97200, DTS: 97200
[NULL @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
NALU 类型: 1 (1), 大小: 9989, PTS: 100800, DTS: 100800
[NULL @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
NALU 类型: 1 (1), 大小: 12735, PTS: 104400, DTS: 104400
[NULL @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
NALU 类型: 1 (1), 大小: 13694, PTS: 108000, DTS: 108000
[NULL @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
NALU 类型: 1 (1), 大小: 14115, PTS: 111600, DTS: 111600
[NULL @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
NALU 类型: 1 (1), 大小: 13653, PTS: 115200, DTS: 115200
[NULL @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
NALU 类型: 1 (1), 大小: 13454, PTS: 118800, DTS: 118800
[NULL @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
NALU 类型: 1 (1), 大小: 13525, PTS: 122400, DTS: 122400
[NULL @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
NALU 类型: 1 (1), 大小: 16936, PTS: 126000, DTS: 126000
[NULL @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
NALU 类型: 1 (1), 大小: 14288, PTS: 129600, DTS: 129600
[NULL @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
NALU 类型: 1 (1), 大小: 15038, PTS: 133200, DTS: 133200
[NULL @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
NALU 类型: 1 (1), 大小: 14235, PTS: 136800, DTS: 136800
[NULL @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
NALU 类型: 1 (1), 大小: 14089, PTS: 140400, DTS: 140400
[NULL @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
NALU 类型: 1 (1), 大小: 13728, PTS: 144000, DTS: 144000
[NULL @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
NALU 类型: 1 (1), 大小: 19235, PTS: 147600, DTS: 147600
[NULL @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
NALU 类型: 1 (1), 大小: 6320, PTS: 151200, DTS: 151200
[NULL @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
NALU 类型: 1 (1), 大小: 13692, PTS: 154800, DTS: 154800
[NULL @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
NALU 类型: 1 (1), 大小: 17981, PTS: 158400, DTS: 158400
[NULL @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
NALU 类型: 1 (1), 大小: 19105, PTS: 162000, DTS: 162000
[NULL @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
NALU 类型: 1 (1), 大小: 16396, PTS: 165600, DTS: 165600
[NULL @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
NALU 类型: 1 (1), 大小: 18006, PTS: 169200, DTS: 169200
[NULL @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
NALU 类型: 1 (1), 大小: 19030, PTS: 172800, DTS: 172800
[NULL @ 0x55842f07c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
NALU 类型: 1 (1), 大小: 17141, PTS: 176400, DTS: 176400
[NULL @ 0x55842f07c0] nal_unit_type: 32(VPS), nuh_layer_id: 0, temporal_id: 0
[NULL @ 0x55842f07c0] nal_unit_type: 33(SPS), nuh_layer_id: 0, temporal_id: 0
[NULL @ 0x55842f07c0] nal_unit_type: 34(PPS), nuh_layer_id: 0, temporal_id: 0
[NULL @ 0x55842f07c0] nal_unit_type: 39(SEI_PREFIX), nuh_layer_id: 0, temporal_id: 0
[NULL @ 0x55842f07c0] nal_unit_type: 19(IDR_W_RADL), nuh_layer_id: 0, temporal_id: 0
[NULL @ 0x55842f07c0] Decoding VPS
[NULL @ 0x55842f07c0] Main profile bitstream
[NULL @ 0x55842f07c0] Decoding SPS
[NULL @ 0x55842f07c0] Main profile bitstream
[NULL @ 0x55842f07c0] Decoding VUI
[NULL @ 0x55842f07c0] Decoding PPS
[NULL @ 0x55842f07c0] Decoding SEI
[NULL @ 0x55842f07c0] Skipped PREFIX SEI 229
NALU 类型: 32 (32), 大小: 23, PTS: 180000, DTS: 180000
NALU 类型: 33 (33), 大小: 34, PTS: 180000, DTS: 180000
NALU 类型: 34 (34), 大小: 7, PTS: 180000, DTS: 180000
NALU 类型: 39 (39), 大小: 9, PTS: 180000, DTS: 180000
NALU 类型: 19 (19), 大小: 236733, PTS: 180000, DTS: 180000

源码

// g++ -o test test.cpp -lavformat -lavcodec -lavutil
// (测试成功)代码已经成功测试使用ffmpeg拉流AVPacket解析出NALU,H265视频格式专用
extern "C"{
    #include <libavformat/avformat.h>
    #include <libavcodec/avcodec.h>
    #include <libavutil/avutil.h>
    #include <libavutil/opt.h>
}
#include <stdio.h>
#include <stdint.h>

// H265的NALU类型
typedef enum {
    NALU_TYPE_UNDEFINED = -1,
    NALU_TYPE_TRAIL_N    = 0,   
    NALU_TYPE_TRAIL_R    = 1,   
    NALU_TYPE_TSA_N      = 2,   
    NALU_TYPE_TSA_R      = 3,   
    NALU_TYPE_STSA_N     = 4,   
    NALU_TYPE_STSA_R     = 5,   
    NALU_TYPE_RADL_N     = 6,   
    NALU_TYPE_RADL_R     = 7,   
    NALU_TYPE_RASL_N     = 8,   
    NALU_TYPE_RASL_R     = 9,   
    NALU_TYPE_BLA_W_LP   = 16,  
    NALU_TYPE_BLA_W_RADL = 17,  
    NALU_TYPE_BLA_N_LP   = 18,  
    NALU_TYPE_IDR_W_RADL = 19,  
    NALU_TYPE_IDR_N_LP   = 20,  
    NALU_TYPE_CRA_NUT    = 21,  
    NALU_TYPE_RSV_VCL_N10= 22,  
    NALU_TYPE_RSV_VCL_H11= 23,  
    NALU_TYPE_RSV_VCL_V12= 24,  
    NALU_TYPE_RSV_VCL_H13= 25,  
    NALU_TYPE_RSV_VCL_N14= 26,  
    NALU_TYPE_RSV_VCL_R15= 27,  
    NALU_TYPE_VPS        = 32,  
    NALU_TYPE_SPS        = 33,  
    NALU_TYPE_PPS        = 34,  
    NALU_TYPE_AUD        = 35,  
    NALU_TYPE_EOS_NUT    = 36,  
    NALU_TYPE_EOB_NUT    = 37,  
    NALU_TYPE_FD_NUT     = 38,  
    NALU_TYPE_SEI_PREFIX = 39,  
    NALU_TYPE_SEI_SUFFIX = 40,  
    NALU_TYPE_RSV_NVCL41 = 41,  
    NALU_TYPE_RSV_NVCL42 = 42,  
    NALU_TYPE_RSV_NVCL43 = 43,  
    NALU_TYPE_RSV_NVCL44 = 44,  
    NALU_TYPE_RSV_NVCL45 = 45,  
    NALU_TYPE_RSV_NVCL46 = 46,  
    NALU_TYPE_RSV_NVCL47 = 47,  
    NALU_TYPE_UNSPEC48   = 48,  
    NALU_TYPE_FRAGMENT_UNIT = 49 
} NaluType;

// 解析出来H265的NALU类型
NaluType parse_nalu_type(uint8_t *nalu_data) {
    if (nalu_data == NULL) {
        fprintf(stderr, "错误: nalu_data 为 nullptr\n");
        return NALU_TYPE_UNDEFINED;
    }
    // H.265 NALU type is in the first byte of NALU data, bits 1-6 (0-indexed)
    return static_cast<NaluType>((nalu_data[0] >> 1) & 0x3F); // Directly parse NALU type from the first byte
}

// 打印NALU类型
void handle_nalu(uint8_t *nalu_data, int nalu_size, NaluType nalu_type, int64_t pts, int64_t dts) {
    printf("NALU 类型: %d (%d), 大小: %d, PTS: %ld, DTS: %ld\n", nalu_type, (int)nalu_type, nalu_size, pts, dts);
}

// 处理AVPacket
void process_packet(AVPacket *pkt) {
    uint8_t *data = pkt->data;
    int size = pkt->size;
    int startcode_len = 0;

    // Logging (optional, for debugging)
    // printf("AVPacket->data (前 64 字节): ..."); 
    // ...

    while (size > 0) {
        startcode_len = 0; 

        if (size >= 4 && data[0] == 0x00 && data[1] == 0x00 && data[2] == 0x00 && data[3] == 0x01) {
            startcode_len = 4;
            // Logging (optional, for debugging)
            // printf("找到 4 字节起始码,位置偏移: ...");
        } 
        else if (size >= 3 && data[0] == 0x00 && data[1] == 0x00 && data[2] == 0x01) {
            startcode_len = 3;
            // Logging (optional, for debugging)
            // printf("找到 3 字节起始码,位置偏移: ...");
        } 
        else {
            data++;
            size--;
            continue;
        }

        uint8_t *next_startcode = NULL;
        for (int i = startcode_len; i < size - 3; i++) {
            if (data[i] == 0x00 && data[i+1] == 0x00 && data[i+2] == 0x01) {
                next_startcode = data + i;
                break; 
            }
             if (data[i] == 0x00 && data[i+1] == 0x00 && data[i+2] == 0x00 && data[i+3] == 0x01) {
                next_startcode = data + i;
                break; 
            }
        }

        int nalu_size = (next_startcode != NULL) ?
                        (next_startcode - (data + startcode_len)) :
                        (size - startcode_len);

        uint8_t *nalu_data = data + startcode_len; 

        // Logging (optional, for debugging)
        // printf("parse_nalu_type 接收到的数据 (前 8 字节): ...");

        // Corrected call: Pass nalu_data directly
        NaluType nalu_type = parse_nalu_type(nalu_data); 

        handle_nalu(nalu_data, nalu_size, nalu_type, pkt->pts, pkt->dts);

        data += startcode_len + nalu_size;
        size -= startcode_len + nalu_size;
    }
}

void set_ffmpeg_log_level() {
    av_log_set_level(AV_LOG_DEBUG); 
}

int test_rtsp_stream() {
    avformat_network_init();
    set_ffmpeg_log_level();
    AVFormatContext *fmt_ctx = NULL;
    AVPacket *pkt = av_packet_alloc();
    if (!pkt) {
        fprintf(stderr, "无法分配 AVPacket\n");
        return -1;
    }
    const char *rtsp_url = "rtsp://admin:jhx12345@192.168.1.181:554/Streaming/Channels/101"; 
    if (avformat_open_input(&fmt_ctx, rtsp_url, NULL, NULL) < 0) {
        fprintf(stderr, "无法打开输入流: %s\n", rtsp_url);
        av_packet_free(&pkt);
        return -1;
    }
    if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
        fprintf(stderr, "无法获取流信息\n");
        avformat_close_input(&fmt_ctx);
        av_packet_free(&pkt);
        return -1;
    }
    int video_stream_idx = -1;
    for (int i = 0; i < fmt_ctx->nb_streams; i++) {
        if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            video_stream_idx = i;
            break;
        }
    }
    if (video_stream_idx == -1) {
        fprintf(stderr, "未找到视频流\n");
        avformat_close_input(&fmt_ctx);
        av_packet_free(&pkt);
        return -1;
    }
    while (av_read_frame(fmt_ctx, pkt) >= 0) {
        if (pkt->stream_index == video_stream_idx) {
            process_packet(pkt); 
        }
        av_packet_unref(pkt); 
    }
    if (feof(stdin)) {
        printf("到达流末尾\n");
    } else if (ferror(stdin)) {
        fprintf(stderr, "读取流时发生错误\n");
    }
    av_packet_free(&pkt);
    avformat_close_input(&fmt_ctx);
    return 0;
}

// main function - Same as before
int main() {
    if (test_rtsp_stream() < 0) {
        fprintf(stderr, "RTSP 流测试失败\n");
        return -1;
    }
    printf("RTSP 流测试成功\n");
    return 0;
}

分析

avpacket

该结构体中主要用于存储音视频数据,其中的data字段存储着多个NALU数据,下面只说明其重要的字段

typedef struct AVPacket {
    uint8_t *data;         // 数据缓冲区指针,指向编码后的音视频数据
    int size;              // 数据大小
    int64_t pts;           // 该数据包的显示时间戳 (Presentation Time Stamp)
    int64_t dts;           // 解码时间戳 (Decoding Time Stamp)
    ...
} AVPacket;
  • data: 这是指向数据缓冲区的指针,它包含了实际的编码数据(例如,H.265 视频的 NALU 数据)。这个缓冲区可能包含一个完整的 NALU,或者包含多个 NALU 数据。
  • size: 这是 data 中存储的字节数,即数据的大小。它告诉我们这个数据包的总大小。

data字段存储NALU

一个标准的NALU主要是由起始码进行标识,一般是三个或者四个字节的0x000001 0x00000001,通过这些起始码来分割NALU

解析过程分析

核心过程分析

  • 查找起始码,循环寻找001、0001开头的,只要找到则代表找到了一个新的NALU
  • 提取NALU,找到起始码后,提取起始码后面的数据,直到下一个起始码出现
  • 解析NALU,H265中的NALU的规定是其类型存储在第一个自己的高六位

具体解析代码

void process_packet(AVPacket *pkt) {
    uint8_t *data = pkt->data;
    int size = pkt->size;
    int startcode_len = 0;

    while (size > 0) {
        startcode_len = 0; 

        if (size >= 4 && data[0] == 0x00 && data[1] == 0x00 && data[2] == 0x00 && data[3] == 0x01) {
            startcode_len = 4;
        } else if (size >= 3 && data[0] == 0x00 && data[1] == 0x00 && data[2] == 0x01) {
            startcode_len = 3;
        } else {
            data++;
            size--;
            continue;
        }

        uint8_t *next_startcode = NULL;
        for (int i = startcode_len; i < size - 3; i++) {
            if (data[i] == 0x00 && data[i+1] == 0x00 && data[i+2] == 0x01) {
                next_startcode = data + i;
                break;
            }
            if (data[i] == 0x00 && data[i+1] == 0x00 && data[i+2] == 0x00 && data[i+3] == 0x01) {
                next_startcode = data + i;
                break;
            }
        }

        int nalu_size = (next_startcode != NULL) ? (next_startcode - (data + startcode_len)) : (size - startcode_len);
        uint8_t *nalu_data = data + startcode_len;
        NaluType nalu_type = parse_nalu_type(nalu_data); 
        handle_nalu(nalu_data, nalu_size, nalu_type, pkt->pts, pkt->dts);

        data += startcode_len + nalu_size;
        size -= startcode_len + nalu_size;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值