【音视频】使用DXGI实现多屏幕采集(4-2)

既然使用ffmpeg gdigrab可以实现辅屏的采集,那么必须适配一下DXGI的多屏幕采集(先前实现参考《【音视频】WIN8|WIN10的桌面采集技术-DXGI(一)》)。

1、修改初始化d3d函数

将获取的IDXGIAdapter和IDXGIOutput保存为成员

int DuplicationCaptor::initD3d()
{
	int err = ERROR_CODE_OK;
	do {
		err = getAdapter(&m_adapter, &m_output, m_outputIndex);
		HCMDR_ERROR_CODE_BREAK(err);

		err = createD3dDevice(m_adapter, &m_d3dDevice);
		HCMDR_ERROR_CODE_BREAK(err);
	} while (0);

	return err;
}

其中getAdapter实现修改如下:

int DuplicationCaptor::getAdapter(IDXGIAdapter** adapter, IDXGIOutput** output, uint32_t& outputIndex)
{
	int err = ERROR_CODE_OK;

	do {
		std::list<IDXGIAdapter*> adapters;
		err = getAdapters(adapters);
		HCMDR_ERROR_CODE_BREAK(err);
		if (adapters.empty()) {
			err = ERROR_CODE_DXGI_FOUND_ADAPTER_FAILED;
			break;
		}

		bool found = false;
		for (std::list<IDXGIAdapter*>::iterator it = adapters.begin(); it != adapters.end(); it++) {
			UINT index = 0;
			IDXGIOutput* adapterOutput = nullptr;
			DXGI_ADAPTER_DESC adapterDesc = { 0 };
			(*it)->GetDesc(&adapterDesc);
			for (index = 0; (*it)->EnumOutputs(index, &adapterOutput) != DXGI_ERROR_NOT_FOUND; index++) {
				DXGI_OUTPUT_DESC outputDesc = { 0 };
				RECT outputRect = { 0 };
				HRESULT hr = adapterOutput->GetDesc(&outputDesc);
				if (FAILED(hr)) {
					continue;
				}

				outputRect = outputDesc.DesktopCoordinates;
				// The target area is within the selected area
				if (m_rect.left >= outputRect.left && m_rect.top >= outputRect.top &&
					m_rect.right <= outputRect.right && m_rect.bottom <= outputRect.bottom) {
					found = true;
					break;
				}
			}
			if (found) {
				err = ERROR_CODE_OK;
				*adapter = *it;
				*output = adapterOutput;
				outputIndex = index;
				break;
			}
		}
	} while (0);

	return err;
}

2、修改初始化dxgi函数

使用获取的IDXGIOutput* m_output获取IDXGIOutputDuplication* m_duplication

int DuplicationCaptor::initDuplication()
{
	int err = ERROR_CODE_OK;

	do {
		HRESULT hr;

		IDXGIOutput* output = m_output;
		output->GetDesc(&m_outputDesc);

		IDXGIOutput1* output1 = nullptr;
		hr = output->QueryInterface(__uuidof(IDXGIOutput1), reinterpret_cast<void**>(&output1));
		output->Release();
		output = nullptr;
		if (FAILED(hr)) {
			err = ERROR_CODE_DXGI_QUERY_INTERFACE_FAILED;
			break;
		}

		// Create desktop duplication
		hr = output1->DuplicateOutput(m_d3dDevice, &m_duplication);
		output1->Release();
		output1 = nullptr;
		if (FAILED(hr)) {
			err = ERROR_CODE_DXGI_DUPLICATE_FAILED;
			if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE) {
				err = ERROR_CODE_DXGI_DUPLICATE_NOT_AVALIABLE;
			}
			LOGGER::Logger::log(LOGGER::LOG_TYPE_ERROR, "[%s] duplicate output failed %ld", __FUNCTION__, hr);
			break;
		}
	} while (0);

	return err;
}

3、采集桌面时注意重启采集器的情况

在采集线程中添加reinit:

void DuplicationCaptor::captureProcess()
{
	/* 省略 */
	while (m_running) {
		err = reinit();
		if (err != ERROR_CODE_OK) {
			if (m_onVideoCaptureError != nullptr) {
				m_onVideoCaptureError(err, m_index);
			}
			break;
		}

		err = getDuplicatedFrame(&frameInfo);
		/* 省略 */
	}
	/* 省略 */
}

reinit的实现如下。当桌面分辨率改变时重启采集器。

int DuplicationCaptor::reinit()
{
	int err = ERROR_CODE_OK;

	long handle = atol(m_deviceId.c_str());
	HMONITOR monitor = reinterpret_cast<HMONITOR>(handle);
	MONITORINFO mi;
	mi.cbSize = sizeof(mi);
	do {
		if (!GetMonitorInfo(monitor, &mi)) {
			err = ERROR_CODE_DEVICE_GET_MONITOR_FAILED;
			break;
		}

		RECT rect = mi.rcMonitor;
		if (rect.left != m_rect.left || rect.top != m_rect.top ||
			rect.right - rect.left != m_rect.right - m_rect.left ||
			rect.bottom - rect.top != m_rect.bottom - m_rect.top) {
			cleanup();
			err = init(m_deviceId, m_fps);
			HCMDR_ERROR_CODE_BREAK(err);

			if (!attatchedDesktop()) {
				err = ERROR_CODE_DXGI_ATTATCH_DESKTOP_FAILED;
				LOGGER::Logger::log(LOGGER::LOG_TYPE_ERROR, "[%s] attach desktop error: %s, last error: %lu",
					__FUNCTION__, HCMDR_GET_ERROR_DESC(err), GetLastError());
				if (m_onVideoCaptureError != nullptr) {
					m_onVideoCaptureError(err, m_index);
				}
				break;
			}

			err = initDuplication();
			if (err != ERROR_CODE_OK) {
				LOGGER::Logger::log(LOGGER::LOG_TYPE_ERROR, "[%s] init duplication error: %s", __FUNCTION__,
					HCMDR_GET_ERROR_DESC(err));
				if (m_onVideoCaptureError != nullptr) {
					m_onVideoCaptureError(err, m_index);
				}
				break;
			}
		}
	} while (0);

	return err;
}
上一篇:【Python】读取Oracle连接表形成CSV文件


下一篇:kettle表输入到csv文件输出