用 Wireshark 解码 H.264

news2024/11/19 18:40:55

H264,你不知道的小技巧-腾讯云开发者社区-腾讯云

这篇文章写的非常好

这里仅做几点补充

init.lua内容:

-- Set enable_lua to false to disable Lua support.
enable_lua = true

if not enable_lua then
    return
end

-- If false and Wireshark was started as (setuid) root, then the user
-- will not be able to execute custom Lua scripts from the personal
-- configuration directory, the -Xlua_script command line option or
-- the Lua Evaluate menu option in the GUI.
-- Note: Not checked on Windows. running_superuser is always false.
--run_user_scripts_when_superuser = true

dofile(DATA_DIR.."rtp_h264_extractor.lua")

init.lua 放到了这个目录:

C:\Program Files\Wireshark\plugins

rtp_h264_extractor.lua 内容为:


--[[
 * rtp_h264_extractor.lua
 * wireshark plugin to extract h264 stream from RTP packets
 *
 * Copyright (C) 2015 Volvet Zhang <volvet2002@gmail.com>
 *
 * rtp_h264_extractor is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * rtp_h264_extractor is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 *]]


do
    local MAX_JITTER_SIZE = 50
    local h264_data = Field.new("h264")
    local rtp_seq = Field.new("rtp.seq")

    local function extract_h264_from_rtp()
        local function dump_filter(fd)
            local fh = "h264";
            if fd ~= nil and fd ~= "" then
                return string.format("%s and (%s)", fh, fd)
            else
                return fh
            end
        end

        local h264_tap = Listener.new("ip", dump_filter(get_filter()))
        local text_window = TextWindow.new("h264 extractor")
        local filename = ""
        local seq_payload_table = { }
        local pass = 0
        local packet_count = 0
        local max_packet_count = 0
        local fu_info = nil
        local pre_seq = 0;

        local function log(info)
            text_window:append(info)
            text_window:append("\n")
        end

        -- get_preference is only available since 3.5.0
        if get_preference then
            filename = get_preference("gui.fileopen.dir") .. "/" .. os.date("video_%Y%m%d-%H%M%S.264")
        else
            filename = "dump.264"
        end

        log("Dumping H264 stream to " .. filename)
        local fp = io.open(filename, "wb")
        if fp == nil then
            log("open dump file fail")
        end

        local function seq_compare(left, right)
            if math.abs(right.key - left.key) < 1000 then
                return left.key < right.key
            else
                return left.key > right.key
            end
        end

        local function dump_single_nal(h264_payload)
            fp:write("\00\00\00\01")
            fp:write(h264_payload:tvb()():raw())
            fp:flush()
        end

        local function dump_fu_a(fu_info)
            if  fu_info.complete ==  true then
                log("dump_fu_a")
                fp:write("\00\00\00\01")
                fp:write(string.char(fu_info.nal_header))

                for i, obj in ipairs(fu_info.payloads) do
                    fp:write(obj:tvb()():raw(2))
                end
                fp:flush()
            else
                log("Incomplete NAL from FUs, dropped")
            end
        end

        local function handle_fu_a(seq, h264_data)
            fu_indicator = h264_data:get_index(0)
            fu_header = h264_data:get_index(1)
            nal_header = bit.bor(bit.band(fu_indicator, 0xe0), bit.band(fu_header, 0x1f))

            if bit.band(fu_header, 0x80) ~= 0 then
                -- fu start flag found
                fu_info = { }
                fu_info.payloads = { }
                fu_info.seq = seq
                fu_info.complete = true
                fu_info.nal_header = nal_header

                table.insert(fu_info.payloads, h264_data)
                log("Fu start: seq = "..tostring(seq))
                return
            end

            if fu_info == nil then
                log("Incomplete FU found: No start flag, dropped")
                return
            end

            if seq ~= (fu_info.seq + 1)% 65536 then
                log("Incomplete FU found:  fu_info.seq = "..tostring(fu_info.seq)..", input seq = "..tostring(seq))
                fu_info.complete = false;
                return
            end

            fu_info.seq = seq

            table.insert(fu_info.payloads, h264_data)

            if bit.band(fu_header, 0x40) ~= 0 then
                -- fu end flag found
                log("Fu stop: seq = "..tostring(seq))
                dump_fu_a(fu_info)
                fu_info = nil
            end

        end

        local function handle_stap_a(h264_data)
            log("start dump stap nals")
            offset = 1		-- skip nal header of STAP-A
            repeat
                size = h264_data:tvb()(offset, 2):uint()
                offset = offset + 2
                local next_nal_type = bit.band(h264_data:get_index(offset), 0x1f)
                log("STAP-A has naltype = "..next_nal_type..", size = "..size)
                fp:write("\00\00\00\01")
                fp:write(h264_data:tvb()():raw(offset, size))
                offset = offset + size
            until offset >= h264_data:tvb():len()
            fp:flush()
            log("finish dump stap nals")
        end

        local function on_ordered_h264_payload(seq, h264_data)
            local naltype = bit.band(h264_data:get_index(0), 0x1f)
            if naltype > 0 and naltype < 24 then
                -- Single NAL unit packet
                if fu_info ~= nil then
                    log("Incomplete FU found: No start flag, dropped")
                    fu_info = nil
                end
                dump_single_nal(h264_data)
                --log("tap.packet: "..", single nal packet dumpped, naltype = "..tostring(naltype)..", len = "..tostring(packet.len))
            elseif naltype == 28 then
                -- FU-A
                handle_fu_a(seq, h264_data)
            elseif naltype == 24 then
                -- STAP-A
                if fu_info ~= nil then
                    log("Incomplete FU found: No start flag, dropped")
                    fu_info = nil
                end
                handle_stap_a(h264_data)
            else
                log("tap.packet: "..", Unsupported nal, naltype = "..tostring(naltype))
            end
        end

        local function on_jitter_buffer_output()
            table.sort(seq_payload_table, seq_compare)

            if #seq_payload_table > 0 then
                log("on_jitter_buffer_output:  seq = "..tostring(seq_payload_table[1].key)..", payload len = "..tostring(seq_payload_table[1].value:len()))
                on_ordered_h264_payload(seq_payload_table[1].key, seq_payload_table[1].value)
                table.remove(seq_payload_table, 1)
            end
        end

        local function jitter_buffer_finilize()
            for i, obj in ipairs(seq_payload_table) do
                log("jitter_buffer_finilize:  seq = "..tostring(obj.key)..", payload len = "..tostring(obj.value:len()))
                on_ordered_h264_payload(obj.key, obj.value)
            end
        end

        local function on_h264_rtp_payload(seq, payload)
            local cur_seq = seq.value
            --log("on_h264_rtp_payload:  seq = "..tostring(seq.value)..", payload len = "..tostring(payload.len)..",pre_seq = "..pre_seq..",cur_seq = "..cur_seq..",packet_count = "..packet_count)
            if packet_count == 0 then
                pre_seq = cur_seq
            else
                if cur_seq == pre_seq then
                    packet_count = packet_count + 1
                    --log("on_h264_rtp_payload, duplicate seq = "..tostring(seq.value)..",packet_count = "..packet_count)
                    return
                else
                    pre_seq = cur_seq
                end
            end

            packet_count = packet_count + 1

            table.insert(seq_payload_table, { key = tonumber(seq.value), value = payload.value })

            --log("on_h264_rtp_payload: table size is "..tostring(#seq_payload_table))
            if #seq_payload_table > MAX_JITTER_SIZE then
                on_jitter_buffer_output()
            end
        end

        function h264_tap.packet(pinfo, tvb)
            local payloadTable = { h264_data() }
            local seqTable = { rtp_seq() }

            if (#payloadTable) < (#seqTable) then
                log("ERROR: payloadTable size is "..tostring(#payloadTable)..", seqTable size is "..tostring(#seqTable))
                return
            end

            if pass == 0 then
                for i, payload in ipairs(payloadTable) do
                    max_packet_count = max_packet_count + 1
                end
            else

                for i, payload in ipairs(payloadTable) do
                    on_h264_rtp_payload(seqTable[1], payload)
                end

                if packet_count == max_packet_count then
                    jitter_buffer_finilize()
                end
            end
        end

        function h264_tap.reset()
        end

        function h264_tap.draw()
        end

        local function remove()
            if fp then
                fp:close()
                fp = nil
            end
            h264_tap:remove()
        end

        log("Start")

        text_window:set_atclose(remove)

        log("phase 1")
        pass = 0
        retap_packets()

        log("phase 2:  max_packet_count = "..tostring(max_packet_count))
        pass = 1
        retap_packets()

        if fp ~= nil then
           fp:close()
           fp = nil
           log("Video stream written to " .. filename)
        end

        log("End")
	end


	register_menu("Extract h264 stream from RTP", extract_h264_from_rtp, MENU_TOOLS_UNSORTED)
end

该文件放到了这里:

C:\Program Files\Wireshark

此外,Elecard StreamEye Tools 可以到这里下载:

Download video analysis and monitoring applications| Elecard: Video Compression GuruDownload Elecard products for video analysis and monitoring, encoding and decoding video of various formats.icon-default.png?t=N7T8https://www.elecard.com/software

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1570551.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

日期专题:做题笔记 (时间显示/星期计算/星系炸弹/第几天/纪念日)

目录 时间显示 代码 星期计算 代码 星系炸弹 代码 第几天 纪念日 代码 时间显示 时间显示 这道题主要是单位换算。 ①单位换算 ②输出格式&#xff1a; a. 不足两位补前导零。利用printf输出 b. 注意 long long 输出格式应该是 %lld 长整型 代码 #include <…

解码零跑汽车2023年报:营收增速大幅滑坡,净亏42亿,如何讲故事

在2023年的新势力车企中&#xff0c;有这么一家低调崛起的品牌&#xff0c;并没有像蔚小理那样高调&#xff0c;但去年全年销量却反超小鹏汽车&#xff0c;晋升成为新势力车企中的销量第三名&#xff0c;它就是第四家登陆港交所上市的新势力品牌—零跑汽车。 不过&#xff0c;…

阿里云短信服务业务

一、了解阿里云用户权限操作 1.注册账号、实名认证&#xff1b; 2.使用AccessKey 步骤一 点击头像&#xff0c;权限安全的AccessKey 步骤二 设置子用户AccessKey 步骤三 添加用户组和用户 步骤四 添加用户组记得绑定短信服务权限 步骤五 添加用户记得勾选openApi访问 添加…

Coursera自然语言处理专项课程04:Natural Language Processing with Attention Models笔记 Week01

Natural Language Processing with Attention Models Course Certificate 本文是学习这门课 Natural Language Processing with Attention Models的学习笔记&#xff0c;如有侵权&#xff0c;请联系删除。 文章目录 Natural Language Processing with Attention ModelsWeek 01…

LeetCode每日一题之专题一:双指针 ——快乐数

快乐数OJ链接&#xff1a;202. 快乐数 - 力扣&#xff08;LeetCode&#xff09; 题目&#xff1a; 题目分析: 为了房便叙述&#xff0c;将「对于⼀个正整数&#xff0c;每⼀次将该数替换为它每个位置上的数字的平方和」这⼀个 操作记为 x 操作&#xff1b; 题目告诉我们&#…

没用的cpp知识又增加了之--cpp11利用宏、模板以及lambda表达式实现python like的装饰器语义

等有时间了我再bb吧&#xff0c;先直接上码 msvc 2015 编译执行效果 用法 style1 int CCar::oilfeed(int degress) DECORATED(logging<int>, ARGS(__FUNCTION__), DECORATED(checkRotateSpeed<int>, ARGS(this, __FUNCTION__), {m_n_rotat_speed degress;retur…

华为云1核2G免费使用一年

个人用户专享云服务器、云数据库产品每天上午9:30开抢&#xff0c;其他产品每天0点开放领取&#xff0c;企业用户所有产品每天0点开放领取&#xff1b; 云产品体验名额有限&#xff0c;领完即止。详情&#xff1a;https://www.vpspick.com/vps/591.html 通用入门型 T6 云服务…

数码管与译码器

目录 数码管 显示的基本原理 LED数码管的显示方式 静态显示方式 动态显示方式 具体案例 数码管静态显示 电路图 keil文件 数码管动态显示 电路图 keil文件 74LS138译码器 简介 译码表 译码器案例 电路图 keil文件 74HC595译码器 前言 举例解释 简单案例 …

手写防抖节流、手写深拷贝、事件总线

一、防抖 手写防抖--基本实现&#xff08;面试&#xff09; 手写防抖并且绑定this和event 添加取消功能 添加立即执行状态&#xff0c;默认不立即执行 underscore库介绍&#xff0c;lodash更轻量级 二、节流 用underscore库&#xff0c;调用throttle函数 手写基础版节流-&#…

《价值》-张磊-高瓴资本-4(上)-价值投资需要研究驱动,而非拍脑袋

第四章 价值投资方法与哲学&#xff08;上&#xff09; 在我的书架上&#xff0c;有一套书最为醒目&#xff0c;我总会在不经意间打开翻看&#xff0c;那就是由本杰明 格雷厄姆&#xff08;Benjamin Graham &#xff09;与戴维 多德&#xff08;David Dodd &#xff09;在 19…

【HTML】制作一个简单的三角形动态图形

目录 前言 开始 HTML部分 CSS部分 效果图 总结 前言 无需多言&#xff0c;本文将详细介绍一段HTML和CSS代码&#xff0c;具体内容如下&#xff1a; 开始 首先新建文件夹&#xff0c;创建两个文本文档&#xff0c;其中HTML的文件名改为[index.html]&#xff0c;CSS的文件名…

实用技巧:如何取消app的截屏禁用

因为我想要在小鹅通App做笔记,但是被小鹅通App禁用截屏了,这真是一个很糟糕的使用体验,虽然可能是为了保护商家权益…… 方法1 可以让商家设置课程可以截屏 方法2 手机root,安装Xposed框架,利用Xposed框架上面的插件我们可以对手机进行高度定制化,而安装Xposed框架的…

微信小程序 电影院售票选座票务系统5w7l6

uni-app框架&#xff1a;使用Vue.js开发跨平台应用的前端框架&#xff0c;编写一套代码&#xff0c;可编译到Android、小程序等平台。 框架支持:springboot/Ssm/thinkphp/django/flask/express均支持 前端开发:vue.js 可选语言&#xff1a;pythonjavanode.jsphp均支持 运行软件…

4.5 day4 FreeRTOS

1.总结二进制信号量和计数型信号量的区别&#xff0c;以及他们的使用场景。 二进制信号量的数值只有0和1&#xff0c;用于共享资源的访问 计数型信号量的值一般是大于或者等于2&#xff0c;用于生产者和消费者模型 2.使用技术型信号量完成生产者和消费者模型实验。 3.总结Free…

基于单片机的炉温自动控制系统设计

**单片机设计介绍&#xff0c;基于单片机的炉温自动控制系统设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的炉温自动控制系统设计是一个综合性的项目&#xff0c;它结合了单片机技术、温度传感技术、控制算法以…

Redis从入门到精通(五)Redis实战(二)商户查询缓存

↑↑↑请在文章头部下载测试项目原代码↑↑↑ 文章目录 前言4.2 商户查询缓存4.2.1 缓存介绍4.2.2 查询商户信息的传统做法4.2.2.1 接口文档4.2.2.2 代码实现4.2.2.3 功能测试 4.2.3 查询商户信息添加Redis缓存4.2.3.1 逻辑分析4.2.3.2 代码实现4.2.3.3 功能测试 4.2.3 数据一致…

case语句

Oracle从入门到总裁:​​​​​​https://blog.csdn.net/weixin_67859959/article/details/135209645 CASE 语句的执行方式与 IF...THEN...ELSIF 语句的执行方式类似&#xff0c;但是它是通过一个表达式的值来决定执行哪个分支 CASE 选择器表达式 WHEN 条件 1 THEN 语句序列 …

【00】【solidity最新教程】-简介

Solidity 是一门面向合约的、为实现智能合约而创建的高级编程语言。这门语言受到了 C&#xff0c;Python 和 Javascript 语言的影响&#xff0c;设计的目的是能在以太坊虚拟机&#xff08;EVM&#xff09;上运行。 Solidity 是静态类型语言&#xff0c;支持继承、库和复杂的用…

h5 笔记2

何谓cookiecookie是记录在浏览器里的变量&#xff0c;用来存放特定的信息&#xff0c;必须利用script程序或CGI程序来写入或读取。例如&#xff0c;有些网站为了让用户不必每次都重新输入账号&#xff0c;会利用cookie来记录账号&#xff0c;下次进入网页时就会自动弹出账号&am…

STM32CubeIDE基础学习-通用定时器中断实验

STM32CubeIDE基础学习-通用定时器中断实验 文章目录 STM32CubeIDE基础学习-通用定时器中断实验前言第1章 工程配置1.1 工程外设配置部分1.2 生成工程代码部分 第2章 代码编写第3章 实验现象总结 前言 生活中很多应用都有用到定时器功能、计时功能等。 定时器中断可以大大降低…