winrt: Fix aspect ratio deviation between preview and encoding streams
The source texture size from camera was wrong due to a missing IMediaDeviceController::SetMediaStreamPropertiesAsync call for the preview media type. Therefore Viewfinder and StillImage capture modes behave tha same from QCamera point of view. By default, the camera takes the lowest capture resolution and the lowest preview resolution with a matching aspect ratio. If the capture resolution is set, the preview resolution is set to the lowest possible resolution with a matching aspect ratio. Until viewfinder settings are implemented for WinRT, there is no way for user to change the viewfinder resolution. Change-Id: I4b76ceb46bd4c366561f5206d913b97c0d0df211 Task-Id: QTBUG-47465 Reviewed-by: Oliver Wolff <oliver.wolff@theqtcompany.com>
This commit is contained in:
@@ -84,9 +84,35 @@ QT_BEGIN_NAMESPACE
|
|||||||
#define FOCUS_RECT_BOUNDARY 1.0f
|
#define FOCUS_RECT_BOUNDARY 1.0f
|
||||||
#define FOCUS_RECT_POSITION_MIN 0.0f
|
#define FOCUS_RECT_POSITION_MIN 0.0f
|
||||||
#define FOCUS_RECT_POSITION_MAX 0.995f // FOCUS_RECT_BOUNDARY - FOCUS_RECT_HALF_SIZE
|
#define FOCUS_RECT_POSITION_MAX 0.995f // FOCUS_RECT_BOUNDARY - FOCUS_RECT_HALF_SIZE
|
||||||
|
#define ASPECTRATIO_EPSILON 0.01f
|
||||||
|
|
||||||
inline uint qHash (const QSize &key) {
|
HRESULT getMediaStreamResolutions(IMediaDeviceController *device,
|
||||||
return key.width() * key.height();
|
MediaStreamType type,
|
||||||
|
IVectorView<IMediaEncodingProperties *> **propertiesList,
|
||||||
|
QVector<QSize> *resolutions)
|
||||||
|
{
|
||||||
|
HRESULT hr;
|
||||||
|
hr = device->GetAvailableMediaStreamProperties(type, propertiesList);
|
||||||
|
Q_ASSERT_SUCCEEDED(hr);
|
||||||
|
quint32 listSize;
|
||||||
|
hr = (*propertiesList)->get_Size(&listSize);
|
||||||
|
Q_ASSERT_SUCCEEDED(hr);
|
||||||
|
resolutions->reserve(listSize);
|
||||||
|
for (quint32 index = 0; index < listSize; ++index) {
|
||||||
|
ComPtr<IMediaEncodingProperties> properties;
|
||||||
|
hr = (*propertiesList)->GetAt(index, &properties);
|
||||||
|
Q_ASSERT_SUCCEEDED(hr);
|
||||||
|
ComPtr<IVideoEncodingProperties> videoProperties;
|
||||||
|
hr = properties.As(&videoProperties);
|
||||||
|
Q_ASSERT_SUCCEEDED(hr);
|
||||||
|
UINT32 width, height;
|
||||||
|
hr = videoProperties->get_Width(&width);
|
||||||
|
Q_ASSERT_SUCCEEDED(hr);
|
||||||
|
hr = videoProperties->get_Height(&height);
|
||||||
|
Q_ASSERT_SUCCEEDED(hr);
|
||||||
|
resolutions->append(QSize(width, height));
|
||||||
|
}
|
||||||
|
return resolutions->isEmpty() ? MF_E_INVALID_FORMAT : hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, size_t typeSize> struct CustomPropertyValue;
|
template<typename T, size_t typeSize> struct CustomPropertyValue;
|
||||||
@@ -549,7 +575,6 @@ public:
|
|||||||
ComPtr<IFocusControl> focusControl;
|
ComPtr<IFocusControl> focusControl;
|
||||||
ComPtr<IRegionsOfInterestControl> regionsOfInterestControl;
|
ComPtr<IRegionsOfInterestControl> regionsOfInterestControl;
|
||||||
|
|
||||||
QSize size;
|
|
||||||
QPointer<QWinRTCameraVideoRendererControl> videoRenderer;
|
QPointer<QWinRTCameraVideoRendererControl> videoRenderer;
|
||||||
QPointer<QWinRTVideoDeviceSelectorControl> videoDeviceSelector;
|
QPointer<QWinRTVideoDeviceSelectorControl> videoDeviceSelector;
|
||||||
QPointer<QWinRTCameraImageCaptureControl> imageCaptureControl;
|
QPointer<QWinRTCameraImageCaptureControl> imageCaptureControl;
|
||||||
@@ -568,7 +593,7 @@ QWinRTCameraControl::QWinRTCameraControl(QObject *parent)
|
|||||||
d->captureMode = QCamera::CaptureStillImage;
|
d->captureMode = QCamera::CaptureStillImage;
|
||||||
d->captureFailedCookie.value = 0;
|
d->captureFailedCookie.value = 0;
|
||||||
d->recordLimitationCookie.value = 0;
|
d->recordLimitationCookie.value = 0;
|
||||||
d->videoRenderer = new QWinRTCameraVideoRendererControl(d->size, this);
|
d->videoRenderer = new QWinRTCameraVideoRendererControl(QSize(), this);
|
||||||
connect(d->videoRenderer, &QWinRTCameraVideoRendererControl::bufferRequested,
|
connect(d->videoRenderer, &QWinRTCameraVideoRendererControl::bufferRequested,
|
||||||
this, &QWinRTCameraControl::onBufferRequested);
|
this, &QWinRTCameraControl::onBufferRequested);
|
||||||
d->videoDeviceSelector = new QWinRTVideoDeviceSelectorControl(this);
|
d->videoDeviceSelector = new QWinRTVideoDeviceSelectorControl(this);
|
||||||
@@ -888,72 +913,72 @@ HRESULT QWinRTCameraControl::initialize()
|
|||||||
ComPtr<IMediaDeviceController> deviceController;
|
ComPtr<IMediaDeviceController> deviceController;
|
||||||
hr = videoDeviceController.As(&deviceController);
|
hr = videoDeviceController.As(&deviceController);
|
||||||
Q_ASSERT_SUCCEEDED(hr);
|
Q_ASSERT_SUCCEEDED(hr);
|
||||||
ComPtr<IVectorView<IMediaEncodingProperties *>> encodingPropertiesList;
|
|
||||||
MediaStreamType mediaStreamType;
|
// Get preview stream properties.
|
||||||
switch (d->captureMode) {
|
ComPtr<IVectorView<IMediaEncodingProperties *>> previewPropertiesList;
|
||||||
default:
|
QVector<QSize> previewResolutions;
|
||||||
case QCamera::CaptureViewfinder:
|
hr = getMediaStreamResolutions(deviceController.Get(),
|
||||||
mediaStreamType = MediaStreamType_VideoPreview;
|
MediaStreamType_VideoPreview,
|
||||||
break;
|
&previewPropertiesList,
|
||||||
case QCamera::CaptureStillImage:
|
&previewResolutions);
|
||||||
mediaStreamType = MediaStreamType_Photo;
|
RETURN_HR_IF_FAILED("Failed to find a suitable video format");
|
||||||
break;
|
|
||||||
case QCamera::CaptureVideo:
|
MediaStreamType mediaStreamType =
|
||||||
mediaStreamType = MediaStreamType_VideoRecord;
|
d->captureMode == QCamera::CaptureVideo ? MediaStreamType_VideoRecord : MediaStreamType_Photo;
|
||||||
break;
|
|
||||||
}
|
// Get capture stream properties.
|
||||||
hr = deviceController->GetAvailableMediaStreamProperties(mediaStreamType, &encodingPropertiesList);
|
ComPtr<IVectorView<IMediaEncodingProperties *>> capturePropertiesList;
|
||||||
|
QVector<QSize> captureResolutions;
|
||||||
|
hr = getMediaStreamResolutions(deviceController.Get(),
|
||||||
|
mediaStreamType,
|
||||||
|
&capturePropertiesList,
|
||||||
|
&captureResolutions);
|
||||||
|
RETURN_HR_IF_FAILED("Failed to find a suitable video format");
|
||||||
|
|
||||||
|
// Set capture resolutions.
|
||||||
|
d->imageEncoderControl->setSupportedResolutionsList(captureResolutions.toList());
|
||||||
|
const QSize captureResolution = d->imageEncoderControl->imageSettings().resolution();
|
||||||
|
const quint32 captureResolutionIndex = captureResolutions.indexOf(captureResolution);
|
||||||
|
ComPtr<IMediaEncodingProperties> captureProperties;
|
||||||
|
hr = capturePropertiesList->GetAt(captureResolutionIndex, &captureProperties);
|
||||||
|
Q_ASSERT_SUCCEEDED(hr);
|
||||||
|
hr = deviceController->SetMediaStreamPropertiesAsync(mediaStreamType, captureProperties.Get(), &op);
|
||||||
|
Q_ASSERT_SUCCEEDED(hr);
|
||||||
|
hr = QWinRTFunctions::await(op);
|
||||||
Q_ASSERT_SUCCEEDED(hr);
|
Q_ASSERT_SUCCEEDED(hr);
|
||||||
|
|
||||||
d->size = QSize();
|
// Set preview resolution.
|
||||||
quint32 encodingPropertiesListSize;
|
QVector<QSize> filtered;
|
||||||
hr = encodingPropertiesList->get_Size(&encodingPropertiesListSize);
|
const float captureAspectRatio = float(captureResolution.width()) / captureResolution.height();
|
||||||
Q_ASSERT_SUCCEEDED(hr);
|
foreach (const QSize &resolution, previewResolutions) {
|
||||||
QHash<QSize, ComPtr<IVideoEncodingProperties>> videoEncodingPropertiesList;
|
const float aspectRatio = float(resolution.width()) / resolution.height();
|
||||||
int pixelCount = 0;
|
if (qAbs(aspectRatio - captureAspectRatio) <= ASPECTRATIO_EPSILON)
|
||||||
for (quint32 i = 0; i < encodingPropertiesListSize; ++i) {
|
filtered.append(resolution);
|
||||||
ComPtr<IMediaEncodingProperties> properties;
|
|
||||||
hr = encodingPropertiesList->GetAt(i, &properties);
|
|
||||||
Q_ASSERT_SUCCEEDED(hr);
|
|
||||||
ComPtr<IVideoEncodingProperties> videoProperties;
|
|
||||||
hr = properties.As(&videoProperties);
|
|
||||||
Q_ASSERT_SUCCEEDED(hr);
|
|
||||||
UINT32 width, height;
|
|
||||||
hr = videoProperties->get_Width(&width);
|
|
||||||
Q_ASSERT_SUCCEEDED(hr);
|
|
||||||
hr = videoProperties->get_Height(&height);
|
|
||||||
Q_ASSERT_SUCCEEDED(hr);
|
|
||||||
if (d->captureMode != QCamera::CaptureStillImage && int(width * height) > pixelCount) {
|
|
||||||
d->size = QSize(width, height);// Choose the Highest-quality format
|
|
||||||
pixelCount = d->size.width() * d->size.height();
|
|
||||||
}
|
|
||||||
videoEncodingPropertiesList.insert(QSize(width, height), videoProperties);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (videoEncodingPropertiesList.isEmpty()) {
|
|
||||||
hr = MF_E_INVALID_FORMAT;
|
|
||||||
RETURN_HR_IF_FAILED("Failed to find a suitable video format");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (d->captureMode == QCamera::CaptureStillImage) {
|
|
||||||
d->imageEncoderControl->setSupportedResolutionsList(videoEncodingPropertiesList.keys());
|
|
||||||
d->size = d->imageEncoderControl->imageSettings().resolution();
|
|
||||||
}
|
}
|
||||||
|
qSort(filtered.begin(),
|
||||||
|
filtered.end(),
|
||||||
|
[](QSize size1, QSize size2) { return size1.width() * size1.height() < size2.width() * size2.height(); });
|
||||||
|
|
||||||
|
const QSize &viewfinderResolution = filtered.first();
|
||||||
|
const quint32 viewfinderResolutionIndex = previewResolutions.indexOf(viewfinderResolution);
|
||||||
hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Media_MediaProperties_MediaEncodingProfile).Get(),
|
hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Media_MediaProperties_MediaEncodingProfile).Get(),
|
||||||
&d->encodingProfile);
|
&d->encodingProfile);
|
||||||
Q_ASSERT_SUCCEEDED(hr);
|
Q_ASSERT_SUCCEEDED(hr);
|
||||||
|
ComPtr<IMediaEncodingProperties> previewProperties;
|
||||||
const ComPtr<IVideoEncodingProperties> videoEncodingProperties = videoEncodingPropertiesList[d->size];
|
hr = previewPropertiesList->GetAt(viewfinderResolutionIndex, &previewProperties);
|
||||||
if (!videoEncodingProperties) {
|
|
||||||
hr = MF_E_INVALID_FORMAT;
|
|
||||||
RETURN_HR_IF_FAILED("Failed to find a suitable video format properties");
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = d->encodingProfile->put_Video(videoEncodingProperties.Get());
|
|
||||||
Q_ASSERT_SUCCEEDED(hr);
|
Q_ASSERT_SUCCEEDED(hr);
|
||||||
|
hr = deviceController->SetMediaStreamPropertiesAsync(MediaStreamType_VideoPreview, previewProperties.Get(), &op);
|
||||||
|
Q_ASSERT_SUCCEEDED(hr);
|
||||||
|
hr = QWinRTFunctions::await(op);
|
||||||
|
Q_ASSERT_SUCCEEDED(hr);
|
||||||
|
ComPtr<IVideoEncodingProperties> videoPreviewProperties;
|
||||||
|
hr = previewProperties.As(&videoPreviewProperties);
|
||||||
|
Q_ASSERT_SUCCEEDED(hr);
|
||||||
|
hr = d->encodingProfile->put_Video(videoPreviewProperties.Get());
|
||||||
|
Q_ASSERT_SUCCEEDED(hr);
|
||||||
|
|
||||||
if (d->videoRenderer)
|
if (d->videoRenderer)
|
||||||
d->videoRenderer->setSize(d->size);
|
d->videoRenderer->setSize(viewfinderResolution);
|
||||||
|
|
||||||
if (SUCCEEDED(hr) && d->state != QCamera::LoadedState) {
|
if (SUCCEEDED(hr) && d->state != QCamera::LoadedState) {
|
||||||
d->state = QCamera::LoadedState;
|
d->state = QCamera::LoadedState;
|
||||||
|
|||||||
@@ -99,23 +99,23 @@ void QWinRTImageEncoderControl::setSupportedResolutionsList(const QList<QSize> r
|
|||||||
void QWinRTImageEncoderControl::applySettings()
|
void QWinRTImageEncoderControl::applySettings()
|
||||||
{
|
{
|
||||||
Q_D(QWinRTImageEncoderControl);
|
Q_D(QWinRTImageEncoderControl);
|
||||||
d->imageEncoderSetting.setCodec(QStringLiteral("jpeg"));
|
if (d->imageEncoderSetting.codec().isEmpty())
|
||||||
|
d->imageEncoderSetting.setCodec(QStringLiteral("jpeg"));
|
||||||
|
|
||||||
QSize requestResolution = d->imageEncoderSetting.resolution();
|
QSize requestResolution = d->imageEncoderSetting.resolution();
|
||||||
if (d->supportedResolutions.isEmpty() || d->supportedResolutions.contains(requestResolution))
|
if (d->supportedResolutions.isEmpty() || d->supportedResolutions.contains(requestResolution))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!requestResolution.isValid())
|
|
||||||
requestResolution = QSize(0, 0);// Find the minimal available resolution
|
|
||||||
|
|
||||||
// Find closest resolution from the list
|
// Find closest resolution from the list
|
||||||
const int pixelCount = requestResolution.width() * requestResolution.height();
|
const int pixelCount = requestResolution.width() * requestResolution.height();
|
||||||
int minPixelCountGap = -1;
|
int minimumGap = std::numeric_limits<int>::max();
|
||||||
for (int i = 0; i < d->supportedResolutions.size(); ++i) {
|
foreach (const QSize &size, d->supportedResolutions) {
|
||||||
int gap = qAbs(pixelCount - d->supportedResolutions.at(i).width() * d->supportedResolutions.at(i).height());
|
int gap = qAbs(pixelCount - size.width() * size.height());
|
||||||
if (gap < minPixelCountGap || minPixelCountGap < 0) {
|
if (gap < minimumGap) {
|
||||||
minPixelCountGap = gap;
|
minimumGap = gap;
|
||||||
requestResolution = d->supportedResolutions.at(i);
|
requestResolution = size;
|
||||||
|
if (gap == 0)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
d->imageEncoderSetting.setResolution(requestResolution);
|
d->imageEncoderSetting.setResolution(requestResolution);
|
||||||
|
|||||||
Reference in New Issue
Block a user