概述
项目中涉及到一些向国标平台推送视频流的逻辑,因之前没有接触过该方面的知识,所以开始走了很多错误的路线,本文主要总结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;
}
}