#ifdef UNSAFE_BUFFERS_BUILD
#pragma allow_unsafe_buffers
#endif
#include "media/capture/video/win/video_capture_device_utils_win.h"
#include <cmath>
#include <iostream>
#include "base/check_op.h"
#include "base/win/win_util.h"
namespace media {
namespace {
const int kDegreesToArcSeconds = 3600;
const int kSecondsTo100MicroSeconds = 10000;
bool IsPortraitDevice(DWORD display_height,
DWORD display_width,
DWORD display_orientation) {
if (display_orientation == DMDO_DEFAULT || display_orientation == DMDO_180)
return display_height >= display_width;
else
return display_height < display_width;
}
}
long CaptureAngleToPlatformValue(double arc_seconds) {
return std::round(arc_seconds / kDegreesToArcSeconds);
}
double PlatformAngleToCaptureValue(long degrees) {
return 1.0 * degrees * kDegreesToArcSeconds;
}
double PlatformAngleToCaptureStep(long step, double min, double max) {
return PlatformAngleToCaptureValue(step);
}
long CaptureExposureTimeToPlatformValue(double hundreds_of_microseconds) {
return std::log2(hundreds_of_microseconds / kSecondsTo100MicroSeconds);
}
double PlatformExposureTimeToCaptureValue(long log_seconds) {
return std::exp2(log_seconds) * kSecondsTo100MicroSeconds;
}
double PlatformExposureTimeToCaptureStep(long log_step,
double min,
double max) {
return (std::exp2(log_step) - 1) * min;
}
int GetCameraRotation(VideoFacingMode facing) {
int rotation = 0;
if (!IsInternalCamera(facing)) {
return rotation;
}
DISPLAY_DEVICE internal_display_device;
if (!HasActiveInternalDisplayDevice(&internal_display_device)) {
return rotation;
}
DCHECK_NE(facing, VideoFacingMode::MEDIA_VIDEO_FACING_NONE);
DEVMODE mode;
::ZeroMemory(&mode, sizeof(mode));
mode.dmSize = sizeof(mode);
mode.dmDriverExtra = 0;
if (::EnumDisplaySettings(internal_display_device.DeviceName,
ENUM_CURRENT_SETTINGS, &mode)) {
int camera_offset = 0;
bool portrait_device = IsPortraitDevice(mode.dmPelsHeight, mode.dmPelsWidth,
mode.dmDisplayOrientation);
switch (mode.dmDisplayOrientation) {
case DMDO_DEFAULT:
if (portrait_device &&
facing == VideoFacingMode::MEDIA_VIDEO_FACING_ENVIRONMENT) {
camera_offset = 270;
} else if (portrait_device) {
camera_offset = 90;
} else {
camera_offset = 0;
}
break;
case DMDO_90:
if (portrait_device)
camera_offset = 180;
else if (facing == VideoFacingMode::MEDIA_VIDEO_FACING_ENVIRONMENT)
camera_offset = 270;
else
camera_offset = 90;
break;
case DMDO_180:
if (portrait_device &&
facing == VideoFacingMode::MEDIA_VIDEO_FACING_ENVIRONMENT) {
camera_offset = 90;
} else if (portrait_device) {
camera_offset = 270;
} else {
camera_offset = 180;
}
break;
case DMDO_270:
if (portrait_device)
camera_offset = 0;
else if (facing == VideoFacingMode::MEDIA_VIDEO_FACING_ENVIRONMENT)
camera_offset = 90;
else
camera_offset = 270;
break;
}
rotation = (360 - camera_offset) % 360;
}
return rotation;
}
bool IsAutoRotationEnabled() {
typedef BOOL(WINAPI * GetAutoRotationState)(PAR_STATE state);
static const auto get_rotation_state = reinterpret_cast<GetAutoRotationState>(
base::win::GetUser32FunctionPointer("GetAutoRotationState"));
if (get_rotation_state) {
AR_STATE auto_rotation_state;
::ZeroMemory(&auto_rotation_state, sizeof(AR_STATE));
if (get_rotation_state(&auto_rotation_state)) {
if (auto_rotation_state == AR_ENABLED) {
return true;
}
}
}
return false;
}
bool IsInternalCamera(VideoFacingMode facing) {
return facing == MEDIA_VIDEO_FACING_USER ||
facing == MEDIA_VIDEO_FACING_ENVIRONMENT;
}
bool HasActiveInternalDisplayDevice(DISPLAY_DEVICE* internal_display_device) {
DISPLAY_DEVICE display_device;
display_device.cb = sizeof(display_device);
for (int device_index = 0;; ++device_index) {
BOOL enum_result =
::EnumDisplayDevices(NULL, device_index, &display_device, 0);
if (!enum_result)
break;
if (!(display_device.StateFlags & DISPLAY_DEVICE_ACTIVE))
continue;
HRESULT hr = CheckPathInfoForInternal(display_device.DeviceName);
if (SUCCEEDED(hr)) {
*internal_display_device = display_device;
return true;
}
}
return false;
}
HRESULT CheckPathInfoForInternal(const PCWSTR device_name) {
HRESULT hr = S_OK;
UINT32 path_info_array_size = 0;
UINT32 mode_info_array_size = 0;
DISPLAYCONFIG_PATH_INFO* path_info_array = nullptr;
DISPLAYCONFIG_MODE_INFO* mode_info_array = nullptr;
do {
delete[] path_info_array;
path_info_array = nullptr;
delete[] mode_info_array;
mode_info_array = nullptr;
hr = HRESULT_FROM_WIN32(::GetDisplayConfigBufferSizes(
QDC_ONLY_ACTIVE_PATHS, &path_info_array_size, &mode_info_array_size));
if (FAILED(hr)) {
break;
}
path_info_array =
new (std::nothrow) DISPLAYCONFIG_PATH_INFO[path_info_array_size];
if (path_info_array == nullptr) {
hr = E_OUTOFMEMORY;
break;
}
mode_info_array =
new (std::nothrow) DISPLAYCONFIG_MODE_INFO[mode_info_array_size];
if (mode_info_array == nullptr) {
hr = E_OUTOFMEMORY;
break;
}
hr = HRESULT_FROM_WIN32(::QueryDisplayConfig(
QDC_ONLY_ACTIVE_PATHS, &path_info_array_size, path_info_array,
&mode_info_array_size, mode_info_array, nullptr));
} while (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));
int desired_path_index = -1;
if (SUCCEEDED(hr)) {
for (UINT32 path_index = 0; path_index < path_info_array_size;
++path_index) {
DISPLAYCONFIG_SOURCE_DEVICE_NAME source_name = {};
source_name.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
source_name.header.size = sizeof(source_name);
source_name.header.adapterId =
path_info_array[path_index].sourceInfo.adapterId;
source_name.header.id = path_info_array[path_index].sourceInfo.id;
hr =
HRESULT_FROM_WIN32(::DisplayConfigGetDeviceInfo(&source_name.header));
if (SUCCEEDED(hr)) {
if (wcscmp(device_name, source_name.viewGdiDeviceName) == 0 &&
IsInternalVideoOutput(
path_info_array[path_index].targetInfo.outputTechnology)) {
desired_path_index = path_index;
break;
}
}
}
}
if (desired_path_index == -1) {
hr = E_INVALIDARG;
}
delete[] path_info_array;
path_info_array = nullptr;
delete[] mode_info_array;
mode_info_array = nullptr;
return hr;
}
bool IsInternalVideoOutput(
const DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY video_output_tech_type) {
switch (video_output_tech_type) {
case DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL:
case DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EMBEDDED:
case DISPLAYCONFIG_OUTPUT_TECHNOLOGY_UDI_EMBEDDED:
return TRUE;
default:
return FALSE;
}
}
}