FFmpeg RGB转MP4

下面是一个读取本地 RGB 文件,转换并输出 MP4 视频文件的一个例子,具体步骤如下:

1、创建编码器

2、创建输出视频上下文

3、添加视频流

4、rgb转yuv

5、写视频文件头

6、写视频文件,循环内部进行H264编码

完整代码如下:

#include <iostream>

extern "C"
{
    #include <libavformat/avformat.h>
    #include <libswscale/swscale.h>
}

#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"avcodec.lib")
#pragma comment(lib,"avutil.lib")
#pragma comment(lib,"swscale.lib")

using namespace std;

int main()
{
    char infile[] = "out.rgb";
    char outfile[] = "rgb.mp4";

    // 注册所有和编解码器有关的组件
    av_register_all();

    // 打开RGB文件
    FILE *fp = fopen(infile, "rb");
    if (!fp)
    {
        cout << infile << " open failed!" << endl;
        getchar();
        return -1;
    }

    // 源图像参数
    int width = 848;
    int height = 480;
    int fps = 25;

    ///1 创建编码器
    // 查找编码器
    AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264);
    if (!codec)
    {
        cout << " avcodec_find_encoder AV_CODEC_ID_H264 failed!" << endl;
        getchar();
        return -1;
    }

    // 给编码器分配内存,返回对应编码器上下文
    AVCodecContext *c = avcodec_alloc_context3(codec);
    if (!c)
    {
        cout << " avcodec_alloc_context3  failed!" << endl;
        getchar();
        return -1;
    }
    // 配置编码器上下文的成员
    c->bit_rate = 400000000; // 比特率(码率),越高视频质量越好
    c->width = width; // 设置编码视频宽度 
    c->height = height; // 设置编码视频高度
    c->time_base.num = 1;
    c->time_base.den = 25; // 设置帧率,num为分子,den为分母,如果是1/25则表示25帧/s

    c->gop_size = 50; // 画面组大小,关键帧
    c->max_b_frames = 0; // 设置B帧最大数,该值表示在两个非B帧之间,所允许插入的B帧的最大帧数

    c->pix_fmt = AV_PIX_FMT_YUV420P; // 设置输出像素格式
    c->codec_id = AV_CODEC_ID_H264; // 设置编码格式
    c->thread_count = 8; // 线程数量
    c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; // 全局的编码信息

    // 打开编码器
    int ret = avcodec_open2(c, codec, NULL);
    if (ret < 0)
    {
        cout << " avcodec_open2  failed!" << endl;
        getchar();
        return -1;
    }
    cout << "avcodec_open2 success!" << endl;

    ///2 创建输出视频上下文
    AVFormatContext *oc = NULL;
    avformat_alloc_output_context2(&oc, 0, 0, outfile);

    ///3 添加视频流
    AVStream *st = avformat_new_stream(oc, NULL);
    st->id = 0;
    st->codecpar->codec_tag = 0;
    // 将AVCodecContext信息拷贝到AVCodecParameterst结构体中
    avcodec_parameters_from_context(st->codecpar, c);

    cout << "===============================================" << endl;
    // 打印AVFormatContext的内容
    av_dump_format(oc, 0, outfile, 1);
    cout << "===============================================" << endl;

    ///4 rgb转yuv
    SwsContext *ctx = NULL;
    ctx = sws_getCachedContext(ctx,
        width, height, AV_PIX_FMT_BGRA,
        width, height, AV_PIX_FMT_YUV420P,
        SWS_BICUBIC,
        NULL, NULL, NULL
        );
    // 分配输入空间
    unsigned char *rgb = new unsigned char[width*height * 4];

    // 分配输出空间
    AVFrame *yuv = av_frame_alloc();
    yuv->format = AV_PIX_FMT_YUV420P;
    yuv->width = width;
    yuv->height = height;
    ret = av_frame_get_buffer(yuv, 32);
    if (ret < 0)
    {
        cout << " av_frame_get_buffer  failed!" << endl;
        getchar();
        return -1;
    }

    ///5 写视频文件头
    // 创建并初始化AVIOContext以访问outfile指示的资源
    ret = avio_open(&oc->pb, outfile, AVIO_FLAG_WRITE);
    if (ret < 0)
    {
        cout << " avio_open  failed!" << endl;
        getchar();
        return -1;
    }
    // 写视频文件头
    ret = avformat_write_header(oc, NULL);
    if (ret < 0)
    {
        cout << " avformat_write_header  failed!" << endl;
        getchar();
        return -1;
    }

    // 循环写视频文件
    int pts = 0;
    for (;;)
    {
        // 读取RGB文件的像素数据长度
        int len = fread(rgb, 1, width*height * 4, fp);
        if (len <= 0)
        {
            break;
        }

        // 像素格式转换:RGB转YUV
        uint8_t *indata[AV_NUM_DATA_POINTERS] = { 0 };
        indata[0] = rgb;
        int inlinesize[AV_NUM_DATA_POINTERS] = { 0 };
        inlinesize[0] = width * 4;
        int h = sws_scale(ctx, indata, inlinesize, 0, height,
            yuv->data, yuv->linesize
            );
        if (h <= 0)
            break;

        ///6 H264编码
        // 将未压缩的AVFrame数据(yuv)给编码器
        yuv->pts = pts;
        //yuv->pict_type = AV_PICTURE_TYPE_I;
        pts = pts + 3600;
        ret = avcodec_send_frame(c, yuv);
        if (ret != 0)
        {
            continue;
        }
        // 将编码数据保存在AVPacket
        AVPacket pkt;
        av_init_packet(&pkt);
        ret = avcodec_receive_packet(c, &pkt);
        if (ret != 0)
            continue;

        // 将AVPacket写入输出媒体文件
        //av_write_frame(oc, &pkt);
        //av_packet_unref(&pkt);
        av_interleaved_write_frame(oc, &pkt);

        cout << "<"<<pkt.size<<">";
    }
    
    //写入视频索引
    av_write_trailer(oc);

    //关闭视频输出io
    avio_close(oc->pb);

    //清理封装输出上下文
    avformat_free_context(oc);

    //关闭编码器
    avcodec_close(c);

    //清理编码器上下文
    avcodec_free_context(&c);

    //清理视频重采样上下文
    sws_freeContext(ctx);

    cout << "======================end=========================" << endl;

    delete rgb;
    getchar();
    return 0;
}


猜你喜欢

转载自www.cnblogs.com/linuxAndMcu/p/12148057.html