从Wireshark/tcpdump文件中提取rtsp over tcp的H264数据

news/2024/5/17 16:16:51 标签: Wireshark, rtsp, tcp, rtp, h264

       对于从Wireshark抓取的rtsp/tcp数据文件,要想提取出里面的h264码流数据,貌似Wireshark并未提供相关功能选项。无赖之下只有自己动手写一个吧。

     下面是在linux上用 tcpdump -i enp3s0 -c 7000 src 192.168.8.0 -w /home/guoke/test.cap 抓取的test.cap文件在Wireshark中的显示界面截图。

从test.cap文件中提取h264数据的关键是,需要像剥洋葱一样,读取各个网络协议头部。剥离完了后,剩下的就是需要的h264视频数据。大致步骤如下:

1. 读取 cap格式文件 的头部

typedef struct _TCPDUMP_CAP_FILE_HEADER_
{
    int magic; //4bytes [D4 C3 B2 A1] = 0xA1B2C3D4
    int version_major;//2bytes [02 00] = 2
    int version_minor;//2bytes [04 00] = 4
    int time_zone;//4bytes 时区
    int timestamp_accuracy;//4bytes 时间戳精度
    int max_capture_size_per_packet;//4bytes 每个包的抓包的最大值(单位:字节) [00 00 04 00] = 0x40000 = 262144;
    int data_link_layer_type;//4bytes 数据链路层类型 [01 00 00 00] = 1 = LINKTYPE_ETHERNET; https://www.tcpdump.org/linktypes.html
}TCPDUMP_CAP_FILE_HEADER;

2. 读取 以太网帧(Ethernet Frame)(物理层的数据帧)的头部

typedef struct _ETHERNET_FRAME_
{
    unsigned long long timestamp; //8bytes 帧时间戳
    char timestampStr[80]; //帧时间戳 Arrival Time: Nov  1, 2019 15:35:59.430461000 中国标准时间
    int frame_length; //4bytes 帧数据大小(不包含头部本身16字节,单位:字节)一般为 0x05EA = 1514 bytes
    int capture_length; //4bytes 捕获的帧数据大小
}ETHERNET_FRAME;

3. 读取 数据链路层以太网帧 的头部(以太网协议版本II)

typedef struct _ETHERNET_II_HEADER_
{
    char destination_address[50]; //6bytes 目的MAC:厂名_序号(网卡地址) Address: HuaweiTe_70:5c:3c (08:4f:0a:70:5c:3c)
    char source_address[50]; //6bytes 源MAC:厂名_序号(网卡地址) Address: Hangzhou_68:5c:9c (48:7a:da:68:5c:9c)
    int type; //2bytes 帧内封装的上层协议类型(IP=0x0800,ARP=0806,RARP=0835 [TCP-IP详解卷1:协议 图2-1 16页])Type: IPv4 (0x0800)
}ETHERNET_II_HEADER;

4. 读取 互联网层IP包 的头部 [TCP-IP详解卷1:协议 图3-1 24页]

   注意:在此处将只挑选含有rtsp server ip的以太帧数据,其他的以太帧全被过滤掉(即不进行接下来的分析步骤)

typedef struct _INTERNET_PROTOCOL_V4_HEADER_
{
    int version; //4bit 版本 Version: 4
    int ip_header_length; //4bit IP包头部长度 Header Length: 20 bytes (5)
    int differentiated_services_field; //8bit 差分服务字段 Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT)
    int total_length; //16bit IP包的总长度 Total Length: 52
    int identification; //16bit 标志字段 Identification: 0x0000 (0)
    int flags_reserved_1bit; //1bit 标志字段 0 = Reserved bit: Not set
    int flags_do_not_fragment_set_1bit; //1bit 标志字段 .1.. .... .... .... = Don't fragment: Set
    int flags_more_fragments_1bit; //1bit 标志字段 ..0. .... .... .... = More fragments: Not set
    int flags_fragments_offset_13bits; //13bit 标志字段 分段偏移量(将一个IP包分段后传输时,本段的标识)...0 0000 0000 0000 = Fragment offset: 0
    int time_to_live; //8bit 生存期TTL Time to live: 62
    int protocol; //8bit 此包内封装的上层协议 Protocol: TCP (6); UDP(17) https://tools.ietf.org/html/rfc1700 Page7
    int header_checksum; //16bit 头部数据的校验和 Header checksum: 0x326e [validation disabled]
    int source_ip_addr; //32bit 源IP地址 Source: 192.168.8.0
    int destination_ip_addr; //32bit 目的IP地址 Destination: 192.168.8.1
}INTERNET_PROTOCOL_V4_HEADER;

5. 读取 传输层TCP数据段 的头部 [TCP-IP详解卷1:协议 图17-2 172页]

typedef struct _TCP_OPTIONS_
{
    int kind; //8bits TCP选项的类型
    int length; //8bits TCP选项的总长度(单位:byte,包含本字段长度)
    char data[32]; //只有当length大于2时,此字段才有效
}TCP_OPTIONS;

typedef struct _TRANSMISSION_CONTROL_PROTOCOL_HEADER_
{
    int source_port; //16bit 源端口号 Source Port: 554
    int destination_port; //16bit 目的端口号 Destination Port: 55014
    unsigned int sequence_number; //32bit 序列号 Sequence number: 0    (relative sequence number)
    unsigned int next_sequence_number; //32bit 下一个期望的序列号 = sequence_number + tcp_payload_length
    unsigned int acknowledgment_number; //32bit 确认序列号 Acknowledgment number: 1    (relative ack number)
    int tcp_header_length; //4bit 给出头部占32比特的数目。没有任何选项字段的TCP头部长度为20字节(5x32=160比特);最多可以有60字节的TCP头部。 1000 .... = Header Length: 32 bytes (8)
    int flags_reserved_3bit; //3bit 保留字段 000. .... .... = Reserved: Not set
    int flags_nonce_1bit; //1bit 保留字段 ....0 .... .... = Nonce: Not set
    int flags_congestion_window_reduced_1bit; //1bit 拥塞窗口减少 ...... 0... .... = Congestion Window Reduced (CWR): Not set
    int flags_ecn_echo_1bit; //1bit 显式拥塞通知Explicit Congestion Notification .... .0.. .... = ECN-Echo: Not set
    int flags_urgent_1bit; //1bit 紧急指针URG( urgent pointer)有效 .... ..0. .... = Urgent: Not set
    int flags_acknowledgment_1bit; //1bit 确认序号有效ACK .... ...1 .... = Acknowledgment: Set
    int flags_push_1bit; //1bit 接收方应该尽快将这个报文段交给应用层PSH .... .... 0... = Push: Not set
    int flags_reset_1bit; //1bit 重建连接RST .... .... .0.. = Reset: Not set
    int flags_syn_1bit; //1bit 同步序号用来发起一个连接SYN .... .... ..1. = Syn: Set
    int flags_fin_1bit; //1bit 发端完成发送任务FIN .... .... ...0 = Fin: Not set
    int window_size; //16bit 流量控制的窗口大小 Window size value: 29200
    int checksum; //16bit TCP数据段的校验和 Checksum: 0x7f9e [unverified]
    int urgent_pointer; //16bit 紧急指针 Urgent pointer: 0
    int tcp_options_size; //TCP可选项的数目,范围[1,40],等于0时tcp_options[40]字段无效
    TCP_OPTIONS tcp_options[40]; //TCP可选项
    unsigned char *tcp_payload; //TCP有效载荷
    int tcp_payload_length; //注意:一个TCP包的有效载荷可能被RTP人为的分成两个部分,原因是:单个RTP包的大小超出了TCP的最大载荷容量(即1460bytes),这时候超出的部分只有放入下一个TCP包
}TRANSMISSION_CONTROL_PROTOCOL_HEADER;

6. 读取 TCP payload 有效载荷数据中的 RTSP 数据,如果tcp_payload的第一个字符是 '$'=0x24,则表示是RTP包数据。否则是      RTSP的请求信令字符串,比如 "OPTIONS rtsp://192.168.8.0:554/h264/ch1/main/av_stream RTSP/1.0"

typedef struct _RTSP_INTERLEAVED_FRAME_
{
    int magic;//1byte 0x24 => '$'
    int channel; //1byte 0-1
    int rtp_length; //2bytes rtp包发送时单个包的载荷总大小(即不包含 magic、channel、rtp_length 这3个字段所占的4字节),但实际上可能超过TCP最大的1460字节
    int rtp_real_read_bytes_in_single_tcp_packet; //在单个TCP包中实际上读到的字节数目,如果该值小于rtp_length,则剩下的字节需要在下一个TCP包中读取
    unsigned char * interleaved_frame_data;
    RTP_AND_RTCP_INFO rtp_and_rtcp;
}RTSP_INTERLEAVED_FRAME;

7. 读取 RTP数据包 的头部

typedef struct _RTP_HEADER_AND_PAYLOAD_
{
    int rtp_packet_total_size; //该rtp包的总大小(单位:字节)
    int channel; //rtsp通道,偶数为数据通道,比如0-表示视频通道,2-表示音频通道
    int version;//2bits 用来标志使用的RTP版本 10.. .... = Version: RFC 1889 Version (2)
    int padding;//1bit .如果为1,则该RTP包的尾部包含附加的填充字节 1. .... = Padding: True
    int extension;//1bit 如果为1,RTP头部后面有一个扩展头部 ...0 .... = Extension: False
    int contributing_source_identifiers_count;//4bits 头部后面跟着的CSRC的数目 .... 0000 = Contributing source identifiers count: 0
    int marker;//1bit 标记位(1代表一帧数据的结束) 0... .... = Marker: False
    int payload_type;//7bits RTP载荷类型 Payload type: DynamicRTP-Type-96 (96); 96是指h264编码
    unsigned int sequence_number;//16bits 序列号 Sequence number: 48657
    unsigned int timestamp;//32bits 该RTP包中数据的第一个字节的采样时刻 Timestamp: 706810978
    int synchronization_source_identifier;//32bits 同步源标识符(SSRC)指RTP包流的来源 Synchronization Source identifier: 0x45439e03 (1162059267)
    int rtp_header_extension_defined_by_profile;//32bits
    int rtp_header_extension_length;//32bits (长度不包含本身)
    int rtp_payload_size;//RTP包有效载荷大小
    unsigned char *rtp_payload;//RTP包有效载荷 Payload: 420101016000000300b0000003000003007ba003c08010e5...
    int padding_data;//(padding_count - 8)bits 附加的填充字节 Padding data: 0000
    int padding_count;//8bits 附加的填充字节数目(包含自身) Padding count: 3
    H264_DATA h264_data; //h264数据
}RTP_HEADER_AND_PAYLOAD;

8. 读取 RTP payload载荷中的h264视频数据 的头部

typedef struct _H264_DATA_
{
    char start_code[5]; // 00 00 00 01 67
    int start_code_length; // 0 or 5,如果为5,则表示一个h264包的开始,为0表示中间部分或结束部分
    int nal_unit_type_h264; //nal的类型(取值范围:1-23)
    int nal_unit_type_h264_rtp; //nal的rtp类型(1-23时表示的意思和h264的nal_unit_type定义一致)
    unsigned char *h264_sub_packet_data; //一个h264包可能被拆成多个RTP包碎片
    int h264_sub_packet_data_length; //h264碎片大小
}H264_DATA;

注意:RTP对H264打包时,有几种打包方式,比如H264 I 帧数据太大了,则需要分成多个RTP包,或者H264 P帧数据不大,将多个P帧数据打包成单个RTP包

Type   Packet    Type name                        Section
---------------------------------------------------------
0      undefined                                    -
1-23   NAL unit  Single NAL unit packet per H.264   5.6
24     STAP-A    Single-time aggregation packet     5.7.1
25     STAP-B    Single-time aggregation packet     5.7.1
26     MTAP16    Multi-time aggregation packet      5.7.2
27     MTAP24    Multi-time aggregation packet      5.7.2
28     FU-A      Fragmentation unit                 5.8
29     FU-B      Fragmentation unit                 5.8
30-31  undefined        -                           -

---------------------------------------------

工程代码的地址:https://github.com/jfu222/wireshark_rtsp_over_tcp

 


http://www.niftyadmin.cn/n/793044.html

相关文章

记vsftpd虚拟用户登录返回530 Login incorrect解决过程

遇到的问题: 最近需要在Ubuntu18.04上编译并安装vsftpd,从官网http://www.linuxfromscratch.org/blfs/view/svn/server/vsftpd.html下载vsftpd-3.0.3.tar.gz后,用源码编译成功了vsftpd,配置好ftp虚拟用户后,输入用户名…

python如何运行turtle_利用Python 调用turtle函数库 绘制奥运五环。

import turtle #调用turtle库绘制图像的函数库turtle.color("blue") #颜色 蓝色turtle.circle(100) #画半径100的圆turtle.penup() #抬起笔turtle.goto(-180,0) #移动到turtle.pendown() #放笔turtle.color("red") #颜色 红色turtle.circle(100) #画半径10…

对Linux svn保存的明文密码加密

需求来源: 随着GitHub/GitLab的兴起,svn已经渐渐的没落了,从公司当初的源代码管理服务器,逐渐演变成公司的ftp服务器。 最近需要部署gitlab的CI单元测试模块,而软件版本都在svn上有备份,我就希望从代码的提…

pcb文件转成原理图_没基础学员如何快速掌握电子产品技师基础PCB转化为原理图...

【摘要】电子产品维修工是国家职业工种,而各种电子产品都是PCB印刷方式呈现电路结构,如何将PCB电路转化为原理图,再由原理图转化为方框图,只有方框图才能研究测试点关健核心数据,进而实现电子产品维修要求找到的故障元…

WinDbg的学习研究

声明:以下内容仅供研究学习之用,不做其他用途 前段时间想要画一个3D的图,找来找去,找到了unity这个软件,但是从网上下载的版本需要注册,但是我只是需要用一下而已,就不想去走这么麻烦的流程&…

python3运算符整除_盘盘 Python3 运算符

点击上方“Python数据之道”,选择“星标公众号”收藏文章的同时,不要忘记「在看」Python3 运算符什么是运算符?运算符是一种“功能”符号,用于执行程序代码运算,会针对一个以上操作数项目来进行运算。例如:…

python画小猪乔治动画片全集_使用Python的turtle画小猪乔治和哆啦 A 梦!附源码!...

本人是Python新人小白,欢迎各位大佬指正,本文介绍的是用Python的turtle画一只小猪乔治。还有本人画画基础超级差 没天赋,画参考度娘上的一个图片画的。先上总代码,然后说说我写的思路,Python学习群:6833805…

如何在Ubuntu16.04上编译vs2013工程为exe文件

“在现在这种时间就是金钱的时代,程序需要自动化,程序需要自动化,程序需要自动化。” “自动化就是程序员的生产力。不要像码农一样,一辈子把自己的双手双脚绑在自己的一亩三分地上。” -------- 过客 曰 很久很久(大…