在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
抓取一个包含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 |
请发表评论