直接播放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 function

    local function export_h264_to_file()

        -- window for showing information

        local 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 window

        function twappend(str)

            tw:append(str)

            tw:append("\n")

        end

        

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

        local first_run = true 

        -- variable for storing rtp stream and dumping parameters

        local stream_infos = {}



        -- trigered by all h264 packats

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

        

        -- get rtp stream info by src and dst address

        function 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 one

                stream_info = { }

                stream_info.filename = key.. ".264"

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

                stream_info.counter = 0 -- counting h264 total NALUs

                stream_info.counter2 = 0 -- for second time running

                stream_infos[key] = stream_info

                twappend("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")

            end

            return stream_info

        end

        

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

        function write_to_file(stream_info, str_bytes, begin_with_nalu_hdr)

            if first_run then

                stream_info.counter = stream_info.counter + 1

                

                if begin_with_nalu_hdr then

                    -- save SPS or PPS

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

                    if not stream_info.sps and nalu_type == 7 then

                        stream_info.sps = str_bytes

                    elseif not stream_info.pps and nalu_type == 8 then

                        stream_info.pps = str_bytes

                    end

                end

                

            else -- second time running

                if stream_info.counter2 == 0 then

                    -- write SPS and PPS to file header first

                    if stream_info.sps then

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

                        stream_info.file:write(stream_info.sps)

                    else

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

                    end

                    if stream_info.pps then

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

                        stream_info.file:write(stream_info.pps)

                    else

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

                    end

                end

            

                if begin_with_nalu_hdr then

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

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

                end

                stream_info.file:write(str_bytes)

                stream_info.counter2 = stream_info.counter2 + 1

                

                if stream_info.counter2 == stream_info.counter then

                    stream_info.file:flush()

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

                end

                -- update progress window's progress bar

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

            end

        end

        

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

        -- single NALU: one rtp payload contains only NALU

        function 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 NALUs

        function process_stap_a(stream_info, h264)

            local h264tvb = h264:tvb()

            local ffset = 1

            repeat

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

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

                ffset = offset + 2 + size

            until 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 body

                local 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 body

            end

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

        end

        

        -- call this function if a packet contains h264 payload

        function my_h264_tap.packet(pinfo,tvb)

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

            for i,h264_f in ipairs(h264s) do

                if h264_f.len < 2 then

                    return

                end

                local h264 = h264_f.value   -- is ByteArray

                local 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 NALU

                    process_single_nalu(stream_info, h264)

                elseif hdr_type == 24 then

                    -- STAP-A Single-time aggregation

                    process_stap_a(stream_info, h264)

                elseif hdr_type == 28 then

                    -- FU-A

                    process_fu_a(stream_info, h264)

                else

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

                end

            end

        end

        

        -- close all open files

        function close_all_files()

            if stream_infos then

                for id,stream in pairs(stream_infos) do

                    if stream and stream.file then

                        stream.file:close()

                        stream.file = nil

                    end

                end

            end

        end

        

        function my_h264_tap.reset()

            -- do nothing now

        end

        

        function remove()

            close_all_files()

            my_h264_tap:remove()

        end

        

        tw:set_atclose(remove)

        

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

        retap_packets()

        first_run = false

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

        retap_packets()

        -- close progress window

        pgtw: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测试的,生成的包正常播放。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值