mirror of https://github.com/opencv/opencv.git
Open Source Computer Vision Library
https://opencv.org/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1486 lines
37 KiB
1486 lines
37 KiB
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF |
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO |
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A |
|
// PARTICULAR PURPOSE. |
|
// |
|
// Copyright (c) Microsoft Corporation. All rights reserved. |
|
|
|
#include "OcvTransform.h" |
|
#include "bufferlock.h" |
|
|
|
#include "opencv2\core\core.hpp" |
|
#include "opencv2\imgproc\imgproc.hpp" |
|
|
|
using namespace Microsoft::WRL; |
|
|
|
/* |
|
|
|
This sample implements a video effect as a Media Foundation transform (MFT). |
|
|
|
NOTES ON THE MFT IMPLEMENTATION |
|
|
|
1. The MFT has fixed streams: One input stream and one output stream. |
|
|
|
2. The MFT supports NV12 format only. |
|
|
|
3. If the MFT is holding an input sample, SetInputType and SetOutputType both fail. |
|
|
|
4. The input and output types must be identical. |
|
|
|
5. If both types are set, no type can be set until the current type is cleared. |
|
|
|
6. Preferred input types: |
|
|
|
(a) If the output type is set, that's the preferred type. |
|
(b) Otherwise, the preferred types are partial types, constructed from the |
|
list of supported subtypes. |
|
|
|
7. Preferred output types: As above. |
|
|
|
8. Streaming: |
|
|
|
The private BeingStreaming() method is called in response to the |
|
MFT_MESSAGE_NOTIFY_BEGIN_STREAMING message. |
|
|
|
If the client does not send MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, the MFT calls |
|
BeginStreaming inside the first call to ProcessInput or ProcessOutput. |
|
|
|
This is a good approach for allocating resources that your MFT requires for |
|
streaming. |
|
|
|
9. The configuration attributes are applied in the BeginStreaming method. If the |
|
client changes the attributes during streaming, the change is ignored until |
|
streaming is stopped (either by changing the media types or by sending the |
|
MFT_MESSAGE_NOTIFY_END_STREAMING message) and then restarted. |
|
|
|
*/ |
|
|
|
|
|
// Static array of media types (preferred and accepted). |
|
const GUID g_MediaSubtypes[] = |
|
{ |
|
MFVideoFormat_NV12 |
|
}; |
|
|
|
HRESULT GetDefaultStride(IMFMediaType *pType, LONG *plStride); |
|
|
|
template <typename T> |
|
inline T clamp(const T& val, const T& minVal, const T& maxVal) |
|
{ |
|
return (val < minVal ? minVal : (val > maxVal ? maxVal : val)); |
|
} |
|
|
|
OcvImageManipulations::OcvImageManipulations() : |
|
m_pSample(NULL), m_pInputType(NULL), m_pOutputType(NULL), |
|
m_imageWidthInPixels(0), m_imageHeightInPixels(0), m_cbImageSize(0), |
|
m_TransformType(Preview), m_bStreamingInitialized(false), |
|
m_pAttributes(NULL) |
|
{ |
|
InitializeCriticalSectionEx(&m_critSec, 3000, 0); |
|
} |
|
|
|
OcvImageManipulations::~OcvImageManipulations() |
|
{ |
|
SafeRelease(&m_pInputType); |
|
SafeRelease(&m_pOutputType); |
|
SafeRelease(&m_pSample); |
|
SafeRelease(&m_pAttributes); |
|
DeleteCriticalSection(&m_critSec); |
|
} |
|
|
|
// Initialize the instance. |
|
STDMETHODIMP OcvImageManipulations::RuntimeClassInitialize() |
|
{ |
|
// Create the attribute store. |
|
return MFCreateAttributes(&m_pAttributes, 3); |
|
} |
|
|
|
// IMediaExtension methods |
|
|
|
//------------------------------------------------------------------- |
|
// SetProperties |
|
// Sets the configuration of the effect |
|
//------------------------------------------------------------------- |
|
HRESULT OcvImageManipulations::SetProperties(ABI::Windows::Foundation::Collections::IPropertySet *pConfiguration) |
|
{ |
|
HRESULT hr = S_OK; |
|
|
|
if (!pConfiguration) |
|
return hr; |
|
|
|
HSTRING key; |
|
WindowsCreateString(L"{698649BE-8EAE-4551-A4CB-3EC98FBD3D86}", 38, &key); |
|
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::Collections::IMap<HSTRING, IInspectable *>> spSetting; |
|
pConfiguration->QueryInterface(IID_PPV_ARGS(&spSetting)); |
|
boolean found; |
|
spSetting->HasKey(key, &found); |
|
|
|
if (found) |
|
{ |
|
IInspectable* value; |
|
spSetting->Lookup(key, &value); |
|
|
|
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IReference<int>> ref; |
|
hr = value->QueryInterface(IID_PPV_ARGS(&ref)); |
|
int effect = InvalidEffect; |
|
hr = ref->get_Value(&effect); |
|
if ((effect >= 0) && (effect < InvalidEffect)) |
|
{ |
|
m_TransformType = (ProcessingType)effect; |
|
} |
|
} |
|
|
|
return hr; |
|
} |
|
|
|
// IMFTransform methods. Refer to the Media Foundation SDK documentation for details. |
|
|
|
//------------------------------------------------------------------- |
|
// GetStreamLimits |
|
// Returns the minimum and maximum number of streams. |
|
//------------------------------------------------------------------- |
|
|
|
HRESULT OcvImageManipulations::GetStreamLimits( |
|
DWORD *pdwInputMinimum, |
|
DWORD *pdwInputMaximum, |
|
DWORD *pdwOutputMinimum, |
|
DWORD *pdwOutputMaximum |
|
) |
|
{ |
|
if ((pdwInputMinimum == NULL) || |
|
(pdwInputMaximum == NULL) || |
|
(pdwOutputMinimum == NULL) || |
|
(pdwOutputMaximum == NULL)) |
|
{ |
|
return E_POINTER; |
|
} |
|
|
|
// This MFT has a fixed number of streams. |
|
*pdwInputMinimum = 1; |
|
*pdwInputMaximum = 1; |
|
*pdwOutputMinimum = 1; |
|
*pdwOutputMaximum = 1; |
|
return S_OK; |
|
} |
|
|
|
|
|
//------------------------------------------------------------------- |
|
// GetStreamCount |
|
// Returns the actual number of streams. |
|
//------------------------------------------------------------------- |
|
|
|
HRESULT OcvImageManipulations::GetStreamCount( |
|
DWORD *pcInputStreams, |
|
DWORD *pcOutputStreams |
|
) |
|
{ |
|
if ((pcInputStreams == NULL) || (pcOutputStreams == NULL)) |
|
|
|
{ |
|
return E_POINTER; |
|
} |
|
|
|
// This MFT has a fixed number of streams. |
|
*pcInputStreams = 1; |
|
*pcOutputStreams = 1; |
|
return S_OK; |
|
} |
|
|
|
|
|
|
|
//------------------------------------------------------------------- |
|
// GetStreamIDs |
|
// Returns stream IDs for the input and output streams. |
|
//------------------------------------------------------------------- |
|
|
|
HRESULT OcvImageManipulations::GetStreamIDs( |
|
DWORD dwInputIDArraySize, |
|
DWORD *pdwInputIDs, |
|
DWORD dwOutputIDArraySize, |
|
DWORD *pdwOutputIDs |
|
) |
|
{ |
|
// It is not required to implement this method if the MFT has a fixed number of |
|
// streams AND the stream IDs are numbered sequentially from zero (that is, the |
|
// stream IDs match the stream indexes). |
|
|
|
// In that case, it is OK to return E_NOTIMPL. |
|
return E_NOTIMPL; |
|
} |
|
|
|
|
|
//------------------------------------------------------------------- |
|
// GetInputStreamInfo |
|
// Returns information about an input stream. |
|
//------------------------------------------------------------------- |
|
|
|
HRESULT OcvImageManipulations::GetInputStreamInfo( |
|
DWORD dwInputStreamID, |
|
MFT_INPUT_STREAM_INFO * pStreamInfo |
|
) |
|
{ |
|
if (pStreamInfo == NULL) |
|
{ |
|
return E_POINTER; |
|
} |
|
|
|
EnterCriticalSection(&m_critSec); |
|
|
|
if (!IsValidInputStream(dwInputStreamID)) |
|
{ |
|
LeaveCriticalSection(&m_critSec); |
|
return MF_E_INVALIDSTREAMNUMBER; |
|
} |
|
|
|
// NOTE: This method should succeed even when there is no media type on the |
|
// stream. If there is no media type, we only need to fill in the dwFlags |
|
// member of MFT_INPUT_STREAM_INFO. The other members depend on having a |
|
// a valid media type. |
|
|
|
pStreamInfo->hnsMaxLatency = 0; |
|
pStreamInfo->dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES | MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER; |
|
|
|
if (m_pInputType == NULL) |
|
{ |
|
pStreamInfo->cbSize = 0; |
|
} |
|
else |
|
{ |
|
pStreamInfo->cbSize = m_cbImageSize; |
|
} |
|
|
|
pStreamInfo->cbMaxLookahead = 0; |
|
pStreamInfo->cbAlignment = 0; |
|
|
|
LeaveCriticalSection(&m_critSec); |
|
return S_OK; |
|
} |
|
|
|
//------------------------------------------------------------------- |
|
// GetOutputStreamInfo |
|
// Returns information about an output stream. |
|
//------------------------------------------------------------------- |
|
|
|
HRESULT OcvImageManipulations::GetOutputStreamInfo( |
|
DWORD dwOutputStreamID, |
|
MFT_OUTPUT_STREAM_INFO * pStreamInfo |
|
) |
|
{ |
|
if (pStreamInfo == NULL) |
|
{ |
|
return E_POINTER; |
|
} |
|
|
|
EnterCriticalSection(&m_critSec); |
|
|
|
if (!IsValidOutputStream(dwOutputStreamID)) |
|
{ |
|
LeaveCriticalSection(&m_critSec); |
|
return MF_E_INVALIDSTREAMNUMBER; |
|
} |
|
|
|
// NOTE: This method should succeed even when there is no media type on the |
|
// stream. If there is no media type, we only need to fill in the dwFlags |
|
// member of MFT_OUTPUT_STREAM_INFO. The other members depend on having a |
|
// a valid media type. |
|
|
|
pStreamInfo->dwFlags = |
|
MFT_OUTPUT_STREAM_WHOLE_SAMPLES | |
|
MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | |
|
MFT_OUTPUT_STREAM_FIXED_SAMPLE_SIZE ; |
|
|
|
if (m_pOutputType == NULL) |
|
{ |
|
pStreamInfo->cbSize = 0; |
|
} |
|
else |
|
{ |
|
pStreamInfo->cbSize = m_cbImageSize; |
|
} |
|
|
|
pStreamInfo->cbAlignment = 0; |
|
|
|
LeaveCriticalSection(&m_critSec); |
|
return S_OK; |
|
} |
|
|
|
|
|
//------------------------------------------------------------------- |
|
// GetAttributes |
|
// Returns the attributes for the MFT. |
|
//------------------------------------------------------------------- |
|
|
|
HRESULT OcvImageManipulations::GetAttributes(IMFAttributes** ppAttributes) |
|
{ |
|
if (ppAttributes == NULL) |
|
{ |
|
return E_POINTER; |
|
} |
|
|
|
EnterCriticalSection(&m_critSec); |
|
|
|
*ppAttributes = m_pAttributes; |
|
(*ppAttributes)->AddRef(); |
|
|
|
LeaveCriticalSection(&m_critSec); |
|
return S_OK; |
|
} |
|
|
|
|
|
//------------------------------------------------------------------- |
|
// GetInputStreamAttributes |
|
// Returns stream-level attributes for an input stream. |
|
//------------------------------------------------------------------- |
|
|
|
HRESULT OcvImageManipulations::GetInputStreamAttributes( |
|
DWORD dwInputStreamID, |
|
IMFAttributes **ppAttributes |
|
) |
|
{ |
|
// This MFT does not support any stream-level attributes, so the method is not implemented. |
|
return E_NOTIMPL; |
|
} |
|
|
|
|
|
//------------------------------------------------------------------- |
|
// GetOutputStreamAttributes |
|
// Returns stream-level attributes for an output stream. |
|
//------------------------------------------------------------------- |
|
|
|
HRESULT OcvImageManipulations::GetOutputStreamAttributes( |
|
DWORD dwOutputStreamID, |
|
IMFAttributes **ppAttributes |
|
) |
|
{ |
|
// This MFT does not support any stream-level attributes, so the method is not implemented. |
|
return E_NOTIMPL; |
|
} |
|
|
|
|
|
//------------------------------------------------------------------- |
|
// DeleteInputStream |
|
//------------------------------------------------------------------- |
|
|
|
HRESULT OcvImageManipulations::DeleteInputStream(DWORD dwStreamID) |
|
{ |
|
// This MFT has a fixed number of input streams, so the method is not supported. |
|
return E_NOTIMPL; |
|
} |
|
|
|
|
|
//------------------------------------------------------------------- |
|
// AddInputStreams |
|
//------------------------------------------------------------------- |
|
|
|
HRESULT OcvImageManipulations::AddInputStreams( |
|
DWORD cStreams, |
|
DWORD *adwStreamIDs |
|
) |
|
{ |
|
// This MFT has a fixed number of output streams, so the method is not supported. |
|
return E_NOTIMPL; |
|
} |
|
|
|
|
|
//------------------------------------------------------------------- |
|
// GetInputAvailableType |
|
// Returns a preferred input type. |
|
//------------------------------------------------------------------- |
|
|
|
HRESULT OcvImageManipulations::GetInputAvailableType( |
|
DWORD dwInputStreamID, |
|
DWORD dwTypeIndex, // 0-based |
|
IMFMediaType **ppType |
|
) |
|
{ |
|
if (ppType == NULL) |
|
{ |
|
return E_INVALIDARG; |
|
} |
|
|
|
EnterCriticalSection(&m_critSec); |
|
|
|
if (!IsValidInputStream(dwInputStreamID)) |
|
{ |
|
LeaveCriticalSection(&m_critSec); |
|
return MF_E_INVALIDSTREAMNUMBER; |
|
} |
|
|
|
HRESULT hr = S_OK; |
|
|
|
// If the output type is set, return that type as our preferred input type. |
|
if (m_pOutputType == NULL) |
|
{ |
|
// The output type is not set. Create a partial media type. |
|
hr = OnGetPartialType(dwTypeIndex, ppType); |
|
} |
|
else if (dwTypeIndex > 0) |
|
{ |
|
hr = MF_E_NO_MORE_TYPES; |
|
} |
|
else |
|
{ |
|
*ppType = m_pOutputType; |
|
(*ppType)->AddRef(); |
|
} |
|
|
|
LeaveCriticalSection(&m_critSec); |
|
return hr; |
|
} |
|
|
|
|
|
|
|
//------------------------------------------------------------------- |
|
// GetOutputAvailableType |
|
// Returns a preferred output type. |
|
//------------------------------------------------------------------- |
|
|
|
HRESULT OcvImageManipulations::GetOutputAvailableType( |
|
DWORD dwOutputStreamID, |
|
DWORD dwTypeIndex, // 0-based |
|
IMFMediaType **ppType |
|
) |
|
{ |
|
if (ppType == NULL) |
|
{ |
|
return E_INVALIDARG; |
|
} |
|
|
|
EnterCriticalSection(&m_critSec); |
|
|
|
if (!IsValidOutputStream(dwOutputStreamID)) |
|
{ |
|
LeaveCriticalSection(&m_critSec); |
|
return MF_E_INVALIDSTREAMNUMBER; |
|
} |
|
|
|
HRESULT hr = S_OK; |
|
|
|
if (m_pInputType == NULL) |
|
{ |
|
// The input type is not set. Create a partial media type. |
|
hr = OnGetPartialType(dwTypeIndex, ppType); |
|
} |
|
else if (dwTypeIndex > 0) |
|
{ |
|
hr = MF_E_NO_MORE_TYPES; |
|
} |
|
else |
|
{ |
|
*ppType = m_pInputType; |
|
(*ppType)->AddRef(); |
|
} |
|
|
|
LeaveCriticalSection(&m_critSec); |
|
return hr; |
|
} |
|
|
|
|
|
//------------------------------------------------------------------- |
|
// SetInputType |
|
//------------------------------------------------------------------- |
|
|
|
HRESULT OcvImageManipulations::SetInputType( |
|
DWORD dwInputStreamID, |
|
IMFMediaType *pType, // Can be NULL to clear the input type. |
|
DWORD dwFlags |
|
) |
|
{ |
|
// Validate flags. |
|
if (dwFlags & ~MFT_SET_TYPE_TEST_ONLY) |
|
{ |
|
return E_INVALIDARG; |
|
} |
|
|
|
EnterCriticalSection(&m_critSec); |
|
|
|
if (!IsValidInputStream(dwInputStreamID)) |
|
{ |
|
LeaveCriticalSection(&m_critSec); |
|
return MF_E_INVALIDSTREAMNUMBER; |
|
} |
|
|
|
HRESULT hr = S_OK; |
|
|
|
// Does the caller want us to set the type, or just test it? |
|
BOOL bReallySet = ((dwFlags & MFT_SET_TYPE_TEST_ONLY) == 0); |
|
|
|
// If we have an input sample, the client cannot change the type now. |
|
if (HasPendingOutput()) |
|
{ |
|
hr = MF_E_TRANSFORM_CANNOT_CHANGE_MEDIATYPE_WHILE_PROCESSING; |
|
goto done; |
|
} |
|
|
|
// Validate the type, if non-NULL. |
|
if (pType) |
|
{ |
|
hr = OnCheckInputType(pType); |
|
if (FAILED(hr)) |
|
{ |
|
goto done; |
|
} |
|
} |
|
|
|
// The type is OK. Set the type, unless the caller was just testing. |
|
if (bReallySet) |
|
{ |
|
OnSetInputType(pType); |
|
|
|
// When the type changes, end streaming. |
|
hr = EndStreaming(); |
|
} |
|
|
|
done: |
|
LeaveCriticalSection(&m_critSec); |
|
return hr; |
|
} |
|
|
|
|
|
|
|
//------------------------------------------------------------------- |
|
// SetOutputType |
|
//------------------------------------------------------------------- |
|
|
|
HRESULT OcvImageManipulations::SetOutputType( |
|
DWORD dwOutputStreamID, |
|
IMFMediaType *pType, // Can be NULL to clear the output type. |
|
DWORD dwFlags |
|
) |
|
{ |
|
// Validate flags. |
|
if (dwFlags & ~MFT_SET_TYPE_TEST_ONLY) |
|
{ |
|
return E_INVALIDARG; |
|
} |
|
|
|
EnterCriticalSection(&m_critSec); |
|
|
|
if (!IsValidOutputStream(dwOutputStreamID)) |
|
{ |
|
LeaveCriticalSection(&m_critSec); |
|
return MF_E_INVALIDSTREAMNUMBER; |
|
} |
|
|
|
HRESULT hr = S_OK; |
|
|
|
// Does the caller want us to set the type, or just test it? |
|
BOOL bReallySet = ((dwFlags & MFT_SET_TYPE_TEST_ONLY) == 0); |
|
|
|
// If we have an input sample, the client cannot change the type now. |
|
if (HasPendingOutput()) |
|
{ |
|
hr = MF_E_TRANSFORM_CANNOT_CHANGE_MEDIATYPE_WHILE_PROCESSING; |
|
goto done; |
|
} |
|
|
|
// Validate the type, if non-NULL. |
|
if (pType) |
|
{ |
|
hr = OnCheckOutputType(pType); |
|
if (FAILED(hr)) |
|
{ |
|
goto done; |
|
} |
|
} |
|
|
|
// The type is OK. Set the type, unless the caller was just testing. |
|
if (bReallySet) |
|
{ |
|
OnSetOutputType(pType); |
|
|
|
// When the type changes, end streaming. |
|
hr = EndStreaming(); |
|
} |
|
|
|
done: |
|
LeaveCriticalSection(&m_critSec); |
|
return hr; |
|
} |
|
|
|
|
|
//------------------------------------------------------------------- |
|
// GetInputCurrentType |
|
// Returns the current input type. |
|
//------------------------------------------------------------------- |
|
|
|
HRESULT OcvImageManipulations::GetInputCurrentType( |
|
DWORD dwInputStreamID, |
|
IMFMediaType **ppType |
|
) |
|
{ |
|
if (ppType == NULL) |
|
{ |
|
return E_POINTER; |
|
} |
|
|
|
HRESULT hr = S_OK; |
|
|
|
EnterCriticalSection(&m_critSec); |
|
|
|
if (!IsValidInputStream(dwInputStreamID)) |
|
{ |
|
hr = MF_E_INVALIDSTREAMNUMBER; |
|
} |
|
else if (!m_pInputType) |
|
{ |
|
hr = MF_E_TRANSFORM_TYPE_NOT_SET; |
|
} |
|
else |
|
{ |
|
*ppType = m_pInputType; |
|
(*ppType)->AddRef(); |
|
} |
|
LeaveCriticalSection(&m_critSec); |
|
return hr; |
|
} |
|
|
|
|
|
//------------------------------------------------------------------- |
|
// GetOutputCurrentType |
|
// Returns the current output type. |
|
//------------------------------------------------------------------- |
|
|
|
HRESULT OcvImageManipulations::GetOutputCurrentType( |
|
DWORD dwOutputStreamID, |
|
IMFMediaType **ppType |
|
) |
|
{ |
|
if (ppType == NULL) |
|
{ |
|
return E_POINTER; |
|
} |
|
|
|
HRESULT hr = S_OK; |
|
|
|
EnterCriticalSection(&m_critSec); |
|
|
|
if (!IsValidOutputStream(dwOutputStreamID)) |
|
{ |
|
hr = MF_E_INVALIDSTREAMNUMBER; |
|
} |
|
else if (!m_pOutputType) |
|
{ |
|
hr = MF_E_TRANSFORM_TYPE_NOT_SET; |
|
} |
|
else |
|
{ |
|
*ppType = m_pOutputType; |
|
(*ppType)->AddRef(); |
|
} |
|
|
|
LeaveCriticalSection(&m_critSec); |
|
return hr; |
|
} |
|
|
|
|
|
//------------------------------------------------------------------- |
|
// GetInputStatus |
|
// Query if the MFT is accepting more input. |
|
//------------------------------------------------------------------- |
|
|
|
HRESULT OcvImageManipulations::GetInputStatus( |
|
DWORD dwInputStreamID, |
|
DWORD *pdwFlags |
|
) |
|
{ |
|
if (pdwFlags == NULL) |
|
{ |
|
return E_POINTER; |
|
} |
|
|
|
EnterCriticalSection(&m_critSec); |
|
|
|
if (!IsValidInputStream(dwInputStreamID)) |
|
{ |
|
LeaveCriticalSection(&m_critSec); |
|
return MF_E_INVALIDSTREAMNUMBER; |
|
} |
|
|
|
// If an input sample is already queued, do not accept another sample until the |
|
// client calls ProcessOutput or Flush. |
|
|
|
// NOTE: It is possible for an MFT to accept more than one input sample. For |
|
// example, this might be required in a video decoder if the frames do not |
|
// arrive in temporal order. In the case, the decoder must hold a queue of |
|
// samples. For the video effect, each sample is transformed independently, so |
|
// there is no reason to queue multiple input samples. |
|
|
|
if (m_pSample == NULL) |
|
{ |
|
*pdwFlags = MFT_INPUT_STATUS_ACCEPT_DATA; |
|
} |
|
else |
|
{ |
|
*pdwFlags = 0; |
|
} |
|
|
|
LeaveCriticalSection(&m_critSec); |
|
return S_OK; |
|
} |
|
|
|
|
|
|
|
//------------------------------------------------------------------- |
|
// GetOutputStatus |
|
// Query if the MFT can produce output. |
|
//------------------------------------------------------------------- |
|
|
|
HRESULT OcvImageManipulations::GetOutputStatus(DWORD *pdwFlags) |
|
{ |
|
if (pdwFlags == NULL) |
|
{ |
|
return E_POINTER; |
|
} |
|
|
|
EnterCriticalSection(&m_critSec); |
|
|
|
// The MFT can produce an output sample if (and only if) there an input sample. |
|
if (m_pSample != NULL) |
|
{ |
|
*pdwFlags = MFT_OUTPUT_STATUS_SAMPLE_READY; |
|
} |
|
else |
|
{ |
|
*pdwFlags = 0; |
|
} |
|
|
|
LeaveCriticalSection(&m_critSec); |
|
return S_OK; |
|
} |
|
|
|
|
|
//------------------------------------------------------------------- |
|
// SetOutputBounds |
|
// Sets the range of time stamps that the MFT will output. |
|
//------------------------------------------------------------------- |
|
|
|
HRESULT OcvImageManipulations::SetOutputBounds( |
|
LONGLONG hnsLowerBound, |
|
LONGLONG hnsUpperBound |
|
) |
|
{ |
|
// Implementation of this method is optional. |
|
return E_NOTIMPL; |
|
} |
|
|
|
|
|
//------------------------------------------------------------------- |
|
// ProcessEvent |
|
// Sends an event to an input stream. |
|
//------------------------------------------------------------------- |
|
|
|
HRESULT OcvImageManipulations::ProcessEvent( |
|
DWORD dwInputStreamID, |
|
IMFMediaEvent *pEvent |
|
) |
|
{ |
|
// This MFT does not handle any stream events, so the method can |
|
// return E_NOTIMPL. This tells the pipeline that it can stop |
|
// sending any more events to this MFT. |
|
return E_NOTIMPL; |
|
} |
|
|
|
|
|
//------------------------------------------------------------------- |
|
// ProcessMessage |
|
//------------------------------------------------------------------- |
|
|
|
HRESULT OcvImageManipulations::ProcessMessage( |
|
MFT_MESSAGE_TYPE eMessage, |
|
ULONG_PTR ulParam |
|
) |
|
{ |
|
EnterCriticalSection(&m_critSec); |
|
|
|
HRESULT hr = S_OK; |
|
|
|
switch (eMessage) |
|
{ |
|
case MFT_MESSAGE_COMMAND_FLUSH: |
|
// Flush the MFT. |
|
hr = OnFlush(); |
|
break; |
|
|
|
case MFT_MESSAGE_COMMAND_DRAIN: |
|
// Drain: Tells the MFT to reject further input until all pending samples are |
|
// processed. That is our default behavior already, so there is nothing to do. |
|
// |
|
// For a decoder that accepts a queue of samples, the MFT might need to drain |
|
// the queue in response to this command. |
|
break; |
|
|
|
case MFT_MESSAGE_SET_D3D_MANAGER: |
|
// Sets a pointer to the IDirect3DDeviceManager9 interface. |
|
|
|
// The pipeline should never send this message unless the MFT sets the MF_SA_D3D_AWARE |
|
// attribute set to TRUE. Because this MFT does not set MF_SA_D3D_AWARE, it is an error |
|
// to send the MFT_MESSAGE_SET_D3D_MANAGER message to the MFT. Return an error code in |
|
// this case. |
|
|
|
// NOTE: If this MFT were D3D-enabled, it would cache the IDirect3DDeviceManager9 |
|
// pointer for use during streaming. |
|
|
|
hr = E_NOTIMPL; |
|
break; |
|
|
|
case MFT_MESSAGE_NOTIFY_BEGIN_STREAMING: |
|
hr = BeginStreaming(); |
|
break; |
|
|
|
case MFT_MESSAGE_NOTIFY_END_STREAMING: |
|
hr = EndStreaming(); |
|
break; |
|
|
|
// The next two messages do not require any action from this MFT. |
|
|
|
case MFT_MESSAGE_NOTIFY_END_OF_STREAM: |
|
break; |
|
|
|
case MFT_MESSAGE_NOTIFY_START_OF_STREAM: |
|
break; |
|
} |
|
|
|
LeaveCriticalSection(&m_critSec); |
|
return hr; |
|
} |
|
|
|
|
|
//------------------------------------------------------------------- |
|
// ProcessInput |
|
// Process an input sample. |
|
//------------------------------------------------------------------- |
|
|
|
HRESULT OcvImageManipulations::ProcessInput( |
|
DWORD dwInputStreamID, |
|
IMFSample *pSample, |
|
DWORD dwFlags |
|
) |
|
{ |
|
// Check input parameters. |
|
if (pSample == NULL) |
|
{ |
|
return E_POINTER; |
|
} |
|
|
|
if (dwFlags != 0) |
|
{ |
|
return E_INVALIDARG; // dwFlags is reserved and must be zero. |
|
} |
|
|
|
HRESULT hr = S_OK; |
|
|
|
EnterCriticalSection(&m_critSec); |
|
|
|
// Validate the input stream number. |
|
if (!IsValidInputStream(dwInputStreamID)) |
|
{ |
|
hr = MF_E_INVALIDSTREAMNUMBER; |
|
goto done; |
|
} |
|
|
|
// Check for valid media types. |
|
// The client must set input and output types before calling ProcessInput. |
|
if (!m_pInputType || !m_pOutputType) |
|
{ |
|
hr = MF_E_NOTACCEPTING; |
|
goto done; |
|
} |
|
|
|
// Check if an input sample is already queued. |
|
if (m_pSample != NULL) |
|
{ |
|
hr = MF_E_NOTACCEPTING; // We already have an input sample. |
|
goto done; |
|
} |
|
|
|
// Initialize streaming. |
|
hr = BeginStreaming(); |
|
if (FAILED(hr)) |
|
{ |
|
goto done; |
|
} |
|
|
|
// Cache the sample. We do the actual work in ProcessOutput. |
|
m_pSample = pSample; |
|
pSample->AddRef(); // Hold a reference count on the sample. |
|
|
|
done: |
|
LeaveCriticalSection(&m_critSec); |
|
return hr; |
|
} |
|
|
|
|
|
//------------------------------------------------------------------- |
|
// ProcessOutput |
|
// Process an output sample. |
|
//------------------------------------------------------------------- |
|
|
|
HRESULT OcvImageManipulations::ProcessOutput( |
|
DWORD dwFlags, |
|
DWORD cOutputBufferCount, |
|
MFT_OUTPUT_DATA_BUFFER *pOutputSamples, // one per stream |
|
DWORD *pdwStatus |
|
) |
|
{ |
|
// Check input parameters... |
|
|
|
// This MFT does not accept any flags for the dwFlags parameter. |
|
|
|
// The only defined flag is MFT_PROCESS_OUTPUT_DISCARD_WHEN_NO_BUFFER. This flag |
|
// applies only when the MFT marks an output stream as lazy or optional. But this |
|
// MFT has no lazy or optional streams, so the flag is not valid. |
|
|
|
if (dwFlags != 0) |
|
{ |
|
return E_INVALIDARG; |
|
} |
|
|
|
if (pOutputSamples == NULL || pdwStatus == NULL) |
|
{ |
|
return E_POINTER; |
|
} |
|
|
|
// There must be exactly one output buffer. |
|
if (cOutputBufferCount != 1) |
|
{ |
|
return E_INVALIDARG; |
|
} |
|
|
|
// It must contain a sample. |
|
if (pOutputSamples[0].pSample == NULL) |
|
{ |
|
return E_INVALIDARG; |
|
} |
|
|
|
HRESULT hr = S_OK; |
|
|
|
IMFMediaBuffer *pInput = NULL; |
|
IMFMediaBuffer *pOutput = NULL; |
|
|
|
EnterCriticalSection(&m_critSec); |
|
|
|
// There must be an input sample available for processing. |
|
if (m_pSample == NULL) |
|
{ |
|
hr = MF_E_TRANSFORM_NEED_MORE_INPUT; |
|
goto done; |
|
} |
|
|
|
// Initialize streaming. |
|
|
|
hr = BeginStreaming(); |
|
if (FAILED(hr)) |
|
{ |
|
goto done; |
|
} |
|
|
|
// Get the input buffer. |
|
hr = m_pSample->ConvertToContiguousBuffer(&pInput); |
|
if (FAILED(hr)) |
|
{ |
|
goto done; |
|
} |
|
|
|
// Get the output buffer. |
|
hr = pOutputSamples[0].pSample->ConvertToContiguousBuffer(&pOutput); |
|
if (FAILED(hr)) |
|
{ |
|
goto done; |
|
} |
|
|
|
hr = OnProcessOutput(pInput, pOutput); |
|
if (FAILED(hr)) |
|
{ |
|
goto done; |
|
} |
|
|
|
// Set status flags. |
|
pOutputSamples[0].dwStatus = 0; |
|
*pdwStatus = 0; |
|
|
|
|
|
// Copy the duration and time stamp from the input sample, if present. |
|
|
|
LONGLONG hnsDuration = 0; |
|
LONGLONG hnsTime = 0; |
|
|
|
if (SUCCEEDED(m_pSample->GetSampleDuration(&hnsDuration))) |
|
{ |
|
hr = pOutputSamples[0].pSample->SetSampleDuration(hnsDuration); |
|
if (FAILED(hr)) |
|
{ |
|
goto done; |
|
} |
|
} |
|
|
|
if (SUCCEEDED(m_pSample->GetSampleTime(&hnsTime))) |
|
{ |
|
hr = pOutputSamples[0].pSample->SetSampleTime(hnsTime); |
|
} |
|
|
|
done: |
|
SafeRelease(&m_pSample); // Release our input sample. |
|
SafeRelease(&pInput); |
|
SafeRelease(&pOutput); |
|
LeaveCriticalSection(&m_critSec); |
|
return hr; |
|
} |
|
|
|
// PRIVATE METHODS |
|
|
|
// All methods that follow are private to this MFT and are not part of the IMFTransform interface. |
|
|
|
// Create a partial media type from our list. |
|
// |
|
// dwTypeIndex: Index into the list of peferred media types. |
|
// ppmt: Receives a pointer to the media type. |
|
|
|
HRESULT OcvImageManipulations::OnGetPartialType(DWORD dwTypeIndex, IMFMediaType **ppmt) |
|
{ |
|
if (dwTypeIndex >= ARRAYSIZE(g_MediaSubtypes)) |
|
{ |
|
return MF_E_NO_MORE_TYPES; |
|
} |
|
|
|
IMFMediaType *pmt = NULL; |
|
|
|
HRESULT hr = MFCreateMediaType(&pmt); |
|
if (FAILED(hr)) |
|
{ |
|
goto done; |
|
} |
|
|
|
hr = pmt->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); |
|
if (FAILED(hr)) |
|
{ |
|
goto done; |
|
} |
|
|
|
hr = pmt->SetGUID(MF_MT_SUBTYPE, g_MediaSubtypes[dwTypeIndex]); |
|
if (FAILED(hr)) |
|
{ |
|
goto done; |
|
} |
|
|
|
*ppmt = pmt; |
|
(*ppmt)->AddRef(); |
|
|
|
done: |
|
SafeRelease(&pmt); |
|
return hr; |
|
} |
|
|
|
|
|
// Validate an input media type. |
|
|
|
HRESULT OcvImageManipulations::OnCheckInputType(IMFMediaType *pmt) |
|
{ |
|
assert(pmt != NULL); |
|
|
|
HRESULT hr = S_OK; |
|
|
|
// If the output type is set, see if they match. |
|
if (m_pOutputType != NULL) |
|
{ |
|
DWORD flags = 0; |
|
hr = pmt->IsEqual(m_pOutputType, &flags); |
|
|
|
// IsEqual can return S_FALSE. Treat this as failure. |
|
if (hr != S_OK) |
|
{ |
|
hr = MF_E_INVALIDMEDIATYPE; |
|
} |
|
} |
|
else |
|
{ |
|
// Output type is not set. Just check this type. |
|
hr = OnCheckMediaType(pmt); |
|
} |
|
return hr; |
|
} |
|
|
|
|
|
// Validate an output media type. |
|
|
|
HRESULT OcvImageManipulations::OnCheckOutputType(IMFMediaType *pmt) |
|
{ |
|
assert(pmt != NULL); |
|
|
|
HRESULT hr = S_OK; |
|
|
|
// If the input type is set, see if they match. |
|
if (m_pInputType != NULL) |
|
{ |
|
DWORD flags = 0; |
|
hr = pmt->IsEqual(m_pInputType, &flags); |
|
|
|
// IsEqual can return S_FALSE. Treat this as failure. |
|
if (hr != S_OK) |
|
{ |
|
hr = MF_E_INVALIDMEDIATYPE; |
|
} |
|
|
|
} |
|
else |
|
{ |
|
// Input type is not set. Just check this type. |
|
hr = OnCheckMediaType(pmt); |
|
} |
|
return hr; |
|
} |
|
|
|
|
|
// Validate a media type (input or output) |
|
|
|
HRESULT OcvImageManipulations::OnCheckMediaType(IMFMediaType *pmt) |
|
{ |
|
BOOL bFoundMatchingSubtype = FALSE; |
|
|
|
// Major type must be video. |
|
GUID major_type; |
|
HRESULT hr = pmt->GetGUID(MF_MT_MAJOR_TYPE, &major_type); |
|
if (FAILED(hr)) |
|
{ |
|
goto done; |
|
} |
|
|
|
if (major_type != MFMediaType_Video) |
|
{ |
|
hr = MF_E_INVALIDMEDIATYPE; |
|
goto done; |
|
} |
|
|
|
// Subtype must be one of the subtypes in our global list. |
|
|
|
// Get the subtype GUID. |
|
GUID subtype; |
|
hr = pmt->GetGUID(MF_MT_SUBTYPE, &subtype); |
|
if (FAILED(hr)) |
|
{ |
|
goto done; |
|
} |
|
|
|
// Look for the subtype in our list of accepted types. |
|
for (DWORD i = 0; i < ARRAYSIZE(g_MediaSubtypes); i++) |
|
{ |
|
if (subtype == g_MediaSubtypes[i]) |
|
{ |
|
bFoundMatchingSubtype = TRUE; |
|
break; |
|
} |
|
} |
|
|
|
if (!bFoundMatchingSubtype) |
|
{ |
|
hr = MF_E_INVALIDMEDIATYPE; // The MFT does not support this subtype. |
|
goto done; |
|
} |
|
|
|
// Reject single-field media types. |
|
UINT32 interlace = MFGetAttributeUINT32(pmt, MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive); |
|
if (interlace == MFVideoInterlace_FieldSingleUpper || interlace == MFVideoInterlace_FieldSingleLower) |
|
{ |
|
hr = MF_E_INVALIDMEDIATYPE; |
|
} |
|
|
|
done: |
|
return hr; |
|
} |
|
|
|
|
|
// Set or clear the input media type. |
|
// |
|
// Prerequisite: The input type was already validated. |
|
|
|
void OcvImageManipulations::OnSetInputType(IMFMediaType *pmt) |
|
{ |
|
// if pmt is NULL, clear the type. |
|
// if pmt is non-NULL, set the type. |
|
|
|
SafeRelease(&m_pInputType); |
|
m_pInputType = pmt; |
|
if (m_pInputType) |
|
{ |
|
m_pInputType->AddRef(); |
|
} |
|
|
|
// Update the format information. |
|
UpdateFormatInfo(); |
|
} |
|
|
|
|
|
// Set or clears the output media type. |
|
// |
|
// Prerequisite: The output type was already validated. |
|
|
|
void OcvImageManipulations::OnSetOutputType(IMFMediaType *pmt) |
|
{ |
|
// If pmt is NULL, clear the type. Otherwise, set the type. |
|
|
|
SafeRelease(&m_pOutputType); |
|
m_pOutputType = pmt; |
|
if (m_pOutputType) |
|
{ |
|
m_pOutputType->AddRef(); |
|
} |
|
} |
|
|
|
|
|
// Initialize streaming parameters. |
|
// |
|
// This method is called if the client sends the MFT_MESSAGE_NOTIFY_BEGIN_STREAMING |
|
// message, or when the client processes a sample, whichever happens first. |
|
|
|
HRESULT OcvImageManipulations::BeginStreaming() |
|
{ |
|
HRESULT hr = S_OK; |
|
|
|
if (!m_bStreamingInitialized) |
|
{ |
|
m_bStreamingInitialized = true; |
|
hr = S_OK; |
|
} |
|
|
|
return hr; |
|
} |
|
|
|
|
|
// End streaming. |
|
|
|
// This method is called if the client sends an MFT_MESSAGE_NOTIFY_END_STREAMING |
|
// message, or when the media type changes. In general, it should be called whenever |
|
// the streaming parameters need to be reset. |
|
|
|
HRESULT OcvImageManipulations::EndStreaming() |
|
{ |
|
m_bStreamingInitialized = false; |
|
return S_OK; |
|
} |
|
|
|
|
|
|
|
// Generate output data. |
|
|
|
HRESULT OcvImageManipulations::OnProcessOutput(IMFMediaBuffer *pIn, IMFMediaBuffer *pOut) |
|
{ |
|
BYTE *pDest = NULL; // Destination buffer. |
|
LONG lDestStride = 0; // Destination stride. |
|
|
|
BYTE *pSrc = NULL; // Source buffer. |
|
LONG lSrcStride = 0; // Source stride. |
|
|
|
// Helper objects to lock the buffers. |
|
VideoBufferLock inputLock(pIn); |
|
VideoBufferLock outputLock(pOut); |
|
|
|
// Stride if the buffer does not support IMF2DBuffer |
|
LONG lDefaultStride = 0; |
|
|
|
HRESULT hr = GetDefaultStride(m_pInputType, &lDefaultStride); |
|
if (FAILED(hr)) |
|
{ |
|
return hr; |
|
} |
|
|
|
// Lock the input buffer. |
|
hr = inputLock.LockBuffer(lDefaultStride, m_imageHeightInPixels, &pSrc, &lSrcStride); |
|
if (FAILED(hr)) |
|
{ |
|
return hr; |
|
} |
|
|
|
// Lock the output buffer. |
|
hr = outputLock.LockBuffer(lDefaultStride, m_imageHeightInPixels, &pDest, &lDestStride); |
|
if (FAILED(hr)) |
|
{ |
|
return hr; |
|
} |
|
|
|
cv::Mat InputFrame(m_imageHeightInPixels + m_imageHeightInPixels/2, m_imageWidthInPixels, CV_8UC1, pSrc, lSrcStride); |
|
cv::Mat InputGreyScale(InputFrame, cv::Range(0, m_imageHeightInPixels), cv::Range(0, m_imageWidthInPixels)); |
|
cv::Mat OutputFrame(m_imageHeightInPixels + m_imageHeightInPixels/2, m_imageWidthInPixels, CV_8UC1, pDest, lDestStride); |
|
|
|
switch (m_TransformType) |
|
{ |
|
case Preview: |
|
{ |
|
InputFrame.copyTo(OutputFrame); |
|
} break; |
|
case GrayScale: |
|
{ |
|
OutputFrame.setTo(cv::Scalar(128)); |
|
cv::Mat OutputGreyScale(OutputFrame, cv::Range(0, m_imageHeightInPixels), cv::Range(0, m_imageWidthInPixels)); |
|
InputGreyScale.copyTo(OutputGreyScale); |
|
} break; |
|
case Canny: |
|
{ |
|
OutputFrame.setTo(cv::Scalar(128)); |
|
cv::Mat OutputGreyScale(OutputFrame, cv::Range(0, m_imageHeightInPixels), cv::Range(0, m_imageWidthInPixels)); |
|
cv::Canny(InputGreyScale, OutputGreyScale, 80, 90); |
|
|
|
} break; |
|
case Sobel: |
|
{ |
|
OutputFrame.setTo(cv::Scalar(128)); |
|
cv::Mat OutputGreyScale(OutputFrame, cv::Range(0, m_imageHeightInPixels), cv::Range(0, m_imageWidthInPixels)); |
|
cv::Sobel(InputGreyScale, OutputGreyScale, CV_8U, 1, 1); |
|
} break; |
|
case Histogram: |
|
{ |
|
const int mHistSizeNum = 25; |
|
const int channels[3][1] = {{0}, {1}, {2}}; |
|
const int mHistSize[] = {25}; |
|
const float baseRabge[] = {0.f,256.f}; |
|
const float* ranges[] = {baseRabge}; |
|
|
|
const cv::Scalar mColorsY[] = { cv::Scalar(76), cv::Scalar(149), cv::Scalar(29) }; |
|
const cv::Scalar mColorsUV[] = { cv::Scalar(84, 255), cv::Scalar(43, 21), cv::Scalar(255, 107) }; |
|
|
|
cv::Mat OutputY(m_imageHeightInPixels, m_imageWidthInPixels, CV_8UC1, pDest, lDestStride); |
|
cv::Mat OutputUV(m_imageHeightInPixels/2, m_imageWidthInPixels/2, |
|
CV_8UC2, pDest+m_imageHeightInPixels*lDestStride, lDestStride); |
|
cv::Mat BgrFrame; |
|
|
|
InputFrame.copyTo(OutputFrame); |
|
|
|
cv::cvtColor(InputFrame, BgrFrame, cv::COLOR_YUV420sp2BGR); |
|
int thikness = (int) (BgrFrame.cols / (mHistSizeNum + 10) / 5); |
|
if(thikness > 5) thikness = 5; |
|
int offset = (int) ((BgrFrame.cols - (5*mHistSizeNum + 4*10)*thikness)/2); |
|
|
|
// RGB |
|
for (int c=0; c<3; c++) |
|
{ |
|
cv::Mat hist; |
|
cv::calcHist(&BgrFrame, 1, channels[c], cv::Mat(), hist, 1, mHistSize, ranges); |
|
cv::normalize(hist, hist, BgrFrame.rows/2, 0, cv::NORM_INF); |
|
for(int h=0; h<mHistSizeNum; h++) { |
|
cv::Point mP1, mP2; |
|
// Draw on Y plane |
|
mP1.x = mP2.x = offset + (c * (mHistSizeNum + 10) + h) * thikness; |
|
mP1.y = BgrFrame.rows-1; |
|
mP2.y = mP1.y - 2 - (int)hist.at<float>(h); |
|
cv::line(OutputY, mP1, mP2, mColorsY[c], thikness); |
|
|
|
// Draw on UV planes |
|
mP1.x /= 2; |
|
mP1.y /= 2; |
|
mP2.x /= 2; |
|
mP2.y /= 2; |
|
cv::line(OutputUV, mP1, mP2, mColorsUV[c], thikness/2); |
|
} |
|
} |
|
} break; |
|
default: |
|
break; |
|
} |
|
|
|
// Set the data size on the output buffer. |
|
hr = pOut->SetCurrentLength(m_cbImageSize); |
|
|
|
return hr; |
|
} |
|
|
|
|
|
// Flush the MFT. |
|
|
|
HRESULT OcvImageManipulations::OnFlush() |
|
{ |
|
// For this MFT, flushing just means releasing the input sample. |
|
SafeRelease(&m_pSample); |
|
return S_OK; |
|
} |
|
|
|
|
|
// Update the format information. This method is called whenever the |
|
// input type is set. |
|
|
|
HRESULT OcvImageManipulations::UpdateFormatInfo() |
|
{ |
|
HRESULT hr = S_OK; |
|
|
|
GUID subtype = GUID_NULL; |
|
|
|
m_imageWidthInPixels = 0; |
|
m_imageHeightInPixels = 0; |
|
m_cbImageSize = 0; |
|
|
|
if (m_pInputType != NULL) |
|
{ |
|
hr = m_pInputType->GetGUID(MF_MT_SUBTYPE, &subtype); |
|
if (FAILED(hr)) |
|
{ |
|
goto done; |
|
} |
|
if (subtype != MFVideoFormat_NV12) |
|
{ |
|
hr = E_UNEXPECTED; |
|
goto done; |
|
} |
|
|
|
hr = MFGetAttributeSize(m_pInputType, MF_MT_FRAME_SIZE, &m_imageWidthInPixels, &m_imageHeightInPixels); |
|
if (FAILED(hr)) |
|
{ |
|
goto done; |
|
} |
|
|
|
// Calculate the image size for YUV NV12 image(not including padding) |
|
m_cbImageSize = (m_imageHeightInPixels + m_imageHeightInPixels/2)*m_imageWidthInPixels; |
|
} |
|
|
|
done: |
|
return hr; |
|
} |
|
|
|
|
|
// Get the default stride for a video format. |
|
HRESULT GetDefaultStride(IMFMediaType *pType, LONG *plStride) |
|
{ |
|
LONG lStride = 0; |
|
|
|
// Try to get the default stride from the media type. |
|
HRESULT hr = pType->GetUINT32(MF_MT_DEFAULT_STRIDE, (UINT32*)&lStride); |
|
if (FAILED(hr)) |
|
{ |
|
// Attribute not set. Try to calculate the default stride. |
|
GUID subtype = GUID_NULL; |
|
|
|
UINT32 width = 0; |
|
UINT32 height = 0; |
|
|
|
// Get the subtype and the image size. |
|
hr = pType->GetGUID(MF_MT_SUBTYPE, &subtype); |
|
if (SUCCEEDED(hr)) |
|
{ |
|
hr = MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &width, &height); |
|
} |
|
if (SUCCEEDED(hr)) |
|
{ |
|
if (subtype == MFVideoFormat_NV12) |
|
{ |
|
lStride = width; |
|
} |
|
else if (subtype == MFVideoFormat_YUY2 || subtype == MFVideoFormat_UYVY) |
|
{ |
|
lStride = ((width * 2) + 3) & ~3; |
|
} |
|
else |
|
{ |
|
hr = E_INVALIDARG; |
|
} |
|
} |
|
|
|
// Set the attribute for later reference. |
|
if (SUCCEEDED(hr)) |
|
{ |
|
(void)pType->SetUINT32(MF_MT_DEFAULT_STRIDE, UINT32(lStride)); |
|
} |
|
} |
|
if (SUCCEEDED(hr)) |
|
{ |
|
*plStride = lStride; |
|
} |
|
return hr; |
|
} |
|
|
|
|