百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

超强详解FLV解复?实战之FlvParser源码分析

cac55 2024-11-02 10:56 39 浏览 0 评论

1.环境准备

下载flvparser的源码:https://github.com/riverlight/FlvParser

qt工具设置如下:

在以下这个函数打下端点:

这个函数很长,只截出了一部分。


打印的媒体数据如下,与MediaInfo输出的一致:


如下图,是FLVParser的源码结构图:

1.main函数调用Process输出。

2.在process方法,把解析的数据存到内存里。


3.看看这个Parser方法。使用CheckBuffer、CreateFlvHeader检测9字节header。


4.进入CreateFlvHeader方法,观察flvHeader及其debug的数据结构。

pHeader->pFlvHeader = new uint8_t[pHeader->nHeadSize];
 memcpy(pHeader->pFlvHeader, pBuf, pHeader->nHeadSize);

//把这9个字节拷贝起来,然后再缓存起来,最终写到文件里。

flvHeader描述的就是一些flv header的信息,与MediaInfo分析出的信息一样。


还有TagHeader。

关于TAG的父类结构体。

子类CVideoTag、CAudioTag、CMetaDataTag都是继承上面父类的TAG。如下:

其中int ParseH264Configuration(CFlvParser *pParser, uint8_t *pTagData);这里一般就是一个AVC头部信息的解析,这里就是int ParseNalu(CFlvParser *pParser, uint8_t *pTagData)的数据NALU的解析;。int ParseAudioSpecificConfig(CFlvParser *pParser, uint8_t *pTagData);这里一般就是一个AAC音频头的解析,int ParseRawAAC(CFlvParser *pParser, uint8_t *pTagData);对于AAC数据的解析。


这里就会分析出相关信息。


关于媒体脚本数据就是解析视频宽高,采样率等。源码如下。



这里就是一些字段信息。



5.需要有完整的15个字节才能检查出TAG Header。15个字节的来源就是nPrevSize(4字节) + Tag header(11字节)。没有15个字节,就不一定能够检查出来,就需要使用return返回。


识别不同的TAG,然后进行解析。返回,不管是什么类型的TAG,然后插入到队列中。这里可以看出,音视频的裸流是没有时间戳。

6.分别详细看看VideoTag,AudioTag,ScriptTag。


初始化是把头部和body信息一起拷贝了。这里的_pTagData就是body信息。这里表示只处理AVC类型。


再来看看ParseH264Tag的数据包做了什么。

nAVCPacketType = pd[1]表示数据包类型,视频数据被压缩之后被打包成数据包在网上传输。根据包类型,分别去解析视频配置信息,还是真正的视频数据包。

注意:有两种类型的数据包:视频信息包(sps、pps等)和视频数据包(视频的压缩数据)。

那我们接下来,继续看看H264的配置信息,也就是具体解析AVC sequence header。要解析H264的配置信息,那么需要跨过 Tag Data的5个字节,比如VIDEODATA(1字节) AVCVIDEOPACKET(AVCPacketType(1字节) 和CompositionTime(3字节) 4字节),这样才能找到相关的配置信息结构体AVCDecoderConfigurationRecord。

注意:这里还有添加TAG Header的start code。

AVCDecoderConfigurationRecord结构体:


这里要说明下,为什么NALU的长度信息,需要4字节长度对齐。

_nNalUnitLength 这个变量告诉我们用几个字节来存储NALU的长度,如果NALULengthSizeMinusOne是0,那么每个NALU使用一个字节的前缀来指定长度,那么每个NALU包的最大长度是255字节,这个明显太小了,使用2个字节的前缀来指定长度,那么每个NALU包的最大长度是64K字节,也不一定够,一般分辨率达到1280*720 的图像编码出的I帧,可能大于64K;3字节是比较完美的,但是因为一些原因(例如对齐)没有被广泛支持;因此4字节长度的前缀是目前使用最多的方式。

注意下:这几个数据结构的说明。一般的flv和MP4文件都是不带startcode,所以解析的时候就需要添加startcode,这样dump下来的裸流才能够正确播放。

接下来再看看,真正解析H264的视频数据,ParseNalu。这里也需要跨过Tag Data的5个字节,分别是VIDEODATA(1字节) AVCVIDEOPACKET(AVCPacketType和CompositionTime 4字节。132 - 5 = 127 = _nNalUnitLength(4字节) + NALU(123字节)=startcode(4字节) + NALU(123字节) = 127。所以一般NALU数据的也包含了NALU的长度。 计算NALU(视频数据被包装成NALU在网上传输)的长度,一个tag可能包含多个nalu(所以可能就有多帧), 所以每个nalu前面有NalUnitLength字节表示每个nalu的长度,来去表示不同的数据长度。这里的switch就描述了,NALU的长度用多少个字节去描述。

大概就是类似这样的图:


这里有个文件是,用户可以根据自己的需要,对该函数进行修改或者扩展,功能大致就是往视频中写入SEI信息等。如果起始码后面的两个字节是0x05或者0x06,那么表示IDR图像或者SEI信息。


继续看看音频的AudioTag, CAudioTag 音频Tag Data区域开始的第一个字节包含了音频数据的参数信息,这里的第一个字节的信息,就仅供参考了,正真的还是要看AACsecuquence里面的参数配置。从第二个字节开始为音频流数据,但第二个字节对于AAC也要判断是AAC sequence header还是AAC raw。可以对照代码看。


第一个字节:SoundFormat 4bit 音频格式 0 = Linear PCM, platform endian
                        1 =ADPCM; 2 = MP3; 3 = Linear PCM, little endian
                        4 = Nellymoser 16-kHz mono ; 5 = Nellymoser 8-kHz mono
                        6 = Nellymoser;  7 = G.711 A-law logarithmic PCM
                        8 = G.711 mu-law logarithmic PCM; 9 = reserved
                        10 = AAC ; 11  Speex 14 = MP3 8-Khz
                        15 = Device-specific sound
              SoundRate 2bit 采样率 0 = 5.5-kHz; 1 = 11-kHz; 2 = 22-kHz; 3 = 44-kHz
                        对于AAC总是3。但实际上AAC是可以支持到48khz以上的频率。
              SoundSize 1bit 采样精度  0 = snd8Bit; 1 = snd16Bit
                        此参数仅适用于未压缩的格式,压缩后的格式都是将其设为1
              SoundType 1bit  0 = sndMono 单声道; 1 = sndStereo 立体声,双声道
                        对于AAC总是1

这里还要根据数据包类型去解析配置信息还是解析音频数据。

在ParseAudioSpecificConfig才会正真拿到解析配置信息,虽然关于媒体脚本包里面也有音频的相关值,比如采样率,通道数,但是最终还是以这里获取处理的值为准。

接下来在ParseRawAAC就是为了解析AAC的真实的数据,这里正如前面文章分析的,如果输入文件是flv或MP4的时候,一样要每帧裸数据前添加adts头,也就是代码注释中制作元数据的地方。最终把裸流和ADTS头一起拼装起来。

注意: memcpy(_pMedia + 7, pTagData + 2, dataSize),这里 pTagData + 2,表示跳过了2个字节,表示分别跳过了AAC header和AAC RAW里的packet type,他们加在一起是2个字节,这里就是跳过了2个字节。

  // 是bits的最高8bit,实际为0  
  p64[0] = (uint8_t)(bits >> 56); 
   // 才是ADTS起始头 0xfff的高8bit
    p64[1] = (uint8_t)(bits >> 48); 
    p64[2] = (uint8_t)(bits >> 40);
    p64[3] = (uint8_t)(bits >> 32);
    p64[4] = (uint8_t)(bits >> 24);
    p64[5] = (uint8_t)(bits >> 16);
    p64[6] = (uint8_t)(bits >> 8);
    p64[7] = (uint8_t)(bits);


再来看看CMetaDataTag,画红色框的部分,这里的m_amf1_type一定是2,否则就视为错误。如果是2,就去解析MetaDataTag。

这里的不同颜色部分,就表示不同的amf包,就表示一个完整的字段。

看看parseMeta具体解析做了什么工作。为什么这里 uint32_t offset = 13,是偏移了13个字节呢?

根据MediaInfo信息,这里Type是1个字节,Value_Size是2个字节,Value是占用了10个字节,一共就是13个字节,所以就需要偏移13个字节,这个值一般也是固定值。



前面已经把相应字段的名字和value值都读取出来了,就是可以去赋值给相应的字段了。

这里pd[offset++] == 0x08就需要判断类型,读4个字节,就偏移4个字节。


通过分析上面的,每个字段都有一个数据长度,然后才是具体字段的值。结合上面代码的注释看,就是结合不同类型,去读取不同的值,然后获取相应的长度,得到相应的偏移值。

为了解析方便,加上这个打印函数,就可以打印出如下值:


截取dump flv数据的一部分。

DumpH264:

这种dump数据,也是非常实用。


本篇解析源码,也花了很长时间整理,如果对你有用,欢迎关注,点赞,转发,收藏。

如果需要源码注释版本,可以关注,私信。

相关推荐

Mac电脑强制删除任何软件方法-含自启动应用

对于打工者来说,进入企业上班使用的电脑大概率是会被监控起来,比如各种流行的数据防泄漏DLP,奇安信天擎,甚至360安全卫士,这些安全软件你想卸载是非常困难的,甚至卸载后它自己又安装回来了,并且还在你不...

Linux基础知识 | 文件与目录大全讲解

1.linux文件权限与目录配置1.文件属性Linux一般将文件可存取的身份分为三个类别,分别是owner/group/others,且三种身份各read/write/execute等权限文...

文件保护不妥协:2025 年 10 款顶级加密工具推荐

数据安全无小事,2025年这10款加密工具凭借独特功能脱颖而出,从个人到企业场景全覆盖,第一款为Ping32,其余为国外英文软件。1.Ping32企业级加密核心工具,支持200+文件格...

省心省力 一个软件搞定系统维护_省心安装在哪里能找到

◆系统类似于我们居住的房间,需要经常打理才能保持清洁、高效。虽然它本身也自带一些清理和优化的工具,但借助于好用的第三方工具来执行这方面的任务,会更让人省心省力。下面笔者就为大家介绍一款集多项功能于一身...

JAVA程序员常用的几个工具类_java程序员一般用什么软件写程序

好的工具做起事来常常事半功倍,下面介绍几个开发中常用到的工具类,收藏一下,也许后面真的会用到。字符串处理:org.apache.commons.lang.StringUtilsisBlank(Char...

手工解决Windows10的若干难题_windows10系统卡顿怎么解决

【电脑报在线】很多朋友已经开始使用Win10,估计还只是测试版本的原因,使用过程中难免会出现一些问题,这里介绍解决一些解决难题的技巧。技巧1:让ProjectSpartan“重归正途”从10074...

System32文件夹千万不能删除,看完这篇你就知道为什么了

C:\Windows\System32目录是Windows操作系统的关键部分,重要的系统文件存储在该目录中。网上的一些恶作剧者可能会告诉你删除它,但你不应该尝试去操作,如果你尝试的话,我们会告诉你会发...

Windows.old 文件夹:系统备份的解析与安全删除指南

Windows.old是Windows系统升级(如Win10升Win11)或重装时,系统自动在C盘创建的备份文件夹,其核心作用是保留旧系统的文件、程序与配置,为“回退旧系统”提供保...

遇到疑难杂症?Windows 10回收站问题巧解决

回收站是Windows10的一个重要组件。然而,我们在使用过程中,可能会遇到一些问题。例如,不论回收站里有没有文件,都显示同一个图标,让人无法判别回收站的空和满的真实情况;没有了像Windows7...

卸载软件怎么彻底删掉?简单几个步骤彻底卸载,电脑小白看过来

日常工作学习生活中,我们需要在安装一些软件程序,但随着软件的更新迭代速度,很多时候我们需要重新下载安装新的程序,这时就需要将旧的一些软件程序进行卸载。但是卸载软件虽然很简单,但是很多小伙伴们表示卸载不...

用不上就删!如何完全卸载OneDrive?

作为Windows10自带的云盘,OneDrive为资料的自动备份和同步提供了方便。然而,从隐私或其他方面考虑,有些人不愿意使用OneDrive。但Windows10本身不提供直接卸载OneDri...

【Linux知识】Linux下快速删除大量文件/文件夹方法

在Linux下,如果需要快速删除大量文件或文件夹,可以使用如下方法:使用rm命令删除文件:可以使用rm命令删除文件,例如:rm-rf/path/to/directory/*这个命令会递...

清理系统不用第三方工具_清理系统垃圾用什么软件

清理优化系统一定要借助于优化工具吗?其实,手动优化系统也没有那么神秘,掌握了方法和技巧,系统清理也是一件简单和随心的事。一方面要为每一个可能产生累赘的文件找到清理的方法,另一方面要寻找能够提高工作效率...

系统小技巧:软件卸载不了?这里办法多

在正常情况下,我们都是通过软件程序组中的卸载图标,或利用控制面板中的“程序和功能”模块来卸载软件的。但有时,我们也会发现利用卸载图标无法卸载软件或者卸载图标干脆丢失找不到了,甚至控制面板中卸载软件的功...

麒麟系统无法删除文件夹_麒麟系统删除文件权限不够

删除文件夹方法例:sudorm-rf文件夹名称。删除文件方法例:sudorm-r文件名包括扩展名。如果没有权限,给文件夹加一下权限再删。加最高权限chmod775文件名加可执行权限...

取消回复欢迎 发表评论: