#include "AudioPlayer.h" #include #include #include extern "C" { #include #include #include #include #include #include } #include "sonic/sonic.h" AudioPlayer::AudioPlayer() {} AudioPlayer::~AudioPlayer() { reset(); } bool AudioPlayer::open(const AVFrame* frame, float speed) { qDebug() << "[AUDIO-PLAYER] open: sample_rate=" << frame->sample_rate << "channels=" << frame->ch_layout.nb_channels << "format=" << frame->format << "speed=" << speed; QMutexLocker locker(&m_mutex); qDebug() << "[AUDIO-PLAYER] got mutex"; reset(); qDebug() << "[AUDIO-PLAYER] after reset"; return initAudioOutput(frame, speed); } bool AudioPlayer::initAudioOutput(const AVFrame* frame, float speed) { qDebug() << "[AUDIO-PLAYER] initAudioOutput begin"; m_sampleRate = frame->sample_rate; m_channels = frame->ch_layout.nb_channels; m_format = (AVSampleFormat)frame->format; m_speed = speed; m_volume = 1.0f; releaseSonic(); ensureSonic(m_sampleRate, m_channels); setSpeed(speed); QAudioFormat fmt; fmt.setSampleRate(m_sampleRate); fmt.setChannelCount(m_channels); fmt.setSampleSize(16); // S16 fmt.setCodec("audio/pcm"); fmt.setByteOrder(QAudioFormat::LittleEndian); fmt.setSampleType(QAudioFormat::SignedInt); qDebug() << "[AUDIO-PLAYER] before QAudioOutput, deviceInfo isNull=" << m_deviceInfo.isNull(); if (!m_deviceInfo.isNull()) { if (!m_deviceInfo.isFormatSupported(fmt)) { qWarning() << "音频格式不支持,尝试最近格式"; fmt = m_deviceInfo.nearestFormat(fmt); } qDebug() << "[AUDIO-PLAYER] before QAudioOutput (custom device)"; m_audioOutput = new QAudioOutput(m_deviceInfo, fmt, nullptr); } else { QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice()); if (!info.isFormatSupported(fmt)) { qWarning() << "音频格式不支持,尝试最近格式"; fmt = info.nearestFormat(fmt); } qDebug() << "[AUDIO-PLAYER] before QAudioOutput (default device)"; m_audioOutput = new QAudioOutput(fmt, nullptr); } qDebug() << "[AUDIO-PLAYER] after QAudioOutput, m_audioOutput=" << m_audioOutput; // m_audioOutput->setBufferSize(16384); m_audioOutput->setVolume(m_volume); qDebug() << "[AUDIO-PLAYER] before start"; m_audioDevice = m_audioOutput->start(); qDebug() << "[AUDIO-PLAYER] after start, m_audioDevice=" << m_audioDevice; return m_audioDevice != nullptr; } bool AudioPlayer::play(const AVFrame* frame, float speed) { QMutexLocker locker(&m_mutex); if (!m_audioDevice) return false; if (m_sampleRate != frame->sample_rate || m_channels != frame->ch_layout.nb_channels || m_format != (AVSampleFormat)frame->format) { open(frame, speed); } m_speed = speed; setSpeed(speed); // 1. 重采样到 S16 const AVSampleFormat targetFmt = AV_SAMPLE_FMT_S16; uint8_t* inputData = nullptr; int inputSamples = frame->nb_samples; int inputBytes = av_samples_get_buffer_size(nullptr, m_channels, inputSamples, targetFmt, 1); if (m_format != targetFmt) { ensureResampler(frame); if (!m_swrBuffer || m_swrBufferSize < inputBytes) { if (m_swrBuffer) av_free(m_swrBuffer); m_swrBuffer = (uint8_t*)av_malloc(inputBytes); m_swrBufferSize = inputBytes; } int outSamples = swr_convert( m_swrCtx, &m_swrBuffer, inputSamples, (const uint8_t**)frame->extended_data, inputSamples ); inputData = m_swrBuffer; inputSamples = outSamples; inputBytes = av_samples_get_buffer_size(nullptr, m_channels, outSamples, targetFmt, 1); } else { inputData = frame->data[0]; inputBytes = av_samples_get_buffer_size(nullptr, m_channels, inputSamples, targetFmt, 1); } // 2. 送 sonic 变速 ensureSonic(m_sampleRate, m_channels); setSpeed(speed); int out_ret = sonicWriteShortToStream(m_sonicCtx, (int16_t*)inputData, inputBytes / (m_channels * 2)); int num_samples = sonicSamplesAvailable(m_sonicCtx); int out_size = num_samples * 2 * m_channels; av_fast_malloc(&m_abufOut, &m_abufOutSize, out_size); int sonic_samples = 0; if (out_ret) { sonic_samples = sonicReadShortFromStream(m_sonicCtx, (int16_t*)m_abufOut, num_samples); out_size = sonic_samples * 2 * m_channels; } // 3. 写入音频设备前判断可用空间 if (m_audioOutput && m_audioOutput->bytesFree() < out_size) { qWarning() << "[AUDIO] QAudioOutput 缓冲区不够,丢帧 out_size=" << out_size << "bytesFree=" << m_audioOutput->bytesFree(); return false; } if (out_size > 0) m_audioDevice->write((const char*)m_abufOut, out_size); return true; } void AudioPlayer::setSpeed(float speed) { // QMutexLocker locker(&m_mutex); m_speed = speed; if (m_sonicCtx) { sonicSetSpeed(m_sonicCtx, speed); sonicSetPitch(m_sonicCtx, 1.0f); sonicSetRate(m_sonicCtx, 1.0f); } } void AudioPlayer::setVolume(float volume) { QMutexLocker locker(&m_mutex); m_volume = volume; if (m_audioOutput) { m_audioOutput->setVolume(volume); } } bool AudioPlayer::setOutputDevice(const QAudioDeviceInfo& deviceInfo) { QMutexLocker locker(&m_mutex); m_deviceInfo = deviceInfo; if (m_audioOutput) { m_audioOutput->stop(); delete m_audioOutput; m_audioOutput = nullptr; } return true; } void AudioPlayer::stop() { QMutexLocker locker(&m_mutex); if (m_audioOutput) { m_audioOutput->stop(); } } void AudioPlayer::reset() { releaseSonic(); releaseResampler(); releaseAudioOutput(); if (m_abufOut) { av_freep(&m_abufOut); m_abufOut = nullptr; m_abufOutSize = 0; } m_sampleRate = 0; m_channels = 0; m_format = AV_SAMPLE_FMT_NONE; m_speed = 1.0f; m_volume = 1.0f; m_deviceInfo = QAudioDeviceInfo(); } void AudioPlayer::freeBuffers() { if (m_abufOut) { av_freep(&m_abufOut); m_abufOut = nullptr; m_abufOutSize = 0; } } bool AudioPlayer::ensureResampler(const AVFrame* frame) { if (!m_swrCtx) { m_swrCtx = swr_alloc(); swr_alloc_set_opts2(&m_swrCtx, &frame->ch_layout, // out_ch_layout AV_SAMPLE_FMT_S16, frame->sample_rate, &frame->ch_layout, // in_ch_layout (AVSampleFormat)frame->format, frame->sample_rate, 0, nullptr); swr_init(m_swrCtx); } return m_swrCtx != nullptr; } void AudioPlayer::releaseResampler() { if (m_swrCtx) { swr_free(&m_swrCtx); m_swrCtx = nullptr; } if (m_swrBuffer) { av_free(m_swrBuffer); m_swrBuffer = nullptr; m_swrBufferSize = 0; } } bool AudioPlayer::ensureSonic(int sampleRate, int channels) { if (!m_sonicCtx) { m_sonicCtx = sonicCreateStream(sampleRate, channels); } return m_sonicCtx != nullptr; } void AudioPlayer::releaseSonic() { if (m_sonicCtx) { sonicDestroyStream(m_sonicCtx); m_sonicCtx = nullptr; } } void AudioPlayer::releaseAudioOutput() { if (m_audioOutput) { m_audioOutput->stop(); delete m_audioOutput; m_audioOutput = nullptr; } m_audioDevice = nullptr; } QIODevice* AudioPlayer::getOutputDevice() const { return m_audioDevice; } QAudioOutput* AudioPlayer::getAudioOutput() const { return m_audioOutput; } float AudioPlayer::getSpeed() const { return m_speed; } float AudioPlayer::getVolume() const { return m_volume; }