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

  • 相关阅读:
    P1144 最短路计数 题解 最短路应用题
    C++高精度加减乘除模板
    HDU3746 Teacher YYF 题解 KMP算法
    POJ3080 Blue Jeans 题解 KMP算法
    POJ2185 Milking Grid 题解 KMP算法
    POJ2752 Seek the Name, Seek the Fame 题解 KMP算法
    POJ2406 Power Strings 题解 KMP算法
    HDU2087 剪花布条 题解 KMP算法
    eclipse创建maven项目(详细)
    maven的作用及优势
  • 原文地址:https://www.cnblogs.com/yongdaimi/p/15622377.html
Copyright © 2011-2022 走看看