|
|
@@ -30,10 +30,7 @@ PlayerController::PlayerController(QWidget* parent)
|
|
|
: QObject(parent)
|
|
|
{
|
|
|
ffmpeg_init();
|
|
|
- connect(this,
|
|
|
- &PlayerController::asyncInitFinished,
|
|
|
- this,
|
|
|
- &PlayerController::onAsyncInitFinished);
|
|
|
+
|
|
|
m_state = PlayerState::Idle;
|
|
|
}
|
|
|
|
|
|
@@ -48,11 +45,56 @@ PlayerController::~PlayerController()
|
|
|
void PlayerController::startToPlay(const QString& file)
|
|
|
{
|
|
|
std::lock_guard<std::mutex> lock(m_stopMutex);
|
|
|
+ qCDebug(playerControllerLog) << "[PlayerController] m_state" << (int) m_state.load();
|
|
|
+ qDebug() << "[PlayerController] checkAndResetState: "
|
|
|
+ << "m_packetReadThread:" << (bool) m_packetReadThread
|
|
|
+ << "m_decodeVideoThread:" << (bool) m_decodeVideoThread
|
|
|
+ << "m_decodeAudioThread:" << (bool) m_decodeAudioThread
|
|
|
+ << "m_audioPlayThread:" << (bool) m_audioPlayThread
|
|
|
+ << "m_videoPlayThread:" << (bool) m_videoPlayThread
|
|
|
+ << "m_decodeSubtitleThread:" << (bool) m_decodeSubtitleThread << Qt::endl
|
|
|
+ << "m_packetReadThread:"
|
|
|
+ << (m_packetReadThread ? m_packetReadThread->isRunning() : false)
|
|
|
+ << "m_decodeVideoThread:"
|
|
|
+ << (m_decodeVideoThread ? m_decodeVideoThread->isRunning() : false)
|
|
|
+ << "m_decodeAudioThread:"
|
|
|
+ << (m_decodeAudioThread ? m_decodeAudioThread->isRunning() : false)
|
|
|
+ << "m_audioPlayThread:" << (m_audioPlayThread ? m_audioPlayThread->isRunning() : false)
|
|
|
+ << "m_videoPlayThread:" << (m_videoPlayThread ? m_videoPlayThread->isRunning() : false)
|
|
|
+ << "m_decodeSubtitleThread:"
|
|
|
+ << (m_decodeSubtitleThread ? m_decodeSubtitleThread->isRunning() : false);
|
|
|
// 自愈:如果状态为Playing但所有线程都已退出,强制Idle
|
|
|
if (m_state == PlayerState::Playing) {
|
|
|
- if (!m_packetReadThread && !m_decodeVideoThread && !m_decodeAudioThread
|
|
|
- && !m_audioPlayThread && !m_videoPlayThread && !m_decodeSubtitleThread) {
|
|
|
- m_videoState.reset();
|
|
|
+ // 检查线程指针是否存在并且是否正在运行
|
|
|
+ bool allThreadsStopped =
|
|
|
+ (!m_packetReadThread || !m_packetReadThread->isRunning()) &&
|
|
|
+ (!m_decodeVideoThread || !m_decodeVideoThread->isRunning()) &&
|
|
|
+ (!m_decodeAudioThread || !m_decodeAudioThread->isRunning()) &&
|
|
|
+ (!m_audioPlayThread || !m_audioPlayThread->isRunning()) &&
|
|
|
+ (!m_videoPlayThread || !m_videoPlayThread->isRunning()) &&
|
|
|
+ (!m_decodeSubtitleThread || !m_decodeSubtitleThread->isRunning());
|
|
|
+
|
|
|
+ if (allThreadsStopped) {
|
|
|
+ auto stopAndReset = [](auto& threadPtr) {
|
|
|
+ if (threadPtr) {
|
|
|
+ qCDebug(playerControllerLog)
|
|
|
+ << "[stopAndReset] try stop/join thread, isRunning="
|
|
|
+ << threadPtr->isRunning();
|
|
|
+ threadPtr->stop();
|
|
|
+ threadPtr->join();
|
|
|
+ qCDebug(playerControllerLog) << "[stopAndReset] thread joined and will reset.";
|
|
|
+ threadPtr.reset();
|
|
|
+ }
|
|
|
+ };
|
|
|
+ stopAndReset(m_stopPlayWaitingThread);
|
|
|
+ stopAndReset(m_beforePlayThread);
|
|
|
+ stopAndReset(m_packetReadThread);
|
|
|
+ stopAndReset(m_decodeVideoThread);
|
|
|
+ stopAndReset(m_decodeAudioThread);
|
|
|
+ stopAndReset(m_decodeSubtitleThread);
|
|
|
+ stopAndReset(m_videoPlayThread);
|
|
|
+ stopAndReset(m_audioPlayThread);
|
|
|
+
|
|
|
m_currentFile.clear();
|
|
|
m_state = PlayerState::Idle;
|
|
|
qCDebug(playerControllerLog)
|
|
|
@@ -75,6 +117,42 @@ void PlayerController::startToPlay(const QString& file)
|
|
|
}
|
|
|
}
|
|
|
if (m_state == PlayerState::Idle) {
|
|
|
+ bool allThreadsStopped = (!m_packetReadThread || !m_packetReadThread->isRunning())
|
|
|
+ && (!m_decodeVideoThread || !m_decodeVideoThread->isRunning())
|
|
|
+ && (!m_decodeAudioThread || !m_decodeAudioThread->isRunning())
|
|
|
+ && (!m_audioPlayThread || !m_audioPlayThread->isRunning())
|
|
|
+ && (!m_videoPlayThread || !m_videoPlayThread->isRunning())
|
|
|
+ && (!m_decodeSubtitleThread
|
|
|
+ || !m_decodeSubtitleThread->isRunning());
|
|
|
+
|
|
|
+ if (allThreadsStopped) {
|
|
|
+ auto stopAndReset = [](auto& threadPtr) {
|
|
|
+ if (threadPtr) {
|
|
|
+ qCDebug(playerControllerLog)
|
|
|
+ << "[stopAndReset] try stop/join thread, isRunning="
|
|
|
+ << threadPtr->isRunning();
|
|
|
+ threadPtr->stop();
|
|
|
+ threadPtr->join();
|
|
|
+ qCDebug(playerControllerLog) << "[stopAndReset] thread joined and will reset.";
|
|
|
+ threadPtr.reset();
|
|
|
+ }
|
|
|
+ };
|
|
|
+ m_videoState.reset();
|
|
|
+ stopAndReset(m_stopPlayWaitingThread);
|
|
|
+ stopAndReset(m_beforePlayThread);
|
|
|
+ stopAndReset(m_packetReadThread);
|
|
|
+ stopAndReset(m_decodeVideoThread);
|
|
|
+ stopAndReset(m_decodeAudioThread);
|
|
|
+ stopAndReset(m_decodeSubtitleThread);
|
|
|
+ stopAndReset(m_videoPlayThread);
|
|
|
+ stopAndReset(m_audioPlayThread);
|
|
|
+
|
|
|
+ m_currentFile.clear();
|
|
|
+ m_state = PlayerState::Idle;
|
|
|
+ qCDebug(playerControllerLog)
|
|
|
+ << "[PlayerController] All threads stopped, force reset to Idle.";
|
|
|
+ }
|
|
|
+
|
|
|
qCDebug(playerControllerLog) << "Player is idle. Starting playback for:" << file;
|
|
|
m_state = PlayerState::Initializing;
|
|
|
m_currentFile = file;
|
|
|
@@ -99,7 +177,7 @@ void PlayerController::asyncInit(const QString& file)
|
|
|
success = true;
|
|
|
}
|
|
|
m_initSuccess = success;
|
|
|
- emit asyncInitFinished(file, success);
|
|
|
+ onAsyncInitFinished(file, success);
|
|
|
qCDebug(playerControllerLog) << "[Init] asyncInit finished in " << timer.elapsed() << " ms";
|
|
|
}
|
|
|
|
|
|
@@ -111,13 +189,6 @@ void PlayerController::onAsyncInitFinished(const QString& file, bool success)
|
|
|
m_state = PlayerState::Idle;
|
|
|
return;
|
|
|
}
|
|
|
- qCDebug(playerControllerLog) << "[Init] createReadThread...";
|
|
|
- if (!createReadThread()) {
|
|
|
- qCWarning(playerControllerLog) << "Packet read thread creation failed";
|
|
|
- playFailed(m_currentFile);
|
|
|
- m_state = PlayerState::Idle;
|
|
|
- return;
|
|
|
- }
|
|
|
qCDebug(playerControllerLog) << "[Init] createVideoState...";
|
|
|
if (!createVideoState(m_currentFile)) {
|
|
|
qCWarning(playerControllerLog) << "Video state creation failed";
|
|
|
@@ -127,6 +198,13 @@ void PlayerController::onAsyncInitFinished(const QString& file, bool success)
|
|
|
return;
|
|
|
}
|
|
|
assert(m_videoState);
|
|
|
+ qCDebug(playerControllerLog) << "[Init] createReadThread...";
|
|
|
+ if (!createReadThread()) {
|
|
|
+ qCWarning(playerControllerLog) << "Packet read thread creation failed";
|
|
|
+ playFailed(m_currentFile);
|
|
|
+ m_state = PlayerState::Idle;
|
|
|
+ return;
|
|
|
+ }
|
|
|
if (!m_videoState) {
|
|
|
qCWarning(playerControllerLog) << "Video state initialization error";
|
|
|
playFailed(m_currentFile);
|
|
|
@@ -177,23 +255,7 @@ void PlayerController::stopPlay()
|
|
|
if (m_state == PlayerState::Idle)
|
|
|
return;
|
|
|
m_state = PlayerState::Stopping;
|
|
|
- auto stopAndReset = [](auto& threadPtr) {
|
|
|
- if (threadPtr) {
|
|
|
- qCDebug(playerControllerLog) << "[stopAndReset] try stop/join thread, isRunning=" << threadPtr->isRunning();
|
|
|
- threadPtr->stop();
|
|
|
- threadPtr->join();
|
|
|
- qCDebug(playerControllerLog) << "[stopAndReset] thread joined and will reset.";
|
|
|
- threadPtr.reset();
|
|
|
- }
|
|
|
- };
|
|
|
- stopAndReset(m_stopPlayWaitingThread);
|
|
|
- stopAndReset(m_beforePlayThread);
|
|
|
- stopAndReset(m_packetReadThread);
|
|
|
- stopAndReset(m_decodeVideoThread);
|
|
|
- stopAndReset(m_decodeAudioThread);
|
|
|
- stopAndReset(m_decodeSubtitleThread);
|
|
|
- stopAndReset(m_videoPlayThread);
|
|
|
- stopAndReset(m_audioPlayThread);
|
|
|
+
|
|
|
m_videoState.reset();
|
|
|
m_currentFile.clear();
|
|
|
m_state = PlayerState::Idle;
|
|
|
@@ -318,40 +380,46 @@ void PlayerController::playFailed(const QString& file)
|
|
|
void PlayerController::readPacketStopped()
|
|
|
{
|
|
|
qCDebug(playerControllerLog) << "************* Read packets thread stopped signal received.";
|
|
|
- m_packetReadThread.reset();
|
|
|
+ //m_packetReadThread.reset();
|
|
|
checkAndResetState();
|
|
|
+
|
|
|
+ // stopPlay();
|
|
|
+ if (m_videoState) {
|
|
|
+ m_videoState->delete_video_state();
|
|
|
+ }
|
|
|
+
|
|
|
emit audioStopped();
|
|
|
}
|
|
|
void PlayerController::decodeVideoStopped()
|
|
|
{
|
|
|
qCDebug(playerControllerLog) << "************* Video decode thread stopped.";
|
|
|
- m_decodeVideoThread.reset();
|
|
|
+ //m_decodeVideoThread.reset();
|
|
|
checkAndResetState();
|
|
|
}
|
|
|
void PlayerController::decodeAudioStopped()
|
|
|
{
|
|
|
qCDebug(playerControllerLog) << "************* Audio decode thread stopped.";
|
|
|
- m_decodeAudioThread.reset();
|
|
|
+ //m_decodeAudioThread.reset();
|
|
|
checkAndResetState();
|
|
|
}
|
|
|
void PlayerController::decodeSubtitleStopped()
|
|
|
{
|
|
|
qCDebug(playerControllerLog) << "************* Subtitle decode thread stopped.";
|
|
|
- m_decodeSubtitleThread.reset();
|
|
|
+ //m_decodeSubtitleThread.reset();
|
|
|
checkAndResetState();
|
|
|
}
|
|
|
void PlayerController::audioPlayStopped()
|
|
|
{
|
|
|
qCDebug(playerControllerLog) << "************* Audio play thread stopped.";
|
|
|
emit audioStopped();
|
|
|
- m_audioPlayThread.reset();
|
|
|
+ //m_audioPlayThread.reset();
|
|
|
checkAndResetState();
|
|
|
}
|
|
|
void PlayerController::videoPlayStopped()
|
|
|
{
|
|
|
qCDebug(playerControllerLog) << "************* Video play thread stopped.";
|
|
|
emit videoStopped();
|
|
|
- m_videoPlayThread.reset();
|
|
|
+ // m_videoPlayThread.reset();
|
|
|
checkAndResetState();
|
|
|
}
|
|
|
|
|
|
@@ -473,8 +541,10 @@ bool PlayerController::startPlay()
|
|
|
bool PlayerController::waitStopPlay(const QString& file)
|
|
|
{
|
|
|
m_stopPlayWaitingThread = std::make_unique<StopWaitingThread>(this, file.toStdString());
|
|
|
+
|
|
|
m_stopPlayWaitingThread->setOnFinished([this]() {
|
|
|
// 可根据需要添加额外处理
|
|
|
+ //m_stopPlayWaitingThread.reset();
|
|
|
});
|
|
|
m_stopPlayWaitingThread->start();
|
|
|
qCDebug(playerControllerLog) << "++++++++++ StopPlay waiting thread started";
|
|
|
@@ -485,7 +555,11 @@ void PlayerController::allThreadStart()
|
|
|
{
|
|
|
// 启动所有创建的线程
|
|
|
if (m_packetReadThread) {
|
|
|
- m_packetReadThread->start();
|
|
|
+ if (!m_videoState || !m_videoState->get_state()) {
|
|
|
+ qCWarning(playerControllerLog) << "VideoState invalid, skip starting read thread";
|
|
|
+ } else {
|
|
|
+ m_packetReadThread->start();
|
|
|
+ }
|
|
|
qCDebug(playerControllerLog) << "++++++++++ Read packets thread started";
|
|
|
}
|
|
|
|
|
|
@@ -542,24 +616,24 @@ void PlayerController::videoSeekInc(double increment)
|
|
|
// 新增自愈机制辅助函数
|
|
|
void PlayerController::checkAndResetState()
|
|
|
{
|
|
|
- std::lock_guard<std::mutex> lock(m_stopMutex);
|
|
|
- if (!m_packetReadThread && !m_decodeVideoThread && !m_decodeAudioThread && !m_audioPlayThread
|
|
|
- && !m_videoPlayThread && !m_decodeSubtitleThread) {
|
|
|
- m_videoState.reset();
|
|
|
- m_currentFile.clear();
|
|
|
- m_state = PlayerState::Idle;
|
|
|
- qCDebug(playerControllerLog)
|
|
|
- << "[PlayerController] All threads stopped, state reset to Idle.";
|
|
|
- emit playbackFinished(); // 新增:通知UI
|
|
|
- } else {
|
|
|
- qCDebug(playerControllerLog) << "[PlayerController] checkAndResetState: "
|
|
|
- << "m_packetReadThread:" << (bool) m_packetReadThread
|
|
|
- << "m_decodeVideoThread:" << (bool) m_decodeVideoThread
|
|
|
- << "m_decodeAudioThread:" << (bool) m_decodeAudioThread
|
|
|
- << "m_audioPlayThread:" << (bool) m_audioPlayThread
|
|
|
- << "m_videoPlayThread:" << (bool) m_videoPlayThread
|
|
|
- << "m_decodeSubtitleThread:" << (bool) m_decodeSubtitleThread;
|
|
|
- }
|
|
|
+ // std::lock_guard<std::mutex> lock(m_stopMutex);
|
|
|
+ // if (!m_packetReadThread && !m_decodeVideoThread && !m_decodeAudioThread && !m_audioPlayThread
|
|
|
+ // && !m_videoPlayThread && !m_decodeSubtitleThread) {
|
|
|
+ // m_videoState.reset();
|
|
|
+ // m_currentFile.clear();
|
|
|
+ // m_state = PlayerState::Idle;
|
|
|
+ // qCDebug(playerControllerLog)
|
|
|
+ // << "[PlayerController] All threads stopped, state reset to Idle.";
|
|
|
+ // emit playbackFinished(); // 新增:通知UI
|
|
|
+ // } else {
|
|
|
+ // qCDebug(playerControllerLog) << "[PlayerController] checkAndResetState: "
|
|
|
+ // << "m_packetReadThread:" << (bool) m_packetReadThread
|
|
|
+ // << "m_decodeVideoThread:" << (bool) m_decodeVideoThread
|
|
|
+ // << "m_decodeAudioThread:" << (bool) m_decodeAudioThread
|
|
|
+ // << "m_audioPlayThread:" << (bool) m_audioPlayThread
|
|
|
+ // << "m_videoPlayThread:" << (bool) m_videoPlayThread
|
|
|
+ // << "m_decodeSubtitleThread:" << (bool) m_decodeSubtitleThread;
|
|
|
+ // }
|
|
|
}
|
|
|
|
|
|
// 线程创建方法
|
|
|
@@ -594,8 +668,10 @@ bool PlayerController::createReadThread()
|
|
|
return false;
|
|
|
m_packetReadThread = std::make_unique<ReadThread>(m_videoState ? m_videoState->get_state()
|
|
|
: nullptr);
|
|
|
- m_packetReadThread->setOnFinished(
|
|
|
- [this]() { QMetaObject::invokeMethod(this, "readPacketStopped", Qt::QueuedConnection); });
|
|
|
+ m_packetReadThread->setOnFinished([this]() {
|
|
|
+
|
|
|
+ readPacketStopped();
|
|
|
+ });
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
@@ -609,8 +685,7 @@ bool PlayerController::createDecodeVideoThread()
|
|
|
return false;
|
|
|
|
|
|
m_decodeVideoThread = std::make_unique<VideoDecodeThread>(state);
|
|
|
- m_decodeVideoThread->setOnFinished(
|
|
|
- [this]() { QMetaObject::invokeMethod(this, "decodeVideoStopped", Qt::QueuedConnection); });
|
|
|
+ m_decodeVideoThread->setOnFinished([this]() { decodeVideoStopped(); });
|
|
|
|
|
|
auto codecContext = m_videoState->get_contex(AVMEDIA_TYPE_VIDEO);
|
|
|
|
|
|
@@ -644,8 +719,7 @@ bool PlayerController::createDecodeAudioThread()
|
|
|
return false;
|
|
|
|
|
|
m_decodeAudioThread = std::make_unique<AudioDecodeThread>(state);
|
|
|
- m_decodeAudioThread->setOnFinished(
|
|
|
- [this]() { QMetaObject::invokeMethod(this, "decodeAudioStopped", Qt::QueuedConnection); });
|
|
|
+ m_decodeAudioThread->setOnFinished([this]() { decodeAudioStopped(); });
|
|
|
|
|
|
auto codecContext = m_videoState->get_contex(AVMEDIA_TYPE_AUDIO);
|
|
|
|
|
|
@@ -678,9 +752,7 @@ bool PlayerController::createDecodeSubtitleThread()
|
|
|
return false;
|
|
|
|
|
|
m_decodeSubtitleThread = std::make_unique<SubtitleDecodeThread>(state);
|
|
|
- m_decodeSubtitleThread->setOnFinished([this]() {
|
|
|
- QMetaObject::invokeMethod(this, "decodeSubtitleStopped", Qt::QueuedConnection);
|
|
|
- });
|
|
|
+ m_decodeSubtitleThread->setOnFinished([this]() { decodeSubtitleStopped(); });
|
|
|
|
|
|
auto codecContext = m_videoState->get_contex(AVMEDIA_TYPE_SUBTITLE);
|
|
|
|
|
|
@@ -713,8 +785,7 @@ bool PlayerController::createVideoPlayThread()
|
|
|
return false;
|
|
|
|
|
|
m_videoPlayThread = std::make_unique<VideoPlayThread>(state);
|
|
|
- m_videoPlayThread->setOnFinished(
|
|
|
- [this]() { QMetaObject::invokeMethod(this, "videoPlayStopped", Qt::QueuedConnection); });
|
|
|
+ m_videoPlayThread->setOnFinished([this]() { videoPlayStopped(); });
|
|
|
m_videoPlayThread->setOnFrameReady([this](AVFrame* frame) { this->onFrameReady(frame); });
|
|
|
m_videoPlayThread->setOnSubtitleReady([this](const QString& text) {
|
|
|
// TODO: 实现 PlayerController::onSubtitleReady(const QString&) 处理字幕
|
|
|
@@ -743,8 +814,7 @@ bool PlayerController::createAudioPlayThread()
|
|
|
return false;
|
|
|
|
|
|
m_audioPlayThread = std::make_unique<AudioPlayThread>(state);
|
|
|
- m_audioPlayThread->setOnFinished(
|
|
|
- [this]() { QMetaObject::invokeMethod(this, "audioPlayStopped", Qt::QueuedConnection); });
|
|
|
+ m_audioPlayThread->setOnFinished([this]() { audioPlayStopped(); });
|
|
|
m_audioPlayThread->setOnUpdatePlayTime([this]() {
|
|
|
// TODO: 实现 PlayerController::onUpdatePlayTime() 处理播放时间更新
|
|
|
// onUpdatePlayTime();
|
|
|
@@ -773,7 +843,8 @@ bool PlayerController::startPlayThread()
|
|
|
if (m_beforePlayThread)
|
|
|
return false;
|
|
|
|
|
|
- m_beforePlayThread = std::make_unique<StartPlayThread>(this);
|
|
|
+ m_beforePlayThread = std::make_unique<StartPlayThread>(m_audioPlayThread.get(), m_videoState.get());
|
|
|
+
|
|
|
m_beforePlayThread->setOnFinished([this]() {
|
|
|
qCDebug(playerControllerLog) << "[StartPlayThread] finished, call playStarted()";
|
|
|
playStarted();
|