av_recorder.cpp 13 KB

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