惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Apple Machine Learning Research
Apple Machine Learning Research
M
MIT News - Artificial intelligence
罗磊的独立博客
博客园 - 【当耐特】
A
About on SuperTechFans
Last Week in AI
Last Week in AI
雷峰网
雷峰网
IT之家
IT之家
aimingoo的专栏
aimingoo的专栏
H
Hackread – Cybersecurity News, Data Breaches, AI and More
博客园_首页
博客园 - 叶小钗
Microsoft Azure Blog
Microsoft Azure Blog
博客园 - Franky
J
Java Code Geeks
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
D
Docker
Engineering at Meta
Engineering at Meta
B
Blog RSS Feed
The Cloudflare Blog
大猫的无限游戏
大猫的无限游戏
阮一峰的网络日志
阮一峰的网络日志
S
SegmentFault 最新的问题
Recent Announcements
Recent Announcements

博客园 - 飘杨......

为ZLMediaKit在windows10编译srtp Windows10安装部署ZLMediaKit SurfaceView+EGL+OpenGL ES+ffmpeg播放视频并解决黑屏不显示画面的问题 SurfaceView+ANativeWindow+ffmpeg播放视频并解决花屏问题 SDL播放音频工具类封装 Qt QWidget中dropEvent(QDropEvent* e)拖拽放开不接收事件/不执行,且拖拽到目标后鼠标显示禁用符号 ffmpeg视频转码遇到H264黑屏问题 ffmpeg根据输入的参数截取视频 ffmpeg将视频流录制下来并保存到本地 ffmpeg解封装mp4并解码渲染(SDL2) Qt+ffmpeg+SDL硬件解码并渲染 ffmpeg+sdl播放H264/H265文件 FFmpeg实战:录制Windows屏幕并用FFmpeg压缩成H.264/H.265并保存 ffmpeg SwsContext像素格式转换BGRA转YUV420P H264/H265 NALU的nalu_unit_type解析 ffmpeg码率控制模式详解 ffmpeg-H264/H265常用编解码预设参数 Qt+FFmpeg+SDL 实现多路多格式原始视频播放工具:从像素处理到实时渲染 SDL将YUV视频渲染到Qt窗口播放
Qt+SDL 实战:多格式视频播放工具开发 —— 支持 RGBA/ARGB/RGB...
飘杨...... · 2025-05-14 · via 博客园 - 飘杨......

一、概述

  本文将使用 Qt 和 SDL 开发一个支持多格式视频播放的工具,覆盖 RGBA、ARGB、RGB24、YUV420P 等常见像素格式。工具将实现窗口关闭事件响应、动态尺寸适配、播放参数(fps / 分辨率)自定义等核心功能。通过 Qt 处理界面交互与窗口生命周期管理(如closeEventresizeEvent),结合 SDL 完成视频解码与渲染,解决多格式数据到屏幕的高效显示问题。

  架构图:

  1. VideoRenderViewWindow为Qt显示界面,用于展示用户操作以及显示具体的视频播放。其依赖于VideoRenderViewController
  2. VideoRenderViewController为控制器,控制播放器工具的大部分逻辑,其引用BinaryFileReader以及SDLVideoRenderView。其会开启一个单独的线程来循环播放是视频
  3. BinaryFileReader二进制文件读取工具。
  4. SDLRenderUtil这是SDL的渲染工具,提供Init、Close、Render方法
  5. IVideoRenderView渲染方式接口,由其提供渲染创建方法,其子类来实现具体的功能。此项目中其子类的视线为SDLVideoRenderView,代表使用SDL渲染,后期如果有空还会加上Qt渲染或者OpenGL渲染。
  6. SDLVideoRenderView SDL渲染器,实现了IVideoRenderView接口。

二、代码示例

  1. VideoRenderViewWindow主要代码。【源码】
    • 设置关闭时自动销毁窗口
      setAttribute(Qt::WA_DeleteOnClose, true);  // 关闭时自动销毁窗口
    • 监听窗口变化并改变视频宽高
      void VideoRenderViewWindow::resizeEvent(QResizeEvent* event) {
          QWidget::resizeEvent(event);
          mWidth = event->size().width();
          mHeight = event->size().height();
          ui.labelVideo->resize(mWidth, mWidth*0.75);
          ui.labelVideo->move(0, 0);
      }
    • 监听点击了关闭事件
      void VideoRenderViewWindow::closeEvent(QCloseEvent* event) {
          event->accept();  // 接受关闭事件(允许关闭)
          VideoRenderViewController::Get()->Close();
          qDebug() << "窗口已关闭";
      }
    • 设置文件像素格式
      void VideoRenderViewWindow::onSelectPixFormatIndex(int pos)
      {
          switch (pos)
          {
          case 0:
              m_fmt = IVideoRenderView::ARGB;
              break;
          case 1:
              m_fmt = IVideoRenderView::RGBA;
              break;
          case 2:
              m_fmt = IVideoRenderView::RGB24;
              break;
          case 3:
              m_fmt = IVideoRenderView::YUV420P;
              break;
          }
          
      }
    • 打开文件、设置参数并开始播放
      void VideoRenderViewWindow::openFileAndStart()
      {
          QString filter = "RGB 文件 (*.*);;所有文件 (*.*)";
          // 打开文件选择对话框,选择单个文件
          QString filePath = QFileDialog::getOpenFileName(this, "选择 RGB 文件", "", filter);
          if (filePath.isEmpty()) {
              return;
          }
          mWidth = ui.lineEditWidth->text().toInt();
          mHeight = ui.lineEditHeight->text().toInt();
          this->resize(QSize(mWidth, mHeight));
          ui.labelVideo->resize(mWidth, mHeight);
          VideoRenderViewController::Get()->Close();
          VideoRenderViewController::Get()->mWidth = mWidth;
          VideoRenderViewController::Get()->mHeight = mHeight;
          VideoRenderViewController::Get()->fps = ui.lineEditFps->text().toInt();
          VideoRenderViewController::Get()->mPixFormat = m_fmt;
          VideoRenderViewController::Get()->win_id = (void*)ui.labelVideo->winId();
          VideoRenderViewController::Get()->mFilePath = filePath;
          VideoRenderViewController::Get()->Init();
          VideoRenderViewController::Get()->Start();
          VideoRenderViewController::Get()->Draw();
      }
  2. VideoRenderViewController主要代码【源码】
    • 初始化
      QString VideoRenderViewController::Init()
      {
          iVideoRenderView = IVideoRenderView::Create(IVideoRenderView::SDL);
          if (mWidth <= 0 || mHeight <= 0 || win_id == NULL)
          {
              qDebug() << "请设要播放文件的宽高";
              QString errorMsg = "请设要播放文件的宽高";
              return errorMsg;
          }
      
          BinaryFileReader::Get()->filename = mFilePath.toStdString();
          BinaryFileReader::Get()->width = mWidth;
          BinaryFileReader::Get()->height = mHeight;
          BinaryFileReader::Get()->Init();
      
          IVideoRenderView::PixFormat mFormat = IVideoRenderView::PixFormat::RGBA;
          switch (mPixFormat) {
          case 0:
              mFormat = IVideoRenderView::PixFormat::RGBA;
              data = new  char[mWidth * mHeight * 4];
              linesize = mWidth * mHeight * 4;
              break;
          case 1:
              mFormat = IVideoRenderView::PixFormat::ARGB;
              data = new  char[mWidth * mHeight * 4];
              linesize = mWidth * mHeight * 4;
              break;
          case 2:
              mFormat = IVideoRenderView::PixFormat::RGB24;
              data = new  char[mWidth * mHeight * 3];
              linesize = mWidth * mHeight * 3;
              break;
          case 3:
              mFormat = IVideoRenderView::PixFormat::YUV420P;
              data = new  char[mWidth * mHeight * 1.5];
              linesize = mWidth * mHeight * 1.5;
              break;
          }
          iVideoRenderView->Init(mWidth, mHeight, mFormat, win_id);
          return NULL;
      }
    • 绘制
      void VideoRenderViewController::Draw()
      {
          for (;;)
          {
              if (!isRunning)break;
              bool ret = BinaryFileReader::Get()->Read(data, linesize);
              if (!ret) {
                  break;
              }
              iVideoRenderView->Draw(data);
              int secondMs = (int)(1000 / fps);
              //std::this_thread::sleep_for(33ms);
              std::this_thread::sleep_for(std::chrono::milliseconds(secondMs));
          }
          /*if (data) {
              delete data;
          }*/
          qDebug() << "已释放了data内存";
      }
  3. BinaryFileReader【源码】
  4. SDLRenderUtil(上一节重点介绍过,本节只是做了一点改造)【源码】
  5. IVideoRenderView主要代码【源码】
    • 创建渲染器
      IVideoRenderView* IVideoRenderView::Create(RenderType type)
      {
          switch (type)
          {
          case IVideoRenderView::SDL:
              return SDLVideoRenderView::Get();
              break;
          default:
              break;
          }
          return nullptr;
      }
  6. SDLVideoRenderView 主要代码【源码】
    • 初始化SDL渲染器
      bool SDLVideoRenderView::Init(int w, int h, IVideoRenderView::PixFormat fmt, void* win_id)
      {
          if (w <= 0 || h <= 0)return false;
          //确保线程安全
          unique_lock<mutex> sdl_lock(m_mutex);
          //初始化SDL 视频库
          SDLRenderUtil::Get()->width = w;
          SDLRenderUtil::Get()->height = h;
          SDLRenderUtil::Get()->winId = win_id;
          SDLRenderUtil::Get()->PIX_FMT = fmt;//渲染模式,这个地方等下需要改
          return SDLRenderUtil::Get()->Init();
      }
    • 使用SDL进行渲染
      bool SDLVideoRenderView::Draw(char* data)
      {
          if (!data)return false;
          SDLRenderUtil::Get()->isEventQuit();
          unique_lock<mutex> sdl_lock(m_mutex);
          if (m_scale_width > 0 && m_scale_height > 0) {
              SDLRenderUtil::Get()->width = m_scale_width;
              SDLRenderUtil::Get()->height = m_scale_height;
          }
          return SDLRenderUtil::Get()->Render(data);
      }