用 Wireshark 解码 H.264

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Vue使用高德地图(快速上手)

1.在高德平台注册账号 2.我的 > 管理管理中添加Key 3.安装依赖 npm i amap/amap-jsapi-loader --save 或 yarn add amap/amap-jsapi-loader --save 4.导入 AMapLoade import AMapLoader from amap/amap-jsapi-loader; 5.直接上代码&#xff0c;做好了注释&#xff08;初…

单细胞RNA测序(scRNA-seq)SRA数据下载及fastq-dumq数据拆分

单细胞RNA测序&#xff08;scRNA-seq&#xff09;入门可查看以下文章&#xff1a; 单细胞RNA测序&#xff08;scRNA-seq&#xff09;工作流程入门 单细胞RNA测序&#xff08;scRNA-seq&#xff09;细胞分离与扩增 1. NCBI查询scRNA-seq SRA数据 NCBI地址&#xff1a; https…

前视声呐目标识别定位(六)-代码解析之目标截图并传输

前视声呐目标识别定位&#xff08;一&#xff09;-基础知识 前视声呐目标识别定位&#xff08;二&#xff09;-目标识别定位模块 前视声呐目标识别定位&#xff08;三&#xff09;-部署至机器人 前视声呐目标识别定位&#xff08;四&#xff09;-代码解析之启动识别模块 …

51单片机实验02- P0口流水灯实验

目录 一、实验的背景和意义 二、实验目的 三、实验步骤 四、实验仪器 五、实验任务及要求 1&#xff0c;从led4开始右移 1&#xff09;思路 ①起始灯 &#xff08;led4&#xff09; ②右移 2&#xff09;效果 3&#xff09;代码☀ 2&#xff0c;从其他小灯并向右依…

服务器设置了端口映射之后外网还是访问不了服务器

目录 排查思路参考&#xff1a; 1、确认服务是否在运行 2、确认端口映射设置是否正确 3、使用防火墙测试到服务器的连通性 4、检查服务内部的配置 5、解决办法 6、学习小分享 我们在一个完整的网络数据存储服务系统设备中都会存有业务服务器、防火墙、交换机、路由器&a…

【Laravel】09 用模型批量赋值简化代码 数据库关系

【Laravel】09 用模型批量赋值简化代码 & 数据库关系 1. 用模型批量赋值简化代码2. 数据库关系 1. 用模型批量赋值简化代码 原来存储一个值 2. 数据库关系 这里可以看到两个SQL是一样的

STM32之HAL开发——不同系列SPI功能对比(附STM32Cube配置)

不同系列STM32——SPI框图 F1系列框图 F4系列框图 TI模式时序图特性 F7系列框图 H7系列框图 注意&#xff1a;F7系列以及H7系列支持Quad-SPI模式&#xff0c;可以连接单&#xff0c;双或者四条数据线的Flash存储介质。 SPI——Cube配置流程 RCC时钟源配置 SYS系统调试模式配…

Spring 详细总结

文章目录 第一章 IOC容器第一节 Spring简介1、一家公司2、Spring旗下的众多项目3、Spring Framework①Spring Framework优良特性②Spring Framework五大功能模块 第二节 IOC容器概念1、普通容器①生活中的普通容器②程序中的普通容器 2、复杂容器①生活中的复杂容器②程序中的复…

MySQL、Oracle查看字节和字符长度个数的函数

目录 0. 总结1. MySQL1.1. 造数据1.2. 查看字符/字节个数 2. Oracle2.1. 造数据2.2. 查看字符/字节个数 0. 总结 databasecharbyteMySQLchar_length()length()Oraclelength()lengthB() 1. MySQL 1.1. 造数据 sql drop table if exists demo; create table demo (id …

Cesium 批量种树

1、准备树种建模 分各种级别建模LOD1-LODN 其中meta.json长这样&#xff1a; Gltf再3Dmax中导出Obj,再通过ObjToGltf的工具转换&#xff0c;参考 https://editor.csdn.net/md/?articleId96484597 2、准备shp点数据。&#xff08;shp中的点位就是种树的位置&#xff09; 3、准…

【并发编程】线程安全

线程安全 1. 讲一下 synchronized 关键字的底层原理 1.1 基本使用 如下抢票的代码&#xff0c;如果不加锁&#xff0c;就会出现超卖或者一张票卖给多个人 synchronized&#xff0c;同步【对象锁】采用互斥的方式让同一时刻至多只有一个线程能持有【对象锁】 其它线程再想获…

多模态AI全解析:概念、应用与风险

大家好&#xff0c;在人工智能的快速发展浪潮中&#xff0c;多模态学习作为一项革命性技术&#xff0c;正逐渐改变着我们与机器交互的方式。 自OpenAI推出ChatGPT以来&#xff0c;人工智能已经从处理单一文本输入的单模态工具&#xff0c;迈向了能够理解和生成包括文本、图像、…

【算法】【floodfill】洪水灌溉

文章目录 1. 岛屿数量2. 岛屿最大面积3. 被围绕的区域4. 太平洋大西洋水流问题5. 扫雷游戏6. 机器人的运动范围 1. 岛屿数量 &#x1f449;&#x1f517;题目链接 给你一个由 ‘1’&#xff08;陆地&#xff09;和 ‘0’&#xff08;水&#xff09;组成的的二维网格&#xff0…

查看MySQL版本的方式

文章目录 一、使用cmd输入命令行查看二、在mysql客户端服务器里查询 一、使用cmd输入命令行查看 1、打开 cmd &#xff0c;输入命令行&#xff1a; mysql --version 2、还是打开cmd&#xff0c;输入命令行&#xff1a;mysql -V (注意了&#xff0c;此时的V是个大写的V) 二、…

unity之 “Allow ‘unsafe‘ code“ 在哪里。

导入unity中的代码&#xff0c;出现如下错误&#xff0c;该如何解决&#xff1f; Unsafe code may only appear if compiling with /unsafe. Enable "Allow unsafe code" in Player Settings to fix this error 解决这个问题&#xff0c;只需要设置就可以。 设置的地…

深入理解计算机系统 家庭作业 2.80

/* 网上很多都没说清楚到底出题人是什么用意,用意就是既要又要,既要不溢出,又要不丢失精度.所以就分开处理,在丢失之前把丢失的部分保存下来,然后两部分算好再相加. 可以先看一下我的2.79题 用的是先乘后除 会溢出 符合题意 2.80要求的是先除后成 不会溢出 但会丢失精度 核…

C++中二叉搜索树的模拟实现(二叉搜索树是map,set的底层原理)

搜索二叉树 定义 搜索二叉树:左子树小于根,右子树大于根.搜索二叉树的中序序列是升序的.所以对于二叉树而言,它的左子树和右子数都是二叉搜索树 下图就是二叉搜索树 二叉搜索树的性质: 二叉搜索树的中序遍历出的数据是有序的,并且二叉树搜索树在查找某个数的时候,一般情况下…

9proxy—数据采集工具全面测评

9Proxy数据采集工具Unlock the web with 9Proxy, the top residential proxy provider. Get unlimited bandwidth, affordable prices, and secure HTTPS and Socks5 configurations.https://9proxy.com/?utm_sourceblog&utm_mediumcsdn&utm_campaignyan 前言 在当今数…

如何实现仿微信界面[我的+首页聊天列表+长按菜单功能+添加菜单功能]

如何实现仿微信界面[我的首页聊天列表长按菜单功能添加菜单功能] 一、简介 如何实现仿微信界面[我的首页聊天列表长按菜单功能添加菜单功能] 采用 uni-app 实现&#xff0c;可以适用微信小程序、其他各种小程序以及 APP、Web等多个平台 具体实现步骤如下&#xff1a; 下载…

Windows 2008虚拟机安装、安装VM Tools、快照和链接克隆、添加硬盘修改格式为GPT

一、安装vmware workstation软件 VMware workstation的安装介质&#xff0c;获取路径&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1AUAw_--yjZAUPbsR7StOJQ 提取码&#xff1a;umz1 所在目录&#xff1a;\vmware\VMware workstation 15.1.0 1.找到百度网盘中vmwa…