抓取一个包含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 (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 -- 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(" ") 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: [" .. stream_info.filename .. "] ... ") 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("