#include "AudioPlayer.h" #include #include extern "C" { #include #include #include #include #include #include } #include "sonic/sonic.h" #include #include #include AudioPlayer::AudioPlayer() {} AudioPlayer::~AudioPlayer() { reset(); } void AudioPlayer::reset() { if (m_sonicCtx) { sonicDestroyStream(m_sonicCtx); m_sonicCtx = nullptr; } if (m_swrCtx) { swr_free(&m_swrCtx); m_swrCtx = nullptr; } if (m_swrBuffer) { av_free(m_swrBuffer); m_swrBuffer = nullptr; m_swrBufferSize = 0; } freeBuffers(); if (m_audioOutput) { m_audioOutput->stop(); delete m_audioOutput; m_audioOutput = nullptr; } m_audioDevice = nullptr; m_sampleRate = 0; m_channels = 0; m_format = AV_SAMPLE_FMT_NONE; m_lastSpeed = 1.0f; } void AudioPlayer::freeBuffers() { if (m_abuf) { av_freep(&m_abuf); m_abuf = nullptr; } if (m_abufOut) { av_freep(&m_abufOut); m_abufOut = nullptr; m_abufOutSize = 0; } m_maxBufSize = -1; } void AudioPlayer::init(const AVFrame* frame, float speed) { reset(); initAudioOutput(frame, speed); } void AudioPlayer::initAudioOutput(const AVFrame* frame, float speed) { m_sampleRate = frame->sample_rate; m_channels = frame->ch_layout.nb_channels; m_format = (AVSampleFormat)frame->format; m_lastSpeed = speed; if (m_sonicCtx) { sonicDestroyStream(m_sonicCtx); m_sonicCtx = nullptr; } m_sonicCtx = sonicCreateStream(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); QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice()); if (!info.isFormatSupported(fmt)) { qWarning() << "音频格式不支持,尝试最近格式"; fmt = info.nearestFormat(fmt); } m_audioOutput = new QAudioOutput(fmt, nullptr); m_audioDevice = m_audioOutput->start(); } void AudioPlayer::setSpeed(float speed) { if (m_sonicCtx) { sonicSetSpeed(m_sonicCtx, speed); sonicSetPitch(m_sonicCtx, 1.0f); sonicSetRate(m_sonicCtx, 1.0f); } m_lastSpeed = speed; } void AudioPlayer::setOutputDevice(QIODevice* device) { m_audioDevice = device; } void AudioPlayer::setAudioOutput(QAudioOutput* output) { m_audioOutput = output; } QIODevice* AudioPlayer::getOutputDevice() const { return m_audioDevice; } QAudioOutput* AudioPlayer::getAudioOutput() const { return m_audioOutput; } bool AudioPlayer::needReinit(const AVFrame* frame, float speed) const { if (!m_audioOutput) return true; if (m_sampleRate != frame->sample_rate) return true; if (m_channels != frame->ch_layout.nb_channels) return true; if (m_format != (AVSampleFormat)frame->format) return true; if (std::abs(m_lastSpeed - speed) > 1e-3) return true; return false; } void AudioPlayer::play(AVFrame* frame, float speed) { if (!m_audioDevice) return; m_sampleRate = frame->sample_rate; m_channels = frame->ch_layout.nb_channels; m_format = (AVSampleFormat)frame->format; // 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) { if (!m_swrCtx) { m_swrCtx = swr_alloc(); swr_alloc_set_opts2(&m_swrCtx, &frame->ch_layout, // out_ch_layout targetFmt, m_sampleRate, &frame->ch_layout, // in_ch_layout m_format, m_sampleRate, 0, nullptr); swr_init(m_swrCtx); } 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 变速 if (!m_sonicCtx) { m_sonicCtx = sonicCreateStream(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. 输出到Qt int bytesWrite = 0; while (bytesWrite < out_size) { int written = m_audioDevice->write((const char*)m_abufOut + bytesWrite, out_size - bytesWrite); if (written <= 0) break; bytesWrite += written; } }