用Wireshark解码H.264

news2024/11/22 22:32:09

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/1562303.html

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

相关文章

31.2k star, 免费开源的白板绘图工具 tldraw

31.2k star, 免费开源的白板绘图工具 tldraw 分类 开源分享 项目名: tldraw -- 无限画布白板 Github 开源地址&#xff1a; https://github.com/tldraw/tldraw 在线测试地址&#xff1a; tldraw 文档地址&#xff1a; tldraw SDK tldraw 是一款开源免费的无限画布白板&…

【NC14326】Rails

题目 Rails 栈 翻译 由于原题是英文的&#xff0c;所以这里先翻译一下&#xff1a; PopPush市有一个著名的火车站。那里的山地多得令人难以置信。这个车站建于上个世纪。不幸的是&#xff0c;当时资金极为有限。只能建立一条地面轨道。此外&#xff0c;事实证明&#xff0c;火…

PurpleKeep:提供Azure管道以创建基础设施并执行Atomic测试

关于PurpleKeep PurpleKeep是一款功能强大的安全测试自动化工具&#xff0c;该工具能够通过提供Azure管道以创建基础设施&#xff0c;并帮助广大研究人员执行Atomic测试。 随着攻击技术种类的迅速增加&#xff0c;以及EDR&#xff08;端点检测和响应&#xff09;和自定义检测规…

[leetcode] 637. 二叉树的层平均值

给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值。与实际答案相差 10-5 以内的答案可以被接受。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;[3.00000,14.50000,11.00000] 解释&#xff1a;第 0 层的平均值…

FMEA引领智能家居安全革新,打造无忧智能生活新纪元!

在智能家居日益普及的今天&#xff0c;如何确保家居安全成为消费者关注的焦点。本文将探讨如何通过FMEA&#xff08;故障模式与影响分析&#xff09;这一强大的质量管理工具&#xff0c;为智能家居赋能&#xff0c;打造安全无忧的智能生活新体验。 一、FMEA在智能家居领域的应用…

Electron的学习

目录 项目初始化可以看官网非常详细根路径创建.vscode文件夹主进程和渲染进程之前的通信ipcRenderer.send和ipcMain.on的使用ipcRenderer.invoke和ipcMain.handle的使用 切换主题模式文件拖放保存消息通知进度展示图标闪烁自定义菜单自定义右键菜单 项目初始化可以看官网非常详…

基于ArgoCD和Testkube打造GitOps驱动的Kubernetes测试环境

本文介绍了一项新工具&#xff0c;可以基于Gitops手动或者自动实现Kubernetes集群应用测试&#xff0c;确保集群的健康状态与Git仓库定义的一致。原文: GitOps-Powered Kubernetes Testing Machine: ArgoCD Testkube 简介&#xff1a;GitOps 云原生测试面临的挑战 现代云原生应…

Qt中出现中文乱码的原因以及解决方法

Qt专栏&#xff1a;http://t.csdnimg.cn/C2SDN 目录 1.引言 2.原因分析 3.源文件的编码格式修改方法 4.程序内部使用的默认编码格式修改方法 5.QString转std::string的方法 6.总结 1.引言 在编写Qt程序的时候&#xff0c;或多或少都可能遇到用QString时候&#xff0c;明明…

游戏APP如何提高广告变现收益的同时,保证用户留存率?

APP广告变现对接第三方聚合广告平台主要通过SDK文档对接&#xff0c;一些媒体APP不具备专业运营广告变现的对接能力和资源沉淀&#xff0c;导致APP被封控&#xff0c;设置列入黑名单&#xff0c;借助第三方聚合广告平台进行商业化变现是最佳选择。#APP广告变现# 接入第三方平台…

51单片机入门:认识开发板

认识开发板 板载资源&#xff1a; 数码管模块 说明&#xff1a; 2个四位一体共阴数码管 详细&#xff1a; 2个四位一体&#xff1a;两个独立的四位数码管&#xff0c;每个四位数码管都是“一体”的设计&#xff0c;也就是说&#xff0c;每个数码管内部集成了四个独立的七段LE…

Stable Diffusion WebUI 附加功能/图片放大(Extras):单张图片/批量处理/从目录进行批量处理

本文收录于《AI绘画从入门到精通》专栏&#xff0c;专栏总目录&#xff1a;点这里&#xff0c;订阅后可阅读专栏内所有文章。 大家好&#xff0c;我是水滴~~ 篇文章主要讲解 Stable Diffusion WebUI 的附加功能/图片放大&#xff08;Extras&#xff09;的使用&#xff0c;主要…

JAV八股--redis

如何保证Redis和数据库数据一致性 关于异步通知中消息队列和Canal的内容。 redisson实现的分布式锁的主从一致性 明天继续深入看这个系列问题 介绍IO复用模型

WebSocket用户验证

在WebSocket中&#xff0c;如何携带用户的验证信息 一、在OnMessage中进行验证 客户端在连接到服务器后&#xff0c;客户端通过发送消息&#xff0c;服务器端在OnMessage方法中&#xff0c;进行信息验证&#xff0c;这种方式需要将用户身份验证及接收用户消息进行混合处理&am…

什么是原生IP?原生IP的作用是什么?

原生IP&#xff08;Native IP&#xff09;是指直接从互联网服务提供商&#xff08;ISP&#xff09;获得的IP地址&#xff0c;而非通过代理服务器、VPN或其他中间层方式获取。这种IP地址直接与用户的设备或网络关联&#xff0c;无需经过任何中间服务器或代理的转发或隐藏&#x…

体验OceanBase 的binlog service

OceanBase对MySQL具备很好的兼容性。目前&#xff0c;已经发布了开源版的binlog service工具&#xff0c;该工具能够将OceanBase特有的clog模式转换成binlog模式&#xff0c;以便下游工具如canal、flink cdc等使用。今天&#xff0c;我们就来简单体验一下这个binlog service的功…

Educational Codeforces Round 163 (Rated for Div. 2) E. Clique Partition

题目 思路&#xff1a; #include <bits/stdc.h> using namespace std; #define int long long #define pb push_back #define fi first #define se second #define lson p << 1 #define rson p << 1 | 1 const int maxn 1e6 5, inf 1e9, maxm 4e4 5; co…

MacBook 访达使用技巧【mac 入门】

快捷键 打开访达搜索窗口默认快捷键【⌥ ⌘ 空格键】可以在键盘【系统偏好设置 -> 键盘->快捷键->聚焦】修改 但是我不会去修改它&#xff0c;因为我不常用访达的搜索窗口&#xff0c;更多的是想快速打开访达文件夹窗口&#xff0c;可以通过第三方软件定义访达的快…

测开——Java、python、SQL、数据结构面试题整理

一、Java 1.Java中finally、final、finalize的区别 1.性质不同 &#xff08;1&#xff09;final为关键字; &#xff08;2&#xff09;finalize()为方法; &#xff08;3&#xff09;finally为为区块标志,用于try语句中; 2. 作用 &#xff08;1&#xff09;final为用于标识…

【通信原理笔记】【三】模拟信号调制——3.2 双边带抑制载波调制(DSB-SC)

文章目录 前言一、DSB-SC的数学表示二、DSB-SC的相干解调三、DSB-SC的性能评价总结 前言 从这一篇开始我们依次介绍几种模拟信号调制的方法&#xff0c;包括其数学表达式&#xff0c;系统框图、解调方式、性能评价等。 一、DSB-SC的数学表示 将 m ( t ) m(t) m(t)作为已调信号…

前端、后端上传文件到OSS,简明记录

前端、后端上传文件到OSS&#xff0c;简明记录 上传文件到oss的方式&#xff1a; **后端上传&#xff1a;**文件先要从页面上传到后端存起来&#xff0c;再通过后端发送到oss&#xff0c;然后后端将存起来的文件删除&#xff08;当然可以不删&#xff09;。 **前端上传&…