zoukankan      html  css  js  c++  java
  • 音视频技术应用(10) 使用SDL 直接播放和渲染YUV文件

    本节记录下如何使用SDL直接播放和渲染RGB文件

    1. 首先准备好需要播放的YUV文件

    这里准备了一个mp4文件,我们要利用ffmpeg将该mp4文件直接转换成YUV文件,另外需要注意的是,由于YUV文件是未经压缩的文件,不同于mp4, 如果转换前的mp4文件时间很长,那么转换后的YUV文件将会很大,所以我们这里只取一小段的mp4文件(分辨率为400*300,帧率为25fps)进行格式转换。转换需要用到ffmpeg.exe, 可以从这里下载,

    链接: https://pan.baidu.com/s/1g18PLIrA-6DhYLnaSwx9Qw 提取码: umby 

    下载完毕后,将ffmpeg.exe和需要转换的mp4文件放置到指定的目录

    然后执行:

    ffmpeg -i 400_300_25.mp4 400_300_25.yuv

    生成完毕后的YUV文件如下:

    可以看到,生成的YUV文件还是很大的。

    另外需要注意的是,我们这里并没有给ffmpeg添加任何参数,所以ffmpeg是按照内部默认的格式进行转换的,它转换后的YUV的采样格式是 yuv420p , 这点可以从刚才的转换窗口中看出来:

    2. 准备coding

    修改之前编写的qt_rgb 解决方案,修改cpp code 如下:

    #include "sdlqtrgb.h"
    
    #include <iostream>
    #include <fstream>
    #include <qmessagebox.h>
    
    #include <sdl/SDL.h>
    
    #pragma comment(lib, "SDL2.lib")
    
    using namespace std;
    
    static int sdl_width = 0;
    static int sdl_height = 0;
    
    static SDL_Window* sdl_window = NULL;
    static SDL_Renderer* sdl_render = NULL;
    static SDL_Texture* sdl_texture = NULL;
    
    static int pixel_size = 2;                  // 在YUV420p采样格式下,单个像素的大小是1.5个字节,这里使用2个字节,便于容纳,这里的单个像素大小可以大于1.5,但是绝对不能小于1.5 
    static unsigned char* yuv = NULL;
    
    static ifstream yuv_file;
    
    SDLQtRGB::SDLQtRGB(QWidget *parent)
        : QWidget(parent)
    {
    
    
        ui.setupUi(this);
    
        // 打开YUV文件
        yuv_file.open("400_300_25.yuv", ios::binary);
        if (!yuv_file)
        {
            QMessageBox::information(this, "", "open yuv failed!");
            return;
        }
    
        // 设定窗口和label的宽高
        sdl_width = 400;
        sdl_height = 300;
    
        // 1. 初始化SDL
        if (SDL_Init(SDL_INIT_VIDEO))
        {
            cout << SDL_GetError() << endl;
            return;
        }
    
        // 2. 创建窗口, 这里取得label所对应的窗口句柄
        sdl_window = SDL_CreateWindowFrom((void*)ui.label->winId());
        if (!sdl_window)
        {
            cout << SDL_GetError() << endl;
            return;
        }
    
        // 3. 创建渲染器
        sdl_render = SDL_CreateRenderer(sdl_window, -1, SDL_RENDERER_ACCELERATED);
        if (!sdl_render)
        {
            cout << SDL_GetError() << endl;
            return;
        }
    
        // 重新设定label的大小, 因为yuv视频文件的分辨率是400*300, 所以这里更新label的大小,以便能够正常显示YUV图像
        ui.label->resize(sdl_width, sdl_height);
    
        // 4. 根据label控件的宽高来创建材质
        // 这里的像素格式要与YUV的图像格式相对应,之前生成的YUV的图像格式是YUV420p, 所以对应的材质的格式必须是 SDL_PIXELFORMAT_IYUV
        sdl_texture = SDL_CreateTexture(sdl_render,
            SDL_PIXELFORMAT_IYUV,
            SDL_TEXTUREACCESS_STREAMING, 
            sdl_width, sdl_height
        );
    
        if (!sdl_texture)
        {
            cout << SDL_GetError() << endl;
            return;
        }
    
        // 申请一块内存空间用于存放RGB数据
        yuv = new unsigned char[sdl_width * sdl_height * pixel_size];
    
    
        // 每隔10ms调用一次timerEvent函数
        startTimer(10);
    }
    
    
    void SDLQtRGB::timerEvent(QTimerEvent* ev)
    {
       
        // 读取yuv数据信息(一次读取一帧)
        // 1.5 代表单个YUV像素点的大小
        yuv_file.read((char *)yuv, sdl_width * sdl_height * 1.5);
    
    
        // 5. 动态更新材质信息
        // 这里宽度为什么可以设置成sdl_width?
        // 这是因为当前的采样格式是YUV420p, 是以平面格式进行存储的,也就是yyyy uu vv,
        // 所以每一行只需要传入y的字节数即可,而yuv420p 采样格式,y的字节大小为1,所以y的字节数为sdl_width
        if (SDL_UpdateTexture(sdl_texture, NULL, yuv, sdl_width))
        {
            cout << SDL_GetError() << endl;
            return;
        }
    
        // 6. 清理屏幕
        if (SDL_RenderClear(sdl_render))
        {
            cout << SDL_GetError() << endl;
            return;
        }
    
        // 7. 复制材质到渲染器对象
        SDL_Rect rect;
        rect.x = 0;
        rect.y = 0;
        rect.w = sdl_width;
        rect.h = sdl_height;
        if (SDL_RenderCopy(sdl_render, sdl_texture, NULL, &rect))
        {
            cout << SDL_GetError() << endl;
            return;
        }
    
        // 8. 执行渲染操作
        SDL_RenderPresent(sdl_render);
    
    }

    运行:

    可以看到yuv文件已经可以顺利渲染到指定的窗口上。

    <完>

  • 相关阅读:
    Delphi TListView刷新闪烁问题
    GO语言下载、安装、配置
    理解领域模型Domain Model
    Competing Consumers Pattern (竞争消费者模式)
    Compensating Transaction Pattern(事务修正模式)
    一致性hash算法简介
    CQRS, Task Based UIs, Event Sourcing agh!
    Circuit Breaker Pattern(断路器模式)
    Cloud Design Patterns: Prescriptive Architecture Guidance for Cloud Applications 云设计模式:云应用的规范架构指导
    Cache-Aside Pattern(缓存模式)
  • 原文地址:https://www.cnblogs.com/yongdaimi/p/15570334.html
Copyright © 2011-2022 走看看