|
|
@@ -265,18 +265,17 @@ void PlayerController::playMute(bool mute)
|
|
|
|
|
|
void PlayerController::playStartSeek()
|
|
|
{
|
|
|
- emit playSeek();
|
|
|
pausePlay();
|
|
|
}
|
|
|
|
|
|
void PlayerController::playSeekPre()
|
|
|
{
|
|
|
- videoSeekInc(-2);
|
|
|
+ videoSeekInc(-0.5); // 将步长从2秒减小到0.5秒,提高精度
|
|
|
}
|
|
|
|
|
|
void PlayerController::playSeekNext()
|
|
|
{
|
|
|
- videoSeekInc(2);
|
|
|
+ videoSeekInc(0.5); // 将步长从2秒减小到0.5秒,提高精度
|
|
|
}
|
|
|
|
|
|
void PlayerController::setVolume(int volume, int maxValue)
|
|
|
@@ -311,14 +310,14 @@ bool PlayerController::isPlaying() const
|
|
|
if (m_state != PlayerState::Playing) {
|
|
|
return false;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 如果状态是Playing但所有线程都已停止,则实际上不是在播放状态
|
|
|
if (areAllThreadsStopped()) {
|
|
|
qCDebug(playerControllerLog) << "[isPlaying] State is Playing but all threads stopped";
|
|
|
|
|
|
return false;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
@@ -368,7 +367,7 @@ void PlayerController::playStarted(bool success)
|
|
|
void PlayerController::playFailed(const QString& file)
|
|
|
{
|
|
|
qCWarning(playerControllerLog) << "Playback failed for file:" << toNativePath(file);
|
|
|
-
|
|
|
+
|
|
|
// 确保状态一致性
|
|
|
if (m_state != PlayerState::Idle) {
|
|
|
// 检查线程状态并重置
|
|
|
@@ -377,7 +376,7 @@ void PlayerController::playFailed(const QString& file)
|
|
|
stopAndResetThreads();
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
emit showMessage(QString("Playback failed: %1").arg(toNativePath(file)), "Warning", "");
|
|
|
}
|
|
|
|
|
|
@@ -457,20 +456,79 @@ void PlayerController::videoSeek(double position, double increment)
|
|
|
&& position < state->ic->start_time / static_cast<double>(AV_TIME_BASE)) {
|
|
|
position = state->ic->start_time / static_cast<double>(AV_TIME_BASE);
|
|
|
}
|
|
|
-
|
|
|
+ // 添加量化操作,精确到0.01秒
|
|
|
+ position = round(position * 100) / 100.0;
|
|
|
+ qDebug() << "position:" << position
|
|
|
+ << "position * AV_TIME_BASE:" << static_cast<int64_t>(position * AV_TIME_BASE)
|
|
|
+ << "increment * AV_TIME_BASE:" << static_cast<int64_t>(increment * AV_TIME_BASE);
|
|
|
stream_seek(state,
|
|
|
static_cast<int64_t>(position * AV_TIME_BASE),
|
|
|
static_cast<int64_t>(increment * AV_TIME_BASE),
|
|
|
0);
|
|
|
}
|
|
|
|
|
|
+void PlayerController::videoSeekEx(double value, double maxValue)
|
|
|
+{
|
|
|
+ if (!m_videoState)
|
|
|
+ return;
|
|
|
+
|
|
|
+ auto state = m_videoState->get_state();
|
|
|
+ if (!state)
|
|
|
+ return;
|
|
|
+ auto cur_stream = state;
|
|
|
+ auto x = value;
|
|
|
+ double frac;
|
|
|
+
|
|
|
+ if (m_videoState->seekByBytes() || cur_stream->ic->duration <= 0) {
|
|
|
+ uint64_t size = avio_size(cur_stream->ic->pb);
|
|
|
+ stream_seek(cur_stream, size * x / maxValue, 0, 1);
|
|
|
+ } else {
|
|
|
+ int64_t ts;
|
|
|
+ int ns, hh, mm, ss;
|
|
|
+ int tns, thh, tmm, tss;
|
|
|
+ tns = cur_stream->ic->duration / 1000000LL;
|
|
|
+ thh = tns / 3600;
|
|
|
+ tmm = (tns % 3600) / 60;
|
|
|
+ tss = (tns % 60);
|
|
|
+ frac = x / maxValue;
|
|
|
+ ns = frac * tns;
|
|
|
+ hh = ns / 3600;
|
|
|
+ mm = (ns % 3600) / 60;
|
|
|
+ ss = (ns % 60);
|
|
|
+ av_log(NULL,
|
|
|
+ AV_LOG_INFO,
|
|
|
+ "Seek to %2.0f%% (%2d:%02d:%02d) of total duration (%2d:%02d:%02d) \n",
|
|
|
+ frac * 100,
|
|
|
+ hh,
|
|
|
+ mm,
|
|
|
+ ss,
|
|
|
+ thh,
|
|
|
+ tmm,
|
|
|
+ tss);
|
|
|
+ qDebug("Seek to %2.0f%% (%2d:%02d:%02d) of total duration (%2d:%02d:%02d)",
|
|
|
+ frac * 100,
|
|
|
+ hh,
|
|
|
+ mm,
|
|
|
+ ss,
|
|
|
+ thh,
|
|
|
+ tmm,
|
|
|
+ tss);
|
|
|
+ ts = frac * cur_stream->ic->duration;
|
|
|
+ if (cur_stream->ic->start_time != AV_NOPTS_VALUE)
|
|
|
+ ts += cur_stream->ic->start_time;
|
|
|
+ stream_seek(cur_stream, ts, 0, 0);
|
|
|
+ }
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
// 线程管理辅助方法
|
|
|
void PlayerController::stopAndResetThreads()
|
|
|
{
|
|
|
auto stopAndReset = [](auto& threadPtr, const QString& threadName) {
|
|
|
if (threadPtr) {
|
|
|
qCDebug(playerControllerLog)
|
|
|
- << "[stopAndReset] [" << threadName << "] try stop/join thread, isRunning=" << threadPtr->isRunning();
|
|
|
+ << "[stopAndReset] [" << threadName
|
|
|
+ << "] try stop/join thread, isRunning=" << threadPtr->isRunning();
|
|
|
threadPtr->stop();
|
|
|
|
|
|
// 添加超时等待机制
|
|
|
@@ -483,7 +541,8 @@ void PlayerController::stopAndResetThreads()
|
|
|
= std::chrono::duration_cast<std::chrono::milliseconds>(now - startTime).count();
|
|
|
if (elapsed > MAX_WAIT_MS) {
|
|
|
qCWarning(playerControllerLog)
|
|
|
- << "[stopAndReset] [" << threadName << "] Thread stop timeout after" << elapsed << "ms";
|
|
|
+ << "[stopAndReset] [" << threadName << "] Thread stop timeout after"
|
|
|
+ << elapsed << "ms";
|
|
|
break;
|
|
|
}
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
|
@@ -768,11 +827,11 @@ bool PlayerController::createAudioPlayThread()
|
|
|
m_audioPlayThread->setOnFinished([this]() { audioPlayStopped(); });
|
|
|
m_audioPlayThread->setOnUpdatePlayTime([this]() {
|
|
|
// TODO: 实现 PlayerController::onUpdatePlayTime() 处理播放时间更新
|
|
|
- // emit updatePlayTime();
|
|
|
+ //emit updatePlayTime();
|
|
|
});
|
|
|
m_audioPlayThread->setOnDataVisualReady([this](const AudioData& data) {
|
|
|
// 异步 ?
|
|
|
- // emit audioData(data);
|
|
|
+ emit audioData(data);
|
|
|
});
|
|
|
|
|
|
// 音频设备初始化在独立线程中完成
|