#include "audio_play_thread.h" #include "playercontroller.h" #include #include Q_LOGGING_CATEGORY(playerControllerAudioPlayThread, "player.controller.AudioPlayThread") #if !NDEBUG #define DEBUG_PLAYFILTER 0 #define WRITE_AUDIO_FILE 0 #else #define DEBUG_PLAYFILTER 0 #define WRITE_AUDIO_FILE 0 #endif #if WRITE_AUDIO_FILE #include #endif AudioPlayThread::AudioPlayThread(VideoState* pState) : m_pOutput(nullptr) , m_pState(pState) { qRegisterMetaType("AudioData"); } AudioPlayThread::~AudioPlayThread() { stop(); stop_device(); final_resample_param(); // 确保QAudioOutput被正确释放 if (m_pOutput) { delete m_pOutput; m_pOutput = nullptr; } } void AudioPlayThread::print_device() const { QAudioDeviceInfo deviceInfo = QAudioDeviceInfo::defaultOutputDevice(); auto deviceInfos = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); for (const QAudioDeviceInfo& deviceInfo : std::as_const(deviceInfos)) qDebug() << "Input device name: " << deviceInfo.deviceName(); deviceInfos = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput); for (const QAudioDeviceInfo& deviceInfo : std::as_const(deviceInfos)) qDebug() << "Output device name: " << deviceInfo.deviceName(); const auto edians = deviceInfo.supportedByteOrders(); for (const QAudioFormat::Endian& endian : edians) qDebug() << "Endian: " << endian; const auto sampleTypes = deviceInfo.supportedSampleTypes(); for (const QAudioFormat::SampleType& sampleType : sampleTypes) qDebug() << "sampleType: " << sampleType; const auto codecs = deviceInfo.supportedCodecs(); for (const QString& codec : codecs) qDebug() << "codec: " << codec; const auto sampleRates = deviceInfo.supportedSampleRates(); for (const int& sampleRate : sampleRates) qDebug() << "sampleRate: " << sampleRate; const auto ChannelCounts = deviceInfo.supportedChannelCounts(); for (const int& channelCount : ChannelCounts) qDebug() << "channelCount: " << channelCount; const auto sampleSizes = deviceInfo.supportedSampleSizes(); for (const int& sampleSize : sampleSizes) qDebug() << "sampleSize: " << sampleSize; } bool AudioPlayThread::init_device(int sample_rate, int channel, AVSampleFormat sample_fmt, float default_vol) { QAudioDeviceInfo deviceInfo = QAudioDeviceInfo::defaultOutputDevice(); QAudioFormat format; format.setSampleRate(sample_rate); format.setChannelCount(channel); format.setSampleSize(8 * av_get_bytes_per_sample(sample_fmt)); format.setCodec("audio/pcm"); format.setByteOrder(QAudioFormat::LittleEndian); format.setSampleType(QAudioFormat::SignedInt); // qDebug("sample size=%d\n", 8 * av_get_bytes_per_sample(sample_fmt)); if (!deviceInfo.isFormatSupported(format)) { qWarning() << "Raw audio format not supported!"; return false; } // 使用智能指针管理QAudioOutput,确保异常安全 m_pOutput = new QAudioOutput(deviceInfo, format); // 注意:不设置parent,手动管理生命周期以确保正确的清理顺序 set_device_volume(default_vol); m_audioDevice = m_pOutput->start(); return true; } float AudioPlayThread::get_device_volume() const { if (m_pOutput) return m_pOutput->volume(); return 0; } void AudioPlayThread::set_device_volume(float volume) { if (m_pOutput) m_pOutput->setVolume(volume); } void AudioPlayThread::stop_device() { if (m_pOutput) { m_pOutput->stop(); m_pOutput->reset(); m_audioDevice = nullptr; // 清空设备指针,避免悬空指针 } } void AudioPlayThread::play_file(const QString& file) { /*play pcm file directly*/ QFile audioFile; audioFile.setFileName(file); audioFile.open(QIODevice::ReadOnly); m_pOutput->start(&audioFile); } void AudioPlayThread::play_buf(const uint8_t* buf, int datasize) { if (!m_audioDevice) return; uint8_t* data = (uint8_t*) buf; while (datasize > 0) { qint64 len = m_audioDevice->write((const char*) data, datasize); if (len < 0) break; if (len > 0) { data = data + len; datasize -= len; } // qDebug("play buf:reslen:%d, write len:%d", len, datasize); } } void AudioPlayThread::run() { qCDebug(playerControllerAudioPlayThread) << "[AudioPlayThread] run start, m_pState:" << (void*) m_pState; assert(m_pState); VideoState* is = m_pState; qCDebug(playerControllerAudioPlayThread) << "[AudioPlayThread] VideoState* is:" << (void*) is << ", abort_request:" << is->abort_request; int audio_size; int loop_count = 0; for (;;) { if (isExit()) { qCDebug(playerControllerAudioPlayThread) << "[AudioPlayThread] m_exit set, exit."; break; } if (is->abort_request) { qCDebug(playerControllerAudioPlayThread) << "[AudioPlayThread] abort_request set, exit."; break; } if (is->paused) { qCDebug(playerControllerAudioPlayThread) << "[AudioPlayThread] paused, wait."; std::unique_lock lock(m_mutex); m_cv.wait_for(lock, std::chrono::milliseconds(10), [this] { return isExit(); }); continue; } qCDebug(playerControllerAudioPlayThread) << "[AudioPlayThread] call audio_decode_frame, loop:" << loop_count << ", sampq size:" << (is ? is->sampq.size : -1); audio_size = audio_decode_frame(is); qCDebug(playerControllerAudioPlayThread) << "[AudioPlayThread] audio_decode_frame ret:" << audio_size << ", loop:" << loop_count++; if (audio_size < 0) { qCWarning(playerControllerAudioPlayThread) << "[AudioPlayThread] audio_decode_frame failed, exit."; break; } if (!isnan(is->audio_clock)) { AVCodecContext* pAudioCodex = is->auddec.avctx; if (pAudioCodex) { int bytes_per_sec = av_samples_get_buffer_size(nullptr, pAudioCodex->ch_layout.nb_channels, pAudioCodex->sample_rate, AV_SAMPLE_FMT_S16, 1); int64_t audio_callback_time = av_gettime_relative(); set_clock_at(&is->audclk, is->audio_clock - (double) (audio_size) / bytes_per_sec, is->audio_clock_serial, audio_callback_time / 1000000.0); sync_clock_to_slave(&is->extclk, &is->audclk); } } } qCDebug(playerControllerAudioPlayThread) << "[AudioPlayThread] run end, abort_request:" << is->abort_request << ", m_exit:" << (m_exit ? m_exit->load() : -1); } int AudioPlayThread::audio_decode_frame(VideoState* is) { int data_size; Frame* af; do { while (frame_queue_nb_remaining(&is->sampq) == 0) { if (is->eof) { // break; return -1; } else { av_usleep(1000); // return -1; } if (is->abort_request || isExit()) break; } if (is->abort_request || isExit()) return -1; if (!(af = frame_queue_peek_readable(&is->sampq))) return -1; frame_queue_next(&is->sampq); } while (af->serial != is->audioq.serial); /*data_size = av_samples_get_buffer_size(nullptr, af->frame->channels, af->frame->nb_samples, AVSampleFormat(af->frame->format), 1);*/ #if USE_AVFILTER_AUDIO data_size = av_samples_get_buffer_size(nullptr, af->frame->ch_layout.nb_channels, af->frame->nb_samples, AV_SAMPLE_FMT_S16, 1); uint8_t* const buffer_audio = (uint8_t*) av_malloc(data_size * sizeof(uint8_t)); memcpy(buffer_audio, af->frame->data[0], data_size); #else struct SwrContext* swrCtx = m_audioResample.swrCtx; data_size = av_samples_get_buffer_size(nullptr, af->frame->channels, af->frame->nb_samples, AV_SAMPLE_FMT_S16, 0); // AVSampleFormat(af->frame->format) uint8_t* buffer_audio = (uint8_t*) av_malloc(data_size * sizeof(uint8_t)); int ret = swr_convert(swrCtx, &buffer_audio, af->frame->nb_samples, (const uint8_t**) (af->frame->data), af->frame->nb_samples); if (ret < 0) { return 0; } #endif if (is->muted && data_size > 0) memset(buffer_audio, 0, data_size); // mute #if WRITE_AUDIO_FILE std::ofstream myfile; myfile.open("audio.pcm", std::ios::out | std::ios::app | std::ios::binary); if (myfile.is_open()) { myfile.write((char*) buffer_audio, data_size); } #endif if (m_bSendToVisual) { AudioData data; // 增强的缓冲区大小检查 if (data_size <= 0) { qCWarning(playerControllerAudioPlayThread) << "Invalid audio data size:" << data_size; } else if (data_size > BUFFER_LEN) { qCWarning(playerControllerAudioPlayThread) << "Audio frame too large, data_size:" << data_size << ", buffer_len:" << BUFFER_LEN << ", will truncate"; } // 安全的内存拷贝 if (data_size > 0 && buffer_audio) { int len = std::min(data_size, BUFFER_LEN); memcpy(data.buffer, buffer_audio, len); data.len = len; // 安全地调用回调函数 try { if (m_onDataVisualReady) { m_onDataVisualReady(data); } } catch (...) { qCWarning(playerControllerAudioPlayThread) << "Exception in visual data callback"; } } } play_buf(buffer_audio, data_size); av_free((void*) buffer_audio); /* update the audio clock with the pts */ if (!isnan(af->pts)) { // is->audio_clock = af->pts + (double)af->frame->nb_samples / // af->frame->sample_rate; double frame = (double) af->frame->nb_samples / af->frame->sample_rate; // frame = frame * is->audio_speed; is->audio_clock = af->pts + frame; #if USE_AVFILTER_AUDIO is->audio_clock = is->audio_clock_old + (is->audio_clock - is->audio_clock_old) * is->audio_speed; // is->audio_clock = is->audio_clock * is->audio_speed; #endif #if DEBUG_PLAYFILTER static int pks_num = 0; pks_num++; qDebug("[%d]audio: clock=%0.3f pts=%0.3f, (nb:%d, sr:%d)frame:%0.3f\n", pks_num, is->audio_clock, af->pts, af->frame->nb_samples, af->frame->sample_rate, frame); // qDebug("audio: clock=%0.3f pts=%0.3f, (nb:%d, sr:%d)frame:%0.3f\n", // is->audio_clock, af->pts, af->frame->nb_samples, af->frame->sample_rate, // frame); #endif } else { is->audio_clock = NAN; } is->audio_clock_serial = af->serial; if (m_onUpdatePlayTime) m_onUpdatePlayTime(); #if (!NDEBUG && PRINT_PACKETQUEUE_AUDIO_INFO) { static double last_clock; qDebug("audio: delay=%0.3f clock=%0.3f\n", is->audio_clock - last_clock, is->audio_clock); last_clock = is->audio_clock; } #endif return data_size; } bool AudioPlayThread::init_resample_param(AVCodecContext* pAudio, AVSampleFormat sample_fmt, VideoState* is) { if (pAudio) { int ret = -1; struct SwrContext* swrCtx = nullptr; #if USE_AVFILTER_AUDIO if (is) { AVFilterContext* sink = is->out_audio_filter; // int sample_rate = av_buffersink_get_sample_rate(sink); // int nb_channels = av_buffersink_get_channels(sink); AVChannelLayout channel_layout; av_buffersink_get_ch_layout(sink, &channel_layout); // int64_t channel_layout = av_buffersink_get_channel_layout(sink); int format = av_buffersink_get_format(sink); ret = swr_alloc_set_opts2(&swrCtx, &pAudio->ch_layout, sample_fmt, pAudio->sample_rate, &channel_layout, (AVSampleFormat) format, pAudio->sample_rate, 0, nullptr); /*m_audioResample.channel_layout = channel_layout; m_audioResample.sample_fmt = sample_fmt; m_audioResample.sample_rate = pAudio->sample_rate;*/ } #else ret = swr_alloc_set_opts2(&swrCtx, &pAudio->ch_layout, sample_fmt, pAudio->sample_rate, &pAudio->ch_layout, pAudio->sample_fmt, pAudio->sample_rate, 0, nullptr); #endif if (!(ret < 0)) { swr_init(swrCtx); m_audioResample.swrCtx = swrCtx; return true; } } return false; } void AudioPlayThread::final_resample_param() { // 安全释放重采样上下文 if (m_audioResample.swrCtx) { swr_free(&m_audioResample.swrCtx); m_audioResample.swrCtx = nullptr; } }