2013-06-06 16:34:57 +08:00
|
|
|
// 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.
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
#include "OcvTransform.h"
|
2013-06-06 16:34:57 +08:00
|
|
|
#include "bufferlock.h"
|
|
|
|
|
2015-02-13 22:12:37 +08:00
|
|
|
#include <opencv2\core\core.hpp>
|
|
|
|
#include <opencv2\imgproc\imgproc.hpp>
|
|
|
|
#include <opencv2\features2d\features2d.hpp>
|
|
|
|
|
|
|
|
|
2013-06-24 17:32:57 +08:00
|
|
|
|
2013-06-06 16:34:57 +08:00
|
|
|
using namespace Microsoft::WRL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
This sample implements a video effect as a Media Foundation transform (MFT).
|
|
|
|
|
|
|
|
NOTES ON THE MFT IMPLEMENTATION
|
|
|
|
|
2013-06-11 02:48:53 +08:00
|
|
|
1. The MFT has fixed streams: One input stream and one output stream.
|
2013-06-06 16:34:57 +08:00
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
2. The MFT supports NV12 format only.
|
2013-06-06 16:34:57 +08:00
|
|
|
|
|
|
|
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:
|
2013-06-11 02:48:53 +08:00
|
|
|
|
2013-06-06 16:34:57 +08:00
|
|
|
(a) If the output type is set, that's the preferred type.
|
2013-06-11 02:48:53 +08:00
|
|
|
(b) Otherwise, the preferred types are partial types, constructed from the
|
2013-06-06 16:34:57 +08:00
|
|
|
list of supported subtypes.
|
2013-06-11 02:48:53 +08:00
|
|
|
|
2013-06-06 16:34:57 +08:00
|
|
|
7. Preferred output types: As above.
|
|
|
|
|
2013-06-11 02:48:53 +08:00
|
|
|
8. Streaming:
|
|
|
|
|
|
|
|
The private BeingStreaming() method is called in response to the
|
|
|
|
MFT_MESSAGE_NOTIFY_BEGIN_STREAMING message.
|
2013-06-06 16:34:57 +08:00
|
|
|
|
|
|
|
If the client does not send MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, the MFT calls
|
2013-06-11 02:48:53 +08:00
|
|
|
BeginStreaming inside the first call to ProcessInput or ProcessOutput.
|
2013-06-06 16:34:57 +08:00
|
|
|
|
|
|
|
This is a good approach for allocating resources that your MFT requires for
|
2013-06-11 02:48:53 +08:00
|
|
|
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
|
2013-06-06 16:34:57 +08:00
|
|
|
MFT_MESSAGE_NOTIFY_END_STREAMING message) and then restarted.
|
2013-06-11 02:48:53 +08:00
|
|
|
|
2013-06-06 16:34:57 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
// Static array of media types (preferred and accepted).
|
|
|
|
const GUID g_MediaSubtypes[] =
|
|
|
|
{
|
2013-06-24 17:32:57 +08:00
|
|
|
MFVideoFormat_NV12
|
2013-06-06 16:34:57 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
OcvImageManipulations::OcvImageManipulations() :
|
2013-06-24 17:32:57 +08:00
|
|
|
m_pSample(NULL), m_pInputType(NULL), m_pOutputType(NULL),
|
2013-06-06 16:34:57 +08:00
|
|
|
m_imageWidthInPixels(0), m_imageHeightInPixels(0), m_cbImageSize(0),
|
2013-06-15 06:01:09 +08:00
|
|
|
m_TransformType(Preview), m_bStreamingInitialized(false),
|
2013-06-06 16:34:57 +08:00
|
|
|
m_pAttributes(NULL)
|
|
|
|
{
|
|
|
|
InitializeCriticalSectionEx(&m_critSec, 3000, 0);
|
|
|
|
}
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
OcvImageManipulations::~OcvImageManipulations()
|
2013-06-06 16:34:57 +08:00
|
|
|
{
|
|
|
|
SafeRelease(&m_pInputType);
|
|
|
|
SafeRelease(&m_pOutputType);
|
|
|
|
SafeRelease(&m_pSample);
|
|
|
|
SafeRelease(&m_pAttributes);
|
|
|
|
DeleteCriticalSection(&m_critSec);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initialize the instance.
|
2013-06-19 18:57:26 +08:00
|
|
|
STDMETHODIMP OcvImageManipulations::RuntimeClassInitialize()
|
2013-06-06 16:34:57 +08:00
|
|
|
{
|
|
|
|
// Create the attribute store.
|
|
|
|
return MFCreateAttributes(&m_pAttributes, 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
// IMediaExtension methods
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
// SetProperties
|
|
|
|
// Sets the configuration of the effect
|
|
|
|
//-------------------------------------------------------------------
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::SetProperties(ABI::Windows::Foundation::Collections::IPropertySet *pConfiguration)
|
2013-06-06 16:34:57 +08:00
|
|
|
{
|
2013-06-15 06:01:09 +08:00
|
|
|
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)
|
|
|
|
{
|
2015-03-18 21:31:27 +08:00
|
|
|
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IPropertyValue> spPropVal;
|
|
|
|
Microsoft::WRL::ComPtr<IInspectable> spInsp;
|
|
|
|
|
|
|
|
spSetting->Lookup(key, spInsp.ReleaseAndGetAddressOf());
|
|
|
|
|
|
|
|
hr = spInsp.As(&spPropVal);
|
2015-03-18 22:37:42 +08:00
|
|
|
if (hr != S_OK)
|
2015-03-18 21:31:27 +08:00
|
|
|
{
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
INT32 effect;
|
|
|
|
hr = spPropVal->GetInt32(&effect);
|
|
|
|
if (hr != S_OK)
|
|
|
|
{
|
|
|
|
return hr;
|
|
|
|
}
|
2013-06-15 06:01:09 +08:00
|
|
|
|
|
|
|
if ((effect >= 0) && (effect < InvalidEffect))
|
|
|
|
{
|
|
|
|
m_TransformType = (ProcessingType)effect;
|
|
|
|
}
|
|
|
|
}
|
2013-06-19 18:57:26 +08:00
|
|
|
|
2013-08-21 22:52:15 +08:00
|
|
|
return hr;
|
2013-06-06 16:34:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// IMFTransform methods. Refer to the Media Foundation SDK documentation for details.
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
// GetStreamLimits
|
|
|
|
// Returns the minimum and maximum number of streams.
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::GetStreamLimits(
|
2013-06-06 16:34:57 +08:00
|
|
|
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.
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::GetStreamCount(
|
2013-06-06 16:34:57 +08:00
|
|
|
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.
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::GetStreamIDs(
|
2013-06-06 16:34:57 +08:00
|
|
|
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.
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::GetInputStreamInfo(
|
2013-06-06 16:34:57 +08:00
|
|
|
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.
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::GetOutputStreamInfo(
|
2013-06-06 16:34:57 +08:00
|
|
|
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.
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::GetAttributes(IMFAttributes** ppAttributes)
|
2013-06-06 16:34:57 +08:00
|
|
|
{
|
|
|
|
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.
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::GetInputStreamAttributes(
|
2013-06-06 16:34:57 +08:00
|
|
|
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.
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::GetOutputStreamAttributes(
|
2013-06-06 16:34:57 +08:00
|
|
|
DWORD dwOutputStreamID,
|
|
|
|
IMFAttributes **ppAttributes
|
|
|
|
)
|
|
|
|
{
|
|
|
|
// This MFT does not support any stream-level attributes, so the method is not implemented.
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
// DeleteInputStream
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::DeleteInputStream(DWORD dwStreamID)
|
2013-06-06 16:34:57 +08:00
|
|
|
{
|
|
|
|
// This MFT has a fixed number of input streams, so the method is not supported.
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
// AddInputStreams
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::AddInputStreams(
|
2013-06-06 16:34:57 +08:00
|
|
|
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.
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::GetInputAvailableType(
|
2013-06-06 16:34:57 +08:00
|
|
|
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.
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::GetOutputAvailableType(
|
2013-06-06 16:34:57 +08:00
|
|
|
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
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::SetInputType(
|
2013-06-06 16:34:57 +08:00
|
|
|
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
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::SetOutputType(
|
2013-06-06 16:34:57 +08:00
|
|
|
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.
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::GetInputCurrentType(
|
2013-06-06 16:34:57 +08:00
|
|
|
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.
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::GetOutputCurrentType(
|
2013-06-06 16:34:57 +08:00
|
|
|
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.
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::GetInputStatus(
|
2013-06-06 16:34:57 +08:00
|
|
|
DWORD dwInputStreamID,
|
|
|
|
DWORD *pdwFlags
|
|
|
|
)
|
|
|
|
{
|
|
|
|
if (pdwFlags == NULL)
|
|
|
|
{
|
|
|
|
return E_POINTER;
|
|
|
|
}
|
|
|
|
|
|
|
|
EnterCriticalSection(&m_critSec);
|
|
|
|
|
|
|
|
if (!IsValidInputStream(dwInputStreamID))
|
|
|
|
{
|
|
|
|
LeaveCriticalSection(&m_critSec);
|
|
|
|
return MF_E_INVALIDSTREAMNUMBER;
|
|
|
|
}
|
|
|
|
|
2013-06-11 02:48:53 +08:00
|
|
|
// If an input sample is already queued, do not accept another sample until the
|
2013-06-06 16:34:57 +08:00
|
|
|
// client calls ProcessOutput or Flush.
|
|
|
|
|
2013-06-11 02:48:53 +08:00
|
|
|
// 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
|
2013-06-06 16:34:57 +08:00
|
|
|
// 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.
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::GetOutputStatus(DWORD *pdwFlags)
|
2013-06-06 16:34:57 +08:00
|
|
|
{
|
|
|
|
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.
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::SetOutputBounds(
|
2013-06-06 16:34:57 +08:00
|
|
|
LONGLONG hnsLowerBound,
|
|
|
|
LONGLONG hnsUpperBound
|
|
|
|
)
|
|
|
|
{
|
|
|
|
// Implementation of this method is optional.
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
// ProcessEvent
|
|
|
|
// Sends an event to an input stream.
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::ProcessEvent(
|
2013-06-06 16:34:57 +08:00
|
|
|
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
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::ProcessMessage(
|
2013-06-06 16:34:57 +08:00
|
|
|
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.
|
|
|
|
|
2013-06-11 02:48:53 +08:00
|
|
|
// The pipeline should never send this message unless the MFT sets the MF_SA_D3D_AWARE
|
2013-06-06 16:34:57 +08:00
|
|
|
// 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.
|
|
|
|
|
2013-06-11 02:48:53 +08:00
|
|
|
// NOTE: If this MFT were D3D-enabled, it would cache the IDirect3DDeviceManager9
|
2013-06-06 16:34:57 +08:00
|
|
|
// 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.
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::ProcessInput(
|
2013-06-06 16:34:57 +08:00
|
|
|
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)
|
|
|
|
{
|
2013-06-11 02:48:53 +08:00
|
|
|
hr = MF_E_NOTACCEPTING;
|
2013-06-06 16:34:57 +08:00
|
|
|
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.
|
|
|
|
//-------------------------------------------------------------------
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::ProcessOutput(
|
2013-06-06 16:34:57 +08:00
|
|
|
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.
|
|
|
|
|
2013-06-11 02:48:53 +08:00
|
|
|
// The only defined flag is MFT_PROCESS_OUTPUT_DISCARD_WHEN_NO_BUFFER. This flag
|
2013-06-06 16:34:57 +08:00
|
|
|
// 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.
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::OnGetPartialType(DWORD dwTypeIndex, IMFMediaType **ppmt)
|
2013-06-06 16:34:57 +08:00
|
|
|
{
|
|
|
|
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.
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::OnCheckInputType(IMFMediaType *pmt)
|
2013-06-06 16:34:57 +08:00
|
|
|
{
|
|
|
|
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.
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::OnCheckOutputType(IMFMediaType *pmt)
|
2013-06-06 16:34:57 +08:00
|
|
|
{
|
|
|
|
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)
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::OnCheckMediaType(IMFMediaType *pmt)
|
2013-06-06 16:34:57 +08:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-06-11 02:48:53 +08:00
|
|
|
// Reject single-field media types.
|
2013-06-06 16:34:57 +08:00
|
|
|
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.
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
void OcvImageManipulations::OnSetInputType(IMFMediaType *pmt)
|
2013-06-06 16:34:57 +08:00
|
|
|
{
|
|
|
|
// 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.
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
void OcvImageManipulations::OnSetOutputType(IMFMediaType *pmt)
|
2013-06-06 16:34:57 +08:00
|
|
|
{
|
|
|
|
// 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.
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::BeginStreaming()
|
2013-06-06 16:34:57 +08:00
|
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
|
|
|
|
if (!m_bStreamingInitialized)
|
|
|
|
{
|
|
|
|
m_bStreamingInitialized = true;
|
2013-06-15 06:01:09 +08:00
|
|
|
hr = S_OK;
|
2013-06-06 16:34:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-11 02:48:53 +08:00
|
|
|
// End streaming.
|
2013-06-06 16:34:57 +08:00
|
|
|
|
|
|
|
// 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.
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::EndStreaming()
|
2013-06-06 16:34:57 +08:00
|
|
|
{
|
|
|
|
m_bStreamingInitialized = false;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Generate output data.
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::OnProcessOutput(IMFMediaBuffer *pIn, IMFMediaBuffer *pOut)
|
2013-06-06 16:34:57 +08:00
|
|
|
{
|
|
|
|
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))
|
|
|
|
{
|
2013-06-24 17:32:57 +08:00
|
|
|
return hr;
|
2013-06-06 16:34:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Lock the input buffer.
|
|
|
|
hr = inputLock.LockBuffer(lDefaultStride, m_imageHeightInPixels, &pSrc, &lSrcStride);
|
|
|
|
if (FAILED(hr))
|
|
|
|
{
|
2013-06-24 17:32:57 +08:00
|
|
|
return hr;
|
2013-06-06 16:34:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Lock the output buffer.
|
|
|
|
hr = outputLock.LockBuffer(lDefaultStride, m_imageHeightInPixels, &pDest, &lDestStride);
|
|
|
|
if (FAILED(hr))
|
|
|
|
{
|
2013-06-24 17:32:57 +08:00
|
|
|
return hr;
|
2013-06-06 16:34:57 +08:00
|
|
|
}
|
|
|
|
|
2013-06-11 02:48:53 +08:00
|
|
|
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};
|
2013-08-21 20:44:09 +08:00
|
|
|
|
2013-08-21 22:52:15 +08:00
|
|
|
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) };
|
2013-06-11 02:48:53 +08:00
|
|
|
|
2013-08-21 22:52:15 +08:00
|
|
|
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);
|
2013-06-11 02:48:53 +08:00
|
|
|
cv::Mat BgrFrame;
|
2013-06-19 18:57:26 +08:00
|
|
|
|
2013-08-21 22:52:15 +08:00
|
|
|
InputFrame.copyTo(OutputFrame);
|
2013-06-19 18:57:26 +08:00
|
|
|
|
2013-06-11 02:48:53 +08:00
|
|
|
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++)
|
|
|
|
{
|
2013-06-15 06:01:09 +08:00
|
|
|
cv::Mat hist;
|
2013-06-11 02:48:53 +08:00
|
|
|
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;
|
2013-08-21 22:52:15 +08:00
|
|
|
// Draw on Y plane
|
|
|
|
mP1.x = mP2.x = offset + (c * (mHistSizeNum + 10) + h) * thikness;
|
2013-06-11 02:48:53 +08:00
|
|
|
mP1.y = BgrFrame.rows-1;
|
2013-06-19 18:57:26 +08:00
|
|
|
mP2.y = mP1.y - 2 - (int)hist.at<float>(h);
|
2013-08-21 22:52:15 +08:00
|
|
|
cv::line(OutputY, mP1, mP2, mColorsY[c], thikness);
|
2013-06-19 18:57:26 +08:00
|
|
|
|
2013-08-21 22:52:15 +08:00
|
|
|
// Draw on UV planes
|
2013-06-19 18:57:26 +08:00
|
|
|
mP1.x /= 2;
|
|
|
|
mP1.y /= 2;
|
2013-08-21 22:52:15 +08:00
|
|
|
mP2.x /= 2;
|
2013-06-19 18:57:26 +08:00
|
|
|
mP2.y /= 2;
|
2013-08-21 22:52:15 +08:00
|
|
|
cv::line(OutputUV, mP1, mP2, mColorsUV[c], thikness/2);
|
2013-06-11 02:48:53 +08:00
|
|
|
}
|
2013-08-21 20:44:09 +08:00
|
|
|
}
|
2013-06-11 02:48:53 +08:00
|
|
|
} break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2013-06-06 16:34:57 +08:00
|
|
|
|
|
|
|
// Set the data size on the output buffer.
|
|
|
|
hr = pOut->SetCurrentLength(m_cbImageSize);
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Flush the MFT.
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::OnFlush()
|
2013-06-06 16:34:57 +08:00
|
|
|
{
|
|
|
|
// 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.
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
HRESULT OcvImageManipulations::UpdateFormatInfo()
|
2013-06-06 16:34:57 +08:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
2013-06-11 02:48:53 +08:00
|
|
|
if (subtype != MFVideoFormat_NV12)
|
2013-06-06 16:34:57 +08:00
|
|
|
{
|
|
|
|
hr = E_UNEXPECTED;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
hr = MFGetAttributeSize(m_pInputType, MF_MT_FRAME_SIZE, &m_imageWidthInPixels, &m_imageHeightInPixels);
|
|
|
|
if (FAILED(hr))
|
|
|
|
{
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2013-06-19 18:57:26 +08:00
|
|
|
// Calculate the image size for YUV NV12 image(not including padding)
|
2013-08-21 22:52:15 +08:00
|
|
|
m_cbImageSize = (m_imageHeightInPixels + m_imageHeightInPixels/2)*m_imageWidthInPixels;
|
2013-06-06 16:34:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-11 02:48:53 +08:00
|
|
|
// Get the default stride for a video format.
|
2013-06-06 16:34:57 +08:00
|
|
|
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;
|
|
|
|
}
|