FFMpeg example:封装和解封装demux
前言:
本文是下面blog的笔记并增加了步骤解析和注释.
muxer和demux原理和使用的函数一样,audio,video是一样的,所以4种情况分析一种即可.
最简单的基于FFmpeg的封装格式处理:视音频复用器(muxer)_雷霄骅(leixiaohua1020)的专栏-CSDN博客_muxer
正文
上图是raw流,压缩,文件封装,流媒体封装的流程
原理
视音频复用器(Muxer)即是将视频压缩数据(例如H.264)和音频压缩数据(例如AAC)合并到一个封装格式数据(例如MKV)中去。
如图所示。在这个过程中并不涉及到编码和解码。
本文记录的程序将一个H.264编码的视频码流文件和一个MP3编码的音频码流文件,合成为一个MP4封装格式的文件。
流程图
- 1: 打开输入文件和codec
avformat_open_input():,avformat_find_stream_info- 2 : 拷贝new context( avcodec_copy_context)
- 3 : 打开输出文件 avio_open-->avformat_write_header
- 4:av_compare_ts 音视频分离
- 5: 写pts :
在编码之前写pts,否则编码不会产生pts
- 6: av_bitstream_filter_filter(h264bsfc...)
使用bitstream filter add h264 header 0x00 00 00 01- 7 Convert PTS/DTS //转不同时间基对应的pts 和响应的duration
- 8 : av_interleaved write_frame 向文件写一帧
本文介绍的视音频复用器,输入的视频不一定是H.264裸流文件,音频也不一定是纯音频文件。
可以选择两个封装过的视音频文件作为输入。程序会从视频输入文件中“挑”出视频流,音频输入文件中“挑”出音频流,再将“挑选”出来的视音频流复用起来。
PS1:对于某些封装格式(例如MP4/FLV/MKV等)中的H.264,需要用到名称为“h264_mp4toannexb”的bitstream filter。
PS2:对于某些封装格式(例如MP4/FLV/MKV等)中的AAC,需要用到名称为“aac_adtstoasc”的bitstream filter。
代码
int main(int argc, char* argv[])
{const char *in_filename_v = "cuc_ieschool.h264";const char *in_filename_a = "huoyuanjia.mp3";const char *out_filename = "cuc_ieschool.mp4";//Output file URL//Inputavformat_open_input(&ifmt_ctx_v, in_filename_v, 0, 0));avformat_find_stream_info(ifmt_ctx_v, 0));avformat_open_input(&ifmt_ctx_a, in_filename_a, 0, 0));avformat_find_stream_info(ifmt_ctx_a, 0));//Output //1 avformat_new_stream & avcodec_copy_contextavformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);ofmt = ofmt_ctx->oformat;for (i = 0; i < ifmt_ctx_v->nb_streams; i++) {//Create output AVStream according to input AVStreamif(ifmt_ctx_v->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){AVStream *in_stream = ifmt_ctx_v->streams[i];AVStream *out_stream = avformat_new_stream(ofmt_ctx,in_stream->codec->codec);//Copy the settings of AVCodecContextavcodec_copy_context(out_stream->codec, in_stream->codec);......break;}}//audio is the same, skip ......printf("==========Output Information==========\n");//Open output fileavio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE) ;//Write file headeravformat_write_header(ofmt_ctx, NULL);//FIX
#if USE_H264BSFAVBitStreamFilterContext* h264bsfc = av_bitstream_filter_init("h264_mp4toannexb");
#endif
#if USE_AACBSFAVBitStreamFilterContext* aacbsfc = av_bitstream_filter_init("aac_adtstoasc");
#endifwhile (1) {AVFormatContext *ifmt_ctx;int stream_index=0;AVStream *in_stream, *out_stream;//Get an AVPacket //2 av_compare_ts比较音视频pts,大于0表示视频帧在前,音频需要连续编码。小于0表示,音频帧在前,应该至少编码一帧视频if(av_compare_ts(cur_pts_v,ifmt_ctx_v->streams[videoindex_v]->time_base,cur_pts_a,ifmt_ctx_a->streams[audioindex_a]->time_base) <= 0){ifmt_ctx=ifmt_ctx_v;stream_index=videoindex_out;if(av_read_frame(ifmt_ctx, &pkt) >= 0){do{in_stream = ifmt_ctx->streams[pkt.stream_index];out_stream = ofmt_ctx->streams[stream_index];if(pkt.stream_index==videoindex_v){//FIX:No PTS (Example: Raw H.264)//Simple Write PTSif(pkt.pts==AV_NOPTS_VALUE){//Write PTSAVRational time_base1=in_stream->time_base;//Duration between 2 frames (us)//按ffmpeg中的1秒(即90000)来计算每帧的间隔;90000 / 25 = 3600(ffmpeg)int64_t calc_duration=(double)AV_TIME_BASE/av_q2d(in_stream->r_frame_rate);//Parameters//计算一桢在整个视频中的时间位置timestamp(秒) = pts * av_q2d(st->time_base);pkt.pts=(double)(frame_index*calc_duration)/(double)(av_q2d(time_base1)*AV_TIME_BASE);pkt.dts=pkt.pts;pkt.duration=(double)calc_duration/(double)(av_q2d(time_base1)*AV_TIME_BASE);frame_index++;}cur_pts_v=pkt.pts;//注意:读取一帧之后breakbreak;}}while(av_read_frame(ifmt_ctx, &pkt) >= 0);}else { //skip audio...}}//3 FIX:Bitstream Filter
#if USE_H264BSFav_bitstream_filter_filter(h264bsfc, in_stream->codec, NULL, &pkt.data, &pkt.size, pkt.data, pkt.size, 0);
#endif
#if USE_AACBSFav_bitstream_filter_filter(aacbsfc, out_stream->codec, NULL, &pkt.data, &pkt.size, pkt.data, pkt.size, 0);
#endif //Convert PTS/DTS //转不同时间基对应的ptspkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);pkt.pos = -1;pkt.stream_index=stream_index;//Writeav_interleaved_write_frame(ofmt_ctx, &pkt);av_free_packet(&pkt);}//Write file trailerav_write_trailer(ofmt_ctx);....}
- 1:打开输入文件和codec
avformat_open_input(),avformat_find_stream_info... - 2 : 拷贝new context( avcodec_copy_context)
1) 根据codec codec_type 分流audio 和video .
2) 创建new stream
3) 拷贝context到new stream的context (avcodec_copy_context).
audio,video不同stream对应的context不同,
对应上面流程图的acodec_copy_context-->AVCodecContext部分.
avcodec_copy_context作用是复制AVCodecContext的设置(Copy the settings of AVCodecContext)
常用于codec设置拷贝avcodec_copy_context(out_stream->codec, in_stream->codec)
audio,video原理一样 - 3 : 打开输出文件 avio_open-->avformat_write_header
- 4: av_compare_ts比较音视频pts,大于0表示视频帧在前,音频需要连续编码。小于0表示,音频帧在前,应该至少编码一帧视频.
video: av_read_frame-->Get AVPacket
输入的视频不一定是H.264裸流文件,可以选择封装过的视音频文件作为输入。
av_read_frame可以解封装.
读取一帧之后会break,在外层循环读下一帧
audio,video原理一样 - 5: 写pts :
在编码之前写pts,否则编码不会产生pts
//FIX:No PTS (Example: Raw H.264)
//Simple Write PTS
if(pkt.pts==AV_NOPTS_VALUE){//Write PTSAVRational time_base1=in_stream->time_base;//Duration between 2 frames (us)//按ffmpeg中的1秒(即90000)来计算每帧的间隔;90000 / 25 = 3600(ffmpeg)int64_t calc_duration=(double)AV_TIME_BASE/av_q2d(in_stream->r_frame_rate);//Parameters//计算一桢在整个视频中的时间位置timestamp(秒) = pts * av_q2d(st->time_base);
pkt.pts=(double)
(frame_index*calc_duration)/(double)(av_q2d(time_base1)*AV_TIME_BASE);pkt.dts=pkt.pts;pkt.duration=(double)calc_duration/(double)(av_q2d(time_base1)*AV_TIME_BASE);frame_index++;
}
- 6: av_bitstream_filter_filter(h264bsfc...)
使用bitstream filter add h264 header 0x00 00 00 01
详见:最简单的基于FFmpeg的封装格式处理:视音频分离器简化版(demuxer-simple)_雷霄骅(leixiaohua1020)的专栏-CSDN博客_ffmpeg 封装 "分离某些封装格式中的H.264"部分 - 7 Convert PTS/DTS //转不同时间基对应的pts 和响应的duration
pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
时间基转换详见:https://blog.csdn.net/fdsafwagdagadg6576/article/details/122844785
3.6 转封装过程中的时间基转换
- 8 : av_interleaved write_frame 向文件写一帧
小结: 对于文件格式的转换,业务逻辑是使用封装好的函数读写,不需要自己根据协议实现文件格式。比如:调用avio_open,av_interleaved_write_frame,实现文件格式转换。
自己只需做时间戳转换和bsf 添加nalu.
FFMpeg example:封装和解封装demux相关推荐
- 封装和解封装、跨层封装
封装和解封装 我们一般把数据从应用层开始到数据链路层的加工过程称为封装,反过程称为解封装 封装 - 将每一层最重要的数据添加到原始数据当中,来实现这一层的功能. 应用层 - 应用需要进行封装,但是,封 ...
- FFmpeg分离(解封装)视频和音频
使用FFmpeg库对mp4文件进行解封装,提取mp4中的视频流和音频流输出到单独的输出文件中. 所谓的分离视频和音频是我们通俗的说法,官方的说法叫解封装.与解封装对应的叫封装或复用器,也就是将多个视频 ...
- OSI参考模型及其数据封装和解封装介绍
数据的封装和解封装的过程 转载于:https://blog.51cto.com/lorna8023/412673
- 最简单的基于FFMPEG 4.2的封装格式转换器(无编解码MP4转FLV)
文章目录 最简单的基于FFMPEG 4.2的封装格式转换器(无编解码) 配置 代码 结果 关键函数说明 avformat_open_input avformat_find_stream_info av ...
- 网络中的OSI7层模型-封装解封装
文章目录 OSI7层模型-封装解封装 OSI七层模型 OSI七层重要层解释说明(这些记下来) TCP/IP协议栈 主机间的通信模型介绍 主机间通信(数据封装解封装) 名词解释 单位说明 OSI7层模型 ...
- 什么叫封装?封装有什么作用?
一.什么是封装? 封装是把彼此相关数据和操作包围起来,抽象成为一个对象,变量和函数就有了归属,想要访问对象的数据只能通过已定义的接口. 说封装就是将属性私有化,太过狭隘,因为封装不仅仅实现了数据的保护 ...
- java 代码封装_封装 java代码
Java工程师必知词汇:封装 |名词定义| 封装(Encapsulation)是将数据和处理数据的程序组合起来,仅对外公开接口,达到信息隐藏的功能.封装的优点是能减少耦合.Java定义对象都是在语法中 ...
- 封装to封装库_关于封装的三个最伟大的段落
封装to封装库 定义. 封装不是性感的. 这是软件设计的特许会计. 功能性Java编程? 配方freakin'之一. 混合云计算? 固体助推火箭在午夜的发射台上爆炸. 但是封装? 但是,不要将这种缺乏 ...
- Java封装和封装的案例
Java封装和封装的案例 目录 一.Java封装知识点简介 二.Java程序中的包 三.static关键字.代码块 四.封装的综合应用案例: 一.Java封装知识点简介 1.面向对象三大特性之封装的概 ...
最新文章
- String : string的长度?string的子串?
- 在python函数中参数分类的详细教程
- 化工原理 蒸馏(下)
- 第二次Soring冲刺计划第二天(个人)
- 命名空间 namespace
- Hadoop系列之DistributedCache用法
- 成都刘女士的第一场锤子科技发布会 | 现场特写
- 平滑阴影blender_【Blender笔记】简单的创建一个平平无奇的石头
- mybatis中的一级和二级缓存,执行顺序,cache属性的应用
- 创强教师办公用计算机配备要求,教师办公室电脑使用与管理有哪些规定
- 从小学算术的速算与二进制速算,分析基础与窍门的关系:
- BZOJ2794[Poi2012]Cloakroom——离线+背包
- 机器学习:CS 229 - Machine Learning - Supervised Learning cheatsheet
- Kotlin项目实战之手机影音---悦单条目实现及BaseListFragment抽取
- 关于理性形象的塑造——歇洛克·福尔摩斯眼中的世界
- 利用hexo和github搭建静态博客(一)
- 计量经济学-简单的一元线性回归模型之一
- VsCode 如何设置背景图以及字体颜色
- Linux切换用户su root 与 su - root 的区别
- vue和react哪个开发效率高,vue 和 react 哪个前景好