当前位置: 首页 > news >正文

音视频解封装demo:使用libmp4v2解封装(demux)出mp4文件中的h264视频数据和aac语音数据

1、README

前言

本demo是使用的mp4v2来将mp4文件解封装得到h264、aac的,目前demo提供的.a静态库文件是在x86_64架构的Ubuntu16.04编译得到的,如果想在其他环境下测试demo,可以自行编译mp4v2并替换相应的库文件(libmp4v2.a)。

a. 编译
$ make # 或者`make DEBUG=1`打开调试打印信息

如果想编译mp4v2,则可以参考以下步骤:

mp4v2源码下载地址:https://github.com/TechSmith/mp4v2

$ tar xjf mp4v2-2.0.0.tar.bz2
$ cd mp4v2-2.0.0/
$ ./configure --prefix=$PWD/_install # 交叉编译可参考加上选项: --host=arm-linux-gnueabihf
$ make -j96
$ make install
b. 使用

注:示例2中的音视频测试源文件是不同步的,不影响本demo的解封装。

$ ./mp4v2_unpack_demo 
Usage: ./mp4v2_unpack_demo ./avfile/test1.mp4 ./test1_out.h264 ./test1_out.aac./mp4v2_unpack_demo ./avfile/test2.mp4 ./test2_out.h264 ./test2_out.aac
c. 参考文章
  • 01.mp4v2应用—mp4转h264 - wade_linux - 博客园.mhtml

  • mp4文件格式解析 - 简书

  • MP4格式详解_DONGHONGBAI的专栏-CSDN博客

  • 使用mp4v2解码mp4转成h264码流和aac码流_lq496387202的博客-CSDN博客_mp4v2解码

d. demo目录结构
.
├── avfile
│   ├── test1.mp4
│   ├── test1_out.aac
│   ├── test1_out.h264
│   ├── test2.mp4
│   ├── test2_out.aac
│   └── test2_out.h264
├── docs
│   ├── 01.mp4v2应用—mp4转h264 - wade_linux - 博客园.mhtml
│   ├── mp4文件格式解析 - 简书.mhtml
│   ├── MP4格式详解_DONGHONGBAI的专栏-CSDN博客.mhtml
│   └── 使用mp4v2解码mp4转成h264码流和aac码流_lq496387202的博客-CSDN博客_mp4v2解码.mhtml
├── include
│   └── mp4v2
│       ├── chapter.h
│       ├── file.h
│       ├── file_prop.h
│       ├── general.h
│       ├── isma.h
│       ├── itmf_generic.h
│       ├── itmf_tags.h
│       ├── mp4v2.h
│       ├── platform.h
│       ├── project.h
│       ├── sample.h
│       ├── streaming.h
│       ├── track.h
│       └── track_prop.h
├── lib
│   └── libmp4v2.a
├── main.c
├── Makefile
└── README.md

2、主要代码片段

main.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>#include "mp4v2/mp4v2.h"// 编译时Makefile里控制
#ifdef ENABLE_DEBUG#define DEBUG(fmt, args...) 	printf(fmt, ##args)
#else#define DEBUG(fmt, args...)
#endifint unpackMp4File(char *mp4FileName, char *videoFileName, char *audioFileName);unsigned char g_sps[64] = {0};
unsigned char g_pps[64] = {0};
unsigned int  g_spslen  = 0;
unsigned int  g_ppslen  = 0;int main(int argc, char **argv)
{if(argc < 2){printf("Usage: \n""   %s ./avfile/test1.mp4 ./test1_out.h264 ./test1_out.aac\n""   %s ./avfile/test2.mp4 ./test2_out.h264 ./test2_out.aac\n",argv[0], argv[0]);return -1;}int ret = unpackMp4File(argv[1], argv[2], argv[3]);if(ret == 0){printf("\033[32mSuccess!\033[0m\n");}else{printf("\033[31mFailed!\033[0m\n");}return 0;
}int getH264Stream(MP4FileHandle mp4Handler, int videoTrackId, int totalSamples, char *saveFileName)
{// 调用的接口要传的参数uint32_t curFrameIndex = 1; // `MP4ReadSample`函数的参数要求是从1开始,但我们打印帧下标还是从0开始uint8_t *pData = NULL;uint32_t nSize = 0;MP4Timestamp pStartTime;MP4Duration pDuration;MP4Duration pRenderingOffset;bool pIsSyncSample = 0;// 写文件要用的参数char naluHeader[4] = {0x00, 0x00, 0x00, 0x01};FILE *fpVideo = NULL;if(!mp4Handler)return -1;fpVideo = fopen(saveFileName, "wb"); if (fpVideo == NULL){printf("open file(%s) error!\n", saveFileName);return -1;}while(curFrameIndex <= totalSamples){   // 如果传入MP4ReadSample的视频pData是null,它内部就会new 一个内存// 如果传入的是已知的内存区域,则需要保证空间bigger then max frames size.MP4ReadSample(mp4Handler, videoTrackId, curFrameIndex, &pData, &nSize, &pStartTime, &pDuration, &pRenderingOffset, &pIsSyncSample);DEBUG("[\033[35mvideo\033[0m] ");if(pIsSyncSample){DEBUG("IDR\t");fwrite(naluHeader, 4, 1, fpVideo);fwrite(g_sps, g_spslen, 1, fpVideo);fwrite(naluHeader, 4, 1, fpVideo);fwrite(g_pps, g_ppslen, 1, fpVideo);}else{DEBUG("SLICE\t");}if(pData && nSize > 4){// `MP4ReadSample`函数的参数要求是从1开始,但我们打印帧下标还是从0开始;而大小已经包含了4字节的start code长度DEBUG("frame index: %d\t size: %d\n", curFrameIndex - 1, nSize);fwrite(naluHeader, 4, 1, fpVideo);fwrite(pData + 4, nSize - 4, 1, fpVideo); // pData+4了,那nSize就要-4}free(pData);pData = NULL;curFrameIndex++;}       fflush(fpVideo);fclose(fpVideo);  return 0;
}int getAACStream(MP4FileHandle mp4Handler, int audioTrackId, int totalSamples, char *saveFileName)
{// 调用的接口要传的参数uint32_t curFrameIndex = 1; // `MP4ReadSample`函数的参数要求是从1开始,但我们打印帧下标还是从0开始uint8_t *pData = NULL;uint32_t nSize = 0;// 写文件要用的参数FILE *fpAudio = NULL;if(!mp4Handler)return -1;fpAudio = fopen(saveFileName, "wb");if (fpAudio == NULL){printf("open file(%s) error!\n", saveFileName);return -1;}while(curFrameIndex <= totalSamples){// 如果传入MP4ReadSample的音频pData是null,它内部就会new 一个内存// 如果传入的是已知的内存区域,则需要保证空间bigger then max frames size.MP4ReadSample(mp4Handler, audioTrackId, curFrameIndex, &pData, &nSize, NULL, NULL, NULL, NULL);DEBUG("[\033[36maudio\033[0m] ");if(pData){			DEBUG("frame index: %d\t size: %d\n", curFrameIndex - 1, nSize);fwrite(pData, nSize, 1, fpAudio);}free(pData);pData = NULL;curFrameIndex++;}		fflush(fpAudio);fclose(fpAudio);  return 0;
}int unpackMp4File(char *mp4FileName, char *videoFileName, char *audioFileName)
{MP4FileHandle mp4Handler = 0;uint32_t trackCnt = 0;	int videoTrackId = -1;int audioTrackId = -1;unsigned int videoSampleCnt = 0;unsigned int audioSampleCnt = 0;mp4Handler = MP4Read(mp4FileName);if (mp4Handler <= 0){printf("MP4Read(%s) error!\n", mp4FileName);return -1;}trackCnt = MP4GetNumberOfTracks(mp4Handler, NULL, 0); //获取音视频轨道数printf("****************************\n");printf("trackCnt: %d\n", trackCnt);for (int i = 0; i < trackCnt; i++){// 获取trackId,判断获取数据类型: 1-获取视频数据,2-获取音频数据MP4TrackId trackId = MP4FindTrackId(mp4Handler, i, NULL, 0);const char* trackType = MP4GetTrackType(mp4Handler, trackId);if (MP4_IS_VIDEO_TRACK_TYPE(trackType)){// 不关心,只是打印出来看看MP4Duration duration = 0;uint32_t timescale = 0;videoTrackId = trackId;duration = MP4GetTrackDuration(mp4Handler, videoTrackId);timescale = MP4GetTrackTimeScale(mp4Handler, videoTrackId);videoSampleCnt = MP4GetTrackNumberOfSamples(mp4Handler, videoTrackId);printf("video params: \n"" - trackId: %d\n"" - duration: %lu\n"" - timescale: %d\n"" - samples count: %d\n",videoTrackId, duration, timescale, videoSampleCnt);// 读取 sps/pps uint8_t **seqheader;			uint32_t *seqheadersize;uint8_t **pictheader;uint32_t *pictheadersize;MP4GetTrackH264SeqPictHeaders(mp4Handler, videoTrackId, &seqheader, &seqheadersize, &pictheader, &pictheadersize);// 获取spsfor (int ix = 0; seqheadersize[ix] != 0; ix++){memcpy(g_sps, seqheader[ix], seqheadersize[ix]);g_spslen = seqheadersize[ix];free(seqheader[ix]);}free(seqheader);free(seqheadersize);// 获取ppsfor (int ix = 0; pictheader[ix] != 0; ix++){memcpy(g_pps, pictheader[ix], pictheadersize[ix]);g_ppslen = pictheadersize[ix];free(pictheader[ix]);}free(pictheader);free(pictheadersize);}else if (MP4_IS_AUDIO_TRACK_TYPE(trackType)){audioTrackId = trackId;audioSampleCnt = MP4GetTrackNumberOfSamples(mp4Handler, audioTrackId);printf("audio params: \n"" - trackId: %d\n"" - samples count: %d\n",audioTrackId, audioSampleCnt);}}printf("****************************\n");// 解析完了mp4,主要是为了获取sps pps 还有video的trackIDif(videoTrackId >= 0){getH264Stream(mp4Handler, videoTrackId, videoSampleCnt, videoFileName);  }if(audioTrackId >= 0){getAACStream(mp4Handler, audioTrackId, audioSampleCnt, audioFileName);}// 需要mp4close 否则在嵌入式设备打开mp4上多了会内存泄露挂掉.MP4Close(mp4Handler, 0);return 0;
}

3、demo下载地址(任选一个)

  • https://download.csdn.net/download/weixin_44498318/89526730
  • https://gitee.com/linriming/av_mp4_unpack_with_mp4v2.git
  • https://github.com/linriming20/av_mp4_unpack_with_mp4v2.git

相关文章:

音视频解封装demo:使用libmp4v2解封装(demux)出mp4文件中的h264视频数据和aac语音数据

1、README 前言 本demo是使用的mp4v2来将mp4文件解封装得到h264、aac的&#xff0c;目前demo提供的.a静态库文件是在x86_64架构的Ubuntu16.04编译得到的&#xff0c;如果想在其他环境下测试demo&#xff0c;可以自行编译mp4v2并替换相应的库文件&#xff08;libmp4v2.a&#…...

手撸俄罗斯方块(一)——简单介绍

手撸俄罗斯方块 简单介绍 《俄罗斯方块》&#xff08;俄语&#xff1a;Тетрис&#xff0c;英语&#xff1a;Tetris&#xff09;&#xff0c;是1980年末期至1990年代初期风靡全世界的电脑游戏&#xff0c;是落下型益智游戏的始祖&#xff0c;电子游戏领域的代表作之一&a…...

构建LangChain应用程序的示例代码:61、如何使用 LangChain 和 LangSmith 优化链

本示例介绍如何使用 LangChain 和 LangSmith 优化链。 设置 我们将为 LangSmith 设置环境变量&#xff0c;并加载相关数据 import osos.environ["LANGCHAIN_PROJECT"] "movie-qa" # 设置 LANGCHAIN_PROJECT 环境变量为 "movie-qa"import pan…...

Android系统通过属性设置来控制log输出的方案

Android系统通过属性设置来控制log输出的方案 背景 项目中经常需要在针对性的模块或者文件&#xff0c;分析问题的时候输出Log&#xff0c;但问题分析完成后&#xff0c;又由于性能问题&#xff0c;需要关闭这些log输出。当前大多数情况下是控制整个系统的log等级来实现&#…...

JavaDoc的最佳实践

文章目录 一、JavaDoc 使用说明1.1 什么是 JavaDoc1.2 文档注释结构1.3 常见的 Javadoc 标签 二、文档最佳实践2.1 注释原则2.2 实际案例 参考资料 一、JavaDoc 使用说明 1.1 什么是 JavaDoc JavaDoc 是一款能根据源代码中的文档注释来产生 HTML 格式的 API 文档的工具。 Jav…...

数字力量助西部职教全面提升——唯众品牌大数据、人工智能系列产品中标甘肃庆阳职院数字经济人才培养基地!

近日&#xff0c;唯众品牌凭借在大数据和人工智能领域深耕多年的技术积累和卓越产品&#xff0c;成功中标庆阳职业技术学院全国一体化算力网络国家枢纽节点数字经济人才培养基地项目&#xff0c;标志着唯众在助力西部职业教育与数字经济融合发展的新征程上迈出了坚实的一步。 …...

Swagger的原理及应用详解(四)

本系列文章简介: 在当今快速发展的软件开发领域,特别是随着微服务架构和前后端分离开发模式的普及,API(Application Programming Interface,应用程序编程接口)的设计与管理变得愈发重要。一个清晰、准确且易于理解的API文档不仅能够提升开发效率,还能促进前后端开发者之…...

Elasticsearch7.10集群搭建

Elasticsearch详细介绍&#xff1a; Elasticsearch 是一个分布式、RESTful 风格的搜索和分析引擎。它的核心基于 Apache Lucene&#xff0c;能够处理海量的数据&#xff0c;并支持实时的全文搜索。以下是关于 Elasticsearch 的详细介绍。 一、基本概念 索引&#xff08;Index…...

SMU Summer 2024 Contest Round 3

A.Hcode OnlineJudge 先用欧拉筛把质数预处理出来&#xff0c;然后枚举左端点的质数&#xff0c;只需要询问右端点是不是质数并取差值的min就行了 #include<bits/stdc.h> #define endl \n #define mk make_pair #define int long long using namespace std; typedef lon…...

uniapp 封装瀑布流组件

思路&#xff1a; 1.coulumns&#xff1a;需要分成几列 2.如何分布数据 3.计算每列的宽度 4.图片进行高度自适应 <template><view :style"{ margin: boxM }"><view class"flex flex-justify-start bg-red" style"background-colo…...

pd虚拟机去虚拟化是什么意思?pd虚拟机去虚拟化教程 PD虚拟机优化设置

Parallels Desktop for Mac&#xff08;PD虚拟机&#xff09;去虚拟化是指在虚拟机&#xff08;Virtual Machine&#xff0c;简称 VM&#xff09;中禁用或减少虚拟化层的影响&#xff0c;使其表现更接近于物理机。这种操作通常用于提高虚拟机的性能或解决某些软件兼容性问题。具…...

低代码研发项目管理流程优化:提效与创新的双重驱动

随着信息技术的迅猛发展&#xff0c;软件项目的规模和复杂度日益增加&#xff0c;传统的软件开发方式已经难以满足快速迭代和高效交付的需求。在这一背景下&#xff0c;低代码平台应运而生&#xff0c;以其高效、灵活、易用的特点&#xff0c;迅速成为软件行业的新宠。然而&…...

32位版 C 库函数time 将在 2038 年溢出,那到时候,它该何去何从

简单地说&#xff0c;通常不必担心&#xff0c;在64位操作系统已经成为主流的今天这基本上不是问题&#xff08;在写这篇回答的时候&#xff0c;我才发现我甚至找不到32位的机器来测试&#xff09;刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「32库函数的…...

C语言 printf函数缓冲机制

printf不立即打印到stdout的原因 printf函数使用了缓冲机制。当我们调用printf时,输出通常不会立即显示在屏幕上,而是先存储在一个缓冲区中。这是为了提高I/O操作的效率。 缓存数据输出的原理 stdio库维护了一个缓冲区。当缓冲区满了,或者在特定条件下,缓冲区的内容会被刷新…...

【Linux进阶】文件系统8——硬链接和符号连接:ln

在Linux下面的链接文件有两种&#xff0c; 一种是类似Windows的快捷方式功能的文件&#xff0c;可以让你快速地链接到目标文件&#xff08;或目录)&#xff1b;另一种则是通过文件系统的inode 链接来产生新文件名&#xff0c;而不是产生新文件&#xff0c;这种称为硬链接&…...

代码随想录算法训练营Day64|拓扑排序(卡码网117)、dijkstra朴素版

拓扑排序 117. 软件构建 (kamacoder.com) 拓扑排序简单的说是将一个有向图转为线性的排序。 它将图中的所有结点排序成一个线性序列&#xff0c;使得对于任何的边uv&#xff0c;结点u在序列中都出现在结点v之前&#xff0c;这样的序列满足图中所有的前驱-后继关系。 拓扑排…...

neo4j 图数据库:Cypher 查询语言、医学知识图谱

neo4j 图数据库&#xff1a;Cypher 查询语言、医学知识图谱 Cypher 查询语言创建数据查询数据查询并返回所有节点查询并返回所有带有特定标签的节点查询特定属性的节点及其所有关系和关系的另一端节点查询从名为“小明”的节点到名为“小红”的节点的路径 更新数据更新一个节点…...

数据结构基础--------【二叉树基础】

二叉树基础 二叉树是一种常见的数据结构&#xff0c;由节点组成&#xff0c;每个节点最多有两个子节点&#xff0c;左子节点和右子节点。二叉树可以用来表示许多实际问题&#xff0c;如计算机程序中的表达式、组织结构等。以下是一些二叉树的概念&#xff1a; 二叉树的深度&a…...

数据开源 | Magic Data大模型高质量十万轮对话数据集

能够自然的与人类进行聊天交谈&#xff0c;是现今的大语言模型 (LLM) 区别于传统语言模型的重要能力之一&#xff0c;近日OpenAI推出的GPT-4o给我们展示了这样的可能性。 对话于人类来说是与生俱来的&#xff0c;但构建具备对话能力的大模型是一项不小的挑战&#xff0c;收集高…...

webpack之ts打包

tsconfig.json配置 // 是否对js文件进行编译&#xff0c;默认false"allowJs": true,// 是否检查js代码是否符合语法规范,默认false(引入的外部文件有可能语法有问题)"checkJs": true, allowJs和checkJs基本是同时出现&#xff0c;因为有了allowJs 这个检查…...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括&#xff1a;采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中&#xff0c;设置任务排序规则尤其重要&#xff0c;因为它让看板视觉上直观地体…...

2.Vue编写一个app

1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...

测试markdown--肇兴

day1&#xff1a; 1、去程&#xff1a;7:04 --11:32高铁 高铁右转上售票大厅2楼&#xff0c;穿过候车厅下一楼&#xff0c;上大巴车 &#xffe5;10/人 **2、到达&#xff1a;**12点多到达寨子&#xff0c;买门票&#xff0c;美团/抖音&#xff1a;&#xffe5;78人 3、中饭&a…...

图表类系列各种样式PPT模版分享

图标图表系列PPT模版&#xff0c;柱状图PPT模版&#xff0c;线状图PPT模版&#xff0c;折线图PPT模版&#xff0c;饼状图PPT模版&#xff0c;雷达图PPT模版&#xff0c;树状图PPT模版 图表类系列各种样式PPT模版分享&#xff1a;图表系列PPT模板https://pan.quark.cn/s/20d40aa…...

Linux --进程控制

本文从以下五个方面来初步认识进程控制&#xff1a; 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程&#xff0c;创建出来的进程就是子进程&#xff0c;原来的进程为父进程。…...

网站指纹识别

网站指纹识别 网站的最基本组成&#xff1a;服务器&#xff08;操作系统&#xff09;、中间件&#xff08;web容器&#xff09;、脚本语言、数据厍 为什么要了解这些&#xff1f;举个例子&#xff1a;发现了一个文件读取漏洞&#xff0c;我们需要读/etc/passwd&#xff0c;如…...

腾讯云V3签名

想要接入腾讯云的Api&#xff0c;必然先按其文档计算出所要求的签名。 之前也调用过腾讯云的接口&#xff0c;但总是卡在签名这一步&#xff0c;最后放弃选择SDK&#xff0c;这次终于自己代码实现。 可能腾讯云翻新了接口文档&#xff0c;现在阅读起来&#xff0c;清晰了很多&…...

怎么让Comfyui导出的图像不包含工作流信息,

为了数据安全&#xff0c;让Comfyui导出的图像不包含工作流信息&#xff0c;导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo&#xff08;推荐&#xff09;​​ 在 save_images 方法中&#xff0c;​​删除或注释掉所有与 metadata …...

Sklearn 机器学习 缺失值处理 获取填充失值的统计值

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 使用 Scikit-learn 处理缺失值并提取填充统计信息的完整指南 在机器学习项目中,数据清…...

Python学习(8) ----- Python的类与对象

Python 中的类&#xff08;Class&#xff09;与对象&#xff08;Object&#xff09;是面向对象编程&#xff08;OOP&#xff09;的核心。我们可以通过“类是模板&#xff0c;对象是实例”来理解它们的关系。 &#x1f9f1; 一句话理解&#xff1a; 类就像“图纸”&#xff0c;对…...