VideoPlayer.cpp 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. #include "VideoPlayer.h"
  2. #include "DecodeVedio.h"
  3. #include <QPainter>
  4. #include <QResizeEvent>
  5. #include <QEventLoop>
  6. #include "spdlog/spdlog.h"
  7. VideoPlayer::VideoPlayer(QWidget *parent) : QWidget(parent)
  8. {
  9. /* 初始化解码线程 */
  10. m_threadDecode = new QThread(this);
  11. m_decodeVedio = new DecodeVedio(m_threadDecode);
  12. m_semRefresh = new QSemaphore(0);
  13. m_timerRefreshUI.setSingleShot(false);
  14. /* 设置精度毫秒级 */
  15. m_timerRefreshUI.setTimerType(Qt::PreciseTimer);
  16. connect(&m_timerRefreshUI, &QTimer::timeout, this, &VideoPlayer::do_refreshUI);
  17. connect(m_decodeVedio, &DecodeVedio::signal_oneImage, this, &VideoPlayer::do_refreshOneUI);
  18. connect(m_decodeVedio, &DecodeVedio::signal_playCompleted, this, &VideoPlayer::do_playCompleted);
  19. SPDLOG_TRACE("UI线程ID:{}", QThread::currentThreadId());
  20. }
  21. VideoPlayer::~VideoPlayer()
  22. {
  23. if(m_timerRefreshUI.isActive())
  24. {
  25. m_timerRefreshUI.stop();
  26. }
  27. delete m_decodeVedio;
  28. if(m_image)
  29. {
  30. delete m_image;
  31. }
  32. }
  33. /**
  34. * @brief 设置播放视频,启动定时器,定时器间隔决定播放的速度
  35. * 视频的宽和高使用QImage进行缩放
  36. * 视频大小在直接设置这个类的resize即可,有最小大小限制
  37. *
  38. * @param fileName
  39. */
  40. void VideoPlayer::setPlayVedio(const QString& fileName)
  41. {
  42. if(isSetVedioFile)
  43. {
  44. isSetVedioFile = false;
  45. }
  46. if(m_decodeVedio->isInitFFmpeg())
  47. {
  48. m_decodeVedio->unInitFFmpeg();
  49. }
  50. m_fileName = fileName;
  51. isSetVedioFile = true;
  52. m_decodeVedio->initFFmpeg(m_fileName);
  53. // m_decodeVedio->setVideoSize(this->width(), this->height());
  54. /* 获取原始视频信息 */
  55. m_srcWidth = m_decodeVedio->getSrcVideoWidth();
  56. m_srcHeight = m_decodeVedio->getSrcVideoHeight();
  57. m_frameCount = m_decodeVedio->getFrameCount();
  58. SPDLOG_DEBUG("视频宽:{} 高:{} 帧数:{}", m_srcWidth, m_srcHeight, m_frameCount);
  59. /* 设置视频宽和高的最小大小 */
  60. this->setMinimumSize(160,90);
  61. /* 开启定时器刷新 */
  62. if(m_frameCount < 0)
  63. {
  64. /* HEVC帧率,获取不到,就按照24帧来刷新 */
  65. if(m_frameCount == -2)
  66. {
  67. m_frameCount = 24;
  68. }
  69. else {
  70. m_frameCount = 25;
  71. }
  72. }
  73. else if(m_frameCount == 0)
  74. {
  75. m_frameCount = 25;
  76. }
  77. SPDLOG_INFO("帧率:{}", m_frameCount);
  78. /* 开启解码,手动刷新第一帧 */
  79. m_decodeVedio->startDecodeVedio();
  80. m_semRefresh->release(2);
  81. // m_timerRefreshUI.setSingleShot(true);
  82. // m_timerRefreshUI.start(500);
  83. }
  84. /* 播放视频 */
  85. bool VideoPlayer::play()
  86. {
  87. if(!isSetVedioFile)
  88. {
  89. SPDLOG_ERROR("文件名为空");
  90. return false;
  91. }
  92. if(m_playStatus)
  93. {
  94. return false;
  95. }
  96. /* 设置刷新时间 */
  97. m_timerRefreshUI.setSingleShot(false);
  98. m_interval = 1000/m_frameCount;
  99. if(1000 % m_frameCount > 5)
  100. {
  101. m_frameCount += 1;
  102. }
  103. SPDLOG_TRACE("刷新UI的定时间隔:{}",m_interval);
  104. m_timerRefreshUI.start(m_interval);
  105. m_playStatus = true;
  106. return true;
  107. }
  108. /* 暂停播放 */
  109. void VideoPlayer::pause()
  110. {
  111. if(!m_playStatus)
  112. {
  113. return;
  114. }
  115. m_timerRefreshUI.stop();
  116. m_playStatus = false;
  117. }
  118. /* 停止播放,停止后停止解码,将时间等复位到开始时间 */
  119. void VideoPlayer::stop()
  120. {
  121. SPDLOG_DEBUG("...停止播放...");
  122. // m_fileName = QString();
  123. if(m_timerRefreshUI.isActive())
  124. {
  125. m_timerRefreshUI.stop();
  126. }
  127. // SPDLOG_DEBUG("...停止解码...");
  128. /* 重新设置播放视频 */
  129. setPlayVedio(m_fileName);
  130. /* 绘制黑帧 */
  131. // SPDLOG_DEBUG("绘制黑帧");
  132. // m_image = new QImage(m_nowWidth, m_nowHeight, QImage::Format_RGB32);
  133. // m_image->fill(Qt::black);
  134. // update();
  135. m_playStatus = false;
  136. // isSetVedioFile = false;
  137. }
  138. /* 获取视频时长 */
  139. qint64 VideoPlayer::getDuration()
  140. {
  141. return m_decodeVedio->getDuration();
  142. }
  143. /* 获取当前播放位置 */
  144. qint64 VideoPlayer::getCurrentPos()
  145. {
  146. return m_decodeVedio->getCurrentPos();
  147. }
  148. /* 设置当前播放位置 */
  149. void VideoPlayer::setCurrentPos(quint64 pos)
  150. {
  151. /* 先停止播放 */
  152. bool temp = m_playStatus;
  153. if(m_playStatus)
  154. {
  155. m_timerRefreshUI.stop();
  156. m_playStatus = false;
  157. }
  158. m_decodeVedio->setCurrentPos(pos);
  159. /* 继续播放 */
  160. if(temp)
  161. {
  162. // SPDLOG_INFO("..........开启定时器..........");
  163. m_timerRefreshUI.start(m_interval);
  164. m_playStatus = true;
  165. }else
  166. {
  167. /* 刷新5张照片,防止第一张是跳转前的时间段 */
  168. m_semRefresh->release(5);
  169. }
  170. }
  171. /* 设置播放视频大小 */
  172. void VideoPlayer::setPlayVedioSize(int width,int height)
  173. {
  174. /* 对宽和高就行缩放,保持比例,同时将其居中放置
  175. * 先计算出比例,和16/9相对比
  176. * 大于16/9,以高为最大极限,计算出宽度和x坐标
  177. * 小于16/9,以宽为最大极限,计算出高度和y坐标 */
  178. double srcRatio = m_srcWidth*1.0 / m_srcHeight;
  179. double ratio = width*1.0 / height;
  180. long w1 = 0, h1 = 0;
  181. int srcX = this->pos().rx(), srcY = this->pos().ry();
  182. int x1 = srcX, y1 = srcY;
  183. if(ratio > srcRatio)
  184. {
  185. w1 = height * srcRatio;
  186. x1 = (width - w1) / 2;
  187. h1 = height;
  188. y1 = srcY;
  189. }
  190. else if(ratio < srcRatio)
  191. {
  192. h1 = width / srcRatio;
  193. y1 = (height - h1) / 2;
  194. w1 = width;
  195. x1 = srcX;
  196. }else {
  197. w1 = width;
  198. h1 = height;
  199. x1 = srcX;
  200. y1 = srcY;
  201. }
  202. this->move(x1, y1);
  203. m_nowWidth = w1;
  204. m_nowHeight = h1;
  205. this->resize(w1, h1);
  206. // SPDLOG_DEBUG("设置窗口位置:{}x{}, 大小:{}x{}, 传入大小:{}x{}", x1, y1, w1, h1, width, height);
  207. SPDLOG_DEBUG("现在位置和大小:{}x{}, {}x{}", this->pos().rx(), this->pos().ry(), this->width(), this->height());
  208. }
  209. /* 设置播放回调函数 */
  210. void VideoPlayer::setPlayCallBack(std::function<Play_CallBack> playCallBack,void* context)
  211. {
  212. m_funcPlayCB = playCallBack;
  213. m_context = context;
  214. }
  215. void VideoPlayer::paintEvent(QPaintEvent *event)
  216. {
  217. if(m_image)
  218. {
  219. // SPDLOG_TRACE("开始绘制画面...");
  220. /* 对图像进行缩放 */
  221. QImage image;
  222. if(m_srcWidth != m_nowWidth || m_srcHeight != m_nowHeight)
  223. {
  224. image = m_image->scaled(m_nowWidth, m_nowHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation);
  225. }
  226. QPainter painter(this);
  227. painter.drawImage(0, 0, image);
  228. }
  229. }
  230. void VideoPlayer::resizeEvent(QResizeEvent *event)
  231. {
  232. SPDLOG_TRACE("窗口大小改变...");
  233. m_nowWidth = event->size().width();
  234. m_nowHeight = event->size().height();
  235. /* 传递给解码器 */
  236. // m_decodeVedio->setVideoSize(m_nowWidth, m_nowHeight);
  237. QWidget::resizeEvent(event);
  238. }
  239. /* 刷新一张图片,直到有图片为止 */
  240. void VideoPlayer::refreshOneUIUntilHave()
  241. {
  242. if(m_decodeVedio != nullptr)
  243. {
  244. // SPDLOG_DEBUG("取出一帧图片...");
  245. /* 删除上一帧图片 */
  246. if(m_image != nullptr)
  247. {
  248. delete m_image;
  249. m_image = nullptr;
  250. }
  251. /* 如果没有图片,这个函数会阻塞 */
  252. m_image = m_decodeVedio->getOneImageUntilHave();
  253. if(m_image)
  254. {
  255. // if(m_srcWidth != m_nowWidth || m_srcHeight != m_nowHeight)
  256. // {
  257. // *m_image = m_image->scaled(m_nowWidth, m_nowHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation);
  258. // }
  259. // SPDLOG_DEBUG("绘制画面...");
  260. update();
  261. }
  262. m_decodeVedio->wakeUpCondQueueNoEmpty();
  263. }
  264. }
  265. /* 双击事件函数 */
  266. void VideoPlayer::mouseDoubleClickEvent(QMouseEvent *event)
  267. {
  268. if(event->button() == Qt::LeftButton)
  269. {
  270. // SPDLOG_DEBUG("双击事件...");
  271. if(m_funcPlayCB != nullptr)
  272. {
  273. m_funcPlayCB(this, 5, nullptr, 0, m_context);
  274. }else {
  275. SPDLOG_INFO("没有设置回调函数");
  276. }
  277. }
  278. }
  279. /* 取出画面,刷新UI */
  280. void VideoPlayer::do_refreshUI()
  281. {
  282. if(m_decodeVedio != nullptr)
  283. {
  284. // SPDLOG_DEBUG("取出一帧图片...");
  285. /* 删除上一帧图片 */
  286. if(m_image != nullptr)
  287. {
  288. delete m_image;
  289. m_image = nullptr;
  290. }
  291. m_image = m_decodeVedio->getOneImage();
  292. if(m_image)
  293. {
  294. if(m_srcWidth != m_nowWidth || m_srcHeight != m_nowHeight)
  295. {
  296. *m_image = m_image->scaled(m_nowWidth, m_nowHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation);
  297. }
  298. // SPDLOG_DEBUG("绘制画面...");
  299. update();
  300. }
  301. m_decodeVedio->wakeUpCondQueueNoEmpty();
  302. }
  303. }
  304. /* 通过信号刷新第一张图片 */
  305. void VideoPlayer::do_refreshOneUI()
  306. {
  307. if(!m_semRefresh->tryAcquire(1))
  308. {
  309. return;
  310. }
  311. /* 取出第一张 */
  312. if(m_decodeVedio != nullptr)
  313. {
  314. // SPDLOG_DEBUG("取出一帧图片...");
  315. /* 删除上一帧图片 */
  316. if(m_image != nullptr)
  317. {
  318. delete m_image;
  319. m_image = nullptr;
  320. }
  321. m_image = m_decodeVedio->getOneImage();
  322. if(m_image)
  323. {
  324. if(m_srcWidth != m_nowWidth || m_srcHeight != m_nowHeight)
  325. {
  326. *m_image = m_image->scaled(m_nowWidth, m_nowHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation);
  327. }
  328. SPDLOG_DEBUG("绘制预览画面...");
  329. update();
  330. }
  331. m_decodeVedio->wakeUpCondQueueNoEmpty();
  332. }
  333. }
  334. /* 播放完成 */
  335. void VideoPlayer::do_playCompleted()
  336. {
  337. SPDLOG_INFO("Video 播放完成...");
  338. m_timerRefreshUI.stop();
  339. m_playStatus = false;
  340. if(m_funcPlayCB != nullptr)
  341. {
  342. /* 播放完成的回调函数 */
  343. m_funcPlayCB(this, 2, nullptr, 0, m_context);
  344. }
  345. }