音视频技术应用(15) 使用h264, h265 编码压缩数据,并使用VLC测试播放

 代码如下:

#include <iostream>
#include <fstream>

using namespace std;

extern "C" { // 指定函数是C语言函数,以C语言的方式去编译
#include <libavcodec/avcodec.h>
}

// 以预处理指令的方式导入库
#pragma comment(lib, "avcodec.lib")
#pragma comment(lib, "avutil.lib")

int main(int argc, char *argv[])
{

    string filename = "400_300_25";
    
    AVCodecID codec_id = AV_CODEC_ID_H264;

    if (argc > 1)
    {
        string codec = argv[1];
        if (codec == "h265" || codec == "hevc")
        {
            codec_id = AV_CODEC_ID_HEVC;
        }
    }

    if (codec_id == AV_CODEC_ID_H264)
    {
        filename += ".h264";
    }
    else if (codec_id == AV_CODEC_ID_HEVC)
    {
        filename += ".h265";
    }

    ofstream ofs;
    ofs.open(filename, ios::binary);

    // 1. 查找编码器        AV_CODEC_ID_H264(h264) AV_CODEC_ID_HEVC(h265)
    auto codec = avcodec_find_encoder(codec_id);
    if (!codec)
    {
        cerr << "codec not found" << endl;
        return -1;
    }

    // 2. 创建上下文
    auto c = avcodec_alloc_context3(codec);
    if (!c)
    {
        cerr << "avcodec_alloc_context3 failed" << endl;
        return -1;
    }

    // 3. 设定编码器的上下文参数
    c->width = 400;                                            // 视频帧的宽度
    c->height = 300;                                        // 视频帧的高度
    c->time_base = { 1, 25 };                                // 时间基数,即时间戳的显示单位,可以认为是1秒内显示25帧
    
    c->pix_fmt = AV_PIX_FMT_YUV420P;                        // 指定源数据的像素格式,跟编码格式有关,如果编码格式是h264, 这里就不能指定为RGBA
    c->thread_count = 16;                                    // 编码线程数,可以通过调用系统接口获取CPU的核心数量

    // 4. 打开编码上下文
    int re = avcodec_open2(c, codec, nullptr);
    if (re != 0)
    {
        char buf[1024] = { 0 };
        av_strerror(re, buf, sizeof(buf) - 1);
        cerr << "avcodec_open2 failed" << buf << endl;
        return -1;
    }

    cout << "avcodec_open2 success" << endl;

    // 创建AVFrame空间,用于表示一帧数据
    auto frame = av_frame_alloc();
    frame->width = c->width;
    frame->height = c->height;
    frame->format = c->pix_fmt;

    re = av_frame_get_buffer(frame, 0);
    if (re != 0)
    {
        char buf[1024] = {0};
        av_strerror(re, buf, sizeof(buf) - 1);
        cerr << "av_frame_get_buffer() failed" << endl;
        return -1;
    }

    auto pkt = av_packet_alloc();

    // 创建一个10s的视频,共250帧
    for (int i = 0; i < 250; i++)
    {
        // 生成一帧图像 每帧数据都不同
        // Y
        for (int y = 0; y < frame->height; y++)
        {
            for (int x = 0; x < frame->width; x++)
            {
                frame->data[0][y * frame->linesize[0] + x] = x + y + i * 3;
            }
        }

        // UV
        for (int y = 0; y < frame->height / 2; y++)
        {
            for (int x = 0; x < frame->width / 2; x++)
            {
                frame->data[1][y * frame->linesize[1] + x] = 128 + y + i * 2;
                frame->data[2][y * frame->linesize[2] + x] = 64 + x + i * 5;
            }
        }

        // 显示的时间
        frame->pts = i;

        re = avcodec_send_frame(c, frame);
        if (re != 0)
        {
            cerr << "avcodec_send_frame() failed" << endl;
            break;
        }

        // >=0 表示有数据返回,编码一个AVFrame 可能会返回多个AVPacket
        while (re >= 0)
        {
            // 接收压缩帧 一般前几次调用会返回空的 (缓冲,立即返回,编码未完成)
            // 编码是在独立的线程中完成的
            // 每次调用会重新分配pkt的数据空间,并改变内部的指针指向
            re = avcodec_receive_packet(c, pkt);
            if (re == AVERROR(EAGAIN) || re == AVERROR_EOF)
                break;

            if (re < 0)
            {
                char buf[1024] = { 0 };
                av_strerror(re, buf, sizeof(buf) - 1);
                cerr << "avcodec_receive_packet() failed" << endl;
                return -1;
            }

            cout << pkt->size << " " << flush;
            
            // 写入编码后的压缩数据
            ofs.write((char *)pkt->data, pkt->size);

            // 注意 一定要调用 av_packet_unref() 删除已申请的数据空间,若不删除,下次还会重新申请,这样会造成内存泄漏
            av_packet_unref(pkt);
        }
    }

    // 释放AVPacket
    av_packet_free(&pkt);

    // 释放AVFrame
    av_frame_free(&frame);

    // 释放上下文
    avcodec_free_context(&c);
}

直接运行程序,会生成一个名为"400_300_25.h264"的h264文件,使用VLC打开:

回到工作目录,执行:first_ffmpeg.exe h265, 会紧接着生成一个名为: 400_300_25.h265 的h265文件,双击使用VLC打开:

原文地址:https://www.cnblogs.com/yongdaimi/p/15685997.html