ffmpeg初次学习(仅针对编码)

学习资源

官方文档: Documentation

雷神博客
可移步 [总结]FFMPEG视音频编解码零基础学习方法

结构体学习:FFMPEG结构体分析

基础知识

视频编码

视频编码的主要作用是将视频像素数据(RGB,YUV等)压缩成为视频码流,从而降低视频的数据量。不压缩就视频就太大了。

主要视频编码

名称推出机构
HEVC(H.265)MPEG/ITU-T
H.264MPEG/ITU-T
MPEG4MPEG
MPEG2MPEG
VP9Google
VP8Google
VC-1Microsoft Inc.

优劣关系

HEVC(H.265) > VP9 > H.264 > VP8 > MPEG4 > H.263 > MPEG2

结构体学习

AVFrame 结构体一般用于存储原始数据(即非压缩数据,例如对视频来说是YUV,RGB,对音频来说是PCM),此外还包含了一些相关的信息

AVCodecContext 是FFmpeg编解码上下文的结构体,而AVCodec是编解码参数的结构体

AVPacket 是存储压缩编码数据相关信息的结构体

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

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

官方demo之encode.c学习理解

学习官方demo:encode_video.c

我自己对官方demo进行了修改并加了注释,修改后的文件可直接运行,已上传至 资源下载地址

流程分析:

  1. 程序运行后给定输出的文件路径
  2. 调用 avcodec_find_encoder 查找编码器 AVCodec
  3. 调用 avcodec_alloc_context3 分配编码器上下文 AVCodecContext
  4. 调用 av_packet_alloc 分配 AVPacket,编码器会将每个AVFrame编码为AVPacket
  5. 给 AVCodecContext 配置参数,如码率、视频宽高、帧格式等等
  6. 调用 avcodec_open2 打开编码器
  7. 调用 av_frame_alloc 分配 AVFrame 内存并设置帧的格式和宽高
  8. 调用 av_frame_get_buffer 分配帧数据使用的空间
  9. 开始for循环,首先调用 av_frame_make_writable 确保帧数据 AVFrame 可写,然后准备一帧虚拟图像数据写入 frame->data ,最后给每帧的pts赋值,告诉播放器在什么时候显示这一帧数据
  10. 开始编码,调用 avcodec_send_frame 把帧数据 AVFrame 发送给编码器
  11. 调用 avcodec_receive_packet 把编码后的帧 AVPacket 从编码器取出,然后把编码后的每一帧写入输出文件 File ,写完后调用 av_packet_unref 解除对 AVPacket 的引用
  12. 如果编码器ID是MPEG,需要向输出文件 File 的结尾添加一个固定序列从而得到一个真正的MPEG文件
  13. 最后关闭文件 File,并释放 AVCodecContext、AVFrame、AVPacket 占用的内存,调用 avcodec_free_contextav_frame_freeav_packet_free

API学习

avcodec_find_encoder

根据一个AVCodecID找到一个注册的编码器

const AVCodec *avcodec_find_encoder(enum AVCodecID id);

常见 AVCodecID 随便列举一些

enum AVCodecID {
    AV_CODEC_ID_NONE,

    /* video codecs */
	AV_CODEC_ID_H263,
	AV_CODEC_ID_MPEG4,
	AV_CODEC_ID_H264,   
	AV_CODEC_ID_HEVC,
}

实例:

const AVCodec *codec;
enum AVCodecID codec_id;

codec_id = AV_CODEC_ID_HEVC;

codec = avcodec_find_encoder(codec_id);
if (!codec) {
    printf("Codec '%d' not found\n", codec_id);
    exit(1);
}

avcodec_alloc_context3

AVCodecContext 结构体分配内存,并将各字段设置为默认值,结果返回的结构体需要使用 avcodec_free_context() 进行释放内存

AVCodecContext *avcodec_alloc_context3(const AVCodec *codec);

实例:

    AVCodecContext *c= NULL;
    const AVCodec *codec;
    
    c = avcodec_alloc_context3(codec);
    if (!c) {
        fprintf(stderr, "Could not allocate video codec context\n");
        exit(1);
    }
    ...
    ...
    ...
    avcodec_free_context(&c);

av_packet_alloc

AVPacket 结构体分配内存,并将各字段设置为默认值,结果返回的结构体需要使用 av_packet_free() 进行释放内存

AVPacket *av_packet_alloc(void);

实例:

    AVPacket *pkt;
    pkt = av_packet_alloc();
    if (!pkt)
        exit(1);
    ...
    ...
    ... 
    av_packet_free(&pkt);   

avcodec_open2

初始化 AVCodecContext 以使用给定的 AVCodec ,使用此函数之前需要使用 avcodec_alloc_context3() 分配内存

int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);

av_frame_get_buffer

分配新的buffers给视频数据
在调用该函数之前需要设置 frame 的一些参数format、width、height

int av_frame_get_buffer(AVFrame *frame, int align);

参数 align 强烈推荐设置为0,系统会根据当前CPU自动设置alignment

实例:

    AVFrame *frame;
    
    frame = av_frame_alloc();
    frame->format = c->pix_fmt;
    frame->width  = c->width;
    frame->height = c->height;
    
	av_frame_get_buffer(frame, 32);

avcodec_send_frame

发送数据帧到编码器。使用 avcodec_receive_packet() 函数来检索缓冲输出数据包

int avcodec_send_frame(AVCodecContext *avctx, const AVFrame *frame);

avcodec_receive_packet

从编码器读取编码好的数据

int avcodec_receive_packet(AVCodecContext *avctx, AVPacket *avpkt);

返回类型:

0		表示成功
AVERROR(EAGAIN)		输出当前不可用,用户必须给予输入
AVERROR_EOF			编码器被完全冲洗,不再会有输出的 packets
AVERROR(EINVAL)		codec没有开启,或者它是一个解码器而非编码器

实例:

        ret = avcodec_receive_packet(enc_ctx, pkt);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            return;
        else if (ret < 0) {
            fprintf(stderr, "Error during encoding\n");
            exit(1);
        }

结束语

刚开始学ffmpeg,摸着石头过河,一点一点看demo和源码还是蛮有意思的

英文阅读有待提高,要不然看不懂开发者给的注释

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值