diff options
Diffstat (limited to 'src/plugins/directshow')
27 files changed, 1211 insertions, 391 deletions
diff --git a/src/plugins/directshow/camera/directshowcameraglobal.h b/src/plugins/directshow/camera/directshowcameraglobal.h index 75112a090..8df387b4a 100644 --- a/src/plugins/directshow/camera/directshowcameraglobal.h +++ b/src/plugins/directshow/camera/directshowcameraglobal.h @@ -44,9 +44,6 @@ #include <dshow.h> -DEFINE_GUID(MEDIASUBTYPE_I420, - 0x30323449,0x0000,0x0010,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71); - extern const GUID MEDIASUBTYPE_RGB24; extern const GUID MEDIASUBTYPE_RGB32; extern const GUID MEDIASUBTYPE_YUY2; diff --git a/src/plugins/directshow/camera/dscamerasession.cpp b/src/plugins/directshow/camera/dscamerasession.cpp index 5587b479c..40e54a94d 100644 --- a/src/plugins/directshow/camera/dscamerasession.cpp +++ b/src/plugins/directshow/camera/dscamerasession.cpp @@ -47,148 +47,13 @@ #include "dscamerasession.h" #include "dsvideorenderer.h" +#include "directshowsamplegrabber.h" #include "directshowcameraglobal.h" +#include "directshowmediatype.h" +#include "directshowutils.h" QT_BEGIN_NAMESPACE - -namespace { -// DirectShow helper implementation -void _CopyMediaType(AM_MEDIA_TYPE *pmtTarget, const AM_MEDIA_TYPE *pmtSource) -{ - *pmtTarget = *pmtSource; - if (pmtTarget->cbFormat != 0) { - pmtTarget->pbFormat = reinterpret_cast<BYTE *>(CoTaskMemAlloc(pmtTarget->cbFormat)); - if (pmtTarget->pbFormat) - memcpy(pmtTarget->pbFormat, pmtSource->pbFormat, pmtTarget->cbFormat); - } - if (pmtTarget->pUnk != NULL) { - // pUnk should not be used. - pmtTarget->pUnk->AddRef(); - } -} - -void _FreeMediaType(AM_MEDIA_TYPE& mt) -{ - if (mt.cbFormat != 0) { - CoTaskMemFree((PVOID)mt.pbFormat); - mt.cbFormat = 0; - mt.pbFormat = NULL; - } - if (mt.pUnk != NULL) { - // pUnk should not be used. - mt.pUnk->Release(); - mt.pUnk = NULL; - } -} -} // end namespace - -static HRESULT getPin(IBaseFilter *filter, PIN_DIRECTION pinDir, IPin **pin); - - -class SampleGrabberCallbackPrivate : public ISampleGrabberCB -{ -public: - explicit SampleGrabberCallbackPrivate(DSCameraSession *session) - : m_ref(1) - , m_session(session) - { } - - virtual ~SampleGrabberCallbackPrivate() { } - - STDMETHODIMP_(ULONG) AddRef() - { - return InterlockedIncrement(&m_ref); - } - - STDMETHODIMP_(ULONG) Release() - { - ULONG ref = InterlockedDecrement(&m_ref); - if (ref == 0) - delete this; - return ref; - } - - STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) - { - if (NULL == ppvObject) - return E_POINTER; - if (riid == IID_IUnknown /*__uuidof(IUnknown) */ ) { - *ppvObject = static_cast<IUnknown*>(this); - return S_OK; - } - if (riid == IID_ISampleGrabberCB /*__uuidof(ISampleGrabberCB)*/ ) { - *ppvObject = static_cast<ISampleGrabberCB*>(this); - return S_OK; - } - return E_NOTIMPL; - } - - STDMETHODIMP SampleCB(double Time, IMediaSample *pSample) - { - Q_UNUSED(Time) - Q_UNUSED(pSample) - return E_NOTIMPL; - } - - STDMETHODIMP BufferCB(double time, BYTE *pBuffer, long bufferLen) - { - // We display frames as they arrive, the presentation time is - // irrelevant - Q_UNUSED(time); - - if (m_session) { - m_session->onFrameAvailable(reinterpret_cast<const char *>(pBuffer), - bufferLen); - } - - return S_OK; - } - -private: - ULONG m_ref; - 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_MJPG) - return QVideoFrame::Format_Jpeg; - 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) , m_graphBuilder(Q_NULLPTR) @@ -196,7 +61,6 @@ DSCameraSession::DSCameraSession(QObject *parent) , m_sourceDeviceName(QLatin1String("default")) , m_sourceFilter(Q_NULLPTR) , m_needsHorizontalMirroring(false) - , m_previewFilter(Q_NULLPTR) , m_previewSampleGrabber(Q_NULLPTR) , m_nullRendererFilter(Q_NULLPTR) , m_previewStarted(false) @@ -207,8 +71,6 @@ DSCameraSession::DSCameraSession(QObject *parent) , m_currentImageId(-1) , m_status(QCamera::UnloadedStatus) { - ZeroMemory(&m_sourceFormat, sizeof(m_sourceFormat)); - connect(this, SIGNAL(statusChanged(QCamera::Status)), this, SLOT(updateReadyForCapture())); } @@ -497,14 +359,13 @@ bool DSCameraSession::unload() setStatus(QCamera::UnloadingStatus); + m_previewSampleGrabber->deleteLater(); + m_previewSampleGrabber = nullptr; + m_needsHorizontalMirroring = false; m_supportedViewfinderSettings.clear(); - for (AM_MEDIA_TYPE f : qAsConst(m_supportedFormats)) - _FreeMediaType(f); m_supportedFormats.clear(); SAFE_RELEASE(m_sourceFilter); - SAFE_RELEASE(m_previewSampleGrabber); - SAFE_RELEASE(m_previewFilter); SAFE_RELEASE(m_nullRendererFilter); SAFE_RELEASE(m_filterGraph); SAFE_RELEASE(m_graphBuilder); @@ -571,6 +432,9 @@ bool DSCameraSession::stopPreview() setStatus(QCamera::StoppingStatus); + if (m_previewSampleGrabber) + m_previewSampleGrabber->stop(); + IMediaControl* pControl = 0; HRESULT hr = m_filterGraph->QueryInterface(IID_IMediaControl, (void**)&pControl); if (FAILED(hr)) { @@ -587,8 +451,7 @@ bool DSCameraSession::stopPreview() disconnectGraph(); - _FreeMediaType(m_sourceFormat); - ZeroMemory(&m_sourceFormat, sizeof(m_sourceFormat)); + m_sourceFormat.clear(); m_previewStarted = false; setStatus(QCamera::LoadedStatus); @@ -646,12 +509,12 @@ int DSCameraSession::captureImage(const QString &fileName) return m_imageIdCounter; } -void DSCameraSession::onFrameAvailable(const char *frameData, long len) +void DSCameraSession::onFrameAvailable(double time, quint8 *buffer, long len) { // !!! Not called on the main thread - + Q_UNUSED(time); // Deep copy, the data might be modified or freed after the callback returns - QByteArray data(frameData, len); + QByteArray data(reinterpret_cast<const char *>(buffer), len); m_presentMutex.lock(); @@ -688,7 +551,7 @@ void DSCameraSession::presentFrame() m_presentMutex.unlock(); QImage captureImage; - int captureId; + const int captureId = m_currentImageId; m_captureMutex.lock(); @@ -705,8 +568,6 @@ void DSCameraSession::presentFrame() m_capturedFrame.unmap(); - captureId = m_currentImageId; - QtConcurrent::run(this, &DSCameraSession::saveCapturedImage, m_currentImageId, captureImage, m_imageCaptureFileName); @@ -737,8 +598,6 @@ void DSCameraSession::saveCapturedImage(int id, const QImage &image, const QStri bool DSCameraSession::createFilterGraph() { // Previously containered in <qedit.h>. - static const IID iID_ISampleGrabber = { 0x6B652FFF, 0x11FE, 0x4fce, { 0x92, 0xAD, 0x02, 0x66, 0xB5, 0xD7, 0xC7, 0x8F } }; - static const CLSID cLSID_SampleGrabber = { 0xC1F400A0, 0x3F08, 0x11d3, { 0x9F, 0x0B, 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37 } }; static const CLSID cLSID_NullRenderer = { 0xC1F400A4, 0x3F08, 0x11d3, { 0x9F, 0x0B, 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37 } }; HRESULT hr; @@ -840,26 +699,12 @@ bool DSCameraSession::createFilterGraph() } // Sample grabber filter - hr = CoCreateInstance(cLSID_SampleGrabber, NULL,CLSCTX_INPROC, - IID_IBaseFilter, (void**)&m_previewFilter); - if (FAILED(hr)) { - qWarning() << "failed to create sample grabber"; - goto failed; + if (!m_previewSampleGrabber) { + m_previewSampleGrabber = new DirectShowSampleGrabber; + connect(m_previewSampleGrabber, &DirectShowSampleGrabber::bufferAvailable, + this, &DSCameraSession::onFrameAvailable); } - hr = m_previewFilter->QueryInterface(iID_ISampleGrabber, (void**)&m_previewSampleGrabber); - if (FAILED(hr)) { - qWarning() << "failed to get sample grabber"; - goto failed; - } - - { - SampleGrabberCallbackPrivate *callback = new SampleGrabberCallbackPrivate(this); - m_previewSampleGrabber->SetCallback(callback, 1); - m_previewSampleGrabber->SetOneShot(FALSE); - m_previewSampleGrabber->SetBufferSamples(FALSE); - callback->Release(); - } // Null renderer. Input connected to the sample grabber's output. Simply // discard the samples it receives. @@ -877,8 +722,6 @@ bool DSCameraSession::createFilterGraph() failed: m_needsHorizontalMirroring = false; SAFE_RELEASE(m_sourceFilter); - SAFE_RELEASE(m_previewSampleGrabber); - SAFE_RELEASE(m_previewFilter); SAFE_RELEASE(m_nullRendererFilter); SAFE_RELEASE(m_filterGraph); SAFE_RELEASE(m_graphBuilder); @@ -910,11 +753,11 @@ bool DSCameraSession::configurePreviewFormat() m_actualViewfinderSettings = resolvedViewfinderSettings; - _CopyMediaType(&m_sourceFormat, &m_supportedFormats[settingsIndex]); + 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); + VIDEOINFOHEADER *videoInfo = reinterpret_cast<VIDEOINFOHEADER*>(m_sourceFormat->pbFormat); videoInfo->AvgTimePerFrame = 10000000 / resolvedViewfinderSettings.maximumFrameRate(); // We only support RGB32, if the capture source doesn't support @@ -955,16 +798,11 @@ bool DSCameraSession::configurePreviewFormat() } // Set sample grabber format (always RGB32) - AM_MEDIA_TYPE grabberFormat; - ZeroMemory(&grabberFormat, sizeof(grabberFormat)); - grabberFormat.majortype = MEDIATYPE_Video; - grabberFormat.subtype = MEDIASUBTYPE_RGB32; - grabberFormat.formattype = FORMAT_VideoInfo; - hr = m_previewSampleGrabber->SetMediaType(&grabberFormat); - if (FAILED(hr)) { - qWarning() << "Failed to set video format on grabber"; + static const AM_MEDIA_TYPE grabberFormat { MEDIATYPE_Video, MEDIASUBTYPE_RGB32, 0, 0, 0, FORMAT_VideoInfo, nullptr, 0, nullptr}; + if (!m_previewSampleGrabber->setMediaType(&grabberFormat)) return false; - } + + m_previewSampleGrabber->start(DirectShowSampleGrabber::CallbackMethod::BufferCB); return true; } @@ -1052,8 +890,7 @@ bool DSCameraSession::connectGraph() return false; } - hr = m_filterGraph->AddFilter(m_previewFilter, L"Sample Grabber"); - if (FAILED(hr)) { + if (FAILED(m_filterGraph->AddFilter(m_previewSampleGrabber->filter(), L"Sample Grabber"))) { qWarning() << "failed to add sample grabber to graph"; return false; } @@ -1066,7 +903,7 @@ bool DSCameraSession::connectGraph() hr = m_graphBuilder->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, m_sourceFilter, - m_previewFilter, + m_previewSampleGrabber->filter(), m_nullRendererFilter); if (FAILED(hr)) { qWarning() << "Graph failed to connect filters" << hr; @@ -1078,40 +915,10 @@ bool DSCameraSession::connectGraph() void DSCameraSession::disconnectGraph() { - IPin *pPin = 0; - HRESULT hr = getPin(m_sourceFilter, PINDIR_OUTPUT, &pPin); - if (SUCCEEDED(hr)) { - m_filterGraph->Disconnect(pPin); - pPin->Release(); - pPin = NULL; - } - - hr = getPin(m_previewFilter, PINDIR_INPUT, &pPin); - if (SUCCEEDED(hr)) { - m_filterGraph->Disconnect(pPin); - pPin->Release(); - pPin = NULL; - } - - hr = getPin(m_previewFilter, PINDIR_OUTPUT, &pPin); - if (SUCCEEDED(hr)) { - m_filterGraph->Disconnect(pPin); - pPin->Release(); - pPin = NULL; - } - - hr = getPin(m_nullRendererFilter, PINDIR_INPUT, &pPin); - if (SUCCEEDED(hr)) { - m_filterGraph->Disconnect(pPin); - pPin->Release(); - pPin = NULL; - } - // To avoid increasing the memory usage every time the graph is re-connected it's // important that all filters are released; also the ones added by the "Intelligent Connect". IEnumFilters *enumFilters = NULL; - hr = m_filterGraph->EnumFilters(&enumFilters); - if (SUCCEEDED(hr)) { + if (SUCCEEDED(m_filterGraph->EnumFilters(&enumFilters))) { IBaseFilter *filter = NULL; while (enumFilters->Next(1, &filter, NULL) == S_OK) { m_filterGraph->RemoveFilter(filter); @@ -1137,8 +944,6 @@ void DSCameraSession::updateSourceCapabilities() m_supportedViewfinderSettings.clear(); m_needsHorizontalMirroring = false; - for (AM_MEDIA_TYPE f : qAsConst(m_supportedFormats)) - _FreeMediaType(f); m_supportedFormats.clear(); m_imageProcessingParametersInfos.clear(); @@ -1150,8 +955,7 @@ void DSCameraSession::updateSourceCapabilities() qWarning() << "Failed to get the video control"; } else { IPin *pPin = 0; - hr = getPin(m_sourceFilter, PINDIR_OUTPUT, &pPin); - if (FAILED(hr)) { + if (!DirectShowUtils::getPin(m_sourceFilter, PINDIR_OUTPUT, &pPin, &hr)) { qWarning() << "Failed to get the pin for the video control"; } else { long supportedModes; @@ -1190,7 +994,7 @@ void DSCameraSession::updateSourceCapabilities() for (int iIndex = 0; iIndex < iCount; ++iIndex) { hr = pConfig->GetStreamCaps(iIndex, &pmt, reinterpret_cast<BYTE*>(&scc)); if (hr == S_OK) { - QVideoFrame::PixelFormat pixelFormat = pixelFormatFromMediaSubtype(pmt->subtype); + QVideoFrame::PixelFormat pixelFormat = DirectShowMediaType::pixelFormatFromType(pmt); if (pmt->majortype == MEDIATYPE_Video && pmt->formattype == FORMAT_VideoInfo @@ -1203,8 +1007,7 @@ void DSCameraSession::updateSourceCapabilities() if (pVideoControl) { IPin *pPin = 0; - hr = getPin(m_sourceFilter, PINDIR_OUTPUT, &pPin); - if (FAILED(hr)) { + if (!DirectShowUtils::getPin(m_sourceFilter, PINDIR_OUTPUT, &pPin, &hr)) { qWarning() << "Failed to get the pin for the video control"; } else { long listSize = 0; @@ -1237,15 +1040,12 @@ void DSCameraSession::updateSourceCapabilities() settings.setPixelFormat(pixelFormat); settings.setPixelAspectRatio(1, 1); m_supportedViewfinderSettings.append(settings); - - AM_MEDIA_TYPE format; - _CopyMediaType(&format, pmt); - m_supportedFormats.append(format); + m_supportedFormats.append(DirectShowMediaType(*pmt)); } } - _FreeMediaType(*pmt); + DirectShowMediaType::deleteType(pmt); } } @@ -1254,30 +1054,4 @@ void DSCameraSession::updateSourceCapabilities() updateImageProcessingParametersInfos(); } -HRESULT getPin(IBaseFilter *pFilter, PIN_DIRECTION PinDir, IPin **ppPin) -{ - *ppPin = 0; - IEnumPins *pEnum = 0; - IPin *pPin = 0; - - HRESULT hr = pFilter->EnumPins(&pEnum); - if (FAILED(hr)) { - return hr; - } - - pEnum->Reset(); - while (pEnum->Next(1, &pPin, NULL) == S_OK) { - PIN_DIRECTION ThisPinDir; - pPin->QueryDirection(&ThisPinDir); - if (ThisPinDir == PinDir) { - pEnum->Release(); - *ppPin = pPin; - return S_OK; - } - pPin->Release(); - } - pEnum->Release(); - return E_FAIL; -} - QT_END_NAMESPACE diff --git a/src/plugins/directshow/camera/dscamerasession.h b/src/plugins/directshow/camera/dscamerasession.h index 61b156d8a..37730736f 100644 --- a/src/plugins/directshow/camera/dscamerasession.h +++ b/src/plugins/directshow/camera/dscamerasession.h @@ -56,6 +56,7 @@ #include <dshow.h> #include <objbase.h> #include <initguid.h> +#include "directshowmediatype.h" #ifdef Q_CC_MSVC # pragma comment(lib, "strmiids.lib") # pragma comment(lib, "ole32.lib") @@ -71,11 +72,10 @@ #define __IDxtKey_INTERFACE_DEFINED__ struct ICaptureGraphBuilder2; -struct ISampleGrabber; QT_BEGIN_NAMESPACE -class SampleGrabberCallbackPrivate; +class DirectShowSampleGrabber; class DSCameraSession : public QObject { @@ -154,7 +154,7 @@ private: void setStatus(QCamera::Status status); - void onFrameAvailable(const char *frameData, long len); + void onFrameAvailable(double time, quint8 *buffer, long len); void saveCapturedImage(int id, const QImage &image, const QString &path); bool createFilterGraph(); @@ -182,14 +182,13 @@ private: QString m_sourceDeviceName; IBaseFilter* m_sourceFilter; bool m_needsHorizontalMirroring; - QList<AM_MEDIA_TYPE> m_supportedFormats; + QList<DirectShowMediaType> m_supportedFormats; QList<QCameraViewfinderSettings> m_supportedViewfinderSettings; - AM_MEDIA_TYPE m_sourceFormat; + DirectShowMediaType m_sourceFormat; QMap<QCameraImageProcessingControl::ProcessingParameter, ImageProcessingParameterInfo> m_imageProcessingParametersInfos; // Preview - IBaseFilter *m_previewFilter; - ISampleGrabber *m_previewSampleGrabber; + DirectShowSampleGrabber *m_previewSampleGrabber; IBaseFilter *m_nullRendererFilter; QVideoFrame m_currentFrame; bool m_previewStarted; diff --git a/src/plugins/directshow/common/common.pri b/src/plugins/directshow/common/common.pri new file mode 100644 index 000000000..9b7c1e3f4 --- /dev/null +++ b/src/plugins/directshow/common/common.pri @@ -0,0 +1,11 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/directshowvideoprobecontrol.h \ + $$PWD/directshowaudioprobecontrol.h \ + $$PWD/directshowsamplegrabber.h + +SOURCES += \ + $$PWD/directshowvideoprobecontrol.cpp \ + $$PWD/directshowaudioprobecontrol.cpp \ + $$PWD/directshowsamplegrabber.cpp diff --git a/src/plugins/directshow/common/directshowaudioprobecontrol.cpp b/src/plugins/directshow/common/directshowaudioprobecontrol.cpp new file mode 100644 index 000000000..55ef70178 --- /dev/null +++ b/src/plugins/directshow/common/directshowaudioprobecontrol.cpp @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "directshowaudioprobecontrol.h" +#include "directshowglobal.h" + +QT_BEGIN_NAMESPACE + +DirectShowAudioProbeControl::DirectShowAudioProbeControl(QObject *p) + : QMediaAudioProbeControl(p) +{ + +} + +DirectShowAudioProbeControl::~DirectShowAudioProbeControl() +{ + if (m_ref.deref()) + qCWarning(qtDirectShowPlugin, "QAudioProbe control destroyed while it's still being referenced!!!"); +} + +QT_END_NAMESPACE diff --git a/src/plugins/directshow/common/directshowaudioprobecontrol.h b/src/plugins/directshow/common/directshowaudioprobecontrol.h new file mode 100644 index 000000000..553bd1178 --- /dev/null +++ b/src/plugins/directshow/common/directshowaudioprobecontrol.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWAUDIOPROBECONTROL_H +#define DIRECTSHOWAUDIOPROBECONTROL_H + +#include <qmediaaudioprobecontrol.h> + +#include <QtCore/qglobal.h> + +QT_BEGIN_NAMESPACE + +class DirectShowAudioProbeControl : public QMediaAudioProbeControl +{ + Q_OBJECT +public: + explicit DirectShowAudioProbeControl(QObject *p = nullptr); + ~DirectShowAudioProbeControl(); + + bool ref() { return m_ref.ref(); } + bool deref() { return m_ref.deref(); } +private: + QAtomicInt m_ref; +}; + +QT_END_NAMESPACE + +#endif // DIRECTSHOWAUDIOPROBECONTROL_H diff --git a/src/plugins/directshow/common/directshowsamplegrabber.cpp b/src/plugins/directshow/common/directshowsamplegrabber.cpp new file mode 100644 index 000000000..b33c3cfc6 --- /dev/null +++ b/src/plugins/directshow/common/directshowsamplegrabber.cpp @@ -0,0 +1,196 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "directshowsamplegrabber.h" + +#include "directshowglobal.h" +#include "directshowutils.h" + + +QT_BEGIN_NAMESPACE + +// Previously contained in <qedit.h>. +static const IID iID_ISampleGrabber = { 0x6B652FFF, 0x11FE, 0x4fce, { 0x92, 0xAD, 0x02, 0x66, 0xB5, 0xD7, 0xC7, 0x8F } }; +static const CLSID cLSID_SampleGrabber = { 0xC1F400A0, 0x3F08, 0x11d3, { 0x9F, 0x0B, 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37 } }; + +class SampleGrabberCallbackPrivate : public ISampleGrabberCB +{ +public: + explicit SampleGrabberCallbackPrivate(DirectShowSampleGrabber *grabber) + : m_ref(1) + , m_grabber(grabber) + { } + + virtual ~SampleGrabberCallbackPrivate() { } + + STDMETHODIMP_(ULONG) AddRef() + { + return InterlockedIncrement(&m_ref); + } + + STDMETHODIMP_(ULONG) Release() + { + ULONG ref = InterlockedDecrement(&m_ref); + if (ref == 0) + delete this; + return ref; + } + + STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) + { + if (NULL == ppvObject) + return E_POINTER; + + if (riid == IID_IUnknown /*__uuidof(IUnknown) */ ) { + AddRef(); + *ppvObject = static_cast<IUnknown *>(this); + return S_OK; + } else if (riid == IID_ISampleGrabberCB /*__uuidof(ISampleGrabberCB)*/ ) { + AddRef(); + *ppvObject = static_cast<ISampleGrabberCB *>(this); + return S_OK; + } + return E_NOTIMPL; + } + + STDMETHODIMP SampleCB(double time, IMediaSample *mediaSample) + { + if (m_grabber) + Q_EMIT m_grabber->sampleAvailable(time, mediaSample); + + return S_OK; + } + + STDMETHODIMP BufferCB(double time, BYTE *buffer, long bufferLen) + { + if (m_grabber) + Q_EMIT m_grabber->bufferAvailable(time, buffer, bufferLen); + + return S_OK; + } + +private: + ULONG m_ref; + DirectShowSampleGrabber *m_grabber; +}; + +DirectShowSampleGrabber::DirectShowSampleGrabber(QObject *p) + : QObject(p) + , m_sampleGrabber(nullptr) + , m_sampleGabberCb(nullptr) + , m_callbackType(CallbackMethod::BufferCB) +{ + // Create sample grabber filter + HRESULT hr = CoCreateInstance(cLSID_SampleGrabber, NULL, CLSCTX_INPROC, iID_ISampleGrabber, reinterpret_cast<void **>(&m_sampleGrabber)); + + if (FAILED(hr)) { + qCWarning(qtDirectShowPlugin, "Failed to create sample grabber"); + return; + } + + hr = m_sampleGrabber->QueryInterface(IID_IBaseFilter, reinterpret_cast<void **>(&m_filter)); + if (FAILED(hr)) + qCWarning(qtDirectShowPlugin, "Failed to get base filter interface"); +} + +DirectShowSampleGrabber::~DirectShowSampleGrabber() +{ + stop(); + SAFE_RELEASE(m_sampleGabberCb); + SAFE_RELEASE(m_filter); + SAFE_RELEASE(m_sampleGrabber); +} + +void DirectShowSampleGrabber::stop() +{ + if (!m_sampleGrabber) + return; + + if (FAILED(m_sampleGrabber->SetCallback(nullptr, static_cast<long>(m_callbackType)))) { + qCWarning(qtDirectShowPlugin, "Failed to stop sample grabber callback"); + return; + } +} + +bool DirectShowSampleGrabber::getConnectedMediaType(AM_MEDIA_TYPE *mediaType) +{ + Q_ASSERT(mediaType); + + if (!isValid()) + return false; + + if (FAILED(m_sampleGrabber->GetConnectedMediaType(mediaType))) { + qCWarning(qtDirectShowPlugin, "Failed to retrieve the connected media type"); + return false; + } + + return true; +} + +bool DirectShowSampleGrabber::setMediaType(const AM_MEDIA_TYPE *mediaType) +{ + Q_ASSERT(mediaType); + + if (FAILED(m_sampleGrabber->SetMediaType(mediaType))) { + qCWarning(qtDirectShowPlugin, "Failed to set media type"); + return false; + } + + return true; +} + +void DirectShowSampleGrabber::start(DirectShowSampleGrabber::CallbackMethod method, + bool oneShot, + bool bufferSamples) +{ + if (!m_sampleGrabber) + return; + + stop(); + + if (!m_sampleGabberCb) + m_sampleGabberCb = new SampleGrabberCallbackPrivate(this); + + m_callbackType = method; + m_sampleGrabber->SetCallback(m_sampleGabberCb, static_cast<long>(method)); + m_sampleGrabber->SetOneShot(oneShot); + m_sampleGrabber->SetBufferSamples(bufferSamples); +} + +QT_END_NAMESPACE diff --git a/src/plugins/directshow/common/directshowsamplegrabber.h b/src/plugins/directshow/common/directshowsamplegrabber.h new file mode 100644 index 000000000..edabae2db --- /dev/null +++ b/src/plugins/directshow/common/directshowsamplegrabber.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWSAMPLEGRABBER_H +#define DIRECTSHOWSAMPLEGRABBER_H + +#include <QtCore/qglobal.h> +#include <QtCore/qobject.h> + +// TODO: Fix this. +#include <directshowcameraglobal.h> + +QT_BEGIN_NAMESPACE + +class SampleGrabberCallbackPrivate; + +class DirectShowSampleGrabber : public QObject +{ + Q_OBJECT +public: + DirectShowSampleGrabber(QObject *p = nullptr); + ~DirectShowSampleGrabber(); + + // 0 = ISampleGrabberCB::SampleCB, 1 = ISampleGrabberCB::BufferCB + enum class CallbackMethod : long + { + SampleCB, + BufferCB + }; + + bool isValid() const { return m_filter && m_sampleGrabber; } + bool getConnectedMediaType(AM_MEDIA_TYPE *mediaType); + bool setMediaType(const AM_MEDIA_TYPE *mediaType); + + void stop(); + void start(CallbackMethod method, bool oneShot = false, bool bufferSamples = false); + + IBaseFilter *filter() const { return m_filter; } + +Q_SIGNALS: + void sampleAvailable(double time, IMediaSample *sample); + void bufferAvailable(double time, quint8 *buffer, long len); + +private: + IBaseFilter *m_filter; + ISampleGrabber *m_sampleGrabber; + SampleGrabberCallbackPrivate *m_sampleGabberCb; + CallbackMethod m_callbackType; +}; + +QT_END_NAMESPACE + +#endif // DIRECTSHOWSAMPLEGRABBER_H diff --git a/src/plugins/directshow/common/directshowvideoprobecontrol.cpp b/src/plugins/directshow/common/directshowvideoprobecontrol.cpp new file mode 100644 index 000000000..5ec1ff064 --- /dev/null +++ b/src/plugins/directshow/common/directshowvideoprobecontrol.cpp @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "directshowvideoprobecontrol.h" +#include "directshowglobal.h" + +QT_BEGIN_NAMESPACE + +DirectShowVideoProbeControl::DirectShowVideoProbeControl(QObject *p) + : QMediaVideoProbeControl(p) +{ + +} + +DirectShowVideoProbeControl::~DirectShowVideoProbeControl() +{ + if (m_ref.deref()) + qCWarning(qtDirectShowPlugin, "QVideoProbe control destroyed while it's still being referenced!!!"); +} + +QT_END_NAMESPACE diff --git a/src/plugins/directshow/common/directshowvideoprobecontrol.h b/src/plugins/directshow/common/directshowvideoprobecontrol.h new file mode 100644 index 000000000..458263234 --- /dev/null +++ b/src/plugins/directshow/common/directshowvideoprobecontrol.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWVIDEOPROBECONTROL_H +#define DIRECTSHOWVIDEOPROBECONTROL_H + +#include <qmediavideoprobecontrol.h> + +#include <QtCore/qglobal.h> + +QT_BEGIN_NAMESPACE + +class DirectShowVideoProbeControl : public QMediaVideoProbeControl +{ + Q_OBJECT +public: + explicit DirectShowVideoProbeControl(QObject *p = nullptr); + ~DirectShowVideoProbeControl(); + + bool ref() { return m_ref.ref(); } + bool deref() { return m_ref.deref(); } +private: + QAtomicInt m_ref; +}; + +QT_END_NAMESPACE + +#endif // DIRECTSHOWVIDEOPROBECONTROL_H diff --git a/src/plugins/directshow/directshow.pro b/src/plugins/directshow/directshow.pro index 8e31527da..35301474f 100644 --- a/src/plugins/directshow/directshow.pro +++ b/src/plugins/directshow/directshow.pro @@ -10,6 +10,7 @@ SOURCES += dsserviceplugin.cpp mingw: DEFINES += NO_DSHOW_STRSAFE +include(common/common.pri) include(helpers/helpers.pri) qtConfig(directshow-player): include(player/player.pri) include(camera/camera.pri) diff --git a/src/plugins/directshow/dsserviceplugin.cpp b/src/plugins/directshow/dsserviceplugin.cpp index 6d8f97f52..9060bac7c 100644 --- a/src/plugins/directshow/dsserviceplugin.cpp +++ b/src/plugins/directshow/dsserviceplugin.cpp @@ -44,6 +44,7 @@ #include <QtCore/qdebug.h> #include <QtCore/QFile> +#include "directshowglobal.h" #include "dsserviceplugin.h" #include "dsvideodevicecontrol.h" @@ -72,6 +73,10 @@ extern const CLSID CLSID_VideoInputDeviceCategory; #include <windows.h> #include <ocidl.h> +QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(qtDirectShowPlugin, "qt.multimedia.plugins.directshow") +QT_END_NAMESPACE + QT_USE_NAMESPACE static int g_refCount = 0; diff --git a/src/plugins/directshow/helpers/directshowglobal.h b/src/plugins/directshow/helpers/directshowglobal.h index f7890c52b..5f391710e 100644 --- a/src/plugins/directshow/helpers/directshowglobal.h +++ b/src/plugins/directshow/helpers/directshowglobal.h @@ -43,6 +43,13 @@ #include <dshow.h> #include <QtCore/qglobal.h> +#include <QtCore/qloggingcategory.h> + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(qtDirectShowPlugin) + +QT_END_NAMESPACE template <typename T> T *com_cast(IUnknown *unknown, const IID &iid) { @@ -77,6 +84,9 @@ template <typename T> T *com_new(const IID &clsid, const IID &iid) : 0; } +DEFINE_GUID(MEDIASUBTYPE_I420, + 0x30323449,0x0000,0x0010,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71); + #ifndef __IFilterGraph2_INTERFACE_DEFINED__ #define __IFilterGraph2_INTERFACE_DEFINED__ #define INTERFACE IFilterGraph2 diff --git a/src/plugins/directshow/helpers/directshowmediatype.cpp b/src/plugins/directshow/helpers/directshowmediatype.cpp index 60c0ee040..d9ddf8248 100644 --- a/src/plugins/directshow/helpers/directshowmediatype.cpp +++ b/src/plugins/directshow/helpers/directshowmediatype.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include "directshowmediatype.h" +#include "directshowglobal.h" namespace { @@ -49,85 +50,168 @@ namespace static const TypeLookup qt_typeLookup[] = { - { QVideoFrame::Format_RGB32, /*MEDIASUBTYPE_RGB32*/ {0xe436eb7e, 0x524f, 0x11ce, {0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}} }, - { QVideoFrame::Format_BGR24, /*MEDIASUBTYPE_RGB24*/ {0xe436eb7d, 0x524f, 0x11ce, {0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}} }, - { QVideoFrame::Format_RGB565, /*MEDIASUBTYPE_RGB565*/ {0xe436eb7b, 0x524f, 0x11ce, {0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}} }, - { QVideoFrame::Format_RGB555, /*MEDIASUBTYPE_RGB555*/ {0xe436eb7c, 0x524f, 0x11ce, {0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}} }, - { QVideoFrame::Format_AYUV444, /*MEDIASUBTYPE_AYUV*/ {0x56555941, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} }, - { QVideoFrame::Format_YUYV, /*MEDIASUBTYPE_YUY2*/ {0x32595559, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} }, - { QVideoFrame::Format_UYVY, /*MEDIASUBTYPE_UYVY*/ {0x59565955, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} }, - { QVideoFrame::Format_IMC1, /*MEDIASUBTYPE_IMC1*/ {0x31434D49, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} }, - { QVideoFrame::Format_IMC2, /*MEDIASUBTYPE_IMC2*/ {0x32434D49, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} }, - { QVideoFrame::Format_IMC3, /*MEDIASUBTYPE_IMC3*/ {0x33434D49, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} }, - { QVideoFrame::Format_IMC4, /*MEDIASUBTYPE_IMC4*/ {0x34434D49, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} }, - { QVideoFrame::Format_YV12, /*MEDIASUBTYPE_YV12*/ {0x32315659, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} }, - { QVideoFrame::Format_NV12, /*MEDIASUBTYPE_NV12*/ {0x3231564E, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} }, - { QVideoFrame::Format_YUV420P, /*MEDIASUBTYPE_IYUV*/ {0x56555949, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} }, - { QVideoFrame::Format_YUV420P, /*MEDIASUBTYPE_I420*/ {0x30323449, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} } + { QVideoFrame::Format_ARGB32, MEDIASUBTYPE_ARGB32 }, + { QVideoFrame::Format_RGB32, MEDIASUBTYPE_RGB32 }, + { QVideoFrame::Format_RGB24, MEDIASUBTYPE_RGB24 }, + { QVideoFrame::Format_RGB565, MEDIASUBTYPE_RGB565 }, + { QVideoFrame::Format_RGB555, MEDIASUBTYPE_RGB555 }, + { QVideoFrame::Format_AYUV444, MEDIASUBTYPE_AYUV }, + { QVideoFrame::Format_YUYV, MEDIASUBTYPE_YUY2 }, + { QVideoFrame::Format_UYVY, MEDIASUBTYPE_UYVY }, + { QVideoFrame::Format_IMC1, MEDIASUBTYPE_IMC1 }, + { QVideoFrame::Format_IMC2, MEDIASUBTYPE_IMC2 }, + { QVideoFrame::Format_IMC3, MEDIASUBTYPE_IMC3 }, + { QVideoFrame::Format_IMC4, MEDIASUBTYPE_IMC4 }, + { QVideoFrame::Format_YV12, MEDIASUBTYPE_YV12 }, + { QVideoFrame::Format_NV12, MEDIASUBTYPE_NV12 }, + { QVideoFrame::Format_YUV420P, MEDIASUBTYPE_IYUV }, + { QVideoFrame::Format_YUV420P, MEDIASUBTYPE_I420 }, + { QVideoFrame::Format_Jpeg, MEDIASUBTYPE_MJPG } }; } -bool DirectShowMediaType::isPartiallySpecified() const +bool DirectShowMediaType::isPartiallySpecified(const AM_MEDIA_TYPE *mediaType) { - return majortype == GUID_NULL || formattype == GUID_NULL; + return mediaType->majortype == GUID_NULL || mediaType->formattype == GUID_NULL; } -bool DirectShowMediaType::isCompatibleWith(const DirectShowMediaType *type) const +DirectShowMediaType::DirectShowMediaType() + : mediaType({ GUID_NULL, GUID_NULL, TRUE, FALSE, 1, GUID_NULL, nullptr, 0, nullptr}) { - if (type->majortype != GUID_NULL && majortype != type->majortype) - return false; +} - if (type->subtype != GUID_NULL && subtype != type->subtype) - return false; +DirectShowMediaType::DirectShowMediaType(const AM_MEDIA_TYPE &type) + : DirectShowMediaType() +{ + copy(&mediaType, &type); +} - if (type->formattype != GUID_NULL) { - if (formattype != type->formattype) - return false; - if (cbFormat != type->cbFormat) - return false; - if (cbFormat != 0 && memcmp(pbFormat, type->pbFormat, cbFormat) != 0) - return false; - } +DirectShowMediaType::DirectShowMediaType(AM_MEDIA_TYPE &&type) + : DirectShowMediaType() +{ + move(&mediaType, type); +} - return true; +DirectShowMediaType::DirectShowMediaType(const DirectShowMediaType &other) + : DirectShowMediaType() +{ + copy(&mediaType, &other.mediaType); +} + +DirectShowMediaType::DirectShowMediaType(DirectShowMediaType &&other) + : DirectShowMediaType() +{ + move(&mediaType, other.mediaType); +} + +DirectShowMediaType &DirectShowMediaType::operator=(const DirectShowMediaType &other) +{ + copy(&mediaType, &other.mediaType); + return *this; +} + +DirectShowMediaType &DirectShowMediaType::operator=(DirectShowMediaType &&other) +{ + move(&mediaType, other.mediaType); + return *this; } void DirectShowMediaType::init(AM_MEDIA_TYPE *type) { - ZeroMemory((PVOID)type, sizeof(*type)); + Q_ASSERT(type); + SecureZeroMemory(reinterpret_cast<void *>(type), sizeof(AM_MEDIA_TYPE)); type->lSampleSize = 1; type->bFixedSizeSamples = TRUE; } -void DirectShowMediaType::copy(AM_MEDIA_TYPE *target, const AM_MEDIA_TYPE &source) +void DirectShowMediaType::copy(AM_MEDIA_TYPE *target, const AM_MEDIA_TYPE *source) { - if (!target) + if (!(target && source)) + return; + + if (target == source) return; - *target = source; + clear(*target); - if (source.cbFormat > 0) { - target->pbFormat = reinterpret_cast<PBYTE>(CoTaskMemAlloc(source.cbFormat)); - memcpy(target->pbFormat, source.pbFormat, source.cbFormat); + *target = *source; + + if (source->cbFormat > 0) { + target->pbFormat = reinterpret_cast<PBYTE>(CoTaskMemAlloc(source->cbFormat)); + memcpy(target->pbFormat, source->pbFormat, source->cbFormat); } if (target->pUnk) target->pUnk->AddRef(); } +void DirectShowMediaType::move(AM_MEDIA_TYPE *target, AM_MEDIA_TYPE **source) +{ + if (!target || !source || !(*source)) + return; + + if (target == *source) + return; + + clear(*target); + *target = *(*source); + SecureZeroMemory(reinterpret_cast<void *>(*source), sizeof(AM_MEDIA_TYPE)); + *source = nullptr; +} + +void DirectShowMediaType::move(AM_MEDIA_TYPE *target, AM_MEDIA_TYPE &source) +{ + AM_MEDIA_TYPE *srcPtr = &source; + move(target, &srcPtr); +} + +/** + * @brief DirectShowMediaType::deleteType - Used for AM_MEDIA_TYPE structures that have + * been allocated by CoTaskMemAlloc or CreateMediaType. + * @param type + */ void DirectShowMediaType::deleteType(AM_MEDIA_TYPE *type) { - freeData(type); + if (!type) + return; + clear(*type); CoTaskMemFree(type); } -void DirectShowMediaType::freeData(AM_MEDIA_TYPE *type) +bool DirectShowMediaType::isCompatible(const AM_MEDIA_TYPE *a, const AM_MEDIA_TYPE *b) +{ + if (b->majortype != GUID_NULL && a->majortype != b->majortype) + return false; + + if (b->subtype != GUID_NULL && a->subtype != b->subtype) + return false; + + if (b->formattype != GUID_NULL) { + if (a->formattype != b->formattype) + return false; + if (a->cbFormat != b->cbFormat) + return false; + if (a->cbFormat != 0 && memcmp(a->pbFormat, b->pbFormat, a->cbFormat) != 0) + return false; + } + + return true; +} + +/** + * @brief DirectShowMediaType::clear - Clears all member data, and releases allocated buffers. + * Use this to release automatic AM_MEDIA_TYPE structures. + * @param type + */ +void DirectShowMediaType::clear(AM_MEDIA_TYPE &type) { - if (type->cbFormat > 0) - CoTaskMemFree(type->pbFormat); + if (type.cbFormat > 0) + CoTaskMemFree(type.pbFormat); + + if (type.pUnk) + type.pUnk->Release(); - if (type->pUnk) - type->pUnk->Release(); + SecureZeroMemory(&type, sizeof(type)); } @@ -142,14 +226,17 @@ GUID DirectShowMediaType::convertPixelFormat(QVideoFrame::PixelFormat format) return MEDIASUBTYPE_None; } -QVideoSurfaceFormat DirectShowMediaType::formatFromType(const AM_MEDIA_TYPE &type) +QVideoSurfaceFormat DirectShowMediaType::videoFormatFromType(const AM_MEDIA_TYPE *type) { + if (!type) + return QVideoSurfaceFormat(); + const int count = sizeof(qt_typeLookup) / sizeof(TypeLookup); for (int i = 0; i < count; ++i) { - if (IsEqualGUID(qt_typeLookup[i].mediaType, type.subtype) && type.cbFormat > 0) { - if (IsEqualGUID(type.formattype, FORMAT_VideoInfo)) { - VIDEOINFOHEADER *header = reinterpret_cast<VIDEOINFOHEADER *>(type.pbFormat); + if (IsEqualGUID(qt_typeLookup[i].mediaType, type->subtype) && type->cbFormat > 0) { + if (IsEqualGUID(type->formattype, FORMAT_VideoInfo)) { + VIDEOINFOHEADER *header = reinterpret_cast<VIDEOINFOHEADER *>(type->pbFormat); QVideoSurfaceFormat format( QSize(header->bmiHeader.biWidth, qAbs(header->bmiHeader.biHeight)), @@ -161,8 +248,8 @@ QVideoSurfaceFormat DirectShowMediaType::formatFromType(const AM_MEDIA_TYPE &typ format.setScanLineDirection(scanLineDirection(format.pixelFormat(), header->bmiHeader)); return format; - } else if (IsEqualGUID(type.formattype, FORMAT_VideoInfo2)) { - VIDEOINFOHEADER2 *header = reinterpret_cast<VIDEOINFOHEADER2 *>(type.pbFormat); + } else if (IsEqualGUID(type->formattype, FORMAT_VideoInfo2)) { + VIDEOINFOHEADER2 *header = reinterpret_cast<VIDEOINFOHEADER2 *>(type->pbFormat); QVideoSurfaceFormat format( QSize(header->bmiHeader.biWidth, qAbs(header->bmiHeader.biHeight)), @@ -180,12 +267,15 @@ QVideoSurfaceFormat DirectShowMediaType::formatFromType(const AM_MEDIA_TYPE &typ return QVideoSurfaceFormat(); } -QVideoFrame::PixelFormat DirectShowMediaType::pixelFormatFromType(const AM_MEDIA_TYPE &type) +QVideoFrame::PixelFormat DirectShowMediaType::pixelFormatFromType(const AM_MEDIA_TYPE *type) { + if (!type) + return QVideoFrame::Format_Invalid; + const int count = sizeof(qt_typeLookup) / sizeof(TypeLookup); for (int i = 0; i < count; ++i) { - if (IsEqualGUID(qt_typeLookup[i].mediaType, type.subtype)) { + if (IsEqualGUID(qt_typeLookup[i].mediaType, type->subtype)) { return qt_typeLookup[i].pixelFormat; } } @@ -198,6 +288,7 @@ int DirectShowMediaType::bytesPerLine(const QVideoSurfaceFormat &format) { switch (format.pixelFormat()) { // 32 bpp packed formats. + case QVideoFrame::Format_ARGB32: case QVideoFrame::Format_RGB32: case QVideoFrame::Format_AYUV444: return format.frameWidth() * 4; @@ -240,8 +331,9 @@ QVideoSurfaceFormat::Direction DirectShowMediaType::scanLineDirection(QVideoFram */ switch (pixelFormat) { + case QVideoFrame::Format_ARGB32: case QVideoFrame::Format_RGB32: - case QVideoFrame::Format_BGR24: + case QVideoFrame::Format_RGB24: case QVideoFrame::Format_RGB565: case QVideoFrame::Format_RGB555: return bmiHeader.biHeight < 0 diff --git a/src/plugins/directshow/helpers/directshowmediatype.h b/src/plugins/directshow/helpers/directshowmediatype.h index b2b074ccc..7849ca9b0 100644 --- a/src/plugins/directshow/helpers/directshowmediatype.h +++ b/src/plugins/directshow/helpers/directshowmediatype.h @@ -45,39 +45,48 @@ #include <qvideosurfaceformat.h> #include <dvdmedia.h> +#include <QtCore/qglobal.h> QT_USE_NAMESPACE -class DirectShowMediaType : public AM_MEDIA_TYPE +class DirectShowMediaType { public: - DirectShowMediaType() { init(this); } - DirectShowMediaType(const AM_MEDIA_TYPE &type) { copy(this, type); } - DirectShowMediaType(const DirectShowMediaType &other) { copy(this, other); } - DirectShowMediaType &operator =(const AM_MEDIA_TYPE &type) { - freeData(this); copy(this, type); return *this; } - DirectShowMediaType &operator =(const DirectShowMediaType &other) { - freeData(this); copy(this, other); return *this; } - ~DirectShowMediaType() { freeData(this); } + DirectShowMediaType(); + DirectShowMediaType(const DirectShowMediaType &other); + DirectShowMediaType(DirectShowMediaType &&other); + explicit DirectShowMediaType(const AM_MEDIA_TYPE &type); + explicit DirectShowMediaType(AM_MEDIA_TYPE &&type); + ~DirectShowMediaType() { clear(mediaType); } - void clear() { freeData(this); init(this); } + DirectShowMediaType &operator =(const DirectShowMediaType &other); + DirectShowMediaType &operator =(DirectShowMediaType &&other); - bool isPartiallySpecified() const; - bool isCompatibleWith(const DirectShowMediaType *type) const; + void clear() { clear(mediaType); } + + inline AM_MEDIA_TYPE *operator &() Q_DECL_NOTHROW { return &mediaType; } + inline AM_MEDIA_TYPE *operator ->() Q_DECL_NOTHROW { return &mediaType; } + + inline const AM_MEDIA_TYPE *operator &() const Q_DECL_NOTHROW { return &mediaType; } + inline const AM_MEDIA_TYPE *operator ->() const Q_DECL_NOTHROW { return &mediaType; } static void init(AM_MEDIA_TYPE *type); - static void copy(AM_MEDIA_TYPE *target, const AM_MEDIA_TYPE &source); - static void freeData(AM_MEDIA_TYPE *type); + static void copy(AM_MEDIA_TYPE *target, const AM_MEDIA_TYPE *source); + static void move(AM_MEDIA_TYPE *target, AM_MEDIA_TYPE **source); + static void move(AM_MEDIA_TYPE *target, AM_MEDIA_TYPE &source); + static void clear(AM_MEDIA_TYPE &type); static void deleteType(AM_MEDIA_TYPE *type); - + static bool isPartiallySpecified(const AM_MEDIA_TYPE *mediaType); + static bool isCompatible(const AM_MEDIA_TYPE *a, const AM_MEDIA_TYPE *b); static GUID convertPixelFormat(QVideoFrame::PixelFormat format); - static QVideoSurfaceFormat formatFromType(const AM_MEDIA_TYPE &type); - static QVideoFrame::PixelFormat pixelFormatFromType(const AM_MEDIA_TYPE &type); + static QVideoSurfaceFormat videoFormatFromType(const AM_MEDIA_TYPE *type); + static QVideoFrame::PixelFormat pixelFormatFromType(const AM_MEDIA_TYPE *type); static int bytesPerLine(const QVideoSurfaceFormat &format); + static QVideoSurfaceFormat::Direction scanLineDirection(QVideoFrame::PixelFormat pixelFormat, const BITMAPINFOHEADER &bmiHeader); private: - static QVideoSurfaceFormat::Direction scanLineDirection(QVideoFrame::PixelFormat pixelFormat, const BITMAPINFOHEADER &bmiHeader); + AM_MEDIA_TYPE mediaType; }; Q_DECLARE_TYPEINFO(DirectShowMediaType, Q_MOVABLE_TYPE); diff --git a/src/plugins/directshow/helpers/directshowmediatypeenum.cpp b/src/plugins/directshow/helpers/directshowmediatypeenum.cpp index a1c8b2306..a58993f7f 100644 --- a/src/plugins/directshow/helpers/directshowmediatypeenum.cpp +++ b/src/plugins/directshow/helpers/directshowmediatypeenum.cpp @@ -72,7 +72,7 @@ HRESULT DirectShowMediaTypeEnum::Next(ULONG cMediaTypes, AM_MEDIA_TYPE **ppMedia for (ULONG i = 0; i < count; ++i, ++m_index) { ppMediaTypes[i] = reinterpret_cast<AM_MEDIA_TYPE *>(CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE))); - DirectShowMediaType::copy(ppMediaTypes[i], m_mediaTypes.at(m_index)); + DirectShowMediaType::copy(ppMediaTypes[i], &m_mediaTypes.at(m_index)); } if (pcFetched) diff --git a/src/plugins/directshow/helpers/directshowpin.cpp b/src/plugins/directshow/helpers/directshowpin.cpp index 6cf4da321..f501747f9 100644 --- a/src/plugins/directshow/helpers/directshowpin.cpp +++ b/src/plugins/directshow/helpers/directshowpin.cpp @@ -81,22 +81,20 @@ HRESULT DirectShowPin::Connect(IPin *pReceivePin, const AM_MEDIA_TYPE *pmt) if (pd == m_direction) return VFW_E_INVALID_DIRECTION; - const DirectShowMediaType *type = reinterpret_cast<const DirectShowMediaType*>(pmt); - - if (type != NULL && !type->isPartiallySpecified()) { + if (pmt != NULL && DirectShowMediaType::isPartiallySpecified(pmt)) { // If the type is fully specified, use it - hr = tryConnect(pReceivePin, type); + hr = tryConnect(pReceivePin, pmt); } else { IEnumMediaTypes *enumMediaTypes = NULL; // First, try the receiving pin's preferred types if (SUCCEEDED(pReceivePin->EnumMediaTypes(&enumMediaTypes))) { - hr = tryMediaTypes(pReceivePin, type, enumMediaTypes); + hr = tryMediaTypes(pReceivePin, pmt, enumMediaTypes); enumMediaTypes->Release(); } // Then, try this pin's preferred types if (FAILED(hr) && SUCCEEDED(EnumMediaTypes(&enumMediaTypes))) { - hr = tryMediaTypes(pReceivePin, type, enumMediaTypes); + hr = tryMediaTypes(pReceivePin, pmt, enumMediaTypes); enumMediaTypes->Release(); } } @@ -109,19 +107,19 @@ HRESULT DirectShowPin::Connect(IPin *pReceivePin, const AM_MEDIA_TYPE *pmt) return S_OK; } -HRESULT DirectShowPin::tryMediaTypes(IPin *pin, const DirectShowMediaType *partialType, IEnumMediaTypes *enumMediaTypes) +HRESULT DirectShowPin::tryMediaTypes(IPin *pin, const AM_MEDIA_TYPE *partialType, IEnumMediaTypes *enumMediaTypes) { HRESULT hr = enumMediaTypes->Reset(); if (FAILED(hr)) return hr; - DirectShowMediaType *mediaType = NULL; + AM_MEDIA_TYPE *mediaType = NULL; ULONG mediaCount = 0; HRESULT hrFailure = VFW_E_NO_ACCEPTABLE_TYPES; - for (; enumMediaTypes->Next(1, reinterpret_cast<AM_MEDIA_TYPE**>(&mediaType), &mediaCount) == S_OK;) { + for (; enumMediaTypes->Next(1, &mediaType, &mediaCount) == S_OK;) { - if (mediaType && (partialType == NULL || mediaType->isCompatibleWith(partialType))) { + if (mediaType && (partialType == NULL || DirectShowMediaType::isCompatible(mediaType, partialType))) { hr = tryConnect(pin, mediaType); if (FAILED(hr) && (hr != E_FAIL) @@ -141,7 +139,7 @@ HRESULT DirectShowPin::tryMediaTypes(IPin *pin, const DirectShowMediaType *parti return hrFailure; } -HRESULT DirectShowPin::tryConnect(IPin *pin, const DirectShowMediaType *type) +HRESULT DirectShowPin::tryConnect(IPin *pin, const AM_MEDIA_TYPE *type) { if (!isMediaTypeSupported(type)) return VFW_E_TYPE_NOT_ACCEPTED; @@ -189,15 +187,14 @@ HRESULT DirectShowPin::ReceiveConnection(IPin *pConnector, const AM_MEDIA_TYPE * if (pd == m_direction) return VFW_E_INVALID_DIRECTION; - const DirectShowMediaType *type = reinterpret_cast<const DirectShowMediaType*>(pmt); - if (!isMediaTypeSupported(type)) + if (!isMediaTypeSupported(pmt)) return VFW_E_TYPE_NOT_ACCEPTED; m_peerPin = pConnector; m_peerPin->AddRef(); HRESULT hr; - if (!setMediaType(type)) + if (!setMediaType(pmt)) hr = VFW_E_TYPE_NOT_ACCEPTED; else hr = completeConnection(pConnector); @@ -265,7 +262,7 @@ HRESULT DirectShowPin::ConnectionMediaType(AM_MEDIA_TYPE *pmt) DirectShowMediaType::init(pmt); return VFW_E_NOT_CONNECTED; } else { - DirectShowMediaType::copy(pmt, m_mediaType); + DirectShowMediaType::copy(pmt, &m_mediaType); return S_OK; } } @@ -309,7 +306,7 @@ HRESULT DirectShowPin::QueryAccept(const AM_MEDIA_TYPE *pmt) if (!pmt) return E_POINTER; - if (!isMediaTypeSupported(reinterpret_cast<const DirectShowMediaType*>(pmt))) + if (!isMediaTypeSupported(pmt)) return S_FALSE; return S_OK; @@ -370,12 +367,12 @@ QList<DirectShowMediaType> DirectShowPin::supportedMediaTypes() return QList<DirectShowMediaType>(); } -bool DirectShowPin::setMediaType(const DirectShowMediaType *type) +bool DirectShowPin::setMediaType(const AM_MEDIA_TYPE *type) { if (!type) m_mediaType.clear(); else - m_mediaType = *type; + DirectShowMediaType::copy(&m_mediaType, type); return true; } @@ -673,7 +670,7 @@ HRESULT DirectShowInputPin::Receive(IMediaSample *pSample) if (!(m_sampleProperties.dwSampleFlags & AM_SAMPLE_TYPECHANGED)) return S_OK; - if (isMediaTypeSupported(reinterpret_cast<DirectShowMediaType*>(m_sampleProperties.pMediaType))) + if (isMediaTypeSupported(m_sampleProperties.pMediaType)) return S_OK; m_inErrorState = true; diff --git a/src/plugins/directshow/helpers/directshowpin.h b/src/plugins/directshow/helpers/directshowpin.h index 823223956..b8207abae 100644 --- a/src/plugins/directshow/helpers/directshowpin.h +++ b/src/plugins/directshow/helpers/directshowpin.h @@ -55,9 +55,9 @@ public: QString name() const { return m_name; } bool isConnected() const { return m_peerPin != NULL; } - virtual bool isMediaTypeSupported(const DirectShowMediaType *type) = 0; + virtual bool isMediaTypeSupported(const AM_MEDIA_TYPE *type) = 0; virtual QList<DirectShowMediaType> supportedMediaTypes(); - virtual bool setMediaType(const DirectShowMediaType *type); + virtual bool setMediaType(const AM_MEDIA_TYPE *type); virtual HRESULT completeConnection(IPin *pin); virtual HRESULT connectionEnded(); @@ -107,8 +107,8 @@ protected: private: Q_DISABLE_COPY(DirectShowPin) - HRESULT tryMediaTypes(IPin *pin, const DirectShowMediaType *type, IEnumMediaTypes *enumMediaTypes); - HRESULT tryConnect(IPin *pin, const DirectShowMediaType *type); + HRESULT tryMediaTypes(IPin *pin, const AM_MEDIA_TYPE *type, IEnumMediaTypes *enumMediaTypes); + HRESULT tryConnect(IPin *pin, const AM_MEDIA_TYPE *type); }; diff --git a/src/plugins/directshow/helpers/directshowutils.cpp b/src/plugins/directshow/helpers/directshowutils.cpp new file mode 100644 index 000000000..d9701fd99 --- /dev/null +++ b/src/plugins/directshow/helpers/directshowutils.cpp @@ -0,0 +1,309 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "directshowutils.h" + +/** + * @brief DirectShowUtils::isPinConnected + * @param pin + * @param hrOut + * @return + */ +bool DirectShowUtils::isPinConnected(IPin *pin, HRESULT *hrOut) +{ + IPin *connectedPin = nullptr; + const ScopedSafeRelease<IPin> releasePin { &connectedPin }; + HRESULT hr = S_OK; + if (!hrOut) + hrOut = &hr; + + *hrOut = pin->ConnectedTo(&connectedPin); + if (*hrOut == VFW_E_NOT_CONNECTED) // Not an error in this case + *hrOut = S_OK; + + if (FAILED(*hrOut)) { + qCDebug(qtDirectShowPlugin, "Querying pin connection failed!"); + return false; + } + + return true; +} + +/** + * @brief DirectShowUtils::hasPinDirection + * @param pin + * @param direction + * @param hrOut + * @return + */ +bool DirectShowUtils::hasPinDirection(IPin *pin, PIN_DIRECTION direction, HRESULT *hrOut) +{ + PIN_DIRECTION pinDir; + HRESULT hr = S_OK; + if (!hrOut) + hrOut = &hr; + + *hrOut = pin->QueryDirection(&pinDir); + + if (FAILED(*hrOut)) { + qCDebug(qtDirectShowPlugin, "Querying pin direction failed!"); + return false; + } + + return (pinDir == direction); +} + +/** + * @brief DirectShowUtils::getPin + * @param filter + * @param pinDirection + * @param pin + * @param hrOut + * @return + */ +bool DirectShowUtils::getPin(IBaseFilter *filter, PIN_DIRECTION pinDirection, IPin **pin, HRESULT *hrOut) +{ + IEnumPins *enumPins = nullptr; + const ScopedSafeRelease<IEnumPins> releaseEnumPins { &enumPins }; + HRESULT hr S_OK; + if (!hrOut) + hrOut = &hr; + + *hrOut = filter->EnumPins(&enumPins); + if (FAILED(*hrOut)) { + qCDebug(qtDirectShowPlugin, "Unable to retrieve pins from the filter!"); + return false; + } + + enumPins->Reset(); + IPin *nextPin = nullptr; + while (enumPins->Next(1, &nextPin, NULL) == S_OK) { + const ScopedSafeRelease<IPin> releasePin { &nextPin }; + PIN_DIRECTION currentPinDir; + *hrOut = nextPin->QueryDirection(¤tPinDir); + if (currentPinDir == pinDirection) { + *pin = nextPin; + (*pin)->AddRef(); + return true; + } + } + + return false; +} + +/** + * @brief DirectShowUtils::matchPin + * @param pin + * @param pinDirection + * @param shouldBeConnected + * @param hrOut + * @return + */ +bool DirectShowUtils::matchPin(IPin *pin, PIN_DIRECTION pinDirection, BOOL shouldBeConnected, HRESULT *hrOut) +{ + HRESULT hr = S_OK; + if (!hrOut) + hrOut = &hr; + + const BOOL isConnected = isPinConnected(pin, hrOut); + if (FAILED(*hrOut)) // Error reason will already be logged, so just return. + return false; + + if (isConnected == shouldBeConnected) + return hasPinDirection(pin, pinDirection, hrOut); + + return SUCCEEDED(*hrOut); +} + +/** + * @brief DirectShowUtils::findUnconnectedPin + * @param filter + * @param pinDirection + * @param pin + * @param hrOut + * @return + */ +bool DirectShowUtils::findUnconnectedPin(IBaseFilter *filter, PIN_DIRECTION pinDirection, IPin **pin, HRESULT *hrOut) +{ + HRESULT hr = S_OK; + if (!hrOut) + hrOut = &hr; + + IEnumPins *enumPins = nullptr; + const ScopedSafeRelease<IEnumPins> releaseEnumPins { &enumPins }; + *hrOut = filter->EnumPins(&enumPins); + if (FAILED(*hrOut)) { + qCDebug(qtDirectShowPlugin, "Unable to retrieve pins from the DS filter"); + return false; + } + + IPin *nextPin = nullptr; + while (S_OK == enumPins->Next(1, &nextPin, nullptr)) { + const ScopedSafeRelease<IPin> releaseNextPin { &nextPin }; + if (matchPin(nextPin, pinDirection, FALSE, hrOut)) { + *pin = nextPin; + (*pin)->AddRef(); + return true; + } + + if (FAILED(*hrOut)) + return false; + } + + qCDebug(qtDirectShowPlugin, "No unconnected pins found"); + *hrOut = VFW_E_NOT_FOUND; + + return false; +} + +/** + * @brief DirectShowUtils::connectFilters - Attempts to connect \a outputPin to \a filter + * @param graph + * @param outputPin + * @param filter + * @param hrOut + * @return + */ +bool DirectShowUtils::connectFilters(IGraphBuilder *graph, IPin *outputPin, IBaseFilter *filter, HRESULT *hrOut) +{ + + // Find an input pin on the downstream filter. + HRESULT hr = S_OK; + if (!hrOut) + hrOut = &hr; + + IPin *inputPin = nullptr; + const ScopedSafeRelease<IPin> releaseInputPin { &inputPin }; + if (!findUnconnectedPin(filter, PINDIR_INPUT, &inputPin, hrOut)) + return false; + + + // Try to connect them. + *hrOut = graph->Connect(outputPin, inputPin); + if (FAILED(*hrOut)) { + qCDebug(qtDirectShowPlugin, "Unable to connect output pin to filter!"); + return false; + } + + return true; +} + +/** + * @brief DirectShowUtils::connectFilters - Attempts to connect \a filter to \a inputPin. + * @param graph + * @param filter + * @param inputPin + * @param hrOut + * @return + */ +bool DirectShowUtils::connectFilters(IGraphBuilder *graph, IBaseFilter *filter, IPin *inputPin, HRESULT *hrOut) +{ + HRESULT hr = S_OK; + if (!hrOut) + hrOut = &hr; + + IPin *outputPin = nullptr; + const ScopedSafeRelease<IPin> releaseOutputPin { &outputPin }; + // Find an output pin on the upstream filter. + if (findUnconnectedPin(filter, PINDIR_OUTPUT, &outputPin, hrOut)) + return false; + + *hrOut = graph->Connect(outputPin, inputPin); + if (FAILED(*hrOut)) { + qCDebug(qtDirectShowPlugin, "Unable to connect filter to input pin!"); + return false; + } + + return true; +} + +/** + * @brief DirectShowUtils::connectFilters - Attempts to connect the \a upstreamFilter to \a downstreamFilter. + * @param graph + * @param upstreamFilter + * @param downstreamFilter + * @param autoConnect - If set to true all filters in the graph will be considered. + * @param hrOut + * @return true if the the filters were connected, false otherwise. + */ +bool DirectShowUtils::connectFilters(IGraphBuilder *graph, + IBaseFilter *upstreamFilter, + IBaseFilter *downstreamFilter, + bool autoConnect, + HRESULT *hrOut) +{ + HRESULT hr = S_OK; + if (!hrOut) + hrOut = &hr; + + const auto findAndConnect = [graph, downstreamFilter, hrOut](IBaseFilter *filter) -> bool { + IPin *outputPin = nullptr; + const ScopedSafeRelease<IPin> releaseOutputPin { &outputPin }; + if (findUnconnectedPin(filter, PINDIR_OUTPUT, &outputPin, hrOut)) + return connectFilters(graph, outputPin, downstreamFilter, hrOut); + + return false; + }; + + // Try to connect to the upstream filter first. + if (findAndConnect(upstreamFilter)) + return S_OK; + + const auto getFilters = [graph, hrOut]() -> IEnumFilters * { + IEnumFilters *f = nullptr; + *hrOut = graph->EnumFilters(&f); + return f; + }; + IEnumFilters *filters = autoConnect ? getFilters() + : nullptr; + const ScopedSafeRelease<IEnumFilters> releaseEnumFilters { &filters }; + if (!filters) { + qCDebug(qtDirectShowPlugin, "No filters found!"); + return false; + } + + IBaseFilter *nextFilter = nullptr; + while (S_OK == filters->Next(1, &nextFilter, 0)) { + const ScopedSafeRelease<IBaseFilter> releaseNextFilter { &nextFilter }; + if (nextFilter && findAndConnect(nextFilter)) + break; + } + + return SUCCEEDED(*hrOut); +} diff --git a/src/plugins/directshow/helpers/directshowutils.h b/src/plugins/directshow/helpers/directshowutils.h new file mode 100644 index 000000000..09c81c257 --- /dev/null +++ b/src/plugins/directshow/helpers/directshowutils.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWUTILS_H +#define DIRECTSHOWUTILS_H + +#include "directshowglobal.h" + +QT_BEGIN_NAMESPACE + +namespace DirectShowUtils +{ +template <typename T> +void safeRelease(T **iface) { + if (!iface) + return; + + if (!*iface) + return; + + (*iface)->Release(); + *iface = nullptr; +} + +template <typename T> +struct ScopedSafeRelease +{ + T **iunknown; + ~ScopedSafeRelease() + { + DirectShowUtils::safeRelease(iunknown); + } +}; + +bool getPin(IBaseFilter *filter, PIN_DIRECTION pinDirection, IPin **pin, HRESULT *hrOut); +bool isPinConnected(IPin *pin, HRESULT *hrOut = nullptr); +bool hasPinDirection(IPin *pin, PIN_DIRECTION direction, HRESULT *hrOut = nullptr); +bool matchPin(IPin *pin, PIN_DIRECTION pinDirection, BOOL shouldBeConnected, HRESULT *hrOut = nullptr); +bool findUnconnectedPin(IBaseFilter *filter, PIN_DIRECTION pinDirection, IPin **pin, HRESULT *hrOut = nullptr); +bool connectFilters(IGraphBuilder *graph, IPin *outputPin, IBaseFilter *filter, HRESULT *hrOut = nullptr); +bool connectFilters(IGraphBuilder *graph, IBaseFilter *filter, IPin *inputPin, HRESULT *hrOut = nullptr); +bool connectFilters(IGraphBuilder *graph, + IBaseFilter *upstreamFilter, + IBaseFilter *downstreamFilter, + bool autoConnect = false, + HRESULT *hrOut = nullptr); +} + +QT_END_NAMESPACE + +#endif // DIRECTSHOWUTILS_H diff --git a/src/plugins/directshow/helpers/helpers.pri b/src/plugins/directshow/helpers/helpers.pri index b3743a680..ca883eeb8 100644 --- a/src/plugins/directshow/helpers/helpers.pri +++ b/src/plugins/directshow/helpers/helpers.pri @@ -9,7 +9,8 @@ HEADERS += \ $$PWD/directshowobject.h \ $$PWD/directshowpin.h \ $$PWD/directshowpinenum.h \ - $$PWD/directshowvideobuffer.h + $$PWD/directshowvideobuffer.h \ + $$PWD/directshowutils.h SOURCES += \ $$PWD/directshowbasefilter.cpp \ @@ -19,4 +20,5 @@ SOURCES += \ $$PWD/directshowobject.cpp \ $$PWD/directshowpin.cpp \ $$PWD/directshowpinenum.cpp \ - $$PWD/directshowvideobuffer.cpp + $$PWD/directshowvideobuffer.cpp \ + $$PWD/directshowutils.cpp diff --git a/src/plugins/directshow/player/directshowiosource.cpp b/src/plugins/directshow/player/directshowiosource.cpp index 3c44dd1ed..96b4b14bf 100644 --- a/src/plugins/directshow/player/directshowiosource.cpp +++ b/src/plugins/directshow/player/directshowiosource.cpp @@ -82,7 +82,7 @@ DirectShowIOSource::DirectShowIOSource(DirectShowEventLoop *loop) // The filter works in pull mode, the downstream filter is responsible for requesting // samples from this one. // - AM_MEDIA_TYPE type = + AM_MEDIA_TYPE type { MEDIATYPE_Stream, // majortype MEDIASUBTYPE_NULL, // subtype @@ -99,7 +99,7 @@ DirectShowIOSource::DirectShowIOSource(DirectShowEventLoop *loop) for (int i = 0; i < count; ++i) { type.subtype = directshow_subtypes[i]; - m_supportedMediaTypes.append(type); + m_supportedMediaTypes.append(DirectShowMediaType(type)); } } @@ -371,12 +371,12 @@ HRESULT DirectShowIOSource::Connect(IPin *pReceivePin, const AM_MEDIA_TYPE *pmt) hr = pReceivePin->ReceiveConnection(this, pmt); // Update the media type for the current connection. if (SUCCEEDED(hr)) - m_connectionMediaType = *pmt; + DirectShowMediaType::copy(&m_connectionMediaType, pmt); } else if (pmt && pmt->subtype == MEDIATYPE_NULL) { // - Partial type (Stream, but no subtype specified). - m_connectionMediaType = *pmt; + DirectShowMediaType::copy(&m_connectionMediaType, pmt); // Check if the receiving pin accepts any of the streaming subtypes. for (const DirectShowMediaType &t : qAsConst(m_supportedMediaTypes)) { - m_connectionMediaType.subtype = t.subtype; + m_connectionMediaType->subtype = t->subtype; hr = pReceivePin->ReceiveConnection(this, &m_connectionMediaType); if (SUCCEEDED(hr)) break; @@ -477,7 +477,7 @@ HRESULT DirectShowIOSource::ConnectionMediaType(AM_MEDIA_TYPE *pmt) return VFW_E_NOT_CONNECTED; } else { - DirectShowMediaType::copy(pmt, m_connectionMediaType); + DirectShowMediaType::copy(pmt, &m_connectionMediaType); return S_OK; } diff --git a/src/plugins/directshow/player/directshowplayercontrol.cpp b/src/plugins/directshow/player/directshowplayercontrol.cpp index fce488af2..2d0ee2d59 100644 --- a/src/plugins/directshow/player/directshowplayercontrol.cpp +++ b/src/plugins/directshow/player/directshowplayercontrol.cpp @@ -200,7 +200,7 @@ qreal DirectShowPlayerControl::playbackRate() const void DirectShowPlayerControl::setPlaybackRate(qreal rate) { - if (m_playbackRate != rate) { + if (!qFuzzyCompare(m_playbackRate, rate)) { m_service->setRate(rate); emit playbackRateChanged(m_playbackRate = rate); @@ -370,7 +370,7 @@ void DirectShowPlayerControl::updateMediaInfo(qint64 duration, int streamTypes, void DirectShowPlayerControl::updatePlaybackRate(qreal rate) { - if (m_playbackRate != rate) { + if (!qFuzzyCompare(m_playbackRate, rate)) { m_playbackRate = rate; scheduleUpdate(PlaybackRateProperty); diff --git a/src/plugins/directshow/player/directshowplayerservice.cpp b/src/plugins/directshow/player/directshowplayerservice.cpp index 1554d194d..5fbb6ef05 100644 --- a/src/plugins/directshow/player/directshowplayerservice.cpp +++ b/src/plugins/directshow/player/directshowplayerservice.cpp @@ -629,13 +629,13 @@ int DirectShowPlayerService::findStreamTypes(IBaseFilter *source) const for (IPin *pin = 0; pins->Next(1, &pin, 0) == S_OK; pin->Release()) { PIN_DIRECTION direction; if (pin->QueryDirection(&direction) == S_OK && direction == PINDIR_OUTPUT) { - AM_MEDIA_TYPE connectionType; + DirectShowMediaType connectionType; if (SUCCEEDED(pin->ConnectionMediaType(&connectionType))) { IPin *peer = 0; - if (connectionType.majortype == MEDIATYPE_Audio) { + if (connectionType->majortype == MEDIATYPE_Audio) { streamTypes |= AudioStream; - } else if (connectionType.majortype == MEDIATYPE_Video) { + } else if (connectionType->majortype == MEDIATYPE_Video) { streamTypes |= VideoStream; } else if (SUCCEEDED(pin->ConnectedTo(&peer))) { PIN_INFO peerInfo; diff --git a/src/plugins/directshow/player/directshowplayerservice.h b/src/plugins/directshow/player/directshowplayerservice.h index b8d30e79a..d933bd660 100644 --- a/src/plugins/directshow/player/directshowplayerservice.h +++ b/src/plugins/directshow/player/directshowplayerservice.h @@ -186,6 +186,7 @@ private: int m_pendingTasks; int m_executingTask; int m_executedTasks; + int m_streamTypes; HANDLE m_taskHandle; HANDLE m_eventHandle; GraphStatus m_graphStatus; @@ -195,20 +196,19 @@ private: IBaseFilter *m_source; IBaseFilter *m_audioOutput; IBaseFilter *m_videoOutput; - int m_streamTypes; qreal m_rate; qint64 m_position; qint64 m_seekPosition; qint64 m_duration; - bool m_buffering; - bool m_seekable; - bool m_atEnd; - bool m_dontCacheNextSeekResult; QMediaTimeRange m_playbackRange; QUrl m_url; QMediaResourceList m_resources; QString m_errorString; QMutex m_mutex; + bool m_buffering; + bool m_seekable; + bool m_atEnd; + bool m_dontCacheNextSeekResult; friend class DirectShowPlayerServiceThread; }; diff --git a/src/plugins/directshow/player/videosurfacefilter.cpp b/src/plugins/directshow/player/videosurfacefilter.cpp index 4cb97be39..13f767a85 100644 --- a/src/plugins/directshow/player/videosurfacefilter.cpp +++ b/src/plugins/directshow/player/videosurfacefilter.cpp @@ -63,8 +63,8 @@ public: VideoSurfaceInputPin(VideoSurfaceFilter *filter); // DirectShowPin - bool isMediaTypeSupported(const DirectShowMediaType *type); - bool setMediaType(const DirectShowMediaType *type); + bool isMediaTypeSupported(const AM_MEDIA_TYPE *type) override; + bool setMediaType(const AM_MEDIA_TYPE *type) override; HRESULT completeConnection(IPin *pin); HRESULT connectionEnded(); @@ -90,12 +90,12 @@ VideoSurfaceInputPin::VideoSurfaceInputPin(VideoSurfaceFilter *filter) { } -bool VideoSurfaceInputPin::isMediaTypeSupported(const DirectShowMediaType *type) +bool VideoSurfaceInputPin::isMediaTypeSupported(const AM_MEDIA_TYPE *type) { return m_videoSurfaceFilter->isMediaTypeSupported(type); } -bool VideoSurfaceInputPin::setMediaType(const DirectShowMediaType *type) +bool VideoSurfaceInputPin::setMediaType(const AM_MEDIA_TYPE *type) { if (!DirectShowInputPin::setMediaType(type)) return false; @@ -282,7 +282,7 @@ void VideoSurfaceFilter::supportedFormatsChanged() } } -bool VideoSurfaceFilter::isMediaTypeSupported(const DirectShowMediaType *type) +bool VideoSurfaceFilter::isMediaTypeSupported(const AM_MEDIA_TYPE *type) { if (type->majortype != MEDIATYPE_Video || type->bFixedSizeSamples == FALSE) return false; @@ -297,7 +297,7 @@ bool VideoSurfaceFilter::isMediaTypeSupported(const DirectShowMediaType *type) return false; } -bool VideoSurfaceFilter::setMediaType(const DirectShowMediaType *type) +bool VideoSurfaceFilter::setMediaType(const AM_MEDIA_TYPE *type) { if (!type) { qCDebug(qLcRenderFilter, "clear media type"); @@ -305,7 +305,7 @@ bool VideoSurfaceFilter::setMediaType(const DirectShowMediaType *type) m_bytesPerLine = 0; return true; } else { - m_surfaceFormat = DirectShowMediaType::formatFromType(*type); + m_surfaceFormat = DirectShowMediaType::videoFormatFromType(type); m_bytesPerLine = DirectShowMediaType::bytesPerLine(m_surfaceFormat); qCDebug(qLcRenderFilter) << "setMediaType -->" << m_surfaceFormat; return m_surfaceFormat.isValid(); @@ -478,7 +478,7 @@ HRESULT VideoSurfaceFilter::Receive(IMediaSample *pMediaSample) // If the format dynamically changed, the sample contains information about the new format. // We need to reset the format and restart the QAbstractVideoSurface. if (m_pin->currentSampleProperties()->pMediaType - && (!m_pin->setMediaType(reinterpret_cast<const DirectShowMediaType *>(m_pin->currentSampleProperties()->pMediaType)) + && (!m_pin->setMediaType(m_pin->currentSampleProperties()->pMediaType) || !restartSurface())) { qCWarning(qLcRenderFilter, " dynamic format change failed, aborting rendering"); NotifyEvent(EC_ERRORABORT, VFW_E_TYPE_NOT_ACCEPTED, 0); diff --git a/src/plugins/directshow/player/videosurfacefilter.h b/src/plugins/directshow/player/videosurfacefilter.h index 581e33c70..6d6cc7593 100644 --- a/src/plugins/directshow/player/videosurfacefilter.h +++ b/src/plugins/directshow/player/videosurfacefilter.h @@ -83,8 +83,8 @@ public: STDMETHODIMP_(ULONG) GetMiscFlags(); // DirectShowPin (delegate) - bool isMediaTypeSupported(const DirectShowMediaType *type); - bool setMediaType(const DirectShowMediaType *type); + bool isMediaTypeSupported(const AM_MEDIA_TYPE *type); + bool setMediaType(const AM_MEDIA_TYPE *type); HRESULT completeConnection(IPin *pin); HRESULT connectionEnded(); |