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

  • 相关阅读:
    SpringCloud Alibaba开篇:SpringCloud这么火,为何还要学习SpringCloud Alibaba?
    SpringBoot整合原生OpenFegin的坑(非SpringCloud)
    Git入门教程,详解Git文件的四大状态
    全世界最强的算法平台codeforces究竟有什么魅力?
    设计模式第二篇,链式方法模式
    matplotlib设置颜色、标记、线条,让你的图像更加丰富
    20行代码实现,使用Tarjan算法求解强连通分量
    深入理解SVM,详解SMO算法
    手把手教你配置git和git仓库
    设计模式 | Catalog设计模式,抵御业务方需求变动
  • 原文地址:https://www.cnblogs.com/yongdaimi/p/15622377.html
Copyright © 2011-2022 走看看