ffmpeg音视频编码入门:视频编码(yuv编码h264)

1、基础概念:

帧类型:

I帧:原始帧,内部编码帧,也叫关键帧。视频的第一帧都是I帧,可独立编码。
P帧:向前预测帧。编码需要依赖前一帧。
B帧:前后预测帧,也叫双向预测帧。编码需要依赖本帧与前一帧和后一帧的对比。B帧压缩率高,但对性能要求也高。

GOP:画面组,一个组连续画面。gop_size = 250时表示每250帧插入一个I帧。I帧越少,视频体积越小。(I帧太少时可能会导致视频编码失败,所以要适量)


2、编码过程

1)初始化编码器(找到编码器,初始化编码器上下文,打开编码器)

2)初始化AVFrame,管理一帧原始图像数据

3)初始化AVPacket,用于存放编码数据

4)逐帧编码(frame数据发送到编码器,编码,将编码得到的packet数据写入输出文件)

5)释放内存资源


3、代码:

#include <stdio.h>
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"

void yuv_to_h264(const char *sourcefile, int w, int h, const char *destfile) {
    FILE *sfp = fopen(sourcefile, "r");
    if (sfp == NULL) {
        printf("sourcefile fopen() failed\n");
        return ;
    }

    FILE *dfp = fopen(destfile, "w+");
    if (dfp == NULL) {
        printf("destfile fopen() failed\n");
        return ;
    }

    // 1. 设置编码器
    AVCodec *cod = avcodec_find_encoder(AV_CODEC_ID_H264);
    if (cod == NULL) {
        printf("failed to find encoder\n");
        return ;
    }
    printf("encoder: %s\n", cod->name);
 
    // 2. 初始化编码器上下文
    AVCodecContext *cod_ctx = avcodec_alloc_context3(cod);
    cod_ctx->pix_fmt = AV_PIX_FMT_YUV420P;      
    cod_ctx->width = w;
    cod_ctx->height = h;
    cod_ctx->time_base.num = 1;     
    cod_ctx->time_base.den = 25;    // 帧率越高视频越流畅
    cod_ctx->bit_rate = 400000;      // 视频码率
    //cod_ctx->gop_size = 50;        // 表示每50帧插入一个I帧,I帧越少,视频越小,大小设置要合理。
    //av_opt_set_int(cod_ctx->priv_data, "qp", 18, 0); // 设置CQP恒定质量
    //av_opt_set_int(cod_ctx->priv_data, "crf", 18, 0); // 设置恒定速率因子
    cod_ctx->qmin = 10;             // 量化参数,设置默认值。量化系数越小,视频越清晰。
    cod_ctx->qmax = 51;             
    cod_ctx->max_b_frames = 0;       // B帧最大值,0表示不需要B帧
    cod_ctx->thread_count = 4;       // 线程数量

    // 3. 编码器初始化
    if (avcodec_open2(cod_ctx, cod, NULL) < 0) {
        printf("failed to open encoder\n");
        return ;
    }

    // 初始frame
    AVFrame *frame = av_frame_alloc();
    frame->width = cod_ctx->width;
    frame->height = cod_ctx->height;
    frame->format = cod_ctx->pix_fmt;
    av_frame_get_buffer(frame, 0);

    // 初始化packet
    AVPacket *packet = av_packet_alloc();

    int y_size = frame->width*frame->height;
    int yuv_size = frame->width*frame->height*3/2;
    char *yuv_data = malloc(sizeof(char) * yuv_size);

    // 循环编码YUV文件为H264
    for (int i = 0; i < 1000; i++) {
        if (feof(sfp)) {
            printf("file read finish\n");
            break;
        }
        fread(yuv_data, 1, yuv_size, sfp);

        frame->data[0] = yuv_data;
        frame->data[1] = yuv_data + y_size;
        frame->data[2] = yuv_data + y_size*5/4;
        frame->pts = i;

        // YUV数据发送到编码器
        int send_result = avcodec_send_frame(cod_ctx, frame);
        
        // 编码器对原始数据进行编码
        while (send_result >= 0) {
            int receive_result = avcodec_receive_packet(cod_ctx, packet);
            // 没有处理完成的packet数据
            if (receive_result == AVERROR(EAGAIN) || receive_result == AVERROR_EOF) break;
            
            fwrite(packet->data, 1, packet->size, dfp);
            // printf("receive %d packet\n", i);
            
            // 释放packet中的数据
            av_packet_unref(packet);
        }

    }

    // 释放内存资源
    fclose(sfp);
    fclose(dfp);
    av_frame_free(&frame);
    av_packet_free(&packet);
    avcodec_free_context(&cod_ctx);
}

int main(int argc, char const* argv[])
{
    yuv_to_h264("testvideo/output.yuv", 640, 272, "output.h264");
    return 0;
}

运行结果:

image-20210510094721314
如果没有找到编码器x264,ffmpeg需要导入外部库: ffmpeg导入x264外部库


将原始图像数据进行编码之后,视频体积缩小了300倍!!!

image-20210510094812191


参考:

YUV编码为h264

FFMPEG编码:YUV转H264(AVFrame转AVPacket)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值