Sfoglia il codice sorgente

修复 错误的constv护理

zhuizhu 7 mesi fa
parent
commit
89ce23f457

+ 2 - 0
LearningSmartClient.pro

@@ -49,6 +49,7 @@ SOURCES += \
     widgets/joinmeetingdialog.cpp \
     widgets/maskoverlay.cpp \
     widgets/meetingselectionwidget.cpp \
+    widgets/recorderwidget.cpp \
     widgets/statswidget.cpp \
     widgets/userprofilewidget.cpp  \
     widgets/windowpreviewwidget.cpp
@@ -90,6 +91,7 @@ HEADERS += \
     widgets/joinmeetingdialog.h \
     widgets/maskoverlay.h \
     widgets/meetingselectionwidget.h \
+    widgets/recorderwidget.h \
     widgets/statswidget.h \
     widgets/userprofilewidget.h  \
     widgets/windowpreviewwidget.h

+ 7 - 7
MainPanel.cpp

@@ -24,7 +24,7 @@
 #include "AvPlayer2/PlayWidget.h"
 #include "api/roomapi.h"
 #include "appevent.h"
-#include "ui/av_recorder.h"
+#include "widgets/recorderwidget.h"
 
 namespace IconUtils {
 QIcon createSettingsIcon()
@@ -276,7 +276,7 @@ void MainPanel::setRole(const QStringList &roleList)
 {
     QWidget *newPlayer = nullptr;
     if (roleList.contains("role.admin")) {
-        newPlayer = new AvRecorder(this);
+        newPlayer = new RecorderWidget(this);
     } else {
         newPlayer = new PlayWidget(this);
     }
@@ -290,11 +290,11 @@ void MainPanel::setRole(const QStringList &roleList)
 void MainPanel::setPushRoomId(const QString &id)
 {
     // 推流配置
-    if (AvRecorder *avRecorder = qobject_cast<AvRecorder *>(playerWidget)) {
-        SettingsPage::Param param;
-        param.liveUrl = "rtmp://106.55.186.74:1935/stream/V1";
-        param.liveName = id.toStdString();
-        avRecorder->setSettings(param);
+    if (RecorderWidget *recorderWidget = qobject_cast<RecorderWidget *>(playerWidget)) {
+        RecorderWidget::Settings settings;
+        settings.liveUrl = "rtmp://106.55.186.74:1935/stream/V1";
+        settings.liveName = id.toStdString();
+        recorderWidget->setSettings(settings);
     }
 
     // 重新进入房间

+ 27 - 3
libs/Recorder/muxer_ffmpeg.cpp

@@ -523,7 +523,8 @@ int muxer_ffmpeg::add_video_stream(const MUX_SETTING_T &setting, record_desktop
             break;
         }
 
-        const_cast<AVOutputFormat*>(_fmt)->video_codec = codec->id;
+        // FFmpeg 7兼容性:不再直接修改AVOutputFormat的video_codec字段
+        // 编码器信息通过AVStream的codecpar设置
 
         AVStream *st = avformat_new_stream(_fmt_ctx, codec);
         if (!st) {
@@ -531,7 +532,7 @@ int muxer_ffmpeg::add_video_stream(const MUX_SETTING_T &setting, record_desktop
             break;
         }
 
-        ffmpeg_set_stream_codec_id(st, AV_CODEC_ID_H264);
+        ffmpeg_set_stream_codec_id(st, _v_stream->v_enc->get_codec_id());
         ffmpeg_set_stream_bit_rate(st, setting.v_bit_rate);
         ffmpeg_set_stream_codec_type(st, AVMEDIA_TYPE_VIDEO);
         st->time_base.den = setting.v_frame_rate;
@@ -695,8 +696,9 @@ int muxer_ffmpeg::add_audio_stream(const MUX_SETTING_T &setting,
             break;
         }
 
+        // FFmpeg 7兼容性:不再直接修改AVOutputFormat的codec字段
+        // 这些字段在新版本中是只读的,编码器信息通过AVStream设置
         AVCodecID audio_codec_id = _a_stream->a_enc->get_codec_id();
-        const_cast<AVOutputFormat*>(_fmt)->audio_codec = audio_codec_id;
 
         AVStream *st = avformat_new_stream(_fmt_ctx, codec);
         if (!st) {
@@ -706,6 +708,28 @@ int muxer_ffmpeg::add_audio_stream(const MUX_SETTING_T &setting,
 
         av_dict_set(&st->metadata, "title", "Track1", 0);
 
+        // 设置音频流编码器参数
+        ffmpeg_set_stream_codec_id(st, audio_codec_id);
+        ffmpeg_set_stream_codec_type(st, AVMEDIA_TYPE_AUDIO);
+        ffmpeg_set_stream_bit_rate(st, setting.a_bit_rate);
+        
+        // 设置采样率到AVStream的codecpar中(FFmpeg 7兼容性)
+#if FFMPEG_VERSION_MAJOR >= 7
+        st->codecpar->sample_rate = setting.a_sample_rate;
+        av_channel_layout_default(&st->codecpar->ch_layout, setting.a_nb_channel);
+        st->codecpar->format = setting.a_sample_fmt;
+#elif FFMPEG_VERSION_MAJOR >= 4
+        st->codecpar->sample_rate = setting.a_sample_rate;
+        st->codecpar->channels = setting.a_nb_channel;
+        st->codecpar->channel_layout = ffmpeg_get_default_channel_layout(setting.a_nb_channel);
+        st->codecpar->format = setting.a_sample_fmt;
+#else
+        st->codec->sample_rate = setting.a_sample_rate;
+        st->codec->channels = setting.a_nb_channel;
+        st->codec->channel_layout = ffmpeg_get_default_channel_layout(setting.a_nb_channel);
+        st->codec->sample_fmt = setting.a_sample_fmt;
+#endif
+        
         st->time_base = {1, setting.a_sample_rate};
 
         AVCodecContext *codec_ctx = ffmpeg_get_codec_context(st);

+ 2 - 2
network/networkaccessmanager.cpp

@@ -13,9 +13,9 @@
 #include <QtConcurrent>
 
 // 基础URL配置
-// static QString base_url("http://106.55.186.74:8200");
+static QString base_url("http://106.55.186.74:8200");
 
-static QString base_url("http://127.0.0.1:8200");
+// static QString base_url("http://127.0.0.1:8200");
 
 static const QString messageVal = "msg";
 

+ 648 - 0
widgets/recorderwidget.cpp

@@ -0,0 +1,648 @@
+#include "recorderwidget.h"
+#include <QApplication>
+#include <QDateTime>
+#include <QDebug>
+#include <QMessageBox>
+#include <QFileDialog>
+#include <QStandardPaths>
+#include <QOpenGLFunctions>
+#include <QPainter>
+
+// 静态实例指针
+RecorderWidget* RecorderWidget::s_instance = nullptr;
+
+RecorderWidget::RecorderWidget(QWidget *parent)
+    : QWidget(parent)
+    , m_mainLayout(nullptr)
+    , m_previewWidget(nullptr)
+    , m_recordButton(nullptr)
+    , m_streamButton(nullptr)
+    , m_settingsButton(nullptr)
+    , m_audioDeviceButton(nullptr)
+    , m_drawCursorCheckBox(nullptr)
+    , m_syncRecordCheckBox(nullptr)
+    , m_encoderComboBox(nullptr)
+    , m_micComboBox(nullptr)
+    , m_speakerComboBox(nullptr)
+    , m_statusBar(nullptr)
+    , m_statusLabel(nullptr)
+    , m_timeLabel(nullptr)
+    , m_encoderLabel(nullptr)
+    , m_previewTimer(nullptr)
+    , m_statusTimer(nullptr)
+    , m_micDevices(nullptr)
+    , m_speakerDevices(nullptr)
+    , m_encoders(nullptr)
+    , m_micCount(0)
+    , m_speakerCount(0)
+    , m_encoderCount(0)
+    , m_isRecording(false)
+    , m_isStreaming(false)
+    , m_isInitialized(false)
+    , m_previewWidth(0)
+    , m_previewHeight(0)
+{
+    // 设置静态实例指针
+    s_instance = this;
+    
+    // 初始化默认设置
+    m_settings.liveUrl = "rtmp://106.55.186.74:1935/stream/V1";
+    m_settings.liveName = "stream";
+    m_settings.outputDir = QStandardPaths::writableLocation(QStandardPaths::MoviesLocation).toStdString();
+    m_settings.videoBitRate = 8000000;
+    m_settings.videoFrameRate = 30;
+    m_settings.videoQuality = 100;
+    m_settings.audioBitRate = 128000;
+    
+    initUI();
+    initRecorder();
+    
+    // 创建定时器
+    m_previewTimer = new QTimer(this);
+    connect(m_previewTimer, &QTimer::timeout, this, &RecorderWidget::updatePreview);
+    
+    m_statusTimer = new QTimer(this);
+    connect(m_statusTimer, &QTimer::timeout, this, &RecorderWidget::updateStatus);
+    m_statusTimer->start(1000); // 每秒更新一次状态
+}
+
+RecorderWidget::~RecorderWidget()
+{
+    releaseRecorder();
+    s_instance = nullptr;
+}
+
+void RecorderWidget::initUI()
+{
+    m_mainLayout = new QVBoxLayout(this);
+    m_mainLayout->setContentsMargins(0, 0, 0, 0);
+    
+    // 预览区域
+    m_previewWidget = new QOpenGLWidget(this);
+    m_previewWidget->setMinimumSize(640, 480);
+    m_previewWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+    
+    // 控制区域
+    QWidget* controlWidget = new QWidget(this);
+    QHBoxLayout* controlLayout = new QHBoxLayout(controlWidget);
+    
+    // 左侧设备选择区
+    QGroupBox* deviceGroup = new QGroupBox("设备选择", this);
+    QVBoxLayout* deviceLayout = new QVBoxLayout(deviceGroup);
+    
+    // 编码器选择
+    QHBoxLayout* encoderLayout = new QHBoxLayout();
+    encoderLayout->addWidget(new QLabel("编码器:"));
+    m_encoderComboBox = new QComboBox(this);
+    encoderLayout->addWidget(m_encoderComboBox);
+    deviceLayout->addLayout(encoderLayout);
+    
+    // 麦克风选择
+    QHBoxLayout* micLayout = new QHBoxLayout();
+    micLayout->addWidget(new QLabel("麦克风:"));
+    m_micComboBox = new QComboBox(this);
+    micLayout->addWidget(m_micComboBox);
+    deviceLayout->addLayout(micLayout);
+    
+    // 扬声器选择
+    QHBoxLayout* speakerLayout = new QHBoxLayout();
+    speakerLayout->addWidget(new QLabel("扬声器:"));
+    m_speakerComboBox = new QComboBox(this);
+    speakerLayout->addWidget(m_speakerComboBox);
+    deviceLayout->addLayout(speakerLayout);
+    
+    // 音频设备按钮
+    m_audioDeviceButton = new QPushButton("刷新设备", this);
+    deviceLayout->addWidget(m_audioDeviceButton);
+    
+    // 右侧操作区
+    QGroupBox* actionGroup = new QGroupBox("操作", this);
+    QVBoxLayout* actionLayout = new QVBoxLayout(actionGroup);
+    
+    // 选项
+    m_drawCursorCheckBox = new QCheckBox("绘制鼠标指针", this);
+    m_drawCursorCheckBox->setChecked(true);
+    m_syncRecordCheckBox = new QCheckBox("推流时同步录制", this);
+    actionLayout->addWidget(m_drawCursorCheckBox);
+    actionLayout->addWidget(m_syncRecordCheckBox);
+    
+    // 按钮
+    m_recordButton = new QPushButton("开始录制", this);
+    m_streamButton = new QPushButton("开始推流", this);
+    m_settingsButton = new QPushButton("设置", this);
+    
+    actionLayout->addWidget(m_recordButton);
+    actionLayout->addWidget(m_streamButton);
+    actionLayout->addWidget(m_settingsButton);
+    
+    // 添加到控制布局
+    controlLayout->addWidget(deviceGroup, 1);
+    controlLayout->addWidget(actionGroup, 1);
+    controlLayout->addStretch();
+    
+    // 状态栏
+    initStatusBar();
+    
+    // 添加到主布局
+    m_mainLayout->addWidget(m_previewWidget, 1);
+    m_mainLayout->addWidget(controlWidget, 0);
+    m_mainLayout->addWidget(m_statusBar, 0);
+    
+    // 连接信号槽
+    connect(m_recordButton, &QPushButton::clicked, this, &RecorderWidget::onRecordButtonClicked);
+    connect(m_streamButton, &QPushButton::clicked, this, &RecorderWidget::onStreamButtonClicked);
+    connect(m_settingsButton, &QPushButton::clicked, this, &RecorderWidget::onSettingsButtonClicked);
+    connect(m_audioDeviceButton, &QPushButton::clicked, this, &RecorderWidget::onAudioDeviceButtonClicked);
+}
+
+void RecorderWidget::initStatusBar()
+{
+    m_statusBar = new QStatusBar(this);
+    
+    m_statusLabel = new QLabel("状态: 就绪", this);
+    m_timeLabel = new QLabel("00:00:00", this);
+    m_encoderLabel = new QLabel("编码器: 未选择", this);
+    
+    m_statusBar->addWidget(m_statusLabel);
+    m_statusBar->addPermanentWidget(m_encoderLabel);
+    m_statusBar->addPermanentWidget(m_timeLabel);
+}
+
+void RecorderWidget::initRecorder()
+{
+    // 设置回调函数
+    setupCallbacks();
+    
+    // 设置录制器日志路径
+    QString logPath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/recorder.log";
+    recorder_set_logpath(logPath.toUtf8().constData());
+    
+    // 延迟刷新设备和编码器列表,避免在构造函数中调用可能导致异常的API
+    QTimer::singleShot(100, this, [this]() {
+        refreshAudioDevices();
+        refreshVideoEncoders();
+    });
+}
+
+void RecorderWidget::releaseRecorder()
+{
+    if (m_isRecording || m_isStreaming) {
+        recorder_stop();
+    }
+    
+    if (m_isInitialized) {
+        recorder_release();
+        m_isInitialized = false;
+    }
+    
+    // 释放设备数组
+    if (m_micDevices) {
+        recorder_free_array(m_micDevices);
+        m_micDevices = nullptr;
+    }
+    if (m_speakerDevices) {
+        recorder_free_array(m_speakerDevices);
+        m_speakerDevices = nullptr;
+    }
+    if (m_encoders) {
+        recorder_free_array(m_encoders);
+        m_encoders = nullptr;
+    }
+}
+
+void RecorderWidget::setupCallbacks()
+{
+    m_callbacks.func_duration = onDurationCallback;
+    m_callbacks.func_error = onErrorCallback;
+    m_callbacks.func_device_change = onDeviceChangeCallback;
+    m_callbacks.func_preview_yuv = onPreviewYUVCallback;
+    m_callbacks.func_preview_audio = onPreviewAudioCallback;
+}
+
+void RecorderWidget::refreshAudioDevices()
+{
+    // 释放之前的设备数组
+    if (m_micDevices) {
+        recorder_free_array(m_micDevices);
+        m_micDevices = nullptr;
+    }
+    if (m_speakerDevices) {
+        recorder_free_array(m_speakerDevices);
+        m_speakerDevices = nullptr;
+    }
+    
+    // 获取麦克风设备
+    try {
+        m_micCount = recorder_get_mics(&m_micDevices);
+        m_micComboBox->clear();
+        for (int i = 0; i < m_micCount; ++i) {
+            QString deviceName = QString::fromUtf8(m_micDevices[i].name);
+            m_micComboBox->addItem(deviceName);
+            if (m_micDevices[i].is_default) {
+                m_micComboBox->setCurrentIndex(i);
+            }
+        }
+    } catch (...) {
+        m_micCount = 0;
+        qWarning() << "Failed to get microphone devices";
+    }
+    
+    // 获取扬声器设备
+    try {
+        m_speakerCount = recorder_get_speakers(&m_speakerDevices);
+        m_speakerComboBox->clear();
+        for (int i = 0; i < m_speakerCount; ++i) {
+            QString deviceName = QString::fromUtf8(m_speakerDevices[i].name);
+            m_speakerComboBox->addItem(deviceName);
+            if (m_speakerDevices[i].is_default) {
+                m_speakerComboBox->setCurrentIndex(i);
+            }
+        }
+    } catch (...) {
+        m_speakerCount = 0;
+        qWarning() << "Failed to get speaker devices";
+    }
+}
+
+void RecorderWidget::refreshVideoEncoders()
+{
+    // 释放之前的编码器数组
+    if (m_encoders) {
+        recorder_free_array(m_encoders);
+        m_encoders = nullptr;
+    }
+    
+    // 获取视频编码器
+    try {
+        m_encoderCount = recorder_get_vencoders(&m_encoders);
+        m_encoderComboBox->clear();
+        for (int i = 0; i < m_encoderCount; ++i) {
+            QString encoderName = QString::fromUtf8(m_encoders[i].name);
+            m_encoderComboBox->addItem(encoderName);
+        }
+        
+        // 默认选择第一个编码器
+        if (m_encoderCount > 0) {
+            m_encoderComboBox->setCurrentIndex(0);
+            m_encoderLabel->setText(QString("编码器: %1").arg(m_encoders[0].name));
+        }
+    } catch (...) {
+        m_encoderCount = 0;
+        qWarning() << "Failed to get video encoders";
+        m_encoderLabel->setText("编码器: 获取失败");
+    }
+}
+
+void RecorderWidget::setSettings(const Settings& settings)
+{
+    m_settings = settings;
+}
+
+bool RecorderWidget::startRecording()
+{
+    if (m_isRecording) {
+        return true;
+    }
+    
+    // 准备录制设置
+    memset(&m_recorderSetting, 0, sizeof(m_recorderSetting));
+    
+    // 视频设置
+    m_recorderSetting.v_left = 0;
+    m_recorderSetting.v_top = 0;
+    m_recorderSetting.v_width = GetSystemMetrics(SM_CXSCREEN);
+    m_recorderSetting.v_height = GetSystemMetrics(SM_CYSCREEN);
+    m_recorderSetting.v_qb = m_settings.videoQuality;
+    m_recorderSetting.v_bit_rate = m_settings.videoBitRate;
+    m_recorderSetting.v_frame_rate = m_settings.videoFrameRate;
+    
+    // 选择编码器
+    int encoderIndex = m_encoderComboBox->currentIndex();
+    if (encoderIndex >= 0 && encoderIndex < m_encoderCount) {
+        m_recorderSetting.v_enc_id = m_encoders[encoderIndex].id;
+    }
+    
+    // 输出文件
+    QString fileName = QString::fromStdString(m_settings.outputDir);
+    if (!fileName.endsWith("/") && !fileName.endsWith("\\")) {
+        fileName += "/";
+    }
+    fileName += QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss") + ".mp4";
+    strcpy_s(m_recorderSetting.output, fileName.toUtf8().constData());
+    
+    // 音频设备设置
+    int micIndex = m_micComboBox->currentIndex();
+    if (micIndex >= 0 && micIndex < m_micCount) {
+        strcpy_s(m_recorderSetting.a_mic.id, m_micDevices[micIndex].id);
+        strcpy_s(m_recorderSetting.a_mic.name, m_micDevices[micIndex].name);
+        m_recorderSetting.a_mic.is_default = m_micDevices[micIndex].is_default;
+    }
+    
+    int speakerIndex = m_speakerComboBox->currentIndex();
+    if (speakerIndex >= 0 && speakerIndex < m_speakerCount) {
+        strcpy_s(m_recorderSetting.a_speaker.id, m_speakerDevices[speakerIndex].id);
+        strcpy_s(m_recorderSetting.a_speaker.name, m_speakerDevices[speakerIndex].name);
+        m_recorderSetting.a_speaker.is_default = m_speakerDevices[speakerIndex].is_default;
+    }
+    
+    // 异步初始化和启动录制器,避免主线程阻塞
+    QFuture<void> future = QtConcurrent::run([this]() {
+        // 初始化录制器
+        int result = recorder_init(m_recorderSetting, m_callbacks);
+        if (result != 0) {
+            QString error = QString("初始化录制器失败: %1").arg(recorder_err2str(result));
+            QMetaObject::invokeMethod(this, [this, error]() {
+                emit errorOccurred(error);
+                m_recordButton->setText("开始录制");
+                m_streamButton->setEnabled(true);
+                m_statusLabel->setText("状态: 就绪");
+            }, Qt::QueuedConnection);
+            return;
+        }
+        
+        m_isInitialized = true;
+        
+        // 启用预览
+        recorder_set_preview_enabled(1);
+        
+        // 开始录制
+        result = recorder_start();
+        if (result != 0) {
+            QString error = QString("开始录制失败: %1").arg(recorder_err2str(result));
+            QMetaObject::invokeMethod(this, [this, error]() {
+                emit errorOccurred(error);
+                recorder_release();
+                m_isInitialized = false;
+                m_recordButton->setText("开始录制");
+                m_streamButton->setEnabled(true);
+                m_statusLabel->setText("状态: 就绪");
+            }, Qt::QueuedConnection);
+            return;
+        }
+        
+        // 成功启动录制
+        QMetaObject::invokeMethod(this, [this]() {
+            m_isRecording = true;
+            m_recordStartTime = QTime::currentTime();
+            
+            // 开始预览定时器
+            m_previewTimer->start(33); // 约30fps
+            
+            emit recordingStarted();
+        }, Qt::QueuedConnection);
+    });
+    
+    // 立即更新UI状态,显示正在初始化
+    m_statusLabel->setText("状态: 初始化中...");
+    
+    return true;
+}
+
+void RecorderWidget::stopRecording()
+{
+    if (!m_isRecording) {
+        return;
+    }
+    
+    recorder_stop();
+    
+    if (m_isInitialized) {
+        recorder_release();
+        m_isInitialized = false;
+    }
+    
+    m_isRecording = false;
+    
+    // 停止预览定时器
+    m_previewTimer->stop();
+    
+    emit recordingStopped();
+}
+
+bool RecorderWidget::startStreaming()
+{
+    if (m_isStreaming) {
+        return true;
+    }
+    
+    // 准备推流设置
+    memset(&m_recorderSetting, 0, sizeof(m_recorderSetting));
+    
+    // 视频设置
+    m_recorderSetting.v_left = 0;
+    m_recorderSetting.v_top = 0;
+    m_recorderSetting.v_width = GetSystemMetrics(SM_CXSCREEN);
+    m_recorderSetting.v_height = GetSystemMetrics(SM_CYSCREEN);
+    m_recorderSetting.v_qb = m_settings.videoQuality;
+    m_recorderSetting.v_bit_rate = m_settings.videoBitRate;
+    m_recorderSetting.v_frame_rate = m_settings.videoFrameRate;
+    
+    // 选择编码器
+    int encoderIndex = m_encoderComboBox->currentIndex();
+    if (encoderIndex >= 0 && encoderIndex < m_encoderCount) {
+        m_recorderSetting.v_enc_id = m_encoders[encoderIndex].id;
+    }
+    
+    // 推流地址
+    QString streamUrl = QString::fromStdString(m_settings.liveUrl + "/" + m_settings.liveName);
+    strcpy_s(m_recorderSetting.output, streamUrl.toUtf8().constData());
+    
+    // 音频设备设置
+    int micIndex = m_micComboBox->currentIndex();
+    if (micIndex >= 0 && micIndex < m_micCount) {
+        strcpy_s(m_recorderSetting.a_mic.id, m_micDevices[micIndex].id);
+        strcpy_s(m_recorderSetting.a_mic.name, m_micDevices[micIndex].name);
+        m_recorderSetting.a_mic.is_default = m_micDevices[micIndex].is_default;
+    }
+    
+    int speakerIndex = m_speakerComboBox->currentIndex();
+    if (speakerIndex >= 0 && speakerIndex < m_speakerCount) {
+        strcpy_s(m_recorderSetting.a_speaker.id, m_speakerDevices[speakerIndex].id);
+        strcpy_s(m_recorderSetting.a_speaker.name, m_speakerDevices[speakerIndex].name);
+        m_recorderSetting.a_speaker.is_default = m_speakerDevices[speakerIndex].is_default;
+    }
+    
+    // 异步初始化和启动推流,避免主线程阻塞
+    QFuture<void> future = QtConcurrent::run([this]() {
+        // 初始化录制器
+        int result = recorder_init(m_recorderSetting, m_callbacks);
+        if (result != 0) {
+            QString error = QString("初始化推流失败: %1").arg(recorder_err2str(result));
+            QMetaObject::invokeMethod(this, [this, error]() {
+                emit errorOccurred(error);
+                m_streamButton->setText("开始推流");
+                m_recordButton->setEnabled(true);
+                m_statusLabel->setText("状态: 就绪");
+            }, Qt::QueuedConnection);
+            return;
+        }
+        
+        m_isInitialized = true;
+        
+        // 启用预览
+        recorder_set_preview_enabled(1);
+        
+        // 开始推流
+        result = recorder_start();
+        if (result != 0) {
+            QString error = QString("开始推流失败: %1").arg(recorder_err2str(result));
+            QMetaObject::invokeMethod(this, [this, error]() {
+                emit errorOccurred(error);
+                recorder_release();
+                m_isInitialized = false;
+                m_streamButton->setText("开始推流");
+                m_recordButton->setEnabled(true);
+                m_statusLabel->setText("状态: 就绪");
+            }, Qt::QueuedConnection);
+            return;
+        }
+        
+        // 成功启动推流
+        QMetaObject::invokeMethod(this, [this]() {
+            m_isStreaming = true;
+            m_recordStartTime = QTime::currentTime();
+            
+            // 开始预览定时器
+            m_previewTimer->start(33); // 约30fps
+            
+            emit streamingStarted();
+        }, Qt::QueuedConnection);
+    });
+    
+    // 立即更新UI状态,显示正在初始化
+    m_statusLabel->setText("状态: 连接中...");
+    
+    return true;
+}
+
+void RecorderWidget::stopStreaming()
+{
+    if (!m_isStreaming) {
+        return;
+    }
+    
+    recorder_stop();
+    
+    if (m_isInitialized) {
+        recorder_release();
+        m_isInitialized = false;
+    }
+    
+    m_isStreaming = false;
+    
+    // 停止预览定时器
+    m_previewTimer->stop();
+    
+    emit streamingStopped();
+}
+
+void RecorderWidget::onRecordButtonClicked()
+{
+    if (!m_isRecording) {
+        if (startRecording()) {
+            m_recordButton->setText("停止录制");
+            m_streamButton->setEnabled(false);
+            m_statusLabel->setText("状态: 录制中");
+        }
+    } else {
+        stopRecording();
+        m_recordButton->setText("开始录制");
+        m_streamButton->setEnabled(true);
+        m_statusLabel->setText("状态: 就绪");
+    }
+}
+
+void RecorderWidget::onStreamButtonClicked()
+{
+    if (!m_isStreaming) {
+        if (startStreaming()) {
+            m_streamButton->setText("停止推流");
+            m_recordButton->setEnabled(!m_syncRecordCheckBox->isChecked());
+            m_statusLabel->setText("状态: 推流中");
+        }
+    } else {
+        stopStreaming();
+        m_streamButton->setText("开始推流");
+        m_recordButton->setEnabled(true);
+        m_statusLabel->setText("状态: 就绪");
+    }
+}
+
+void RecorderWidget::onSettingsButtonClicked()
+{
+    // 打开设置对话框
+    QMessageBox::information(this, "设置", "设置功能待实现");
+}
+
+void RecorderWidget::onAudioDeviceButtonClicked()
+{
+    refreshAudioDevices();
+    QMessageBox::information(this, "音频设备", "设备列表已刷新");
+}
+
+void RecorderWidget::updatePreview()
+{
+    // 预览更新在回调函数中处理
+    if (m_previewWidget && m_previewWidth > 0 && m_previewHeight > 0) {
+        m_previewWidget->update();
+    }
+}
+
+void RecorderWidget::updateStatus()
+{
+    if (m_isRecording || m_isStreaming) {
+        QTime currentTime = QTime::currentTime();
+        int elapsed = m_recordStartTime.secsTo(currentTime);
+        int hours = elapsed / 3600;
+        int minutes = (elapsed % 3600) / 60;
+        int seconds = elapsed % 60;
+        
+        QString timeStr = QString("%1:%2:%3")
+                         .arg(hours, 2, 10, QChar('0'))
+                         .arg(minutes, 2, 10, QChar('0'))
+                         .arg(seconds, 2, 10, QChar('0'));
+        m_timeLabel->setText(timeStr);
+    } else {
+        m_timeLabel->setText("00:00:00");
+    }
+}
+
+// 静态回调函数实现
+void RecorderWidget::onDurationCallback(uint64_t duration)
+{
+    if (s_instance) {
+        // 可以在这里处理持续时间更新
+        qDebug() << "Recording duration:" << duration << "ms";
+    }
+}
+
+void RecorderWidget::onErrorCallback(int error)
+{
+    if (s_instance) {
+        QString errorStr = QString("录制错误: %1").arg(recorder_err2str(error));
+        QMetaObject::invokeMethod(s_instance, "errorOccurred", Qt::QueuedConnection, Q_ARG(QString, errorStr));
+    }
+}
+
+void RecorderWidget::onDeviceChangeCallback(int type)
+{
+    if (s_instance) {
+        // 设备变化时刷新设备列表
+        QMetaObject::invokeMethod(s_instance, "refreshAudioDevices", Qt::QueuedConnection);
+    }
+}
+
+void RecorderWidget::onPreviewYUVCallback(const unsigned char *data, unsigned int size, int width, int height, int type)
+{
+    if (s_instance && data && size > 0) {
+        QMutexLocker locker(&s_instance->m_previewMutex);
+        s_instance->m_previewData = QByteArray(reinterpret_cast<const char*>(data), size);
+        s_instance->m_previewWidth = width;
+        s_instance->m_previewHeight = height;
+    }
+}
+
+void RecorderWidget::onPreviewAudioCallback()
+{
+    // 音频预览回调,暂时不处理
+}

+ 147 - 0
widgets/recorderwidget.h

@@ -0,0 +1,147 @@
+#pragma once
+
+#include <QWidget>
+#include <QVBoxLayout>
+#include <QHBoxLayout>
+#include <QPushButton>
+#include <QLabel>
+#include <QCheckBox>
+#include <QComboBox>
+#include <QGroupBox>
+#include <QTimer>
+#include <QTime>
+#include <QStatusBar>
+#include <QOpenGLWidget>
+#include <QOpenGLFunctions>
+#include <QOpenGLShaderProgram>
+#include <QOpenGLTexture>
+#include <QMutex>
+#include <QThread>
+#include <QFuture>
+#include <QtConcurrent>
+
+// 引入新的Recorder库
+#include "../libs/Recorder/export.h"
+
+/**
+ * 基于新Recorder库的录制推流组件
+ * 用于替换原有的AvRecorder实现
+ */
+class RecorderWidget : public QWidget
+{
+    Q_OBJECT
+
+public:
+    struct Settings {
+        std::string liveUrl = "rtmp://106.55.186.74:1935/stream/V1";
+        std::string liveName = "stream";
+        std::string outputDir = ".";
+        int videoBitRate = 8000000;  // 8Mbps
+        int videoFrameRate = 30;
+        int videoQuality = 100;
+        int audioBitRate = 128000;   // 128kbps
+    };
+
+public:
+    explicit RecorderWidget(QWidget *parent = nullptr);
+    ~RecorderWidget();
+
+    // 设置录制推流参数
+    void setSettings(const Settings& settings);
+    
+    // 开始录制
+    bool startRecording();
+    
+    // 停止录制
+    void stopRecording();
+    
+    // 开始推流
+    bool startStreaming();
+    
+    // 停止推流
+    void stopStreaming();
+    
+    // 获取状态栏
+    QWidget* statusBar() const { return m_statusBar; }
+
+public slots:
+    void onRecordButtonClicked();
+    void onStreamButtonClicked();
+    void onSettingsButtonClicked();
+    void onAudioDeviceButtonClicked();
+    void updatePreview();
+    void updateStatus();
+
+signals:
+    void recordingStarted();
+    void recordingStopped();
+    void streamingStarted();
+    void streamingStopped();
+    void errorOccurred(const QString& error);
+
+private:
+    void initUI();
+    void initStatusBar();
+    void initRecorder();
+    void releaseRecorder();
+    void setupCallbacks();
+    void refreshAudioDevices();
+    void refreshVideoEncoders();
+    
+    // 回调函数
+    static void onDurationCallback(uint64_t duration);
+    static void onErrorCallback(int error);
+    static void onDeviceChangeCallback(int type);
+    static void onPreviewYUVCallback(const unsigned char *data, unsigned int size, int width, int height, int type);
+    static void onPreviewAudioCallback();
+
+private:
+    // UI组件
+    QVBoxLayout* m_mainLayout = nullptr;
+    QOpenGLWidget* m_previewWidget = nullptr;
+    QPushButton* m_recordButton = nullptr;
+    QPushButton* m_streamButton = nullptr;
+    QPushButton* m_settingsButton = nullptr;
+    QPushButton* m_audioDeviceButton = nullptr;
+    QCheckBox* m_drawCursorCheckBox = nullptr;
+    QCheckBox* m_syncRecordCheckBox = nullptr;
+    QComboBox* m_encoderComboBox = nullptr;
+    QComboBox* m_micComboBox = nullptr;
+    QComboBox* m_speakerComboBox = nullptr;
+    QStatusBar* m_statusBar = nullptr;
+    QLabel* m_statusLabel = nullptr;
+    QLabel* m_timeLabel = nullptr;
+    QLabel* m_encoderLabel = nullptr;
+    
+    // 定时器
+    QTimer* m_previewTimer = nullptr;
+    QTimer* m_statusTimer = nullptr;
+    
+    // 录制相关
+    Settings m_settings;
+    AMRECORDER_SETTING m_recorderSetting;
+    AMRECORDER_CALLBACK m_callbacks;
+    
+    // 设备信息
+    AMRECORDER_DEVICE* m_micDevices = nullptr;
+    AMRECORDER_DEVICE* m_speakerDevices = nullptr;
+    AMRECORDER_ENCODERS* m_encoders = nullptr;
+    int m_micCount = 0;
+    int m_speakerCount = 0;
+    int m_encoderCount = 0;
+    
+    // 状态
+    bool m_isRecording = false;
+    bool m_isStreaming = false;
+    bool m_isInitialized = false;
+    QTime m_recordStartTime;
+    
+    // 预览相关
+    QMutex m_previewMutex;
+    QByteArray m_previewData;
+    int m_previewWidth = 0;
+    int m_previewHeight = 0;
+    
+    // 静态实例指针,用于回调
+    static RecorderWidget* s_instance;
+};