FFmpeg总体介绍

·  阅读 228

FFmpeg是很优秀的音视频库,很多播放器的内核都是使用ffmpeg来实现的,ijk、暴风影音等......本系列是对FFmpeg4.4源码进行的分析,FFmpeg在源码上不同版本间还是有点差别的,不过流程都很类似。

先介绍FFmpeg大概模块、结构体和基本使用,为之后的分析打个基础,先有个基本框架。

 整体流程图

image.png 这里引用一张流程图,大概流程为注册-初始化数据-探测文件格式-寻找解码器-打开文件(判断起播条件)-收帧-解码-渲染。

模块

  • libavformat 该模块下实现了目前绝大多数媒体封装格式,包括封装和解封装,如MP4、FLV、TS等文件格式;RTMP、RTSP、HLS等网络协议封装格式。

  • libavcodec 该模块下实现了目前绝大多数常用的编解码格式,包括编码和解码。如AAC、MP3、、H.264、H.265等

  • libavutil 该模块包含一些公共的工具函数的使用库,包括算数运算,字符操作等。

  • libswscale 该模块提供了高级别的图像转换API,如按原始视频的比例缩放、色彩映射转换、图像颜色空间或格式转换等功能(比如YUV转RGB)

  • libswresample 该模块提供了高级别的音频重采样API,如采样格式转换、音频通道布局转换与布局等功能

  • libavfilter 该模块提供了一个通用的音频、视频、字幕等滤镜处理框架。

  • libpostproc 该模块用于后期效果处理,如图像的去块效应等。

  • libavdevice 用于硬件的音视频采集、加速和显示。

常用的结构体

封装格式

  • AVFormatContext  描述了媒体文件的构成及基本信息,是统领全局的基本结构体,贯穿程序始终,很多函数都要用它作为参数;
  • AVInputFormat 解复用器对象,每种作为输入的封装格式(例如FLVMP4TS等)对应一个该结构体。如ff_flv_demuxer。
  • AVOutputFormat 复用器对象,每种作为输出的封装格式(例如FLVMP4TS等)对应一个该结构体,如ff_flv_muxer。
  • AVStream 用于描述一个视频/音频流的相关数据信息。

编解码

  • AVCodecContext  描述编解码器上下文的数据结构,包含了众多编解码器需要的参数信息,持有AVCodec。
  • AVCodec 编解码器对象,每种编解码格式(例如H.264AAC等)对应一个该结构体,如aacdec.c。
  • AVCodecParameters 编解码参数,每个AVStream中都含有一个AVCodecParameters,用来存放当前流的编解码参数。

IO协议

  • AVIOContext 提供输入输出数据,透传给了URLContext。
  • URLContext 封装了协议对象及其上下文信息,持有URLProtocol
  • URLProtocol 描述了音视频数据传输所使用的协议,用URLProtocol结构体来表达某种传输协议,如ff_librtmp_protocol、ff_https_protocol。

数据封装

  • AVPacket 存放解码前、编码后的压缩数据,即封装数据
  • AVFrame 存放解码后、编码前、的原始数据,如YUV格式的视频数据或PCM格式的音频数据等;

基本使用

void start(){
    //注册复用器、编码器等
    av_register_all();
    avformat_network_init();
 
    char *url = "fileAbsolutePath";
 
    //打开文件
    AVFormatContext *formatContext = avformat_alloc_context();
    avformat_open_input(&formatContext, url, NULL, NULL);
    avformat_find_stream_info(formatContext, NULL);
 
    // 创建解码器解码线程
    for (int i = 0; i < formatContext->nb_streams; ++i) {
        AVStream *stream = formatContext->streams[i];
        AVCodecParameters *codecParameters = stream->codecpar;
 
        AVCodec *codec = avcodec_find_decoder(codecParameters->codec_id);
        AVCodecContext *codecContext = avcodec_alloc_context3(codec);
        avcodec_parameters_to_context(codecContext, codecParameters);
        avcodec_open2(codecContext, codec, 0)
     
        if (codecParameters->codec_type == AVMEDIA_TYPE_AUDIO) {
            // 创建音频解码线程
        } else if (codecParameters->codec_type == AVMEDIA_TYPE_VIDEO) {
            // 创建视频解码线程
        }  
    }
 
 
    AVPacket pkt;
    int packet = 0;
    //读取音视频数据
    while(isPlaying) {
        packet = av_packet_alloc();
        av_read_frame(formatContext, &packet);
         
        // 音视频解码线程读取packet解码播放
        queue.put(packet);
    }
 
    avformat_close_input(&formatContext);
    avformat_free_context(formatContext);
}
 
void decodePacket() {
    AVPacket *packet;
     
    while (isPlaying) {
        ret = pkt_queue.take(packet);
         
        // 送入解码器
        ret = avcodec_send_packet(avCodecContext, packet);
        releaseAvPacket(packet);
 
        // 解码获取一帧
        AVFrame *frame = av_frame_alloc();
        ret = avcodec_receive_frame(avCodecContext, frame);
 
        frame_queue.put(frame);
    }
}
复制代码
分类:
Android