|
@@ -46,65 +46,24 @@ void PlayerController::startToPlay(const QString& file)
|
|
|
{
|
|
{
|
|
|
std::lock_guard<std::mutex> lock(m_stopMutex);
|
|
std::lock_guard<std::mutex> lock(m_stopMutex);
|
|
|
qCDebug(playerControllerLog) << "[PlayerController] m_state" << (int) m_state.load();
|
|
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
|
|
// 自愈:如果状态为Playing但所有线程都已退出,强制Idle
|
|
|
- if (m_state == PlayerState::Playing) {
|
|
|
|
|
- // 检查线程指针是否存在并且是否正在运行
|
|
|
|
|
- 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)
|
|
|
|
|
- << "[PlayerController] All threads stopped, force reset to Idle.";
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (m_state == PlayerState::Playing && areAllThreadsStopped()) {
|
|
|
|
|
+ stopAndResetThreads();
|
|
|
|
|
+ m_videoState.reset();
|
|
|
|
|
+ m_currentFile.clear();
|
|
|
|
|
+ m_state = PlayerState::Idle;
|
|
|
|
|
+ qCDebug(playerControllerLog)
|
|
|
|
|
+ << "[PlayerController] All threads stopped, force reset to Idle.";
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // 初始化中,忽略新请求
|
|
|
if (m_state == PlayerState::Initializing) {
|
|
if (m_state == PlayerState::Initializing) {
|
|
|
qCDebug(playerControllerLog) << "Player is initializing. Ignoring request.";
|
|
qCDebug(playerControllerLog) << "Player is initializing. Ignoring request.";
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // 正在播放中,检查是否需要切换文件
|
|
|
if (m_state == PlayerState::Playing) {
|
|
if (m_state == PlayerState::Playing) {
|
|
|
if (m_currentFile == file) {
|
|
if (m_currentFile == file) {
|
|
|
qCDebug(playerControllerLog) << "Already playing the same file. Ignoring request.";
|
|
qCDebug(playerControllerLog) << "Already playing the same file. Ignoring request.";
|
|
@@ -116,46 +75,24 @@ void PlayerController::startToPlay(const QString& file)
|
|
|
// 这里直接 fallthrough 到 Idle 状态
|
|
// 这里直接 fallthrough 到 Idle 状态
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // 空闲状态,开始新播放
|
|
|
if (m_state == PlayerState::Idle) {
|
|
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.";
|
|
|
|
|
|
|
+ // 确保所有线程已停止
|
|
|
|
|
+ if (!areAllThreadsStopped()) {
|
|
|
|
|
+ qCDebug(playerControllerLog) << "Some threads still running, stopping them first";
|
|
|
|
|
+ stopAndResetThreads();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 重置状态
|
|
|
|
|
+ m_videoState.reset();
|
|
|
|
|
+ m_currentFile.clear();
|
|
|
|
|
+
|
|
|
qCDebug(playerControllerLog) << "Player is idle. Starting playback for:" << file;
|
|
qCDebug(playerControllerLog) << "Player is idle. Starting playback for:" << file;
|
|
|
m_state = PlayerState::Initializing;
|
|
m_state = PlayerState::Initializing;
|
|
|
m_currentFile = file;
|
|
m_currentFile = file;
|
|
|
|
|
+
|
|
|
|
|
+ // 启动异步初始化线程
|
|
|
if (m_initThread.joinable()) {
|
|
if (m_initThread.joinable()) {
|
|
|
m_initThread.join();
|
|
m_initThread.join();
|
|
|
}
|
|
}
|
|
@@ -169,13 +106,24 @@ void PlayerController::asyncInit(const QString& file)
|
|
|
QElapsedTimer timer;
|
|
QElapsedTimer timer;
|
|
|
timer.start();
|
|
timer.start();
|
|
|
qCDebug(playerControllerLog) << "[Init] asyncInit started";
|
|
qCDebug(playerControllerLog) << "[Init] asyncInit started";
|
|
|
|
|
+
|
|
|
|
|
+ // 检查文件有效性
|
|
|
if (file.isEmpty()) {
|
|
if (file.isEmpty()) {
|
|
|
qCWarning(playerControllerLog) << "Filename is invalid. Please select a valid media file.";
|
|
qCWarning(playerControllerLog) << "Filename is invalid. Please select a valid media file.";
|
|
|
success = false;
|
|
success = false;
|
|
|
} else {
|
|
} else {
|
|
|
- qCInfo(playerControllerLog) << "Check file: " << toNativePath(file);
|
|
|
|
|
- success = true;
|
|
|
|
|
|
|
+ // 检查文件是否存在和可访问
|
|
|
|
|
+ QFileInfo fileInfo(file);
|
|
|
|
|
+ if (!fileInfo.exists() || !fileInfo.isReadable()) {
|
|
|
|
|
+ qCWarning(playerControllerLog)
|
|
|
|
|
+ << "File does not exist or is not readable:" << toNativePath(file);
|
|
|
|
|
+ success = false;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ qCInfo(playerControllerLog) << "File check passed:" << toNativePath(file);
|
|
|
|
|
+ success = true;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
m_initSuccess = success;
|
|
m_initSuccess = success;
|
|
|
onAsyncInitFinished(file, success);
|
|
onAsyncInitFinished(file, success);
|
|
|
qCDebug(playerControllerLog) << "[Init] asyncInit finished in " << timer.elapsed() << " ms";
|
|
qCDebug(playerControllerLog) << "[Init] asyncInit finished in " << timer.elapsed() << " ms";
|
|
@@ -184,11 +132,17 @@ void PlayerController::asyncInit(const QString& file)
|
|
|
void PlayerController::onAsyncInitFinished(const QString& file, bool success)
|
|
void PlayerController::onAsyncInitFinished(const QString& file, bool success)
|
|
|
{
|
|
{
|
|
|
std::lock_guard<std::mutex> lock(m_stopMutex);
|
|
std::lock_guard<std::mutex> lock(m_stopMutex);
|
|
|
|
|
+ QElapsedTimer timer;
|
|
|
|
|
+ timer.start();
|
|
|
|
|
+
|
|
|
|
|
+ // 初始化失败处理
|
|
|
if (!success) {
|
|
if (!success) {
|
|
|
playFailed(m_currentFile);
|
|
playFailed(m_currentFile);
|
|
|
m_state = PlayerState::Idle;
|
|
m_state = PlayerState::Idle;
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // 创建视频状态对象
|
|
|
qCDebug(playerControllerLog) << "[Init] createVideoState...";
|
|
qCDebug(playerControllerLog) << "[Init] createVideoState...";
|
|
|
if (!createVideoState(m_currentFile)) {
|
|
if (!createVideoState(m_currentFile)) {
|
|
|
qCWarning(playerControllerLog) << "Video state creation failed";
|
|
qCWarning(playerControllerLog) << "Video state creation failed";
|
|
@@ -197,24 +151,34 @@ void PlayerController::onAsyncInitFinished(const QString& file, bool success)
|
|
|
m_state = PlayerState::Idle;
|
|
m_state = PlayerState::Idle;
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // 检查状态有效性
|
|
|
assert(m_videoState);
|
|
assert(m_videoState);
|
|
|
- qCDebug(playerControllerLog) << "[Init] createReadThread...";
|
|
|
|
|
- if (!createReadThread()) {
|
|
|
|
|
- qCWarning(playerControllerLog) << "Packet read thread creation failed";
|
|
|
|
|
|
|
+ if (!m_videoState) {
|
|
|
|
|
+ qCWarning(playerControllerLog) << "Video state initialization error";
|
|
|
playFailed(m_currentFile);
|
|
playFailed(m_currentFile);
|
|
|
m_state = PlayerState::Idle;
|
|
m_state = PlayerState::Idle;
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
- if (!m_videoState) {
|
|
|
|
|
- qCWarning(playerControllerLog) << "Video state initialization error";
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // 创建数据包读取线程
|
|
|
|
|
+ qCDebug(playerControllerLog) << "[Init] createReadThread...";
|
|
|
|
|
+ if (!createReadThread()) {
|
|
|
|
|
+ qCWarning(playerControllerLog) << "Packet read thread creation failed";
|
|
|
playFailed(m_currentFile);
|
|
playFailed(m_currentFile);
|
|
|
m_state = PlayerState::Idle;
|
|
m_state = PlayerState::Idle;
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // 设置视频状态
|
|
|
m_packetReadThread->set_video_state(m_videoState->get_state());
|
|
m_packetReadThread->set_video_state(m_videoState->get_state());
|
|
|
|
|
+
|
|
|
|
|
+ // 检查媒体流类型
|
|
|
const bool hasVideo = playingHasVideo();
|
|
const bool hasVideo = playingHasVideo();
|
|
|
const bool hasAudio = playingHasAudio();
|
|
const bool hasAudio = playingHasAudio();
|
|
|
const bool hasSubtitle = playingHasSubtitle();
|
|
const bool hasSubtitle = playingHasSubtitle();
|
|
|
|
|
+
|
|
|
|
|
+ // 创建视频相关线程
|
|
|
if (hasVideo) {
|
|
if (hasVideo) {
|
|
|
if (!createDecodeVideoThread() || !createVideoPlayThread()) {
|
|
if (!createDecodeVideoThread() || !createVideoPlayThread()) {
|
|
|
qCWarning(playerControllerLog) << "Video processing setup failed";
|
|
qCWarning(playerControllerLog) << "Video processing setup failed";
|
|
@@ -224,6 +188,8 @@ void PlayerController::onAsyncInitFinished(const QString& file, bool success)
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // 创建音频相关线程
|
|
|
if (hasAudio) {
|
|
if (hasAudio) {
|
|
|
if (!createDecodeAudioThread() || !createAudioPlayThread()) {
|
|
if (!createDecodeAudioThread() || !createAudioPlayThread()) {
|
|
|
qCWarning(playerControllerLog) << "Audio processing setup failed";
|
|
qCWarning(playerControllerLog) << "Audio processing setup failed";
|
|
@@ -233,6 +199,8 @@ void PlayerController::onAsyncInitFinished(const QString& file, bool success)
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // 创建字幕线程
|
|
|
if (hasSubtitle && !createDecodeSubtitleThread()) {
|
|
if (hasSubtitle && !createDecodeSubtitleThread()) {
|
|
|
qCWarning(playerControllerLog) << "Subtitle processing setup failed";
|
|
qCWarning(playerControllerLog) << "Subtitle processing setup failed";
|
|
|
playFailed(m_currentFile);
|
|
playFailed(m_currentFile);
|
|
@@ -240,13 +208,18 @@ void PlayerController::onAsyncInitFinished(const QString& file, bool success)
|
|
|
m_state = PlayerState::Idle;
|
|
m_state = PlayerState::Idle;
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // 开始播放
|
|
|
if (hasAudio) {
|
|
if (hasAudio) {
|
|
|
- startPlayThread();
|
|
|
|
|
|
|
+ startPlayThread(); // 异步启动(处理音频设备初始化)
|
|
|
} else {
|
|
} else {
|
|
|
- playStarted();
|
|
|
|
|
|
|
+ playStarted(); // 同步启动(无音频流)
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
emit startToPlaySignal();
|
|
emit startToPlaySignal();
|
|
|
m_state = PlayerState::Playing;
|
|
m_state = PlayerState::Playing;
|
|
|
|
|
+
|
|
|
|
|
+ qCDebug(playerControllerLog) << "Playback initialized in " << timer.elapsed() << " ms";
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void PlayerController::stopPlay()
|
|
void PlayerController::stopPlay()
|
|
@@ -254,11 +227,19 @@ void PlayerController::stopPlay()
|
|
|
std::lock_guard<std::mutex> lock(m_stopMutex);
|
|
std::lock_guard<std::mutex> lock(m_stopMutex);
|
|
|
if (m_state == PlayerState::Idle)
|
|
if (m_state == PlayerState::Idle)
|
|
|
return;
|
|
return;
|
|
|
|
|
+
|
|
|
|
|
+ qCDebug(playerControllerLog) << "Stopping playback...";
|
|
|
m_state = PlayerState::Stopping;
|
|
m_state = PlayerState::Stopping;
|
|
|
|
|
|
|
|
|
|
+ // 停止并重置所有线程
|
|
|
|
|
+ stopAndResetThreads();
|
|
|
|
|
+
|
|
|
|
|
+ // 清理视频状态和文件信息
|
|
|
m_videoState.reset();
|
|
m_videoState.reset();
|
|
|
m_currentFile.clear();
|
|
m_currentFile.clear();
|
|
|
m_state = PlayerState::Idle;
|
|
m_state = PlayerState::Idle;
|
|
|
|
|
+
|
|
|
|
|
+ qCDebug(playerControllerLog) << "Playback stopped.";
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void PlayerController::pausePlay()
|
|
void PlayerController::pausePlay()
|
|
@@ -381,7 +362,6 @@ void PlayerController::readPacketStopped()
|
|
|
{
|
|
{
|
|
|
qCDebug(playerControllerLog) << "************* Read packets thread stopped signal received.";
|
|
qCDebug(playerControllerLog) << "************* Read packets thread stopped signal received.";
|
|
|
//m_packetReadThread.reset();
|
|
//m_packetReadThread.reset();
|
|
|
- checkAndResetState();
|
|
|
|
|
|
|
|
|
|
// stopPlay();
|
|
// stopPlay();
|
|
|
if (m_videoState) {
|
|
if (m_videoState) {
|
|
@@ -393,34 +373,24 @@ void PlayerController::readPacketStopped()
|
|
|
void PlayerController::decodeVideoStopped()
|
|
void PlayerController::decodeVideoStopped()
|
|
|
{
|
|
{
|
|
|
qCDebug(playerControllerLog) << "************* Video decode thread stopped.";
|
|
qCDebug(playerControllerLog) << "************* Video decode thread stopped.";
|
|
|
- //m_decodeVideoThread.reset();
|
|
|
|
|
- checkAndResetState();
|
|
|
|
|
}
|
|
}
|
|
|
void PlayerController::decodeAudioStopped()
|
|
void PlayerController::decodeAudioStopped()
|
|
|
{
|
|
{
|
|
|
qCDebug(playerControllerLog) << "************* Audio decode thread stopped.";
|
|
qCDebug(playerControllerLog) << "************* Audio decode thread stopped.";
|
|
|
- //m_decodeAudioThread.reset();
|
|
|
|
|
- checkAndResetState();
|
|
|
|
|
}
|
|
}
|
|
|
void PlayerController::decodeSubtitleStopped()
|
|
void PlayerController::decodeSubtitleStopped()
|
|
|
{
|
|
{
|
|
|
qCDebug(playerControllerLog) << "************* Subtitle decode thread stopped.";
|
|
qCDebug(playerControllerLog) << "************* Subtitle decode thread stopped.";
|
|
|
- //m_decodeSubtitleThread.reset();
|
|
|
|
|
- checkAndResetState();
|
|
|
|
|
}
|
|
}
|
|
|
void PlayerController::audioPlayStopped()
|
|
void PlayerController::audioPlayStopped()
|
|
|
{
|
|
{
|
|
|
qCDebug(playerControllerLog) << "************* Audio play thread stopped.";
|
|
qCDebug(playerControllerLog) << "************* Audio play thread stopped.";
|
|
|
emit audioStopped();
|
|
emit audioStopped();
|
|
|
- //m_audioPlayThread.reset();
|
|
|
|
|
- checkAndResetState();
|
|
|
|
|
}
|
|
}
|
|
|
void PlayerController::videoPlayStopped()
|
|
void PlayerController::videoPlayStopped()
|
|
|
{
|
|
{
|
|
|
qCDebug(playerControllerLog) << "************* Video play thread stopped.";
|
|
qCDebug(playerControllerLog) << "************* Video play thread stopped.";
|
|
|
emit videoStopped();
|
|
emit videoStopped();
|
|
|
- // m_videoPlayThread.reset();
|
|
|
|
|
- checkAndResetState();
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 线程管理槽函数
|
|
// 线程管理槽函数
|
|
@@ -466,76 +436,40 @@ void PlayerController::videoSeek(double position, double increment)
|
|
|
0);
|
|
0);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// 核心私有实现
|
|
|
|
|
-bool PlayerController::startPlay()
|
|
|
|
|
|
|
+// 线程管理辅助方法
|
|
|
|
|
+void PlayerController::stopAndResetThreads()
|
|
|
{
|
|
{
|
|
|
- QElapsedTimer timer;
|
|
|
|
|
- timer.start();
|
|
|
|
|
-
|
|
|
|
|
- if (m_currentFile.isEmpty()) {
|
|
|
|
|
- qCWarning(playerControllerLog) << "Filename is invalid. Please select a valid media file.";
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- qCInfo(playerControllerLog) << "Starting playback:" << toNativePath(m_currentFile);
|
|
|
|
|
-
|
|
|
|
|
- // 创建数据包读取线程
|
|
|
|
|
- if (!createReadThread()) {
|
|
|
|
|
- qCWarning(playerControllerLog) << "Packet read thread creation failed";
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 创建视频状态对象
|
|
|
|
|
- if (!createVideoState(m_currentFile)) {
|
|
|
|
|
- qCWarning(playerControllerLog) << "Video state creation failed";
|
|
|
|
|
- readPacketStopped();
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 检查状态有效性
|
|
|
|
|
- assert(m_videoState);
|
|
|
|
|
- if (!m_videoState) {
|
|
|
|
|
- qCWarning(playerControllerLog) << "Video state initialization error";
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- m_packetReadThread->set_video_state(m_videoState->get_state());
|
|
|
|
|
-
|
|
|
|
|
- const bool hasVideo = playingHasVideo();
|
|
|
|
|
- const bool hasAudio = playingHasAudio();
|
|
|
|
|
- const bool hasSubtitle = playingHasSubtitle();
|
|
|
|
|
-
|
|
|
|
|
- // 创建视频相关线程
|
|
|
|
|
- if (hasVideo) {
|
|
|
|
|
- if (!createDecodeVideoThread() || !createVideoPlayThread()) {
|
|
|
|
|
- qCWarning(playerControllerLog) << "Video processing setup failed";
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 创建音频相关线程
|
|
|
|
|
- if (hasAudio) {
|
|
|
|
|
- if (!createDecodeAudioThread() || !createAudioPlayThread()) {
|
|
|
|
|
- qCWarning(playerControllerLog) << "Audio processing setup failed";
|
|
|
|
|
- return false;
|
|
|
|
|
|
|
+ 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();
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 创建字幕线程
|
|
|
|
|
- if (hasSubtitle && !createDecodeSubtitleThread()) {
|
|
|
|
|
- qCWarning(playerControllerLog) << "Subtitle processing setup failed";
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- // 开始播放
|
|
|
|
|
- if (hasAudio) {
|
|
|
|
|
- startPlayThread(); // 异步启动(处理音频设备初始化)
|
|
|
|
|
- } else {
|
|
|
|
|
- playStarted(); // 同步启动(无音频流)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // 按依赖顺序停止线程
|
|
|
|
|
+ stopAndReset(m_stopPlayWaitingThread);
|
|
|
|
|
+ stopAndReset(m_beforePlayThread);
|
|
|
|
|
+ stopAndReset(m_packetReadThread);
|
|
|
|
|
+ stopAndReset(m_decodeVideoThread);
|
|
|
|
|
+ stopAndReset(m_decodeAudioThread);
|
|
|
|
|
+ stopAndReset(m_decodeSubtitleThread);
|
|
|
|
|
+ stopAndReset(m_videoPlayThread);
|
|
|
|
|
+ stopAndReset(m_audioPlayThread);
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- qCDebug(playerControllerLog) << "Playback initialized in " << timer.elapsed() << " ms";
|
|
|
|
|
- return true;
|
|
|
|
|
|
|
+bool PlayerController::areAllThreadsStopped() const
|
|
|
|
|
+{
|
|
|
|
|
+ // 检查所有线程是否已停止
|
|
|
|
|
+ return (!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());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
bool PlayerController::waitStopPlay(const QString& file)
|
|
bool PlayerController::waitStopPlay(const QString& file)
|
|
@@ -613,29 +547,6 @@ void PlayerController::videoSeekInc(double increment)
|
|
|
videoSeek(position, increment);
|
|
videoSeek(position, 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;
|
|
|
|
|
- // }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
// 线程创建方法
|
|
// 线程创建方法
|
|
|
bool PlayerController::createVideoState(const QString& file)
|
|
bool PlayerController::createVideoState(const QString& file)
|
|
|
{
|
|
{
|
|
@@ -668,10 +579,7 @@ bool PlayerController::createReadThread()
|
|
|
return false;
|
|
return false;
|
|
|
m_packetReadThread = std::make_unique<ReadThread>(m_videoState ? m_videoState->get_state()
|
|
m_packetReadThread = std::make_unique<ReadThread>(m_videoState ? m_videoState->get_state()
|
|
|
: nullptr);
|
|
: nullptr);
|
|
|
- m_packetReadThread->setOnFinished([this]() {
|
|
|
|
|
-
|
|
|
|
|
- readPacketStopped();
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ m_packetReadThread->setOnFinished([this]() { readPacketStopped(); });
|
|
|
return true;
|
|
return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -695,7 +603,8 @@ bool PlayerController::createDecodeVideoThread()
|
|
|
&state->videoq,
|
|
&state->videoq,
|
|
|
state->continue_read_thread);
|
|
state->continue_read_thread);
|
|
|
if (ret < 0) {
|
|
if (ret < 0) {
|
|
|
- qCWarning(playerControllerLog) << "Video decoder initialization failed (error: " << ret << ")";
|
|
|
|
|
|
|
+ qCWarning(playerControllerLog)
|
|
|
|
|
+ << "Video decoder initialization failed (error: " << ret << ")";
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -729,7 +638,8 @@ bool PlayerController::createDecodeAudioThread()
|
|
|
&state->audioq,
|
|
&state->audioq,
|
|
|
state->continue_read_thread);
|
|
state->continue_read_thread);
|
|
|
if (ret < 0) {
|
|
if (ret < 0) {
|
|
|
- qCWarning(playerControllerLog) << "Audio decoder initialization failed (error: " << ret << ")";
|
|
|
|
|
|
|
+ qCWarning(playerControllerLog)
|
|
|
|
|
+ << "Audio decoder initialization failed (error: " << ret << ")";
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -762,7 +672,8 @@ bool PlayerController::createDecodeSubtitleThread()
|
|
|
&state->subtitleq,
|
|
&state->subtitleq,
|
|
|
state->continue_read_thread);
|
|
state->continue_read_thread);
|
|
|
if (ret < 0) {
|
|
if (ret < 0) {
|
|
|
- qCWarning(playerControllerLog) << "Subtitle decoder initialization failed (error: " << ret << ")";
|
|
|
|
|
|
|
+ qCWarning(playerControllerLog)
|
|
|
|
|
+ << "Subtitle decoder initialization failed (error: " << ret << ")";
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -833,7 +744,8 @@ bool PlayerController::startPlayThread()
|
|
|
if (m_beforePlayThread)
|
|
if (m_beforePlayThread)
|
|
|
return false;
|
|
return false;
|
|
|
|
|
|
|
|
- m_beforePlayThread = std::make_unique<StartPlayThread>(m_audioPlayThread.get(), m_videoState.get());
|
|
|
|
|
|
|
+ m_beforePlayThread = std::make_unique<StartPlayThread>(m_audioPlayThread.get(),
|
|
|
|
|
+ m_videoState.get());
|
|
|
|
|
|
|
|
m_beforePlayThread->setOnFinished([this]() {
|
|
m_beforePlayThread->setOnFinished([this]() {
|
|
|
qCDebug(playerControllerLog) << "[StartPlayThread] finished, call playStarted()";
|
|
qCDebug(playerControllerLog) << "[StartPlayThread] finished, call playStarted()";
|
|
@@ -851,14 +763,20 @@ void PlayerController::printDecodeContext(const AVCodecContext* codecCtx, bool i
|
|
|
if (!codecCtx)
|
|
if (!codecCtx)
|
|
|
return;
|
|
return;
|
|
|
|
|
|
|
|
- qCInfo(playerControllerLog) << (isVideo ? "Video" : "Audio") << " codec: " << codecCtx->codec->name;
|
|
|
|
|
- qCInfo(playerControllerLog) << " Type:" << codecCtx->codec_type << "ID:" << codecCtx->codec_id << "Tag:" << codecCtx->codec_tag;
|
|
|
|
|
|
|
+ qCInfo(playerControllerLog) << (isVideo ? "Video" : "Audio")
|
|
|
|
|
+ << " codec: " << codecCtx->codec->name;
|
|
|
|
|
+ qCInfo(playerControllerLog) << " Type:" << codecCtx->codec_type << "ID:" << codecCtx->codec_id
|
|
|
|
|
+ << "Tag:" << codecCtx->codec_tag;
|
|
|
|
|
|
|
|
if (isVideo) {
|
|
if (isVideo) {
|
|
|
- qCInfo(playerControllerLog) << " Dimensions: " << codecCtx->width << "x" << codecCtx->height;
|
|
|
|
|
|
|
+ qCInfo(playerControllerLog)
|
|
|
|
|
+ << " Dimensions: " << codecCtx->width << "x" << codecCtx->height;
|
|
|
} else {
|
|
} else {
|
|
|
- qCInfo(playerControllerLog) << " Sample rate: " << codecCtx->sample_rate << " Hz, Channels: " << codecCtx->ch_layout.nb_channels << ", Format: " << codecCtx->sample_fmt;
|
|
|
|
|
- qCInfo(playerControllerLog) << " Frame size: " << codecCtx->frame_size << ", Block align: " << codecCtx->block_align;
|
|
|
|
|
|
|
+ qCInfo(playerControllerLog) << " Sample rate: " << codecCtx->sample_rate
|
|
|
|
|
+ << " Hz, Channels: " << codecCtx->ch_layout.nb_channels
|
|
|
|
|
+ << ", Format: " << codecCtx->sample_fmt;
|
|
|
|
|
+ qCInfo(playerControllerLog) << " Frame size: " << codecCtx->frame_size
|
|
|
|
|
+ << ", Block align: " << codecCtx->block_align;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|