5. FFmpeg核心功能

FFmpeg共有8个可信开发库:

  • avutil 包含一些公共的工具函数,主要有数学函数、字符串操作、内存管理相关、数据结构相关、错误码及错误处理、日志输出、其他复制信息比如密钥、哈希值、宏、库版本、常量等。

  • avformat 用于各种音视频封装格式的生成和解析,包括获取解码所需信息以生成解码上下文结构和读取音视频帧等功能,包含demuxers和muxer库

  • avcodec 用于各种类型声音/图像编解码
  • avfilter 在多媒体处理中,filter的意思是被编码到输出文件之前用来修改输入文件内容的一个软件工具
  • avdevice FFmpeg中有一个和多媒体设备交互的类库:Libavdevice。使用这个库可以读取电脑(或其他设备) 的多媒体设备的数据,或者输出数据到指定的多媒体设备上
  • postproc 用于后期效果处理
  • swresample libswresample库功能主要包括高度优化的音频重采样、rematrixing和样本格式转换操作
  • swscale 用于视频场景比例缩放、色彩映射转换

FFmpeg重要结构体

Image

FFmpeg中的结构体有非常多,其中重要的结构体大概可以分以下几类:

1.解协议(http,rtsp,rtmp,mms,file,tcp,udp ...)

AVIOContext,URLProtocol,URLContext主要存储视音频使用的协议的类型以及状态。URLProtocol存储输入视音频使用的封装格式。每种协议都对应一个URLProtocol结构。(注意:FFMPEG中文件也被当做一种协议“file”)。

2.解封装(flv,rmvb,mp4)

AVFormatContext主要存储视音频封装格式中包含的信息;AVInputFormat存储输入视音频使用的封装格式。每种视音频封装格式都对应一个AVInputFormat 结构。

3.解码(h264,mpeg2,aac,mp3)

每个AVStream存储一个视频/音频流的相关数据;每个AVStream对应一个AVCodecContext,存储该视频/音频流使用解码方式的相关数据;每个AVCodecContext中对应一个AVCodec,包含该视频/音频对应的解码器。每种解码器都对应一个AVCodec结构。

4.存数据

视频的话,每个结构一般是存一帧;音频可能有好几帧

解码前数据:AVPacket

解码后数据:AVFrame

FFmpeg 4.x解码流程

Image

FFMpeg 中主要数据结构存在包含关系,如下标题显示的就是包含层级的关系。

AVFormatContext ->AVStream-> AVCodecContext -> AVCodec,其中后者是前者的数据成员。 AVFormatContext 是一个贯穿始终的数据结构,很多函数都用到它作为参数,是输入输 出相关信息的一个容器。 主要成员如下:

  1. AVInputFormat 和AVOutputFormat,同一时间只能存在一个。当播放视频时AVInputFormat 生效,录制视频时则AVOutputFormat 生效。
  2. AVStream 是继AVFormatContext 之后第二个贯穿始终的数据结构,它保存于数据流相关 的编解码器、数据段等信息,还包含“流”这个概念中的一些信息。

  3. AVCodecContext 保存AVCodec 指针和与codec 相关的数据。

在AVStream 初始化后,AVCodecContext 的初始化时Codec 使用中最重要的一环。 AVCodecContext 中的codec_type,codec_id 二个变量对于encoder/decoder 的匹配来说, 最为重要。 AVCodecContext 中有两个成员:AVCodec,AVFrame。

  • AVCodec 记录了所要使用的Codec 的信息并有5 个函数:init,encoder,close,decode,flush 来完成编解码工作。
  • AVFrame 中主要包饭了编码后的帧信息。
    typedef struct AVFrame {
      FF_COMMON_FRAME
    } AVFrame;
    
    其中FF_COMMON_FRAME 是以宏出现的,由于编码过程中AVFrame 中的数据是要经常存取 的,为了加速,采取这样的代码手段。

API介绍

FFmpeg中奖编码帧及未编码帧均称作frame,这里为了方便,将编码帧称为packet,未编码帧称为frame。 原始图片(yuv、rgb)或声音(pcm流)都是未压缩的,未编码的。 H264/H265, aac/ac3/mp3:都是压缩的,编码的。

  • avformat_open_input() 这个函数会打开输入媒体文件,读取文件头,将文件格式信息存储在第一个参数AVFormatContext中。

  • avformat_find_stream_info() 这个函数会读取一段视频文件数据并尝试解码,将取到的流信息填入AVFormatContext.streams中。 AVFormatContext.streams是一个指针数组,熟读大小是AVFormatContext.nb_streams。

  • av_read_frame 本函数用于解复用过程。 本函数将存储在输入文件中的数据分割为多个packet,每次调用将得到一个packet。packet 可能是视频帧、音频帧或其他数据,解码器只会解码视频帧或音频帧,非音视频数据并不会 被扔掉、从而能向解码器提供尽可能多的信息。 对于视频来说,一个packet 只包含一个视频帧;

对于音频来说,若是帧长固定的格式则一个packet 可包含整数个音频帧,若是帧长可 变的格式则一个packet 只包含一个音频帧。 读取到的packet 每次使用完之后应调用av_packet_unref(AVPacket *pkt)清空packet。否则会 造成内存泄露。

  • av_write_frame() 本函数用于复用过程,将packet 写入输出媒体。 packet 交织是指:不同流的packet 在输出媒体文件中应严格按照packet 中dts 递增的顺序交 错存放。 本函数直接将packet 写入复用器(muxer),不会缓存或记录任何packet。本函数不负责不同 流的packet 交织问题。由调用者负责。 如果调用者不愿处理packet 交织问题,应调用av_interleaved_write_frame()替代本函数。
  • av_interleaved_write_frame() 本函数用于复用过程,将packet 写入输出媒体。 本函数将按需在内部缓存packet,从而确保输出媒体中不同流的packet 能按照dts 增长的顺 序正确交织。
  • avio_open() 创建并初始化一个AVIOContext,用于访问输出媒体文件。
  • avformat_write_header() 向输出文件写入文件头信息。
  • av_write_trailer() 向输出文件写入文件尾信息。

解封装demuxer

1.分配解复用器上下文avformat_alloc_context 2.根据url 打开本地文件或网络流avformat_open_input 3.读取媒体的部分数据包以获取码流信息avformat_find_stream_info 4.读取码流信息:循环处理 4.1 从文件中读取数据包av_read_frame 4.2 定位文件avformat_seek_file 或av_seek_frame 5.关闭解复用器avformat_close_input

转封装

从一种视频容器转成另一种视频容器 所谓的封装格式转换, 就是在AVI , FLV , MKV , MP4 这些格式之间转换( 对 应.avi,.flv,.mkv,.mp4 文件)。需要注意的是,本程序并不进行视音频的编码和解码工作。 而是直接将视音频压缩码流从一种封装格式文件中获取出来然后打包成另外一种封装格式 的文件。

读取视频MetaData信息

#if defined(__cplusplus)
extern "C"
{
#endif
#include "libavcodec/avcodec.h"
#include "libavutil/avutil.h"
#include "libavutil/file.h"
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavformat/avio.h"

#if defined(__cplusplus)
}
#endif

#include <iostream>
using namespace std;


int main(int argc, const char * argv[]) {
    avformat_network_init();
    AVFormatContext *avFmtCtx = NULL;
    char *url = "http://vjs.zencdn.net/v/oceans.mp4";
    int ret = avformat_open_input(&avFmtCtx, url, NULL, NULL);
    if (ret < 0) {
        cout << "error";
        return 0;
    }
    avformat_find_stream_info(avFmtCtx, NULL);
    av_dump_format(avFmtCtx, 0, url, 0);
    avformat_close_input(&avFmtCtx);
    return 0;
}

运行结果:

Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'http://vjs.zencdn.net/v/oceans.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 1
    compatible_brands: isomavc1
    creation_time   : 2013-05-03T22:51:07.000000Z
  Duration: 00:00:46.61, start: 0.000000, bitrate: 3949 kb/s
  Stream #0:0[0x1](und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuv420p(progressive), 960x400 [SAR 1:1 DAR 12:5], 3859 kb/s, 23.98 fps, 23.98 tbr, 24k tbn (default)
    Metadata:
      creation_time   : 2013-05-03T22:50:47.000000Z
      handler_name    : GPAC ISO Video Handler
      vendor_id       : [0][0][0][0]
  Stream #0:1[0x2](und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 92 kb/s (default)
    Metadata:
      creation_time   : 2013-05-03T22:51:07.000000Z
      handler_name    : GPAC ISO Audio Handler
      vendor_id       : [0][0][0][0]

results matching ""

    No results matching ""