Przeglądaj źródła

修复音频bug
WASAPILoopbackCapturer添加麦克风采集

zhuizhu 8 miesięcy temu
rodzic
commit
4206725b80

+ 1 - 1
AvPlayer2/mainwindowa.cpp

@@ -10,6 +10,7 @@
 #include "common.h"
 
 #include <QApplication>
+#include <QMenuBar>
 #include <QStatusBar>
 
 #include "ffmpeg_init.h"
@@ -17,7 +18,6 @@
 #include "qimage_operation.h"
 #include "qscreen.h"
 #include "start_play_thread.h"
-#include "ui_mainwindowa.h"
 
 #include "AvRecorder/ui/opengl_video_widget.h"
 

+ 23 - 55
AvRecorder/capturer/audio/audio_qt_capturer.cpp

@@ -141,7 +141,6 @@ QtAudioCapturer::QtAudioCapturer(QObject* parent)
     : QObject(parent)
     , m_deviceType(Microphone)
     , m_isRunning(false)
-    , m_audioDevice(nullptr)
 {
     // 初始化缓冲区
     m_buffer.open(QIODevice::ReadWrite);
@@ -181,14 +180,20 @@ bool QtAudioCapturer::Start()
              << (m_deviceType == Microphone ? "麦克风" : "扬声器");
 
     if (m_deviceType == Microphone && m_audioInput) {
-        m_audioDevice = m_audioInput->start();
-        if (!m_audioDevice) {
-            qWarning() << "QtAudioCapturer::Start - 启动音频输入失败";
-            return false;
-        }
+        m_buffer.buffer().clear();
+        m_buffer.seek(0);
+        m_audioInput->start(&m_buffer); // 使用push模式,将数据写入m_buffer
+
         qDebug() << "QtAudioCapturer::Start - QAudioInput state:" << m_audioInput->state();
         qDebug() << "QtAudioCapturer::Start - QAudioInput error:" << m_audioInput->error();
-        connect(m_audioDevice, &QIODevice::readyRead, this, &QtAudioCapturer::handleReadyRead);
+
+        if (m_audioInput->error() != QAudio::NoError) {
+            qWarning() << "QtAudioCapturer::Start - 启动音频输入失败, error:"
+                     << m_audioInput->error();
+            return false;
+        }
+
+        m_processTimer.start(20); // 启动定时器来处理m_buffer中的数据
     }
     m_isRunning = true;
     return true;
@@ -202,17 +207,15 @@ void QtAudioCapturer::Stop()
 
     m_processTimer.stop();
 
-    if (m_audioDevice) {
-        disconnect(m_audioDevice, &QIODevice::readyRead, this, &QtAudioCapturer::handleReadyRead);
-        m_audioDevice = nullptr;
-    }
-
     if (m_audioInput) {
-        m_audioInput->stop();
+        if (m_audioInput->state() != QAudio::StoppedState) {
+            m_audioInput->stop();
+        }
     }
 
     m_buffer.buffer().clear();
     m_buffer.seek(0);
+    m_dataBuffer.clear();
     m_isRunning = false;
 }
 
@@ -221,25 +224,11 @@ const AudioFormat& QtAudioCapturer::GetFormat() const
     return m_audioFormat;
 }
 
-void QtAudioCapturer::handleReadyRead()
-{
-    //qDebug() << "handleReadyRead called, m_audioDevice:" << m_audioDevice;
-    if (m_audioDevice) {
-        QByteArray data = m_audioDevice->readAll();
-        //qDebug() << "handleReadyRead: read data size:" << data.size();
-        if (data.size() > 0) {
-            QMutexLocker locker(&m_mutex);
-            m_dataBuffer.append(data);
-            //qDebug() << "Audio data appended, buffer size now:" << m_dataBuffer.size();
-        }
-    }
-}
-
 int QtAudioCapturer::readAudioData(char* buf, int maxLen)
 {
     QMutexLocker locker(&m_mutex);
     int toRead = qMin(maxLen, m_dataBuffer.size());
-    // qDebug() << "readAudioData called, buffer size:" << m_dataBuffer.size() << ", toRead:" << toRead;
+    qDebug() << "[readAudioData] toRead=" << toRead << ", m_dataBuffer.size()=" << m_dataBuffer.size();
     if (toRead > 0) {
         memcpy(buf, m_dataBuffer.constData(), toRead);
         m_dataBuffer.remove(0, toRead);
@@ -249,38 +238,17 @@ int QtAudioCapturer::readAudioData(char* buf, int maxLen)
 
 void QtAudioCapturer::processAudioData()
 {
-    QByteArray data = m_buffer.buffer();
-    if (data.size() > 0) {
-        // qDebug() << "QtAudioCapturer::processAudioData - 处理缓冲区数据,大小:" << data.size()
-        //          << "字节";
-
-        // 清空缓冲区
+    int bufSize = m_buffer.size();
+    qDebug() << "[processAudioData] m_buffer.size()=" << bufSize << ", m_dataBuffer.size()=" << m_dataBuffer.size();
+    // 定时器触发,将m_buffer中的数据转移到m_dataBuffer,以供readAudioData消费
+    if (bufSize > 0) {
+        QMutexLocker locker(&m_mutex);
+        m_dataBuffer.append(m_buffer.buffer());
         m_buffer.buffer().clear();
         m_buffer.seek(0);
     }
 }
 
-void QtAudioCapturer::onAudioNotify()
-{
-    if (m_audioDevice && m_audioDevice->bytesAvailable() > 0) {
-        handleReadyRead();
-    } else {
-        // 尝试直接从 QAudioInput 读取数据
-        QByteArray buffer(m_audioInput->bufferSize() / 4, 0);
-        qint64 len = buffer.size();
-
-        if (len > 0) {
-            qDebug() << "QtAudioCapturer::onAudioNotify - 直接读取到数据,大小:" << len << "字节";
-            buffer.resize(len);
-
-            // 调用回调函数
-            // if (m_callback) {
-            //     m_callback(buffer.data(), buffer.size(), m_userInfo);
-            // }
-        }
-    }
-}
-
 bool QtAudioCapturer::initMicrophone()
 {
     qDebug() << "QtAudioCapturer::initMicrophone - 默认输入设备: "

+ 0 - 4
AvRecorder/capturer/audio/audio_qt_capturer.h

@@ -49,9 +49,7 @@ public:
     int readAudioData(char* buf, int maxLen) override;
 
 private slots:
-    void handleReadyRead();
     void processAudioData();
-    void onAudioNotify();
 
 private:
     bool initMicrophone();
@@ -68,12 +66,10 @@ private:
     QScopedPointer<AudioInfo> m_audioInfo;
 
     std::unique_ptr<QAudioInput> m_audioInput;
-    QIODevice* m_audioDevice;
     QBuffer m_buffer;
     QTimer m_processTimer;
     QByteArray m_dataBuffer;
     QMutex m_mutex;
-    static constexpr int PROCESS_INTERVAL_MS = 20; // 处理间隔,20ms
 };
 
 #endif // AUDIO_QT_CAPTURER_H

+ 18 - 11
AvRecorder/capturer/audio/wasapi_loopback_capturer.cpp

@@ -10,7 +10,11 @@
 class WASAPILoopbackCapturerPrivate
 {
 public:
-    WASAPILoopbackCapturerPrivate() { CoInitialize(nullptr); }
+    IAudioCapturer::Type deviceType;
+    WASAPILoopbackCapturerPrivate()
+    {
+        CoInitialize(nullptr);
+    }
     ~WASAPILoopbackCapturerPrivate()
     {
         if (pwfx)
@@ -26,26 +30,29 @@ public:
         CoUninitialize();
     }
 
-    bool init()
+    bool init(IAudioCapturer::Type type)
     {
         HRESULT hr;
         hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
                               nullptr,
                               CLSCTX_ALL,
                               __uuidof(IMMDeviceEnumerator),
-                              (void**) &pEnumerator);
+                              (void**)&pEnumerator);
         if (FAILED(hr)) {
             qDebug() << "Failed to create MMDeviceEnumerator";
             return false;
         }
 
-        hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice);
+        deviceType = type;
+        EDataFlow dataFlow = (deviceType == IAudioCapturer::Type::Speaker) ? eRender : eCapture;
+
+        hr = pEnumerator->GetDefaultAudioEndpoint(dataFlow, eConsole, &pDevice);
         if (FAILED(hr)) {
             qDebug() << "Failed to get default audio endpoint";
             return false;
         }
 
-        hr = pDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr, (void**) &pAudioClient);
+        hr = pDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr, (void**)&pAudioClient);
         if (FAILED(hr)) {
             qDebug() << "Failed to activate audio client";
             return false;
@@ -57,8 +64,11 @@ public:
     bool initializeAudioClient()
     {
         AUDCLNT_SHAREMODE shareMode = AUDCLNT_SHAREMODE_SHARED;
-        DWORD streamFlags = AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM
+        DWORD streamFlags = AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM
                             | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY;
+        if (deviceType == IAudioCapturer::Type::Speaker) {
+            streamFlags |= AUDCLNT_STREAMFLAGS_LOOPBACK;
+        }
         REFERENCE_TIME hnsBufferDuration = 0;
 
         HRESULT hr
@@ -214,11 +224,8 @@ WASAPILoopbackCapturer::~WASAPILoopbackCapturer()
 
 bool WASAPILoopbackCapturer::Init(Type deviceType)
 {
-    // 只支持扬声器
-    if (deviceType != Type::Speaker)
-        return false;
-
-    if (!d->init()) {
+    m_deviceType = deviceType;
+    if (!d->init(deviceType)) {
         qDebug() << "Failed to initialize WASAPI components";
         return false;
     }

+ 1 - 0
AvRecorder/capturer/audio/wasapi_loopback_capturer.h

@@ -32,6 +32,7 @@ private:
     std::mutex m_mutex;
     std::condition_variable m_cv;
     AudioFormat m_audioFormat;
+    Type m_deviceType;
 
     class WASAPILoopbackCapturerPrivate* d;
 };

+ 4 - 12
AvRecorder/encoder/audio_mixer.cpp

@@ -208,6 +208,9 @@ bool AudioMixer::_AdjustVolume()
         }
     }
 
+    auto writeStream = (float*)(_outputFrame->data[0]);
+    memset(writeStream, 0, _outputFrame->linesize[0]);
+
     for (auto iter = _audioInputInfos.begin(); iter != _audioInputInfos.end(); ++iter) {
         auto&& frameQueue = iter->second.resampler->GetQueue();
         if (minSize > MAX_BUF_SIZE) {
@@ -218,20 +221,9 @@ bool AudioMixer::_AdjustVolume()
         }
         auto frame = frameQueue.Pop();
         auto scale = iter->second.scale;
-        auto writeStream = (float*) (_outputFrame->data[0]);
         auto readStream = (float*) (frame.frame->data[0]);
         iter->second.volume = readStream[0] * scale;
 
-        if (iter == _audioInputInfos.begin()) {
-            if (std::abs(scale - 1)
-                < 0.01) { // 这种情况可以直接使用 memcpy 而不是下面那种低效率的逐个赋值
-                memcpy(writeStream, readStream, _outputFrame->linesize[0]);
-                continue;
-            }
-            // 要进行 scale, 只能逐个赋值
-            // 所以这里要清零
-            memset(writeStream, 0, _outputFrame->linesize[0]);
-        }
         // 逐个计算赋值,优化音量处理
         for (int idx = 0; idx < _outputFrame->nb_samples; ++idx) {
             float sample = readStream[idx] * scale;
@@ -241,7 +233,7 @@ bool AudioMixer::_AdjustVolume()
             } else if (sample < -0.8f) {
                 sample = -0.8f + (sample + 0.8f) * 0.3f; // 软限制
             }
-            writeStream[idx] = sample;
+            writeStream[idx] += sample;
         }
     }
     return true;

+ 1 - 1
AvRecorder/recorder/audio_recorder.cpp

@@ -33,7 +33,7 @@ bool AudioRecorder::Open(const std::vector<AudioCapturer::Type>& deviceTypes,
         if (deviceTypes[index] == AudioCapturer::Type::Speaker) {
             m_audioCapturers.push_back(new WASAPILoopbackCapturer());
         } else {
-            m_audioCapturers.push_back(new QtAudioCapturer());
+            m_audioCapturers.push_back(new WASAPILoopbackCapturer());
         }
     }
 

+ 1 - 1
AvRecorder/recorder/audio_recorder.h

@@ -42,9 +42,9 @@ private:
     bool _isRecord = false;
     int _streamIndex;
     Encoder<MediaType::AUDIO>::Param _param;
+
     Timer m_audioTimer; // 新增高精度定时器
     static constexpr int AUDIO_PULL_INTERVAL_MS = 10;
-    static void _Callback(void* data, size_t size, void* userInfo);
     AVSampleFormat _GetAVSampleFormat(int wBitsPerSample, bool isFloat = true)
     {
         // isFloat=true 表示32/64位时优先返回浮点格式,否则返回整型

+ 1 - 0
LearningSmartClient.pro

@@ -109,6 +109,7 @@ LIBS += -lwindowsapp    # For CreateDispatcherQueueController
 
 DESTDIR     = $$PWD/bin
 CONFIG(debug, debug|release) {
+    DESTDIR     = $$PWD/bin_debug
     TARGET = $$join(TARGET,,,d)
 }