FFmpeg--FlvPaser源码:解析.flv输出.h264
文章目录
- 程序功能:
- 函数调用流程:
- 部分FlvParse.h
- 部分FlvParse.cpp
程序功能:
解析flv文件,重写一个h264文件,如输入movie.flv , 输出movie.h264
(只有视频,没有声音)
函数调用流程:
1
Process() 处理函数:1 读文件 2 解析flv 3 将flv的视频数据输出到.h264文件2
Parse() 解析flv 文件,入口函数
DumpH264() 生成h264文件3
CreateFlvHeader() 解析 flv head4
CreateTag() 解析视频标签(tag)入口函数5
CVideoTag() 创建video tag6
ParseH264Tag() 解析 vido tag7
ParseH264Configuration() 解析配置信息8
ParseNalu() 解析nalu数据9
DumpH264() 生成h264文件,通过_pMedia写入
部分FlvParse.h
class CFlvParser
{
public:CFlvParser();virtual ~CFlvParser();int Parse(uint8_t *pBuf, int nBufSize, int &nUsedLen);int PrintInfo();int DumpH264(const std::string &path);int DumpAAC(const std::string &path);int DumpFlv(const std::string &path);private:// FLV头typedef struct FlvHeader_s{int nVersion; // 版本int bHaveVideo; // 是否包含视频int bHaveAudio; // 是否包含音频int nHeadSize; // FLV头部长度/*** 指向存放FLV头部的buffer** 上面的三个成员指明了FLV头部的信息,是从FLV的头部中“翻译”得到的,** 真实的FLV头部是一个二进制比特串,放在一个buffer中,由pFlvHeader成员指明*/uint8_t *pFlvHeader;} FlvHeader;// Tag头部struct TagHeader{int nType; // 类型int nDataSize; // 标签body的大小int nTimeStamp; // 时间戳int nTSEx; // 时间戳的扩展字节int nStreamID; // 流的ID,总是0uint32_t nTotalTS; // 完整的时间戳nTimeStamp和nTSEx拼装TagHeader() : nType(0), nDataSize(0), nTimeStamp(0), nTSEx(0), nStreamID(0), nTotalTS(0) {}~TagHeader() {}};class Tag{public:Tag() : _pTagHeader(NULL), _pTagData(NULL), _pMedia(NULL), _nMediaLen(0) {}void Init(TagHeader *pHeader, uint8_t *pBuf, int nLeftLen);TagHeader _header;uint8_t *_pTagHeader; // 指向标签头部uint8_t *_pTagData; // 指向标签body,原始的tag data数据uint8_t *_pMedia; // 指向标签的元数据,改造后的数据int _nMediaLen; // 数据长度};class CVideoTag : public Tag{public:/*** @brief CVideoTag* @param pHeader* @param pBuf 整个tag的起始地址* @param nLeftLen* @param pParser*/CVideoTag(TagHeader *pHeader, uint8_t *pBuf, int nLeftLen, CFlvParser *pParser);int _nFrameType; // 帧类型int _nCodecID; // 视频编解码类型int ParseH264Tag(CFlvParser *pParser);int ParseH264Configuration(CFlvParser *pParser, uint8_t *pTagData);int ParseNalu(CFlvParser *pParser, uint8_t *pTagData);};friend class Tag;private:FlvHeader *CreateFlvHeader(uint8_t *pBuf);Tag *CreateTag(uint8_t *pBuf, int nLeftLen);private:FlvHeader* _pFlvHeader;vector<Tag *> _vpTag;
部分FlvParse.cpp
int main()
{fstream fin;fin.open(argv[1], ios_base::in | ios_base::binary); // 打开文件if (!fin)return 0;Process(fin); //输出文件fin.close();return 1;
}// 循环读文件
void Process(fstream &fin)
{CFlvParser parser;int nBufSize = 2*1024 * 1024;int nFlvPos = 0;uint8_t *pBuf, *pBak;pBuf = new uint8_t[nBufSize];pBak = new uint8_t[nBufSize];while (1){int nReadNum = 0;int nUsedLen = 0;fin.read((char *)pBuf + nFlvPos, nBufSize - nFlvPos); // 循环读取数据,读到内存当中nReadNum = fin.gcount();if (nReadNum == 0)break;nFlvPos += nReadNum;parser.Parse(pBuf, nFlvPos, nUsedLen);if (nFlvPos != nUsedLen){memcpy(pBak, pBuf + nUsedLen, nFlvPos - nUsedLen);memcpy(pBuf, pBak, nFlvPos - nUsedLen);}nFlvPos -= nUsedLen;}parser.PrintInfo();parser.DumpH264("parser.264");delete []pBak;delete []pBuf;
}
// 解析
int CFlvParser::Parse(uint8_t *pBuf, int nBufSize, int &nUsedLen){CheckBuffer(9); // 检测9字节的head// 检测headerwhile(1) {CheckBuffer(15); // nPrevSize(4字节) + Tag header(11字节)}Tag *pTag = CreateTag(pBuf + nOffset, nBufSize-nOffset); //创建头部if (pTag == NULL){nOffset -= 4;break;}nOffset += (11 + pTag->_header.nDataSize);_vpTag.push_back(pTag); // vector<Tag *> _vpTag;
}// 写flv header
CFlvParser::FlvHeader *CFlvParser::CreateFlvHeader(uint8_t *pBuf)
{pHeader->pFlvHeader = new uint8_t[pHeader->nHeadSize];memcpy(pHeader->pFlvHeader, pBuf, pHeader->nHeadSize);
}// 写tag 头部
CFlvParser::Tag *CFlvParser::CreateTag(uint8_t *pBuf, int nLeftLen) {TagHeader header;header.nType = ShowU8(pBuf+0); // 类型header.nDataSize = ShowU24(pBuf + 1); // 1 为 偏移 nType的字节数header.nTimeStamp = ShowU24(pBuf + 4); // 4 为 偏移nType和 nDataSize的 字数之和switch (header.nType) {case 0x09: // 视频类型的TagpTag = new CVideoTag(&header, pBuf, nLeftLen, this);break;case 0x08: // 音频类型的TagpTag = new CAudioTag(&header, pBuf, nLeftLen, this);break;case 0x12: // script TagpTag = new CMetaDataTag(&header, pBuf, nLeftLen, this);break;default: // script类型的TagpTag = new Tag();pTag->Init(&header, pBuf, nLeftLen);}
}// 之后解析tag的类型 :video aac script
// 构造函数
CFlvParser::CVideoTag::CVideoTag(TagHeader *pHeader, uint8_t *pBuf, int nLeftLen, CFlvParser *pParser)
{// 初始化Init(pHeader, pBuf, nLeftLen); uint8_t *pd = _pTagData;_nFrameType = (pd[0] & 0xf0) >> 4; // 帧类型_nCodecID = pd[0] & 0x0f; // 视频编码类型// 开始解析if (_header.nType == 0x09 && _nCodecID == 7) //类型为7进行解析{ParseH264Tag(pParser);}
}int CFlvParser::CVideoTag::ParseH264Tag(CFlvParser *pParser)
{uint8_t *pd = _pTagData;/*** 数据包的类型** 视频数据被压缩之后被打包成数据包在网上传输** 有两种类型的数据包:视频信息包(sps、pps等)和视频数据包(视频的压缩数据)*/int nAVCPacketType = pd[1];int nCompositionTime = CFlvParser::ShowU24(pd + 2);// 如果是视频配置信息if (nAVCPacketType == 0) // AVC sequence header{ParseH264Configuration(pParser, pd);}// 如果是视频数据else if (nAVCPacketType == 1) // AVC NALU{ParseNalu(pParser, pd);}return 1;
}
int CFlvParser::CVideoTag::ParseH264Configuration(CFlvParser *pParser, uint8_t *pTagData)
{uint8_t *pd = pTagData;// 跨过 Tag Data的VIDEODATA(1字节) AVCVIDEOPACKET(AVCPacketType(1字节) 和CompositionTime(3字节) 4字节)// 总共跨过5个字节// NalUnit长度表示占用的字节pParser->_nNalUnitLength = (pd[9] & 0x03) + 1; // lengthSizeMinusOne 9 = 5 + 4 &0x03只占两个字节int sps_size, pps_size;// sps(序列参数集)的长度sps_size = CFlvParser::ShowU16(pd + 11); // sequenceParameterSetLength 11 = 5 + 6// pps(图像参数集)的长度pps_size = CFlvParser::ShowU16(pd + 11 + (2 + sps_size) + 1); // pictureParameterSetLength// 元数据的长度_nMediaLen = 4 + sps_size + 4 + pps_size; // 添加start code_pMedia = new uint8_t[_nMediaLen];// 保存元数据memcpy(_pMedia, &nH264StartCode, 4);memcpy(_pMedia + 4, pd + 11 + 2, sps_size);memcpy(_pMedia + 4 + sps_size, &nH264StartCode, 4);memcpy(_pMedia + 4 + sps_size + 4, pd + 11 + 2 + sps_size + 2 + 1, pps_size);return 1;
}int CFlvParser::CVideoTag::ParseNalu(CFlvParser *pParser, uint8_t *pTagData)
{uint8_t *pd = pTagData;int nOffset = 0;_pMedia = new uint8_t[_header.nDataSize+10];_nMediaLen = 0;// 跨过 Tag Data的VIDEODATA(1字节) AVCVIDEOPACKET(AVCPacketType和CompositionTime 4字节)nOffset = 5; // 总共跨过5个字节 132 - 5 = 127 = _nNalUnitLength(4字节) + NALU(123字节)// startcode(4字节) + NALU(123字节) = 127while (1){// 如果解析完了一个Tag,那么就跳出循环if (nOffset >= _header.nDataSize)break;// 计算NALU(视频数据被包装成NALU在网上传输)的长度,// 一个tag可能包含多个nalu, 所以每个nalu前面有NalUnitLength字节表示每个nalu的长度// video tag data 如果有两个nalu ,一个长度是300字节,一个500字节// 0x00 0x00 0x01 0x2c (300) ------nalu--------0x00 0x00 0x01 0xF4(500)// _pMeida 先拷贝startcode,再拷贝nalu int nNaluLen;switch (pParser->_nNalUnitLength){case 4:nNaluLen = CFlvParser::ShowU32(pd + nOffset);break;case 3:nNaluLen = CFlvParser::ShowU24(pd + nOffset);break;case 2:nNaluLen = CFlvParser::ShowU16(pd + nOffset);break;default:nNaluLen = CFlvParser::ShowU8(pd + nOffset);}// 获取NALU的起始码memcpy(_pMedia + _nMediaLen, &nH264StartCode, 4);// 复制NALU的数据memcpy(_pMedia + _nMediaLen + 4, pd + nOffset + pParser->_nNalUnitLength, nNaluLen);// 解析NALU
// pParser->_vjj->Process(_pMedia+_nMediaLen, 4+nNaluLen, _header.nTotalTS);_nMediaLen += (4 + nNaluLen); // 4 startcodenOffset += (pParser->_nNalUnitLength + nNaluLen);}return 1;
}// 写h264数据
int CFlvParser::DumpH264(const std::string &path)
{fstream f;f.open(path.c_str(), ios_base::out|ios_base::binary);vector<Tag *>::iterator it_tag;for (it_tag = _vpTag.begin(); it_tag != _vpTag.end(); it_tag++){if ((*it_tag)->_header.nType != 0x09)continue;f.write((char *)(*it_tag)->_pMedia, (*it_tag)->_nMediaLen);}f.close();return 1;
}
相关文章:
FFmpeg--FlvPaser源码:解析.flv输出.h264
文章目录 程序功能:函数调用流程:部分FlvParse.h部分FlvParse.cpp 程序功能: 解析flv文件,重写一个h264文件,如输入movie.flv , 输出movie.h264 (只有视频,没有声音) 函数调用流程: 1 Proce…...

【项目笔记】java微服务:黑马头条(day02)
文章目录 app端文章查看,静态化freemarker,分布式文件系统minIO1)文章列表加载1.1)需求分析1.2)表结构分析1.3)导入文章数据库1.3.1)导入数据库1.3.2)导入对应的实体类 1.4)实现思路1.5)接口定义1.6)功能实现1.6.1):导入heima-leadnews-article微服务&am…...

每天五分钟计算机视觉:图像数据不足带来的问题和解决办法
本文重点 在当今的数字时代,图像数据的应用已经渗透到各个领域,包括但不限于计算机视觉、机器学习、自动驾驶、医疗诊断等。然而,当图像数据不足时,会引发一系列问题,对相关应用产生负面影响。 尤其是计算机视觉领域,图像数据尤为珍贵和稀缺,如果计算机视觉的任务中,如…...
手机App防沉迷系统C卷(JavaPythonC++Node.jsC语言)
智能手机方便了我们生活的同时,也侵占了我们不少的时间。"手机App防沉迷系统"能够让我们每天合理的规划手机App使用时间,在正确的时间做正确的事。 它的大概原理是这样的: 1、在一天24小时内,可注册每个App的允许使用时段; 2、一个时段只能使用一个App,举例说明…...
【WEEK2】学习目标及总结【SpringMVC】【中文版】
学习目标: 三周完成SpringMVC入门——第二周 学习内容: 参考视频教程【狂神说Java】SpringMVC最新教程IDEA版通俗易懂使用注释完成MVC程序Controller控制器RestFul风格结果跳转方式数据处理 学习时间及产出: 第二周 MON~FRI 2024.3.4 【W…...

Git版本工具学习
目录 版本控制git配置工作区域文件状态git对象模型基础命令.gitignore忽略文件IDEA集成Git 版本控制 本地版本控制:在本地记录每一次版本更新。 集中版本控制:版本数据都保存在单一服务器,不联网就看不到版本信息。SVN 分布式版本控制&…...
baidu, google和chatgpt -- 翻译对比
原文 That ChatGPT can automatically generate something that reads even superficially like human-written text is remarkable, and unexpected. But how does it do it? And why does it work? My purpose here is to give a rough outline of what’s going on inside…...

高分辨率全球海洋温度和盐度再分析数据Global Ocean Physics Reanalysis(0.083°),并利用matlab读取绘图
1.引言 在研究全球海平面变化的问题中,卫星测高获得总的海平面变化,而海平面变化包含质量变化和比容变化。因此测高数据和海洋物理分析数据对于海平面研究至关重要。 测高数据下载网址: Global Ocean Gridded L 4 Sea Surface Heights And …...
微信小程序修改placeholder样式
微信小程序有既定的修改placeholder的标签 一、placeholder-style直接修改样式 <input type"text" placeholder"请输入" placeholder-style"color:#e2e2e2;"></input>二、placeholder-class设置样式类 <input type"text&…...

爬虫案例1
通过get请求直接获取电影信息 目标页面: https://spa6.scrape.center/在network中可以看到是通过Ajax发送的请求,这个请求在postman中也可以直接请求成功,这只是一个用来练习爬虫的,没有达到js逆向的过程,需要通过分析js 代码来获…...

修改表结构
目录 修改表结构 创建数据表插入数据 修改已有列 修改 member 表的 name 列的定义 为表增加列 增加一个 address 列,这个列上不设置默认值 增加一个 sex 列,这个列上设置默认值 删除表中的列 删除 sex 列 Oracle从入门到总裁:https…...
Rust 语言中的 into() 方法
在 Rust 中,into() 方法通常用于将一个类型的值转换为另一个类型,这通常涉及到资源的所有权转移。into() 方法通常定义在实现了 Into<T> trait 的类型上,该 trait 允许一个类型被“转换”为另一个类型。 into() 方法的一个常见用途是在…...

MinIO权限提升漏洞CVE-2024-24747详细解决办法
漏洞名称: MinIO权限提升漏洞(CVE-2024-24747) 漏洞简介 2024年2月2日,深瞳漏洞实验室监测到一则MinIO 存在权限提升漏洞的信息,漏洞编号:CVE-2024-24747,漏洞威胁等级:高危。 该漏洞是由于用户创建的访…...

“我快无聊死了”用英语怎么说?柯桥英语口语学习,成人零基础学外语
每日一句 Im bored to death. 我快无聊死了。 单词解析: bored / bɔːd / adj.无聊的,厌倦的 bored to d15857575376eath:指非常无聊或厌烦,达到了极点的程度。 "bored" 和 "boring" 都与无聊相关&#…...

JS ATM练习案例(复习循环知识)
需求:用户可以选择存钱、取钱、查看余额和退出功能。 分析:1循环时反复出现提示框,所以提示框写到循环里面。 2.退出的条件是4,所以是4就会结束循环 3.提前准备一个金额预存储 4取钱为减法操作,存钱为加法操作…...
Android 二维码相关(一)
Android 二维码相关(一) 本篇文章主要记录下android下使用zxing来创建二维码. 1: 导入依赖 api "com.google.zxing:core:3.5.1"2: 创建二维码 创建QRCodeWriter对象 QRCodeWriter qrCodeWriter new QRCodeWriter(); 将文本内容转换成BitMatrix BitMatrix encode …...

利用tree命令自动保存文件层级结构
tree命令的使用 为了将上图左侧的文件目录,生成上图右侧中的文件夹结构列表,保存在txt中,使用了如下cmd命令: C:\armadillo-12.8.0>tree .>list.txt以上tree命令分为3部分: tree 命令. 在当前目录>list.tx…...

C++初阶:内存管理
目录 1. C/C中各种资源的内存分布1.1 C/C程序内存区域划分1.2 各资源的内存分布情况(练习) 2. C中的动态内存管理方式2.1 new/delete开辟内置类型空间2.2 new/delete开辟销毁自定义类型空间 3. operator new 与 operator delete函数4. new与delete的实现…...
vue和react的diff算法源码
Vue.js 中的虚拟 DOM Diff 算法是其性能优化的关键之一。 Vue.js 的 Diff 算法主要基于 Snabbdom,以下是 Vue.js 中虚拟 DOM Diff 算法的简化版伪代码,以便说明其基本思想: function patch(oldVnode, vnode) {// 如果 oldVnode 不存在&…...

Coordinate Attention(CVPR 2021)
paper:Coordinate Attention for Efficient Mobile Network Design official implementation:GitHub - houqb/CoordAttention: Code for our CVPR2021 paper coordinate attention 背景 注意力机制,已经被广泛用于提高深度神经网络的性能&…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...

边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
mongodb源码分析session执行handleRequest命令find过程
mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程,并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令,把数据流转换成Message,状态转变流程是:State::Created 》 St…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...
基于Uniapp开发HarmonyOS 5.0旅游应用技术实践
一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架,支持"一次开发,多端部署",可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务,为旅游应用带来…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...