Wireshark提取RTP包中的H264码流

最近对之前用到的东西做整理发现一些有用的东西,在此做下记录,本文参考


http://blog.sina.com.cn/s/blog_3e3601200101e4lp.html


   抓取一个包含H.264 Payload RTP包的SIP会话或RTSP会话后,用WiresharkPlay功能只能播放声音,不能播放视频。把RTP payload直接导出成文件后也是不能直接播放的,因为H.264 over RTP封包是符合RFC3984规范的,必须按照该规范把H.264数据取出来后,组成NALU,放到avi/mp4或裸码流文件等容器里后才能播放。

     本人写了一个wireshark插件,可以在打开包含H.264码流的抓包后,选菜单Tools->Export H264 to file [HQX's plugins]后,把抓包文件里的H.264码流自动导出到抓包文件所在目录(工作目录)里,名为from_<RTP流源ip>_<RTP流源端口>_to_<RTP流目的ip>_<RTP流目的端口>.264264裸码流文件里。(文件格式为每个NALU前加0x00000001分隔符)。

      本程序可以识别RFC3984里提到的三种H.264 over RTP封装,分别是Single NALU(一个RTP含一个NALU)、STAP-A(一个RTP包含多个NALU)、FU-A(一个NALU分布到多个RTP包)三种封装格式,且会自动把SPSPPS放到裸码流文件头部。

Lua脚本如下:

[python] view plaincopy

1         -- Dump RTP h.264 payload to raw h.264 file (*.264) 

2         -- According to RFC3984 to dissector H264 payload of RTP to NALU, and write it 

3         -- to from<sourceIp_sourcePort>to<dstIp_dstPort>.264 file. By now, we support single NALU, 

4         -- STAP-A and FU-A format RTP payload for H.264. 

5         -- You can access this feature by menu "Tools->Export H264 to file [HQX's plugins]" 

6         -- Author: Huang Qiangxiong (qiangxiong.huang@gmail.com) 

7         -- change log: 

8         --      2012-03-13 

9         --          Just can play 

10      ------------------------------------------------------------------------------------------------ 

11      do 

12          -- for geting h264 data (the field's value is type of ByteArray) 

13          local f_h264 = Field.new("h264")  

14        

15          -- menu action. When you click "Tools->Export H264 to file [HQX's plugins]" will run this function 

16          local function export_h264_to_file() 

17              -- window for showing information 

18              local tw = TextWindow.new("Export H264 to File Info Win") 

19              local pgtw = ProgDlg.new("Export H264 to File Process", "Dumping H264 data to file...") 

20                

21              -- add message to information window 

22              function twappend(str) 

23                  tw:append(str) 

24                  tw:append("\n") 

25              end 

26                

27              -- running first time for counting and finding sps+pps, second time for real saving 

28              local first_run = true  

29              -- variable for storing rtp stream and dumping parameters 

30              local stream_infos = {} 

31        

32              -- trigered by all h264 packats 

33              local my_h264_tap = Listener.new(tap, "h264") 

34                

35              -- get rtp stream info by src and dst address 

36              function get_stream_info(pinfo) 

37                  local key = "from_" .. tostring(pinfo.src) .. "_" .. tostring(pinfo.src_port) .. "to" .. tostring(pinfo.dst) .. "_" .. tostring(pinfo.dst_port) 

38                  local stream_info = stream_infos[key] 

39                  if not stream_info then -- if not exists, create one 

40                      stream_info = { } 

41                      stream_info.filename = key.. ".264" 

42                      stream_info.file = io.open(stream_info.filename, "wb") 

43                      stream_info.counter = 0 -- counting h264 total NALUs 

44                      stream_info.counter2 = 0 -- for second time running 

45                      stream_infos[key] = stream_info 

46                      twappend("Ready to export H.264 data (RTP from " .. tostring(pinfo.src) .. ":" .. tostring(pinfo.src_port)  

47                               .. " to " .. tostring(pinfo.dst) .. ":" .. tostring(pinfo.dst_port) .. " to file:\n         [" .. stream_info.filename .. "] ...\n") 

48                  end 

49                  return stream_info 

50              end 

51                

52              -- write a NALU or part of NALU to file. 

53              function write_to_file(stream_info, str_bytes, begin_with_nalu_hdr) 

54                  if first_run then 

55                      stream_info.counter = stream_info.counter + 1 

56                        

57                      if begin_with_nalu_hdr then 

58                          -- save SPS or PPS 

59                          local nalu_type = bit.band(str_bytes:byte(0,1), 0x1F) 

60                          if not stream_info.sps and nalu_type == 7 then 

61                              stream_info.sps = str_bytes 

62                          elseif not stream_info.pps and nalu_type == 8 then 

63                              stream_info.pps = str_bytes 

64                          end 

65                      end 

66                        

67                  else -- second time running 

68                      if stream_info.counter2 == 0 then 

69                          -- write SPS and PPS to file header first 

70                          if stream_info.sps then 

71                              stream_info.file:write("\00\00\00\01") 

72                              stream_info.file:write(stream_info.sps) 

73                          else 

74                              twappend("Not found SPS for [" .. stream_info.filename .. "], it might not be played!\n") 

75                          end 

76                          if stream_info.pps then 

77                              stream_info.file:write("\00\00\00\01") 

78                              stream_info.file:write(stream_info.pps) 

79                          else 

80                              twappend("Not found PPS for [" .. stream_info.filename .. "], it might not be played!\n") 

81                          end 

82                      end 

83                    

84                      if begin_with_nalu_hdr then 

85                          -- *.264 raw file format seams that every nalu start with 0x00000001 

86                          stream_info.file:write("\00\00\00\01") 

87                      end 

88                      stream_info.file:write(str_bytes) 

89                      stream_info.counter2 = stream_info.counter2 + 1 

90                        

91                      if stream_info.counter2 == stream_info.counter then 

92                          stream_info.file:flush() 

93                          twappend("File [" .. stream_info.filename .. "] generated OK!\n") 

94                      end 

95                      -- update progress window's progress bar 

96                      if stream_info.counter > 0 then pgtw:update(stream_info.counter2 / stream_info.counter) end 

97                  end 

98              end 

99                

100           -- read RFC3984 about single nalu/stap-a/fu-a H264 payload format of rtp 

101           -- single NALU: one rtp payload contains only NALU 

102           function process_single_nalu(stream_info, h264) 

103               write_to_file(stream_info, h264:tvb()():string(), true) 

104           end 

105             

106           -- STAP-A: one rtp payload contains more than one NALUs 

107           function process_stap_a(stream_info, h264) 

108               local h264tvb = h264:tvb() 

109               local offset = 1 

110               repeat 

111                   local size = h264tvb(offset,2):uint() 

112                   write_to_file(stream_info, h264tvb(offset+2, size):string(), true) 

113                   offset = offset + 2 + size 

114               until offset >= h264tvb:len() 

115           end 

116             

117           -- FU-A: one rtp payload contains only one part of a NALU (might be begin, middle and end part of a NALU) 

118           function process_fu_a(stream_info, h264) 

119               local h264tvb = h264:tvb() 

120               local fu_idr = h264:get_index(0) 

121               local fu_hdr = h264:get_index(1) 

122               if bit.band(fu_hdr, 0x80) ~= 0 then 

123                   -- start bit is set then save nalu header and body 

124                   local nalu_hdr = bit.bor(bit.band(fu_idr, 0xE0), bit.band(fu_hdr, 0x1F)) 

125                   write_to_file(stream_info, string.char(nalu_hdr), true) 

126               else 

127                   -- start bit not set, just write part of nalu body 

128               end 

129               write_to_file(stream_info, h264tvb(2):string(), false) 

130           end 

131             

132           -- call this function if a packet contains h264 payload 

133           function my_h264_tap.packet(pinfo,tvb) 

134               local h264s = { f_h264() } -- using table because one packet may contains more than one RTP 

135               for i,h264_f in ipairs(h264s) do 

136                   if h264_f.len < 2 then 

137                       return 

138                   end 

139                   local h264 = h264_f.value   -- is ByteArray 

140                   local hdr_type = bit.band(h264:get_index(0), 0x1F) 

141                   local stream_info = get_stream_info(pinfo) 

142                     

143                   if hdr_type > 0 and hdr_type < 24 then 

144                       -- Single NALU 

145                       process_single_nalu(stream_info, h264) 

146                   elseif hdr_type == 24 then 

147                       -- STAP-A Single-time aggregation 

148                       process_stap_a(stream_info, h264) 

149                   elseif hdr_type == 28 then 

150                       -- FU-A 

151                       process_fu_a(stream_info, h264) 

152                   else 

153                       twappend("Error: unknown type=" .. hdr_type .. " ; we only know 1-23(Single NALU),24(STAP-A),28(FU-A)!") 

154                   end 

155               end 

156           end 

157             

158           -- close all open files 

159           function close_all_files() 

160               if stream_infos then 

161                   for id,stream in pairs(stream_infos) do 

162                       if stream and stream.file then 

163                           stream.file:close() 

164                           stream.file = nil 

165                       end 

166                   end 

167               end 

168           end 

169             

170           function my_h264_tap.reset() 

171               -- do nothing now 

172           end 

173             

174           function remove() 

175               close_all_files() 

176               my_h264_tap:remove() 

177           end 

178             

179           tw:set_atclose(remove) 

180             

181           -- first time it runs for counting h.264 packets and finding SPS and PPS 

182           retap_packets() 

183           first_run = false 

184           -- second time it runs for saving h264 data to target file. 

185           retap_packets() 

186           -- close progress window 

187           pgtw:close() 

188       end 

189         

190       -- Find this feature in menu "Tools->"Export H264 to file [HQX's plugins]"" 

191       register_menu("Export H264 to file [HQX's plugins]", export_h264_to_file, MENU_TOOLS_UNSORTED) 

192   end 


把代码保存成h264_export.lua文件,放到wireshark安装目录下,然后修改wireshark安装目录下的init.lua文件:

1)若有disable_lua = true这样的行,则注释掉;

2)在文件末加入dofile("h264_export.lua")

重新打开wirekshark就能使用该功能了。

 

另外,264裸码流文件一般播放器不一定能播放,推荐使用ffmpegffplay播放,或用ffmpeg转成通用文件格式播放。


 


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值