文档章节

解密多媒体封装解封装框架

MichaelWH
 MichaelWH
发布于 2015/04/03 09:46
字数 1667
阅读 953
收藏 56

        上一篇文章我们搭好了环境并编译出所需的ffmpeg库,本篇我们讨论如何利用ffmpeg提供的API函数进行多媒体文件的解封装(demux)过程。在讲解之前,我们需要了解一些基本的多媒体文件知识,大虾请飘过。

  • 容器格式:不管是音频文件还是视频格式的文件,都是一个多媒体的容器,即container,比如常见的视频容器格式有avi、mp4、mkv、flv、rm/rmvb、mov、ts、vob、dat,音频容器格式有MP3、WAV、AAC、APE,FLAC等等,它容纳了视频、音频、字幕(subtitle)等一个或多个基本流数据,有的甚至一个容器中存放有多个视频、音频以及字幕。

  • 压缩格式:对视频、音频数据的基本流进行的压缩方式就是音视频的压缩格式。常见的视频压缩格式如mpeg2、mpeg4、H264、VC1、Rm/Rmvb,常见音频压缩格式如MPA、AAC、AC3、DTS。注意这里的部分名字和上面的一样,但意义不同,上面是封装格式,这里是压缩格式。为什么要压缩呢?因为不压缩的话,要存储图像或声音就需要非常多的空间,比如mpeg2压缩比能达到25:1左右,而H264甚至能达到102:1的惊人程度!

  • ES:也就是ElementaryStream,也称为基本流、组件流等称呼,就是单独的一路视频、一条音频、一个subtitle字幕或者单个的附加数据。显然常见的多媒体文件一个都有一个视频ES、音频ES,有的也含有多个视频ES和音频ES以及subtitleES。比如蓝光原版的TS一般都含有多个音轨ES和字幕ES,但不是所有有字幕都有字幕ES,可能字幕已经内嵌进视频,这样的字幕其实成了视频的一部分。

  • Demux:在播放时,需要把这些视音频以及字幕等基本流分离出来,这个过程就叫Demux,或者解封装,也称为解复用。分离出来的各个基本流(ES)分别送给视频解码器、音频解码器等解码后才能得到图像声音。Demux过程如下图(subtitle也可能需要解码):


  • Remux:当然Demux反过来把基本的音频、视频、字幕等组合成一个完整的多媒体就是Remux或者封装,也称为复用。比如很多电影网站的音视频压制的人就需要先做Demux,分离成ES,在加入必要的中文字幕和音轨后、重新封装。所有的转码工具也都必须有Remux和重新Demux的过程。复用与解复用的概念对于熟悉DVB行业的读者来说应该比较清楚。

  • PTS:也就是显示时间戳,指图像或者声音在解码后应该显示或者发声的时间点。音视频不是一解码出来就播出来,否则就乱了,性能好的解码器播放的快,差的播放的慢,并且视频和音频也对不上号。所有这些都是靠PTS来同步的。至于DTS解码时间戳在现在相对以前较大解码内存缓冲下,显得不那么重要了。

有了这些基本的多媒体知识,我们就可以继续讲解如何利用ffmpeg来进行Demux这个过程。首先介绍一下主要的几个API函数:

intavformat_open_input(AVFormatContext **ps, const char *filename,

AVInputFormat *fmt, AVDictionary **options);

这个函数用于打开多媒体文件,并读取相关文件头信息。

voidavformat_close_input(AVFormatContext **ps);

这个函数用于关闭上面打开的多媒体文件,释放相关资源。

intavformat_find_stream_info(AVFormatContext *ic, AVDictionary**options);

这个函数通过注册的文件格式解析器读取文件的取各种信息,比如播放持续时间、音视频压缩格式、音轨信息、字幕信息、帧率、采样率等等。

int av_read_frame(AVFormatContext*s, AVPacket *pkt);

这个函数对于Demux过程是最重要的一个函数,它从文件中读取一帧视频、一帧或多帧音频、字幕等ES数据包,除了数据本身之外,还包括PTS、持续时间、参考帧等重要信息。

void av_free_packet(AVPacket *pkt);

这个函数用于释放ES数据包,与上面的函数成对使用。

有了这些函数和上面的基本知识,下面我们来实现一个简单的Demux框架实例。这个实例的功能是把多媒体文件中的音视频ES数据抽出来分别写入不同文件。我们为了简单,这里不处理返回错误,在实际项目中自己添加错误处理机制。本文力求用最简单最原始的方式把ffmpeg解封装的基本框架讲解清楚。

#include <stdio.h>
#include "libavformat/avformat.h"

static const char *media_file = "test_media.mp4";
int main(void)
{
int i, vid_idx, aud_idx;
FILE *fp_vides = NULL, *fp_audes = NULL;
AVFormatContext *pFormatCtx = NULL;
AVPacket pkt;

av_register_all();
avformat_open_input(&pFormatCtx, media_file, NULL, NULL);
avformat_find_stream_info(pFormatCtx, NULL);

fp_vides = fopen("vid_es.dat", "wb");
fp_audes = fopen("aud_es.dat", "wb");
// 1, handle stream info
for (i=0; i<pFormatCtx->nb_streams; i++)
{
if (pFormatCtx->streams[i]->codec->codec_type ==AVMEDIA_TYPE_VIDEO)
vid_idx = i;
else if (pFormatCtx->streams[i]->codec->codec_type ==AVMEDIA_TYPE_AUDIO)
aud_idx = i;
else
;//such as subtitile
}
while (av_read_frame(pFormatCtx, &pkt) >= 0)
{
// 2, handle pkt data
if (pkt.stream_index == vid_idx)
fwrite(pkt.data, pkt.size, 1, fp_vides);
else if (pkt.stream_index == aud_idx)
fwrite(pkt.data, pkt.size, 1, fp_audes);
else
;// such as subtitile
av_free_packet(&pkt);
}
fclose(fp_vides);
fclose(fp_audes);
avformat_close_input(&pFormatCtx);
return 0;
}

在注释1的地方,需要处理基本流索引与音视频对应的关系和重要信息记录,这个关系会在注释2的地方用到,并且也是后续的多音轨、字幕切换的凭据,本例只处理了最简单的只有一路音视频的情况,且没有对其他信息进行记录,比如帧率、视频宽高、编码类型、时间标度、第一个PTS等等。原则上这些跟Demux的框架没有关系,且每个人有有自己的处理方式,就不在这里贴出来。

第一时间获得博客更新,获得更详细信息和Demo代码,请关注微信号:程序员互动联盟,扫一扫下方二维码或者搜索微信号coder_online即可关注,我们可以在线交流。

如需转载请注明出处:谢谢合作!


© 著作权归作者所有

共有 人打赏支持
MichaelWH
粉丝 9
博文 13
码字总数 10328
作品 0
东城
私信 提问
加载中

评论(15)

MichaelWH
MichaelWH

引用来自“金三胖”的评论

引用来自“瘦石”的评论

写的很好,收益

test
谢谢,我会再接再厉,更多内容请关注公众号coder_online
MichaelWH
MichaelWH
请关注公众号coder_online,获取所有以往发布的博客分享,谢谢
我的乖乖
我的乖乖
上一篇在哪,lz?
金三胖
金三胖
test5
金三胖
金三胖
test4
金三胖
金三胖

引用来自“东辉在线”的评论

Lz写的很棒

test
金三胖
金三胖

引用来自“瘦石”的评论

写的很好,收益

test
金三胖
金三胖
test2
金三胖
金三胖
test
金三胖
金三胖
test
Asp.Net Core 2.0 项目实战(7)MD5加密、AES&DES对称加解密

Asp.Net Core 2.0 项目实战(1) NCMVC开源下载了 Asp.Net Core 2.0 项目实战(2)NCMVC一个基于Net Core2.0搭建的角色权限管理开发框架 Asp.Net Core 2.0 项目实战(3)NCMVC角色权限管理前...

郑州-在路上
2018/04/26
0
0
利用FFMPEG简单分离音视频数据流

上一篇文章我们搭好了环境并编译出所需的ffmpeg库,本篇我们讨论如何利用ffmpeg提供的API函数进行多媒体文件的解封装(demux)过程。在讲解之前,我们需要了解一些基本的多媒体文件知识,大虾...

yang_danny
2015/04/11
0
0
OSChina 技术周刊第二十八期 —— 用 React 编写移动应用

每周技术抢先看,总有你想要的! 移动开发 【软件】RichEditor for Android 【软件】用 React 编写移动应用 React Native 【软件】iOS 图表控件 ios-charts 【博客】iOS 越狱开发——如何将应...

OSC编辑部
2015/04/05
1K
0
OSChina 技术周刊第二十八期 —— 每周技术精粹

每周技术抢先看,总有你想要的! 移动开发 【软件】RichEditor for Android 【软件】用 React 编写移动应用 React Native 【软件】iOS 图表控件 ios-charts 【博客】【iOS越狱开发】如何将应...

OSC编辑部
2015/04/05
0
0
Android NDK开发之旅30--FFmpeg视频播放

1.播放多媒体文件步骤 通常情况下,我们下载的视频文件如MP4,MKV、FLV等都属于封装格式,就是把音视频数据按照相应的规范,打包成一个文本文件。我们可以使用MediaInfo这个工具查看媒体文件...

香沙小熊
2017/12/08
0
0

没有更多内容

加载失败,请刷新页面

加载更多

深入 理解char * ,char ** ,char a[ ] ,char *a[] 的区别

C语言中由于指针的灵活性,导致指针能代替数组使用,或者混合使用,这些导致了许多指针和数组的迷惑,因此,刻意再次深入探究了指针和数组这玩意儿,其他类型的数组比较简单,容易混淆的是字...

天王盖地虎626
16分钟前
1
0
关于我这三年的架构历程(待完成)

从16年7月实习至今,快三年的开发经历中,经手了好几个项目。目前有幸作为一个项目的负责人,完成了一个项目的完全架构设计。因此想记录下这份架构设计中的点点面面。 总架构: 基于DNS的负载...

赵熠熠
17分钟前
0
0
springboot 使用 flyway 进行数据库版本管理

要在启动时自动运行Flyway数据库迁移,请将其添加 org.flywaydb:flyway-core到类路径中。 迁移是表单中的脚本V<VERSION>__<NAME>.sql(使用<VERSION>下划线分隔的版本,例如“1”或“2_1”)...

NotFound403
36分钟前
4
0
spring 5.1.5版本(二)

spring 5.1.5版本(一) spring 5.1.5版本(二) spring 5.1.5版本(三) 对象创建方式 方式一 applicationContext.xml <?xml version='1.0' encoding='UTF-8'?><beans xmlns="http://ww......

gwl_
37分钟前
0
0
CMake生成Mingw用的Make文件

CMake 在win下 默认会生成vc++的nmake用的make 当没安装时 就会报 -- Building for: NMake Makefiles -- The C compiler identification is unknown -- The CXX compiler identification is......

shzwork
55分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部