فهرست منبع

添加: 音频编码处理

zhuizhu 7 ماه پیش
والد
کامیت
42807fdf24

+ 70 - 5
AvRecorder/encoder/audio_encoder.cpp

@@ -1,13 +1,67 @@
 #include "audio_encoder.h"
 
 #include <QDebug>
+#include <algorithm>
+
+std::vector<std::string> Encoder<MediaType::AUDIO>::_usableEncoders;
+
+const std::vector<std::string>& Encoder<MediaType::AUDIO>::GetUsableEncoders()
+{
+    if (_usableEncoders.empty()) {
+        _FindUsableEncoders();
+    }
+    return _usableEncoders;
+}
+
+void Encoder<MediaType::AUDIO>::_FindUsableEncoders()
+{
+    qDebug() << "AudioEncoder: Finding usable encoders...";
+
+    Param param;
+    param.bitRate = 128000; // 128 kbps
+    Encoder encoder;
+    AVFormatContext* fmtCtx = nullptr;
+    __CheckNo(avformat_alloc_output_context2(&fmtCtx, nullptr, nullptr, "test.mp4") >= 0);
+
+    for (const auto& name : _encoderNames) {
+        param.name = name;
+        qDebug() << "AudioEncoder: Testing encoder:" << name;
+        if (encoder.Open(param, fmtCtx)) {
+            _usableEncoders.push_back(name);
+            qDebug() << "AudioEncoder: Encoder" << name << "is usable";
+        } else {
+            qDebug() << "AudioEncoder: Encoder" << name << "failed to open";
+        }
+        encoder.Close();
+    }
+
+    // 简单的优先级排序:AAC优先,其次Opus,最后MP3
+    if (!_usableEncoders.empty()) {
+        auto priority = [](const std::string& n) -> int {
+            if (n == "aac" || n == "libfdk_aac") return 0; // 与mp4/flv/rtmp兼容性最好
+            if (n == "libopus" || n == "opus") return 1;  // 需容器/推流支持
+            if (n == "libmp3lame") return 2;               // 一般不用于flv
+            return 100;
+        };
+        std::sort(_usableEncoders.begin(), _usableEncoders.end(), [&](const std::string& a, const std::string& b) {
+            int pa = priority(a);
+            int pb = priority(b);
+            if (pa != pb) return pa < pb;
+            return a < b;
+        });
+    }
+
+    qDebug() << "AudioEncoder: Found" << _usableEncoders.size() << "usable encoders";
+    Free(fmtCtx, [&fmtCtx] { avformat_free_context(fmtCtx); });
+}
 
 bool Encoder<MediaType::AUDIO>::Open(const Param& audioParam, AVFormatContext* fmtCtx)
 {
     Close();
     _isOpen = false;
     
-    qDebug() << "AudioEncoder::Open: Initializing audio encoder with bitrate" << audioParam.bitRate;
+    qDebug() << "AudioEncoder::Open: Initializing audio encoder with bitrate" << audioParam.bitRate
+             << ", name:" << (audioParam.name.empty() ? "<auto>" : audioParam.name.c_str());
     
     if (!_Init(audioParam, fmtCtx)) {
         qDebug() << "AudioEncoder::Open failed: Audio encoder initialization failed";
@@ -28,6 +82,7 @@ bool Encoder<MediaType::AUDIO>::Open(const Param& audioParam, AVFormatContext* f
     qDebug() << "AudioEncoder::Open: Audio encoder opened successfully";
     return true;
 }
+
 void Encoder<MediaType::AUDIO>::Close()
 {
     if (_codecCtx != nullptr) {
@@ -35,10 +90,20 @@ void Encoder<MediaType::AUDIO>::Close()
     }
     Free(_codecCtx, [this] { avcodec_free_context(&_codecCtx); });
 }
+
 bool Encoder<MediaType::AUDIO>::_Init(const Param& audioParam, AVFormatContext* fmtCtx)
 {
-    // codec
-    __CheckBool(_codec = avcodec_find_encoder(AV_CODEC_ID_AAC));
+    // codec 选择:优先按名称,其次回退到容器默认音频编码器,再次回退到AAC
+    if (!audioParam.name.empty()) {
+        _codec = avcodec_find_encoder_by_name(audioParam.name.c_str());
+    }
+    if (!_codec && fmtCtx && fmtCtx->oformat && fmtCtx->oformat->audio_codec != AV_CODEC_ID_NONE) {
+        _codec = avcodec_find_encoder(fmtCtx->oformat->audio_codec);
+    }
+    if (!_codec) {
+        _codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
+    }
+    __CheckBool(_codec);
     // codeccontext
     __CheckBool(_codecCtx = avcodec_alloc_context3(_codec));
     _codecCtx->sample_fmt = AV_SAMPLE_FMT_FLTP;
@@ -46,8 +111,8 @@ bool Encoder<MediaType::AUDIO>::_Init(const Param& audioParam, AVFormatContext*
     _codecCtx->sample_rate = AUDIO_SAMPLE_RATE;
     AVChannelLayout layout;
     layout.order = AV_CHANNEL_ORDER_NATIVE;
-    layout.nb_channels = 1;
-    layout.u.mask = AV_CH_LAYOUT_MONO;
+    layout.nb_channels = AUDIO_CHANNEL;
+    layout.u.mask = (AUDIO_CHANNEL == 1 ? AV_CH_LAYOUT_MONO : AV_CH_LAYOUT_STEREO);
     av_channel_layout_copy(&_codecCtx->ch_layout, &layout);
     if (fmtCtx->oformat->flags & AVFMT_GLOBALHEADER) {
         _codecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

+ 16 - 0
AvRecorder/encoder/audio_encoder.h

@@ -2,6 +2,8 @@
 #define __AUDIO_ENCODER_H__
 
 #include "abstract_encoder.h"
+#include <string>
+#include <vector>
 
 template<>
 class Encoder<MediaType::AUDIO> : public AbstractEncoder
@@ -10,15 +12,29 @@ public:
     struct Param
     {
         int bitRate;
+        // 可选:优先使用的编码器名称,例如 "aac"、"libopus"、"libmp3lame"
+        std::string name;
     };
     ~Encoder() { Close(); }
 
     bool Open(const Param& audioParma, AVFormatContext* fmtCtx);
     virtual void Close() override;
     virtual bool PushFrame(AVFrame* frame, bool isEnd, uint64_t pts) override;
+    // 获取可用的音频编码器名称列表(按优先级排序)
+    static const std::vector<std::string>& GetUsableEncoders();
 
 private:
     bool _Init(const Param& audioParam, AVFormatContext* fmtCtx);
+    static void _FindUsableEncoders();
+    static std::vector<std::string> _usableEncoders;
+    // 常见音频编码器名称:优先保证与常见容器/推流(flv/mp4/rtmp)兼容
+    static constexpr const char* _encoderNames[5] = {
+        "aac",         // 通用AAC
+        "libfdk_aac", // 可选的高质量AAC
+        "libopus",     // Opus
+        "opus",        // 有些构建使用此名
+        "libmp3lame"   // MP3(不建议用于flv)
+    };
 };
 
 #endif

+ 37 - 14
AvRecorder/encoder/video_encoder.cpp

@@ -5,6 +5,7 @@ extern "C" {
 }
 
 #include <QDebug>
+#include <algorithm>
 
 std::vector<std::string> Encoder<MediaType::VIDEO>::_usableEncoders;
 
@@ -77,12 +78,6 @@ const std::vector<std::string>& Encoder<MediaType::VIDEO>::GetUsableEncoders()
 {
     if (_usableEncoders.empty()) {
         _FindUsableEncoders();
-        
-        // 如果仍然没有找到可用编码器,至少添加libx264作为后备
-        if (_usableEncoders.empty()) {
-            qDebug() << "VideoEncoder: No encoders found during testing, adding libx264 as fallback";
-            _usableEncoders.push_back("libx264");
-        }
     }
     return _usableEncoders;
 }
@@ -115,6 +110,28 @@ void Encoder<MediaType::VIDEO>::_FindUsableEncoders()
         }
         encoder.Close();
     }
+
+    // 调整优先级排序:优先显卡(硬件)中兼容性较好的,然后再选择软件中兼容性最好的
+    if (!_usableEncoders.empty()) {
+        auto priority = [](const std::string& n) -> int {
+            // 硬件优先(Windows常见顺序:NVIDIA -> Intel -> AMD;Apple放在后面)
+            if (n == "h264_nvenc") return 0;        // NVIDIA 硬件
+            if (n == "h264_qsv") return 1;          // Intel 硬件
+            if (n == "h264_amf") return 2;          // AMD 硬件
+            if (n == "h264_videotoolbox") return 3; // Apple 硬件
+            // 软件其次
+            if (n == "libx264") return 4;           // 软件中兼容性最好
+            if (n == "libx265") return 5;           // 兼容性一般
+            if (n == "libaom-av1") return 6;        // 兼容性较差/更慢
+            return 100;                                // 未知放最后
+        };
+        std::sort(_usableEncoders.begin(), _usableEncoders.end(), [&](const std::string& a, const std::string& b) {
+            int pa = priority(a);
+            int pb = priority(b);
+            if (pa != pb) return pa < pb;
+            return a < b; // 同优先级按名称稳定排序
+        });
+    }
     
     qDebug() << "VideoEncoder: Found" << _usableEncoders.size() << "usable encoders";
     Free(fmtCtx, [&fmtCtx] { avformat_free_context(fmtCtx); });
@@ -223,27 +240,33 @@ bool Encoder<MediaType::VIDEO>::_Trans(AVFrame* frame)
         _bufferFrame = frame;
     } else {
         if (_converter == nullptr) {
-            _converter = std::make_unique<FfmpegConverter>(AVPixelFormat(frame->format),
-                                                           AV_PIX_FMT_NV12);
+            // Convert from source pixel format to NV12 for downstream processing
+            _converter = std::make_unique<FfmpegConverter>(static_cast<AVPixelFormat>(frame->format), AV_PIX_FMT_NV12);
             _converter->SetSize(frame->width, frame->height);
         }
         _bufferFrame = _converter->Trans(frame);
     }
     if (_isHardware) {
         _bufferFrame = _ToHardware();
+        __CheckBool(_bufferFrame);
     }
-    __CheckBool(_bufferFrame);
     return true;
 }
 
 AVFrame* Encoder<MediaType::VIDEO>::_ToHardware()
 {
-    if (_bufferFrame == nullptr) {
+    __CheckBool(_hwDeviceCtx);
+    __CheckBool(_codecCtx && _codecCtx->hw_frames_ctx);
+    if (_hwFrame == nullptr) {
+        __CheckBool(_hwFrame = av_frame_alloc());
+        _hwFrame->format = ((AVHWFramesContext*)_codecCtx->hw_frames_ctx->data)->format;
+        _hwFrame->width = _codecCtx->width;
+        _hwFrame->height = _codecCtx->height;
+        __CheckBool(av_hwframe_get_buffer(_codecCtx->hw_frames_ctx, _hwFrame, 0) >= 0);
+    }
+    if (av_hwframe_transfer_data(_hwFrame, _bufferFrame, 0) < 0) {
+        __DebugPrint("hw transfer failed\n");
         return nullptr;
     }
-    __CheckNullptr(_hwFrame = av_frame_alloc());
-    __CheckNullptr(av_hwframe_get_buffer(_codecCtx->hw_frames_ctx, _hwFrame, 0) >= 0);
-    __CheckNullptr(_hwFrame->hw_frames_ctx);
-    __CheckNullptr(av_hwframe_transfer_data(_hwFrame, _bufferFrame, 0) >= 0);
     return _hwFrame;
 }

+ 71 - 9
AvRecorder/muxer/av_muxer.cpp

@@ -275,29 +275,91 @@ int AvMuxer::AddAudioStream(const Encoder<MediaType::AUDIO>::Param& param)
 
     qDebug() << "AddAudioStream: Creating audio encoder with codec"
              << avcodec_get_name(_fmtCtx->oformat->audio_codec);
-    // << "sample rate" << param.sampleRate << "channels" << param.channels;
+
+    // 获取可用编码器列表
+    const auto& usableEncoders = Encoder<MediaType::AUDIO>::GetUsableEncoders();
 
     Info info;
     info.pts = 0;
     info.fps = AUDIO_SAMPLE_RATE;
+
     auto encoder = new Encoder<MediaType::AUDIO>;
-    info.type = MediaType::AUDIO;
-    info.encoder = encoder;
-    
-    if (!encoder->Open(param, _fmtCtx)) {
-        qDebug() << "AddAudioStream failed: Cannot open audio encoder";
+    auto modifiableParam = param; // 创建可修改的副本
+
+    bool encoderOpened = false;
+    std::string usedEncoder = modifiableParam.name;
+
+    // 首先尝试用户指定的编码器
+    if (!modifiableParam.name.empty()) {
+        qDebug() << "AddAudioStream: Trying user specified encoder:" << QString::fromStdString(modifiableParam.name);
+        if (encoder->Open(modifiableParam, _fmtCtx)) {
+            encoderOpened = true;
+            qDebug() << "AddAudioStream: Successfully opened user specified encoder:" << QString::fromStdString(modifiableParam.name);
+        } else {
+            qDebug() << "AddAudioStream: User specified encoder failed:" << QString::fromStdString(modifiableParam.name);
+        }
+    }
+
+    // 如果指定编码器失败,尝试可用编码器列表
+    if (!encoderOpened) {
+        qDebug() << "AddAudioStream: Trying fallback encoders...";
+
+        // 构建回退列表
+        std::vector<std::string> fallbackList;
+        if (!usableEncoders.empty()) {
+            fallbackList = usableEncoders;
+        } else {
+            // 基础回退集合
+            fallbackList = {"aac", "libfdk_aac", "libopus", "opus", "libmp3lame"};
+        }
+
+        for (const auto& encoderName : fallbackList) {
+            if (!modifiableParam.name.empty() && encoderName == modifiableParam.name) {
+                continue; // 跳过已经尝试过的编码器
+            }
+
+            modifiableParam.name = encoderName;
+            qDebug() << "AddAudioStream: Trying fallback encoder:" << QString::fromStdString(encoderName);
+
+            if (encoder->Open(modifiableParam, _fmtCtx)) {
+                encoderOpened = true;
+                usedEncoder = encoderName;
+                qDebug() << "AddAudioStream: Successfully opened fallback encoder:" << QString::fromStdString(encoderName);
+                break;
+            } else {
+                qDebug() << "AddAudioStream: Fallback encoder failed:" << QString::fromStdString(encoderName);
+            }
+        }
+    }
+
+    // 如果仍未打开成功,最后尝试自动按容器默认/AAC回退(通过传空name让内部回退)
+    if (!encoderOpened) {
+        qDebug() << "AddAudioStream: Trying auto/default encoder as last resort...";
+        modifiableParam.name.clear();
+        if (encoder->Open(modifiableParam, _fmtCtx)) {
+            encoderOpened = true;
+            usedEncoder = encoder->GetCtx() && encoder->GetCtx()->codec ? encoder->GetCtx()->codec->name : std::string("<auto>");
+            qDebug() << "AddAudioStream: Auto/default encoder opened successfully:" << (encoder->GetCtx() && encoder->GetCtx()->codec ? encoder->GetCtx()->codec->name : "unknown");
+        }
+    }
+
+    if (!encoderOpened) {
+        qDebug() << "AddAudioStream failed: All audio encoders failed to open";
         delete encoder;
         return -1;
     }
-    
+
+    info.type = MediaType::AUDIO;
+    info.encoder = encoder;
+
     if (!_AddStream(info)) {
         qDebug() << "AddAudioStream failed: Cannot add audio stream to format context";
         delete encoder;
         return -1;
     }
-    
+
     _infos.back().stream->time_base = {1, AUDIO_SAMPLE_RATE};
-    qDebug() << "AddAudioStream: Audio stream added successfully, index:" << info.streamIndex;
+    qDebug() << "AddAudioStream: Audio stream added successfully with encoder:" << QString::fromStdString(usedEncoder) << "index:" << info.streamIndex;
     return info.streamIndex;
 }
 

+ 5 - 0
AvRecorder/muxer/av_muxer.h

@@ -1,6 +1,11 @@
 #ifndef __AV_MUXER_H__
 #define __AV_MUXER_H__
 
+#include <vector>
+#include <string>
+#include <string_view>
+#include <mutex>
+
 #include "encoder/audio_encoder.h"
 #include "encoder/video_encoder.h"
 

+ 12 - 0
AvRecorder/recorder/audio_recorder.cpp

@@ -210,3 +210,15 @@ void AudioRecorder::PullAndProcessAudio()
         }
     }
 }
+
+std::string AudioRecorder::GetEncoderNameForMuxer(AvMuxer& muxer)
+{
+    std::lock_guard<std::mutex> lock(_muxersMtx);
+    auto it = std::find_if(_muxers.begin(), _muxers.end(), [&](const MuxerInfo& info){
+        return info.muxer == &muxer;
+    });
+    if (it == _muxers.end()) return {};
+    AVCodecContext* ctx = muxer.GetCodecCtx(it->streamIndex);
+    if (!ctx || !ctx->codec || !ctx->codec->name) return {};
+    return std::string(ctx->codec->name);
+}

+ 2 - 0
AvRecorder/recorder/audio_recorder.h

@@ -5,6 +5,7 @@
 #include "encoder/audio_mixer.h"
 #include "muxer/av_muxer.h"
 #include "basic/timer.h"
+#include <string>
 
 class AudioRecorder
 {
@@ -42,6 +43,7 @@ public:
     void PullAndProcessAudio(); // 新增:主动拉取音频数据
     auto GetCaptureInfo(int mixIndex) { return _mixer.GetInputInfo(mixIndex); }
     void SetVolumeScale(float scale, int mixIndex);
+    std::string GetEncoderNameForMuxer(AvMuxer& muxer);
 
 private:
     std::vector<IAudioCapturer*> m_audioCapturers;

+ 18 - 9
AvRecorder/recorder/video_recorder.cpp

@@ -5,6 +5,7 @@
 #include <vector>
 #include <algorithm>
 #include <mutex>
+#include <string>
 using namespace avrecorder::video;
 
 bool VideoRecorder::Open(HWND srcHwnd, Encoder<MediaType::VIDEO>::Param& param, CaptureMethod method)
@@ -100,21 +101,13 @@ bool VideoRecorder::LoadMuxer(AvMuxer& muxer)
     // 检查是否已经加载过这个muxer
     for (const auto& info : _muxers) {
         if (info.muxer == &muxer) {
-            qDebug() << "VideoRecorder::LoadMuxer: Muxer already loaded, streamIndex:" << info.streamIndex;
             return true; // 已经加载过,直接返回成功
         }
     }
     
-    qDebug() << "VideoRecorder::LoadMuxer: Adding video stream with params - name:" << QString::fromStdString(_param.name) 
-             << "width:" << _param.width << "height:" << _param.height << "fps:" << _param.fps << "bitRate:" << _param.bitRate;
-    
     int streamIndex = muxer.AddVideoStream(_param);
-    if (streamIndex == -1) {
-        qDebug() << "VideoRecorder::LoadMuxer: Failed to add video stream to muxer";
-        return false;
-    }
+    __CheckBool(streamIndex != -1);
     
-    qDebug() << "VideoRecorder::LoadMuxer: Successfully added video stream, streamIndex:" << streamIndex;
     _muxers.emplace_back(&muxer, streamIndex);
     return true;
 }
@@ -232,3 +225,19 @@ void VideoRecorder::SetCaptureSource(int monitorIdx, CaptureMethod method)
     int height = rect.bottom - rect.top;
     _capturer.open(target, method, width, height);
 }
+
+std::string VideoRecorder::GetEncoderNameForMuxer(AvMuxer& muxer)
+{
+    std::lock_guard<std::mutex> lock(_muxersMtx);
+    auto it = std::find_if(_muxers.begin(), _muxers.end(), [&muxer](const MuxerInfo& info) {
+        return info.muxer == &muxer;
+    });
+    if (it == _muxers.end()) {
+        return std::string();
+    }
+    AVCodecContext* ctx = muxer.GetCodecCtx(it->streamIndex);
+    if (ctx && ctx->codec && ctx->codec->name) {
+        return std::string(ctx->codec->name);
+    }
+    return std::string();
+}

+ 3 - 0
AvRecorder/recorder/video_recorder.h

@@ -7,6 +7,7 @@ using namespace avrecorder::video;
 
 #include "muxer/av_muxer.h"
 #include <deque>
+#include <string>
 // #include <condition_variable>
 // #include <queue>
 
@@ -25,6 +26,8 @@ public:
     double GetLossRate();
     void SetCaptureSource(HWND srcHwnd, CaptureMethod method);
     void SetCaptureSource(int monitorIdx, CaptureMethod method);
+    // 新增:获取指定muxer对应的视频编码器名称
+    std::string GetEncoderNameForMuxer(AvMuxer& muxer);
 
 private:
     struct MuxerInfo {

+ 39 - 2
AvRecorder/ui/av_recorder.cpp

@@ -12,7 +12,17 @@ AvRecorder::AvRecorder(QWidget* parent)
     m_settingsParam.audioParam.bitRate = 160'000;
     m_settingsParam.videoParam.bitRate = 8'000'000;
     m_settingsParam.videoParam.fps = 30;
-    m_settingsParam.videoParam.name = Encoder<MediaType::VIDEO>::GetUsableEncoders().front();
+    {
+        const auto& encs = Encoder<MediaType::VIDEO>::GetUsableEncoders();
+        m_settingsParam.videoParam.name = encs.empty() ? std::string("libx264") : encs.front();
+    }
+    {
+        const auto& aencs = Encoder<MediaType::AUDIO>::GetUsableEncoders();
+        m_settingsParam.audioParam.name = aencs.empty() ? std::string("aac") : aencs.front();
+    }
+    // 设置安全的默认分辨率,避免在捕获首帧前width/height为0
+    m_settingsParam.videoParam.width = 1280;
+    m_settingsParam.videoParam.height = 720;
     m_settingsParam.outputDir = ".";
     m_settingsParam.liveUrl = "rtmp://127.0.0.1:1935/stream/V1";
     m_settingsParam.liveName = "stream";
@@ -114,7 +124,17 @@ void AvRecorder::setSettings(const SettingsPage::Param& param)
     m_settingsParam.audioParam.bitRate = 160'000;
     m_settingsParam.videoParam.bitRate = 8'000'000;
     m_settingsParam.videoParam.fps = 30;
-    m_settingsParam.videoParam.name = Encoder<MediaType::VIDEO>::GetUsableEncoders().front();
+    {
+        const auto& encs = Encoder<MediaType::VIDEO>::GetUsableEncoders();
+        m_settingsParam.videoParam.name = encs.empty() ? std::string("libx264") : encs.front();
+    }
+    {
+        const auto& aencs = Encoder<MediaType::AUDIO>::GetUsableEncoders();
+        m_settingsParam.audioParam.name = aencs.empty() ? std::string("aac") : aencs.front();
+    }
+    // 重置时也保持安全默认分辨率,实际分辨率会在捕获后更新
+    m_settingsParam.videoParam.width = 1280;
+    m_settingsParam.videoParam.height = 720;
     m_settingsParam.outputDir = ".";
     m_settingsParam.liveUrl = param.liveUrl;   // "rtmp://192.168.3.76:1935/stream/V1";
     m_settingsParam.liveName = param.liveName; // "stream";
@@ -321,6 +341,9 @@ void AvRecorder::dealCapture()
     m_speakerWidget->setEnabled(m_audioRecorder.GetCaptureInfo(SPEAKER_INDEX) != nullptr);
     m_fpsLabel->setText(QString("FPS: %1").arg(m_settingsParam.videoParam.fps));
     m_videoEncodeLabel->setText(("编码器: " + m_settingsParam.videoParam.name).c_str());
+    if (m_audioEncodeLabel) {
+        m_audioEncodeLabel->setText(("音频编码器: " + m_settingsParam.audioParam.name).c_str());
+    }
 }
 
 void AvRecorder::stopCapture()
@@ -401,6 +424,18 @@ bool AvRecorder::startStream(std::string_view path, std::string_view format)
         qDebug() << "Failed to start video recording";
         return false;
     }
+    // 同步:展示实际使用的编码器名称(考虑自动回退后的结果)
+    {
+        std::string used = m_videoRecorder.GetEncoderNameForMuxer(m_avMuxer);
+        if (!used.empty()) {
+            m_videoEncodeLabel->setText((std::string("编码器: ") + used).c_str());
+        }
+        std::string aused = m_audioRecorder.GetEncoderNameForMuxer(m_avMuxer);
+        if (!aused.empty()) {
+            m_audioEncodeLabel->setText((std::string("音频编码器: ") + aused).c_str());
+        }
+    }
+
     m_recordTime = QTime::currentTime();
     m_captureStatusLabel->setText("状态: 正在工作");
     m_settingsBtn->setEnabled(false);
@@ -527,6 +562,7 @@ void AvRecorder::updateCaptureList()
 void AvRecorder::initStatusBarUi()
 {
     m_videoEncodeLabel = new QLabel;
+    m_audioEncodeLabel = new QLabel;
     auto hLayout = new QHBoxLayout;
     hLayout->setContentsMargins(0, 0, 0, 0);
     hLayout->addWidget(new QLabel("捕获方式:"));
@@ -543,6 +579,7 @@ void AvRecorder::initStatusBarUi()
 
     // 添加各个状态信息到状态栏
     m_statusBar->addWidget(m_videoEncodeLabel);
+    m_statusBar->addWidget(m_audioEncodeLabel);
 
     auto widget = new QWidget;
     widget->setLayout(hLayout);

+ 1 - 0
AvRecorder/ui/av_recorder.h

@@ -73,6 +73,7 @@ private:
     QLabel* m_captureTimeLabel = nullptr;
     QLabel* m_fpsLabel = nullptr;
     QLabel* m_videoEncodeLabel = nullptr;
+    QLabel* m_audioEncodeLabel = nullptr;
     QLabel* m_videolossRate = nullptr;
 
     QTime m_recordTime;

+ 35 - 1
AvRecorder/ui/settings_page.cpp

@@ -1,6 +1,7 @@
 #include "settings_page.h"
 #include <QFileDialog>
 #include "encoder/video_encoder.h"
+#include "encoder/audio_encoder.h"
 
 SettingsPage::SettingsPage(Param* param, QWidget* parent)
     : QDialog(parent)
@@ -41,6 +42,9 @@ void SettingsPage::_WriteSettings()
     _param->videoParam.fps = _videoFpsBox->value();
     _param->videoParam.name = _videoEncoderBox->currentText().toStdString();
     _param->audioParam.bitRate = _audioBitRateBox->value() * 1000;
+    if (_audioEncoderBox) {
+        _param->audioParam.name = _audioEncoderBox->currentText().toStdString();
+    }
     _param->outputDir = _fileDirEdit->text().toStdString();
     // _param->liveUrl = _liveUrlEdit->text().toStdString();
     // _param->liveName = _liveNameEdit->text().toStdString();
@@ -82,7 +86,15 @@ QGroupBox* SettingsPage::_InitVideoUi()
     for (auto&& encoder : encoders) {
         _videoEncoderBox->addItem(encoder.c_str());
     }
-    _videoEncoderBox->setCurrentText(_param->videoParam.name.c_str());
+    if (_videoEncoderBox->count() == 0) {
+        _videoEncoderBox->addItem("libx264");
+    }
+    // 如果当前参数名不在列表中,则默认选中第一项
+    if (_videoEncoderBox->findText(_param->videoParam.name.c_str()) == -1) {
+        _videoEncoderBox->setCurrentIndex(0);
+    } else {
+        _videoEncoderBox->setCurrentText(_param->videoParam.name.c_str());
+    }
     layout->addLayout(
         _CreateDescription("比特率(kB):", "越高的比特率越清晰, 但越占用硬件资源", _videoBitRateBox));
     layout->addLayout(
@@ -102,8 +114,30 @@ QGroupBox* SettingsPage::_InitAudioUi()
     _audioBitRateBox->setMinimum(0);
     _audioBitRateBox->setMaximum(INT_MAX);
     _audioBitRateBox->setValue(_param->audioParam.bitRate / 1000);
+
+    // 音频编码器选择
+    _audioEncoderBox = new QComboBox;
+    {
+        auto&& encoders = Encoder<MediaType::AUDIO>::GetUsableEncoders();
+        for (auto&& encoder : encoders) {
+            _audioEncoderBox->addItem(encoder.c_str());
+        }
+        if (_audioEncoderBox->count() == 0) {
+            _audioEncoderBox->addItem("aac");
+        }
+        // 如果当前参数名不在列表中,则默认选中第一项
+        if (_audioEncoderBox->findText(_param->audioParam.name.c_str()) == -1) {
+            _audioEncoderBox->setCurrentIndex(0);
+        } else {
+            _audioEncoderBox->setCurrentText(_param->audioParam.name.c_str());
+        }
+    }
     layout->addLayout(
         _CreateDescription("比特率(kB):", "越高的比特率越清晰, 但越占用硬件资源", _audioBitRateBox));
+    layout->addLayout(_CreateDescription(
+        "编码器:",
+        "aac 兼容性最好;opus 音质高但需容器/推流支持;mp3 不建议用于flv",
+        _audioEncoderBox));
     groupBox->setLayout(layout);
     return groupBox;
 }

+ 1 - 0
AvRecorder/ui/settings_page.h

@@ -35,6 +35,7 @@ private:
     QSpinBox* _videoFpsBox = nullptr;
     QComboBox* _videoEncoderBox = nullptr;
     QSpinBox* _audioBitRateBox = nullptr;
+    QComboBox* _audioEncoderBox = nullptr;
     QLineEdit* _fileDirEdit = nullptr;
     QPushButton* _selDirBtn = nullptr;
     QPushButton* _applyBtn = nullptr;