zoukankan      html  css  js  c++  java
  • 音视频之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);
  • 相关阅读:
    [原]实例-简单设计&精简代码&复用代码
    [原创]实例-少用单例及降低耦合
    c#实现数据集合转换为csv文本
    [转]SqlServer索引的原理与应用
    [转]AngularJS:何时应该使用Directive、Controller、Service?
    [转]AngularJS移动开发中的坑汇总
    [转]Hibernate对象的三种状态
    [转]AngularJS Cookies Example
    [转]LESS CSS 框架简介
    [转]为ReportViewer导出的PDF文档加上水印
  • 原文地址:https://www.cnblogs.com/muzichenyu/p/15598657.html
Copyright © 2011-2022 走看看