ffmpeg 入门

326 阅读3分钟

概念

  • AVIOContext: 输入输出对应的结构体,用于输入输出(读写文件,RTMP协议等)。通过 avio_alloc_context 进行初始化。
  • AVFormatContext 音视频的封装格式
  • AVStream, AVCodecContext: 用于音视频编解码。AVStream 存储 stream 信息, AVCodecContext 存储编码信息。
  • AVPacket: 存储压缩数据
  • AVFRrame: 存储非压缩数据

转码过程:

  1. 将输入的 stream 作为参数,通过 avio_alloc_context 方法拿到输入输出结构 AVIOContext。

AVFormatContext 根据 index 获取 AVStream,从 AVStream 中获取 AVPacket, 将其解压成 AVFrame, AVFrame 可以编码成 AVPacket. AVPacket 可以组成 AVStream, AVStream 组成 AVFormatContext 返回。

源代码学习转码过程

Code source: ffmpeg-libav-tutorial/3_transcoding.c at master · leandromoreira/ffmpeg-libav-tutorial (github.com)

定义存储结构 StreamingContext

image.png

动态分配存储空间

calloc(1, sizeof()StreamingContext)。AVFormatContext 内存地址信息需要传给 open_media, 用于存储 AVFormatContext. 要分配输入 decoder 和输出 encoder 的内存空间。

读取媒体获取 AVFormatContext: open_media

参数:文件名称,分配的 decoder AVFormatContext 的内存地址 处理:

  • a. 初始化 AVFormatContext
  • b. avformat_open_input 打开文件。该方法会设置 AVFormatContext 的 pb 属性,pb 是 AVIOContext 类型。注: AVFomartContext 可以在调用 avformat_open_input 之前开发自行定义赋值。在 avformat_open_input 时候将其作为参数传入。
  • c. avformat_find_stream_info 查找文件信息, 包括获取每个流中的 time_base

准备输入文件的结构体 decoder: prepare_decoder

入参: StreamContext,把信息放到 StreamContext 结构中 处理:

  • 遍历 AVFormatContext 中的 stream。当 codec 是 video 时可以判断当前是 video stream, 设置 StreamContext 的 video_avs 以及 video_index。
  • fill_stream_info 填充信息,入参是 video stream, video codec 地址,video context 地址。处理过程:根据 videostream 的 codec_id 查询到 AVCodec 赋值给 StreamContext.avc. 根据 AVCodec 初始化AVCodecContext. 打开 AVCodecContext。
  • 同样存储 audio 的 AVCodecContext。

初始化用于输出的AVFormatContext

通过 avformat_alloc_output_context2 打开输出文件,入参为输出格式名,以及输出文件名称。

准备输出文件的结构体 encoder: prepare_encoder

  • a. 通过 av_guess_frame_rate 猜测帧率
  • b. prepare_video_encoder 准备输出文件信息。传入分配的存储空间,codec context, 帧率,AVIOContext。
  • c. prepare_video_encoder 过程:avformat_new_stream 创建通道使得 AVFormatContext 增加该通道,并且赋值给了 encoder.video_avs. 通过参数获取 codec 信息赋值给 encoder.video_codec.

打开输出文件: avio_open

传入 encoder 的信息打开输出文件

写视频文件头

avformat_write_header(encoder -> avfc, &options) 写文件头。这个过程包含了根据输出文件封装格式确定每个流的 time_base 并写入到输出文件中。

写视频文件数据,transcode 发生在这个过程

  • a. av_frame_alloc 初始化 frame ==> input_frame
  • b. av_packet_alloc 初始化 packet ==> input_packet
  • c. 循环 av_read_frame 从输入 AVFormatContext 上下文中 frame 放到 input_packet 中
    • 通过 codec 判断是视频的时候,进行视频转码
    • 视频转码过程:
      • 入参输入输出结构体 AVFormatContext, AVPacket, AVFormatFrame
      • avcodec_send_packet 将包 input_packet 以及 codec context 发送给解码器
      • avcodec_receive_frame 拿到解析以后的 frame
      • 对 frame 进行 encode
        • 初始化输出包 output_packet
        • avcodec_send_frame 将要编码的音频帧送入编码器,如果 frame 为空表示继续刷新内部缓冲区的编码。输出编码后的数据包。
        • 通过 avcodec_receive_packet 拿到编码以后的包 output_packet。 output_packet的 stream_index 被设置为 decoder 的 video_index。计算 packet 的 duration。
        • av_packet_rescale_ts 将 AVPacket 中的时间值从一种时间基转换成另一种时间基。这一步涉及到音视频时间戳可以参考(音视频学习之ffmpeg时间戳相关整理(时间基tbr,tbn,tbc) - 知乎 (zhihu.com))。入参是 packet, 输入流封装格式的时间基、输出流封装格式的时间基。
        • av_interleaved_write_frame(encoder -> avfc, output_packet) 将转码后的 AVPacket
    • 转码一个 frame 后通过 av_packet_unref 将缓存空间的引用计数 -1, 并将 Packet 中的其他字段设置为初始值。
    • 通过 codec 判断如果是一个音频的时候,进行音频拷贝
    • 音频拷贝过程
      • av_packet_rescale_ts(AVPacket, decoder_AVRational, encoder_AVRational) 转化时间基到 packet.
      • av_interleaved_write_frame(encoder->AVFormatContext, packet) 将 packet 写到输出的 AVFormatContext 中。

滤波器

相关结构体: AVFileterGraph、AVFilterContext、AVFilter.

Reference: