• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

Wireshark Lua: 一个从RTP抓包里导出H.264 Payload,变成264裸码流文件(xxx.264)的W ...

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

抓取一个包含H.264 Payload RTP包的SIP会话或RTSP会话后,用Wireshark的Play功能只能播放声音,不能播放视频。把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流目的端口>.264的264裸码流文件里。(文件格式为每个NALU前加0x00000001分隔符)。

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

Lua脚本如下:

  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 ([email protected])
  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     -- menu action. When you click "Tools->Export H264 to file [HQX's plugins]" will run this function
 15     local function export_h264_to_file()
 16         -- window for showing information
 17         local tw = TextWindow.new("Export H264 to File Info Win")
 18         local pgtw = ProgDlg.new("Export H264 to File Process", "Dumping H264 data to file...")
 19         
 20         -- add message to information window
 21         function twappend(str)
 22             tw:append(str)
 23             tw:append("\n")
 24         end
 25         
 26         -- running first time for counting and finding sps+pps, second time for real saving
 27         local first_run = true 
 28         -- variable for storing rtp stream and dumping parameters
 29         local stream_infos = {}
 30         -- trigered by all h264 packats
 31         local my_h264_tap = Listener.new(tap, "h264")
 32         
 33         -- get rtp stream info by src and dst address
 34         function get_stream_info(pinfo)
 35             local key = "from_" .. tostring(pinfo.src) .. "_" .. tostring(pinfo.src_port) .. "to" .. tostring(pinfo.dst) .. "_" .. tostring(pinfo.dst_port)
 36             local stream_info = stream_infos[key]
 37             if not stream_info then -- if not exists, create one
 38                 stream_info = { }
 39                 stream_info.filename = key.. ".264"
 40                 stream_info.file = io.open(stream_info.filename, "wb")
 41                 stream_info.counter = 0 -- counting h264 total NALUs
 42                 stream_info.counter2 = 0 -- for second time running
 43                 stream_infos[key] = stream_info
 44                 twappend("Ready to export H.264 data (RTP from " .. tostring(pinfo.src) .. ":" .. tostring(pinfo.src_port) 
 45                          .. " to " .. tostring(pinfo.dst) .. ":" .. tostring(pinfo.dst_port) .. " to file:\n         [" .. stream_info.filename .. "] ...\n")
 46             end
 47             return stream_info
 48         end
 49         
 50         -- write a NALU or part of NALU to file.
 51         function write_to_file(stream_info, str_bytes, begin_with_nalu_hdr)
 52             if first_run then
 53                 stream_info.counter = stream_info.counter + 1
 54                 
 55                 if begin_with_nalu_hdr then
 56                     -- save SPS or PPS
 57                     local nalu_type = bit.band(str_bytes:byte(0,1), 0x1F)
 58                     if not stream_info.sps and nalu_type == 7 then
 59                         stream_info.sps = str_bytes
 60                     elseif not stream_info.pps and nalu_type == 8 then
 61                         stream_info.pps = str_bytes
 62                     end
 63                 end
 64                 
 65             else -- second time running
 66                 if stream_info.counter2 == 0 then
 67                     -- write SPS and PPS to file header first
 68                     if stream_info.sps then
 69                         stream_info.file:write("\00\00\00\01")
 70                         stream_info.file:write(stream_info.sps)
 71                     else
 72                         twappend("Not found SPS for [" .. stream_info.filename .. "], it might not be played!\n")
 73                     end
 74                     if stream_info.pps then
 75                         stream_info.file:write("\00\00\00\01")
 76                         stream_info.file:write(stream_info.pps)
 77                     else
 78                         twappend("Not found PPS for [" .. stream_info.filename .. "], it might not be played!\n")
 79                     end
 80                 end
 81             
 82                 if begin_with_nalu_hdr then
 83                     -- *.264 raw file format seams that every nalu start with 0x00000001
 84                     stream_info.file:write("\00\00\00\01")
 85                 end
 86                 stream_info.file:write(str_bytes)
 87                 stream_info.counter2 = stream_info.counter2 + 1
 88                 
 89                 if stream_info.counter2 == stream_info.counter then
 90                     stream_info.file:flush()
 91                     twappend("File [" .. stream_info.filename .. "] generated OK!\n")
 92                 end
 93                 -- update progress window's progress bar
 94                 if stream_info.counter > 0 then pgtw:update(stream_info.counter2 / stream_info.counter) end
 95             end
 96         end
 97         
 98         -- read RFC3984 about single nalu/stap-a/fu-a H264 payload format of rtp
 99         -- single NALU: one rtp payload contains only NALU
100         function process_single_nalu(stream_info, h264)
101             write_to_file(stream_info, h264:tvb()():string(), true)
102         end
103         
104         -- STAP-A: one rtp payload contains more than one NALUs
105         function process_stap_a(stream_info, h264)
106             local h264tvb = h264:tvb()
107             local offset = 1
108             repeat
109                 local size = h264tvb(offset,2):uint()
110                 write_to_file(stream_info, h264tvb(offset+2, size):string(), true)
111                 offset = offset + 2 + size
112             until offset >= h264tvb:len()
113         end
114         
115         -- FU-A: one rtp payload contains only one part of a NALU (might be begin, middle and end part of a NALU)
116         function process_fu_a(stream_info, h264)
117             local h264tvb = h264:tvb()
118             local fu_idr = h264:get_index(0)
119             local fu_hdr = h264:get_index(1)
120             if bit.band(fu_hdr, 0x80) ~= 0 then
121                 -- start bit is set then save nalu header and body
122                 local nalu_hdr = bit.bor(bit.band(fu_idr, 0xE0), bit.band(fu_hdr, 0x1F))
123                 write_to_file(stream_info, string.char(nalu_hdr), true)
124             else
125                 -- start bit not set, just write part of nalu body
126             end
127             write_to_file(stream_info, h264tvb(2):string(), false)
128         end
129         
130         -- call this function if a packet contains h264 payload
131         function my_h264_tap.packet(pinfo,tvb)
132             local h264s = { f_h264() } -- using table because one packet may contains more than one RTP
133             for i,h264_f in ipairs(h264s) do
134                 if h264_f.len < 2 then
135                     return
136                 end
137                 local h264 = h264_f.value   -- is ByteArray
138                 local hdr_type = bit.band(h264:get_index(0), 0x1F)
139                 local stream_info = get_stream_info(pinfo)
140                 
141                 if hdr_type > 0 and hdr_type < 24 then
142                     -- Single NALU
143                     process_single_nalu(stream_info, h264)
144                 elseif hdr_type == 24 then
145                     -- STAP-A Single-time aggregation
146                     process_stap_a(stream_info, h264)
147                 elseif hdr_type == 28 then
148                     -- FU-A
149                     process_fu_a(stream_info, h264)
150                 else
151                     twappend("Error: unknown type=" .. hdr_type .. " ; we only know 1-23(Single NALU),24(STAP-A),28(FU-A)!")
152                 end
153             end
154         end
155         
156         -- close all open files
157         function close_all_files()
158             if stream_infos then
159                 for id,stream in pairs(stream_infos) do
160                     if stream and stream.file then
161                         stream.file:close()
162                         stream.file = nil
163                     end
164                 end
165             end
166         end
167         
168         function my_h264_tap.reset()
169             -- do nothing now
170         end
171         
172         function remove()
173             close_all_files()
174             my_h264_tap:remove()
175         end
176         
177         tw:set_atclose(remove)
178         
179         -- first time it runs for counting h.264 packets and finding SPS and PPS
180         retap_packets()
181         first_run = false
182         -- second time it runs for saving h264 data to target file.
183         retap_packets()
184         -- close progress window
185         pgtw:close()
186     end
187     
188     -- Find this feature in menu "Tools->"Export H264 to file [HQX's plugins]""
189     register_menu("Export H264 to file [HQX's plugins]", export_h264_to_file, MENU_TOOLS_UNSORTED)
190 end

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

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

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

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

 

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

 

2014年升级版,支持排序、丢弃不完整帧,注意生成的文件from...在抓拍文件相同的目录:

  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 ([email protected])
  7 -- change log:
  8 --      2012-03-13
  9 --          Just can play
 10 --      2012-04-28
 11 --          Add local to local function, and add [local bit = require("bit")] to prevent
 12 --          bit recleared in previous file.
 13 --      2013-07-11
 14 --          Add sort RTP and drop uncompleted frame option.
 15 --      2013-07-19
 16 --          Do nothing when tap is triggered other than button event.
 17 --          Add check for first or last packs lost of one frame.
 18 ------------------------------------------------------------------------------------------------
 19 do
 20     local bit = require("bit")
 21  
 22     -- for geting h264 data (the field's value is type of ByteArray)
 23     local f_h264 = Field.new("h264") 
 24     local f_rtp = Field.new("rtp") 
 25     local f_rtp_seq = Field.new("rtp.seq")
 26     local f_rtp_timestamp = Field.new("rtp.timestamp")
 27     local nalu_type_list = {
 28         [0] = "Unspecified",
 29         [1] = "P/B_slice",
 30         [2] = "P/B_A",
 31         [3] = "P/B_B",
 32         [4] = "P/B_C",
 33         [5] = "I_slice",
 34         [6] = "SEI",
 35         [7] = "SPS",
 36         [8] = "PPS",
 37         [9] = "AUD",
 38     }
 39     
 40     local function get_enum_name(list, index)
 41         local value = list[index]
 42         return value and value or "Unknown"
 43     end
 44     -- menu action. When you click "Tools->Export H264 to file [HQX's plugins]" will run this function
 45     local function export_h264_to_file()
 46         -- window for showing information
 47         local tw = TextWindow.new("Export H264 to File Info Win")
 48         --local pgtw = ProgDlg.new("Export H264 to File Process", "Dumping H264 data to file...")
 49         local pgtw;
 50         
 51         -- add message to information window
 52         function twappend(str)
 53             tw:append(str)
 54             tw:append("\n")
 55         end
 56         
 57         -- running first time for counting and finding sps+pps, second time for real saving
 58         local first_run = true 
 59         -- variable for storing rtp stream and dumping parameters
 60         local stream_infos = nil
 61         -- drop_uncompleted_frame
 62         local drop_uncompleted_frame = false
 63         -- max frame buffer size
 64         local MAX_FRAME_NUM = 3
 65         -- trigered by all h264 packats
 66         local my_h264_tap = Listener.new(tap, "h264")
 67         
 68         -- get rtp stream info by src and dst address
 69         function get_stream_info(pinfo)
 70             local key = "from_" .. tostring(pinfo.src) .. "_" .. tostring(pinfo.src_port) .. "to" .. tostring(pinfo.dst) .. "_" .. tostring(pinfo.dst_port) .. (drop_uncompleted_frame and "_dropped" or "_all")
 71             local stream_info = stream_infos[key]
 72             if not stream_info then -- if not exists, create one
 73                 stream_info = { }
 74                 stream_info.filename = key.. ".264"
 75                 stream_info.file = io.open(stream_info.filename, "wb")
 76                 stream_info.counter = 0 -- counting h264 total NALUs
 77                 stream_info.counter2 = 0 -- for second time running
 78                 stream_infos[key] = stream_info
 79                 twappend("Ready to export H.264 data (RTP from " .. tostring(pinfo.src) .. ":" .. tostring(pinfo.src_port) 
 80                          .. " to " .. tostring(pinfo.dst) .. ":" .. tostring(pinfo.dst_port) .. " to file:\n         [" .. stream_info.filename .. "] ...\n")
 81             end
 82             return stream_info
 83         end
 84         
 85         -- write a NALU or part of NALU to file.
 86         local function real_write_to_file(stream_info, str_bytes, begin_with_nalu_hdr)
 87             if first_run then
 88                 stream_info.counter = stream_info.counter + 1
 89                 
 90                 if begin_with_nalu_hdr then
 91                     -- save SPS or PPS
 92                     local nalu_type = bit.band(str_bytes:byte(0,1), 0x1F)
 93                     if not stream_info.sps and nalu_type == 7 then
 94                         stream_info.sps = str_bytes
 95                     elseif not stream_info.pps and nalu_type == 8 then
 96                         stream_info.pps = str_bytes
 97                     end
 98                 end
 99                 
100             else -- second time running
101                 --[[
102                 if begin_with_nalu_hdr then
103                     -- drop AUD
104                     local nalu_type = bit.band(str_bytes:byte(0,1), 0x1F)
105                     if nalu_type == 9 then
106                         return;
107                     end
108                 end
109                 ]]
110                 
111                 if stream_info.counter2 == 0 then
112                     -- write SPS and PPS to file header first

                      

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
centos编译安装vim并支持lua发布时间:2022-07-22
下一篇:
Lua垃圾收集机制发布时间:2022-07-22
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap