| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554 |
- #include "audio_output.h"
- #include "../base/logger.h"
- #include "../utils/utils_synchronizer_v2.h"
- #include <QAudioDeviceInfo>
- #include <QDebug>
- #include <algorithm>
- #include <cstring>
- extern "C" {
- #include <libavutil/channel_layout.h>
- #include <libavutil/avutil.h>
- #include <libavutil/time.h>
- #include <libswresample/swresample.h>
- }
- namespace av {
- namespace player {
- AudioOutput::AudioOutput(QObject* parent)
- : QObject(parent)
- , m_audioOutput(nullptr)
- , m_audioDevice(nullptr)
- , m_sampleRate(44100)
- , m_channels(2)
- , m_inputFormat(AV_SAMPLE_FMT_S16)
- , m_swrContext(nullptr)
- , m_needResampling(false)
- , m_volume(1.0)
- , m_playbackSpeed(1.0)
- , m_lastAudioPts(0.0)
- , m_initialized(false)
- , m_playing(false)
- {
- }
- AudioOutput::~AudioOutput()
- {
- stop();
- cleanupResampler();
-
- if (m_audioOutput) {
- delete m_audioOutput;
- m_audioOutput = nullptr;
- }
- }
- bool AudioOutput::initialize(int sampleRate, int channels, AVSampleFormat sampleFormat)
- {
- QMutexLocker locker(&m_mutex);
-
- if (m_initialized) {
- av::Logger::instance().warning("Audio output already initialized");
- return true;
- }
-
- m_sampleRate = sampleRate;
- m_channels = channels;
- m_inputFormat = sampleFormat;
-
- // 设置Qt音频格式
- m_audioFormat.setSampleRate(sampleRate);
- m_audioFormat.setChannelCount(channels);
- m_audioFormat.setSampleSize(16); // 输出16位
- m_audioFormat.setCodec("audio/pcm");
- m_audioFormat.setByteOrder(QAudioFormat::LittleEndian);
- m_audioFormat.setSampleType(QAudioFormat::SignedInt);
-
- // 检查设备是否支持该格式
- QAudioDeviceInfo deviceInfo = QAudioDeviceInfo::defaultOutputDevice();
- if (!deviceInfo.isFormatSupported(m_audioFormat)) {
- av::Logger::instance().warning("Audio format not supported, using nearest format");
- m_audioFormat = deviceInfo.nearestFormat(m_audioFormat);
-
- // 记录实际使用的格式
- av::Logger::instance().info("Using audio format: " +
- std::to_string(m_audioFormat.sampleRate()) + "Hz, " +
- std::to_string(m_audioFormat.channelCount()) + "ch, " +
- std::to_string(m_audioFormat.sampleSize()) + "bit");
- }
-
- // 创建音频输出设备
- m_audioOutput = new QAudioOutput(m_audioFormat, this);
- if (!m_audioOutput) {
- av::Logger::instance().error("Failed to create audio output device");
- return false;
- }
-
- // 设置缓冲区大小
- m_audioOutput->setBufferSize(32768); // 32KB缓冲区
-
- // 连接状态变化信号
- connect(m_audioOutput, &QAudioOutput::stateChanged,
- this, &AudioOutput::onStateChanged);
-
- // 记录音频格式信息
- av::Logger::instance().info("Audio format: " +
- std::to_string(m_audioFormat.sampleRate()) + "Hz, " +
- std::to_string(m_audioFormat.channelCount()) + "ch, " +
- std::to_string(m_audioFormat.sampleSize()) + "bit, " +
- m_audioFormat.codec().toStdString());
-
- // 初始化重采样器(如果需要)
- if (!initResampler()) {
- av::Logger::instance().error("Failed to initialize audio resampler");
- return false;
- }
-
- m_initialized = true;
- av::Logger::instance().info("Audio output initialized successfully");
-
- return true;
- }
- void AudioOutput::start()
- {
- QMutexLocker locker(&m_mutex);
-
- if (!m_initialized || !m_audioOutput) {
- av::Logger::instance().error("Audio output not initialized");
- return;
- }
-
- if (m_playing) {
- return;
- }
-
- av::Logger::instance().debug("Starting audio output device...");
- av::Logger::instance().debug("Audio output state before start: " + std::to_string(m_audioOutput->state()));
-
- // 检查音频格式
- QAudioDeviceInfo deviceInfo = QAudioDeviceInfo::defaultOutputDevice();
- av::Logger::instance().debug("Default audio device: " + deviceInfo.deviceName().toStdString());
- av::Logger::instance().debug("Audio format supported: " + std::to_string(deviceInfo.isFormatSupported(m_audioFormat)));
-
- // 确保音频输出设备处于正确状态
- if (m_audioOutput->state() == QAudio::StoppedState) {
- av::Logger::instance().debug("Resetting audio output device");
- m_audioOutput->reset();
- // 给设备一点时间重置
- std::this_thread::sleep_for(std::chrono::milliseconds(50));
- }
-
- // 如果设备不支持当前格式,尝试使用最接近的格式
- if (!deviceInfo.isFormatSupported(m_audioFormat)) {
- av::Logger::instance().warning("Audio format not supported, using nearest format");
- QAudioFormat nearestFormat = deviceInfo.nearestFormat(m_audioFormat);
- // m_audioOutput->setFormat(nearestFormat);
- av::Logger::instance().info("Using nearest format: " +
- std::to_string(nearestFormat.sampleRate()) + "Hz, " +
- std::to_string(nearestFormat.channelCount()) + "ch, " +
- std::to_string(nearestFormat.sampleSize()) + "bit");
- }
-
- // 尝试多次启动音频设备
- int retryCount = 0;
- const int maxRetries = 3;
- bool deviceStarted = false;
-
- while (retryCount < maxRetries && !deviceStarted) {
- if (retryCount > 0) {
- av::Logger::instance().info("Retrying audio device start (attempt " + std::to_string(retryCount + 1) + "/" + std::to_string(maxRetries) + ")");
-
- // 重新创建音频输出设备
- if (m_audioOutput) {
- delete m_audioOutput;
- m_audioOutput = nullptr;
- }
-
- m_audioOutput = new QAudioOutput(m_audioFormat, this);
- if (!m_audioOutput) {
- av::Logger::instance().error("Failed to recreate audio output device");
- return;
- }
-
- m_audioOutput->setBufferSize(32768);
- connect(m_audioOutput, &QAudioOutput::stateChanged, this, &AudioOutput::onStateChanged);
-
- // 等待一段时间再重试
- std::this_thread::sleep_for(std::chrono::milliseconds(200));
- }
-
- m_audioDevice = m_audioOutput->start();
- if (!m_audioDevice) {
- av::Logger::instance().warning("Failed to start audio output device (attempt " + std::to_string(retryCount + 1) + ")");
- retryCount++;
- continue;
- }
-
- av::Logger::instance().debug("Audio device started, waiting for active state...");
-
- // 写入一些静音数据来激活设备
- QByteArray silentData(1024, 0); // 1KB的静音数据
- m_audioDevice->write(silentData);
-
- // 等待音频设备进入活动状态
- int maxWaitMs = 1000; // 减少等待时间到1秒
- int waitMs = 0;
- while (m_audioOutput->state() != QAudio::ActiveState && waitMs < maxWaitMs) {
- std::this_thread::sleep_for(std::chrono::milliseconds(50));
- waitMs += 50;
-
- if (waitMs % 200 == 0) {
- av::Logger::instance().debug("Audio output state: " + std::to_string(m_audioOutput->state()) +
- " (waited " + std::to_string(waitMs) + "ms)");
- }
- }
-
- if (m_audioOutput->state() == QAudio::ActiveState) {
- deviceStarted = true;
- av::Logger::instance().info("Audio device successfully entered active state");
- } else {
- av::Logger::instance().warning("Audio device failed to enter active state: " + std::to_string(m_audioOutput->state()) + " (attempt " + std::to_string(retryCount + 1) + ")");
- m_audioDevice = nullptr;
- retryCount++;
- }
- }
-
- if (!deviceStarted) {
- av::Logger::instance().error("Failed to start audio device after " + std::to_string(maxRetries) + " attempts");
- return;
- }
-
- av::Logger::instance().debug("Audio device entered active state");
-
- // 初始化并启动Synchronizer
- if (!m_synchronizer) {
- av::utils::SyncConfigV2 syncConfig;
- syncConfig.syncStrategy = av::utils::SyncStrategy::AUDIO_MASTER;
- syncConfig.audioSyncThreshold = 0.04; // 40ms,与AVPlayer2一致
- syncConfig.videoSyncThreshold = 0.04;
- m_synchronizer = std::make_shared<av::utils::SynchronizerV2>(syncConfig);
- m_synchronizer->initialize();
- m_synchronizer->start();
- }
- m_playing = true;
- av::Logger::instance().info("Audio output started successfully");
- }
- void AudioOutput::stop()
- {
- QMutexLocker locker(&m_mutex);
-
- if (!m_playing || !m_audioOutput) {
- return;
- }
-
- m_audioOutput->stop();
- m_audioDevice = nullptr;
- m_playing = false;
-
- // 停止Synchronizer
- if (m_synchronizer) {
- m_synchronizer->stop();
- }
-
- av::Logger::instance().info("Audio output stopped");
- }
- void AudioOutput::pause()
- {
- QMutexLocker locker(&m_mutex);
-
- if (!m_playing || !m_audioOutput) {
- return;
- }
-
- m_audioOutput->suspend();
- av::Logger::instance().info("Audio output paused");
- }
- void AudioOutput::resume()
- {
- QMutexLocker locker(&m_mutex);
-
- if (!m_playing || !m_audioOutput) {
- return;
- }
-
- m_audioOutput->resume();
- av::Logger::instance().info("Audio output resumed");
- }
- bool AudioOutput::writeFrame(const AVFramePtr& frame)
- {
- if (!frame || !m_playing || !m_audioDevice) {
- av::Logger::instance().debug("Audio write failed: frame=" + std::to_string(frame != nullptr) +
- ", playing=" + std::to_string(m_playing) + ", device=" + std::to_string(m_audioDevice != nullptr));
- return false;
- }
-
- // 检查音频设备状态
- if (m_audioOutput && m_audioOutput->state() != QAudio::ActiveState) {
- av::Logger::instance().warning("Audio device not in active state: " + std::to_string(m_audioOutput->state()));
- return false;
- }
-
- // 使用Synchronizer进行时间同步
- if (frame->pts != AV_NOPTS_VALUE && m_synchronizer) {
- // 计算音频PTS(转换为秒)
- double audioPts = (double)frame->pts / AV_TIME_BASE;
-
- // 更新音频时钟
- m_synchronizer->setAudioClock(audioPts);
-
- // 计算同步延迟
- double delay = 0.0;
- ErrorCode result = m_synchronizer->shouldPlayAudioFrame(audioPts).action == av::utils::FrameAction::DISPLAY ? ErrorCode::SUCCESS : ErrorCode::PROCESSING_ERROR;
- delay = m_synchronizer->shouldPlayAudioFrame(audioPts).delay;
-
- if (result == ErrorCode::SUCCESS && delay > 0) {
- // 应用播放速度控制
- delay = delay / m_playbackSpeed;
-
- // 限制最大延迟时间,避免异常情况
- const double MAX_DELAY = 0.1; // 100ms
- if (delay > 0 && delay < MAX_DELAY) {
- int delayUs = static_cast<int>(delay * 1000000);
- av_usleep(delayUs);
- }
- }
-
- m_lastAudioPts = audioPts;
- }
-
- // 转换音频帧格式
- QByteArray audioData = convertFrame(frame);
- if (audioData.isEmpty()) {
- av::Logger::instance().warning("Audio frame conversion failed");
- return false;
- }
-
- // 应用音量控制
- applyVolume(audioData);
-
- // 写入音频设备 - 添加异常处理
- try {
- qint64 bytesWritten = m_audioDevice->write(audioData);
- if (bytesWritten != audioData.size()) {
- av::Logger::instance().warning("Audio write incomplete: " +
- std::to_string(bytesWritten) + "/" + std::to_string(audioData.size()));
- return false;
- }
-
- av::Logger::instance().debug("Audio frame written successfully: " +
- std::to_string(audioData.size()) + " bytes");
- return true;
- } catch (const std::exception& e) {
- av::Logger::instance().error("Exception during audio write: " + std::string(e.what()));
- return false;
- } catch (...) {
- av::Logger::instance().error("Unknown exception during audio write");
- return false;
- }
- }
- void AudioOutput::setVolume(double volume)
- {
- m_volume = std::clamp(volume, 0.0, 1.0);
-
- if (m_audioOutput) {
- m_audioOutput->setVolume(m_volume);
- }
- }
- double AudioOutput::getVolume() const
- {
- return m_volume;
- }
- void AudioOutput::setPlaybackSpeed(double speed)
- {
- speed = std::max(0.1, std::min(4.0, speed));
- m_playbackSpeed = speed;
-
- // 同时更新Synchronizer的播放速度
- if (m_synchronizer) {
- m_synchronizer->setPlaybackSpeed(speed);
- }
-
- av::Logger::instance().debug("Audio playback speed set to: " + std::to_string(speed));
- }
- double AudioOutput::getPlaybackSpeed() const
- {
- return m_playbackSpeed;
- }
- void AudioOutput::flush()
- {
- QMutexLocker locker(&m_mutex);
-
- if (m_audioOutput) {
- m_audioOutput->reset();
- }
- }
- int AudioOutput::getBufferSize() const
- {
- if (m_audioOutput) {
- return m_audioOutput->bufferSize() * 1000 /
- (m_audioFormat.sampleRate() * m_audioFormat.channelCount() *
- m_audioFormat.sampleSize() / 8);
- }
- return 0;
- }
- bool AudioOutput::isPlaying() const
- {
- return m_playing;
- }
- void AudioOutput::onStateChanged(QAudio::State state)
- {
- switch (state) {
- case QAudio::ActiveState:
- av::Logger::instance().debug("Audio output state: Active");
- break;
- case QAudio::SuspendedState:
- av::Logger::instance().debug("Audio output state: Suspended");
- break;
- case QAudio::StoppedState:
- av::Logger::instance().debug("Audio output state: Stopped");
- break;
- case QAudio::IdleState:
- av::Logger::instance().debug("Audio output state: Idle");
- break;
- }
- }
- bool AudioOutput::initResampler()
- {
- // 检查是否需要重采样
- bool needSampleRateConversion = (m_audioFormat.sampleRate() != m_sampleRate);
- bool needChannelConversion = (m_audioFormat.channelCount() != m_channels);
- bool needFormatConversion = (m_inputFormat != AV_SAMPLE_FMT_S16);
-
- m_needResampling = needSampleRateConversion || needChannelConversion || needFormatConversion;
-
- if (!m_needResampling) {
- av::Logger::instance().info("No audio resampling needed");
- return true;
- }
-
- // 创建重采样上下文
- m_swrContext = swr_alloc();
- if (!m_swrContext) {
- av::Logger::instance().error("Failed to allocate resampler context");
- return false;
- }
-
- // 设置输入参数
- AVChannelLayout inputLayout;
- av_channel_layout_default(&inputLayout, m_channels);
-
- // 设置输出参数
- AVChannelLayout outputLayout;
- av_channel_layout_default(&outputLayout, m_audioFormat.channelCount());
-
- // 配置重采样器
- int ret = swr_alloc_set_opts2(&m_swrContext,
- &outputLayout, AV_SAMPLE_FMT_S16, m_audioFormat.sampleRate(),
- &inputLayout, m_inputFormat, m_sampleRate,
- 0, nullptr);
-
- if (ret < 0) {
- av::Logger::instance().error("Failed to set resampler options");
- swr_free(&m_swrContext);
- return false;
- }
-
- // 初始化重采样器
- ret = swr_init(m_swrContext);
- if (ret < 0) {
- av::Logger::instance().error("Failed to initialize resampler");
- swr_free(&m_swrContext);
- return false;
- }
-
- av::Logger::instance().info("Audio resampler initialized successfully");
- return true;
- }
- void AudioOutput::cleanupResampler()
- {
- if (m_swrContext) {
- swr_free(&m_swrContext);
- m_swrContext = nullptr;
- }
- }
- QByteArray AudioOutput::convertFrame(const AVFramePtr& frame)
- {
- if (!frame) {
- return QByteArray();
- }
-
- if (!m_needResampling) {
- // 直接复制数据
- int dataSize = frame->nb_samples * m_channels * sizeof(int16_t);
- return QByteArray(reinterpret_cast<const char*>(frame->data[0]), dataSize);
- }
-
- if (!m_swrContext) {
- av::Logger::instance().error("Resampler not initialized");
- return QByteArray();
- }
-
- // 计算输出采样数
- int outputSamples = swr_get_out_samples(m_swrContext, frame->nb_samples);
- if (outputSamples <= 0) {
- return QByteArray();
- }
-
- // 分配输出缓冲区
- int outputChannels = m_audioFormat.channelCount();
- int outputDataSize = outputSamples * outputChannels * sizeof(int16_t);
- QByteArray outputData(outputDataSize, 0);
-
- uint8_t* outputBuffer = reinterpret_cast<uint8_t*>(outputData.data());
-
- // 执行重采样
- int convertedSamples = swr_convert(m_swrContext,
- &outputBuffer, outputSamples,
- const_cast<const uint8_t**>(frame->data), frame->nb_samples);
-
- if (convertedSamples < 0) {
- av::Logger::instance().error("Audio resampling failed");
- return QByteArray();
- }
-
- // 调整输出数据大小
- int actualDataSize = convertedSamples * outputChannels * sizeof(int16_t);
- outputData.resize(actualDataSize);
-
- return outputData;
- }
- void AudioOutput::applyVolume(QByteArray& data)
- {
- if (std::abs(m_volume - 1.0) < 0.001) {
- return; // 音量为1.0,无需调整
- }
-
- int16_t* samples = reinterpret_cast<int16_t*>(data.data());
- int sampleCount = data.size() / sizeof(int16_t);
-
- for (int i = 0; i < sampleCount; ++i) {
- samples[i] = static_cast<int16_t>(samples[i] * m_volume);
- }
- }
- } // namespace player
- } // namespace av
|