FFmpeg 结构体与函数 简单记录

记录FFmpeg的相关结构体与函数。

libavcodec: 提供了一系列编码器的实现
libavformat: 实现在流协议,容器格式及基本IO访问
libavutil: 包含了hash器,解码器和各种函数
libavfilter: 提供了各种音视频过滤器
libavdevice: 提供了访问捕获设备和回放设备的接口
libswresample: 实现了混音和重采样
libswscale: 实现了色彩转换和缩放功能

结构体

AVFormatContext 格式上下文

    是一个贯穿全局的数据结构,此结构包含一个视频流的格式内容。其中存有AVInputFormat(或AVOutputFormat)、AVStream、AVPacket等数据结构以及其它的相关信息。

AVFormatContext *formatContext = nullptr;

// 格式上下文 初始化
formatContext = avformat_alloc_context();   // 堆区创建
AVFormatContext描述
formatContext->nb_streams流的个数
formatContext->streams[i]获取媒体流(视频 、 音频) - AVStream
formatContext->duration获取Stream总时长

AVInputFormat 输入文件容器格式

    一个文件容器格式对应一个AVInputFormat 结构,在程序运行时有多个实例。

AVStream 媒体流(视频、音频)

    存储每一个视频/音频流信息的结构体。

AVStream描述
stream->codecpar从AVStream 获取 编码解码的参数 - AVCodecParameters

AVCodecParameters 编码解码的参数

AVCodecParameters描述
parameters->codec_id获取编解码器ID - AVCodecID
parameters->codec_type从编解码器参数中,获取流的类型(音频 或 视频)AVMediaType::AVMEDIA_TYPE_AUDIO、AVMediaType::AVMEDIA_TYPE_VIDEO

AVCodecContext 编解码器上下文

    描述编解码器上下文的数据结构,包含了众多编解码器需要的参数信息,如果是单纯地使用libavcodec,这部分信息需要调用者进行初始化;如果使用整个FFmpeg库,这部分信息在调用av_open_input_file 和 av_find_stream_info的过程中会根据文件的头部信息及媒体流内头部信息完成初始化。

AVCodecContext描述
codecContext->width视频的宽
codecContext->height视频的高
codecContext->pix_fmt视频的像素格式

AVPacket 压缩数据包

    FFmpeg用AVPacket来存放编码后的视频帧数据,AVPacket保存解复用只有、解码之前的数据(仍然是压缩之后的数据) 和关于这些数据的一些附加信息,如显示时间戳(PTS)、解码时间戳(DTS)、数据时长、所在媒体流的索引等。

    对于视频(Video)来说,AVPacket通常包含一个压缩的帧,而音频(Audio)则有可能包含多个压缩的帧。
    一个Packet有可能是空的,不包含任何压缩数据,只含有side data(即附加信息)。

    对于多个Packet共享一个缓存空间,FFmpeg使用了引用计数(reference-count)机制。当有新的Packet引用共享的缓存空间时,就将引用计数+1,当释放引用共享空间的Packet时,就将引用计数-1,当引用计数为0时,释放引用的缓存空间。
    AVPacket中的AVBuffer *buf用来管理引用计数。

    AVPacket通常将Demuxer导出的数据包作为解码器的输入数据,或是收到来自编解码器的数据包,通过Muxer进入输出数据。

AVPacket描述
packet->stream_index获取压缩数据包中的流的下标,可能是视频的,或者音频,或者字幕的

AVCodec 编解码器

    是存储编解码器信息的结构体。

SwsContext 格式转换上下文

SwrContext 重采样上下文

AVRational 时间基

AVFrame 音视频数据结构体

    一般用于存储原始数据(非压缩数据,如对于视频来说是YUV、RGB,对于音频是PCM)。
    AVFrame存放从AVPacket中解码出来的原始数据,其必须通过av_frame_alloc来创建,通过av_frame_free释放。
    AVFrame有一块数据缓存空间,在调用av_frame_alloc的时候并不会为这块缓存区域分配空间,需要使用其他的方法。在解码的过程中使用了两个AVFrame,这两个AVFrame分配缓存空间的方法并不相同。
        一个AVFrame用来存放从AVPacket中解码出来的原始数据,这个AVFrame的数据缓存空间通过调用avcodec_decode_video来分配和填充。
        另一个AVFrame用来存放将解码出来的原始数据变换为需要的数据格式(如RGB、RGBA)的数据,这个AVFrame需要手动分配数据缓存空间。

AVFrame描述
frame->best_effort_timestamp当前的时间戳
stream->time_base获取到时间基
stream->avg_frame_rate获取fps,需要使用av_q2d转换
frame->best_effort_timestamp视频延迟多少

函数

avformat_alloc_context 初始化格式上下文AVFormatContext

给AVFormatContext分配内存空间。

AVFormatContext *avformat_alloc_context(void);

使用:

AVFormatContext * formatContext = nullptr;
formatContext = avformat_alloc_context();

avformat_open_input 打开媒体文件

打开媒体文件,读取header,不涉及打开解码器

/***
 * AVFormatContext **ps:格式上下文
 * const char *url:文件地址
 * ff_const59 AVInputFormat *fmt:输入格式,如麦克风、摄像头,Android中用不到
 * AVDictionary **options:各种设置,如:Http连接超时等,它是个二级指针
 * @return 0 on success, a negative AVERROR on failure. 返回0就是成功了
 */
int avformat_open_input(AVFormatContext **ps, const char *url, ff_const59 AVInputFormat *fmt, AVDictionary **options);

使用:

// 格式上下文初始化
AVFormatContext * formatContext = nullptr;
formatContext = avformat_alloc_context();   // 堆区创建

// 字典初始化
AVDictionary * dictionary = nullptr;
av_dict_set(&dictionary, "timeout", "5000000", 0); // 单位微秒

// 打开文件
int r = avformat_open_input(&formatContext, data_source, nullptr, &dictionary);

// 释放字典
av_dict_free(&dictionary);

if (r) {
    LOGE("avformat_open_input error =====================");
    
    return;
}

avformat_close_input 关闭媒体文件

void avformat_close_input(AVFormatContext **s);

avformat_find_stream_info 查找媒体中的音视频流的信息

查找媒体中的音视频流的信息

/***
 * 查找媒体中的音视频流的信息
 * @return >=0 if OK, AVERROR_xxx on error
 */
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);

使用:

r = avformat_find_stream_info(formatContext, nullptr); // >=0 if OK
if (r < 0){
    LOGE("avformat_find_stream_info error =====================");
    
    return;
}

avcodec_find_decoder 获取编解码器

根据 编解码器ID 查找编解码器

AVCodec *avcodec_find_decoder(enum AVCodecID id);

avcodec_alloc_context3 获取编解码器上下文

/***
* 获取 编解码器上下文
* @return An AVCodecContext filled with default values or NULL on failure.
*/
AVCodecContext *avcodec_alloc_context3(const AVCodec *codec);

使用:

codecContext = avcodec_alloc_context3(codec);
if (!codecContext) {
    LOGE("avcodec_alloc_context3 error =====================");
    
    avcodec_free_context(&codecContext);
    avformat_close_input(&formatContext);

    return;
}

avcodec_parameters_to_context 把 AVCodecParameters 复制给 AVCodecContext

/***
 * 把 AVCodecParameters 复制给 AVCodecContext
 * @return >= 0 on success, a negative AVERROR code on failure.
 */
int avcodec_parameters_to_context(AVCodecContext *codec, const AVCodecParameters *par);

使用:

r = avcodec_parameters_to_context(codecContext, parameters);
if (r < 0) {
    LOGE("avcodec_parameters_to_context error =====================");
    
    avcodec_free_context(&codecContext);
    avformat_close_input(&formatContext);

    return;
}

avcodec_open2 打开解码器

 /***
 * 打开解码器
 * @return zero on success, a negative value on error
 */
 int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);

使用:

r = avcodec_open2(codecContext, codec, nullptr);
if (r) {
    LOGE("avcodec_open2 error =====================");
    
    avcodec_free_context(&codecContext);
    avformat_close_input(&formatContext);
    
    return;
}

av_packet_alloc 初始化压缩包

// 压缩包 可能是音频,或者视频
AVPacket *packet = av_packet_alloc();

av_read_frame 读取音频包/视频包

读取码流中的若干音频帧 或 1帧视频。

/***
 * 从格式上下文中读取音频包/视频包
 * AVFormatContext *s: 格式上下文
 * AVPacket *pkt: 压缩包
 * @return 0 if OK, < 0 on error or end of file
 */
int av_read_frame(AVFormatContext *s, AVPacket *pkt);

使用:

int ret = av_read_frame(formatContext, packet);
if (!ret) {
    // TODO  成功
} else if (ret == AVERROR_EOF) {  // 读到文件末尾
    // TODO  文件结尾
} else {
    break; // 错误
}

av_packet_free 释放数据包

void av_packet_free(AVPacket **pkt);

avcodec_send_packet 发送压缩包给缓冲区

/***
 * 发送压缩包给缓冲区
 * AVCodecContext *avctx: 编解码器上下文
 * AVPacket *avpkt: 压缩数据包
 * @return 0 on success, otherwise negative error code
 */
int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);

使用:

ret = avcodec_send_packet(codecContext, pkt);

// av_packet_free(&pkt); // FFmpeg源码缓存一份pkt,可以释放

if (ret) {
    break; // 失败
}

av_frame_alloc 初始化AVFrame

AVFrame *frame = av_frame_alloc();

av_frame_free 释放AVFrame

void av_frame_free(AVFrame **frame);

avcodec_receive_frame 从缓冲区拿到原始包

/***
 * 从缓冲区拿到原始包
 * AVCodecContext *avctx: 编解码器上下文
 * AVFrame *frame: 原始包AVFrame
 * @return  
      0:                  success, a frame was returned
      AVERROR(EAGAIN):    output is not available in this state - user must try to send new input
 */
int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);

使用:

ret = avcodec_receive_frame(codecContext, frame);
if (ret == AVERROR(EAGAIN)) {
    continue; // B帧 参考后面的帧失败等
} else if (ret != 0) {
    if (frame) {
        av_frame_free(&frame);
    }
    break; // 错误
}

avcodec_free_context 释放AVCodecContext

void avcodec_free_context(AVCodecContext **avctx);

av_packet_unref 释放AVPacket成员指向的堆空间

void av_packet_unref(AVPacket *pkt);

sws_getContext 初始化格式转换上下文SwsContext

/**
 * 格式转换上下文
 * @return a pointer to an allocated context, or NULL in case of error
 */
struct SwsContext *sws_getContext(int srcW, int srcH, enum AVPixelFormat srcFormat,
                                  int dstW, int dstH, enum AVPixelFormat dstFormat,
                                  int flags, SwsFilter *srcFilter,
                                  SwsFilter *dstFilter, const double *param);

使用:

SwsContext *sws_ctx = sws_getContext(
        // 输入
        codecContext->width,                            // 视频的宽
        codecContext->height,                           // 视频的高
        codecContext->pix_fmt,                          // 视频的像素格式

        // 输出
        codecContext->width,
        codecContext->height,
        AV_PIX_FMT_RGBA,
        SWS_BILINEAR,
        NULL, NULL, NULL
        );

sws_scale 格式转换 yuv --> rgba

/***
 * 格式转换 yuv --> rgba
 */
int sws_scale(struct SwsContext *c, const uint8_t *const srcSlice[],
              const int srcStride[], int srcSliceY, int srcSliceH,
              uint8_t *const dst[], const int dstStride[]);

使用:

uint8_t *dst_data[4];                                   // RGBA
int dst_linesize[4];                                    // RGBA

// 给dst_data申请内存
av_image_alloc(dst_data, dst_linesize, codecContext->width, codecContext->height, AV_PIX_FMT_RGBA, 1);

sws_scale(sws_ctx,
          // 输入
          frame->data,
          frame->linesize,
          0,
          codecContext->height,

          // 输出
          dst_data,
          dst_linesize);

sws_freeContext 释放格式转换上下文SwsContext

void sws_freeContext(struct SwsContext *swsContext);

swr_alloc_set_opts 分配SwrContext并设置/重置公共参数

struct SwrContext *swr_alloc_set_opts(struct SwrContext *s,
                                      int64_t out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate,
                                      int64_t  in_ch_layout, enum AVSampleFormat  in_sample_fmt, int  in_sample_rate,
                                      int log_offset, void *log_ctx);

swr_init 初始化重采样上下文SwrContext

int swr_init(struct SwrContext *s);

swr_convert 每个通道输出的样本数

int swr_convert(struct SwrContext *s, uint8_t **out, int out_count,
                                const uint8_t **in , int in_count);

swr_free 释放重采样上下文

void swr_free(struct SwrContext **s);

av_get_channel_layout_nb_channels 返回通道布局中的通道数。

av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);

av_get_bytes_per_sample 返回每个样本的字节数。

av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);

av_err2str 转换成错误详情

int r = avformat_open_input(&formatContext, data_source, nullptr, &dictionary);
if (r) {
   char * errorInfo = av_err2str(r);
   LOGE("FFMPEG_CAN_NOT_OPEN_URL errorInfo: %s \n", errorInfo);
}

av_rescale_rnd 获取单通道的样本数

int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding rnd) av_const;

使用:

int dst_nb_samples = av_rescale_rnd(
        swr_get_delay(swr_ctx, frame->sample_rate) + frame->nb_samples,
        out_sample_rate,
        frame->sample_rate,
        AV_ROUND_UP);

av_q2d 转换AVRational 为 double

// 获取fps
AVRational fps_rational = stream->avg_frame_rate;
int fps = av_q2d(fps_rational);

av_seek_frame 跳转到指定时间

/**
 * AVFormatContext *s:
 * int stream_index: 为-1时,ffmpeg自动选择音频或视频
 * int64_t timestamp: 时间戳
 * int flags: seek的模式,如:AVSEEK_FLAG_FRAME 找关键帧;
 *                          AVSEEK_FLAG_BACKWARD 找附近的关键帧,找不到花屏了
 *                          AVSEEK_FLAG_ANY 精确跳转,花屏
 */
int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags);

使用:

av_seek_frame(formatContext, -1, progress * AV_TIME_BASE, AVSEEK_FLAG_FRAME | AVSEEK_FLAG_BACKWARD);

参考:《FFmpeg从入门到精通》、《Android音视频开发》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值