av_recorder.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. #include "av_recorder.h"
  2. #include <QDateTime>
  3. #include <QStatusBar>
  4. #include <capturer/finder.h>
  5. AvRecorder::AvRecorder(QWidget* parent)
  6. : QWidget(parent)
  7. {
  8. setWindowTitle("Recorder");
  9. _settingsParam.audioParam.bitRate = 160'000;
  10. _settingsParam.videoParam.bitRate = 8'000'000;
  11. _settingsParam.videoParam.fps = 30;
  12. _settingsParam.videoParam.name = Encoder<MediaType::VIDEO>::GetUsableEncoders().front();
  13. _settingsParam.outputDir = ".";
  14. _settingsParam.liveUrl = "rtmp://127.0.0.1:1935";
  15. _settingsParam.liveName = "stream";
  16. glWidget = new OpenGLVideoWidget(this);
  17. WgcCapturer::Init();
  18. auto layout = new QVBoxLayout;
  19. auto hLayout = new QHBoxLayout;
  20. hLayout->addLayout(_InitAudioUi(), 2);
  21. hLayout->addLayout(_InitListUi(), 2);
  22. hLayout->addLayout(_InitOtherUi(), 1);
  23. _InitStatusBarUi();
  24. layout->addWidget(glWidget, 4);
  25. layout->addLayout(hLayout, 1);
  26. setLayout(layout);
  27. _UpdateCaptureList();
  28. _InitConnect();
  29. }
  30. void AvRecorder::_InitConnect()
  31. {
  32. // 启动
  33. auto timer = new QTimer(this);
  34. connect(timer, &QTimer::timeout, [this, timer] {
  35. _isLocked = true;
  36. _StopPreview();
  37. _StopCapture();
  38. _StartCapture(VideoCapturer::WGC);
  39. _StartPreview();
  40. _isLocked = false;
  41. timer->stop();
  42. });
  43. timer->start(100);
  44. connect(_recordBtn, &QPushButton::released, [this] {
  45. if (!_isRecord) {
  46. auto fileName = _settingsParam.outputDir;
  47. if (fileName.back() != '\\') {
  48. fileName.push_back('\\');
  49. }
  50. auto format = "mp4";
  51. fileName += QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss").toStdString()
  52. + "." + format;
  53. // fileName += std::string("test.") + format;
  54. __CheckNo(_StartStream(fileName, format));
  55. _liveBtn->setEnabled(false);
  56. _recordBtn->setText("停止录制");
  57. } else {
  58. _StopStream();
  59. _liveBtn->setEnabled(true);
  60. _recordBtn->setText("开始录制");
  61. }
  62. _isRecord = !_isRecord;
  63. });
  64. connect(_liveBtn, &QPushButton::released, [this] {
  65. if (!_isLive) {
  66. auto fileName = _settingsParam.liveUrl + "/" + _settingsParam.liveName;
  67. bool isRtsp = _settingsParam.liveUrl.find("rtsp") != std::string::npos;
  68. __CheckNo(_StartStream(fileName, isRtsp ? "rtsp" : "flv"));
  69. _recordBtn->setEnabled(false);
  70. _liveBtn->setText("停止直播");
  71. } else {
  72. _StopStream();
  73. _recordBtn->setEnabled(true);
  74. _liveBtn->setText("开始直播");
  75. }
  76. _isLive = !_isLive;
  77. });
  78. connect(_microphoneWidget, &AudioWidget::SetVolumeScale, [this](float scale) {
  79. m_audioRecorder.SetVolumeScale(scale, MICROPHONE_INDEX);
  80. });
  81. connect(_speakerWidget, &AudioWidget::SetVolumeScale, [this](float scale) {
  82. m_audioRecorder.SetVolumeScale(scale, SPEAKER_INDEX);
  83. });
  84. connect(_updateListBtn, &QPushButton::released, [this] { _UpdateCaptureList(); });
  85. connect(_captureListWidget, &QListWidget::currentTextChanged, [this](const QString& text) {
  86. if (text.isEmpty() || _isLocked) {
  87. return;
  88. }
  89. _isLocked = true;
  90. _StopPreview();
  91. _StopCapture();
  92. _StartCapture(VideoCapturer::WGC);
  93. _StartPreview();
  94. _isLocked = false;
  95. });
  96. connect(_isDrawCursorBox, &QCheckBox::stateChanged, [this] {
  97. m_videoRecorder.SetIsDrawCursor(_isDrawCursorBox->isChecked());
  98. });
  99. connect(_captureMethodBox, &QComboBox::currentTextChanged, [this](const QString& text) {
  100. if (_isLocked || text.isEmpty()) {
  101. return;
  102. }
  103. _StopPreview();
  104. _StopCapture();
  105. if (text == "WGC") {
  106. _StartCapture(VideoCapturer::WGC);
  107. } else if (text == "DXGI") {
  108. _StartCapture(VideoCapturer::DXGI);
  109. } else {
  110. _StartCapture(VideoCapturer::GDI);
  111. }
  112. _StartPreview();
  113. });
  114. connect(_settingsBtn, &QPushButton::released, [this] {
  115. auto settingsPage = std::make_unique<SettingsPage>(&_settingsParam, this);
  116. settingsPage->exec();
  117. _isLocked = true;
  118. _StopPreview();
  119. _StopCapture();
  120. _StartCapture(VideoCapturer::WGC);
  121. _StartPreview();
  122. _isLocked = false;
  123. });
  124. _otherTimer.callOnTimeout([this] {
  125. if (windowState() == Qt::WindowMinimized) {
  126. return;
  127. }
  128. // 音频
  129. auto info = m_audioRecorder.GetCaptureInfo(MICROPHONE_INDEX);
  130. _microphoneWidget->ShowVolume(info == nullptr ? 0 : info->volume);
  131. info = m_audioRecorder.GetCaptureInfo(SPEAKER_INDEX);
  132. _speakerWidget->ShowVolume(info == nullptr ? 0 : info->volume);
  133. // 状态栏
  134. if (_isRecord || _isLive) {
  135. int interval = _recordTime.secsTo(QTime::currentTime());
  136. int sec = interval % 60;
  137. interval /= 60;
  138. int minute = interval % 60;
  139. int hour = interval / 60;
  140. _captureTimeLabel->setText(QString("%1:%2:%3")
  141. .arg(hour, 2, 10, QChar('0'))
  142. .arg(minute, 2, 10, QChar('0'))
  143. .arg(sec, 2, 10, QChar('0')));
  144. auto lossRate = m_videoRecorder.GetLossRate();
  145. int num = lossRate * 10000;
  146. _videolossRate->setText(QString("丢帧率: %1.%2%")
  147. .arg(num / 100, 2, 10, QChar('0'))
  148. .arg(num % 100, 2, 10, QChar('0')));
  149. } else if (_captureTimeLabel->text() != "00:00:00") {
  150. _captureTimeLabel->setText("00:00:00");
  151. }
  152. });
  153. }
  154. AvRecorder::~AvRecorder()
  155. {
  156. _StopStream();
  157. _StopPreview();
  158. _StopCapture();
  159. WgcCapturer::Uninit();
  160. }
  161. void AvRecorder::_StartCapture(VideoCapturer::Method method)
  162. {
  163. if (_isLocked) {
  164. _captureMethodBox->clear();
  165. _captureMethodBox->addItem("WGC");
  166. }
  167. // 判断是要捕获屏幕还是窗口
  168. int idx = _captureListWidget->currentRow();
  169. if (idx < 0) {
  170. idx = 0;
  171. _captureListWidget->setCurrentRow(idx);
  172. }
  173. int monitorCnt = (int) MonitorFinder::GetList().size();
  174. if (idx < monitorCnt) { // 捕获屏幕
  175. if (_captureMethodBox->count() < 2) {
  176. _captureMethodBox->addItem("DXGI");
  177. }
  178. m_videoRecorder.Open(idx, _settingsParam.videoParam, method);
  179. } else {
  180. if (_captureMethodBox->count() < 2) {
  181. _captureMethodBox->addItem("GDI");
  182. }
  183. auto hwnd = WindowFinder::GetList()[idx - monitorCnt].hwnd;
  184. m_videoRecorder.Open(hwnd, _settingsParam.videoParam, method);
  185. }
  186. _DealCapture();
  187. _isDrawCursorBox->setEnabled(true);
  188. _recordBtn->setEnabled(true);
  189. _liveBtn->setEnabled(true);
  190. m_videoRecorder.SetIsDrawCursor(_isDrawCursorBox->isChecked());
  191. m_audioRecorder.SetVolumeScale(_microphoneWidget->GetVolume(), MICROPHONE_INDEX);
  192. m_audioRecorder.SetVolumeScale(_speakerWidget->GetVolume(), SPEAKER_INDEX);
  193. }
  194. void AvRecorder::_DealCapture()
  195. {
  196. __CheckNo(m_audioRecorder.Open({AudioCapturer::Microphone, AudioCapturer::Speaker},
  197. _settingsParam.audioParam));
  198. _microphoneWidget->setEnabled(m_audioRecorder.GetCaptureInfo(MICROPHONE_INDEX) != nullptr);
  199. _speakerWidget->setEnabled(m_audioRecorder.GetCaptureInfo(SPEAKER_INDEX) != nullptr);
  200. _fpsLabel->setText(QString("FPS: %1").arg(_settingsParam.videoParam.fps));
  201. _videoEncodeLabel->setText(("编码器: " + _settingsParam.videoParam.name).c_str());
  202. }
  203. void AvRecorder::_StopCapture()
  204. {
  205. m_videoRecorder.Close();
  206. m_audioRecorder.Close();
  207. }
  208. void AvRecorder::_StartPreview()
  209. {
  210. glWidget->Open(_settingsParam.videoParam.width, _settingsParam.videoParam.height);
  211. // __CheckNo(_videoRender.Open(_videoWidget->GetHwnd(),
  212. // _settingsParam.videoParam.width,
  213. // _settingsParam.videoParam.height));
  214. // _videoWidget->SetScaleFixSize(_settingsParam.videoParam.width, _settingsParam.videoParam.height);
  215. // _videoWidget->setAttribute(Qt::WA_TransparentForMouseEvents, true);
  216. // 视频需要做到和帧率一样的渲染速度,QTimer 达不到要求
  217. // 需要自己封装一个计时器
  218. _videoRenderTimer.Start(_settingsParam.videoParam.fps, [this] {
  219. if (windowState() == Qt::WindowMinimized) {
  220. return;
  221. }
  222. // 视频
  223. auto frame = m_videoRecorder.GetRenderFrame();
  224. // __CheckNo(_videoRender.Render(frame));
  225. glWidget->Render(frame);
  226. });
  227. // 刷新率设置为 25
  228. _otherTimer.start(40);
  229. }
  230. void AvRecorder::_StopPreview()
  231. {
  232. _videoRenderTimer.Stop();
  233. // _videoRender.Close();
  234. _otherTimer.stop();
  235. }
  236. bool AvRecorder::_StartStream(std::string_view path, std::string_view format)
  237. {
  238. __CheckBool(_avMuxer.Open(path, format));
  239. __CheckBool(m_audioRecorder.LoadMuxer(_avMuxer));
  240. __CheckBool(m_videoRecorder.LoadMuxer(_avMuxer));
  241. __CheckBool(_avMuxer.WriteHeader());
  242. __CheckBool(m_audioRecorder.StartRecord());
  243. __CheckBool(m_videoRecorder.StartRecord());
  244. _recordTime = QTime::currentTime();
  245. _captureStatusLabel->setText("状态: 正在工作");
  246. _settingsBtn->setEnabled(false);
  247. _captureListWidget->setEnabled(false);
  248. _updateListBtn->setEnabled(false);
  249. _captureMethodBox->setEnabled(false);
  250. return true;
  251. }
  252. void AvRecorder::_StopStream()
  253. {
  254. m_audioRecorder.StopRecord();
  255. m_videoRecorder.StopRecord();
  256. _avMuxer.Close();
  257. _captureStatusLabel->setText("状态: 正常");
  258. _settingsBtn->setEnabled(true);
  259. _captureListWidget->setEnabled(true);
  260. _updateListBtn->setEnabled(true);
  261. _captureMethodBox->setEnabled(true);
  262. }
  263. void AvRecorder::_UpdateCaptureList()
  264. {
  265. _captureListWidget->clear();
  266. auto&& monitorList = MonitorFinder::GetList(true);
  267. for (auto&& monitor : monitorList) {
  268. _captureListWidget->addItem("屏幕: " + QString::fromStdWString(monitor.title));
  269. }
  270. auto&& windowList = WindowFinder::GetList(true);
  271. for (auto&& window : windowList) {
  272. _captureListWidget->addItem("窗口: " + QString::fromStdWString(window.title));
  273. }
  274. // _captureListWidget->hide();
  275. // _updateListBtn->hide();
  276. }
  277. QVBoxLayout* AvRecorder::_InitListUi()
  278. {
  279. auto layout = new QVBoxLayout;
  280. _captureListWidget = new QListWidget;
  281. layout->addWidget(_captureListWidget);
  282. return layout;
  283. }
  284. QVBoxLayout* AvRecorder::_InitAudioUi()
  285. {
  286. _microphoneWidget = new AudioWidget;
  287. _speakerWidget = new AudioWidget;
  288. _microphoneWidget->SetName("麦克风");
  289. _speakerWidget->SetName("扬声器");
  290. auto layout = new QVBoxLayout;
  291. layout->addWidget(_microphoneWidget);
  292. layout->addWidget(_speakerWidget);
  293. return layout;
  294. }
  295. QVBoxLayout* AvRecorder::_InitOtherUi()
  296. {
  297. _isDrawCursorBox = new QCheckBox("绘制鼠标指针");
  298. _isDrawCursorBox->setChecked(true);
  299. _isDrawCursorBox->setEnabled(false);
  300. _updateListBtn = new QPushButton("刷新窗口列表");
  301. _recordBtn = new QPushButton("开始录制");
  302. _recordBtn->setEnabled(false);
  303. _liveBtn = new QPushButton("开始直播");
  304. _liveBtn->setEnabled(false);
  305. _settingsBtn = new QPushButton("设置");
  306. auto layout = new QVBoxLayout;
  307. layout->addWidget(_isDrawCursorBox);
  308. layout->addWidget(_updateListBtn);
  309. layout->addWidget(_recordBtn);
  310. layout->addWidget(_liveBtn);
  311. layout->addWidget(_settingsBtn);
  312. return layout;
  313. }
  314. void AvRecorder::_InitStatusBarUi()
  315. {
  316. _videoEncodeLabel = new QLabel;
  317. auto hLayout = new QHBoxLayout;
  318. hLayout->addWidget(new QLabel("捕获方式:"));
  319. _captureMethodBox = new QComboBox;
  320. hLayout->addWidget(_captureMethodBox);
  321. _captureStatusLabel = new QLabel("状态: 正常");
  322. _captureTimeLabel = new QLabel("00:00:00");
  323. _videolossRate = new QLabel("丢帧率: 00.00%");
  324. _fpsLabel = new QLabel("FPS: 30");
  325. // auto statusBar = this->statusBar();
  326. // statusBar->layout()->setSpacing(20);
  327. // statusBar->layout()->addWidget(_videoEncodeLabel);
  328. auto widget = new QWidget;
  329. widget->setLayout(hLayout);
  330. // statusBar->layout()->addWidget(widget);
  331. // statusBar->layout()->addWidget(_videolossRate);
  332. // statusBar->layout()->addWidget(_captureStatusLabel);
  333. // statusBar->layout()->addWidget(_captureTimeLabel);
  334. // statusBar->layout()->addWidget(_fpsLabel);
  335. }