音视频技术应用(13) YUV420P 转 RGBA 并写入文件

#include <iostream>
#include <fstream>

using namespace std;

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

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

#define YUV_FILE "400_300_25.yuv"
#define RGBA_FILE "400_300_25.rgba"

// 模拟把一个400*300的YUV图像转换成 800 * 600 的RGBA 图像
int main()
{
    // 指定输入视频的宽和高
    int yuv_width = 400;
    int yuv_height = 300;

    // 指定输出的RGBA图像的宽和高
    int rgb_width = 800;
    int rgb_height = 600;

    // 申请一块内存用于存储YUV420P的图像
    unsigned char* yuv[3] = {0};
    int yuv_linesize[3] = {yuv_width, yuv_width / 2, yuv_width / 2};
    
    yuv[0] = new unsigned char[yuv_width * yuv_height];                    // Y
    yuv[1] = new unsigned char[yuv_width * yuv_height / 4];                // U
    yuv[2] = new unsigned char[yuv_width * yuv_height / 4];                // V

    // 申请一块内存用于存储转换完成后的RGBA的图像
    unsigned char* rgba[1] = {0};
    rgba[0] = new unsigned char[rgb_width * rgb_height * 4];
    int rgba_linesize[1] = { rgb_width * 4};

    //====================================== YUV 文件转 RGBA 文件 ===============================================

    // 定义输入流
    ifstream ifs;
    ifs.open(YUV_FILE, ios::binary);
    if (!ifs)
    {
        cerr << "open " << YUV_FILE << " failed" << endl;
        return -1;
    }

    // 定义输出流
    ofstream ofs;
    ofs.open(RGBA_FILE, ios::binary);
    if (!ofs)
    {
        cerr << "open " << RGBA_FILE << " failed" << endl;
        return -1;
    }

    SwsContext* yuv2rgb = nullptr;

    for (int i = 0; i < 10; i++)                                        // 尝试转换10帧图像
    {
        ifs.read((char*)yuv[0], yuv_width * yuv_height);                // 读取Y
        ifs.read((char*)yuv[1], yuv_width * yuv_height /4);                // 读取U
        ifs.read((char*)yuv[2], yuv_width * yuv_height / 4);            // 读取V

        if (ifs.gcount() == 0)
        {
            break;
        }

        yuv2rgb = sws_getCachedContext(yuv2rgb,                            // 转换的上下文,NULL代表创建新创建上下文,非NULL会判断与现有参数是否一致,若不一致,则会删除现有参数,并重新创建上下文
            yuv_width, yuv_height,                                        // 输入视频的宽和高
            AV_PIX_FMT_YUV420P,                                            // 输入的像素格式
            rgb_width, rgb_height,                                        // 输出视频的宽和高
            AV_PIX_FMT_RGBA,                                            // 输出的像素格式
            SWS_BILINEAR,                                                // 选择尺寸变化的算法,这里我们选用双线性插值
            0, 0, 0                                                        // 过滤器参数,这里不指定
        );

        if (!yuv2rgb)
        {
            cerr << "sws_getCachedContext(yuv2rgb) failed" << endl;
            return -1;
        }


        // 输出的结果代表转换的高度
        int re = sws_scale(yuv2rgb,
            yuv,                                                        // 输入的数据
            yuv_linesize,                                                // 输入数据的行字节数
            0,                                                            // 输入数据的Y轴,这里没有
            yuv_height,                                                    // 输入的高度
            rgba,                                                        // 输出的数据
            rgba_linesize                                                // 输出的行大小
        );

        cout << re << " " << flush;

        ofs.write((char *)rgba[0], rgb_width * rgb_height * 4);
    }

    ifs.close();
    ofs.close();


    //====================================== RGBA 文件转 YUV 文件 ===============================================
    ifs.open(RGBA_FILE, ios::binary);
    if (!ifs)
    {
        cerr << "open " << RGBA_FILE << " failed" << endl;
        return -1;
    }

    SwsContext* rgb2yuv = nullptr;

    for (;;)                                        
    {
        ifs.read((char *)rgba[0], rgb_width * rgb_height * 4);            // 读取一帧RGBA数据

        if (ifs.gcount() == 0)
        {
            break;
        }

        rgb2yuv = sws_getCachedContext(rgb2yuv,                            // 转换的上下文,NULL代表创建新创建上下文,非NULL会判断与现有参数是否一致,若不一致,则会删除现有参数,并重新创建上下文
            rgb_width, rgb_height,                                        // 输入视频的宽和高
            AV_PIX_FMT_RGBA,                                            // 输入的像素格式
            yuv_width, yuv_height,                                        // 输出视频的宽和高
            AV_PIX_FMT_YUV420P,                                            // 输出的像素格式
            SWS_BILINEAR,                                                // 选择尺寸变化的算法,这里我们选用双线性插值
            0, 0, 0                                                        // 过滤器参数,这里不指定
        );

        if (!rgb2yuv)
        {
            cerr << "sws_getCachedContext(rgb2yuv) failed" << endl;
            return -1;
        }


        // 输出的结果代表转换的高度
        int re = sws_scale(rgb2yuv,
            rgba,                                                        // 输入的数据
            rgba_linesize,                                                // 输入数据的行字节数
            0,                                                            // 输入数据的Y轴,这里没有
            rgb_height,                                                    // 输入的高度
            yuv,                                                        // 输出的数据
            yuv_linesize                                                // 输出的行大小
        );

        cout <<"("<< re <<") "<< flush;
    }

    delete yuv[0];
    delete yuv[1];
    delete yuv[2];
    
    delete rgba[0];

    return 0;
}

运行结果:

另外分享一个YUV/RGB的播放器,用于播放转换完成后的YUV或RGB文件:

链接: https://pan.baidu.com/s/1sYjfCZLOTmhzXyG3gmn7ww 提取码: tcyw 

软件界面如下:

可以直接拖动YUV文件到该播放器进行播放,但是需要选择对应的分辨率及像素格式:

分辨率

输入该视频的分辨率:

 像素格式

一般指定完这两项后,点击下方的播放按钮就可以正常播放了。RGB文件类似。详情可参考雷神的博客:https://blog.csdn.net/leixiaohua1020/article/details/50466201

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