• 音视频录制器—打包数据流


    ------------------------------------全系列文章目录------------------------------------

    大致流程
    1. 打开输入的aac和h264文件,并找到对应的数据流;创建并打开一个输出文件,为该文件创建视频/音频数据流;同时拷贝输入数据流的编码器信息;创建并初始化一个AVIOContext以访问url指示的资源;写入媒体文件的数据头数据。

    2. 在各自数据流时间基中比较当前视频帧和音频帧pts的顺序,选取下一帧打包到容器的数据包为音频帧还是视频帧

    3. 从对应的输入文件中读取一帧的数据包

    4. 若当前数据包pts为非法值

      1. 根据帧率计算出该数据包持续时间,并根据输入数据流时间基,换算成输入数据流中的持续时间

      2. 根据帧持续时间和帧数计算出当前数据流时间,再根据输入数据流时间基,换算成输入数据流中的时间戳

    5. 将pts,dts和duration从输入数据流时间基转换为输出数据流时间基

    6. 写入数据包至媒体文件

    7. 释放读取的数据包,更新当前视频帧或音频帧pts,重复上述2—6步操作;直至结束后给媒体文件写入文件尾。
      在这里插入图片描述

    相关API函数
    • avformat_alloc_output_context2:申请一个用于输出格式的AVFormatContext。

      int avformat_alloc_output_context2(AVFormatContext **ctx, 
                                         AVOutputFormat *oformat,
                                         const char *format_name, 
                                         const char *filename);
      
      • 1
      • 2
      • 3
      • 4
      • ctx:返回创建的AVFormatContext,失败返回NULL。

      • oformat:用于申请ctx的格式,如果为NULL,则使用format_name和filename。

      • format_name:用于申请ctx的格式名称,如果为NULL,则使用filename。

      • filename:用于申请ctx的文件名称,可能为NULL。

      • 返回值:>=0表示成功,否则失败。

    • avformat_new_stream:增加一路数据流到媒体文件。

      AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c);
      
      • 1
      • s:媒体文件句柄。

      • c:unused, does nothing。

      • 返回值:创建的数据流,为NULL表示失败。

    • avcodec_copy_context:将源AVCodecContext的设置复制到目标AVCodecContext。生成的目标编解码器上下文将是未打开的,在使用此AVCodecContext对视频/音频数据进行解码/编码之前,需要调用avcodec_open2。

      int avcodec_copy_context(AVCodecContext *dest, 
                         const AVCodecContext *src);
      
      • 1
      • 2
      • dest:目标AVCodecContext

      • src:源AVCodecContext

      • 返回值:0表示成功,否则失败

    • avio_open:创建并初始化一个 AVIOContext 以访问url(在此处fille也属于一种url)指示的资源。

      int avio_open(AVIOContext **s, const char *url, int flags);
      
      • 1
      • s:返回指向创建的AVIOContext结构体的指针,NULL表示创建失败。

      • url:要访问的url。

      • flags:如何访问url的标志;AVIO_FLAG_READ / AVIO_FLAG_WRITE / AVIO_FLAG_READ_WRITE。

      • 返回值:>=0表示成功,否则失败。

    • avformat_write_header:申请数据流的私有数据,并将数据流header写入到输出文件

      int avformat_write_header(AVFormatContext *s, AVDictionary **options);
      
      • 1
      • s:必须使用 avformat_alloc_context() 分配的媒体文件句柄。它的 oformat 字段必须设置为所需的输出格式;它的 pb 字段必须设置为已打开的 AVIOContext。

      • options:选项参数。

      • 返回值:AVERROR表示失败。如果编解码器未在avformat_init中初始化,AVSTREAM_INIT_IN_WRITE_HEADER表示成功;已初始化,AVSTREAM_INIT_IN_INIT_OUTPUT表示成功。

    • av_compare_ts:在各自的时间基中比较时间戳

      int av_compare_ts(int64_t ts_a, AVRational tb_a, 
                        int64_t ts_b, AVRational tb_b);
      
      • 1
      • 2
      • ts_a / ts_b:时间戳

      • tb_a / tb_b:时间基

      • 返回值:-1表示ts_a在ts_b之前;0表示相等;1表示ts_a在ts_b之后。

    • av_interleaved_write_frame:将数据包写入输出媒体文件,却把正确交错。

      int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt);
      
      • 1
      • s:媒体文件句柄。

      • pkt:要写入的数据包。

      • 返回值:0表示成功,否则表示失败。

    • av_write_trailer:写入数据流文件尾到输出媒体文件,并释放私有数据;在avformat_write_header成功后才能调用。

      int av_write_trailer(AVFormatContext *s);
      
      • 1
      • s:媒体文件句柄。

      • 返回值:0表示成功,否则失败。

    • av_rescale_q_rnd:通过指定方式,使用两个时间基,调整一个64位整数;即将以时间基cq表示的数值a转换成以时间基bq来表示;

      /* The operation is mathematically equivalent to a * bq / cq */
      int64_t av_rescale_q_rnd(int64_t a, AVRational bq, AVRational cq,
                               enum AVRounding);
      enum AVRounding {
          AV_ROUND_ZERO     = 0, ///趋近于0
          AV_ROUND_INF      = 1, ///趋远于0
          AV_ROUND_DOWN     = 2, ///< Round toward -infinity.
          AV_ROUND_UP       = 3, ///< Round toward +infinity.
          AV_ROUND_NEAR_INF = 5, ///四舍五入
          AV_ROUND_PASS_MINMAX = 8192, ///< Flag to pass INT64_MIN/MAX through instead of rescaling, this avoids special cases for AV_NOPTS_VALUE
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
  • 相关阅读:
    毕业设计 基于51单片机的车辆防追尾系统 - 物联网 嵌入式
    Java如何更高效且大批量地读取文件数据(tsv,csv,txt等等)
    项目总结-新增商品-Pagehelper插件分页查询
    CompletableFuture使用详解
    【老生谈算法】matlab实现蚁群算法源码——数值计算
    MySQL索引失效场景以及解决方案
    基于springboot零食商城
    前端JavaScript入门到精通,javascript核心进阶ES6语法、API、js高级等基础知识和实战 —— Web APIs(四)
    大数据-之LibrA数据库系统告警处理(ALM-25005 Nscd服务异常)
    Linux多线程控制:深入理解与应用(万字详解!)
  • 原文地址:https://blog.csdn.net/weixin_44322983/article/details/126338576