VideoPlayer.cpp 13 KB

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