|
@@ -50,10 +50,17 @@ PlayerCoreV2::PlayerCoreV2(const SyncConfigV2& syncConfig)
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 创建队列
|
|
|
|
|
- m_packetQueue = av::utils::PacketQueueFactory::createStandardQueue(2000); // 增加队列大小
|
|
|
|
|
- if (!m_packetQueue) {
|
|
|
|
|
- Logger::instance().error("Failed to create packet queue");
|
|
|
|
|
|
|
+ // 创建分离的视频和音频包队列
|
|
|
|
|
+ m_videoPacketQueue = av::utils::PacketQueueFactory::createStandardQueue(1000); // 视频包队列
|
|
|
|
|
+ if (!m_videoPacketQueue) {
|
|
|
|
|
+ Logger::instance().error("Failed to create video packet queue");
|
|
|
|
|
+ setState(PlayerState::Error);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ m_audioPacketQueue = av::utils::PacketQueueFactory::createStandardQueue(1000); // 音频包队列
|
|
|
|
|
+ if (!m_audioPacketQueue) {
|
|
|
|
|
+ Logger::instance().error("Failed to create audio packet queue");
|
|
|
setState(PlayerState::Error);
|
|
setState(PlayerState::Error);
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
@@ -85,7 +92,7 @@ PlayerCoreV2::PlayerCoreV2(const SyncConfigV2& syncConfig)
|
|
|
handleSyncError(error, reason);
|
|
handleSyncError(error, reason);
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- m_synchronizer->setFrameDropCallback([this](av::utils::ClockType type, double pts) {
|
|
|
|
|
|
|
+ m_synchronizer->setFrameDropCallback([this](av::utils::ClockType type, int64_t pts) {
|
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
|
|
m_stats.droppedFrames++;
|
|
m_stats.droppedFrames++;
|
|
|
if (m_eventCallback) {
|
|
if (m_eventCallback) {
|
|
@@ -190,12 +197,8 @@ ErrorCode PlayerCoreV2::openFile(const std::string& filename) {
|
|
|
return ErrorCode::CODEC_OPEN_FAILED;
|
|
return ErrorCode::CODEC_OPEN_FAILED;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 初始化同步器
|
|
|
|
|
- if (m_synchronizer->initialize() != ErrorCode::SUCCESS) {
|
|
|
|
|
- Logger::instance().error("Failed to initialize synchronizer");
|
|
|
|
|
- setState(PlayerState::Error);
|
|
|
|
|
- return ErrorCode::SYNC_ERROR;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // 注意:同步器的初始化已经在openMediaFile()中完成,这里不需要重复调用
|
|
|
|
|
+ // 因为initialize()会重新选择主时钟,可能覆盖之前设置的流信息
|
|
|
|
|
|
|
|
setState(PlayerState::Stopped);
|
|
setState(PlayerState::Stopped);
|
|
|
|
|
|
|
@@ -376,7 +379,8 @@ ErrorCode PlayerCoreV2::stop() {
|
|
|
resetDecoders();
|
|
resetDecoders();
|
|
|
|
|
|
|
|
// 清空队列
|
|
// 清空队列
|
|
|
- if (m_packetQueue) m_packetQueue->clear();
|
|
|
|
|
|
|
+ if (m_videoPacketQueue) m_videoPacketQueue->clear();
|
|
|
|
|
+ if (m_audioPacketQueue) m_audioPacketQueue->clear();
|
|
|
if (m_videoFrameQueue) m_videoFrameQueue->clear();
|
|
if (m_videoFrameQueue) m_videoFrameQueue->clear();
|
|
|
if (m_audioFrameQueue) m_audioFrameQueue->clear();
|
|
if (m_audioFrameQueue) m_audioFrameQueue->clear();
|
|
|
|
|
|
|
@@ -463,7 +467,8 @@ PlaybackStats PlayerCoreV2::getStats() const {
|
|
|
stats.currentTime = getCurrentTime();
|
|
stats.currentTime = getCurrentTime();
|
|
|
|
|
|
|
|
// 更新队列大小
|
|
// 更新队列大小
|
|
|
- if (m_packetQueue) stats.queuedPackets = m_packetQueue->size();
|
|
|
|
|
|
|
+ if (m_videoPacketQueue) stats.queuedPackets += m_videoPacketQueue->size();
|
|
|
|
|
+ if (m_audioPacketQueue) stats.queuedPackets += m_audioPacketQueue->size();
|
|
|
if (m_videoFrameQueue) stats.queuedVideoFrames = m_videoFrameQueue->size();
|
|
if (m_videoFrameQueue) stats.queuedVideoFrames = m_videoFrameQueue->size();
|
|
|
if (m_audioFrameQueue) stats.queuedAudioFrames = m_audioFrameQueue->size();
|
|
if (m_audioFrameQueue) stats.queuedAudioFrames = m_audioFrameQueue->size();
|
|
|
|
|
|
|
@@ -705,6 +710,13 @@ bool PlayerCoreV2::openMediaFile(const std::string& filename) {
|
|
|
m_synchronizer->setStreamInfo(m_mediaInfo.hasAudio, m_mediaInfo.hasVideo);
|
|
m_synchronizer->setStreamInfo(m_mediaInfo.hasAudio, m_mediaInfo.hasVideo);
|
|
|
Logger::instance().info("Synchronizer stream info set: hasAudio=" + std::to_string(m_mediaInfo.hasAudio) +
|
|
Logger::instance().info("Synchronizer stream info set: hasAudio=" + std::to_string(m_mediaInfo.hasAudio) +
|
|
|
", hasVideo=" + std::to_string(m_mediaInfo.hasVideo));
|
|
", hasVideo=" + std::to_string(m_mediaInfo.hasVideo));
|
|
|
|
|
+
|
|
|
|
|
+ // 在设置流信息后初始化同步器,确保主时钟选择基于正确的流信息
|
|
|
|
|
+ if (m_synchronizer->initialize() != ErrorCode::SUCCESS) {
|
|
|
|
|
+ Logger::instance().error("Failed to initialize synchronizer");
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ Logger::instance().info("Synchronizer initialized with correct stream info");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
Logger::instance().info("Media file opened successfully: " + filename);
|
|
Logger::instance().info("Media file opened successfully: " + filename);
|
|
@@ -832,6 +844,12 @@ bool PlayerCoreV2::setupAudioDecoder() {
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 设置音频输出的同步器
|
|
|
|
|
+ if (m_audioOutput && m_synchronizer) {
|
|
|
|
|
+ m_audioOutput->setSynchronizer(std::shared_ptr<SynchronizerV2>(m_synchronizer.get(), [](SynchronizerV2*) {}));
|
|
|
|
|
+ Logger::instance().info("Audio output synchronizer set");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 释放解码器上下文
|
|
// 释放解码器上下文
|
|
|
avcodec_free_context(&codecContext);
|
|
avcodec_free_context(&codecContext);
|
|
|
|
|
|
|
@@ -1067,7 +1085,8 @@ void PlayerCoreV2::stopAllThreads() {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void PlayerCoreV2::flushBuffers() {
|
|
void PlayerCoreV2::flushBuffers() {
|
|
|
- if (m_packetQueue) m_packetQueue->clear();
|
|
|
|
|
|
|
+ if (m_videoPacketQueue) m_videoPacketQueue->clear();
|
|
|
|
|
+ if (m_audioPacketQueue) m_audioPacketQueue->clear();
|
|
|
if (m_videoFrameQueue) m_videoFrameQueue->clear();
|
|
if (m_videoFrameQueue) m_videoFrameQueue->clear();
|
|
|
if (m_audioFrameQueue) m_audioFrameQueue->clear();
|
|
if (m_audioFrameQueue) m_audioFrameQueue->clear();
|
|
|
}
|
|
}
|
|
@@ -1110,7 +1129,9 @@ void PlayerCoreV2::readThreadFunc() {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 检查队列是否已满
|
|
// 检查队列是否已满
|
|
|
- if (m_packetQueue && m_packetQueue->size() > 1000) {
|
|
|
|
|
|
|
+ bool videoQueueFull = m_videoPacketQueue && m_videoPacketQueue->size() > 500;
|
|
|
|
|
+ bool audioQueueFull = m_audioPacketQueue && m_audioPacketQueue->size() > 500;
|
|
|
|
|
+ if (videoQueueFull && audioQueueFull) {
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
|
continue;
|
|
continue;
|
|
|
}
|
|
}
|
|
@@ -1121,16 +1142,26 @@ void PlayerCoreV2::readThreadFunc() {
|
|
|
if (ret == AVERROR_EOF) {
|
|
if (ret == AVERROR_EOF) {
|
|
|
Logger::instance().info("End of file reached, sending EOF packets to queues");
|
|
Logger::instance().info("End of file reached, sending EOF packets to queues");
|
|
|
|
|
|
|
|
- // 向队列发送EOF标记,让解码线程知道文件结束
|
|
|
|
|
- if (m_packetQueue) {
|
|
|
|
|
- // 创建EOF标记包
|
|
|
|
|
|
|
+ // 向视频和音频队列分别发送EOF标记,让解码线程知道文件结束
|
|
|
|
|
+ if (m_videoPacketQueue && m_mediaInfo.hasVideo) {
|
|
|
AVPacket* eofPacket = av_packet_alloc();
|
|
AVPacket* eofPacket = av_packet_alloc();
|
|
|
if (eofPacket) {
|
|
if (eofPacket) {
|
|
|
eofPacket->data = nullptr;
|
|
eofPacket->data = nullptr;
|
|
|
eofPacket->size = 0;
|
|
eofPacket->size = 0;
|
|
|
eofPacket->stream_index = -1; // 特殊标记表示EOF
|
|
eofPacket->stream_index = -1; // 特殊标记表示EOF
|
|
|
- m_packetQueue->push(eofPacket);
|
|
|
|
|
- Logger::instance().info("EOF packet sent to queue");
|
|
|
|
|
|
|
+ m_videoPacketQueue->push(eofPacket);
|
|
|
|
|
+ Logger::instance().info("EOF packet sent to video queue");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (m_audioPacketQueue && m_mediaInfo.hasAudio) {
|
|
|
|
|
+ AVPacket* eofPacket = av_packet_alloc();
|
|
|
|
|
+ if (eofPacket) {
|
|
|
|
|
+ eofPacket->data = nullptr;
|
|
|
|
|
+ eofPacket->size = 0;
|
|
|
|
|
+ eofPacket->stream_index = -1; // 特殊标记表示EOF
|
|
|
|
|
+ m_audioPacketQueue->push(eofPacket);
|
|
|
|
|
+ Logger::instance().info("EOF packet sent to audio queue");
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
break;
|
|
break;
|
|
@@ -1141,14 +1172,23 @@ void PlayerCoreV2::readThreadFunc() {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 将数据包放入队列
|
|
|
|
|
- if (packet->stream_index == m_mediaInfo.videoStreamIndex ||
|
|
|
|
|
- packet->stream_index == m_mediaInfo.audioStreamIndex) {
|
|
|
|
|
-
|
|
|
|
|
- if (m_packetQueue) {
|
|
|
|
|
|
|
+ // 根据流索引将数据包分发到对应队列
|
|
|
|
|
+ if (packet->stream_index == m_mediaInfo.videoStreamIndex) {
|
|
|
|
|
+ // 视频包放入视频队列
|
|
|
|
|
+ if (m_videoPacketQueue && (!videoQueueFull || m_videoPacketQueue->size() < 500)) {
|
|
|
AVPacket* packetCopy = av_packet_alloc();
|
|
AVPacket* packetCopy = av_packet_alloc();
|
|
|
if (packetCopy && av_packet_ref(packetCopy, packet) == 0) {
|
|
if (packetCopy && av_packet_ref(packetCopy, packet) == 0) {
|
|
|
- m_packetQueue->push(packetCopy);
|
|
|
|
|
|
|
+ m_videoPacketQueue->push(packetCopy);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ av_packet_free(&packetCopy);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (packet->stream_index == m_mediaInfo.audioStreamIndex) {
|
|
|
|
|
+ // 音频包放入音频队列
|
|
|
|
|
+ if (m_audioPacketQueue && (!audioQueueFull || m_audioPacketQueue->size() < 500)) {
|
|
|
|
|
+ AVPacket* packetCopy = av_packet_alloc();
|
|
|
|
|
+ if (packetCopy && av_packet_ref(packetCopy, packet) == 0) {
|
|
|
|
|
+ m_audioPacketQueue->push(packetCopy);
|
|
|
} else {
|
|
} else {
|
|
|
av_packet_free(&packetCopy);
|
|
av_packet_free(&packetCopy);
|
|
|
}
|
|
}
|
|
@@ -1168,15 +1208,15 @@ void PlayerCoreV2::videoDecodeThreadFunc() {
|
|
|
int packetCount = 0;
|
|
int packetCount = 0;
|
|
|
int frameCount = 0;
|
|
int frameCount = 0;
|
|
|
while (!m_threadsShouldStop) {
|
|
while (!m_threadsShouldStop) {
|
|
|
- if (!m_packetQueue || !m_videoFrameQueue || !m_videoDecoder) {
|
|
|
|
|
|
|
+ if (!m_videoPacketQueue || !m_videoFrameQueue || !m_videoDecoder) {
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
|
continue;
|
|
continue;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 从包队列获取视频包
|
|
|
|
|
|
|
+ // 从视频包队列获取包
|
|
|
AVPacket* packet = nullptr;
|
|
AVPacket* packet = nullptr;
|
|
|
while (!m_threadsShouldStop && !packet) {
|
|
while (!m_threadsShouldStop && !packet) {
|
|
|
- packet = m_packetQueue->pop();
|
|
|
|
|
|
|
+ packet = m_videoPacketQueue->pop();
|
|
|
if (packet) {
|
|
if (packet) {
|
|
|
// 检查是否是EOF标记
|
|
// 检查是否是EOF标记
|
|
|
if (packet->stream_index == -1 && packet->data == nullptr) {
|
|
if (packet->stream_index == -1 && packet->data == nullptr) {
|
|
@@ -1200,13 +1240,6 @@ void PlayerCoreV2::videoDecodeThreadFunc() {
|
|
|
Logger::instance().info("Video decode thread finishing due to EOF");
|
|
Logger::instance().info("Video decode thread finishing due to EOF");
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- if (packet->stream_index != m_mediaInfo.videoStreamIndex) {
|
|
|
|
|
- Logger::instance().debug("Skipping non-video packet, stream_index=" + std::to_string(packet->stream_index) +
|
|
|
|
|
- ", expected=" + std::to_string(m_mediaInfo.videoStreamIndex));
|
|
|
|
|
- av_packet_free(&packet);
|
|
|
|
|
- packet = nullptr;
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|
|
|
if (!packet) {
|
|
if (!packet) {
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
|
@@ -1240,12 +1273,9 @@ void PlayerCoreV2::videoDecodeThreadFunc() {
|
|
|
", height=" + std::to_string(framePtr->height) +
|
|
", height=" + std::to_string(framePtr->height) +
|
|
|
", pts=" + std::to_string(framePtr->pts));
|
|
", pts=" + std::to_string(framePtr->pts));
|
|
|
|
|
|
|
|
- // 设置帧的时间戳
|
|
|
|
|
|
|
+ // 设置帧的时间戳(保持原始PTS,在播放时再转换)
|
|
|
if (framePtr->pts != AV_NOPTS_VALUE) {
|
|
if (framePtr->pts != AV_NOPTS_VALUE) {
|
|
|
- AVStream* stream = m_formatContext->streams[m_mediaInfo.videoStreamIndex];
|
|
|
|
|
- double pts = framePtr->pts * av_q2d(stream->time_base) * 1000000; // 转换为微秒
|
|
|
|
|
- framePtr->pts = static_cast<int64_t>(pts);
|
|
|
|
|
- Logger::instance().debug("Frame PTS converted to: " + std::to_string(framePtr->pts) + " microseconds");
|
|
|
|
|
|
|
+ Logger::instance().debug("Frame PTS: " + std::to_string(framePtr->pts));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 将帧放入队列
|
|
// 将帧放入队列
|
|
@@ -1276,15 +1306,15 @@ void PlayerCoreV2::audioDecodeThreadFunc() {
|
|
|
int packetCount = 0;
|
|
int packetCount = 0;
|
|
|
int frameCount = 0;
|
|
int frameCount = 0;
|
|
|
while (!m_threadsShouldStop) {
|
|
while (!m_threadsShouldStop) {
|
|
|
- if (!m_packetQueue || !m_audioFrameQueue || !m_audioDecoder) {
|
|
|
|
|
|
|
+ if (!m_audioPacketQueue || !m_audioFrameQueue || !m_audioDecoder) {
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
|
continue;
|
|
continue;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 从包队列获取音频包
|
|
|
|
|
|
|
+ // 从音频包队列获取包
|
|
|
AVPacket* packet = nullptr;
|
|
AVPacket* packet = nullptr;
|
|
|
while (!m_threadsShouldStop && !packet) {
|
|
while (!m_threadsShouldStop && !packet) {
|
|
|
- packet = m_packetQueue->pop();
|
|
|
|
|
|
|
+ packet = m_audioPacketQueue->pop();
|
|
|
if (packet) {
|
|
if (packet) {
|
|
|
// 检查是否是EOF标记
|
|
// 检查是否是EOF标记
|
|
|
if (packet->stream_index == -1 && packet->data == nullptr) {
|
|
if (packet->stream_index == -1 && packet->data == nullptr) {
|
|
@@ -1307,13 +1337,6 @@ void PlayerCoreV2::audioDecodeThreadFunc() {
|
|
|
Logger::instance().info("Audio decode thread finishing due to EOF");
|
|
Logger::instance().info("Audio decode thread finishing due to EOF");
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- if (packet->stream_index != m_mediaInfo.audioStreamIndex) {
|
|
|
|
|
- Logger::instance().debug("Skipping non-audio packet, stream_index=" + std::to_string(packet->stream_index) +
|
|
|
|
|
- ", expected=" + std::to_string(m_mediaInfo.audioStreamIndex));
|
|
|
|
|
- av_packet_free(&packet);
|
|
|
|
|
- packet = nullptr;
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|
|
|
if (!packet) {
|
|
if (!packet) {
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
|
@@ -1346,12 +1369,9 @@ void PlayerCoreV2::audioDecodeThreadFunc() {
|
|
|
", nb_samples=" + std::to_string(framePtr->nb_samples) +
|
|
", nb_samples=" + std::to_string(framePtr->nb_samples) +
|
|
|
", pts=" + std::to_string(framePtr->pts));
|
|
", pts=" + std::to_string(framePtr->pts));
|
|
|
|
|
|
|
|
- // 设置帧的时间戳
|
|
|
|
|
|
|
+ // 设置帧的时间戳(保持原始PTS,在播放时再转换)
|
|
|
if (framePtr->pts != AV_NOPTS_VALUE) {
|
|
if (framePtr->pts != AV_NOPTS_VALUE) {
|
|
|
- AVStream* stream = m_formatContext->streams[m_mediaInfo.audioStreamIndex];
|
|
|
|
|
- double pts = framePtr->pts * av_q2d(stream->time_base) * 1000000; // 转换为微秒
|
|
|
|
|
- framePtr->pts = static_cast<int64_t>(pts);
|
|
|
|
|
- Logger::instance().debug("Frame PTS converted to: " + std::to_string(framePtr->pts) + " microseconds");
|
|
|
|
|
|
|
+ Logger::instance().debug("Audio Frame PTS: " + std::to_string(framePtr->pts));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 将帧放入队列
|
|
// 将帧放入队列
|
|
@@ -1399,13 +1419,25 @@ void PlayerCoreV2::videoPlayThreadFunc() {
|
|
|
|
|
|
|
|
Logger::instance().debug("Video play thread got frame, pts=" + std::to_string(frame->pts));
|
|
Logger::instance().debug("Video play thread got frame, pts=" + std::to_string(frame->pts));
|
|
|
|
|
|
|
|
- // 更新视频时钟
|
|
|
|
|
|
|
+ // 获取视频流的时间基准
|
|
|
|
|
+ double timeBase = 0.0;
|
|
|
if (frame->pts != AV_NOPTS_VALUE) {
|
|
if (frame->pts != AV_NOPTS_VALUE) {
|
|
|
- m_synchronizer->setVideoClock(frame->pts / 1000000.0); // 转换为秒
|
|
|
|
|
|
|
+ AVStream* videoStream = m_formatContext->streams[m_mediaInfo.videoStreamIndex];
|
|
|
|
|
+ timeBase = av_q2d(videoStream->time_base);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 检查是否应该显示这一帧
|
|
|
|
|
- auto decision = m_synchronizer->shouldDisplayVideoFrame(frame->pts / 1000000.0);
|
|
|
|
|
|
|
+ // 检查是否应该显示这一帧 (直接传递原始PTS和时间基准)
|
|
|
|
|
+ auto decision = m_synchronizer->shouldDisplayVideoFrame(frame->pts, timeBase);
|
|
|
|
|
+
|
|
|
|
|
+ // 记录视频帧决策日志
|
|
|
|
|
+ Logger::instance().debug("Video frame decision: action=" + std::to_string(static_cast<int>(decision.action)) +
|
|
|
|
|
+ ", delay=" + std::to_string(decision.delay) +
|
|
|
|
|
+ ", syncError=" + std::to_string(decision.syncError));
|
|
|
|
|
+
|
|
|
|
|
+ // 只有在决定显示帧时才更新视频时钟
|
|
|
|
|
+ if (decision.action == av::utils::FrameAction::DISPLAY && frame->pts != AV_NOPTS_VALUE) {
|
|
|
|
|
+ m_synchronizer->setVideoClock(frame->pts, timeBase);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
// 创建智能指针管理帧内存
|
|
// 创建智能指针管理帧内存
|
|
|
AVFramePtr framePtr(frame);
|
|
AVFramePtr framePtr(frame);
|
|
@@ -1417,10 +1449,13 @@ void PlayerCoreV2::videoPlayThreadFunc() {
|
|
|
m_openGLVideoRenderer->Render(framePtr.get());
|
|
m_openGLVideoRenderer->Render(framePtr.get());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 计算下一帧的延迟
|
|
|
|
|
|
|
+ // 计算下一帧的延迟 - 使用FFmpeg的精确延迟
|
|
|
if (decision.delay > 0) {
|
|
if (decision.delay > 0) {
|
|
|
- auto delayMs = static_cast<int>(decision.delay * 1000);
|
|
|
|
|
- std::this_thread::sleep_for(std::chrono::milliseconds(delayMs));
|
|
|
|
|
|
|
+ // 限制最大延迟时间,避免异常情况
|
|
|
|
|
+ const double MAX_DELAY = 0.1; // 100ms
|
|
|
|
|
+ double actualDelay = std::min(decision.delay, MAX_DELAY);
|
|
|
|
|
+ int delayUs = static_cast<int>(actualDelay * 1000000);
|
|
|
|
|
+ av_usleep(delayUs);
|
|
|
}
|
|
}
|
|
|
break;
|
|
break;
|
|
|
|
|
|
|
@@ -1437,11 +1472,15 @@ void PlayerCoreV2::videoPlayThreadFunc() {
|
|
|
break;
|
|
break;
|
|
|
|
|
|
|
|
case av::utils::FrameAction::DELAY:
|
|
case av::utils::FrameAction::DELAY:
|
|
|
- // 延迟显示
|
|
|
|
|
- auto delayMs = static_cast<int>(decision.delay * 1000);
|
|
|
|
|
- std::this_thread::sleep_for(std::chrono::milliseconds(delayMs));
|
|
|
|
|
- if (m_openGLVideoRenderer) {
|
|
|
|
|
- m_openGLVideoRenderer->Render(framePtr.get());
|
|
|
|
|
|
|
+ // 延迟显示 - 使用FFmpeg的精确延迟
|
|
|
|
|
+ {
|
|
|
|
|
+ const double MAX_DELAY = 0.1; // 100ms
|
|
|
|
|
+ double actualDelay = std::min(decision.delay, MAX_DELAY);
|
|
|
|
|
+ int delayUs = static_cast<int>(actualDelay * 1000000);
|
|
|
|
|
+ av_usleep(delayUs);
|
|
|
|
|
+ if (m_openGLVideoRenderer) {
|
|
|
|
|
+ m_openGLVideoRenderer->Render(framePtr.get());
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
@@ -1491,13 +1530,24 @@ void PlayerCoreV2::audioPlayThreadFunc() {
|
|
|
", pts=" + std::to_string(frame->pts) +
|
|
", pts=" + std::to_string(frame->pts) +
|
|
|
", nb_samples=" + std::to_string(frame->nb_samples));
|
|
", nb_samples=" + std::to_string(frame->nb_samples));
|
|
|
|
|
|
|
|
- // 更新音频时钟
|
|
|
|
|
|
|
+ // 获取音频流的时间基准
|
|
|
|
|
+ double timeBase = 0.0;
|
|
|
if (frame->pts != AV_NOPTS_VALUE) {
|
|
if (frame->pts != AV_NOPTS_VALUE) {
|
|
|
- m_synchronizer->setAudioClock(frame->pts / 1000000.0); // 转换为秒
|
|
|
|
|
|
|
+ AVStream* audioStream = m_formatContext->streams[m_mediaInfo.audioStreamIndex];
|
|
|
|
|
+ timeBase = av_q2d(audioStream->time_base);
|
|
|
|
|
+
|
|
|
|
|
+ // 添加调试信息
|
|
|
|
|
+ Logger::instance().debug("Audio PTS info: pts=" + std::to_string(frame->pts) +
|
|
|
|
|
+ ", time_base=" + std::to_string(timeBase));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 检查是否应该播放这一帧
|
|
|
|
|
- auto decision = m_synchronizer->shouldPlayAudioFrame(frame->pts / 1000000.0);
|
|
|
|
|
|
|
+ // 检查是否应该播放这一帧 (直接传递原始PTS和时间基准)
|
|
|
|
|
+ auto decision = m_synchronizer->shouldPlayAudioFrame(frame->pts, timeBase);
|
|
|
|
|
+
|
|
|
|
|
+ // 只有在决定播放帧时才更新音频时钟
|
|
|
|
|
+ if (decision.action == av::utils::FrameAction::DISPLAY && frame->pts != AV_NOPTS_VALUE) {
|
|
|
|
|
+ m_synchronizer->setAudioClock(frame->pts, timeBase);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
Logger::instance().debug("Audio frame decision: action=" + std::to_string(static_cast<int>(decision.action)) +
|
|
Logger::instance().debug("Audio frame decision: action=" + std::to_string(static_cast<int>(decision.action)) +
|
|
|
", delay=" + std::to_string(decision.delay));
|
|
", delay=" + std::to_string(decision.delay));
|
|
@@ -1512,12 +1562,20 @@ void PlayerCoreV2::audioPlayThreadFunc() {
|
|
|
Logger::instance().debug("Writing audio frame to output device");
|
|
Logger::instance().debug("Writing audio frame to output device");
|
|
|
bool writeResult = m_audioOutput->writeFrame(framePtr);
|
|
bool writeResult = m_audioOutput->writeFrame(framePtr);
|
|
|
Logger::instance().debug("Audio frame write result: " + std::to_string(writeResult));
|
|
Logger::instance().debug("Audio frame write result: " + std::to_string(writeResult));
|
|
|
|
|
+
|
|
|
|
|
+ // 如果写入失败,等待一段时间避免快速循环
|
|
|
|
|
+ if (!writeResult) {
|
|
|
|
|
+ Logger::instance().warning("Audio frame write failed, waiting before next frame");
|
|
|
|
|
+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 如果需要延迟
|
|
|
|
|
|
|
+ // 如果需要延迟 - 使用FFmpeg的精确延迟
|
|
|
if (decision.delay > 0) {
|
|
if (decision.delay > 0) {
|
|
|
- auto delayMs = static_cast<int>(decision.delay * 1000);
|
|
|
|
|
- std::this_thread::sleep_for(std::chrono::milliseconds(delayMs));
|
|
|
|
|
|
|
+ const double MAX_DELAY = 0.1; // 100ms
|
|
|
|
|
+ double actualDelay = std::min(decision.delay, MAX_DELAY);
|
|
|
|
|
+ int delayUs = static_cast<int>(actualDelay * 1000000);
|
|
|
|
|
+ av_usleep(delayUs);
|
|
|
}
|
|
}
|
|
|
break;
|
|
break;
|
|
|
|
|
|
|
@@ -1532,17 +1590,32 @@ void PlayerCoreV2::audioPlayThreadFunc() {
|
|
|
Logger::instance().debug("Duplicating audio frame");
|
|
Logger::instance().debug("Duplicating audio frame");
|
|
|
bool writeResult = m_audioOutput->writeFrame(framePtr);
|
|
bool writeResult = m_audioOutput->writeFrame(framePtr);
|
|
|
Logger::instance().debug("Audio frame duplicate write result: " + std::to_string(writeResult));
|
|
Logger::instance().debug("Audio frame duplicate write result: " + std::to_string(writeResult));
|
|
|
|
|
+
|
|
|
|
|
+ // 如果写入失败,等待一段时间避免快速循环
|
|
|
|
|
+ if (!writeResult) {
|
|
|
|
|
+ Logger::instance().warning("Audio frame duplicate write failed, waiting before next frame");
|
|
|
|
|
+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
break;
|
|
break;
|
|
|
|
|
|
|
|
case av::utils::FrameAction::DELAY:
|
|
case av::utils::FrameAction::DELAY:
|
|
|
- // 延迟播放
|
|
|
|
|
- auto delayMs = static_cast<int>(decision.delay * 1000);
|
|
|
|
|
- std::this_thread::sleep_for(std::chrono::milliseconds(delayMs));
|
|
|
|
|
|
|
+ // 延迟播放 - 使用FFmpeg的精确延迟
|
|
|
{
|
|
{
|
|
|
|
|
+ const double MAX_DELAY = 0.1; // 100ms
|
|
|
|
|
+ double actualDelay = std::min(decision.delay, MAX_DELAY);
|
|
|
|
|
+ int delayUs = static_cast<int>(actualDelay * 1000000);
|
|
|
|
|
+ av_usleep(delayUs);
|
|
|
|
|
+
|
|
|
Logger::instance().debug("Writing delayed audio frame to output device");
|
|
Logger::instance().debug("Writing delayed audio frame to output device");
|
|
|
bool writeResult = m_audioOutput->writeFrame(framePtr);
|
|
bool writeResult = m_audioOutput->writeFrame(framePtr);
|
|
|
Logger::instance().debug("Audio frame delayed write result: " + std::to_string(writeResult));
|
|
Logger::instance().debug("Audio frame delayed write result: " + std::to_string(writeResult));
|
|
|
|
|
+
|
|
|
|
|
+ // 如果写入失败,等待一段时间避免快速循环
|
|
|
|
|
+ if (!writeResult) {
|
|
|
|
|
+ Logger::instance().warning("Audio frame delayed write failed, waiting before next frame");
|
|
|
|
|
+ av_usleep(10000); // 10ms
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|