record_desktop_gdi.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. #include "record_desktop_gdi.h"
  2. #include "error_define.h"
  3. #include "log_helper.h"
  4. namespace am {
  5. record_desktop_gdi::record_desktop_gdi()
  6. {
  7. _data_type = RECORD_DESKTOP_DATA_TYPES::AT_DESKTOP_BGRA;
  8. _buffer = NULL;
  9. _buffer_size = 0;
  10. _draw_cursor = true;
  11. _hdc = NULL;
  12. _bmp = NULL;
  13. _bmp_old = NULL;
  14. _ci = {0};
  15. }
  16. record_desktop_gdi::~record_desktop_gdi()
  17. {
  18. stop();
  19. clean_up();
  20. }
  21. int record_desktop_gdi::init(const RECORD_DESKTOP_RECT &rect, const int fps)
  22. {
  23. int error = AE_NO;
  24. if (_inited == true) {
  25. return error;
  26. }
  27. _fps = fps;
  28. _rect = rect;
  29. do {
  30. _width = rect.right - rect.left;
  31. _height = rect.bottom - rect.top;
  32. _buffer_size = (_width * 32 + 31) / 32 * _height * 4;
  33. _buffer = new uint8_t[_buffer_size];
  34. _start_time = av_gettime_relative();
  35. _time_base = {1, AV_TIME_BASE};
  36. _pixel_fmt = AV_PIX_FMT_BGRA;
  37. _inited = true;
  38. } while (0);
  39. al_info("init gdi finished,error: %s %ld", err2str(error), GetLastError());
  40. return error;
  41. }
  42. int record_desktop_gdi::start()
  43. {
  44. if (_running == true) {
  45. al_warn("record desktop gdi is already running");
  46. return AE_NO;
  47. }
  48. if (_inited == false) {
  49. return AE_NEED_INIT;
  50. }
  51. _running = true;
  52. _thread = std::thread(std::bind(&record_desktop_gdi::record_func, this));
  53. return AE_NO;
  54. }
  55. int record_desktop_gdi::pause()
  56. {
  57. _paused = true;
  58. return AE_NO;
  59. }
  60. int record_desktop_gdi::resume()
  61. {
  62. _paused = false;
  63. return AE_NO;
  64. }
  65. int record_desktop_gdi::stop()
  66. {
  67. _running = false;
  68. if (_thread.joinable())
  69. _thread.join();
  70. return AE_NO;
  71. }
  72. void record_desktop_gdi::clean_up()
  73. {
  74. _inited = false;
  75. if (_buffer)
  76. delete[] _buffer;
  77. _buffer = nullptr;
  78. }
  79. void record_desktop_gdi::draw_cursor(HDC hdc)
  80. {
  81. if (!(_ci.flags & CURSOR_SHOWING))
  82. return;
  83. //is cursor in the tartet zone
  84. if (_ci.ptScreenPos.x < _rect.left || _ci.ptScreenPos.x > _rect.right
  85. || _ci.ptScreenPos.y < _rect.top || _ci.ptScreenPos.y > _rect.bottom)
  86. return;
  87. HICON icon;
  88. ICONINFO ii;
  89. icon = CopyIcon(_ci.hCursor);
  90. if (!icon)
  91. return;
  92. int dstx = 0, dsty = 0;
  93. dstx = abs(_ci.ptScreenPos.x - _rect.left);
  94. dsty = abs(_ci.ptScreenPos.y - _rect.top);
  95. if (GetIconInfo(icon, &ii)) {
  96. POINT pos;
  97. DrawIconEx(hdc, dstx, dsty, icon, 0, 0, 0, NULL, DI_NORMAL);
  98. DeleteObject(ii.hbmColor);
  99. DeleteObject(ii.hbmMask);
  100. }
  101. DestroyIcon(icon);
  102. }
  103. int record_desktop_gdi::do_record()
  104. {
  105. //int width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
  106. //int height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
  107. HDC hdc_screen = NULL, hdc_mem = NULL;
  108. HBITMAP hbm_mem = NULL;
  109. int error = AE_ERROR;
  110. do {
  111. hdc_screen = GetWindowDC(NULL);
  112. if (!hdc_screen) {
  113. al_error("get window dc failed:%lu", GetLastError());
  114. error = AE_GDI_GET_DC_FAILED;
  115. break;
  116. }
  117. hdc_mem = CreateCompatibleDC(hdc_screen);
  118. if (!hdc_mem) {
  119. al_error("create compatible dc failed:%lu", GetLastError());
  120. error = AE_GDI_CREATE_DC_FAILED;
  121. break;
  122. }
  123. hbm_mem = CreateCompatibleBitmap(hdc_screen, _width, _height);
  124. if (!hbm_mem) {
  125. al_error("create compatible bitmap failed:%lu", GetLastError());
  126. error = AE_GDI_CREATE_BMP_FAILED;
  127. break;
  128. }
  129. SelectObject(hdc_mem, hbm_mem);
  130. //must have CAPTUREBLT falg,otherwise some layered window can not be captured
  131. if (!BitBlt(hdc_mem,
  132. 0,
  133. 0,
  134. _width,
  135. _height,
  136. hdc_screen,
  137. _rect.left,
  138. _rect.top,
  139. SRCCOPY | CAPTUREBLT)) {
  140. al_error("bitblt data failed:%lu", GetLastError());
  141. //error = AE_GDI_BITBLT_FAILED;
  142. //administrator UAC will trigger invalid handle error
  143. break;
  144. }
  145. memset(&_ci, 0, sizeof(CURSORINFO));
  146. _ci.cbSize = sizeof(CURSORINFO);
  147. if (GetCursorInfo(&_ci)) {
  148. draw_cursor(hdc_mem);
  149. }
  150. BITMAPINFOHEADER bi;
  151. bi.biSize = sizeof(BITMAPINFOHEADER);
  152. bi.biWidth = _width;
  153. bi.biHeight = _height * (-1);
  154. bi.biPlanes = 1;
  155. bi.biBitCount = 32; //should get from system color bits
  156. bi.biCompression = BI_RGB;
  157. bi.biSizeImage = 0;
  158. bi.biXPelsPerMeter = 0;
  159. bi.biYPelsPerMeter = 0;
  160. bi.biClrUsed = 0;
  161. bi.biClrImportant = 0;
  162. //scan colors by line order
  163. int ret
  164. = GetDIBits(hdc_mem, hbm_mem, 0, _height, _buffer, (BITMAPINFO *) &bi, DIB_RGB_COLORS);
  165. if (ret <= 0 || ret == ERROR_INVALID_PARAMETER) {
  166. al_error("get dibits failed:%lu", GetLastError());
  167. error = AE_GDI_GET_DIBITS_FAILED;
  168. break;
  169. }
  170. #if 0
  171. //save bmp to test
  172. BITMAPFILEHEADER bf;
  173. bf.bfType = 0x4d42;
  174. bf.bfReserved1 = 0;
  175. bf.bfReserved2 = 0;
  176. bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
  177. bf.bfSize = bf.bfOffBits + _width * _height * 4;
  178. FILE *fp = fopen("..\\..\\save.bmp", "wb+");
  179. fwrite(&bf, 1, sizeof(bf), fp);
  180. fwrite(&bi, 1, sizeof(bi), fp);
  181. fwrite(_buffer, 1, _buffer_size, fp);
  182. fflush(fp);
  183. fclose(fp);
  184. #endif
  185. error = AE_NO;
  186. } while (0);
  187. if (hbm_mem)
  188. DeleteObject(hbm_mem);
  189. if (hdc_mem)
  190. DeleteObject(hdc_mem);
  191. if (hdc_screen)
  192. ReleaseDC(NULL, hdc_screen);
  193. return AE_NO;
  194. }
  195. void record_desktop_gdi::do_sleep(int64_t dur, int64_t pre, int64_t now)
  196. {
  197. int64_t delay = now - pre;
  198. dur = delay > dur ? max(0, dur - (delay - dur)) : (dur + dur - delay);
  199. // 优化:使用更精确的睡眠机制,减少延迟抖动
  200. if (dur > 0) {
  201. // 对于小于2ms的延迟,使用忙等待以获得更精确的时序
  202. if (dur < 2000) { // 2ms
  203. int64_t start = av_gettime_relative();
  204. while (av_gettime_relative() - start < dur) {
  205. // 忙等待,但让出CPU时间片
  206. std::this_thread::yield();
  207. }
  208. } else {
  209. av_usleep(dur);
  210. }
  211. }
  212. }
  213. void record_desktop_gdi::record_func()
  214. {
  215. AVFrame *frame = av_frame_alloc();
  216. int64_t pre_pts = 0;
  217. int64_t dur = AV_TIME_BASE / _fps;
  218. // 优化:预分配光标信息结构,减少每帧的内存分配开销
  219. memset(&_ci, 0, sizeof(CURSORINFO));
  220. _ci.cbSize = sizeof(CURSORINFO);
  221. int ret = AE_NO;
  222. while (_running) {
  223. int64_t frame_start = av_gettime_relative();
  224. ret = do_record();
  225. if (ret != AE_NO) {
  226. if (_on_error)
  227. _on_error(ret);
  228. break;
  229. }
  230. // 使用相对时间戳,与muxer的_base_time保持一致
  231. frame->pts = frame_start; // 使用帧开始时间作为时间戳
  232. frame->pkt_dts = frame->pts;
  233. frame->width = _width;
  234. frame->height = _height;
  235. frame->format = AV_PIX_FMT_BGRA;
  236. frame->pict_type = AV_PICTURE_TYPE_I;
  237. frame->pkt_size = _width * _height * 4;
  238. av_image_fill_arrays(frame->data,
  239. frame->linesize,
  240. _buffer,
  241. AV_PIX_FMT_BGRA,
  242. _width,
  243. _height,
  244. 1);
  245. if (_on_data)
  246. _on_data(frame);
  247. // 优化:更精确的帧率控制
  248. int64_t frame_end = av_gettime_relative();
  249. int64_t processing_time = frame_end - frame_start;
  250. int64_t sleep_time = dur - processing_time;
  251. if (sleep_time > 0) {
  252. do_sleep(sleep_time, pre_pts, frame_end);
  253. }
  254. pre_pts = frame_end;
  255. }
  256. av_frame_free(&frame);
  257. }
  258. } // namespace am