音视频之H.264编码encode(十四)

本文的主要内容: 使用H.264编码对YUV视频进行压缩。
如果是命令行的操作,非常简单。

ffmpeg -s 640x480 -pix_fmt yuv420p -i in.yuv -c:v libx264 out.h264
# -c:v libx264是指定使用libx264作为编码器


接下来主要讲解如何通过代码的方式使用H.264编码,用到avcodec、avutil两个库。

类的声明

#define __STDC_CONSTANT_MACROS

extern "C" {
#include <libavutil/imgutils.h>
}

typedef struct {
    const char *filename;
    int width;
    int height;
    AVPixelFormat pixFmt;
    int fps;
} VideoEncodeSpec;

class FFmpegs
{
public:
    FFmpegs();

    static void h264Encode(VideoEncodeSpec &in, const char *outFilename);
};

类的使用

 VideoEncodeSpec in;
 in.filename = "/Users/muzi/Desktop/out.yuv";
 in.width = 320;
 in.height = 240;
 in.fps = 30;
 in.pixFmt = AV_PIX_FMT_YUV420P;
 FFmpegs::h264Encode(in, "/Users/muzi/Desktop/out.h264");

宏定义

extern "C" {
#include <libavutil/avutil.h>
#include <libavcodec/avcodec.h>
}

#define ERROR_BUF(ret) \
    char errbuf[1024]; \
    av_strerror(ret, errbuf, sizeof(errbuf));

变量定义

// 文件
    QFile inFile(in.filename);
    QFile outFile(outFilename);

    // 一帧图片的大小
    int imgSize = av_image_get_buffer_size(in.pixFmt, in.width, in.height, 1);

    // 返回结果
    int ret = 0;

    // 编码器
    AVCodec *codec = nullptr;

    // 编码上下文
    AVCodecContext *ctx = nullptr;

    // 存放编码前的数据(yuv)
    AVFrame *frame = nullptr;

    // 存放编码后的数据(h264)
    AVPacket *pkt = nullptr;

初始化

// 获取编码器
    codec = avcodec_find_encoder_by_name("libx264");
    if (!codec) {
        qDebug() << "encoder not found";
        return;
    }

    // 检查输入数据的采样格式
    if (!check_pix_fmt(codec, in.pixFmt)) {
        qDebug() << "unsupported pixel format" << av_get_pix_fmt_name(in.pixFmt);
        return;
    }

    // 创建编码上下文
    ctx = avcodec_alloc_context3(codec);
    if (!ctx) {
        qDebug() << "avcodec_alloc_context3 error";
        return;
    }

    // 设置yuv参数
    ctx->width = in.width;
    ctx->height = in.height;
    ctx->pix_fmt = in.pixFmt;
    // 设置帧率(1秒钟显示的帧数in.fps)
    ctx->time_base = {1, in.fps};

    // 打开编码器
    ret = avcodec_open2(ctx, codec, nullptr);
    if (ret < 0) {
        ERROR_BUF(ret);
        qDebug() << "avcodec_open2 error" << errbuf;
        goto end;
    }

    // 创建AVFrame
    frame = av_frame_alloc();
    if (!frame) {
        qDebug() << "av_frame_alloc error";
        goto end;
    }

    frame->width = ctx->width;
    frame->height = ctx->height;
    frame->format = ctx->pix_fmt;
    frame->pts = 0;

    // 利用width、height、format创建缓冲区
    ret = av_image_alloc(frame->data, frame->linesize, in.width, in.height, in.pixFmt, 1);

    if (ret < 0) {
        ERROR_BUF(ret);
        qDebug() << "av_frame_get_buffer error" << errbuf;
        goto end;
    }

    // 创建AVPacket
    pkt = av_packet_alloc();
    if (!pkt) {
        qDebug() << "ac_packet_alloc error";
        goto end;
    }

编码

// 打开文件
    if (!inFile.open(QFile::ReadOnly)) {
        qDebug() << "file open error" << in.filename;
        goto end;
    }
    if (!outFile.open(QFile::WriteOnly)) {
        qDebug() << "file open error" << outFilename;
        goto end;
    }

    // 读取数据到frame中
    while ((ret = inFile.read((char *)frame->data[0], imgSize)) > 0) {

        // 进行编码
        if (encode(ctx, frame, pkt, outFile) < 0) {
            goto end;
        }

        // 设置帧的序号
        frame->pts++;
    }

    // 刷新缓冲区
    encode(ctx, nullptr, pkt, outFile);

回收资源

 // 关闭文件
    inFile.close();
    outFile.close();

    // 释放资源
    if (frame) {
        av_freep(&frame->data[0]);
        av_frame_free(&frame);
    }
    av_packet_free(&pkt);
    avcodec_free_context(&ctx);

 

上一篇:对DataTable(或者DataSet)修改后,提交修改到数据库


下一篇:图像视频编码和FFmpeg(3)-----用FFmpeg进行图像格式转换和AVFrame简介_luotuo44的专栏-程序员资料_avframe格式转换