123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622 |
- #include "VideoPlayer.h"
- #include "DecodeVedio.h"
- #include <QPainter>
- #include <QResizeEvent>
- #include <QEventLoop>
- #include <QVBoxLayout>
- #include "spdlog/spdlog.h"
- #include "FmtLog/fmtlog.h"
- VideoPlayer::VideoPlayer(QWidget *parent) : QWidget(parent)
- {
- // /* 初始化解码线程 */
- // m_threadDecode = new QThread(this);
- // m_decodeVedio = new DecodeVedio(m_threadDecode);
- m_previewImage = 2;
- m_fps = 0;
- m_semRefresh = new QSemaphore(0);
- m_timerRefreshUI.setSingleShot(false);
- /* 设置精度毫秒级 */
- m_timerRefreshUI.setTimerType(Qt::PreciseTimer);
- connect(&m_timerRefreshUI, &QTimer::timeout, this, &VideoPlayer::do_refreshUI);
- connect(this, &VideoPlayer::signal_refreshImage, this, &VideoPlayer::do_refreshSamImage);
- SPDLOG_TRACE("播放器线程ID:{}", QThread::currentThreadId());
- QStringList listDecoder;
- DecodeVedio::findHWDecoder(listDecoder);
- if(listDecoder.isEmpty())
- {
- SPDLOG_WARN("没有找到硬件解码器");
- }else {
- SPDLOG_DEBUG("支持的硬件解码器:");
- for(auto it : listDecoder)
- {
- SPDLOG_DEBUG("{}", it.toStdString());
- }
- }
- }
- VideoPlayer::~VideoPlayer()
- {
- if(m_timerRefreshUI.isActive())
- {
- m_timerRefreshUI.stop();
- }
- delete m_decodeVedio;
- if(m_image)
- {
- delete m_image;
- }
- SPDLOG_DEBUG("视频播放器已关闭");
- }
- /**
- * @brief 设置播放视频,启动定时器,定时器间隔决定播放的速度
- * 视频的宽和高使用QImage进行缩放
- * 视频大小在直接设置这个类的resize即可,有最小大小限制
- *
- * @param fileName
- */
- void VideoPlayer::openPlayVedio(const QString& fileName)
- {
- if(m_isOpenFile)
- {
- m_isOpenFile = false;
- stop();
- }
- if(m_decodeVedio == nullptr)
- {
- /* 初始化解码线程 */
- m_threadDecode = new QThread(this);
- m_decodeVedio = new DecodeVedio(m_threadDecode);
- connect(m_decodeVedio, &DecodeVedio::signal_playCompleted, this, &VideoPlayer::do_playCompleted);
- }
- if(m_decodeVedio->isDecoding())
- {
- m_decodeVedio->stopDecodeVedio();
- }
- if(fileName.isEmpty())
- {
- SPDLOG_WARN("文件名为空");
- return;
- }
- m_fileName = fileName;
- m_isOpenFile = true;
- m_isLocalFile = isLocalFile(fileName);
- m_decodeVedio->openVedio(fileName);
- /* 获取原始视频信息 */
- m_srcWidth = m_decodeVedio->getSrcVideoSize().width();
- m_srcHeight = m_decodeVedio->getSrcVideoSize().height();
- m_fps = m_decodeVedio->getFPS();
- m_duration = m_decodeVedio->getDuration();
- auto totalFarame = m_decodeVedio->getTotalFrame();
- SPDLOG_INFO("视频编码格式:{}", m_decodeVedio->getDecoderName().toStdString());
- int hh = m_duration / 3600000;
- int mm = (m_duration % 3600000) / 60000;
- int ss = (m_duration % 60000) / 1000;
- int ms = m_duration % 1000;
- SPDLOG_INFO("视频分辨率:{}x{} 帧率:{} 总帧数:{}", m_srcWidth, m_srcHeight, m_fps, totalFarame);
- SPDLOG_INFO("时长:{}h:{}m:{}.{}s 总时长:{}ms", hh, mm, ss, ms, m_duration);
- /* 设置视频宽和高的最小大小 */
- this->setMinimumSize(160,90);
- /* 开启定时器刷新 */
- if(m_fps <= 0)
- {
- m_fps = 25;
- }
- /* 开启解码,手动刷新第一帧 */
- m_decodeVedio->startDecodeVedio();
- m_semRefresh->release(2);
- emit signal_refreshImage();
- this->show();
- SPDLOG_DEBUG("打开视频成功:{}", fileName.toStdString());
- }
- /* 播放视频 */
- bool VideoPlayer::play()
- {
- if(!m_isOpenFile)
- {
- SPDLOG_ERROR("未打开视频文件!");
- return false;
- }
- if(m_playStatus)
- {
- return false;
- }
-
- /* 设置刷新时间 */
- m_timerRefreshUI.setSingleShot(false);
- m_interval = qRound64(1000.0 / m_fps);
- SPDLOG_DEBUG("刷新UI的定时间隔:{}",m_interval);
- m_timerRefreshUI.start(m_interval);
- m_playStatus = true;
-
- return true;
- }
- /* 暂停播放 */
- void VideoPlayer::pause()
- {
- if(!m_isOpenFile)
- {
- SPDLOG_ERROR("未打开视频文件!");
- return;
- }
- if(!m_isLocalFile)
- {
- SPDLOG_ERROR("不是本地视频文件,无法暂停!");
- return;
- }
- if(!m_playStatus)
- {
- return;
- }
- m_timerRefreshUI.stop();
- m_playStatus = false;
- }
- /* 停止播放,停止后停止解码,将时间等复位到开始时间 */
- void VideoPlayer::stop()
- {
- if(!m_isOpenFile)
- {
- SPDLOG_ERROR("未打开视频文件!");
- return;
- }
- SPDLOG_DEBUG("...停止播放...");
- m_fileName = QString();
- if(m_timerRefreshUI.isActive())
- {
- m_timerRefreshUI.stop();
- }
- // SPDLOG_DEBUG("...停止解码...");
- /* 删除解码器 */
- delete m_decodeVedio;
- m_decodeVedio = nullptr;
- delete m_threadDecode;
- m_threadDecode = nullptr;
-
- m_playStatus = false;
- m_isOpenFile = false;
- /* 绘制黑帧 */
- SPDLOG_DEBUG("绘制黑帧");
- m_image = new QImage(m_nowWidth, m_nowHeight, QImage::Format_RGB32);
- m_image->fill(Qt::black);
- update();
-
- }
- /* 后退,单位ms */
- void VideoPlayer::backward(qint64 ms)
- {
- if(!m_isOpenFile)
- {
- SPDLOG_ERROR("未打开视频文件!");
- return;
- }
- if(!m_isLocalFile)
- {
- SPDLOG_ERROR("不是本地视频文件,无法后退!");
- return;
- }
- /* 获取当前位置 */
- qint64 pos = m_decodeVedio->getCurrentPos();
- pos = pos - ms;
- if(pos < 0)
- {
- pos = 0;
- }
- setCurrentPos(pos);
- }
- /* 前进,单位ms */
- void VideoPlayer::forward(qint64 ms)
- {
- if(!m_isOpenFile)
- {
- SPDLOG_ERROR("未打开视频文件!");
- return;
- }
- if(!m_isLocalFile)
- {
- SPDLOG_ERROR("不是本地视频文件,无法前进!");
- return;
- }
- /* 获取当前位置 */
- qint64 pos = m_decodeVedio->getCurrentPos();
- SPDLOG_DEBUG("pos:{} ms:{}", pos, ms);
- pos = pos + ms;
-
- setCurrentPos(pos);
- }
- /* 获取视频时长 */
- qint64 VideoPlayer::getDuration()
- {
- if(!m_isOpenFile)
- {
- SPDLOG_ERROR("未打开视频文件!");
- return -1;
- }
- auto duration = m_decodeVedio->getDuration();
- if(duration <= 0)
- {
- return 0;
- }
- return duration;
- }
- /* 获取当前播放位置 */
- qint64 VideoPlayer::getCurrentPos()
- {
- if(!m_isOpenFile)
- {
- SPDLOG_ERROR("未打开视频文件!");
- return -1;
- }
- auto pos = m_decodeVedio->getCurrentPos();
- if(pos < 0)
- {
- return 0;
- }
- return pos;
- }
- /* 设置当前播放位置,单位ms */
- void VideoPlayer::setCurrentPos(qint64 pos)
- {
- if(!m_isOpenFile)
- {
- SPDLOG_ERROR("未打开视频文件!");
- return;
- }
- if(!m_isLocalFile)
- {
- SPDLOG_ERROR("不是本地视频文件,无法设置播放位置!");
- return;
- }
- if(pos < 0)
- {
- pos = 0;
- }
- /* 先停止播放 */
- bool temp = m_playStatus;
- if(m_playStatus)
- {
- m_timerRefreshUI.stop();
- m_playStatus = false;
- }
- m_decodeVedio->setCurrentPos(pos);
- /* 继续播放 */
- if(temp)
- {
- // SPDLOG_INFO("..........开启定时器..........");
- m_timerRefreshUI.start(m_interval);
- m_playStatus = true;
- }else
- {
- /* 刷新2张照片 */
- m_semRefresh->release(m_previewImage);
- emit signal_refreshImage();
- }
- SPDLOG_INFO("设置播放位置:{} s", m_decodeVedio->getCurrentPos() / 1000.0);
- }
- /* 设置播放视频大小 */
- void VideoPlayer::setPlayWidgetSize(int width,int height)
- {
- /* 对宽和高就行缩放,保持比例,同时将其居中放置
- * 先计算出比例,和16/9相对比
- * 大于16/9,以高为最大极限,计算出宽度和x坐标
- * 小于16/9,以宽为最大极限,计算出高度和y坐标 */
- double srcRatio = m_srcWidth*1.0 / m_srcHeight;
- double ratio = width*1.0 / height;
- long w1 = 0, h1 = 0;
- int srcX = this->pos().rx(), srcY = this->pos().ry();
- int x1 = srcX, y1 = srcY;
- if(ratio > srcRatio)
- {
- w1 = height * srcRatio;
- x1 = (width - w1) / 2;
- h1 = height;
- y1 = srcY;
- }
- else if(ratio < srcRatio)
- {
- h1 = width / srcRatio;
- y1 = (height - h1) / 2;
- w1 = width;
- x1 = srcX;
- }else {
- w1 = width;
- h1 = height;
- x1 = srcX;
- y1 = srcY;
- }
- this->move(x1, y1);
-
- m_nowWidth = w1;
- m_nowHeight = h1;
- this->resize(w1, h1);
- // SPDLOG_DEBUG("设置窗口位置:{}x{}, 大小:{}x{}, 传入大小:{}x{}", x1, y1, w1, h1, width, height);
- SPDLOG_DEBUG("现在位置和大小:{}x{}, {}x{}", this->pos().rx(), this->pos().ry(), this->width(), this->height());
- }
- /**
- * @brief 设置播放窗口,这用于独占一个传入的widget,这里会自动添加一个布局,外面窗口变化,这里也跟随着变化
- *
- * @param widget
- * @param flag
- * @arg true:独占widget,并设置一个layout,会随着传入的widget大小变化
- * @arg false:不独占
- */
- void VideoPlayer::setPlayWidget(QWidget* widget, bool flag)
- {
- if(widget == nullptr)
- {
- SPDLOG_WARN("传入的widget为空");
- return;
- }
- if(flag)
- {
- /* 设置布局 */
- QVBoxLayout* layout = new QVBoxLayout(widget);
- layout->addWidget(this);
- layout->setMargin(0);
- layout->setSpacing(0);
- widget->setLayout(layout);
- }else
- {
- this->setParent(widget);
- /* 设置窗口大小 */
- setPlayWidgetSize(widget->width(), widget->height());
- }
-
- }
- /**
- * @brief 设置预览图片数目,在暂停时跳转,可能会有花屏或者黑帧,可以设置跳转图片个数跳过黑帧
- * 默认是2帧
- *
- * @param num
- */
- void VideoPlayer::setPreviewImage(int num)
- {
- m_previewImage = num;
- }
- /**
- * @brief 设置帧率,有些视频无法获取到帧率,就会使用默认的25fps,如果需要,可以通过这个函数设置
- * 注意:这个函数需要在打开视频文件之后设置,打开一次视频文件会覆盖这个参数
- *
- * @param fps
- */
- void VideoPlayer::setFPS(int fps)
- {
- m_fps = fps;
- if(m_decodeVedio != nullptr)
- {
- m_decodeVedio->setFPS(fps);
- }
- if(m_timerRefreshUI.isActive())
- {
- m_timerRefreshUI.stop();
- m_interval = qRound64(1000.0 / m_fps);
- m_timerRefreshUI.start(m_interval);
- }
- }
- /* 设置播放回调函数 */
- // void VideoPlayer::setPlayCallBack(std::function<Play_CallBack> playCallBack,void* context)
- // {
- // m_funcPlayCB = playCallBack;
- // m_context = context;
- // }
- void VideoPlayer::paintEvent(QPaintEvent *event)
- {
- if(m_image != nullptr)
- {
- // SPDLOG_TRACE("开始绘制画面...");
- /* 对图像进行缩放 */
- if(m_srcWidth != m_nowWidth || m_srcHeight != m_nowHeight)
- {
- *m_image = m_image->scaled(m_nowWidth, m_nowHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation);
- }
- QPainter painter(this);
- painter.drawImage(0, 0, *m_image);
- }
- }
- void VideoPlayer::resizeEvent(QResizeEvent *event)
- {
- SPDLOG_TRACE("窗口大小改变...");
- m_nowWidth = event->size().width();
- m_nowHeight = event->size().height();
- QWidget::resizeEvent(event);
- }
- /* 刷新一张图片,直到有图片为止 */
- void VideoPlayer::refreshOneUIUntilHave()
- {
- if(m_decodeVedio != nullptr)
- {
- // SPDLOG_DEBUG("取出一帧图片...");
- /* 删除上一帧图片 */
- if(m_image != nullptr)
- {
- delete m_image;
- m_image = nullptr;
- }
- /* 如果没有图片,这个函数会阻塞 */
- m_image = m_decodeVedio->getOneImageUntilHave();
-
- if(m_image)
- {
- if(m_image->isNull())
- {
- SPDLOG_WARN("取出的图片为空...");
- return;
- }
- // SPDLOG_DEBUG("绘制画面...");
- update();
- }
- }
- }
- /* 双击事件函数 */
- // void VideoPlayer::mouseDoubleClickEvent(QMouseEvent *event)
- // {
- // if(event->button() == Qt::LeftButton)
- // {
- // // SPDLOG_DEBUG("双击事件...");
- // // if(m_funcPlayCB != nullptr)
- // // {
- // // m_funcPlayCB(this, 5, nullptr, 0, m_context);
- // // }else {
- // // SPDLOG_INFO("没有设置回调函数");
- // // }
- // }
- // }
- /* 取出画面,刷新UI */
- void VideoPlayer::do_refreshUI()
- {
- if(m_decodeVedio != nullptr)
- {
- // SPDLOG_DEBUG("取出一帧图片...");
- /* 删除上一帧图片 */
- if(m_image != nullptr)
- {
- delete m_image;
- m_image = nullptr;
- }
- m_image = m_decodeVedio->getOneImage();
-
- if(m_image)
- {
- if(m_image->isNull())
- {
- SPDLOG_WARN("取出的图片为空...");
- return;
- }
- // SPDLOG_DEBUG("绘制画面...");
- update();
- }
- // m_decodeVedio->wakeUpCondQueueNoEmpty();
- }
- }
- /* 通过信号刷新第一张图片 */
- void VideoPlayer::do_refreshSamImage()
- {
- if(!m_isOpenFile)
- {
- return;
- }
- while(m_semRefresh->tryAcquire(1))
- {
- /* 取出第一张 */
- if(m_decodeVedio != nullptr)
- {
- // SPDLOG_DEBUG("取出一帧图片...");
- /* 删除上一帧图片 */
- if(m_image != nullptr)
- {
- delete m_image;
- m_image = nullptr;
- }
- /* 等待图片,最多等待50ms */
- m_image = m_decodeVedio->getOneImageUntilHave(100);
- if(m_image)
- {
- if(m_image->isNull())
- {
- SPDLOG_WARN("取出的图片为空...");
- return;
- }
- SPDLOG_DEBUG("绘制预览画面。");
- update();
- }
- }
- }
- }
- /* 播放完成 */
- void VideoPlayer::do_playCompleted()
- {
- SPDLOG_INFO("视频播放完成。");
- m_timerRefreshUI.stop();
- /* 手动刷新剩余的环形队列中的图片 */
- while(true)
- {
- if(m_decodeVedio != nullptr)
- {
- QImage* image = nullptr;
- image = m_decodeVedio->getOneImage();
- if(image == nullptr)
- {
- break;
- }
- /* 删除上一帧图片 */
- if(m_image != nullptr)
- {
- delete m_image;
- m_image = nullptr;
- }
- m_image = image;
-
- if(m_image->isNull())
- {
- SPDLOG_WARN("取出的图片为空...");
- return;
- }
- // SPDLOG_DEBUG("绘制画面...");
- update();
- }
- }
- m_playStatus = false;
- // if(m_funcPlayCB != nullptr)
- // {
- // /* 播放完成的回调函数 */
- // m_funcPlayCB(this, 2, nullptr, 0, m_context);
- // }
- }
- /* 判断是否是本地文件 */
- bool VideoPlayer::isLocalFile(const QString& fileName)
- {
- if(fileName.isEmpty())
- {
- return false;
- }
- if(fileName.startsWith("http://") || fileName.startsWith("rtsp://")
- || fileName.startsWith("rtmp://") || fileName.startsWith("https://"))
- {
- return false;
- }
- return true;
- }
|