diff options
Diffstat (limited to 'src/plugins/wmf/player')
-rw-r--r-- | src/plugins/wmf/player/mfactivate.cpp | 87 | ||||
-rw-r--r-- | src/plugins/wmf/player/mfactivate.h | 212 | ||||
-rw-r--r-- | src/plugins/wmf/player/mftvideo.cpp | 753 | ||||
-rw-r--r-- | src/plugins/wmf/player/mftvideo.h | 117 | ||||
-rw-r--r-- | src/plugins/wmf/player/player.pri | 13 | ||||
-rw-r--r-- | src/plugins/wmf/player/samplegrabber.cpp | 173 | ||||
-rw-r--r-- | src/plugins/wmf/player/samplegrabber.h | 96 |
7 files changed, 1448 insertions, 3 deletions
diff --git a/src/plugins/wmf/player/mfactivate.cpp b/src/plugins/wmf/player/mfactivate.cpp new file mode 100644 index 000000000..e06906584 --- /dev/null +++ b/src/plugins/wmf/player/mfactivate.cpp @@ -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$ +** +****************************************************************************/ + +#include "mfactivate.h" + +#include <mfapi.h> + +MFAbstractActivate::MFAbstractActivate() + : m_attributes(0) + , m_cRef(1) +{ + MFCreateAttributes(&m_attributes, 0); +} + +MFAbstractActivate::~MFAbstractActivate() +{ + if (m_attributes) + m_attributes->Release(); +} + + +HRESULT MFAbstractActivate::QueryInterface(REFIID riid, LPVOID *ppvObject) +{ + if (!ppvObject) + return E_POINTER; + if (riid == IID_IMFActivate) { + *ppvObject = static_cast<IMFActivate*>(this); + } else if (riid == IID_IMFAttributes) { + *ppvObject = static_cast<IMFAttributes*>(this); + } else if (riid == IID_IUnknown) { + *ppvObject = static_cast<IUnknown*>(static_cast<IMFActivate*>(this)); + } else { + *ppvObject = NULL; + return E_NOINTERFACE; + } + AddRef(); + return S_OK; +} + +ULONG MFAbstractActivate::AddRef(void) +{ + return InterlockedIncrement(&m_cRef); +} + +ULONG MFAbstractActivate::Release(void) +{ + ULONG cRef = InterlockedDecrement(&m_cRef); + if (cRef == 0) + delete this; + return cRef; +} diff --git a/src/plugins/wmf/player/mfactivate.h b/src/plugins/wmf/player/mfactivate.h new file mode 100644 index 000000000..3243296e8 --- /dev/null +++ b/src/plugins/wmf/player/mfactivate.h @@ -0,0 +1,212 @@ +/**************************************************************************** +** +** 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 MFACTIVATE_H +#define MFACTIVATE_H + +#include <mfidl.h> + +class MFAbstractActivate : public IMFActivate +{ +public: + explicit MFAbstractActivate(); + virtual ~MFAbstractActivate(); + + //from IUnknown + STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + + //from IMFAttributes + STDMETHODIMP GetItem(REFGUID guidKey, PROPVARIANT *pValue) + { + return m_attributes->GetItem(guidKey, pValue); + } + + STDMETHODIMP GetItemType(REFGUID guidKey, MF_ATTRIBUTE_TYPE *pType) + { + return m_attributes->GetItemType(guidKey, pType); + } + + STDMETHODIMP CompareItem(REFGUID guidKey, REFPROPVARIANT Value, BOOL *pbResult) + { + return m_attributes->CompareItem(guidKey, Value, pbResult); + } + + STDMETHODIMP Compare(IMFAttributes *pTheirs, MF_ATTRIBUTES_MATCH_TYPE MatchType, BOOL *pbResult) + { + return m_attributes->Compare(pTheirs, MatchType, pbResult); + } + + STDMETHODIMP GetUINT32(REFGUID guidKey, UINT32 *punValue) + { + return m_attributes->GetUINT32(guidKey, punValue); + } + + STDMETHODIMP GetUINT64(REFGUID guidKey, UINT64 *punValue) + { + return m_attributes->GetUINT64(guidKey, punValue); + } + + STDMETHODIMP GetDouble(REFGUID guidKey, double *pfValue) + { + return m_attributes->GetDouble(guidKey, pfValue); + } + + STDMETHODIMP GetGUID(REFGUID guidKey, GUID *pguidValue) + { + return m_attributes->GetGUID(guidKey, pguidValue); + } + + STDMETHODIMP GetStringLength(REFGUID guidKey, UINT32 *pcchLength) + { + return m_attributes->GetStringLength(guidKey, pcchLength); + } + + STDMETHODIMP GetString(REFGUID guidKey, LPWSTR pwszValue, UINT32 cchBufSize, UINT32 *pcchLength) + { + return m_attributes->GetString(guidKey, pwszValue, cchBufSize, pcchLength); + } + + STDMETHODIMP GetAllocatedString(REFGUID guidKey, LPWSTR *ppwszValue, UINT32 *pcchLength) + { + return m_attributes->GetAllocatedString(guidKey, ppwszValue, pcchLength); + } + + STDMETHODIMP GetBlobSize(REFGUID guidKey, UINT32 *pcbBlobSize) + { + return m_attributes->GetBlobSize(guidKey, pcbBlobSize); + } + + STDMETHODIMP GetBlob(REFGUID guidKey, UINT8 *pBuf, UINT32 cbBufSize, UINT32 *pcbBlobSize) + { + return m_attributes->GetBlob(guidKey, pBuf, cbBufSize, pcbBlobSize); + } + + STDMETHODIMP GetAllocatedBlob(REFGUID guidKey, UINT8 **ppBuf, UINT32 *pcbSize) + { + return m_attributes->GetAllocatedBlob(guidKey, ppBuf, pcbSize); + } + + STDMETHODIMP GetUnknown(REFGUID guidKey, REFIID riid, LPVOID *ppv) + { + return m_attributes->GetUnknown(guidKey, riid, ppv); + } + + STDMETHODIMP SetItem(REFGUID guidKey, REFPROPVARIANT Value) + { + return m_attributes->SetItem(guidKey, Value); + } + + STDMETHODIMP DeleteItem(REFGUID guidKey) + { + return m_attributes->DeleteItem(guidKey); + } + + STDMETHODIMP DeleteAllItems() + { + return m_attributes->DeleteAllItems(); + } + + STDMETHODIMP SetUINT32(REFGUID guidKey, UINT32 unValue) + { + return m_attributes->SetUINT32(guidKey, unValue); + } + + STDMETHODIMP SetUINT64(REFGUID guidKey, UINT64 unValue) + { + return m_attributes->SetUINT64(guidKey, unValue); + } + + STDMETHODIMP SetDouble(REFGUID guidKey, double fValue) + { + return m_attributes->SetDouble(guidKey, fValue); + } + + STDMETHODIMP SetGUID(REFGUID guidKey, REFGUID guidValue) + { + return m_attributes->SetGUID(guidKey, guidValue); + } + + STDMETHODIMP SetString(REFGUID guidKey, LPCWSTR wszValue) + { + return m_attributes->SetString(guidKey, wszValue); + } + + STDMETHODIMP SetBlob(REFGUID guidKey, const UINT8 *pBuf, UINT32 cbBufSize) + { + return m_attributes->SetBlob(guidKey, pBuf, cbBufSize); + } + + STDMETHODIMP SetUnknown(REFGUID guidKey, IUnknown *pUnknown) + { + return m_attributes->SetUnknown(guidKey, pUnknown); + } + + STDMETHODIMP LockStore() + { + return m_attributes->LockStore(); + } + + STDMETHODIMP UnlockStore() + { + return m_attributes->UnlockStore(); + } + + STDMETHODIMP GetCount(UINT32 *pcItems) + { + return m_attributes->GetCount(pcItems); + } + + STDMETHODIMP GetItemByIndex(UINT32 unIndex, GUID *pguidKey, PROPVARIANT *pValue) + { + return m_attributes->GetItemByIndex(unIndex, pguidKey, pValue); + } + + STDMETHODIMP CopyAllItems(IMFAttributes *pDest) + { + return m_attributes->CopyAllItems(pDest); + } + +private: + IMFAttributes *m_attributes; + ULONG m_cRef; +}; + +#endif // MFACTIVATE_H diff --git a/src/plugins/wmf/player/mftvideo.cpp b/src/plugins/wmf/player/mftvideo.cpp new file mode 100644 index 000000000..747fe6aea --- /dev/null +++ b/src/plugins/wmf/player/mftvideo.cpp @@ -0,0 +1,753 @@ +/**************************************************************************** +** +** 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 "mftvideo.h" +#include "mfvideoprobecontrol.h" +#include <private/qmemoryvideobuffer_p.h> +#include <Mferror.h> +#include <strmif.h> +#include <uuids.h> +#include <InitGuid.h> +#include <d3d9.h> +#include <qdebug.h> + +// This MFT sends all samples it processes to connected video probes. +// Sample is sent to probes in ProcessInput. +// In ProcessOutput this MFT simply returns the original sample. + +// The implementation is based on a boilerplate from the MF SDK example. + +MFTransform::MFTransform(): + m_cRef(1), + m_inputType(0), + m_outputType(0), + m_sample(0), + m_videoSinkTypeHandler(0), + m_bytesPerLine(0) +{ +} + +MFTransform::~MFTransform() +{ + if (m_inputType) + m_inputType->Release(); + + if (m_outputType) + m_outputType->Release(); + + if (m_videoSinkTypeHandler) + m_videoSinkTypeHandler->Release(); +} + +void MFTransform::addProbe(MFVideoProbeControl *probe) +{ + QMutexLocker locker(&m_videoProbeMutex); + + if (m_videoProbes.contains(probe)) + return; + + m_videoProbes.append(probe); +} + +void MFTransform::removeProbe(MFVideoProbeControl *probe) +{ + QMutexLocker locker(&m_videoProbeMutex); + m_videoProbes.removeOne(probe); +} + +void MFTransform::setVideoSink(IUnknown *videoSink) +{ + // This transform supports the same input types as the video sink. + // Store its type handler interface in order to report the correct supported types. + + if (m_videoSinkTypeHandler) { + m_videoSinkTypeHandler->Release(); + m_videoSinkTypeHandler = NULL; + } + + if (videoSink) + videoSink->QueryInterface(IID_PPV_ARGS(&m_videoSinkTypeHandler)); +} + +STDMETHODIMP MFTransform::QueryInterface(REFIID riid, void** ppv) +{ + if (!ppv) + return E_POINTER; + if (riid == IID_IMFTransform) { + *ppv = static_cast<IMFTransform*>(this); + } else if (riid == IID_IUnknown) { + *ppv = static_cast<IUnknown*>(this); + } else { + *ppv = NULL; + return E_NOINTERFACE; + } + AddRef(); + return S_OK; +} + +STDMETHODIMP_(ULONG) MFTransform::AddRef() +{ + return InterlockedIncrement(&m_cRef); +} + +STDMETHODIMP_(ULONG) MFTransform::Release() +{ + ULONG cRef = InterlockedDecrement(&m_cRef); + if (cRef == 0) { + delete this; + } + return cRef; +} + +STDMETHODIMP MFTransform::GetStreamLimits(DWORD *pdwInputMinimum, DWORD *pdwInputMaximum, DWORD *pdwOutputMinimum, DWORD *pdwOutputMaximum) +{ + if (!pdwInputMinimum || !pdwInputMaximum || !pdwOutputMinimum || !pdwOutputMaximum) + return E_POINTER; + *pdwInputMinimum = 1; + *pdwInputMaximum = 1; + *pdwOutputMinimum = 1; + *pdwOutputMaximum = 1; + return S_OK; +} + +STDMETHODIMP MFTransform::GetStreamCount(DWORD *pcInputStreams, DWORD *pcOutputStreams) +{ + if (!pcInputStreams || !pcOutputStreams) + return E_POINTER; + + *pcInputStreams = 1; + *pcOutputStreams = 1; + return S_OK; +} + +STDMETHODIMP MFTransform::GetStreamIDs(DWORD dwInputIDArraySize, DWORD *pdwInputIDs, DWORD dwOutputIDArraySize, DWORD *pdwOutputIDs) +{ + // streams are numbered consecutively + Q_UNUSED(dwInputIDArraySize); + Q_UNUSED(pdwInputIDs); + Q_UNUSED(dwOutputIDArraySize); + Q_UNUSED(pdwOutputIDs); + return E_NOTIMPL; +} + +STDMETHODIMP MFTransform::GetInputStreamInfo(DWORD dwInputStreamID, MFT_INPUT_STREAM_INFO *pStreamInfo) +{ + QMutexLocker locker(&m_mutex); + + if (dwInputStreamID > 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (!pStreamInfo) + return E_POINTER; + + pStreamInfo->cbSize = 0; + pStreamInfo->hnsMaxLatency = 0; + pStreamInfo->cbMaxLookahead = 0; + pStreamInfo->cbAlignment = 0; + pStreamInfo->dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES + | MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER + | MFT_INPUT_STREAM_PROCESSES_IN_PLACE; + + return S_OK; +} + +STDMETHODIMP MFTransform::GetOutputStreamInfo(DWORD dwOutputStreamID, MFT_OUTPUT_STREAM_INFO *pStreamInfo) +{ + QMutexLocker locker(&m_mutex); + + if (dwOutputStreamID > 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (!pStreamInfo) + return E_POINTER; + + pStreamInfo->cbSize = 0; + pStreamInfo->cbAlignment = 0; + pStreamInfo->dwFlags = MFT_OUTPUT_STREAM_WHOLE_SAMPLES + | MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER + | MFT_OUTPUT_STREAM_PROVIDES_SAMPLES + | MFT_OUTPUT_STREAM_DISCARDABLE; + + return S_OK; +} + +STDMETHODIMP MFTransform::GetAttributes(IMFAttributes **pAttributes) +{ + // This MFT does not support attributes. + Q_UNUSED(pAttributes); + return E_NOTIMPL; +} + +STDMETHODIMP MFTransform::GetInputStreamAttributes(DWORD dwInputStreamID, IMFAttributes **pAttributes) +{ + // This MFT does not support input stream attributes. + Q_UNUSED(dwInputStreamID); + Q_UNUSED(pAttributes); + return E_NOTIMPL; +} + +STDMETHODIMP MFTransform::GetOutputStreamAttributes(DWORD dwOutputStreamID, IMFAttributes **pAttributes) +{ + // This MFT does not support output stream attributes. + Q_UNUSED(dwOutputStreamID); + Q_UNUSED(pAttributes); + return E_NOTIMPL; +} + +STDMETHODIMP MFTransform::DeleteInputStream(DWORD dwStreamID) +{ + // This MFT has a fixed number of input streams. + Q_UNUSED(dwStreamID); + return E_NOTIMPL; +} + +STDMETHODIMP MFTransform::AddInputStreams(DWORD cStreams, DWORD *adwStreamIDs) +{ + // This MFT has a fixed number of input streams. + Q_UNUSED(cStreams); + Q_UNUSED(adwStreamIDs); + return E_NOTIMPL; +} + +STDMETHODIMP MFTransform::GetInputAvailableType(DWORD dwInputStreamID, DWORD dwTypeIndex, IMFMediaType **ppType) +{ + // We support the same input types as the video sink + if (!m_videoSinkTypeHandler) + return E_NOTIMPL; + + if (dwInputStreamID > 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (!ppType) + return E_POINTER; + + return m_videoSinkTypeHandler->GetMediaTypeByIndex(dwTypeIndex, ppType); +} + +STDMETHODIMP MFTransform::GetOutputAvailableType(DWORD dwOutputStreamID, DWORD dwTypeIndex, IMFMediaType **ppType) +{ + // Since we don't modify the samples, the output type must be the same as the input type. + // Report our input type as the only available output type. + + if (dwOutputStreamID > 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (!ppType) + return E_POINTER; + + // Input type must be set first + if (!m_inputType) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + if (dwTypeIndex > 0) + return MF_E_NO_MORE_TYPES; + + // Return a copy to make sure our type is not modified + if (FAILED(MFCreateMediaType(ppType))) + return E_OUTOFMEMORY; + + return m_inputType->CopyAllItems(*ppType); +} + +STDMETHODIMP MFTransform::SetInputType(DWORD dwInputStreamID, IMFMediaType *pType, DWORD dwFlags) +{ + if (dwInputStreamID > 0) + return MF_E_INVALIDSTREAMNUMBER; + + QMutexLocker locker(&m_mutex); + + if (m_sample) + return MF_E_TRANSFORM_CANNOT_CHANGE_MEDIATYPE_WHILE_PROCESSING; + + if (!isMediaTypeSupported(pType)) + return MF_E_INVALIDMEDIATYPE; + + if (dwFlags == MFT_SET_TYPE_TEST_ONLY) + return pType ? S_OK : E_POINTER; + + if (m_inputType) { + m_inputType->Release(); + // Input type has changed, discard output type (if it's set) so it's reset later on + DWORD flags = 0; + if (m_outputType && m_outputType->IsEqual(pType, &flags) != S_OK) { + m_outputType->Release(); + m_outputType = 0; + } + } + + m_inputType = pType; + + if (m_inputType) + m_inputType->AddRef(); + + return S_OK; +} + +STDMETHODIMP MFTransform::SetOutputType(DWORD dwOutputStreamID, IMFMediaType *pType, DWORD dwFlags) +{ + if (dwOutputStreamID > 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (dwFlags == MFT_SET_TYPE_TEST_ONLY && !pType) + return E_POINTER; + + QMutexLocker locker(&m_mutex); + + // Input type must be set first + if (!m_inputType) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + if (m_sample) + return MF_E_TRANSFORM_CANNOT_CHANGE_MEDIATYPE_WHILE_PROCESSING; + + DWORD flags = 0; + if (pType && m_inputType->IsEqual(pType, &flags) != S_OK) + return MF_E_INVALIDMEDIATYPE; + + if (dwFlags == MFT_SET_TYPE_TEST_ONLY) + return pType ? S_OK : E_POINTER; + + if (m_outputType) + m_outputType->Release(); + + m_outputType = pType; + + if (m_outputType) { + m_outputType->AddRef(); + m_format = videoFormatForMFMediaType(m_outputType, &m_bytesPerLine); + } + + return S_OK; +} + +STDMETHODIMP MFTransform::GetInputCurrentType(DWORD dwInputStreamID, IMFMediaType **ppType) +{ + if (dwInputStreamID > 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (ppType == NULL) + return E_POINTER; + + QMutexLocker locker(&m_mutex); + + if (!m_inputType) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + // Return a copy to make sure our type is not modified + if (FAILED(MFCreateMediaType(ppType))) + return E_OUTOFMEMORY; + + return m_inputType->CopyAllItems(*ppType); +} + +STDMETHODIMP MFTransform::GetOutputCurrentType(DWORD dwOutputStreamID, IMFMediaType **ppType) +{ + if (dwOutputStreamID > 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (ppType == NULL) + return E_POINTER; + + QMutexLocker locker(&m_mutex); + + if (!m_outputType) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + // Return a copy to make sure our type is not modified + if (FAILED(MFCreateMediaType(ppType))) + return E_OUTOFMEMORY; + + return m_outputType->CopyAllItems(*ppType); +} + +STDMETHODIMP MFTransform::GetInputStatus(DWORD dwInputStreamID, DWORD *pdwFlags) +{ + if (dwInputStreamID > 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (!pdwFlags) + return E_POINTER; + + QMutexLocker locker(&m_mutex); + + if (!m_inputType || !m_outputType) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + if (m_sample) + *pdwFlags = 0; + else + *pdwFlags = MFT_INPUT_STATUS_ACCEPT_DATA; + + return S_OK; +} + +STDMETHODIMP MFTransform::GetOutputStatus(DWORD *pdwFlags) +{ + if (!pdwFlags) + return E_POINTER; + + QMutexLocker locker(&m_mutex); + + if (!m_inputType || !m_outputType) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + if (m_sample) + *pdwFlags = MFT_OUTPUT_STATUS_SAMPLE_READY; + else + *pdwFlags = 0; + + return S_OK; +} + +STDMETHODIMP MFTransform::SetOutputBounds(LONGLONG hnsLowerBound, LONGLONG hnsUpperBound) +{ + Q_UNUSED(hnsLowerBound); + Q_UNUSED(hnsUpperBound); + return E_NOTIMPL; +} + +STDMETHODIMP MFTransform::ProcessEvent(DWORD dwInputStreamID, IMFMediaEvent *pEvent) +{ + // This MFT ignores all events, and the pipeline should send all events downstream. + Q_UNUSED(dwInputStreamID); + Q_UNUSED(pEvent); + return E_NOTIMPL; +} + +STDMETHODIMP MFTransform::ProcessMessage(MFT_MESSAGE_TYPE eMessage, ULONG_PTR ulParam) +{ + Q_UNUSED(ulParam); + + HRESULT hr = S_OK; + + switch (eMessage) + { + case MFT_MESSAGE_COMMAND_FLUSH: + hr = OnFlush(); + break; + + case MFT_MESSAGE_COMMAND_DRAIN: + // Drain: Tells the MFT not to accept any more input until + // all of the pending output has been processed. That is our + // default behevior already, so there is nothing to do. + break; + + case MFT_MESSAGE_SET_D3D_MANAGER: + // The pipeline should never send this message unless the MFT + // has the MF_SA_D3D_AWARE attribute set to TRUE. However, if we + // do get this message, it's invalid and we don't implement it. + hr = E_NOTIMPL; + break; + + // The remaining messages do not require any action from this MFT. + case MFT_MESSAGE_NOTIFY_BEGIN_STREAMING: + case MFT_MESSAGE_NOTIFY_END_STREAMING: + case MFT_MESSAGE_NOTIFY_END_OF_STREAM: + case MFT_MESSAGE_NOTIFY_START_OF_STREAM: + break; + } + + return hr; +} + +STDMETHODIMP MFTransform::ProcessInput(DWORD dwInputStreamID, IMFSample *pSample, DWORD dwFlags) +{ + if (dwInputStreamID > 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (dwFlags != 0) + return E_INVALIDARG; // dwFlags is reserved and must be zero. + + QMutexLocker locker(&m_mutex); + + if (!m_inputType) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + if (m_sample) + return MF_E_NOTACCEPTING; + + // Validate the number of buffers. There should only be a single buffer to hold the video frame. + DWORD dwBufferCount = 0; + HRESULT hr = pSample->GetBufferCount(&dwBufferCount); + if (FAILED(hr)) + return hr; + + if (dwBufferCount == 0) + return E_FAIL; + + if (dwBufferCount > 1) + return MF_E_SAMPLE_HAS_TOO_MANY_BUFFERS; + + m_sample = pSample; + m_sample->AddRef(); + + QMutexLocker lockerProbe(&m_videoProbeMutex); + + if (!m_videoProbes.isEmpty()) { + QVideoFrame frame = makeVideoFrame(); + + for (MFVideoProbeControl* probe : qAsConst(m_videoProbes)) + probe->bufferProbed(frame); + } + + return S_OK; +} + +STDMETHODIMP MFTransform::ProcessOutput(DWORD dwFlags, DWORD cOutputBufferCount, MFT_OUTPUT_DATA_BUFFER *pOutputSamples, DWORD *pdwStatus) +{ + if (pOutputSamples == NULL || pdwStatus == NULL) + return E_POINTER; + + if (cOutputBufferCount != 1) + return E_INVALIDARG; + + QMutexLocker locker(&m_mutex); + + if (!m_inputType) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + if (!m_outputType) { + pOutputSamples[0].dwStatus = MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE; + return MF_E_TRANSFORM_STREAM_CHANGE; + } + + IMFMediaBuffer *input = NULL; + IMFMediaBuffer *output = NULL; + + if (dwFlags == MFT_PROCESS_OUTPUT_DISCARD_WHEN_NO_BUFFER) + goto done; + else if (dwFlags != 0) + return E_INVALIDARG; + + if (!m_sample) + return MF_E_TRANSFORM_NEED_MORE_INPUT; + + // Since the MFT_OUTPUT_STREAM_PROVIDES_SAMPLES flag is set, the client + // should not be providing samples here + if (pOutputSamples[0].pSample != NULL) + return E_INVALIDARG; + + pOutputSamples[0].pSample = m_sample; + pOutputSamples[0].pSample->AddRef(); + + // Send video frame to probes + // We do it here (instead of inside ProcessInput) to make sure samples discarded by the renderer + // are not sent. + m_videoProbeMutex.lock(); + if (!m_videoProbes.isEmpty()) { + QVideoFrame frame = makeVideoFrame(); + + foreach (MFVideoProbeControl* probe, m_videoProbes) + probe->bufferProbed(frame); + } + m_videoProbeMutex.unlock(); + +done: + pOutputSamples[0].dwStatus = 0; + *pdwStatus = 0; + + m_sample->Release(); + m_sample = 0; + + if (input) + input->Release(); + if (output) + output->Release(); + + return S_OK; +} + +HRESULT MFTransform::OnFlush() +{ + QMutexLocker locker(&m_mutex); + + if (m_sample) { + m_sample->Release(); + m_sample = 0; + } + return S_OK; +} + +QVideoFrame::PixelFormat MFTransform::formatFromSubtype(const GUID& subtype) +{ + if (subtype == MFVideoFormat_ARGB32) + return QVideoFrame::Format_ARGB32; + else if (subtype == MFVideoFormat_RGB32) + return QVideoFrame::Format_RGB32; + else if (subtype == MFVideoFormat_RGB24) + return QVideoFrame::Format_RGB24; + else if (subtype == MFVideoFormat_RGB565) + return QVideoFrame::Format_RGB565; + else if (subtype == MFVideoFormat_RGB555) + return QVideoFrame::Format_RGB555; + else if (subtype == MFVideoFormat_AYUV) + return QVideoFrame::Format_AYUV444; + else if (subtype == MFVideoFormat_I420) + return QVideoFrame::Format_YUV420P; + else if (subtype == MFVideoFormat_UYVY) + return QVideoFrame::Format_UYVY; + else if (subtype == MFVideoFormat_YV12) + return QVideoFrame::Format_YV12; + else if (subtype == MFVideoFormat_NV12) + return QVideoFrame::Format_NV12; + + return QVideoFrame::Format_Invalid; +} + +QVideoSurfaceFormat MFTransform::videoFormatForMFMediaType(IMFMediaType *mediaType, int *bytesPerLine) +{ + UINT32 stride; + if (FAILED(mediaType->GetUINT32(MF_MT_DEFAULT_STRIDE, &stride))) { + *bytesPerLine = 0; + return QVideoSurfaceFormat(); + } + + *bytesPerLine = (int)stride; + + QSize size; + UINT32 width, height; + if (FAILED(MFGetAttributeSize(mediaType, MF_MT_FRAME_SIZE, &width, &height))) + return QVideoSurfaceFormat(); + + size.setWidth(width); + size.setHeight(height); + + GUID subtype = GUID_NULL; + if (FAILED(mediaType->GetGUID(MF_MT_SUBTYPE, &subtype))) + return QVideoSurfaceFormat(); + + QVideoFrame::PixelFormat pixelFormat = formatFromSubtype(subtype); + QVideoSurfaceFormat format(size, pixelFormat); + + UINT32 num, den; + if (SUCCEEDED(MFGetAttributeRatio(mediaType, MF_MT_PIXEL_ASPECT_RATIO, &num, &den))) { + format.setPixelAspectRatio(num, den); + } + if (SUCCEEDED(MFGetAttributeRatio(mediaType, MF_MT_FRAME_RATE, &num, &den))) { + format.setFrameRate(qreal(num)/den); + } + + return format; +} + +QVideoFrame MFTransform::makeVideoFrame() +{ + QVideoFrame frame; + + if (!m_format.isValid()) + return frame; + + IMFMediaBuffer *buffer = 0; + + do { + if (FAILED(m_sample->ConvertToContiguousBuffer(&buffer))) + break; + + QByteArray array = dataFromBuffer(buffer, m_format.frameHeight(), &m_bytesPerLine); + if (array.isEmpty()) + break; + + // Wrapping IMFSample or IMFMediaBuffer in a QVideoFrame is not possible because we cannot hold + // IMFSample for a "long" time without affecting the rest of the topology. + // If IMFSample is held for more than 5 frames decoder starts to reuse it even though it hasn't been released it yet. + // That is why we copy data from IMFMediaBuffer here. + frame = QVideoFrame(new QMemoryVideoBuffer(array, m_bytesPerLine), m_format.frameSize(), m_format.pixelFormat()); + + // WMF uses 100-nanosecond units, Qt uses microseconds + LONGLONG startTime = -1; + if (SUCCEEDED(m_sample->GetSampleTime(&startTime))) { + frame.setStartTime(startTime * 0.1); + + LONGLONG duration = -1; + if (SUCCEEDED(m_sample->GetSampleDuration(&duration))) + frame.setEndTime((startTime + duration) * 0.1); + } + } while (false); + + if (buffer) + buffer->Release(); + + return frame; +} + +QByteArray MFTransform::dataFromBuffer(IMFMediaBuffer *buffer, int height, int *bytesPerLine) +{ + QByteArray array; + BYTE *bytes; + DWORD length; + HRESULT hr = buffer->Lock(&bytes, NULL, &length); + if (SUCCEEDED(hr)) { + array = QByteArray((const char *)bytes, (int)length); + buffer->Unlock(); + } else { + // try to lock as Direct3DSurface + IDirect3DSurface9 *surface = 0; + do { + if (FAILED(MFGetService(buffer, MR_BUFFER_SERVICE, IID_IDirect3DSurface9, (void**)&surface))) + break; + + D3DLOCKED_RECT rect; + if (FAILED(surface->LockRect(&rect, NULL, D3DLOCK_READONLY))) + break; + + if (bytesPerLine) + *bytesPerLine = (int)rect.Pitch; + + array = QByteArray((const char *)rect.pBits, rect.Pitch * height); + surface->UnlockRect(); + } while (false); + + if (surface) { + surface->Release(); + surface = 0; + } + } + + return array; +} + +bool MFTransform::isMediaTypeSupported(IMFMediaType *type) +{ + // If we don't have the video sink's type handler, + // assume it supports anything... + if (!m_videoSinkTypeHandler || !type) + return true; + + return m_videoSinkTypeHandler->IsMediaTypeSupported(type, NULL) == S_OK; +} diff --git a/src/plugins/wmf/player/mftvideo.h b/src/plugins/wmf/player/mftvideo.h new file mode 100644 index 000000000..ffcb80b32 --- /dev/null +++ b/src/plugins/wmf/player/mftvideo.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** 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 MFTRANSFORM_H +#define MFTRANSFORM_H + +#include <mfapi.h> +#include <mfidl.h> +#include <QtCore/qlist.h> +#include <QtCore/qmutex.h> +#include <QtMultimedia/qvideosurfaceformat.h> + +QT_USE_NAMESPACE + +class MFVideoProbeControl; + +class MFTransform: public IMFTransform +{ +public: + MFTransform(); + ~MFTransform(); + + void addProbe(MFVideoProbeControl* probe); + void removeProbe(MFVideoProbeControl* probe); + + void setVideoSink(IUnknown *videoSink); + + // IUnknown methods + STDMETHODIMP QueryInterface(REFIID iid, void** ppv); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + + // IMFTransform methods + STDMETHODIMP GetStreamLimits(DWORD *pdwInputMinimum, DWORD *pdwInputMaximum, DWORD *pdwOutputMinimum, DWORD *pdwOutputMaximum); + STDMETHODIMP GetStreamCount(DWORD *pcInputStreams, DWORD *pcOutputStreams); + STDMETHODIMP GetStreamIDs(DWORD dwInputIDArraySize, DWORD *pdwInputIDs, DWORD dwOutputIDArraySize, DWORD *pdwOutputIDs); + STDMETHODIMP GetInputStreamInfo(DWORD dwInputStreamID, MFT_INPUT_STREAM_INFO *pStreamInfo); + STDMETHODIMP GetOutputStreamInfo(DWORD dwOutputStreamID, MFT_OUTPUT_STREAM_INFO *pStreamInfo); + STDMETHODIMP GetAttributes(IMFAttributes **pAttributes); + STDMETHODIMP GetInputStreamAttributes(DWORD dwInputStreamID, IMFAttributes **pAttributes); + STDMETHODIMP GetOutputStreamAttributes(DWORD dwOutputStreamID, IMFAttributes **pAttributes); + STDMETHODIMP DeleteInputStream(DWORD dwStreamID); + STDMETHODIMP AddInputStreams(DWORD cStreams, DWORD *adwStreamIDs); + STDMETHODIMP GetInputAvailableType(DWORD dwInputStreamID, DWORD dwTypeIndex, IMFMediaType **ppType); + STDMETHODIMP GetOutputAvailableType(DWORD dwOutputStreamID,DWORD dwTypeIndex, IMFMediaType **ppType); + STDMETHODIMP SetInputType(DWORD dwInputStreamID, IMFMediaType *pType, DWORD dwFlags); + STDMETHODIMP SetOutputType(DWORD dwOutputStreamID, IMFMediaType *pType, DWORD dwFlags); + STDMETHODIMP GetInputCurrentType(DWORD dwInputStreamID, IMFMediaType **ppType); + STDMETHODIMP GetOutputCurrentType(DWORD dwOutputStreamID, IMFMediaType **ppType); + STDMETHODIMP GetInputStatus(DWORD dwInputStreamID, DWORD *pdwFlags); + STDMETHODIMP GetOutputStatus(DWORD *pdwFlags); + STDMETHODIMP SetOutputBounds(LONGLONG hnsLowerBound, LONGLONG hnsUpperBound); + STDMETHODIMP ProcessEvent(DWORD dwInputStreamID, IMFMediaEvent *pEvent); + STDMETHODIMP ProcessMessage(MFT_MESSAGE_TYPE eMessage, ULONG_PTR ulParam); + STDMETHODIMP ProcessInput(DWORD dwInputStreamID, IMFSample *pSample, DWORD dwFlags); + STDMETHODIMP ProcessOutput(DWORD dwFlags, DWORD cOutputBufferCount, MFT_OUTPUT_DATA_BUFFER *pOutputSamples, DWORD *pdwStatus); + +private: + HRESULT OnFlush(); + static QVideoFrame::PixelFormat formatFromSubtype(const GUID& subtype); + static QVideoSurfaceFormat videoFormatForMFMediaType(IMFMediaType *mediaType, int *bytesPerLine); + QVideoFrame makeVideoFrame(); + QByteArray dataFromBuffer(IMFMediaBuffer *buffer, int height, int *bytesPerLine); + bool isMediaTypeSupported(IMFMediaType *type); + + long m_cRef; + IMFMediaType *m_inputType; + IMFMediaType *m_outputType; + IMFSample *m_sample; + QMutex m_mutex; + + IMFMediaTypeHandler *m_videoSinkTypeHandler; + + QList<MFVideoProbeControl*> m_videoProbes; + QMutex m_videoProbeMutex; + + QVideoSurfaceFormat m_format; + int m_bytesPerLine; +}; + +#endif diff --git a/src/plugins/wmf/player/player.pri b/src/plugins/wmf/player/player.pri index c24370eea..38ccb82a6 100644 --- a/src/plugins/wmf/player/player.pri +++ b/src/plugins/wmf/player/player.pri @@ -1,6 +1,7 @@ INCLUDEPATH += $$PWD -LIBS += -lstrmiids -ldmoguids -luuid -lmsdmo -lgdi32 -luser32 -lole32 -loleaut32 -lMf -lMfuuid -lMfplat -lPropsys +LIBS += -lgdi32 -luser32 +QMAKE_USE += wmf DEFINES += QMEDIA_MEDIAFOUNDATION_PLAYER @@ -13,7 +14,10 @@ HEADERS += \ $$PWD/mfmetadatacontrol.h \ $$PWD/mfaudioprobecontrol.h \ $$PWD/mfvideoprobecontrol.h \ - $$PWD/mfevrvideowindowcontrol.h + $$PWD/mfevrvideowindowcontrol.h \ + $$PWD/samplegrabber.h \ + $$PWD/mftvideo.h \ + $$PWD/mfactivate.h SOURCES += \ $$PWD/mfplayerservice.cpp \ @@ -24,6 +28,9 @@ SOURCES += \ $$PWD/mfmetadatacontrol.cpp \ $$PWD/mfaudioprobecontrol.cpp \ $$PWD/mfvideoprobecontrol.cpp \ - $$PWD/mfevrvideowindowcontrol.cpp + $$PWD/mfevrvideowindowcontrol.cpp \ + $$PWD/samplegrabber.cpp \ + $$PWD/mftvideo.cpp \ + $$PWD/mfactivate.cpp include($$PWD/../../common/evr.pri) diff --git a/src/plugins/wmf/player/samplegrabber.cpp b/src/plugins/wmf/player/samplegrabber.cpp new file mode 100644 index 000000000..d137335f3 --- /dev/null +++ b/src/plugins/wmf/player/samplegrabber.cpp @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** 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 "samplegrabber.h" +#include "mfaudioprobecontrol.h" + +STDMETHODIMP SampleGrabberCallback::QueryInterface(REFIID riid, void** ppv) +{ + if (!ppv) + return E_POINTER; + if (riid == IID_IMFSampleGrabberSinkCallback) { + *ppv = static_cast<IMFSampleGrabberSinkCallback*>(this); + } else if (riid == IID_IMFClockStateSink) { + *ppv = static_cast<IMFClockStateSink*>(this); + } else if (riid == IID_IUnknown) { + *ppv = static_cast<IUnknown*>(this); + } else { + *ppv = NULL; + return E_NOINTERFACE; + } + AddRef(); + return S_OK; +} + +STDMETHODIMP_(ULONG) SampleGrabberCallback::AddRef() +{ + return InterlockedIncrement(&m_cRef); +} + +STDMETHODIMP_(ULONG) SampleGrabberCallback::Release() +{ + ULONG cRef = InterlockedDecrement(&m_cRef); + if (cRef == 0) { + delete this; + } + return cRef; + +} + +// IMFClockStateSink methods. + +STDMETHODIMP SampleGrabberCallback::OnClockStart(MFTIME hnsSystemTime, LONGLONG llClockStartOffset) +{ + Q_UNUSED(hnsSystemTime); + Q_UNUSED(llClockStartOffset); + return S_OK; +} + +STDMETHODIMP SampleGrabberCallback::OnClockStop(MFTIME hnsSystemTime) +{ + Q_UNUSED(hnsSystemTime); + return S_OK; +} + +STDMETHODIMP SampleGrabberCallback::OnClockPause(MFTIME hnsSystemTime) +{ + Q_UNUSED(hnsSystemTime); + return S_OK; +} + +STDMETHODIMP SampleGrabberCallback::OnClockRestart(MFTIME hnsSystemTime) +{ + Q_UNUSED(hnsSystemTime); + return S_OK; +} + +STDMETHODIMP SampleGrabberCallback::OnClockSetRate(MFTIME hnsSystemTime, float flRate) +{ + Q_UNUSED(hnsSystemTime); + Q_UNUSED(flRate); + return S_OK; +} + +// IMFSampleGrabberSink methods. + +STDMETHODIMP SampleGrabberCallback::OnSetPresentationClock(IMFPresentationClock* pClock) +{ + Q_UNUSED(pClock); + return S_OK; +} + +STDMETHODIMP SampleGrabberCallback::OnShutdown() +{ + return S_OK; +} + +void AudioSampleGrabberCallback::addProbe(MFAudioProbeControl* probe) +{ + QMutexLocker locker(&m_audioProbeMutex); + + if (m_audioProbes.contains(probe)) + return; + + m_audioProbes.append(probe); +} + +void AudioSampleGrabberCallback::removeProbe(MFAudioProbeControl* probe) +{ + QMutexLocker locker(&m_audioProbeMutex); + m_audioProbes.removeOne(probe); +} + +void AudioSampleGrabberCallback::setFormat(const QAudioFormat& format) +{ + m_format = format; +} + +STDMETHODIMP AudioSampleGrabberCallback::OnProcessSample(REFGUID guidMajorMediaType, DWORD dwSampleFlags, + LONGLONG llSampleTime, LONGLONG llSampleDuration, const BYTE * pSampleBuffer, + DWORD dwSampleSize) +{ + Q_UNUSED(dwSampleFlags); + Q_UNUSED(llSampleTime); + Q_UNUSED(llSampleDuration); + + if (guidMajorMediaType != GUID_NULL && guidMajorMediaType != MFMediaType_Audio) + return S_OK; + + QMutexLocker locker(&m_audioProbeMutex); + + if (m_audioProbes.isEmpty()) + return S_OK; + + // Check if sample has a presentation time + if (llSampleTime == _I64_MAX) { + // Set default QAudioBuffer start time + llSampleTime = -1; + } else { + // WMF uses 100-nanosecond units, Qt uses microseconds + llSampleTime /= 10; + } + + for (MFAudioProbeControl* probe : qAsConst(m_audioProbes)) + probe->bufferProbed((const char*)pSampleBuffer, dwSampleSize, m_format, llSampleTime); + + return S_OK; +} diff --git a/src/plugins/wmf/player/samplegrabber.h b/src/plugins/wmf/player/samplegrabber.h new file mode 100644 index 000000000..9ca673a1b --- /dev/null +++ b/src/plugins/wmf/player/samplegrabber.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** 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 SAMPLEGRABBER_H +#define SAMPLEGRABBER_H + +#include <QtCore/qmutex.h> +#include <QtCore/qlist.h> +#include <QtMultimedia/qaudioformat.h> +#include <mfapi.h> +#include <mfidl.h> + +class MFAudioProbeControl; + +class SampleGrabberCallback : public IMFSampleGrabberSinkCallback +{ +public: + // IUnknown methods + STDMETHODIMP QueryInterface(REFIID iid, void** ppv); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + + // IMFClockStateSink methods + STDMETHODIMP OnClockStart(MFTIME hnsSystemTime, LONGLONG llClockStartOffset); + STDMETHODIMP OnClockStop(MFTIME hnsSystemTime); + STDMETHODIMP OnClockPause(MFTIME hnsSystemTime); + STDMETHODIMP OnClockRestart(MFTIME hnsSystemTime); + STDMETHODIMP OnClockSetRate(MFTIME hnsSystemTime, float flRate); + + // IMFSampleGrabberSinkCallback methods + STDMETHODIMP OnSetPresentationClock(IMFPresentationClock* pClock); + STDMETHODIMP OnShutdown(); + +protected: + SampleGrabberCallback() : m_cRef(1) {} + +public: + virtual ~SampleGrabberCallback() {} + +private: + long m_cRef; +}; + +class AudioSampleGrabberCallback: public SampleGrabberCallback { +public: + void addProbe(MFAudioProbeControl* probe); + void removeProbe(MFAudioProbeControl* probe); + void setFormat(const QAudioFormat& format); + + STDMETHODIMP OnProcessSample(REFGUID guidMajorMediaType, DWORD dwSampleFlags, + LONGLONG llSampleTime, LONGLONG llSampleDuration, const BYTE * pSampleBuffer, + DWORD dwSampleSize); + +private: + QList<MFAudioProbeControl*> m_audioProbes; + QMutex m_audioProbeMutex; + QAudioFormat m_format; +}; + +#endif // SAMPLEGRABBER_H |