zoukankan      html  css  js  c++  java
  • 音视频技术应用(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打开:

  • 相关阅读:
    现代软件工程 第一章 概论 第4题——邓琨
    现代软件工程 第一章 概论 第9题——邓琨
    现代软件工程 第一章 概论 第7题——张星星
    现代软件工程 第一章 概论 第5题——韩婧
    hdu 5821 Ball 贪心(多校)
    hdu 1074 Doing Homework 状压dp
    hdu 1074 Doing Homework 状压dp
    hdu 1069 Monkey and Banana LIS变形
    最长上升子序列的初步学习
    hdu 1024 Max Sum Plus Plus(m段最大子列和)
  • 原文地址:https://www.cnblogs.com/yongdaimi/p/15685997.html
Copyright © 2011-2022 走看看