XVideoView.h
#ifndef XVIDEOVIEW_H #define XVIDEOVIEW_H #includeclass XVideoView { public: enum PixelFormat { RGBA = 0, ARGB, YUV420P }; enum ViewType { VIEW_SDL = 0 }; XVideoView(); static XVideoView* create(ViewType type = VIEW_SDL); virtual bool init(int w, int h, PixelFormat fmt = RGBA, void* winId = nullptr) = 0; // 清理所有清理的资源,包括关闭窗口 virtual void close() = 0; // 处理窗口退出事件 virtual bool isExit() = 0; virtual bool draw(const unsigned char* data, int lineSize = 0) = 0; void scale(int w, int h); protected: int m_width; // 材质宽高 int m_height; int m_scaleWid; // 显示大小 int m_scaleHgh; PixelFormat m_fmt; std::mutex m_mtx; // 保证线程安全 }; #endif
XVideoView.cpp
#include "XVideoView.h"
#include "XSDL.h"
XVideoView::XVideoView()
{
m_width = 0;
m_height = 0;
m_fmt = ARGB;
m_scaleWid = 0;
m_scaleHgh = 0;
}
XVideoView* XVideoView::create(ViewType type)
{
XVideoView* ret = nullptr;
switch (type)
{
case VIEW_SDL:
ret = new XSDL(); // 使用 SDL 方式进行渲染
break;
default:
break;
}
return ret;
}
void XVideoView::scale(int w, int h)
{
m_scaleWid = w;
m_scaleHgh = h;
}
XSDL.h
#ifndef XSDL_H
#define XSDL_H
#include "XVideoView.h"
class SDL_Window;
class SDL_Renderer;
class SDL_Texture;
class XSDL : public XVideoView
{
public:
XSDL();
bool init(int w, int h, PixelFormat fmt = RGBA, void* winId = nullptr) override;
// 清理所有清理的资源,包括关闭窗口
void close() override;
// 处理窗口退出事件
bool isExit() override;
bool draw(const unsigned char* data, int lineSize = 0) override;
private:
bool initVideo();
SDL_Window* m_win;
SDL_Renderer* m_render;
SDL_Texture* m_texture;
};
#endif
XSDL.cpp
#include测试代码#include "XSDL.h" #include "sdl/SDL.h" #pragma comment(lib, "SDL2.lib") using namespace std; XSDL::XSDL() { m_win = nullptr; m_render = nullptr; m_texture = nullptr; } bool XSDL::initVideo() { static bool first_init = false; static mutex mtx; unique_lock sdl_lock(mtx); // sdl_lock 在创建时自动加锁,生命周期结束时自动解锁,为了解决锁还没释放函数就返回可能导致的死锁问题 if (!first_init) { first_init = (SDL_Init(SDL_INIT_VIDEO) == 0); if (first_init) { //设定缩放算法,解决锯齿问题,线性插值算法 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); } else { cerr << SDL_GetError() << endl; } } return first_init; } bool XSDL::init(int w, int h, PixelFormat fmt, void* winId) { bool ret = ((w > 0) && (h > 0) && initVideo()); if (ret) { close(); // 可能已经初始化,先进行清理操作 // 确保线程安全 unique_lock sdl_lock(m_mtx); m_width = w; m_height = h; m_fmt = fmt; if (winId) { // 渲染到控件窗口 m_win = SDL_CreateWindowFrom(winId); } else { // 新建窗口 m_win = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, w, h, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); } if (!m_win) { cerr << SDL_GetError() << endl; ret = false; } if (ret) { m_render = SDL_CreateRenderer(m_win, -1, SDL_RENDERER_ACCELERATED); if (!m_render) { cerr << SDL_GetError() << endl; ret = false; } } if (ret) { unsigned int pixFmt = SDL_PIXELFORMAT_ARGB8888; switch (fmt) { case XVideoView::ARGB: break; case XVideoView::RGBA: pixFmt = SDL_PIXELFORMAT_RGBA8888; break; case XVideoView::YUV420P: pixFmt = SDL_PIXELFORMAT_IYUV; break; } m_texture = SDL_CreateTexture(m_render, pixFmt, SDL_TEXTUREACCESS_STREAMING, w, h); // 这里的 w, h 为分辨率 if (!m_texture) { cerr << SDL_GetError() << endl; ret = false; } } } return ret; } void XSDL::close() { unique_lock sdl_lock(m_mtx); if (m_texture) { SDL_DestroyTexture(m_texture); m_texture = nullptr; } if (m_render) { SDL_DestroyRenderer(m_render); m_render = nullptr; } if (m_win) { SDL_DestroyWindow(m_win); m_win = nullptr; } } bool XSDL::isExit() { bool ret = false; SDL_Event evt; SDL_WaitEventTimeout(&evt, 1); if (evt.type == SDL_QUIT) { ret = true; } return ret; } bool XSDL::draw(const unsigned char* data, int lineSize) { unique_lock sdl_lock(m_mtx); bool ret = (data && (m_width > 0) && (m_height > 0) && m_win && m_render && m_texture); if (ret) { if (lineSize <= 0) { switch (m_fmt) { case XVideoView::RGBA: case XVideoView::ARGB: lineSize = m_width * 4; break; case XVideoView::YUV420P: lineSize = m_width; break; default: break; } if (lineSize <= 0) { cerr << "Param lineSize is invalid ...n" << endl; ret = false; } } if (ret) { // 复制内存到显存中 ret = (SDL_UpdateTexture(m_texture, nullptr, data, lineSize) == 0); if (ret) { SDL_Rect rect; SDL_Rect* pr = nullptr; rect.x = 0; rect.y = 0; rect.w = m_scaleWid; // rect.w, rect.h 为显示的尺寸大小 rect.h = m_scaleHgh; if ((m_scaleWid > 0) && (m_scaleHgh > 0)) { pr = ▭ } // 清空屏幕 SDL_RenderClear(m_render); // 将材质复制到渲染器 ret = (SDL_RenderCopy(m_render, m_texture, nullptr, pr) == 0); if (ret) { SDL_RenderPresent(m_render); } else { cerr << SDL_GetError() << endl; } } else { cerr << SDL_GetError() << endl; } } } return ret; }
SdlQtRGB.h
#pragma once #include#include "ui_SdlQtRGB.h" class SdlQtRGB : public QWidget { Q_OBJECT public: SdlQtRGB(QWidget *parent = Q_NULLPTR); ~SdlQtRGB(); private: Ui::SdlQtRGBClass ui; void timerEvent(QTimerEvent* evt) override; void resizeEvent(QResizeEvent* evt) override; };
SdlQtRGB.cpp
#include "SdlQtRGB.h" #include "XVideoView.h" #include测试结果#include #include using namespace std; static unsigned char* yuv = nullptr; static int sdl_width = 0; static int sdl_height = 0; static int pixel_size = 2; static ifstream yuv_file; static XVideoView* view = nullptr; SdlQtRGB::SdlQtRGB(QWidget *parent) : QWidget(parent) { yuv_file.open("400_300_25.yuv", ios::in | ios::binary); if (!yuv_file) { QMessageBox::information(this, "information", "open 400_300_25.yuv failed!"); return; } ui.setupUi(this); sdl_width = 400; sdl_height = 300; ui.label->resize(400, 300); view = XVideoView::create(); if (view) { view->init(sdl_width, sdl_height, XVideoView::YUV420P); view->init(sdl_width, sdl_height, XVideoView::YUV420P, (void*)ui.label->winId()); } yuv = new unsigned char[sdl_width * sdl_height * pixel_size]; startTimer(10); // 每过10ms就会调用一次 timerEvent,1s大约有100帧的图像 } void SdlQtRGB::timerEvent(QTimerEvent* evt) { yuv_file.read((char*)yuv, sdl_width * sdl_height * 1.5); // 读取一帧数据 if (view) { view->draw(yuv); if (view->isExit()) { view->close(); delete view; exit(0); } } } void SdlQtRGB::resizeEvent(QResizeEvent* evt) { ui.label->resize(size()); ui.label->move(0, 0); // view->scale(width(), height()); } SdlQtRGB::~SdlQtRGB() { delete[] yuv; }
可以成功的渲染。



