DirectShow: implemented QCameraViewfinderSettingsControl2.

Change-Id: I42ed49676e2fbc7207d8fe4579ad1fc0d62df138
Reviewed-by: Christian Stromme <christian.stromme@theqtcompany.com>
This commit is contained in:
Yoann Lopes
2014-12-19 20:08:58 +01:00
parent 4e07ff99f6
commit 5a0a3791a1
7 changed files with 283 additions and 54 deletions

View File

@@ -13,7 +13,8 @@ HEADERS += \
$$PWD/dsvideodevicecontrol.h \
$$PWD/dsimagecapturecontrol.h \
$$PWD/dscamerasession.h \
$$PWD/directshowglobal.h
$$PWD/directshowglobal.h \
$$PWD/dscameraviewfindersettingscontrol.h
SOURCES += \
$$PWD/dscameraservice.cpp \
@@ -21,7 +22,8 @@ SOURCES += \
$$PWD/dsvideorenderer.cpp \
$$PWD/dsvideodevicecontrol.cpp \
$$PWD/dsimagecapturecontrol.cpp \
$$PWD/dscamerasession.cpp
$$PWD/dscamerasession.cpp \
$$PWD/dscameraviewfindersettingscontrol.cpp
*-msvc*:INCLUDEPATH += $$(DXSDK_DIR)/include
LIBS += -lstrmiids -ldmoguids -luuid -lmsdmo -lole32 -loleaut32

View File

@@ -40,6 +40,7 @@
#include "dsvideorenderer.h"
#include "dsvideodevicecontrol.h"
#include "dsimagecapturecontrol.h"
#include "dscameraviewfindersettingscontrol.h"
QT_BEGIN_NAMESPACE
@@ -51,11 +52,13 @@ DSCameraService::DSCameraService(QObject *parent):
m_control = new DSCameraControl(m_session);
m_videoDevice = new DSVideoDeviceControl(m_session);
m_imageCapture = new DSImageCaptureControl(m_session);
m_viewfinderSettings = new DSCameraViewfinderSettingsControl(m_session);
}
DSCameraService::~DSCameraService()
{
delete m_control;
delete m_viewfinderSettings;
delete m_videoDevice;
delete m_videoRenderer;
delete m_imageCapture;
@@ -80,6 +83,9 @@ QMediaControl* DSCameraService::requestControl(const char *name)
if (qstrcmp(name,QVideoDeviceSelectorControl_iid) == 0)
return m_videoDevice;
if (qstrcmp(name, QCameraViewfinderSettingsControl2_iid) == 0)
return m_viewfinderSettings;
return 0;
}

View File

@@ -45,7 +45,7 @@ class DSCameraSession;
class DSVideoOutputControl;
class DSVideoDeviceControl;
class DSImageCaptureControl;
class DSCameraViewfinderSettingsControl;
class DSCameraService : public QMediaService
{
@@ -65,6 +65,7 @@ private:
DSVideoDeviceControl *m_videoDevice;
QMediaControl *m_videoRenderer;
DSImageCaptureControl *m_imageCapture;
DSCameraViewfinderSettingsControl *m_viewfinderSettings;
};
QT_END_NAMESPACE

View File

@@ -78,9 +78,6 @@ void _FreeMediaType(AM_MEDIA_TYPE& mt)
}
} // end namespace
typedef QList<QSize> SizeList;
Q_GLOBAL_STATIC(SizeList, commonPreviewResolutions)
static HRESULT getPin(IBaseFilter *filter, PIN_DIRECTION pinDir, IPin **pin);
@@ -148,6 +145,42 @@ private:
DSCameraSession *m_session;
};
QVideoFrame::PixelFormat pixelFormatFromMediaSubtype(GUID uid)
{
if (uid == MEDIASUBTYPE_ARGB32)
return QVideoFrame::Format_ARGB32;
else if (uid == MEDIASUBTYPE_RGB32)
return QVideoFrame::Format_RGB32;
else if (uid == MEDIASUBTYPE_RGB24)
return QVideoFrame::Format_RGB24;
else if (uid == MEDIASUBTYPE_RGB565)
return QVideoFrame::Format_RGB565;
else if (uid == MEDIASUBTYPE_RGB555)
return QVideoFrame::Format_RGB555;
else if (uid == MEDIASUBTYPE_AYUV)
return QVideoFrame::Format_AYUV444;
else if (uid == MEDIASUBTYPE_I420 || uid == MEDIASUBTYPE_IYUV)
return QVideoFrame::Format_YUV420P;
else if (uid == MEDIASUBTYPE_YV12)
return QVideoFrame::Format_YV12;
else if (uid == MEDIASUBTYPE_UYVY)
return QVideoFrame::Format_UYVY;
else if (uid == MEDIASUBTYPE_YUYV || uid == MEDIASUBTYPE_YUY2)
return QVideoFrame::Format_YUYV;
else if (uid == MEDIASUBTYPE_NV12)
return QVideoFrame::Format_NV12;
else if (uid == MEDIASUBTYPE_IMC1)
return QVideoFrame::Format_IMC1;
else if (uid == MEDIASUBTYPE_IMC2)
return QVideoFrame::Format_IMC2;
else if (uid == MEDIASUBTYPE_IMC3)
return QVideoFrame::Format_IMC3;
else if (uid == MEDIASUBTYPE_IMC4)
return QVideoFrame::Format_IMC4;
else
return QVideoFrame::Format_Invalid;
}
DSCameraSession::DSCameraSession(QObject *parent)
: QObject(parent)
@@ -167,7 +200,7 @@ DSCameraSession::DSCameraSession(QObject *parent)
, m_currentImageId(-1)
, m_status(QCamera::UnloadedStatus)
{
ZeroMemory(&m_sourcePreferredFormat, sizeof(m_sourcePreferredFormat));
ZeroMemory(&m_sourceFormat, sizeof(m_sourceFormat));
connect(this, SIGNAL(statusChanged(QCamera::Status)),
this, SLOT(updateReadyForCapture()));
@@ -188,6 +221,16 @@ void DSCameraSession::setDevice(const QString &device)
m_sourceDeviceName = device;
}
QCameraViewfinderSettings DSCameraSession::viewfinderSettings() const
{
return m_status == QCamera::ActiveStatus ? m_actualViewfinderSettings : m_viewfinderSettings;
}
void DSCameraSession::setViewfinderSettings(const QCameraViewfinderSettings &settings)
{
m_viewfinderSettings = settings;
}
bool DSCameraSession::load()
{
unload();
@@ -214,9 +257,10 @@ bool DSCameraSession::unload()
setStatus(QCamera::UnloadingStatus);
m_needsHorizontalMirroring = false;
m_sourcePreferredResolution = QSize();
_FreeMediaType(m_sourcePreferredFormat);
ZeroMemory(&m_sourcePreferredFormat, sizeof(m_sourcePreferredFormat));
m_supportedViewfinderSettings.clear();
Q_FOREACH (AM_MEDIA_TYPE f, m_supportedFormats)
_FreeMediaType(f);
m_supportedFormats.clear();
SAFE_RELEASE(m_sourceFilter);
SAFE_RELEASE(m_previewSampleGrabber);
SAFE_RELEASE(m_previewFilter);
@@ -302,6 +346,9 @@ bool DSCameraSession::stopPreview()
disconnectGraph();
_FreeMediaType(m_sourceFormat);
ZeroMemory(&m_sourceFormat, sizeof(m_sourceFormat));
m_previewStarted = false;
setStatus(QCamera::LoadedStatus);
return true;
@@ -581,9 +628,6 @@ bool DSCameraSession::createFilterGraph()
failed:
m_needsHorizontalMirroring = false;
m_sourcePreferredResolution = QSize();
_FreeMediaType(m_sourcePreferredFormat);
ZeroMemory(&m_sourcePreferredFormat, sizeof(m_sourcePreferredFormat));
SAFE_RELEASE(m_sourceFilter);
SAFE_RELEASE(m_previewSampleGrabber);
SAFE_RELEASE(m_previewFilter);
@@ -596,6 +640,34 @@ failed:
bool DSCameraSession::configurePreviewFormat()
{
// Resolve viewfinder settings
int settingsIndex = 0;
QCameraViewfinderSettings resolvedViewfinderSettings;
Q_FOREACH (const QCameraViewfinderSettings &s, m_supportedViewfinderSettings) {
if ((m_viewfinderSettings.resolution().isEmpty() || m_viewfinderSettings.resolution() == s.resolution())
&& (qFuzzyIsNull(m_viewfinderSettings.minimumFrameRate()) || qFuzzyCompare((float)m_viewfinderSettings.minimumFrameRate(), (float)s.minimumFrameRate()))
&& (qFuzzyIsNull(m_viewfinderSettings.maximumFrameRate()) || qFuzzyCompare((float)m_viewfinderSettings.maximumFrameRate(), (float)s.maximumFrameRate()))
&& (m_viewfinderSettings.pixelFormat() == QVideoFrame::Format_Invalid || m_viewfinderSettings.pixelFormat() == s.pixelFormat())) {
resolvedViewfinderSettings = s;
break;
}
++settingsIndex;
}
if (resolvedViewfinderSettings.isNull()) {
qWarning("Invalid viewfinder settings");
return false;
}
m_actualViewfinderSettings = resolvedViewfinderSettings;
_CopyMediaType(&m_sourceFormat, &m_supportedFormats[settingsIndex]);
// Set frame rate.
// We don't care about the minimumFrameRate, DirectShow only allows to set an
// average frame rate, so set that to the maximumFrameRate.
VIDEOINFOHEADER *videoInfo = reinterpret_cast<VIDEOINFOHEADER*>(m_sourceFormat.pbFormat);
videoInfo->AvgTimePerFrame = 10000000 / resolvedViewfinderSettings.maximumFrameRate();
// We only support RGB32, if the capture source doesn't support
// that format, the graph builder will automatically insert a
// converter.
@@ -607,7 +679,7 @@ bool DSCameraSession::configurePreviewFormat()
}
m_previewPixelFormat = QVideoFrame::Format_RGB32;
m_previewSize = m_sourcePreferredResolution;
m_previewSize = resolvedViewfinderSettings.resolution();
m_previewSurfaceFormat = QVideoSurfaceFormat(m_previewSize,
m_previewPixelFormat,
QAbstractVideoBuffer::NoHandle);
@@ -624,7 +696,7 @@ bool DSCameraSession::configurePreviewFormat()
return false;
}
hr = pConfig->SetFormat(&m_sourcePreferredFormat);
hr = pConfig->SetFormat(&m_sourceFormat);
pConfig->Release();
@@ -716,6 +788,11 @@ void DSCameraSession::disconnectGraph()
m_filterGraph->RemoveFilter(m_sourceFilter);
}
static bool qt_frameRateRangeGreaterThan(const QCamera::FrameRateRange &r1, const QCamera::FrameRateRange &r2)
{
return r1.second > r2.second;
}
void DSCameraSession::updateSourceCapabilities()
{
HRESULT hr;
@@ -724,10 +801,11 @@ void DSCameraSession::updateSourceCapabilities()
VIDEO_STREAM_CONFIG_CAPS scc;
IAMStreamConfig* pConfig = 0;
m_supportedViewfinderSettings.clear();
m_needsHorizontalMirroring = false;
m_sourcePreferredResolution = QSize();
_FreeMediaType(m_sourcePreferredFormat);
ZeroMemory(&m_sourcePreferredFormat, sizeof(m_sourcePreferredFormat));
Q_FOREACH (AM_MEDIA_TYPE f, m_supportedFormats)
_FreeMediaType(f);
m_supportedFormats.clear();
IAMVideoControl *pVideoControl = 0;
hr = m_graphBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,
@@ -774,53 +852,68 @@ void DSCameraSession::updateSourceCapabilities()
return;
}
// Use preferred pixel format (first in the list)
// Then, pick the highest available resolution among the typical resolutions
// used for camera preview.
if (commonPreviewResolutions->isEmpty())
populateCommonResolutions();
long maxPixelCount = 0;
for (int iIndex = 0; iIndex < iCount; ++iIndex) {
hr = pConfig->GetStreamCaps(iIndex, &pmt, reinterpret_cast<BYTE*>(&scc));
if (hr == S_OK) {
if ((pmt->majortype == MEDIATYPE_Video) &&
(pmt->formattype == FORMAT_VideoInfo) &&
(!m_sourcePreferredFormat.cbFormat ||
m_sourcePreferredFormat.subtype == pmt->subtype)) {
QVideoFrame::PixelFormat pixelFormat = pixelFormatFromMediaSubtype(pmt->subtype);
if (pmt->majortype == MEDIATYPE_Video
&& pmt->formattype == FORMAT_VideoInfo
&& pixelFormat != QVideoFrame::Format_Invalid) {
pvi = reinterpret_cast<VIDEOINFOHEADER*>(pmt->pbFormat);
QSize resolution(pvi->bmiHeader.biWidth, pvi->bmiHeader.biHeight);
long pixelCount = resolution.width() * resolution.height();
if (!m_sourcePreferredFormat.cbFormat ||
(pixelCount > maxPixelCount && commonPreviewResolutions->contains(resolution))) {
_FreeMediaType(m_sourcePreferredFormat);
_CopyMediaType(&m_sourcePreferredFormat, pmt);
m_sourcePreferredResolution = resolution;
maxPixelCount = pixelCount;
QList<QCamera::FrameRateRange> frameRateRanges;
if (pVideoControl) {
IPin *pPin = 0;
hr = getPin(m_sourceFilter, PINDIR_OUTPUT, &pPin);
if (FAILED(hr)) {
qWarning() << "Failed to get the pin for the video control";
} else {
long listSize = 0;
LONGLONG *frameRates = 0;
SIZE size = { resolution.width(), resolution.height() };
if (SUCCEEDED(pVideoControl->GetFrameRateList(pPin, iIndex, size,
&listSize, &frameRates))) {
for (long i = 0; i < listSize; ++i) {
qreal fr = qreal(10000000) / frameRates[i];
frameRateRanges.append(QCamera::FrameRateRange(fr, fr));
}
// Make sure higher frame rates come first
std::sort(frameRateRanges.begin(), frameRateRanges.end(), qt_frameRateRangeGreaterThan);
}
pPin->Release();
}
}
if (frameRateRanges.isEmpty()) {
frameRateRanges.append(QCamera::FrameRateRange(qreal(10000000) / scc.MaxFrameInterval,
qreal(10000000) / scc.MinFrameInterval));
}
Q_FOREACH (const QCamera::FrameRateRange &frameRateRange, frameRateRanges) {
QCameraViewfinderSettings settings;
settings.setResolution(resolution);
settings.setMinimumFrameRate(frameRateRange.first);
settings.setMaximumFrameRate(frameRateRange.second);
settings.setPixelFormat(pixelFormat);
m_supportedViewfinderSettings.append(settings);
AM_MEDIA_TYPE format;
_CopyMediaType(&format, pmt);
m_supportedFormats.append(format);
}
}
_FreeMediaType(*pmt);
}
}
pConfig->Release();
if (!m_sourcePreferredResolution.isValid())
m_sourcePreferredResolution = QSize(640, 480);
}
void DSCameraSession::populateCommonResolutions()
{
commonPreviewResolutions->append(QSize(1920, 1080)); // 1080p
commonPreviewResolutions->append(QSize(1280, 720)); // 720p
commonPreviewResolutions->append(QSize(1024, 576)); // WSVGA
commonPreviewResolutions->append(QSize(720, 480)); // 480p (16:9)
commonPreviewResolutions->append(QSize(640, 480)); // 480p (4:3)
commonPreviewResolutions->append(QSize(352, 288)); // CIF
commonPreviewResolutions->append(QSize(320, 240)); // QVGA
}
HRESULT getPin(IBaseFilter *pFilter, PIN_DIRECTION PinDir, IPin **ppPin)

View File

@@ -91,6 +91,12 @@ public:
void setSurface(QAbstractVideoSurface* surface);
QCameraViewfinderSettings viewfinderSettings() const;
void setViewfinderSettings(const QCameraViewfinderSettings &settings);
QList<QCameraViewfinderSettings> supportedViewfinderSettings() const
{ return m_supportedViewfinderSettings; }
Q_SIGNALS:
void statusChanged(QCamera::Status);
void imageExposed(int id);
@@ -105,7 +111,6 @@ private Q_SLOTS:
private:
void setStatus(QCamera::Status status);
void populateCommonResolutions();
void onFrameAvailable(const char *frameData, long len);
void saveCapturedImage(int id, const QImage &image, const QString &path);
@@ -126,9 +131,10 @@ private:
// Source (camera)
QString m_sourceDeviceName;
IBaseFilter* m_sourceFilter;
AM_MEDIA_TYPE m_sourcePreferredFormat;
QSize m_sourcePreferredResolution;
bool m_needsHorizontalMirroring;
QList<AM_MEDIA_TYPE> m_supportedFormats;
QList<QCameraViewfinderSettings> m_supportedViewfinderSettings;
AM_MEDIA_TYPE m_sourceFormat;
// Preview
IBaseFilter *m_previewFilter;
@@ -140,6 +146,8 @@ private:
QVideoSurfaceFormat m_previewSurfaceFormat;
QVideoFrame::PixelFormat m_previewPixelFormat;
QSize m_previewSize;
QCameraViewfinderSettings m_viewfinderSettings;
QCameraViewfinderSettings m_actualViewfinderSettings;
// Image capture
QString m_imageCaptureFileName;

View File

@@ -0,0 +1,60 @@
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** As a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "dscameraviewfindersettingscontrol.h"
#include "dscamerasession.h"
QT_BEGIN_NAMESPACE
DSCameraViewfinderSettingsControl::DSCameraViewfinderSettingsControl(DSCameraSession *session)
: QCameraViewfinderSettingsControl2(session)
, m_session(session)
{
}
QList<QCameraViewfinderSettings> DSCameraViewfinderSettingsControl::supportedViewfinderSettings() const
{
return m_session->supportedViewfinderSettings();
}
QCameraViewfinderSettings DSCameraViewfinderSettingsControl::viewfinderSettings() const
{
return m_session->viewfinderSettings();
}
void DSCameraViewfinderSettingsControl::setViewfinderSettings(const QCameraViewfinderSettings &settings)
{
m_session->setViewfinderSettings(settings);
}
QT_END_NAMESPACE

View File

@@ -0,0 +1,59 @@
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** As a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef DSCAMERAVIEWFINDERSETTINGSCONTROL_H
#define DSCAMERAVIEWFINDERSETTINGSCONTROL_H
#include <qcameraviewfindersettingscontrol.h>
QT_BEGIN_NAMESPACE
class DSCameraSession;
class DSCameraViewfinderSettingsControl : public QCameraViewfinderSettingsControl2
{
public:
DSCameraViewfinderSettingsControl(DSCameraSession *session);
QList<QCameraViewfinderSettings> supportedViewfinderSettings() const;
QCameraViewfinderSettings viewfinderSettings() const;
void setViewfinderSettings(const QCameraViewfinderSettings &settings);
private:
DSCameraSession *m_session;
};
QT_END_NAMESPACE
#endif // DSCAMERAVIEWFINDERSETTINGSCONTROL_H