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:
Samuel Nevala
2015-08-13 14:37:36 +03:00
parent 23acd9f01d
commit 1b957a05fe
2 changed files with 96 additions and 71 deletions

View File

@@ -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;

View File

@@ -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);