直接播放H264视频流的方法或工具
歡迎大家轉載,為保留作者成果,轉載請注明出處,http://blog.csdn.net/netluoriver,有些文件在資源中也可以下載!如果你沒有積分,可以聯系我!
前幾天在查找資料的時候發現一篇文章特別實用,鏈接如下:
http://blog.csdn.net/jasonhwang/article/details/7359095抓取一個包含H.264 Payload RTP包的SIP會話或RTSP會話后,用Wireshark的Play功能只能播放聲音,不能播放視頻。把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腳本如下:
-- Dump RTP h.264 payload to raw h.264 file (*.264)-- According to RFC3984 to dissector H264 payload of RTP to NALU, and write it-- to from<sourceIp_sourcePort>to<dstIp_dstPort>.264 file. By now, we support single NALU,-- STAP-A and FU-A format RTP payload for H.264.-- You can access this feature by menu "Tools->Export H264 to file [HQX's plugins]"-- Author: Huang Qiangxiong (qiangxiong.huang@gmail.com)-- change log:-- 2012-03-13-- Just can play------------------------------------------------------------------------------------------------do-- for geting h264 data (the field's value is type of ByteArray)local f_h264 = Field.new("h264") -- menu action. When you click "Tools->Export H264 to file [HQX's plugins]" will run this functionlocal function export_h264_to_file()-- window for showing informationlocal tw = TextWindow.new("Export H264 to File Info Win")local pgtw = ProgDlg.new("Export H264 to File Process", "Dumping H264 data to file...")-- add message to information windowfunction twappend(str)tw:append(str)tw:append("\n")end-- running first time for counting and finding sps+pps, second time for real savinglocal first_run = true -- variable for storing rtp stream and dumping parameterslocal stream_infos = {}-- trigered by all h264 packatslocal my_h264_tap = Listener.new(tap, "h264")-- get rtp stream info by src and dst addressfunction get_stream_info(pinfo)local key = "from_" .. tostring(pinfo.src) .. "_" .. tostring(pinfo.src_port) .. "to" .. tostring(pinfo.dst) .. "_" .. tostring(pinfo.dst_port)local stream_info = stream_infos[key]if not stream_info then -- if not exists, create onestream_info = { }stream_info.filename = key.. ".264"stream_info.file = io.open(stream_info.filename, "wb")stream_info.counter = 0 -- counting h264 total NALUsstream_info.counter2 = 0 -- for second time runningstream_infos[key] = stream_infotwappend("Ready to export H.264 data (RTP from " .. tostring(pinfo.src) .. ":" .. tostring(pinfo.src_port) .. " to " .. tostring(pinfo.dst) .. ":" .. tostring(pinfo.dst_port) .. " to file:\n [" .. stream_info.filename .. "] ...\n")endreturn stream_infoend-- write a NALU or part of NALU to file.function write_to_file(stream_info, str_bytes, begin_with_nalu_hdr)if first_run thenstream_info.counter = stream_info.counter + 1if begin_with_nalu_hdr then-- save SPS or PPSlocal nalu_type = bit.band(str_bytes:byte(0,1), 0x1F)if not stream_info.sps and nalu_type == 7 thenstream_info.sps = str_byteselseif not stream_info.pps and nalu_type == 8 thenstream_info.pps = str_bytesendendelse -- second time runningif stream_info.counter2 == 0 then-- write SPS and PPS to file header firstif stream_info.sps thenstream_info.file:write("\00\00\00\01")stream_info.file:write(stream_info.sps)elsetwappend("Not found SPS for [" .. stream_info.filename .. "], it might not be played!\n")endif stream_info.pps thenstream_info.file:write("\00\00\00\01")stream_info.file:write(stream_info.pps)elsetwappend("Not found PPS for [" .. stream_info.filename .. "], it might not be played!\n")endendif begin_with_nalu_hdr then-- *.264 raw file format seams that every nalu start with 0x00000001stream_info.file:write("\00\00\00\01")endstream_info.file:write(str_bytes)stream_info.counter2 = stream_info.counter2 + 1if stream_info.counter2 == stream_info.counter thenstream_info.file:flush()twappend("File [" .. stream_info.filename .. "] generated OK!\n")end-- update progress window's progress barif stream_info.counter > 0 then pgtw:update(stream_info.counter2 / stream_info.counter) endendend-- read RFC3984 about single nalu/stap-a/fu-a H264 payload format of rtp-- single NALU: one rtp payload contains only NALUfunction process_single_nalu(stream_info, h264)write_to_file(stream_info, h264:tvb()():string(), true)end-- STAP-A: one rtp payload contains more than one NALUsfunction process_stap_a(stream_info, h264)local h264tvb = h264:tvb()local ffset = 1repeatlocal size = h264tvb(offset,2):uint()write_to_file(stream_info, h264tvb(offset+2, size):string(), true)ffset = offset + 2 + sizeuntil offset >= h264tvb:len()end-- FU-A: one rtp payload contains only one part of a NALU (might be begin, middle and end part of a NALU)function process_fu_a(stream_info, h264)local h264tvb = h264:tvb()local fu_idr = h264:get_index(0)local fu_hdr = h264:get_index(1)if bit.band(fu_hdr, 0x80) ~= 0 then-- start bit is set then save nalu header and bodylocal nalu_hdr = bit.bor(bit.band(fu_idr, 0xE0), bit.band(fu_hdr, 0x1F))write_to_file(stream_info, string.char(nalu_hdr), true)else-- start bit not set, just write part of nalu bodyendwrite_to_file(stream_info, h264tvb(2):string(), false)end-- call this function if a packet contains h264 payloadfunction my_h264_tap.packet(pinfo,tvb)local h264s = { f_h264() } -- using table because one packet may contains more than one RTPfor i,h264_f in ipairs(h264s) doif h264_f.len < 2 thenreturnendlocal h264 = h264_f.value -- is ByteArraylocal hdr_type = bit.band(h264:get_index(0), 0x1F)local stream_info = get_stream_info(pinfo)if hdr_type > 0 and hdr_type < 24 then-- Single NALUprocess_single_nalu(stream_info, h264)elseif hdr_type == 24 then-- STAP-A Single-time aggregationprocess_stap_a(stream_info, h264)elseif hdr_type == 28 then-- FU-Aprocess_fu_a(stream_info, h264)elsetwappend("Error: unknown type=" .. hdr_type .. " ; we only know 1-23(Single NALU),24(STAP-A),28(FU-A)!")endendend-- close all open filesfunction close_all_files()if stream_infos thenfor id,stream in pairs(stream_infos) doif stream and stream.file thenstream.file:close()stream.file = nilendendendendfunction my_h264_tap.reset()-- do nothing nowendfunction remove()close_all_files()my_h264_tap:remove()endtw:set_atclose(remove)-- first time it runs for counting h.264 packets and finding SPS and PPSretap_packets()first_run = false-- second time it runs for saving h264 data to target file.retap_packets()-- close progress windowpgtw:close()end-- Find this feature in menu "Tools->"Export H264 to file [HQX's plugins]""register_menu("Export H264 to file [HQX's plugins]", export_h264_to_file, MENU_TOOLS_UNSORTED)end
把代碼保存成h264_export.lua文件,放到wireshark安裝目錄下,然后修改wireshark安裝目錄下的init.lua文件:
(1)若有disable_lua = true這樣的行,則注釋掉;
(2)在文件末加入dofile("h264_export.lua")
重新打開wirekshark就能使用該功能了。(低版本的wireshark可能不支持此功能,升級到最新版本即可)
?生成的文件在桌面。
另外,264裸碼流文件一般播放器不一定能播放,推薦使用ffmpeg的ffplay播放,或用ffmpeg轉成通用文件格式播放。
?我用的是Elecard_StreamEye測試的,生成的包正常播放。
總結
以上是生活随笔為你收集整理的直接播放H264视频流的方法或工具的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java tostring apache
- 下一篇: [vue] vue组件里的定时器要怎么销