#include "record_desktop_duplication.h" #include "d3d_helper.h" #include "d3d_pixelshader.h" #include "d3d_vertexshader.h" #include "system_lib.h" #include "utils_string.h" #include "error_define.h" #include "log_helper.h" #include "system_error.h" namespace am { record_desktop_duplication::record_desktop_duplication() { _data_type = RECORD_DESKTOP_DATA_TYPES::AT_DESKTOP_BGRA; _buffer = NULL; _buffer_size = 0; _d3d11 = nullptr; _dxgi = nullptr; _d3d_device = nullptr; _d3d_ctx = nullptr; _d3d_vshader = nullptr; _d3d_pshader = nullptr; _d3d_inlayout = nullptr; _d3d_samplerlinear = nullptr; _duplication = nullptr; _image = nullptr; _output_des = {0}; _output_index = 0; ZeroMemory(&_cursor_info, sizeof(_cursor_info)); } record_desktop_duplication::~record_desktop_duplication() { stop(); clean_up(); } int record_desktop_duplication::init(const RECORD_DESKTOP_RECT &rect, const int fps) { int error = AE_NO; if (_inited == true) { return error; } _fps = fps; _rect = rect; do { _d3d11 = load_system_library("d3d11.dll"); _dxgi = load_system_library("dxgi.dll"); if (!_d3d11 || !_dxgi) { error = AE_D3D_LOAD_FAILED; break; } error = init_d3d11(); if (error != AE_NO) break; _width = rect.right - rect.left; _height = rect.bottom - rect.top; _buffer_size = (_width * 32 + 31) / 32 * _height * 4; _buffer = new uint8_t[_buffer_size]; _start_time = av_gettime_relative(); _time_base = {1, AV_TIME_BASE}; _pixel_fmt = AV_PIX_FMT_BGRA; error = init_duplication(); if (error != AE_NO) { break; } _inited = true; } while (0); if (error != AE_NO) { al_debug("%s,last error:%s", err2str(error), system_error::error2str(GetLastError()).c_str()); } return error; } int record_desktop_duplication::start() { if (_running == true) { al_warn("record desktop duplication is already running"); return AE_NO; } if (_inited == false) { return AE_NEED_INIT; } _running = true; _thread = std::thread(std::bind(&record_desktop_duplication::record_func, this)); return AE_NO; } int record_desktop_duplication::pause() { _paused = true; return AE_NO; } int record_desktop_duplication::resume() { _paused = false; return AE_NO; } int record_desktop_duplication::stop() { _running = false; if (_thread.joinable()) _thread.join(); return AE_NO; } void record_desktop_duplication::clean_up() { _inited = false; if (_buffer) delete[] _buffer; _buffer = nullptr; if (_cursor_info.buff) { delete[] _cursor_info.buff; _cursor_info.buff = nullptr; } ZeroMemory(&_cursor_info, sizeof(_cursor_info)); //Clean up duplication interfaces clean_duplication(); //Clean up d3d11 interfaces clean_d3d11(); //finally free d3d11 & dxgi library if (_d3d11) free_system_library(_d3d11); _d3d11 = nullptr; if (_dxgi) free_system_library(_dxgi); _dxgi = nullptr; } int record_desktop_duplication::get_dst_adapter(IDXGIAdapter **adapter) { int error = AE_NO; do { auto adapters = d3d_helper::get_adapters(&error, true); if (error != AE_NO || adapters.size() == 0) break; for (std::list::iterator itr = adapters.begin(); itr != adapters.end(); itr++) { IDXGIOutput *adapter_output = nullptr; DXGI_ADAPTER_DESC adapter_desc = {0}; DXGI_OUTPUT_DESC adapter_output_desc = {0}; (*itr)->GetDesc(&adapter_desc); al_debug("adaptor:%s", utils_string::unicode_ascii(adapter_desc.Description).c_str()); unsigned int n = 0; RECT output_rect; while ((*itr)->EnumOutputs(n, &adapter_output) != DXGI_ERROR_NOT_FOUND) { HRESULT hr = adapter_output->GetDesc(&adapter_output_desc); if (FAILED(hr)) continue; output_rect = adapter_output_desc.DesktopCoordinates; al_debug(" output:%s left:%d top:%d right:%d bottom:%d", utils_string::unicode_ascii(adapter_output_desc.DeviceName).c_str(), output_rect.left, output_rect.top, output_rect.right, output_rect.bottom); if (output_rect.left <= _rect.left && output_rect.top <= _rect.top && output_rect.right >= _rect.right && output_rect.bottom >= _rect.bottom) { error = AE_NO; break; } ++n; } if (error != AE_DXGI_FOUND_ADAPTER_FAILED) { *adapter = *itr; break; } } } while (0); return error; } int record_desktop_duplication::create_d3d_device(IDXGIAdapter *adapter, ID3D11Device **device) { int error = AE_NO; do { PFN_D3D11_CREATE_DEVICE create_device = (PFN_D3D11_CREATE_DEVICE) GetProcAddress(_d3d11, "D3D11CreateDevice"); if (!create_device) { error = AE_D3D_GET_PROC_FAILED; break; } HRESULT hr = S_OK; // Driver types supported // If you set the pAdapter parameter to a non - NULL value, // you must also set the DriverType parameter to the D3D_DRIVER_TYPE_UNKNOWN value. D3D_DRIVER_TYPE driver_types[] = { D3D_DRIVER_TYPE_UNKNOWN, D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_WARP, D3D_DRIVER_TYPE_REFERENCE, }; UINT n_driver_types = ARRAYSIZE(driver_types); // Feature levels supported D3D_FEATURE_LEVEL feature_levels[] = {D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_1}; UINT n_feature_levels = ARRAYSIZE(feature_levels); D3D_FEATURE_LEVEL feature_level; // Create device for (UINT driver_index = 0; driver_index < n_driver_types; ++driver_index) { hr = create_device(adapter, driver_types[driver_index], nullptr, 0, feature_levels, n_feature_levels, D3D11_SDK_VERSION, device, &feature_level, &_d3d_ctx); if (SUCCEEDED(hr)) break; } if (FAILED(hr)) { error = AE_D3D_CREATE_DEVICE_FAILED; break; } } while (0); return error; } int record_desktop_duplication::init_d3d11() { int error = AE_NO; do { IDXGIAdapter *adapter = nullptr; error = get_dst_adapter(&adapter); if (error != AE_NO) break; error = create_d3d_device(adapter, &_d3d_device); if (error != AE_NO) break; //No need for grab full screen,but in move & dirty rects copy #if 0 // VERTEX shader UINT Size = ARRAYSIZE(g_VS); HRESULT hr = _d3d_device->CreateVertexShader(g_VS, Size, nullptr, &_d3d_vshader); if (FAILED(hr)) { error = AE_D3D_CREATE_VERTEX_SHADER_FAILED; break; } // Input layout D3D11_INPUT_ELEMENT_DESC layouts[] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 } }; UINT n_layouts = ARRAYSIZE(layouts); hr = _d3d_device->CreateInputLayout(layouts, n_layouts, g_VS, Size, &_d3d_inlayout); if (FAILED(hr)) { error = AE_D3D_CREATE_INLAYOUT_FAILED; break; } _d3d_ctx->IASetInputLayout(_d3d_inlayout); // Pixel shader Size = ARRAYSIZE(g_PS); hr = _d3d_device->CreatePixelShader(g_PS, Size, nullptr, &_d3d_pshader); if (FAILED(hr)) { error = AE_D3D_CREATE_PIXEL_SHADER_FAILED; break; } // Set up sampler D3D11_SAMPLER_DESC sampler_desc; RtlZeroMemory(&sampler_desc, sizeof(sampler_desc)); sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; sampler_desc.ComparisonFunc = D3D11_COMPARISON_NEVER; sampler_desc.MinLOD = 0; sampler_desc.MaxLOD = D3D11_FLOAT32_MAX; hr = _d3d_device->CreateSamplerState(&sampler_desc, &_d3d_samplerlinear); if (FAILED(hr)) { error = AE_D3D_CREATE_SAMPLERSTATE_FAILED; break; } #endif } while (0); return error; } void record_desktop_duplication::clean_d3d11() { if (_d3d_device) _d3d_device->Release(); _d3d_device = nullptr; if (_d3d_ctx) _d3d_ctx->Release(); _d3d_ctx = nullptr; if (_d3d_vshader) _d3d_vshader->Release(); _d3d_vshader = nullptr; if (_d3d_pshader) _d3d_pshader->Release(); _d3d_pshader = nullptr; if (_d3d_inlayout) _d3d_inlayout->Release(); _d3d_inlayout = nullptr; if (_d3d_samplerlinear) _d3d_samplerlinear->Release(); _d3d_samplerlinear = nullptr; } int record_desktop_duplication::init_duplication() { int error = AE_NO; do { // Get DXGI device IDXGIDevice *dxgi_device = nullptr; HRESULT hr = _d3d_device->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast(&dxgi_device)); if (FAILED(hr)) { error = AE_D3D_QUERYINTERFACE_FAILED; break; } // Get DXGI adapter IDXGIAdapter *dxgi_adapter = nullptr; hr = dxgi_device->GetParent(__uuidof(IDXGIAdapter), reinterpret_cast(&dxgi_adapter)); dxgi_device->Release(); dxgi_device = nullptr; if (FAILED(hr)) { error = AE_DUP_GET_PARENT_FAILED; break; } // Get output IDXGIOutput *dxgi_output = nullptr; hr = dxgi_adapter->EnumOutputs(_output_index, &dxgi_output); dxgi_adapter->Release(); dxgi_adapter = nullptr; if (FAILED(hr)) { error = AE_DUP_ENUM_OUTPUT_FAILED; break; } dxgi_output->GetDesc(&_output_des); // QI for Output 1 IDXGIOutput1 *dxgi_output1 = nullptr; hr = dxgi_output->QueryInterface(__uuidof(dxgi_output1), reinterpret_cast(&dxgi_output1)); dxgi_output->Release(); dxgi_output = nullptr; if (FAILED(hr)) { error = AE_DUP_QI_FAILED; break; } // Create desktop duplication hr = dxgi_output1->DuplicateOutput(_d3d_device, &_duplication); dxgi_output1->Release(); dxgi_output1 = nullptr; if (FAILED(hr)) { error = AE_DUP_DUPLICATE_FAILED; if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE) { error = AE_DUP_DUPLICATE_MAX_FAILED; } al_error("duplicate output failed,%lld", hr); break; } } while (0); return error; } int record_desktop_duplication::free_duplicated_frame() { HRESULT hr = _duplication->ReleaseFrame(); if (FAILED(hr)) { return AE_DUP_RELEASE_FRAME_FAILED; } if (_image) { _image->Release(); _image = nullptr; } return AE_DUP_RELEASE_FRAME_FAILED; } void record_desktop_duplication::clean_duplication() { if (_duplication) _duplication->Release(); if (_image) _image->Release(); _duplication = nullptr; _image = nullptr; } bool record_desktop_duplication::attatch_desktop() { HDESK desktop = nullptr; desktop = OpenInputDesktop(0, FALSE, GENERIC_ALL); if (!desktop) { // We do not have access to the desktop so request a retry return false; } // Attach desktop to this thread bool battached = SetThreadDesktop(desktop) != 0; CloseDesktop(desktop); if (!battached) { // We do not have access to the desktop so request a retry return false; } return true; } int record_desktop_duplication::get_desktop_image(DXGI_OUTDUPL_FRAME_INFO *frame_info) { IDXGIResource *dxgi_res = nullptr; // Get new frame (adaptive low-latency timeout) int timeout_ms = 10; if (_fps > 0) { timeout_ms = 1000 / _fps / 2; if (timeout_ms < 1) timeout_ms = 1; } HRESULT hr = _duplication->AcquireNextFrame(timeout_ms, frame_info, &dxgi_res); // Timeout will return when desktop has no chane if (hr == DXGI_ERROR_WAIT_TIMEOUT) return AE_TIMEOUT; if (FAILED(hr)) return AE_DUP_ACQUIRE_FRAME_FAILED; // QI for IDXGIResource hr = dxgi_res->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast(&_image)); dxgi_res->Release(); dxgi_res = nullptr; if (FAILED(hr)) return AE_DUP_QI_FRAME_FAILED; // Copy old description D3D11_TEXTURE2D_DESC frame_desc; _image->GetDesc(&frame_desc); // Create a new staging buffer for fill frame image ID3D11Texture2D *new_image = NULL; frame_desc.Usage = D3D11_USAGE_STAGING; frame_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; frame_desc.BindFlags = 0; frame_desc.MiscFlags = 0; frame_desc.MipLevels = 1; frame_desc.ArraySize = 1; frame_desc.SampleDesc.Count = 1; frame_desc.SampleDesc.Quality = 0; hr = _d3d_device->CreateTexture2D(&frame_desc, NULL, &new_image); if (FAILED(hr)) return AE_DUP_CREATE_TEXTURE_FAILED; // Copy next staging buffer to new staging buffer _d3d_ctx->CopyResource(new_image, _image); #if 1 // Should calc the row pitch ,and compare dst row pitch with frame row pitch // Create staging buffer for map bits IDXGISurface *dxgi_surface = NULL; hr = new_image->QueryInterface(__uuidof(IDXGISurface), (void **) (&dxgi_surface)); new_image->Release(); if (FAILED(hr)) return AE_DUP_QI_DXGI_FAILED; // Map buff to mapped rect structure DXGI_MAPPED_RECT mapped_rect; hr = dxgi_surface->Map(&mapped_rect, DXGI_MAP_READ); if (FAILED(hr)) return AE_DUP_MAP_FAILED; int dst_offset_x = _rect.left - _output_des.DesktopCoordinates.left; int dst_offset_y = _rect.top - _output_des.DesktopCoordinates.top; int dst_rowpitch = min(frame_desc.Width, _rect.right - _rect.left) * 4; int dst_colpitch = min(_height, _output_des.DesktopCoordinates.bottom - _output_des.DesktopCoordinates.top - dst_offset_y); for (int h = 0; h < dst_colpitch; h++) { memcpy_s(_buffer + h * dst_rowpitch, dst_rowpitch, (BYTE *) mapped_rect.pBits + (h + dst_offset_y) * mapped_rect.Pitch + dst_offset_x * 4, min(mapped_rect.Pitch, dst_rowpitch)); } dxgi_surface->Unmap(); dxgi_surface->Release(); dxgi_surface = nullptr; #else D3D11_MAPPED_SUBRESOURCE resource; UINT subresource = D3D11CalcSubresource(0, 0, 0); hr = _d3d_ctx->Map(new_image, subresource, D3D11_MAP_READ_WRITE, 0, &resource); new_image->Release(); if (FAILED(hr)) return AE_DUP_MAP_FAILED; int dst_rowpitch = frame_desc.Width * 4; for (int h = 0; h < frame_desc.Height; h++) { memcpy_s(_buffer + h * dst_rowpitch, dst_rowpitch, (BYTE *) resource.pData + h * resource.RowPitch, min(resource.RowPitch, dst_rowpitch)); } #endif return AE_NO; } int record_desktop_duplication::get_desktop_cursor(const DXGI_OUTDUPL_FRAME_INFO *frame_info) { // A non-zero mouse update timestamp indicates that there is a mouse position update and optionally a shape change if (frame_info->LastMouseUpdateTime.QuadPart == 0) return AE_NO; bool b_updated = true; // Make sure we don't update pointer position wrongly // If pointer is invisible, make sure we did not get an update from another output that the last time that said pointer // was visible, if so, don't set it to invisible or update. if (!frame_info->PointerPosition.Visible && (_cursor_info.output_index != _output_index)) b_updated = false; // If two outputs both say they have a visible, only update if new update has newer timestamp if (frame_info->PointerPosition.Visible && _cursor_info.visible && (_cursor_info.output_index != _output_index) && (_cursor_info.pre_timestamp.QuadPart > frame_info->LastMouseUpdateTime.QuadPart)) b_updated = false; // Update position if (b_updated) { _cursor_info.position.x = frame_info->PointerPosition.Position.x + _output_des.DesktopCoordinates.left; _cursor_info.position.y = frame_info->PointerPosition.Position.y + _output_des.DesktopCoordinates.top; _cursor_info.output_index = _output_index; _cursor_info.pre_timestamp = frame_info->LastMouseUpdateTime; _cursor_info.visible = frame_info->PointerPosition.Visible != 0; } // No new shape only update cursor positions & visible state if (frame_info->PointerShapeBufferSize == 0) { return AE_NO; } // Old buffer too small if (frame_info->PointerShapeBufferSize > _cursor_info.size) { if (_cursor_info.buff) { delete[] _cursor_info.buff; _cursor_info.buff = nullptr; } _cursor_info.buff = new (std::nothrow) BYTE[frame_info->PointerShapeBufferSize]; if (!_cursor_info.buff) { _cursor_info.size = 0; return AE_ALLOCATE_FAILED; } // Update buffer size _cursor_info.size = frame_info->PointerShapeBufferSize; } // Get shape UINT BufferSizeRequired; HRESULT hr = _duplication->GetFramePointerShape(frame_info->PointerShapeBufferSize, reinterpret_cast(_cursor_info.buff), &BufferSizeRequired, &(_cursor_info.shape)); if (FAILED(hr)) { delete[] _cursor_info.buff; _cursor_info.buff = nullptr; _cursor_info.size = 0; return AE_DUP_GET_CURSORSHAPE_FAILED; } return AE_NO; } static unsigned int bit_reverse(unsigned int n) { n = ((n >> 1) & 0x55555555) | ((n << 1) & 0xaaaaaaaa); n = ((n >> 2) & 0x33333333) | ((n << 2) & 0xcccccccc); n = ((n >> 4) & 0x0f0f0f0f) | ((n << 4) & 0xf0f0f0f0); n = ((n >> 8) & 0x00ff00ff) | ((n << 8) & 0xff00ff00); n = ((n >> 16) & 0x0000ffff) | ((n << 16) & 0xffff0000); return n; } void record_desktop_duplication::draw_cursor() { if (_cursor_info.visible == false) return; int cursor_width = 0, cursor_height = 0, left = 0, top = 0; cursor_width = _cursor_info.shape.Width; cursor_height = _cursor_info.shape.Height; // In case that,the value of position is negative value left = abs(_cursor_info.position.x - _rect.left); top = abs(_cursor_info.position.y - _rect.top); // Notice here if (_cursor_info.shape.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME) cursor_height = cursor_height / 2; //Skip invisible pixel cursor_width = min(_width - left, cursor_width); cursor_height = min(_height - top, cursor_height); //al_debug("left:%d top:%d width:%d height:%d type:%d", left, top, cursor_width, height, _cursor_info.shape.Type); switch (_cursor_info.shape.Type) { // The pointer type is a color mouse pointer, // which is a color bitmap. The bitmap's size // is specified by width and height in a 32 bpp // ARGB DIB format. // should trans cursor to BGRA? case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR: { unsigned int *cursor_32 = reinterpret_cast(_cursor_info.buff); unsigned int *screen_32 = reinterpret_cast(_buffer); for (int row = 0; row < cursor_height; row++) { for (int col = 0; col < cursor_width; col++) { unsigned int cur_cursor_val = cursor_32[col + (row * (_cursor_info.shape.Pitch / sizeof(UINT)))]; //Skip black or empty value if (cur_cursor_val == 0x00000000) continue; else screen_32[(abs(top) + row) * _width + abs(left) + col] = cur_cursor_val; //bit_reverse(cur_cursor_val); } } break; } // The pointer type is a monochrome mouse pointer, // which is a monochrome bitmap. The bitmap's size // is specified by width and height in a 1 bits per // pixel (bpp) device independent bitmap (DIB) format // AND mask that is followed by another 1 bpp DIB format // XOR mask of the same size. case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME: { unsigned int *cursor_32 = reinterpret_cast(_cursor_info.buff); unsigned int *screen_32 = reinterpret_cast(_buffer); for (int row = 0; row < cursor_height; row++) { BYTE MASK = 0x80; for (int col = 0; col < cursor_width; col++) { // Get masks using appropriate offsets BYTE AndMask = _cursor_info.buff[(col / 8) + (row * (_cursor_info.shape.Pitch))] & MASK; BYTE XorMask = _cursor_info.buff[(col / 8) + ((row + cursor_height) * (_cursor_info.shape.Pitch))] & MASK; UINT AndMask32 = (AndMask) ? 0xFFFFFFFF : 0xFF000000; UINT XorMask32 = (XorMask) ? 0x00FFFFFF : 0x00000000; // Set new pixel screen_32[(abs(top) + row) * _width + abs(left) + col] = (screen_32[(abs(top) + row) * _width + abs(left) + col] & AndMask32) ^ XorMask32; // Adjust mask if (MASK == 0x01) { MASK = 0x80; } else { MASK = MASK >> 1; } } } break; } // The pointer type is a masked color mouse pointer. // A masked color mouse pointer is a 32 bpp ARGB format // bitmap with the mask value in the alpha bits. The only // allowed mask values are 0 and 0xFF. When the mask value // is 0, the RGB value should replace the screen pixel. // When the mask value is 0xFF, an XOR operation is performed // on the RGB value and the screen pixel; the result replaces the screen pixel. case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: { unsigned int *cursor_32 = reinterpret_cast(_cursor_info.buff); unsigned int *screen_32 = reinterpret_cast(_buffer); for (int row = 0; row < cursor_height; row++) { for (int col = 0; col < cursor_width; col++) { unsigned int cur_cursor_val = cursor_32[col + (row * (_cursor_info.shape.Pitch / sizeof(UINT)))]; unsigned int cur_screen_val = screen_32[(abs(top) + row) * _width + abs(left) + col]; unsigned int mask_val = 0xFF000000 & cur_cursor_val; if (mask_val) { //0xFF: XOR operation is performed on the RGB value and the screen pixel cur_screen_val = (cur_screen_val ^ cur_cursor_val) | 0xFF000000; } else { //0x00: the RGB value should replace the screen pixel cur_screen_val = cur_cursor_val | 0xFF000000; } } } break; } default: break; } } void record_desktop_duplication::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); } void record_desktop_duplication::record_func() { AVFrame *frame = av_frame_alloc(); int64_t pre_pts = 0, dur = AV_TIME_BASE / _fps; int error = AE_NO; #if 0 if (attatch_desktop() != true) { al_fatal("duplication attach desktop failed :%s", system_error::error2str(GetLastError()).c_str()); if (_on_error) _on_error(AE_DUP_ATTATCH_FAILED); return; } #endif DXGI_OUTDUPL_FRAME_INFO frame_info; while (_running) { //Timeout is no new picture,no need to update if ((error = get_desktop_image(&frame_info)) == AE_TIMEOUT) { // Push duplicate frame to keep steady cadence and avoid buffer growth frame->pts = av_gettime_relative(); frame->pkt_dts = frame->pts; frame->width = _width; frame->height = _height; frame->format = AV_PIX_FMT_BGRA; frame->pict_type = AV_PICTURE_TYPE_NONE; frame->pkt_size = _width * _height * 4; av_image_fill_arrays(frame->data, frame->linesize, _buffer, AV_PIX_FMT_BGRA, _width, _height, 1); if (_on_data) _on_data(frame); do_sleep(dur, pre_pts, frame->pts); pre_pts = frame->pts; continue; } if (error != AE_NO) { while (_running) { Sleep(50); clean_duplication(); if ((error = init_duplication()) != AE_NO) { if (_on_error) _on_error(error); } else break; } continue; } if ((error = get_desktop_cursor(&frame_info)) == AE_NO) draw_cursor(); free_duplicated_frame(); // 使用相对时间戳,与muxer的_base_time保持一致 frame->pts = av_gettime_relative(); frame->pkt_dts = frame->pts; // frame->pkt_pts = frame->pts; frame->width = _width; frame->height = _height; frame->format = AV_PIX_FMT_BGRA; frame->pict_type = AV_PICTURE_TYPE_NONE; frame->pkt_size = _width * _height * 4; av_image_fill_arrays(frame->data, frame->linesize, _buffer, AV_PIX_FMT_BGRA, _width, _height, 1); #if 0 //save bmp to test BITMAPINFOHEADER bi; bi.biSize = sizeof(BITMAPINFOHEADER); bi.biWidth = _width; bi.biHeight = _height * (-1); bi.biPlanes = 1; bi.biBitCount = 32;//should get from system color bits bi.biCompression = BI_RGB; bi.biSizeImage = 0; bi.biXPelsPerMeter = 0; bi.biYPelsPerMeter = 0; bi.biClrUsed = 0; bi.biClrImportant = 0; BITMAPFILEHEADER bf; bf.bfType = 0x4d42; bf.bfReserved1 = 0; bf.bfReserved2 = 0; bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); bf.bfSize = bf.bfOffBits + _width * _height * 4; FILE *fp = fopen("..\\..\\save.bmp", "wb+"); fwrite(&bf, 1, sizeof(bf), fp); fwrite(&bi, 1, sizeof(bi), fp); fwrite(_buffer, 1, _buffer_size, fp); fflush(fp); fclose(fp); #endif if (_on_data) _on_data(frame); do_sleep(dur, pre_pts, frame->pts); pre_pts = frame->pts; } av_frame_free(&frame); } } // namespace am