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_POSITION_MIN 0.0f
|
||||
#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) {
|
||||
return key.width() * key.height();
|
||||
HRESULT getMediaStreamResolutions(IMediaDeviceController *device,
|
||||
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;
|
||||
@@ -549,7 +575,6 @@ public:
|
||||
ComPtr<IFocusControl> focusControl;
|
||||
ComPtr<IRegionsOfInterestControl> regionsOfInterestControl;
|
||||
|
||||
QSize size;
|
||||
QPointer<QWinRTCameraVideoRendererControl> videoRenderer;
|
||||
QPointer<QWinRTVideoDeviceSelectorControl> videoDeviceSelector;
|
||||
QPointer<QWinRTCameraImageCaptureControl> imageCaptureControl;
|
||||
@@ -568,7 +593,7 @@ QWinRTCameraControl::QWinRTCameraControl(QObject *parent)
|
||||
d->captureMode = QCamera::CaptureStillImage;
|
||||
d->captureFailedCookie.value = 0;
|
||||
d->recordLimitationCookie.value = 0;
|
||||
d->videoRenderer = new QWinRTCameraVideoRendererControl(d->size, this);
|
||||
d->videoRenderer = new QWinRTCameraVideoRendererControl(QSize(), this);
|
||||
connect(d->videoRenderer, &QWinRTCameraVideoRendererControl::bufferRequested,
|
||||
this, &QWinRTCameraControl::onBufferRequested);
|
||||
d->videoDeviceSelector = new QWinRTVideoDeviceSelectorControl(this);
|
||||
@@ -888,72 +913,72 @@ HRESULT QWinRTCameraControl::initialize()
|
||||
ComPtr<IMediaDeviceController> deviceController;
|
||||
hr = videoDeviceController.As(&deviceController);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
ComPtr<IVectorView<IMediaEncodingProperties *>> encodingPropertiesList;
|
||||
MediaStreamType mediaStreamType;
|
||||
switch (d->captureMode) {
|
||||
default:
|
||||
case QCamera::CaptureViewfinder:
|
||||
mediaStreamType = MediaStreamType_VideoPreview;
|
||||
break;
|
||||
case QCamera::CaptureStillImage:
|
||||
mediaStreamType = MediaStreamType_Photo;
|
||||
break;
|
||||
case QCamera::CaptureVideo:
|
||||
mediaStreamType = MediaStreamType_VideoRecord;
|
||||
break;
|
||||
}
|
||||
hr = deviceController->GetAvailableMediaStreamProperties(mediaStreamType, &encodingPropertiesList);
|
||||
|
||||
// Get preview stream properties.
|
||||
ComPtr<IVectorView<IMediaEncodingProperties *>> previewPropertiesList;
|
||||
QVector<QSize> previewResolutions;
|
||||
hr = getMediaStreamResolutions(deviceController.Get(),
|
||||
MediaStreamType_VideoPreview,
|
||||
&previewPropertiesList,
|
||||
&previewResolutions);
|
||||
RETURN_HR_IF_FAILED("Failed to find a suitable video format");
|
||||
|
||||
MediaStreamType mediaStreamType =
|
||||
d->captureMode == QCamera::CaptureVideo ? MediaStreamType_VideoRecord : MediaStreamType_Photo;
|
||||
|
||||
// Get capture stream properties.
|
||||
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);
|
||||
|
||||
d->size = QSize();
|
||||
quint32 encodingPropertiesListSize;
|
||||
hr = encodingPropertiesList->get_Size(&encodingPropertiesListSize);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
QHash<QSize, ComPtr<IVideoEncodingProperties>> videoEncodingPropertiesList;
|
||||
int pixelCount = 0;
|
||||
for (quint32 i = 0; i < encodingPropertiesListSize; ++i) {
|
||||
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();
|
||||
// Set preview resolution.
|
||||
QVector<QSize> filtered;
|
||||
const float captureAspectRatio = float(captureResolution.width()) / captureResolution.height();
|
||||
foreach (const QSize &resolution, previewResolutions) {
|
||||
const float aspectRatio = float(resolution.width()) / resolution.height();
|
||||
if (qAbs(aspectRatio - captureAspectRatio) <= ASPECTRATIO_EPSILON)
|
||||
filtered.append(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(),
|
||||
&d->encodingProfile);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
|
||||
const ComPtr<IVideoEncodingProperties> videoEncodingProperties = videoEncodingPropertiesList[d->size];
|
||||
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());
|
||||
ComPtr<IMediaEncodingProperties> previewProperties;
|
||||
hr = previewPropertiesList->GetAt(viewfinderResolutionIndex, &previewProperties);
|
||||
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)
|
||||
d->videoRenderer->setSize(d->size);
|
||||
d->videoRenderer->setSize(viewfinderResolution);
|
||||
|
||||
if (SUCCEEDED(hr) && d->state != QCamera::LoadedState) {
|
||||
d->state = QCamera::LoadedState;
|
||||
|
||||
@@ -99,23 +99,23 @@ void QWinRTImageEncoderControl::setSupportedResolutionsList(const QList<QSize> r
|
||||
void QWinRTImageEncoderControl::applySettings()
|
||||
{
|
||||
Q_D(QWinRTImageEncoderControl);
|
||||
d->imageEncoderSetting.setCodec(QStringLiteral("jpeg"));
|
||||
if (d->imageEncoderSetting.codec().isEmpty())
|
||||
d->imageEncoderSetting.setCodec(QStringLiteral("jpeg"));
|
||||
|
||||
QSize requestResolution = d->imageEncoderSetting.resolution();
|
||||
if (d->supportedResolutions.isEmpty() || d->supportedResolutions.contains(requestResolution))
|
||||
return;
|
||||
|
||||
if (!requestResolution.isValid())
|
||||
requestResolution = QSize(0, 0);// Find the minimal available resolution
|
||||
|
||||
// Find closest resolution from the list
|
||||
const int pixelCount = requestResolution.width() * requestResolution.height();
|
||||
int minPixelCountGap = -1;
|
||||
for (int i = 0; i < d->supportedResolutions.size(); ++i) {
|
||||
int gap = qAbs(pixelCount - d->supportedResolutions.at(i).width() * d->supportedResolutions.at(i).height());
|
||||
if (gap < minPixelCountGap || minPixelCountGap < 0) {
|
||||
minPixelCountGap = gap;
|
||||
requestResolution = d->supportedResolutions.at(i);
|
||||
int minimumGap = std::numeric_limits<int>::max();
|
||||
foreach (const QSize &size, d->supportedResolutions) {
|
||||
int gap = qAbs(pixelCount - size.width() * size.height());
|
||||
if (gap < minimumGap) {
|
||||
minimumGap = gap;
|
||||
requestResolution = size;
|
||||
if (gap == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
d->imageEncoderSetting.setResolution(requestResolution);
|
||||
|
||||
Reference in New Issue
Block a user