playerdemowindow.cpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. #include "playerdemowindow.h"
  2. #include <QAudioDeviceInfo>
  3. #include <QAudioFormat>
  4. #include <QDebug>
  5. #include <QHBoxLayout>
  6. #include <QMessageBox>
  7. #include <QVBoxLayout>
  8. #include "AudioPlayer.h"
  9. #include "VideoPlayer.h"
  10. // 静态回调函数
  11. static int audioWriteFunc(const char* data, int size, void* ctx) {
  12. QIODevice* device = static_cast<QIODevice*>(ctx);
  13. return device->write(data, size);
  14. }
  15. static void videoRenderFunc(AVFrame* frame, void* ctx) {
  16. OpenGLVideoWidget* widget = static_cast<OpenGLVideoWidget*>(ctx);
  17. widget->Render(frame);
  18. }
  19. PlayerDemoWindow::PlayerDemoWindow(QWidget* parent)
  20. : QWidget(parent)
  21. , m_audioPlayer(new AudioPlayer())
  22. , m_videoPlayer(new VideoPlayer())
  23. {
  24. QVBoxLayout* layout = new QVBoxLayout(this);
  25. m_videoWidget = new OpenGLVideoWidget(this);
  26. layout->addWidget(m_videoWidget);
  27. // 进度条和时间
  28. QHBoxLayout* progressLayout = new QHBoxLayout();
  29. m_progressSlider = new QSlider(Qt::Horizontal, this);
  30. m_progressSlider->setRange(0, 1000);
  31. m_timeLabel = new QLabel("00:00:00 / 00:00:00", this);
  32. progressLayout->addWidget(m_progressSlider, 1);
  33. progressLayout->addWidget(m_timeLabel);
  34. layout->addLayout(progressLayout);
  35. // 倍速选择和播放按钮
  36. QHBoxLayout* controlLayout = new QHBoxLayout();
  37. m_playBtn = new QPushButton("播放", this);
  38. m_speedCombo = new QComboBox(this);
  39. m_speedCombo->addItem("0.5x", 0.5);
  40. m_speedCombo->addItem("1.0x", 1.0);
  41. m_speedCombo->addItem("1.5x", 1.5);
  42. m_speedCombo->addItem("2.0x", 2.0);
  43. controlLayout->addWidget(m_playBtn);
  44. controlLayout->addWidget(new QLabel("倍速:", this));
  45. controlLayout->addWidget(m_speedCombo);
  46. // 添加保持比例复选框
  47. m_keepAspectCheck = new QCheckBox("保持比例", this);
  48. m_keepAspectCheck->setChecked(true);
  49. controlLayout->addWidget(m_keepAspectCheck);
  50. layout->addLayout(controlLayout);
  51. m_speedCombo->setCurrentText("1.0x");
  52. connect(m_progressSlider, &QSlider::sliderPressed, [this]() { m_sliderPressed = true; });
  53. connect(m_progressSlider,
  54. &QSlider::sliderReleased,
  55. this,
  56. &PlayerDemoWindow::onProgressSliderReleased);
  57. connect(m_progressSlider, &QSlider::sliderMoved, this, &PlayerDemoWindow::onProgressSliderMoved);
  58. connect(m_speedCombo,
  59. QOverload<int>::of(&QComboBox::currentIndexChanged),
  60. this,
  61. &PlayerDemoWindow::onSpeedChanged);
  62. connect(m_playBtn, &QPushButton::clicked, this, &PlayerDemoWindow::onPlayClicked);
  63. connect(m_keepAspectCheck, &QCheckBox::toggled, this, [this](bool checked) {
  64. m_videoWidget->setKeepAspectRatio(checked);
  65. });
  66. }
  67. PlayerDemoWindow::~PlayerDemoWindow()
  68. {
  69. if (m_puller) {
  70. m_puller->stop();
  71. delete m_puller;
  72. }
  73. if (m_swrCtx) {
  74. swr_free(&m_swrCtx);
  75. }
  76. if (m_swrBuffer) {
  77. av_free(m_swrBuffer);
  78. }
  79. if (m_audioPlayer) {
  80. delete m_audioPlayer;
  81. }
  82. if (m_videoPlayer) {
  83. delete m_videoPlayer;
  84. }
  85. }
  86. void PlayerDemoWindow::startPlay(const QString& url)
  87. {
  88. stopPlay();
  89. m_puller = new FFmpegVideoPuller();
  90. m_audioPlayer = new AudioPlayer();
  91. m_videoPlayer = new VideoPlayer();
  92. if (!m_puller->open(url, 300, 300)) {
  93. QMessageBox::critical(this, "错误", "无法打开流");
  94. return;
  95. }
  96. m_puller->setSpeed(m_speedCombo->currentData().toFloat());
  97. // 启动主同步播放线程
  98. m_syncPlayRunning = true;
  99. m_syncPlayThread = QThread::create([this]() { this->syncPlayLoop(); });
  100. m_syncPlayThread->start();
  101. m_firstPts = m_puller->getFirstPts();
  102. m_lastPts = m_puller->getLastPts();
  103. m_duration = m_lastPts - m_firstPts;
  104. m_progressSlider->setValue(0);
  105. updateProgress();
  106. m_playing = true;
  107. }
  108. void PlayerDemoWindow::stopPlay()
  109. {
  110. m_syncPlayRunning = false;
  111. if (m_syncPlayThread) {
  112. m_syncPlayThread->quit();
  113. m_syncPlayThread->wait();
  114. delete m_syncPlayThread;
  115. m_syncPlayThread = nullptr;
  116. }
  117. if (m_puller) {
  118. m_puller->stop();
  119. delete m_puller;
  120. m_puller = nullptr;
  121. }
  122. if (m_audioPlayer) {
  123. delete m_audioPlayer;
  124. m_audioPlayer = nullptr;
  125. }
  126. if (m_videoPlayer) {
  127. delete m_videoPlayer;
  128. m_videoPlayer = nullptr;
  129. }
  130. m_playing = false;
  131. }
  132. void PlayerDemoWindow::setSpeed(float speed)
  133. {
  134. if (m_puller) {
  135. m_puller->setSpeed(speed);
  136. }
  137. }
  138. void PlayerDemoWindow::onPlayClicked()
  139. {
  140. if (!m_puller)
  141. return;
  142. float curSpeed = m_puller->getSpeed();
  143. if (curSpeed > 0.01f) {
  144. m_puller->setSpeed(0.0f);
  145. m_playBtn->setText("播放");
  146. } else {
  147. float speed = m_speedCombo->currentData().toFloat();
  148. m_puller->setSpeed(speed);
  149. m_playBtn->setText("暂停");
  150. }
  151. }
  152. void PlayerDemoWindow::onProgressSliderMoved(int value)
  153. {
  154. m_sliderPressed = true;
  155. if (m_duration <= 0) {
  156. m_timeLabel->setText("00:00:00 / 00:00:00");
  157. return;
  158. }
  159. double seekPts = m_firstPts + (m_duration * value / 1000.0);
  160. QString cur = formatTime(seekPts - m_firstPts);
  161. QString total = formatTime(m_duration);
  162. m_timeLabel->setText(cur + " / " + total);
  163. }
  164. void PlayerDemoWindow::onProgressSliderReleased()
  165. {
  166. if (!m_puller || m_duration <= 0)
  167. return;
  168. int value = m_progressSlider->value();
  169. double seekPts = m_firstPts + (m_duration * value / 1000.0);
  170. if (seekPts < m_firstPts)
  171. seekPts = m_firstPts;
  172. if (seekPts > m_lastPts)
  173. seekPts = m_lastPts;
  174. m_puller->seekToPts(seekPts);
  175. m_sliderPressed = false;
  176. }
  177. void PlayerDemoWindow::onSpeedChanged(int index)
  178. {
  179. if (!m_puller)
  180. return;
  181. float speed = m_speedCombo->itemData(index).toFloat();
  182. m_puller->setSpeed(speed);
  183. }
  184. void PlayerDemoWindow::updateProgress()
  185. {
  186. if (!m_puller || m_sliderPressed)
  187. return;
  188. double duration = m_puller->getTotalDuration();
  189. if (duration > 0) {
  190. m_duration = duration;
  191. m_firstPts = 0;
  192. m_lastPts = duration;
  193. } else {
  194. m_firstPts = m_puller->getFirstPts();
  195. m_lastPts = m_puller->getLastPts();
  196. m_duration = m_lastPts - m_firstPts;
  197. }
  198. // 检查有效性
  199. if (m_firstPts < 0 || m_lastPts < 0 || m_duration <= 0) {
  200. m_progressSlider->setValue(0);
  201. m_timeLabel->setText("00:00:00 / 00:00:00");
  202. return;
  203. }
  204. double curPts = m_puller->getCurrentPts();
  205. if (curPts < m_firstPts)
  206. curPts = m_firstPts;
  207. if (curPts > m_lastPts)
  208. curPts = m_lastPts;
  209. int value = int((curPts - m_firstPts) / m_duration * 1000);
  210. m_progressSlider->setValue(value);
  211. QString cur = formatTime(curPts - m_firstPts);
  212. QString total = formatTime(m_duration);
  213. m_timeLabel->setText(cur + " / " + total);
  214. }
  215. QString PlayerDemoWindow::formatTime(double seconds) const
  216. {
  217. if (seconds < 0)
  218. seconds = 0;
  219. int sec = int(seconds + 0.5);
  220. int h = sec / 3600;
  221. int m = (sec % 3600) / 60;
  222. int s = sec % 60;
  223. return QString("%1:%2:%3")
  224. .arg(h, 2, 10, QChar('0'))
  225. .arg(m, 2, 10, QChar('0'))
  226. .arg(s, 2, 10, QChar('0'));
  227. }
  228. void PlayerDemoWindow::syncPlayLoop()
  229. {
  230. using namespace std::chrono;
  231. double basePts = 0.0;
  232. bool baseSet = false;
  233. auto startTime = high_resolution_clock::now();
  234. while (m_syncPlayRunning) {
  235. auto now = high_resolution_clock::now();
  236. double elapsed = duration_cast<milliseconds>(now - startTime).count();
  237. if (!baseSet) {
  238. AVFrame* af = m_puller->getCurrentAudioFrame();
  239. if (af) {
  240. basePts = af->best_effort_timestamp * av_q2d(m_puller->m_fmtCtx->streams[m_puller->m_audioStreamIdx]->time_base);
  241. baseSet = true;
  242. } else {
  243. std::this_thread::sleep_for(milliseconds(1));
  244. continue;
  245. }
  246. }
  247. double curPts = basePts + elapsed / 1000.0 * m_puller->getSpeed();
  248. // 音频
  249. AVFrame* audioFrame = m_puller->popNearestAudioFrame(curPts);
  250. if (audioFrame && m_audioPlayer) {
  251. m_audioPlayer->play(audioFrame, m_puller->getSpeed());
  252. }
  253. // 视频
  254. AVFrame* videoFrame = m_puller->popNearestVideoFrame(curPts);
  255. if (videoFrame && m_videoPlayer) {
  256. m_videoPlayer->render(videoFrame);
  257. }
  258. std::this_thread::sleep_for(milliseconds(1));
  259. }
  260. }