recorderwidget.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  1. #include "recorderwidget.h"
  2. #include <QApplication>
  3. #include <QDateTime>
  4. #include <QDebug>
  5. #include <QMessageBox>
  6. #include <QFileDialog>
  7. #include <QStandardPaths>
  8. #include <QOpenGLFunctions>
  9. #include <QPainter>
  10. // 静态实例指针
  11. RecorderWidget* RecorderWidget::s_instance = nullptr;
  12. RecorderWidget::RecorderWidget(QWidget *parent)
  13. : QWidget(parent)
  14. , m_mainLayout(nullptr)
  15. , m_previewWidget(nullptr)
  16. , m_recordButton(nullptr)
  17. , m_streamButton(nullptr)
  18. , m_settingsButton(nullptr)
  19. , m_audioDeviceButton(nullptr)
  20. , m_drawCursorCheckBox(nullptr)
  21. , m_syncRecordCheckBox(nullptr)
  22. , m_encoderComboBox(nullptr)
  23. , m_micComboBox(nullptr)
  24. , m_speakerComboBox(nullptr)
  25. , m_statusBar(nullptr)
  26. , m_statusLabel(nullptr)
  27. , m_timeLabel(nullptr)
  28. , m_encoderLabel(nullptr)
  29. , m_previewTimer(nullptr)
  30. , m_statusTimer(nullptr)
  31. , m_micDevices(nullptr)
  32. , m_speakerDevices(nullptr)
  33. , m_encoders(nullptr)
  34. , m_micCount(0)
  35. , m_speakerCount(0)
  36. , m_encoderCount(0)
  37. , m_isRecording(false)
  38. , m_isStreaming(false)
  39. , m_isInitialized(false)
  40. , m_previewWidth(0)
  41. , m_previewHeight(0)
  42. {
  43. // 设置静态实例指针
  44. s_instance = this;
  45. // rtmp://106.55.186.74:1935/stream/V1/0198da41-cdb6-78e3-879d-2ea32d58f73f
  46. // 初始化默认设置
  47. m_settings.liveUrl = "rtmp://106.55.186.74:1935/stream/V1";
  48. m_settings.liveName = "0198da41-cdb6-78e3-879d-2ea32d58f73f";
  49. m_settings.outputDir = QStandardPaths::writableLocation(QStandardPaths::MoviesLocation).toStdString();
  50. m_settings.videoBitRate = 8000000;
  51. m_settings.videoFrameRate = 30;
  52. m_settings.videoQuality = 100;
  53. m_settings.audioBitRate = 128000;
  54. initUI();
  55. initRecorder();
  56. // 创建定时器
  57. m_previewTimer = new QTimer(this);
  58. connect(m_previewTimer, &QTimer::timeout, this, &RecorderWidget::updatePreview);
  59. m_statusTimer = new QTimer(this);
  60. connect(m_statusTimer, &QTimer::timeout, this, &RecorderWidget::updateStatus);
  61. m_statusTimer->start(1000); // 每秒更新一次状态
  62. }
  63. RecorderWidget::~RecorderWidget()
  64. {
  65. releaseRecorder();
  66. s_instance = nullptr;
  67. }
  68. void RecorderWidget::initUI()
  69. {
  70. m_mainLayout = new QVBoxLayout(this);
  71. m_mainLayout->setContentsMargins(0, 0, 0, 0);
  72. // 预览区域
  73. m_previewWidget = new QOpenGLWidget(this);
  74. m_previewWidget->setMinimumSize(640, 480);
  75. m_previewWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  76. // 控制区域
  77. QWidget* controlWidget = new QWidget(this);
  78. QHBoxLayout* controlLayout = new QHBoxLayout(controlWidget);
  79. // 左侧设备选择区
  80. QGroupBox* deviceGroup = new QGroupBox("设备选择", this);
  81. QVBoxLayout* deviceLayout = new QVBoxLayout(deviceGroup);
  82. // 编码器选择
  83. QHBoxLayout* encoderLayout = new QHBoxLayout();
  84. encoderLayout->addWidget(new QLabel("编码器:"));
  85. m_encoderComboBox = new QComboBox(this);
  86. encoderLayout->addWidget(m_encoderComboBox);
  87. deviceLayout->addLayout(encoderLayout);
  88. // 麦克风选择
  89. QHBoxLayout* micLayout = new QHBoxLayout();
  90. micLayout->addWidget(new QLabel("麦克风:"));
  91. m_micComboBox = new QComboBox(this);
  92. micLayout->addWidget(m_micComboBox);
  93. deviceLayout->addLayout(micLayout);
  94. // 扬声器选择
  95. QHBoxLayout* speakerLayout = new QHBoxLayout();
  96. speakerLayout->addWidget(new QLabel("扬声器:"));
  97. m_speakerComboBox = new QComboBox(this);
  98. speakerLayout->addWidget(m_speakerComboBox);
  99. deviceLayout->addLayout(speakerLayout);
  100. // 音频设备按钮
  101. m_audioDeviceButton = new QPushButton("刷新设备", this);
  102. deviceLayout->addWidget(m_audioDeviceButton);
  103. // 右侧操作区
  104. QGroupBox* actionGroup = new QGroupBox("操作", this);
  105. QVBoxLayout* actionLayout = new QVBoxLayout(actionGroup);
  106. // 选项
  107. m_drawCursorCheckBox = new QCheckBox("绘制鼠标指针", this);
  108. m_drawCursorCheckBox->setChecked(true);
  109. m_syncRecordCheckBox = new QCheckBox("推流时同步录制", this);
  110. actionLayout->addWidget(m_drawCursorCheckBox);
  111. actionLayout->addWidget(m_syncRecordCheckBox);
  112. // 按钮
  113. m_recordButton = new QPushButton("开始录制", this);
  114. m_streamButton = new QPushButton("开始推流", this);
  115. m_settingsButton = new QPushButton("设置", this);
  116. actionLayout->addWidget(m_recordButton);
  117. actionLayout->addWidget(m_streamButton);
  118. actionLayout->addWidget(m_settingsButton);
  119. // 添加到控制布局
  120. controlLayout->addWidget(deviceGroup, 1);
  121. controlLayout->addWidget(actionGroup, 1);
  122. controlLayout->addStretch();
  123. // 状态栏
  124. initStatusBar();
  125. // 添加到主布局
  126. m_mainLayout->addWidget(m_previewWidget, 1);
  127. m_mainLayout->addWidget(controlWidget, 0);
  128. m_mainLayout->addWidget(m_statusBar, 0);
  129. // 连接信号槽
  130. connect(m_recordButton, &QPushButton::clicked, this, &RecorderWidget::onRecordButtonClicked);
  131. connect(m_streamButton, &QPushButton::clicked, this, &RecorderWidget::onStreamButtonClicked);
  132. connect(m_settingsButton, &QPushButton::clicked, this, &RecorderWidget::onSettingsButtonClicked);
  133. connect(m_audioDeviceButton, &QPushButton::clicked, this, &RecorderWidget::onAudioDeviceButtonClicked);
  134. }
  135. void RecorderWidget::initStatusBar()
  136. {
  137. m_statusBar = new QStatusBar(this);
  138. m_statusLabel = new QLabel("状态: 就绪", this);
  139. m_timeLabel = new QLabel("00:00:00", this);
  140. m_encoderLabel = new QLabel("编码器: 未选择", this);
  141. m_statusBar->addWidget(m_statusLabel);
  142. m_statusBar->addPermanentWidget(m_encoderLabel);
  143. m_statusBar->addPermanentWidget(m_timeLabel);
  144. }
  145. void RecorderWidget::initRecorder()
  146. {
  147. // 设置回调函数
  148. setupCallbacks();
  149. // 设置录制器日志路径
  150. QString logPath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/recorder.log";
  151. recorder_set_logpath(logPath.toUtf8().constData());
  152. // 延迟刷新设备和编码器列表,避免在构造函数中调用可能导致异常的API
  153. QTimer::singleShot(100, this, [this]() {
  154. refreshAudioDevices();
  155. refreshVideoEncoders();
  156. });
  157. }
  158. void RecorderWidget::releaseRecorder()
  159. {
  160. if (m_isRecording || m_isStreaming) {
  161. recorder_stop();
  162. }
  163. if (m_isInitialized) {
  164. recorder_release();
  165. m_isInitialized = false;
  166. }
  167. // 释放设备数组
  168. if (m_micDevices) {
  169. recorder_free_array(m_micDevices);
  170. m_micDevices = nullptr;
  171. }
  172. if (m_speakerDevices) {
  173. recorder_free_array(m_speakerDevices);
  174. m_speakerDevices = nullptr;
  175. }
  176. if (m_encoders) {
  177. recorder_free_array(m_encoders);
  178. m_encoders = nullptr;
  179. }
  180. }
  181. void RecorderWidget::setupCallbacks()
  182. {
  183. m_callbacks.func_duration = onDurationCallback;
  184. m_callbacks.func_error = onErrorCallback;
  185. m_callbacks.func_device_change = onDeviceChangeCallback;
  186. m_callbacks.func_preview_yuv = onPreviewYUVCallback;
  187. m_callbacks.func_preview_audio = onPreviewAudioCallback;
  188. }
  189. void RecorderWidget::refreshAudioDevices()
  190. {
  191. // 释放之前的设备数组
  192. if (m_micDevices) {
  193. recorder_free_array(m_micDevices);
  194. m_micDevices = nullptr;
  195. }
  196. if (m_speakerDevices) {
  197. recorder_free_array(m_speakerDevices);
  198. m_speakerDevices = nullptr;
  199. }
  200. // 获取麦克风设备
  201. try {
  202. m_micCount = recorder_get_mics(&m_micDevices);
  203. m_micComboBox->clear();
  204. for (int i = 0; i < m_micCount; ++i) {
  205. QString deviceName = QString::fromUtf8(m_micDevices[i].name);
  206. m_micComboBox->addItem(deviceName);
  207. if (m_micDevices[i].is_default) {
  208. m_micComboBox->setCurrentIndex(i);
  209. }
  210. }
  211. } catch (...) {
  212. m_micCount = 0;
  213. qWarning() << "Failed to get microphone devices";
  214. }
  215. // 获取扬声器设备
  216. try {
  217. m_speakerCount = recorder_get_speakers(&m_speakerDevices);
  218. m_speakerComboBox->clear();
  219. for (int i = 0; i < m_speakerCount; ++i) {
  220. QString deviceName = QString::fromUtf8(m_speakerDevices[i].name);
  221. m_speakerComboBox->addItem(deviceName);
  222. if (m_speakerDevices[i].is_default) {
  223. m_speakerComboBox->setCurrentIndex(i);
  224. }
  225. }
  226. } catch (...) {
  227. m_speakerCount = 0;
  228. qWarning() << "Failed to get speaker devices";
  229. }
  230. }
  231. void RecorderWidget::refreshVideoEncoders()
  232. {
  233. // 释放之前的编码器数组
  234. if (m_encoders) {
  235. recorder_free_array(m_encoders);
  236. m_encoders = nullptr;
  237. }
  238. // 获取视频编码器
  239. try {
  240. m_encoderCount = recorder_get_vencoders(&m_encoders);
  241. m_encoderComboBox->clear();
  242. for (int i = 0; i < m_encoderCount; ++i) {
  243. QString encoderName = QString::fromUtf8(m_encoders[i].name);
  244. m_encoderComboBox->addItem(encoderName);
  245. }
  246. // 默认选择第一个编码器
  247. if (m_encoderCount > 0) {
  248. m_encoderComboBox->setCurrentIndex(0);
  249. m_encoderLabel->setText(QString("编码器: %1").arg(m_encoders[0].name));
  250. }
  251. } catch (...) {
  252. m_encoderCount = 0;
  253. qWarning() << "Failed to get video encoders";
  254. m_encoderLabel->setText("编码器: 获取失败");
  255. }
  256. }
  257. void RecorderWidget::setSettings(const Settings& settings)
  258. {
  259. m_settings = settings;
  260. }
  261. bool RecorderWidget::startRecording()
  262. {
  263. if (m_isRecording) {
  264. return true;
  265. }
  266. // 准备录制设置
  267. memset(&m_recorderSetting, 0, sizeof(m_recorderSetting));
  268. // 视频设置
  269. m_recorderSetting.v_left = 0;
  270. m_recorderSetting.v_top = 0;
  271. m_recorderSetting.v_width = GetSystemMetrics(SM_CXSCREEN);
  272. m_recorderSetting.v_height = GetSystemMetrics(SM_CYSCREEN);
  273. m_recorderSetting.v_qb = m_settings.videoQuality;
  274. m_recorderSetting.v_bit_rate = m_settings.videoBitRate;
  275. m_recorderSetting.v_frame_rate = m_settings.videoFrameRate;
  276. // 选择编码器
  277. int encoderIndex = m_encoderComboBox->currentIndex();
  278. if (encoderIndex >= 0 && encoderIndex < m_encoderCount) {
  279. m_recorderSetting.v_enc_id = m_encoders[encoderIndex].id;
  280. }
  281. // 输出文件
  282. QString fileName = QString::fromStdString(m_settings.outputDir);
  283. if (!fileName.endsWith("/") && !fileName.endsWith("\\")) {
  284. fileName += "/";
  285. }
  286. fileName += QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss") + ".mp4";
  287. strcpy_s(m_recorderSetting.output, fileName.toUtf8().constData());
  288. // 音频设备设置
  289. int micIndex = m_micComboBox->currentIndex();
  290. if (micIndex >= 0 && micIndex < m_micCount) {
  291. strcpy_s(m_recorderSetting.a_mic.id, m_micDevices[micIndex].id);
  292. strcpy_s(m_recorderSetting.a_mic.name, m_micDevices[micIndex].name);
  293. m_recorderSetting.a_mic.is_default = m_micDevices[micIndex].is_default;
  294. }
  295. int speakerIndex = m_speakerComboBox->currentIndex();
  296. if (speakerIndex >= 0 && speakerIndex < m_speakerCount) {
  297. strcpy_s(m_recorderSetting.a_speaker.id, m_speakerDevices[speakerIndex].id);
  298. strcpy_s(m_recorderSetting.a_speaker.name, m_speakerDevices[speakerIndex].name);
  299. m_recorderSetting.a_speaker.is_default = m_speakerDevices[speakerIndex].is_default;
  300. }
  301. // 异步初始化和启动录制器,避免主线程阻塞
  302. QFuture<void> future = QtConcurrent::run([this]() {
  303. // 初始化录制器
  304. int result = recorder_init(m_recorderSetting, m_callbacks);
  305. if (result != 0) {
  306. QString error = QString("初始化录制器失败: %1").arg(recorder_err2str(result));
  307. QMetaObject::invokeMethod(this, [this, error]() {
  308. emit errorOccurred(error);
  309. m_recordButton->setText("开始录制");
  310. m_streamButton->setEnabled(true);
  311. m_statusLabel->setText("状态: 就绪");
  312. }, Qt::QueuedConnection);
  313. return;
  314. }
  315. m_isInitialized = true;
  316. // 启用预览
  317. recorder_set_preview_enabled(1);
  318. // 开始录制
  319. result = recorder_start();
  320. if (result != 0) {
  321. QString error = QString("开始录制失败: %1").arg(recorder_err2str(result));
  322. QMetaObject::invokeMethod(this, [this, error]() {
  323. emit errorOccurred(error);
  324. recorder_release();
  325. m_isInitialized = false;
  326. m_recordButton->setText("开始录制");
  327. m_streamButton->setEnabled(true);
  328. m_statusLabel->setText("状态: 就绪");
  329. }, Qt::QueuedConnection);
  330. return;
  331. }
  332. // 成功启动录制
  333. QMetaObject::invokeMethod(this, [this]() {
  334. m_isRecording = true;
  335. m_recordStartTime = QTime::currentTime();
  336. // 开始预览定时器
  337. m_previewTimer->start(33); // 约30fps
  338. emit recordingStarted();
  339. }, Qt::QueuedConnection);
  340. });
  341. // 立即更新UI状态,显示正在初始化
  342. m_statusLabel->setText("状态: 初始化中...");
  343. return true;
  344. }
  345. void RecorderWidget::stopRecording()
  346. {
  347. if (!m_isRecording) {
  348. return;
  349. }
  350. recorder_stop();
  351. if (m_isInitialized) {
  352. recorder_release();
  353. m_isInitialized = false;
  354. }
  355. m_isRecording = false;
  356. // 停止预览定时器
  357. m_previewTimer->stop();
  358. emit recordingStopped();
  359. }
  360. bool RecorderWidget::startStreaming()
  361. {
  362. if (m_isStreaming) {
  363. return true;
  364. }
  365. // 准备推流设置
  366. memset(&m_recorderSetting, 0, sizeof(m_recorderSetting));
  367. // 视频设置
  368. m_recorderSetting.v_left = 0;
  369. m_recorderSetting.v_top = 0;
  370. m_recorderSetting.v_width = GetSystemMetrics(SM_CXSCREEN);
  371. m_recorderSetting.v_height = GetSystemMetrics(SM_CYSCREEN);
  372. m_recorderSetting.v_qb = m_settings.videoQuality;
  373. m_recorderSetting.v_bit_rate = m_settings.videoBitRate;
  374. m_recorderSetting.v_frame_rate = m_settings.videoFrameRate;
  375. // 选择编码器
  376. int encoderIndex = m_encoderComboBox->currentIndex();
  377. if (encoderIndex >= 0 && encoderIndex < m_encoderCount) {
  378. m_recorderSetting.v_enc_id = m_encoders[encoderIndex].id;
  379. }
  380. // 推流地址
  381. QString streamUrl = QString::fromStdString(m_settings.liveUrl + "/" + m_settings.liveName);
  382. strcpy_s(m_recorderSetting.output, streamUrl.toUtf8().constData());
  383. // 音频设备设置
  384. int micIndex = m_micComboBox->currentIndex();
  385. if (micIndex >= 0 && micIndex < m_micCount) {
  386. strcpy_s(m_recorderSetting.a_mic.id, m_micDevices[micIndex].id);
  387. strcpy_s(m_recorderSetting.a_mic.name, m_micDevices[micIndex].name);
  388. m_recorderSetting.a_mic.is_default = m_micDevices[micIndex].is_default;
  389. }
  390. int speakerIndex = m_speakerComboBox->currentIndex();
  391. if (speakerIndex >= 0 && speakerIndex < m_speakerCount) {
  392. strcpy_s(m_recorderSetting.a_speaker.id, m_speakerDevices[speakerIndex].id);
  393. strcpy_s(m_recorderSetting.a_speaker.name, m_speakerDevices[speakerIndex].name);
  394. m_recorderSetting.a_speaker.is_default = m_speakerDevices[speakerIndex].is_default;
  395. }
  396. // 异步初始化和启动推流,避免主线程阻塞
  397. QFuture<void> future = QtConcurrent::run([this]() {
  398. // 初始化录制器
  399. int result = recorder_init(m_recorderSetting, m_callbacks);
  400. if (result != 0) {
  401. QString error = QString("初始化推流失败: %1").arg(recorder_err2str(result));
  402. QMetaObject::invokeMethod(this, [this, error]() {
  403. emit errorOccurred(error);
  404. m_streamButton->setText("开始推流");
  405. m_recordButton->setEnabled(true);
  406. m_statusLabel->setText("状态: 就绪");
  407. }, Qt::QueuedConnection);
  408. return;
  409. }
  410. m_isInitialized = true;
  411. // 启用预览
  412. recorder_set_preview_enabled(1);
  413. // 开始推流
  414. result = recorder_start();
  415. if (result != 0) {
  416. QString error = QString("开始推流失败: %1").arg(recorder_err2str(result));
  417. QMetaObject::invokeMethod(this, [this, error]() {
  418. emit errorOccurred(error);
  419. recorder_release();
  420. m_isInitialized = false;
  421. m_streamButton->setText("开始推流");
  422. m_recordButton->setEnabled(true);
  423. m_statusLabel->setText("状态: 就绪");
  424. }, Qt::QueuedConnection);
  425. return;
  426. }
  427. // 成功启动推流
  428. QMetaObject::invokeMethod(this, [this]() {
  429. m_isStreaming = true;
  430. m_recordStartTime = QTime::currentTime();
  431. // 开始预览定时器
  432. m_previewTimer->start(33); // 约30fps
  433. emit streamingStarted();
  434. }, Qt::QueuedConnection);
  435. });
  436. // 立即更新UI状态,显示正在初始化
  437. m_statusLabel->setText("状态: 连接中...");
  438. return true;
  439. }
  440. void RecorderWidget::stopStreaming()
  441. {
  442. if (!m_isStreaming) {
  443. return;
  444. }
  445. recorder_stop();
  446. if (m_isInitialized) {
  447. recorder_release();
  448. m_isInitialized = false;
  449. }
  450. m_isStreaming = false;
  451. // 停止预览定时器
  452. m_previewTimer->stop();
  453. emit streamingStopped();
  454. }
  455. void RecorderWidget::onRecordButtonClicked()
  456. {
  457. if (!m_isRecording) {
  458. if (startRecording()) {
  459. m_recordButton->setText("停止录制");
  460. m_streamButton->setEnabled(false);
  461. m_statusLabel->setText("状态: 录制中");
  462. }
  463. } else {
  464. stopRecording();
  465. m_recordButton->setText("开始录制");
  466. m_streamButton->setEnabled(true);
  467. m_statusLabel->setText("状态: 就绪");
  468. }
  469. }
  470. void RecorderWidget::onStreamButtonClicked()
  471. {
  472. if (!m_isStreaming) {
  473. if (startStreaming()) {
  474. m_streamButton->setText("停止推流");
  475. m_recordButton->setEnabled(!m_syncRecordCheckBox->isChecked());
  476. m_statusLabel->setText("状态: 推流中");
  477. }
  478. } else {
  479. stopStreaming();
  480. m_streamButton->setText("开始推流");
  481. m_recordButton->setEnabled(true);
  482. m_statusLabel->setText("状态: 就绪");
  483. }
  484. }
  485. void RecorderWidget::onSettingsButtonClicked()
  486. {
  487. // 打开设置对话框
  488. QMessageBox::information(this, "设置", "设置功能待实现");
  489. }
  490. void RecorderWidget::onAudioDeviceButtonClicked()
  491. {
  492. refreshAudioDevices();
  493. QMessageBox::information(this, "音频设备", "设备列表已刷新");
  494. }
  495. void RecorderWidget::updatePreview()
  496. {
  497. // 预览更新在回调函数中处理
  498. if (m_previewWidget && m_previewWidth > 0 && m_previewHeight > 0) {
  499. m_previewWidget->update();
  500. }
  501. }
  502. void RecorderWidget::updateStatus()
  503. {
  504. if (m_isRecording || m_isStreaming) {
  505. QTime currentTime = QTime::currentTime();
  506. int elapsed = m_recordStartTime.secsTo(currentTime);
  507. int hours = elapsed / 3600;
  508. int minutes = (elapsed % 3600) / 60;
  509. int seconds = elapsed % 60;
  510. QString timeStr = QString("%1:%2:%3")
  511. .arg(hours, 2, 10, QChar('0'))
  512. .arg(minutes, 2, 10, QChar('0'))
  513. .arg(seconds, 2, 10, QChar('0'));
  514. m_timeLabel->setText(timeStr);
  515. } else {
  516. m_timeLabel->setText("00:00:00");
  517. }
  518. }
  519. // 静态回调函数实现
  520. void RecorderWidget::onDurationCallback(uint64_t duration)
  521. {
  522. if (s_instance) {
  523. // 可以在这里处理持续时间更新
  524. qDebug() << "Recording duration:" << duration << "ms";
  525. }
  526. }
  527. void RecorderWidget::onErrorCallback(int error)
  528. {
  529. if (s_instance) {
  530. QString errorStr = QString("录制错误: %1").arg(recorder_err2str(error));
  531. QMetaObject::invokeMethod(s_instance, "errorOccurred", Qt::QueuedConnection, Q_ARG(QString, errorStr));
  532. }
  533. }
  534. void RecorderWidget::onDeviceChangeCallback(int type)
  535. {
  536. if (s_instance) {
  537. // 设备变化时刷新设备列表
  538. QMetaObject::invokeMethod(s_instance, "refreshAudioDevices", Qt::QueuedConnection);
  539. }
  540. }
  541. void RecorderWidget::onPreviewYUVCallback(const unsigned char *data, unsigned int size, int width, int height, int type)
  542. {
  543. if (s_instance && data && size > 0) {
  544. QMutexLocker locker(&s_instance->m_previewMutex);
  545. s_instance->m_previewData = QByteArray(reinterpret_cast<const char*>(data), size);
  546. s_instance->m_previewWidth = width;
  547. s_instance->m_previewHeight = height;
  548. }
  549. }
  550. void RecorderWidget::onPreviewAudioCallback()
  551. {
  552. // 音频预览回调,暂时不处理
  553. }