VideoPlayer.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680
  1. #include "VideoPlayer.h"
  2. #include "DecodeVedio.h"
  3. #include <QPainter>
  4. #include <QResizeEvent>
  5. #include <QEventLoop>
  6. #include <QVBoxLayout>
  7. #include "FrameFormat.h"
  8. #include "OpenGLWidgetAPI.h"
  9. #include "spdlog/spdlog.h"
  10. VideoPlayer::VideoPlayer()
  11. {
  12. // /* 初始化解码线程 */
  13. // m_threadDecode = new QThread(this);
  14. // m_decodeVedio = new DecodeVedio(m_threadDecode);
  15. m_previewImage = 2;
  16. m_fps = 0;
  17. if(!loadOpenGLWidgetLibrary())
  18. {
  19. SPDLOG_ERROR("加载OpenGL窗口库失败,请检查是否存在OpenGLWidget库");
  20. return;
  21. }
  22. /* 创建OpenGLWidget */
  23. m_playWidget = createOpenGLWidget(nullptr);
  24. if(m_playWidget == nullptr)
  25. {
  26. SPDLOG_ERROR("创建OpenGLWidget失败");
  27. return;
  28. }
  29. m_semRefresh = new QSemaphore(0);
  30. m_timerRefreshUI.setSingleShot(false);
  31. /* 设置精度毫秒级 */
  32. m_timerRefreshUI.setTimerType(Qt::PreciseTimer);
  33. connect(&m_timerRefreshUI, &QTimer::timeout, this, &VideoPlayer::do_refreshUI);
  34. connect(this, &VideoPlayer::signal_refreshImage, this, &VideoPlayer::do_refreshSamImage);
  35. SPDLOG_TRACE("播放器线程ID:{}", QThread::currentThreadId());
  36. QStringList listDecoder;
  37. DecodeVedio::findHWDecoder(listDecoder);
  38. if(listDecoder.isEmpty())
  39. {
  40. SPDLOG_WARN("没有找到硬件解码器");
  41. }else {
  42. SPDLOG_DEBUG("支持的硬件解码器:");
  43. for(auto it : listDecoder)
  44. {
  45. SPDLOG_DEBUG("{}", it.toStdString());
  46. }
  47. }
  48. }
  49. VideoPlayer::~VideoPlayer()
  50. {
  51. if(m_timerRefreshUI.isActive())
  52. {
  53. m_timerRefreshUI.stop();
  54. }
  55. delete m_decodeVedio;
  56. if(m_image)
  57. {
  58. delete m_image;
  59. }
  60. if(m_playWidget != nullptr)
  61. {
  62. destroyOpenGLWidget(m_playWidget);
  63. }
  64. }
  65. /**
  66. * @brief 设置播放视频,启动定时器,定时器间隔决定播放的速度
  67. * 视频的宽和高使用QImage进行缩放
  68. * 视频大小在直接设置这个类的resize即可,有最小大小限制
  69. *
  70. * @param fileName
  71. */
  72. bool VideoPlayer::openPlayVedio(const QString& fileName)
  73. {
  74. if(m_isOpenFile)
  75. {
  76. m_isOpenFile = false;
  77. stop();
  78. }
  79. if(m_decodeVedio == nullptr)
  80. {
  81. /* 初始化解码线程 */
  82. m_threadDecode = new QThread(this);
  83. m_decodeVedio = new DecodeVedio(m_threadDecode);
  84. connect(m_decodeVedio, &DecodeVedio::signal_playCompleted, this, &VideoPlayer::do_playCompleted);
  85. }
  86. if(m_decodeVedio->isDecoding())
  87. {
  88. m_decodeVedio->stopDecodeVedio();
  89. }
  90. if(fileName.isEmpty())
  91. {
  92. SPDLOG_WARN("文件名为空");
  93. return false;
  94. }
  95. m_fileName = fileName;
  96. m_isOpenFile = true;
  97. m_isLocalFile = isLocalFile(fileName);
  98. if(!m_decodeVedio->openVedio(fileName))
  99. {
  100. return false;
  101. }
  102. /* 获取原始视频信息 */
  103. m_srcWidth = m_decodeVedio->getSrcVideoSize().width();
  104. m_srcHeight = m_decodeVedio->getSrcVideoSize().height();
  105. m_fps = m_decodeVedio->getFPS();
  106. m_duration = m_decodeVedio->getDuration();
  107. auto totalFarame = m_decodeVedio->getTotalFrame();
  108. SPDLOG_INFO("视频编码格式:{}", m_decodeVedio->getDecoderName().toStdString());
  109. int hh = m_duration / 3600000;
  110. int mm = (m_duration % 3600000) / 60000;
  111. int ss = (m_duration % 60000) / 1000;
  112. int ms = m_duration % 1000;
  113. SPDLOG_INFO("视频分辨率:{}x{} 帧率:{} 总帧数:{}", m_srcWidth, m_srcHeight, m_fps, totalFarame);
  114. SPDLOG_INFO("时长:{}h:{}m:{}.{}s 总时长:{}ms", hh, mm, ss, ms, m_duration);
  115. /* 设置视频宽和高的最小大小 */
  116. m_playWidget->setMinimumSize(160,90);
  117. /* 开启定时器刷新 */
  118. if(m_fps <= 0)
  119. {
  120. m_fps = 25;
  121. }
  122. /* 开启解码,手动刷新第一帧 */
  123. m_decodeVedio->startDecodeVedio();
  124. m_semRefresh->release(2);
  125. emit signal_refreshImage();
  126. return true;
  127. }
  128. /* 播放视频 */
  129. bool VideoPlayer::play()
  130. {
  131. if(!m_isOpenFile)
  132. {
  133. SPDLOG_ERROR("未打开视频文件!");
  134. return false;
  135. }
  136. if(m_playStatus)
  137. {
  138. return false;
  139. }
  140. /* 设置刷新时间 */
  141. m_timerRefreshUI.setSingleShot(false);
  142. m_interval = qRound64(1000.0 / m_fps);
  143. SPDLOG_DEBUG("刷新UI的定时间隔:{}",m_interval);
  144. m_timerRefreshUI.start(m_interval);
  145. m_playStatus = true;
  146. return true;
  147. }
  148. /* 暂停播放 */
  149. void VideoPlayer::pause()
  150. {
  151. if(!m_isOpenFile)
  152. {
  153. SPDLOG_ERROR("未打开视频文件!");
  154. return;
  155. }
  156. if(!m_isLocalFile)
  157. {
  158. SPDLOG_ERROR("不是本地视频文件,无法暂停!");
  159. return;
  160. }
  161. if(!m_playStatus)
  162. {
  163. return;
  164. }
  165. m_timerRefreshUI.stop();
  166. m_playStatus = false;
  167. }
  168. /* 停止播放,停止后停止解码,将时间等复位到开始时间 */
  169. void VideoPlayer::stop()
  170. {
  171. if(!m_isOpenFile)
  172. {
  173. SPDLOG_ERROR("未打开视频文件!");
  174. return;
  175. }
  176. SPDLOG_DEBUG("...停止播放...");
  177. m_fileName = QString();
  178. if(m_timerRefreshUI.isActive())
  179. {
  180. m_timerRefreshUI.stop();
  181. }
  182. // SPDLOG_DEBUG("...停止解码...");
  183. /* 删除解码器 */
  184. delete m_decodeVedio;
  185. m_decodeVedio = nullptr;
  186. delete m_threadDecode;
  187. m_threadDecode = nullptr;
  188. m_playStatus = false;
  189. m_isOpenFile = false;
  190. /* 绘制黑帧 */
  191. SPDLOG_DEBUG("绘制黑帧");
  192. QImage image(m_nowWidth, m_nowHeight, QImage::Format_RGBA8888);
  193. image.fill(Qt::black);
  194. // update();
  195. refreshRGBAImage(m_playWidget, image);
  196. }
  197. /* 后退,单位ms */
  198. void VideoPlayer::backward(qint64 ms)
  199. {
  200. if(!m_isOpenFile)
  201. {
  202. SPDLOG_ERROR("未打开视频文件!");
  203. return;
  204. }
  205. if(!m_isLocalFile)
  206. {
  207. SPDLOG_ERROR("不是本地视频文件,无法后退!");
  208. return;
  209. }
  210. /* 获取当前位置 */
  211. qint64 pos = m_decodeVedio->getCurrentPos();
  212. pos = pos - ms;
  213. if(pos < 0)
  214. {
  215. pos = 0;
  216. }
  217. setCurrentPos(pos);
  218. }
  219. /* 前进,单位ms */
  220. void VideoPlayer::forward(qint64 ms)
  221. {
  222. if(!m_isOpenFile)
  223. {
  224. SPDLOG_ERROR("未打开视频文件!");
  225. return;
  226. }
  227. if(!m_isLocalFile)
  228. {
  229. SPDLOG_ERROR("不是本地视频文件,无法前进!");
  230. return;
  231. }
  232. /* 获取当前位置 */
  233. qint64 pos = m_decodeVedio->getCurrentPos();
  234. SPDLOG_DEBUG("pos:{} ms:{}", pos, ms);
  235. pos = pos + ms;
  236. setCurrentPos(pos);
  237. }
  238. /* 获取视频时长 */
  239. qint64 VideoPlayer::getDuration()
  240. {
  241. if(!m_isOpenFile)
  242. {
  243. SPDLOG_ERROR("未打开视频文件!");
  244. return -1;
  245. }
  246. auto duration = m_decodeVedio->getDuration();
  247. if(duration <= 0)
  248. {
  249. return 0;
  250. }
  251. return duration;
  252. }
  253. /* 获取当前播放位置 */
  254. qint64 VideoPlayer::getCurrentPos()
  255. {
  256. if(!m_isOpenFile)
  257. {
  258. SPDLOG_ERROR("未打开视频文件!");
  259. return -1;
  260. }
  261. auto pos = m_decodeVedio->getCurrentPos();
  262. if(pos < 0)
  263. {
  264. return 0;
  265. }
  266. return pos;
  267. }
  268. /* 设置当前播放位置,单位ms */
  269. void VideoPlayer::setCurrentPos(qint64 pos)
  270. {
  271. if(!m_isOpenFile)
  272. {
  273. SPDLOG_ERROR("未打开视频文件!");
  274. return;
  275. }
  276. if(!m_isLocalFile)
  277. {
  278. SPDLOG_ERROR("不是本地视频文件,无法设置播放位置!");
  279. return;
  280. }
  281. if(pos < 0)
  282. {
  283. pos = 0;
  284. }
  285. /* 先停止播放 */
  286. bool temp = m_playStatus;
  287. if(m_playStatus)
  288. {
  289. m_timerRefreshUI.stop();
  290. m_playStatus = false;
  291. }
  292. m_decodeVedio->setCurrentPos(pos);
  293. /* 继续播放 */
  294. if(temp)
  295. {
  296. // SPDLOG_INFO("..........开启定时器..........");
  297. m_timerRefreshUI.start(m_interval);
  298. m_playStatus = true;
  299. }else
  300. {
  301. /* 刷新2张照片 */
  302. m_semRefresh->release(m_previewImage);
  303. emit signal_refreshImage();
  304. }
  305. }
  306. /* 设置播放视频大小 */
  307. void VideoPlayer::setPlayWidgetSize(int width,int height)
  308. {
  309. /* 对宽和高就行缩放,保持比例,同时将其居中放置
  310. * 先计算出比例,和16/9相对比
  311. * 大于16/9,以高为最大极限,计算出宽度和x坐标
  312. * 小于16/9,以宽为最大极限,计算出高度和y坐标 */
  313. double srcRatio = m_srcWidth*1.0 / m_srcHeight;
  314. double ratio = width*1.0 / height;
  315. long w1 = 0, h1 = 0;
  316. int srcX = m_playWidget->pos().rx(), srcY = m_playWidget->pos().ry();
  317. int x1 = srcX, y1 = srcY;
  318. if(ratio > srcRatio)
  319. {
  320. w1 = height * srcRatio;
  321. x1 = (width - w1) / 2;
  322. h1 = height;
  323. y1 = srcY;
  324. }
  325. else if(ratio < srcRatio)
  326. {
  327. h1 = width / srcRatio;
  328. y1 = (height - h1) / 2;
  329. w1 = width;
  330. x1 = srcX;
  331. }else {
  332. w1 = width;
  333. h1 = height;
  334. x1 = srcX;
  335. y1 = srcY;
  336. }
  337. m_playWidget->move(x1, y1);
  338. m_nowWidth = w1;
  339. m_nowHeight = h1;
  340. m_playWidget->resize(w1, h1);
  341. // SPDLOG_DEBUG("设置窗口位置:{}x{}, 大小:{}x{}, 传入大小:{}x{}", x1, y1, w1, h1, width, height);
  342. SPDLOG_DEBUG("现在位置和大小:{}x{}, {}x{}", m_playWidget->pos().rx(), m_playWidget->pos().ry(), m_playWidget->width(), m_playWidget->height());
  343. }
  344. /**
  345. * @brief 设置播放窗口,这用于独占一个传入的widget,这里会自动添加一个布局,外面窗口变化,这里也跟随着变化
  346. *
  347. * @param widget
  348. * @param flag
  349. * @arg true:独占widget,并设置一个layout,会随着传入的widget大小变化
  350. * @arg false:不独占
  351. */
  352. void VideoPlayer::setWidgetParent(QWidget* widget, bool isFull)
  353. {
  354. if(widget == nullptr)
  355. {
  356. SPDLOG_WARN("传入的widget为空");
  357. return;
  358. }
  359. if(isFull)
  360. {
  361. /* 设置布局 */
  362. QVBoxLayout* layout = new QVBoxLayout(widget);
  363. layout->addWidget(m_playWidget);
  364. layout->setMargin(0);
  365. layout->setSpacing(0);
  366. widget->setLayout(layout);
  367. }else
  368. {
  369. this->setParent(widget);
  370. /* 设置窗口大小 */
  371. setPlayWidgetSize(widget->width(), widget->height());
  372. }
  373. }
  374. /**
  375. * @brief 设置预览图片数目,在暂停时跳转,可能会有花屏或者黑帧,可以设置跳转图片个数跳过黑帧
  376. * 默认是2帧
  377. *
  378. * @param num
  379. */
  380. void VideoPlayer::setPreviewImage(int num)
  381. {
  382. m_previewImage = num;
  383. }
  384. /**
  385. * @brief 设置帧率,有些视频无法获取到帧率,就会使用默认的25fps,如果需要,可以通过这个函数设置
  386. * 注意:这个函数需要在打开视频文件之后设置,打开一次视频文件会覆盖这个参数
  387. *
  388. * @param fps
  389. */
  390. void VideoPlayer::setFPS(int fps)
  391. {
  392. m_fps = fps;
  393. // if(m_decodeVedio != nullptr)
  394. // {
  395. // m_decodeVedio->setFPS(fps);
  396. // }
  397. if(m_timerRefreshUI.isActive())
  398. {
  399. m_timerRefreshUI.stop();
  400. m_interval = qRound64(1000.0 / m_fps);
  401. m_timerRefreshUI.start(m_interval);
  402. }
  403. }
  404. /* 设置播放速度 */
  405. void VideoPlayer::setPlaySpeed(float speed)
  406. {
  407. int fps = m_fps * speed;
  408. if(fps <= 0)
  409. {
  410. fps = m_fps; // 最小帧率为1
  411. }
  412. if(m_timerRefreshUI.isActive())
  413. {
  414. m_timerRefreshUI.stop();
  415. m_interval = qRound64(1000.0 / fps);
  416. m_timerRefreshUI.start(m_interval);
  417. }
  418. }
  419. /* 设置播放回调函数 */
  420. // void VideoPlayer::setPlayCallBack(std::function<Play_CallBack> playCallBack,void* context)
  421. // {
  422. // m_funcPlayCB = playCallBack;
  423. // m_context = context;
  424. // }
  425. // void VideoPlayer::paintEvent(QPaintEvent *event)
  426. // {
  427. // if(m_image != nullptr)
  428. // {
  429. // // SPDLOG_TRACE("开始绘制画面...");
  430. // /* 对图像进行缩放 */
  431. // if(m_srcWidth != m_nowWidth || m_srcHeight != m_nowHeight)
  432. // {
  433. // *m_image = m_image->scaled(m_nowWidth, m_nowHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation);
  434. // }
  435. // QPainter painter(this);
  436. // painter.drawImage(0, 0, *m_image);
  437. // }
  438. // }
  439. // void VideoPlayer::resizeEvent(QResizeEvent *event)
  440. // {
  441. // SPDLOG_TRACE("窗口大小改变...");
  442. // m_nowWidth = event->size().width();
  443. // m_nowHeight = event->size().height();
  444. // QWidget::resizeEvent(event);
  445. // }
  446. /* 刷新一张图片,直到有图片为止 */
  447. void VideoPlayer::refreshOneUIUntilHave()
  448. {
  449. if(m_decodeVedio != nullptr)
  450. {
  451. // SPDLOG_DEBUG("取出一帧图片...");
  452. // /* 删除上一帧图片 */
  453. // if(m_image != nullptr)
  454. // {
  455. // delete m_image;
  456. // m_image = nullptr;
  457. // }
  458. // /* 如果没有图片,这个函数会阻塞 */
  459. // m_image = m_decodeVedio->getOneImageUntilHave();
  460. // if(m_image)
  461. // {
  462. // if(m_image->isNull())
  463. // {
  464. // SPDLOG_WARN("取出的图片为空...");
  465. // return;
  466. // }
  467. // // SPDLOG_DEBUG("绘制画面...");
  468. // refreshRGBAImage(m_playWidget, *m_image);
  469. // }
  470. refreshFrame(m_decodeVedio->getOneImageUntilHave());
  471. }
  472. }
  473. /* 刷新一帧 */
  474. void VideoPlayer::refreshFrame(FrameBase* frame)
  475. {
  476. if(frame == nullptr)
  477. {
  478. SPDLOG_WARN("刷新一帧,传入的帧为空");
  479. return;
  480. }
  481. /* 删除上一帧图片 */
  482. deleteOneFrame(m_image);
  483. m_image = frame;
  484. frame = nullptr; // 防止外部继续使用这个帧
  485. if(m_image->frameFormat() == EFrameFormat::Frame_RGBA8888)
  486. {
  487. QImage* rgbaImage = static_cast<Frame_RGBA8888*>(m_image)->frameData;
  488. if(rgbaImage != nullptr && !rgbaImage->isNull())
  489. {
  490. refreshRGBAImage(m_playWidget, *rgbaImage);
  491. }else {
  492. SPDLOG_WARN("刷新一帧,RGBA图片为空");
  493. }
  494. }
  495. else if(m_image->frameFormat() == EFrameFormat::Frame_YUV420P)
  496. {
  497. /* YUV420P格式的图片 */
  498. Frame_YUV420P* yuvImage = static_cast<Frame_YUV420P*>(m_image);
  499. if(yuvImage != nullptr && yuvImage->frameData != nullptr)
  500. {
  501. refreshYUV420Image(m_playWidget, *yuvImage->frameData);
  502. }else {
  503. SPDLOG_WARN("刷新一帧,YUV420P图片为空");
  504. }
  505. }
  506. }
  507. /* 双击事件函数 */
  508. // void VideoPlayer::mouseDoubleClickEvent(QMouseEvent *event)
  509. // {
  510. // if(event->button() == Qt::LeftButton)
  511. // {
  512. // // SPDLOG_DEBUG("双击事件...");
  513. // // if(m_funcPlayCB != nullptr)
  514. // // {
  515. // // m_funcPlayCB(this, 5, nullptr, 0, m_context);
  516. // // }else {
  517. // // SPDLOG_INFO("没有设置回调函数");
  518. // // }
  519. // }
  520. // }
  521. /* 取出画面,刷新UI */
  522. void VideoPlayer::do_refreshUI()
  523. {
  524. if(m_decodeVedio != nullptr)
  525. {
  526. // SPDLOG_DEBUG("取出一帧图片...");
  527. refreshFrame(m_decodeVedio->getOneImage());
  528. }
  529. }
  530. /* 通过信号刷新第一张图片 */
  531. void VideoPlayer::do_refreshSamImage()
  532. {
  533. if(!m_isOpenFile)
  534. {
  535. return;
  536. }
  537. while(m_semRefresh->tryAcquire(1))
  538. {
  539. /* 取出第一张 */
  540. if(m_decodeVedio != nullptr)
  541. {
  542. // SPDLOG_DEBUG("取出一帧图片...");
  543. // /* 删除上一帧图片 */
  544. // if(m_image != nullptr)
  545. // {
  546. // delete m_image;
  547. // m_image = nullptr;
  548. // }
  549. // /* 等待图片,最多等待50ms */
  550. // m_image = m_decodeVedio->getOneImageUntilHave(100);
  551. // if(m_image)
  552. // {
  553. // if(m_image->isNull())
  554. // {
  555. // SPDLOG_WARN("取出的图片为空...");
  556. // return;
  557. // }
  558. // SPDLOG_DEBUG("绘制预览画面。");
  559. // refreshRGBAImage(m_playWidget, *m_image);
  560. // }
  561. refreshFrame(m_decodeVedio->getOneImageUntilHave(100));
  562. }
  563. }
  564. }
  565. /* 播放完成 */
  566. void VideoPlayer::do_playCompleted()
  567. {
  568. SPDLOG_INFO("视频播放完成。");
  569. m_timerRefreshUI.stop();
  570. /* 手动刷新剩余的环形队列中的图片 */
  571. while(true)
  572. {
  573. if(m_decodeVedio != nullptr)
  574. {
  575. // QImage* image = nullptr;
  576. // image = m_decodeVedio->getOneImage();
  577. // if(image == nullptr)
  578. // {
  579. // break;
  580. // }
  581. // /* 删除上一帧图片 */
  582. // if(m_image != nullptr)
  583. // {
  584. // delete m_image;
  585. // m_image = nullptr;
  586. // }
  587. // m_image = image;
  588. // if(m_image->isNull())
  589. // {
  590. // SPDLOG_WARN("取出的图片为空...");
  591. // return;
  592. // }
  593. // // SPDLOG_DEBUG("绘制画面...");
  594. // refreshRGBAImage(m_playWidget, *m_image);
  595. refreshFrame(m_decodeVedio->getOneImage());
  596. }
  597. }
  598. m_playStatus = false;
  599. // if(m_funcPlayCB != nullptr)
  600. // {
  601. // /* 播放完成的回调函数 */
  602. // m_funcPlayCB(this, 2, nullptr, 0, m_context);
  603. // }
  604. emit signal_playCompleted(); // 播放完成信号
  605. }
  606. /* 判断是否是本地文件 */
  607. bool VideoPlayer::isLocalFile(const QString& fileName)
  608. {
  609. if(fileName.isEmpty())
  610. {
  611. return false;
  612. }
  613. if(fileName.startsWith("http://") || fileName.startsWith("rtsp://")
  614. || fileName.startsWith("rtmp://") || fileName.startsWith("https://"))
  615. {
  616. return false;
  617. }
  618. return true;
  619. }