playerdemowindow.cpp 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  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. , speedFlag(false)
  24. {
  25. QVBoxLayout* layout = new QVBoxLayout(this);
  26. m_videoWidget = new OpenGLVideoWidget(this);
  27. layout->addWidget(m_videoWidget);
  28. // 进度条和时间
  29. QHBoxLayout* progressLayout = new QHBoxLayout();
  30. m_progressSlider = new QSlider(Qt::Horizontal, this);
  31. m_progressSlider->setRange(0, 1000);
  32. m_timeLabel = new QLabel("00:00:00 / 00:00:00", this);
  33. progressLayout->addWidget(m_progressSlider, 1);
  34. progressLayout->addWidget(m_timeLabel);
  35. layout->addLayout(progressLayout);
  36. // 倍速选择和播放按钮
  37. QHBoxLayout* controlLayout = new QHBoxLayout();
  38. m_playBtn = new QPushButton("播放", this);
  39. m_speedCombo = new QComboBox(this);
  40. m_speedCombo->addItem("0.5x", 0.5);
  41. m_speedCombo->addItem("1.0x", 1.0);
  42. m_speedCombo->addItem("1.5x", 1.5);
  43. m_speedCombo->addItem("2.0x", 2.0);
  44. controlLayout->addWidget(m_playBtn);
  45. controlLayout->addWidget(new QLabel("倍速:", this));
  46. controlLayout->addWidget(m_speedCombo);
  47. // 添加保持比例复选框
  48. m_keepAspectCheck = new QCheckBox("保持比例", this);
  49. m_keepAspectCheck->setChecked(true);
  50. controlLayout->addWidget(m_keepAspectCheck);
  51. layout->addLayout(controlLayout);
  52. m_speedCombo->setCurrentText("1.0x");
  53. connect(m_progressSlider, &QSlider::sliderPressed, [this]() { m_sliderPressed = true; });
  54. connect(m_progressSlider,
  55. &QSlider::sliderReleased,
  56. this,
  57. &PlayerDemoWindow::onProgressSliderReleased);
  58. connect(m_progressSlider, &QSlider::sliderMoved, this, &PlayerDemoWindow::onProgressSliderMoved);
  59. connect(m_speedCombo,
  60. QOverload<int>::of(&QComboBox::currentIndexChanged),
  61. this,
  62. &PlayerDemoWindow::onSpeedChanged);
  63. connect(m_playBtn, &QPushButton::clicked, this, &PlayerDemoWindow::onPlayClicked);
  64. connect(m_keepAspectCheck, &QCheckBox::toggled, this, [this](bool checked) {
  65. m_videoWidget->setKeepAspectRatio(checked);
  66. });
  67. }
  68. PlayerDemoWindow::~PlayerDemoWindow()
  69. {
  70. if (m_puller) {
  71. m_puller->stop();
  72. delete m_puller;
  73. }
  74. if (m_swrCtx) {
  75. swr_free(&m_swrCtx);
  76. }
  77. if (m_swrBuffer) {
  78. av_free(m_swrBuffer);
  79. }
  80. if (m_audioPlayer) {
  81. delete m_audioPlayer;
  82. }
  83. if (m_videoPlayer) {
  84. delete m_videoPlayer;
  85. }
  86. }
  87. void PlayerDemoWindow::startPlay(const QString& url)
  88. {
  89. if (m_puller) {
  90. m_puller->stop();
  91. delete m_puller;
  92. m_puller = nullptr;
  93. }
  94. m_puller = new FFmpegVideoPuller();
  95. if (!m_puller->open(url, 300, 300)) {
  96. QMessageBox::critical(this, "错误", "无法打开流");
  97. return;
  98. }
  99. m_puller->setSpeed(m_speedCombo->currentData().toFloat());
  100. // 设置回调
  101. m_puller->setVideoRenderCallback([this](AVFrame* frame) {
  102. static bool firstFrame = true;
  103. if (firstFrame) {
  104. m_videoPlayer->init(frame);
  105. m_videoWidget->Open(frame->width, frame->height);
  106. firstFrame = false;
  107. }
  108. m_videoPlayer->render(frame, (void*)videoRenderFunc, m_videoWidget);
  109. updateProgress();
  110. });
  111. m_puller->setAudioPlayCallback([this](AVFrame* frame) {
  112. float speed = m_puller->getSpeed();
  113. if (m_audioPlayer->needReinit(frame, speed)) {
  114. m_audioPlayer->init(frame, speed);
  115. }
  116. m_audioPlayer->play(frame, speed);
  117. });
  118. m_puller->start();
  119. // 进度条初始化
  120. m_firstPts = m_puller->getFirstPts();
  121. m_lastPts = m_puller->getLastPts();
  122. m_duration = m_lastPts - m_firstPts;
  123. m_progressSlider->setValue(0);
  124. updateProgress();
  125. }
  126. void PlayerDemoWindow::onPlayClicked()
  127. {
  128. if (!m_puller)
  129. return;
  130. float curSpeed = m_puller->getSpeed();
  131. if (curSpeed > 0.01f) {
  132. m_puller->setSpeed(0.0f);
  133. m_playBtn->setText("播放");
  134. } else {
  135. float speed = m_speedCombo->currentData().toFloat();
  136. m_puller->setSpeed(speed);
  137. m_playBtn->setText("暂停");
  138. }
  139. }
  140. void PlayerDemoWindow::onProgressSliderMoved(int value)
  141. {
  142. m_sliderPressed = true;
  143. if (m_duration <= 0) {
  144. m_timeLabel->setText("00:00:00 / 00:00:00");
  145. return;
  146. }
  147. double seekPts = m_firstPts + (m_duration * value / 1000.0);
  148. QString cur = formatTime(seekPts - m_firstPts);
  149. QString total = formatTime(m_duration);
  150. m_timeLabel->setText(cur + " / " + total);
  151. }
  152. void PlayerDemoWindow::onProgressSliderReleased()
  153. {
  154. if (!m_puller || m_duration <= 0)
  155. return;
  156. int value = m_progressSlider->value();
  157. double seekPts = m_firstPts + (m_duration * value / 1000.0);
  158. if (seekPts < m_firstPts)
  159. seekPts = m_firstPts;
  160. if (seekPts > m_lastPts)
  161. seekPts = m_lastPts;
  162. m_puller->seekToPts(seekPts);
  163. m_sliderPressed = false;
  164. }
  165. void PlayerDemoWindow::onSpeedChanged(int index)
  166. {
  167. if (!m_puller)
  168. return;
  169. float speed = m_speedCombo->itemData(index).toFloat();
  170. m_puller->setSpeed(speed);
  171. speedFlag.store(true, std::memory_order_release);
  172. }
  173. void PlayerDemoWindow::updateProgress()
  174. {
  175. if (!m_puller || m_sliderPressed)
  176. return;
  177. double duration = m_puller->getTotalDuration();
  178. if (duration > 0) {
  179. m_duration = duration;
  180. m_firstPts = 0;
  181. m_lastPts = duration;
  182. } else {
  183. m_firstPts = m_puller->getFirstPts();
  184. m_lastPts = m_puller->getLastPts();
  185. m_duration = m_lastPts - m_firstPts;
  186. }
  187. // 检查有效性
  188. if (m_firstPts < 0 || m_lastPts < 0 || m_duration <= 0) {
  189. m_progressSlider->setValue(0);
  190. m_timeLabel->setText("00:00:00 / 00:00:00");
  191. return;
  192. }
  193. double curPts = m_puller->getCurrentPts();
  194. if (curPts < m_firstPts)
  195. curPts = m_firstPts;
  196. if (curPts > m_lastPts)
  197. curPts = m_lastPts;
  198. int value = int((curPts - m_firstPts) / m_duration * 1000);
  199. m_progressSlider->setValue(value);
  200. QString cur = formatTime(curPts - m_firstPts);
  201. QString total = formatTime(m_duration);
  202. m_timeLabel->setText(cur + " / " + total);
  203. }
  204. QString PlayerDemoWindow::formatTime(double seconds) const
  205. {
  206. if (seconds < 0)
  207. seconds = 0;
  208. int sec = int(seconds + 0.5);
  209. int h = sec / 3600;
  210. int m = (sec % 3600) / 60;
  211. int s = sec % 60;
  212. return QString("%1:%2:%3")
  213. .arg(h, 2, 10, QChar('0'))
  214. .arg(m, 2, 10, QChar('0'))
  215. .arg(s, 2, 10, QChar('0'));
  216. }