当前位置: 首页 > news >正文

OpenCV实现一个视频播放器

OpenCV计算机视觉开发实践:基于Qt C++ - 商品搜索 - 京东

播放器的基本功能包括播放、暂停、快进、后退、重新播放、停止、拖动进度条等。通过OpenCV实现视频播放器,其思路大致就是在线程中使用OpenCV的VideoCapture循环读取本地视频的每一帧Mat,然后将其发送到界面转换成QImage进行显示,而进度条拖动则用到了VideoCapture中的set函数,进度条则使用QSlider,并且通过自定义新的进度条类实现单击跳转功能。

由于本书不是专门讲述Qt编程的书,因此一些Qt界面设计和线程编程的基础知识这里就不展开讲解了。

【例12.5】实现一个视频播放器

   新建一个Qt Widgets应用工程,工程名是viPlayer。在“Class Information”对话框上设置“Base Class”为QWidget,其他保持默认。

   设计界面。在Qt Creator中双击打开widget.ui,从控件盒子中拖放6个Tool Button、3个Horizontal Spacer以及1个Horizontal Slider到窗口上,并设置每个控件的objectName以及布局等,最终设计界面如图12-8所示。

图12-8

这些按钮的图片都在项目文件夹的img目录下,可以作为资源添加到IDE中。

   实现滑块功能。在项目中添加newqslider.h和newqslider.cpp。这里需要对horizontalSlider的单击事件函数进行重写,实现单击进度条后获取单击处的进度值并更新进度条的功能。

#include "newqslider.h"newqslider::newqslider(QWidget *parent) : QSlider(parent)
{
}
/*****************************************************************
* 函数名称:mousePressEvent(QMouseEvent *ev)
* 功能描述:重写鼠标单击事件,实现进度条单击哪儿就跳到哪儿
* 参数说明: 无
* 返回值:   无
******************************************************************/
void newqslider::mousePressEvent(QMouseEvent *ev)
{// 先调用父类的鼠标单击处理事件,这样可以不影响拖动的情况QSlider::mousePressEvent(ev);// 获取鼠标的位置,这里并不能直接从ev中取值(因为如果是拖动的话,鼠标开始单击的位置就没有意义了)double pos = ev->pos().x() / (double)width();setValue(pos * (maximum() - minimum()) + minimum());// 发送自定义的鼠标单击信号emit costomSliderClicked();
}

实现OpenCV采集线程。在项目中添加videothread.h,代码如下:

#include <QObject>
#include <QThread>
#include <opencv2/opencv.hpp>
#include <iostream>
#include <QDebug>
#include <QDateTime>
using namespace  std;
using namespace  cv;
class videothread : public QThread
{Q_OBJECT
public:videothread(const char* filename);void run();// 释放视频采集对象void releaseCap();// 获取视频总帧数int getVideoAllFramecount();// 设置当前进度条void setCurrentFrame(int value);bool getStop() const;// 设置视频结束标识void setStop(bool value);bool getIsrun() const;void setIsrun(bool value);// 暂停void pauseThread();// 继续void resumeThread();// 停止void stopThread();signals:// 发送当前帧和帧数void sendFrame(int currentFrame,Mat frame);
private:// 视频对象VideoCapture cap;Mat frame;// 视频当前帧数int currentFramecount;// 总帧数int allFramecount;// 视频帧率int fps;// 录制视频帧int videoWriterFrame;// 线程结束标识位bool stop;// 视频暂停标识位bool isrun;
};

在项目中添加videothread.cpp,代码如下:

#include "videothread.h"videothread::videothread(const char* filename)
{this->stop = false;this->isrun =false;this->currentFramecount=0;this->videoWriterFrame=0;if(cap.open(filename));// 创建视频对象{this->allFramecount=cap.get(CAP_PROP_FRAME_COUNT);// 获取视频文件中的总帧数this->fps=int(round(cap.get(CAP_PROP_FPS)));// 获取视频帧率}
}void videothread::run()
{while(stop==false)// 线程运行和停止,卡住线程,暂停时不退出线程{while(isrun==true)// 视频运行和暂停{if(cap.read(frame))// 捕获视频帧{this->currentFramecount++;cvtColor(frame, frame, COLOR_BGR2RGB);// 将opencvBGR格式转换成Image用到的RGBemit sendFrame(currentFramecount,frame);// 发送帧数据}msleep(40);// 延时}}cap.release();// 释放打开的视频
}int videothread::getVideoAllFramecount()
{return allFramecount;
}void videothread::setStop(bool value)
{stop = value;
}void videothread::setCurrentFrame(int value)
{this->currentFramecount=value;// 当前帧数cap.set(CAP_PROP_POS_FRAMES,currentFramecount);// 进度条跳转对应帧
}bool videothread::getIsrun() const
{return isrun;
}void videothread::setIsrun(bool value)
{isrun = value;
}void videothread::pauseThread()// 这两个函数用于确保在运行情况下才能切换状态
{if(this->isRunning()&&this->isrun==true)// 当前线程运行且视频运行{this->isrun=false;}
}void videothread::resumeThread()
{if(this->isRunning()&&this->isrun==false)// 当前线程运行且视频暂停{this->isrun=true;}
}void videothread::stopThread()
{if(this->isRunning())// 当前线程运行{this->stop=true;// 结束线程// msleep(10);releaseCap();this->terminate();}
}bool videothread::getStop() const
{return stop;
}void videothread::releaseCap()
{if(cap.isOpened()){cap.release();}
}

采集线程中设置了暂停、继续播放、停止功能,并可以获取帧率和帧数量。这个线程实现了核心功能。界面上的按钮事件处理函数和滑块事件处理函数都和线程有关,比如重播按钮的事件处理函数代码如下:

void Widget::on_btn_replay_clicked()
{pthread->stopThread();			// 结束线程qDebug()<<"重新播放";if(!pthread->isRunning())		// 线程没有运行{if(isend!=true)			// 此时线程结束并已释放,就不再释放{// 断开连接disconnect(pthread,SIGNAL(sendFrame(int,Mat)),this,SLOT(receiveFrame(int,Mat)));// 接收每一帧Matdelete pthread;pthread = nullptr;}// 创建新线程QByteArray ba = videoFilePath.toLocal8Bit();char* ch = ba.data();pthread=new videothread(ch);pthread->start();pthread->setIsrun(true);// 视频开始connect(pthread,SIGNAL(sendFrame(int,Mat)),this,SLOT(receiveFrame(int,Mat)));
// 接收每一帧Matisend=false;// 表明此时线程还未结束ui->btn_startPlay->setIcon(QIcon(":/img/24gf-pause.png"));ui->btn_backward->setEnabled(true);ui->btn_forward->setEnabled(true);}}

限于篇幅,其他控件的事件处理函数不在这里给出,具体可以查看本书配套资源中的源码工程中的widget.cpp。

   运行程序,/root/下的hd.mp4就可以播放了,按暂停键后的界面如图12-9所示。

​​​​​​​

图12-9

此视频播放器只涉及OpenCV加载视频和对视频帧的处理,以及进度条管理等部分功能。在使用中需要注意线程指针的创建和释放以及OpenCV采集对象的新建和释放,否则频繁停止和加载会出现野指针和内存泄漏的bug。

http://www.xdnf.cn/news/410509.html

相关文章:

  • 基于FastAPI框架的日志模块设计
  • 2025年网站安全防御全解析:应对DDoS与CC攻击的智能策略
  • 处理 Websocket 超时问题
  • Python 处理图像并生成 JSONL 元数据文件 - 固定text版本
  • 从 JIT 即时编译一直讲到CGI|FastGGI|WSGI|ASGI四种协议的实现细节
  • 【Java ee初阶】IP协议
  • spdlog日志器(logger)的创建方法大全
  • 优选算法第十一讲:栈
  • 电磁场分析创建过程
  • 设置环境变量启动jar报
  • elementPlus el-switch 进入页面时触发@change事件
  • 计算机网络:CPU与时钟的关系
  • 【计算机网络】高频计网面试总结
  • Java静态变量笔记
  • 告别数据僵尸!Redis实现自动清理过期键值对
  • Icepak水冷教程
  • mysql中索引的使用
  • 如何利用自然语言生成技术重塑内容生产范式?
  • 细说STM32单片机FreeRTOS事件组及其编程应用实例
  • C++核心编程解析:模板、容器与异常处理全指南
  • AIGC时代的内容安全:AI检测技术如何应对新型风险挑战?
  • 【八股消消乐】慢SQL优化手段总结
  • Claude深度解析:从技术原理到实战应用的全栈指南
  • 大模型剪枝技术介绍
  • Kotlin 懒初始化值
  • Android音频解码中的时钟同步问题:原理、挑战与解决方案
  • 基于SpringBoot3实现MyBatis-Plus两种条件构造器(QueryWrapper、UpdateWrapper)入门实战
  • AI工具分享篇|VDraw.ai免费生成长图
  • 第十部分:文件与动静态库
  • C# 基础 try-catch代码块