前言:

本文是下面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相关推荐

  1. 封装和解封装、跨层封装

    封装和解封装 我们一般把数据从应用层开始到数据链路层的加工过程称为封装,反过程称为解封装 封装 - 将每一层最重要的数据添加到原始数据当中,来实现这一层的功能. 应用层 - 应用需要进行封装,但是,封 ...

  2. FFmpeg分离(解封装)视频和音频

    使用FFmpeg库对mp4文件进行解封装,提取mp4中的视频流和音频流输出到单独的输出文件中. 所谓的分离视频和音频是我们通俗的说法,官方的说法叫解封装.与解封装对应的叫封装或复用器,也就是将多个视频 ...

  3. OSI参考模型及其数据封装和解封装介绍

    数据的封装和解封装的过程 转载于:https://blog.51cto.com/lorna8023/412673

  4. 最简单的基于FFMPEG 4.2的封装格式转换器(无编解码MP4转FLV)

    文章目录 最简单的基于FFMPEG 4.2的封装格式转换器(无编解码) 配置 代码 结果 关键函数说明 avformat_open_input avformat_find_stream_info av ...

  5. 网络中的OSI7层模型-封装解封装

    文章目录 OSI7层模型-封装解封装 OSI七层模型 OSI七层重要层解释说明(这些记下来) TCP/IP协议栈 主机间的通信模型介绍 主机间通信(数据封装解封装) 名词解释 单位说明 OSI7层模型 ...

  6. 什么叫封装?封装有什么作用?

    一.什么是封装? 封装是把彼此相关数据和操作包围起来,抽象成为一个对象,变量和函数就有了归属,想要访问对象的数据只能通过已定义的接口. 说封装就是将属性私有化,太过狭隘,因为封装不仅仅实现了数据的保护 ...

  7. java 代码封装_封装 java代码

    Java工程师必知词汇:封装 |名词定义| 封装(Encapsulation)是将数据和处理数据的程序组合起来,仅对外公开接口,达到信息隐藏的功能.封装的优点是能减少耦合.Java定义对象都是在语法中 ...

  8. 封装to封装库_关于封装的三个最伟大的段落

    封装to封装库 定义. 封装不是性感的. 这是软件设计的特许会计. 功能性Java编程? 配方freakin'之一. 混合云计算? 固体助推火箭在午夜的发射台上爆炸. 但是封装? 但是,不要将这种缺乏 ...

  9. Java封装和封装的案例

    Java封装和封装的案例 目录 一.Java封装知识点简介 二.Java程序中的包 三.static关键字.代码块 四.封装的综合应用案例: 一.Java封装知识点简介 1.面向对象三大特性之封装的概 ...

最新文章

  1. String : string的长度?string的子串?
  2. 在python函数中参数分类的详细教程
  3. 化工原理 蒸馏(下)
  4. 第二次Soring冲刺计划第二天(个人)
  5. 命名空间 namespace
  6. Hadoop系列之DistributedCache用法
  7. 成都刘女士的第一场锤子科技发布会 | 现场特写
  8. 平滑阴影blender_【Blender笔记】简单的创建一个平平无奇的石头
  9. mybatis中的一级和二级缓存,执行顺序,cache属性的应用
  10. 创强教师办公用计算机配备要求,教师办公室电脑使用与管理有哪些规定
  11. 从小学算术的速算与二进制速算,分析基础与窍门的关系:
  12. BZOJ2794[Poi2012]Cloakroom——离线+背包
  13. 机器学习:CS 229 - Machine Learning - Supervised Learning cheatsheet
  14. Kotlin项目实战之手机影音---悦单条目实现及BaseListFragment抽取
  15. 关于理性形象的塑造——歇洛克·福尔摩斯眼中的世界
  16. 利用hexo和github搭建静态博客(一)
  17. 计量经济学-简单的一元线性回归模型之一
  18. VsCode 如何设置背景图以及字体颜色
  19. Linux切换用户su root 与 su - root 的区别
  20. vue和react哪个开发效率高,vue 和 react 哪个前景好

热门文章

  1. AUTOSAR (存储器驱动 CP)
  2. 恒压恒流电源的工作原理
  3. 最新还不错的周易起名网PHP完整源码
  4. 所选之路,为足球而生。2020—2022我与足球那些事
  5. Dede采集插件-织梦采集器无需授权码免费
  6. 小站长如何利用软文进行推广网站
  7. Google Earth Engine(GEE)——MODIS NDVI 时空动态变化的动态图下载案例
  8. 关于论文排版的一些记录
  9. COMSOL——水杯中的自然对流问题
  10. webpack2--tidying up