| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439 |
- #include "record_desktop_mag.h"
- #include "system_lib.h"
- #include "error_define.h"
- #include "log_helper.h"
- namespace am {
- namespace {
- // kMagnifierWindowClass has to be "Magnifier" according to the Magnification
- // API. The other strings can be anything.
- static wchar_t kMagnifierHostClass[] = L"ScreenCapturerWinMagnifierHost";
- static wchar_t kHostWindowName[] = L"MagnifierHost";
- static wchar_t kMagnifierWindowClass[] = L"Magnifier";
- static wchar_t kMagnifierWindowName[] = L"MagnifierWindow";
- DWORD GetTlsIndex()
- {
- static const DWORD tls_index = TlsAlloc();
- return tls_index;
- }
- } // namespace
- BOOL __stdcall record_desktop_mag::on_mag_scaling_callback(HWND hwnd,
- void *srcdata,
- MAGIMAGEHEADER srcheader,
- void *destdata,
- MAGIMAGEHEADER destheader,
- RECT unclipped,
- RECT clipped,
- HRGN dirty)
- {
- record_desktop_mag *owner = reinterpret_cast<record_desktop_mag *>(TlsGetValue(GetTlsIndex()));
- TlsSetValue(GetTlsIndex(), nullptr);
- owner->on_mag_data(srcdata, srcheader);
- return TRUE;
- }
- record_desktop_mag::record_desktop_mag() {}
- record_desktop_mag::~record_desktop_mag() {}
- int record_desktop_mag::init(const RECORD_DESKTOP_RECT &rect, const int fps)
- {
- if (_inited == true) {
- return AE_NO;
- }
- _fps = fps;
- _rect = rect;
- _width = rect.right - rect.left;
- _height = rect.bottom - rect.top;
- _start_time = av_gettime_relative();
- _time_base = {1, AV_TIME_BASE};
- _pixel_fmt = AV_PIX_FMT_BGRA;
- _inited = true;
- return AE_NO;
- }
- int record_desktop_mag::start()
- {
- if (_running == true) {
- al_warn("record desktop gdi is already running");
- return AE_NO;
- }
- if (_inited == false) {
- return AE_NEED_INIT;
- }
- _running = true;
- _thread = std::thread(std::bind(&record_desktop_mag::record_func, this));
- return AE_NO;
- }
- int record_desktop_mag::pause()
- {
- _paused = true;
- return AE_NO;
- }
- int record_desktop_mag::resume()
- {
- _paused = false;
- return AE_NO;
- }
- int record_desktop_mag::stop()
- {
- _running = false;
- if (_thread.joinable())
- _thread.join();
- return AE_NO;
- }
- void record_desktop_mag::clean_up()
- {
- // DestroyWindow must be called before MagUninitialize. _magnifier_window is
- // destroyed automatically when _host_window is destroyed.
- if (_host_window)
- DestroyWindow(_host_window);
- if (_magnifier_initialized)
- _mag_uninitialize_func();
- if (_mag_lib_handle)
- free_system_library(_mag_lib_handle);
- if (_desktop_dc)
- ReleaseDC(NULL, _desktop_dc);
- _inited = false;
- }
- void record_desktop_mag::record_func()
- {
- int64_t pre_pts = 0;
- int64_t dur = AV_TIME_BASE / _fps;
- int ret = AE_NO;
- // must call this in a new thread, otherwise SetWindowPos will stuck before
- // capture
- if (!do_mag_initialize()) {
- al_info("Failed to initialize ScreenCapturerWinMagnifier.");
- if (_on_error)
- _on_error(AE_NEED_INIT);
- return;
- }
- while (_running) {
- ret = do_mag_record();
- if (ret != AE_NO) {
- if (_on_error)
- _on_error(ret);
- break;
- }
- do_sleep(dur, pre_pts, _current_pts);
- pre_pts = _current_pts;
- }
- }
- void record_desktop_mag::do_sleep(int64_t dur, int64_t pre, int64_t now)
- {
- int64_t delay = now - pre;
- dur = delay > dur ? max(0, dur - (delay - dur)) : (dur + dur - delay);
- // al_debug("%lld", delay);
- if (dur)
- av_usleep(dur);
- }
- bool record_desktop_mag::do_mag_initialize()
- {
- #if 0 // we can handle crash
- if (GetSystemMetrics(SM_CMONITORS) != 1) {
- // Do not try to use the magnifier in multi-screen setup (where the API
- // crashes sometimes).
- al_info("Magnifier capturer cannot work on multi-screen system.");
- return false;
- }
- #endif
- _desktop_dc = GetDC(nullptr);
- _mag_lib_handle = load_system_library("Magnification.dll");
- if (!_mag_lib_handle)
- return false;
- // Initialize Magnification API function pointers.
- _mag_initialize_func = reinterpret_cast<MagInitializeFunc>(
- GetProcAddress(_mag_lib_handle, "MagInitialize"));
- _mag_uninitialize_func = reinterpret_cast<MagUninitializeFunc>(
- GetProcAddress(_mag_lib_handle, "MagUninitialize"));
- _mag_set_window_source_func = reinterpret_cast<MagSetWindowSourceFunc>(
- GetProcAddress(_mag_lib_handle, "MagSetWindowSource"));
- _mag_set_window_filter_list_func = reinterpret_cast<MagSetWindowFilterListFunc>(
- GetProcAddress(_mag_lib_handle, "MagSetWindowFilterList"));
- _mag_set_image_scaling_callback_func = reinterpret_cast<MagSetImageScalingCallbackFunc>(
- GetProcAddress(_mag_lib_handle, "MagSetImageScalingCallback"));
- if (!_mag_initialize_func || !_mag_uninitialize_func || !_mag_set_window_source_func
- || !_mag_set_window_filter_list_func || !_mag_set_image_scaling_callback_func) {
- al_info("Failed to initialize ScreenCapturerWinMagnifier: library functions "
- "missing.");
- return false;
- }
- BOOL result = _mag_initialize_func();
- if (!result) {
- al_info("Failed to initialize ScreenCapturerWinMagnifier: error from "
- "MagInitialize %ld",
- GetLastError());
- return false;
- }
- HMODULE hInstance = nullptr;
- result = GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
- | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
- reinterpret_cast<char *>(&DefWindowProc),
- &hInstance);
- if (!result) {
- _mag_uninitialize_func();
- al_info("Failed to initialize ScreenCapturerWinMagnifier: "
- "error from GetModulehandleExA %ld",
- GetLastError());
- return false;
- }
- // Register the host window class. See the MSDN documentation of the
- // Magnification API for more infomation.
- WNDCLASSEXW wcex = {};
- wcex.cbSize = sizeof(WNDCLASSEX);
- wcex.lpfnWndProc = &DefWindowProc;
- wcex.hInstance = hInstance;
- wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
- wcex.lpszClassName = kMagnifierHostClass;
- // Ignore the error which may happen when the class is already registered.
- RegisterClassExW(&wcex);
- // Create the host window.
- _host_window = CreateWindowExW(WS_EX_LAYERED,
- kMagnifierHostClass,
- kHostWindowName,
- 0,
- 0,
- 0,
- 0,
- 0,
- nullptr,
- nullptr,
- hInstance,
- nullptr);
- if (!_host_window) {
- _mag_uninitialize_func();
- al_info("Failed to initialize ScreenCapturerWinMagnifier: "
- "error from creating host window %ld",
- GetLastError());
- return false;
- }
- // Create the magnifier control.
- _magnifier_window = CreateWindowW(kMagnifierWindowClass,
- kMagnifierWindowName,
- WS_CHILD | WS_VISIBLE,
- 0,
- 0,
- 0,
- 0,
- _host_window,
- nullptr,
- hInstance,
- nullptr);
- if (!_magnifier_window) {
- _mag_uninitialize_func();
- al_info("Failed to initialize ScreenCapturerWinMagnifier: "
- "error from creating magnifier window %ld",
- GetLastError());
- return false;
- }
- // Hide the host window.
- ShowWindow(_host_window, SW_HIDE);
- // Set the scaling callback to receive captured image.
- result = _mag_set_image_scaling_callback_func(_magnifier_window,
- &record_desktop_mag::on_mag_scaling_callback);
- if (!result) {
- _mag_uninitialize_func();
- al_info("Failed to initialize ScreenCapturerWinMagnifier: "
- "error from MagSetImageScalingCallback %ld",
- GetLastError());
- return false;
- }
- if (_excluded_window) {
- result = _mag_set_window_filter_list_func(_magnifier_window,
- MW_FILTERMODE_EXCLUDE,
- 1,
- &_excluded_window);
- if (!result) {
- _mag_uninitialize_func();
- al_warn("Failed to initialize ScreenCapturerWinMagnifier: "
- "error from MagSetWindowFilterList %ld",
- GetLastError());
- return false;
- }
- }
- _magnifier_initialized = true;
- return true;
- }
- int record_desktop_mag::do_mag_record()
- {
- if (!_magnifier_initialized) {
- al_error("Magnifier initialization failed.");
- return AE_NEED_INIT;
- }
- auto capture_image = [&](const RECORD_DESKTOP_RECT &rect) {
- // Set the magnifier control to cover the captured rect. The content of the
- // magnifier control will be the captured image.
- BOOL result = SetWindowPos(_magnifier_window,
- NULL,
- rect.left,
- rect.top,
- rect.right - rect.left,
- rect.bottom - rect.top,
- 0);
- if (!result) {
- al_error("Failed to call SetWindowPos: %ld. Rect = {%d, %d, %d, %d}",
- GetLastError(),
- rect.left,
- rect.top,
- rect.right,
- rect.bottom);
- return false;
- }
- _magnifier_capture_succeeded = false;
- RECT native_rect = {rect.left, rect.top, rect.right, rect.bottom};
- TlsSetValue(GetTlsIndex(), this);
- // on_mag_data will be called via on_mag_scaling_callback and fill in the
- // frame before _mag_set_window_source_func returns.
- DWORD exception = 0;
- result = seh_mag_set_window_source(_magnifier_window, native_rect, exception);
- if (!result) {
- al_error(
- "Failed to call MagSetWindowSource: %ld Exception: %ld. Rect = {%d, %d, %d, %d}",
- GetLastError(),
- exception,
- rect.left,
- rect.top,
- rect.right,
- rect.bottom);
- return false;
- }
- return _magnifier_capture_succeeded;
- };
- #if 0
- // Switch to the desktop receiving user input if different from the current
- // one.
- std::unique_ptr<Desktop> input_desktop(Desktop::GetInputDesktop());
- if (input_desktop.get() != NULL && !desktop_.IsSame(*input_desktop)) {
- // Release GDI resources otherwise SetThreadDesktop will fail.
- if (_desktop_dc) {
- ReleaseDC(NULL, _desktop_dc);
- _desktop_dc = NULL;
- }
- // If SetThreadDesktop() fails, the thread is still assigned a desktop.
- // So we can continue capture screen bits, just from the wrong desktop.
- desktop_.SetThreadDesktop(input_desktop.release());
- }
- DesktopRect rect = GetScreenRect(current_screen_id_, current_device_key_);
- #endif
- // capture_image may fail in some situations, e.g. windows8 metro mode. So
- // defer to the fallback capturer if magnifier capturer did not work.
- if (!capture_image(_rect)) {
- al_error("Magnifier capturer failed to capture a frame.");
- return AE_ERROR;
- }
- return AE_NO;
- }
- void record_desktop_mag::set_exclude(HWND excluded_window)
- {
- _excluded_window = excluded_window;
- if (_excluded_window && _magnifier_initialized) {
- _mag_set_window_filter_list_func(_magnifier_window,
- MW_FILTERMODE_EXCLUDE,
- 1,
- &_excluded_window);
- }
- }
- void record_desktop_mag::on_mag_data(void *data, const MAGIMAGEHEADER &header)
- {
- const int kBytesPerPixel = 4;
- int captured_bytes_per_pixel = header.cbSize / header.width / header.height;
- if (header.format != GUID_WICPixelFormat32bppRGBA || header.width != static_cast<UINT>(_width)
- || header.height != static_cast<UINT>(_height)
- || header.stride != static_cast<UINT>(kBytesPerPixel * _width)
- || captured_bytes_per_pixel != kBytesPerPixel) {
- al_warn("Output format does not match the captured format: width = %d, "
- "height = %d, stride= %d, bpp = %d, pixel format RGBA ? %d.",
- header.width,
- header.height,
- header.stride,
- captured_bytes_per_pixel,
- (header.format == GUID_WICPixelFormat32bppRGBA));
- return;
- }
- _current_pts = av_gettime_relative();
- AVFrame *frame = av_frame_alloc();
- frame->pts = _current_pts;
- frame->pkt_dts = frame->pts;
- frame->width = _width;
- frame->height = _height;
- frame->format = AV_PIX_FMT_BGRA;
- frame->pict_type = AV_PICTURE_TYPE_I;
- frame->pkt_size = _width * _height * 4;
- av_image_fill_arrays(frame->data,
- frame->linesize,
- reinterpret_cast<uint8_t *>(data),
- AV_PIX_FMT_BGRA,
- _width,
- _height,
- 1);
- if (_on_data)
- _on_data(frame);
- av_frame_free(&frame);
- _magnifier_capture_succeeded = true;
- }
- bool record_desktop_mag::seh_mag_set_window_source(HWND hwnd, RECT rect, DWORD &exception)
- {
- if (!_mag_set_window_source_func)
- return false;
- __try {
- return _mag_set_window_source_func(hwnd, rect);
- } __except (EXCEPTION_EXECUTE_HANDLER) {
- exception = ::GetExceptionCode();
- return false;
- }
- return false;
- }
- } // namespace am
|