美文网首页
FFmpeg笔记(五)-- 编解码函数详解

FFmpeg笔记(五)-- 编解码函数详解

作者: rookiesss | 来源:发表于2020-05-11 10:21 被阅读0次

    1.libavformat

    AVFormatContext可以进行格式的封装与解封装,它的数据部分由底层提供,底层使用了AVIOContext,这个AVIOContext实际上就是为普通的I/O增加了一层Buffer缓冲区,再往 底层就是URLContext,也就是到达了协议层。如图:


    libavformat

    相关函数及结构体:

    av_register_all()

    编译FFmpeg的时候,做了一个configure的配置,configure的配置会生成两个文件:config.mk与config.h。 config.mk实际上就是makefile文件需要包含进去的子模块,会作用在编译阶段,帮助开发者编译出正确的库;而config.h是作用在运行阶段, 这一阶段将确定需要注册哪些容器以及编解码格式到FFmpeg框架中。 所以该函数的内部实现会先调用avcodec_register_all来注册所有config.h 里面开放的编解码器,然后会注册所有的Muxer和Demuxer(也就是封 装格式),最后注册所有的Protocol(即协议层的东西)。

    avformat_open_input()

    avformat_open_input会根据所提供的文件路径判断文件的格式,其实就是通过这一步来决定使用的到底是哪一个Demuxer。

    avformat_find_stream_info()

    该方法的作用就是把所有Stream的MetaData信息填充好。方法内部会先查找对应的解码器,然后打开对应的解码器,紧接着会利用Demuxer中的 read_packet函数读取一段数据进行解码,当然解码的数据越多,分析出的流信息就会越准确,如果是本地资源,那么很快就可以得到非常准确的信息了,但是对于网络资源来说,则会比较慢,因此该函数有几个参数可以控制读取数据的长度,一个是probe size,一个是max_analyze_duration,还有一个是fps_probe_size,这三个参数共同控制解码数据的长度,当然,如果配置这几个参数的值越小,那么这个函数执行的时间就会越快,但是会导致AVStream结构体里面一些信息(视频的宽、高、fps、编码类型等)不准确。

    av_find_best_stream()

    获取各种流的索引。

    av_read_frame()

    使用该方法读取出来的数据是AVPacket,在FFmpeg的早期版本中 开放给开发者的函数其实就是av_read_packet,但是需要开发者自己来处理AVPacket中的数据不能被解码器完全处理完的情况,即需要把未处理完的压缩数据缓存起来的问题。所以到了新版本的FFmpeg中,其提供了该函数,用于处理此状况。该函数的实现首先会委托到Demuxer的 read_packet方法中去,当然read_packet通过解复用层和协议层的处理之后,会将数据返回到这里,在该函数中进行数据缓冲处理。为了保证每次读取完整的一帧,读取到的数据长度可能不一样。

    2.libavcodec

    对于开发者来说,这一层我们能接触到的最顶层的结构体就是 AVCodecContext,该结构体包含的就是与实际的编解码有关的部分。首先,AVCodecContext是包含在一个AVStream里面的,即描述了这路流的编码格式是什么,其中存放了具体的编码格式信息,根据Codec的信息可以打开编码器或者解码器,然后利用该编码器或者解码器进行 AVPacket与AVFrame之间的转换(实际上就是解码或者编码的过程), 这是FFmpeg中最重要的一部分。结构如图:


    libavformat

    相关函数及结构体:

    avcodec_find_decoder()

    根据上下文找到解码器。

    avcodec_open2()

    打开解码器。

    av_frame_alloc()

    AVFrame对象必须调用av_frame_alloc()在堆上分配。

    avcodec_decode_audio4()
    参数1:avctx编解码器上下文
    参数2:frame用于存储解码音频样本的AVFrame
    参数3:got_frame_ptr如果没有帧可以解码则为零,否则为非零
    参数4:avpkt包含输入缓冲区的输入AVPacket
    参数5:如果在解码期间发生错误,则返回否定错误代码,否则返回从输入AVPacket消耗的字节数。
    
    avcodec_send_packet():发送解码未处理数据
    avcodec_receive_frame():接收解码后的数据
    avcodec_send_frame:发送编码未处理数据
    avcodec_receive_packet():接收编码后的数据

    一个packet会被解码出一个frame,不过也存在一个packet被解码出多个frame或者多个packet才能解码出一个frame的情况,甚至也有些解码器在输入以及输出端上可能会有延迟。了解更多看这里

    返回状态:

    send 0        :send_packet返回值为0,正常状态,意味着输入的packet被解码器正常接收。
    send EAGAIN   :send_packet返回值为EAGAIN,输入的packet未被接收,需要输出一个或多个的frame后才能重新输入当前packet。
    send EOF      :send_packet返回值为EOF,当send_packet输入为NULL时才会触发该状态,用于通知解码器输入packet已结束。
    send EINVAL   :没有打开×××,或者这是一个编码器,或者要求刷新
    receive 0     :receive_frame返回值为0,正常状态,意味着已经输出一帧。
    receive EAGAIN:receive_frame返回值为EAGAIN,未能输出frame,需要输入更多的packet才能输出当前frame。
    receive EOF   :receive_frame返回值为EOF,当处于send EOF状态后,调用一次或者多次receive_frame后就能得到该状态,表示所有的帧已经被输出。
    简略流程:av_read_frame读取一帧数据,avcodec_send_packet发送数据,解码后avcodec_receive_frame接收一个完整帧,但因为一些特殊原因avcodec_send_packet发送的不是一个完成帧,此时avcodec_receive_frame会返回
    

    3.libavutil

    av_image_get_buffer_size()

    给缓冲区设置类型类型,得到YUV420P缓冲区大小。

    参数一:视频像素数据格式类型->YUV420P格式
    参数二:一帧视频像素数据宽 = 视频宽
    参数三:一帧视频像素数据高 = 视频高
    参数四:字节对齐方式->默认是1
    int buffer_size = av_image_get_buffer_size(AV_PIX_FMT_YUV420P,
                                               videoCodecCtx->width,
                                               videoCodecCtx->height,
                                               1);
    
    av_image_fill_arrays()

    缓存区填充数据。

    参数一:目标->填充数据(avframe_yuv420p)
    参数二:目标->每一行大小
    参数三:原始数据
    参数四:目标->格式类型
    参数五:宽
    参数六:高
    参数七:字节对齐方式
    av_image_fill_arrays(avframe_yuv420p->data,
                         avframe_yuv420p->linesize,
                         out_buffer,
                         AV_PIX_FMT_YUV420P,
                         videoCodecCtx->width,
                         videoCodecCtx->height,
                         1);
    

    4.libswresample

    音频重采样,就是改变音频的采样率、sample format、声道数等参数,使之按照我们期望的参数输出。

    SwrContext

    音频格式转换上下文。

    swr_alloc()

    初始化上下文结构体。

    swr_alloc_set_opts()

    设置重采样参数。

    swr_init()

    在swr_alloc_set_opts之后调用,将采样参数设置到上下文。

    参数1:重采样上下文
    参数2:输出的layout, 如:5.1声道…
    参数3:输出的样本格式。Float, S16, S24
    参数4:输出的样本率。可以不变。
    参数5:输入的layout。
    参数6:输入的样本格式。
    参数7:输入的样本率。
    参数8,参数9,日志,不用管,可直接传0 */
    
    av_samples_get_buffer_size()

    使用av_sample_get_buffer_size来计算音频占用的字节数。

    参数1:linesize calculated linesize, may be NULL
    参数2:nb_channels   声道
    参数3:nb_samples    单个通道中的样本数
    参数4:sample_fmt    采样格式
    参数5:align         对齐缓冲区大小对齐(0 =默认,1 =无对齐)
    
    swr_convert()

    转码。使用avcodec_decode_audio4函数解码音频得到的数据类型为float 4bit,而播放器播放的格式一般为S16(signed 16bit),这就需要对解码得到的数据进行转换。

    参数1:音频重采样的上下文
    参数2:输出的指针。传递的输出的数组
    参数3:输出的样本数量,不是字节数。单通道的样本数量。
    参数4:输入的数组,AVFrame解码出来的DATA
    参数5:输入的单通道的样本数量。
    

    5.libswscale

    SwsContext

    视频格式转换上下文,编解码中可能需要对视频格式重新设置,就需要用到SwsContext。

    sws_getContext()

    设置视频转换上下文。

    参数一:源文件->原始视频像素数据格式宽
    参数二:源文件->原始视频像素数据格式高
    参数三:源文件->原始视频像素数据格式类型
    参数四:目标文件->目标视频像素数据格式宽
    参数五:目标文件->目标视频像素数据格式高
    参数六:目标文件->目标视频像素数据格式类型
    sws_getContext(videoCodecCtx->width,
                   videoCodecCtx->height,
                   videoCodecCtx->pix_fmt,
                   videoCodecCtx->width,
                   videoCodecCtx->height,
                   AV_PIX_FMT_YUV420P,
                   SWS_FAST_BILINEAR,
                   NULL,
                   NULL,
                   NULL);
    
    sws_scale()

    视频参数转换。

    参数一:视频像素数据格式上下文
    参数二:原来的视频像素数据格式->输入数据
    参数三:原来的视频像素数据格式->输入画面每一行大小
    参数四:原来的视频像素数据格式->输入画面每一行开始位置(填写:0->表示从原点开始读取)
    参数五:原来的视频像素数据格式->输入数据行数
    参数六:转换类型后视频像素数据格式->输出数据
    参数七:转换类型后视频像素数据格式->输出画面每一行大小
    sws_scale(swscontext,
              (const uint8_t *const *)avframe_in->data,
              avframe_in->linesize,
              0,
              avcodec_context->height,
              avframe_yuv420p->data,
              avframe_yuv420p->linesize);
    

    相关文章

      网友评论

          本文标题:FFmpeg笔记(五)-- 编解码函数详解

          本文链接:https://www.haomeiwen.com/subject/olmbihtx.html