1526 lines
37 KiB
C++
1526 lines
37 KiB
C++
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// winmain.cpp : Defines the entry point for the application.
|
|
//
|
|
// 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.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
#define CHECK_HR(hr) if (FAILED(hr)) { goto done; }
|
|
|
|
#include "MFT_Grayscale.h"
|
|
#include "Grayscale.h"
|
|
#include "logmediatype.h"
|
|
|
|
#include <uuids.h> // DirectShow GUIDs
|
|
#include <assert.h>
|
|
#include <evr.h>
|
|
|
|
|
|
// This sample implements a Media Foundation transform (MFT) that
|
|
// converts YUV video frames to grayscale. The conversion is done
|
|
// simply by setting all of the U and V bytes to zero (0x80).
|
|
|
|
// NOTES:
|
|
// 1-in, 1-out
|
|
// Fixed streams
|
|
// Formats: UYVY, YUY2, NV12
|
|
|
|
// Assumptions:
|
|
// 1. If the MFT is holding an input sample, SetInputType and SetOutputType
|
|
// return MF_E_UNSUPPORTED_MEDIATYPE
|
|
// 2. If the input type is set, the output type must match (and vice versa).
|
|
// 3. If both types are set, no type can be set until the current type is
|
|
// cleared.
|
|
// 4. 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
|
|
// a list of supported video subtypes.
|
|
// 5. Preferred output types: As above.
|
|
|
|
// Video FOURCC codes.
|
|
const FOURCC FOURCC_YUY2 = MAKEFOURCC('Y', 'U', 'Y', '2');
|
|
const FOURCC FOURCC_UYVY = MAKEFOURCC('U', 'Y', 'V', 'Y');
|
|
const FOURCC FOURCC_NV12 = MAKEFOURCC('N', 'V', '1', '2');
|
|
|
|
// Static array of media types (preferred and accepted).
|
|
const GUID* g_MediaSubtypes[] =
|
|
{
|
|
& MEDIASUBTYPE_NV12,
|
|
& MEDIASUBTYPE_YUY2,
|
|
& MEDIASUBTYPE_UYVY
|
|
};
|
|
|
|
// Number of media types in the aray.
|
|
DWORD g_cNumSubtypes = ARRAY_SIZE(g_MediaSubtypes);
|
|
|
|
// GetImageSize: Returns the size of a video frame, in bytes.
|
|
HRESULT GetImageSize(FOURCC fcc, UINT32 width, UINT32 height, DWORD* pcbImage);
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: TransformImage_UYVY
|
|
// Description: Converts an image in UYVY format to grayscale.
|
|
//
|
|
// The image conversion functions take the following parameters:
|
|
//
|
|
// pDest: Pointer to the destination buffer.
|
|
// lDestStride: Stride of the destination buffer, in bytes.
|
|
// pSrc: Pointer to the source buffer.
|
|
// lSrcStride: Stride of the source buffer, in bytes.
|
|
// dwWidthInPixels: Frame width in pixels.
|
|
// dwHeightInPixels: Frame height, in pixels.
|
|
//-------------------------------------------------------------------
|
|
|
|
void TransformImage_UYVY(
|
|
BYTE* pDest,
|
|
LONG lDestStride,
|
|
const BYTE* pSrc,
|
|
LONG lSrcStride,
|
|
DWORD dwWidthInPixels,
|
|
DWORD dwHeightInPixels
|
|
)
|
|
{
|
|
for (DWORD y = 0; y < dwHeightInPixels; y++)
|
|
{
|
|
WORD *pSrc_Pixel = (WORD*)pSrc;
|
|
WORD *pDest_Pixel = (WORD*)pDest;
|
|
|
|
for (DWORD x = 0; x < dwWidthInPixels; x++)
|
|
{
|
|
// Byte order is U0 Y0 V0 Y1
|
|
// Each WORD is a byte pair (U/V, Y)
|
|
// Windows is little-endian so the order appears reversed.
|
|
|
|
WORD pixel = pSrc_Pixel[x] & 0xFF00;
|
|
pixel |= 0x0080;
|
|
pDest_Pixel[x] = pixel;
|
|
}
|
|
pDest += lDestStride;
|
|
pSrc += lSrcStride;
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: TransformImage_YUY2
|
|
// Description: Converts an image in YUY2 format to grayscale.
|
|
//-------------------------------------------------------------------
|
|
|
|
void TransformImage_YUY2(
|
|
BYTE* pDest,
|
|
LONG lDestStride,
|
|
const BYTE* pSrc,
|
|
LONG lSrcStride,
|
|
DWORD dwWidthInPixels,
|
|
DWORD dwHeightInPixels
|
|
)
|
|
{
|
|
for (DWORD y = 0; y < dwHeightInPixels; y++)
|
|
{
|
|
WORD *pSrc_Pixel = (WORD*)pSrc;
|
|
WORD *pDest_Pixel = (WORD*)pDest;
|
|
|
|
for (DWORD x = 0; x < dwWidthInPixels; x++)
|
|
{
|
|
// Byte order is Y0 U0 Y1 V0
|
|
// Each WORD is a byte pair (Y, U/V)
|
|
// Windows is little-endian so the order appears reversed.
|
|
|
|
WORD pixel = pSrc_Pixel[x] & 0x00FF;
|
|
pixel |= 0x8000;
|
|
pDest_Pixel[x] = pixel;
|
|
}
|
|
pDest += lDestStride;
|
|
pSrc += lSrcStride;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: TransformImage_NV12
|
|
// Description: Converts an image in NV12 format to grayscale.
|
|
//-------------------------------------------------------------------
|
|
|
|
void TransformImage_NV12(
|
|
BYTE* pDest,
|
|
LONG lDestStride,
|
|
const BYTE* pSrc,
|
|
LONG lSrcStride,
|
|
DWORD dwWidthInPixels,
|
|
DWORD dwHeightInPixels
|
|
)
|
|
{
|
|
// NV12 is planar: Y plane, followed by packed U-V plane.
|
|
|
|
// Y plane
|
|
for (DWORD y = 0; y < dwHeightInPixels; y++)
|
|
{
|
|
CopyMemory(pDest, pSrc, dwWidthInPixels);
|
|
pDest += lDestStride;
|
|
pSrc += lSrcStride;
|
|
}
|
|
|
|
// U-V plane
|
|
for (DWORD y = 0; y < dwHeightInPixels/2; y++)
|
|
{
|
|
FillMemory(pDest, dwWidthInPixels, 0x80);
|
|
pDest += lDestStride;
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: CreateInstance
|
|
// Description: Static method to create an instance of the source.
|
|
//
|
|
// pUnkOuter: Aggregating object or NULL.
|
|
// iid: IID of the requested interface on the source.
|
|
// ppSource: Receives a ref-counted pointer to the source.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::CreateInstance(IUnknown *pUnkOuter, REFIID iid, void **ppMFT)
|
|
{
|
|
if (ppMFT == NULL)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
// This object does not support aggregation.
|
|
if (pUnkOuter != NULL)
|
|
{
|
|
return CLASS_E_NOAGGREGATION;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
CGrayscale *pMFT = new CGrayscale(hr);
|
|
if (pMFT == NULL)
|
|
{
|
|
CHECK_HR(hr = E_OUTOFMEMORY);
|
|
}
|
|
|
|
CHECK_HR(hr = pMFT->QueryInterface(iid, ppMFT));
|
|
|
|
done:
|
|
SAFE_RELEASE(pMFT);
|
|
return hr;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Constructor
|
|
//-------------------------------------------------------------------
|
|
|
|
CGrayscale::CGrayscale(HRESULT& hr) :
|
|
m_nRefCount(1),
|
|
m_pSample(NULL),
|
|
m_pInputType(NULL),
|
|
m_pOutputType(NULL),
|
|
m_pTransformFn(NULL),
|
|
m_videoFOURCC(0),
|
|
m_imageWidthInPixels(0),
|
|
m_imageHeightInPixels(0),
|
|
m_cbImageSize(0)
|
|
{
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Destructor
|
|
//-------------------------------------------------------------------
|
|
|
|
CGrayscale::~CGrayscale()
|
|
{
|
|
assert(m_nRefCount == 0);
|
|
|
|
SAFE_RELEASE(m_pInputType);
|
|
SAFE_RELEASE(m_pOutputType);
|
|
SAFE_RELEASE(m_pSample);
|
|
}
|
|
|
|
// IUnknown methods
|
|
|
|
ULONG CGrayscale::AddRef()
|
|
{
|
|
return InterlockedIncrement(&m_nRefCount);
|
|
}
|
|
|
|
ULONG CGrayscale::Release()
|
|
{
|
|
ULONG uCount = InterlockedDecrement(&m_nRefCount);
|
|
if (uCount == 0)
|
|
{
|
|
delete this;
|
|
}
|
|
// For thread safety, return a temporary variable.
|
|
return uCount;
|
|
}
|
|
|
|
HRESULT CGrayscale::QueryInterface(REFIID iid, void** ppv)
|
|
{
|
|
if (!ppv)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
if (iid == IID_IUnknown)
|
|
{
|
|
*ppv = static_cast<IUnknown*>(this);
|
|
}
|
|
else if (iid == __uuidof(IMFTransform))
|
|
{
|
|
*ppv = static_cast<IMFTransform*>(this);
|
|
}
|
|
else
|
|
{
|
|
*ppv = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// IMFTransform methods. Refer to the Media Foundation SDK documentation for details.
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: GetStreamLimits
|
|
// Returns the minimum and maximum number of streams.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::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;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: GetStreamCount
|
|
// Returns the actual number of streams.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::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;
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: GetStreamIDs
|
|
// Returns stream IDs for the input and output streams.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::GetStreamIDs(
|
|
DWORD dwInputIDArraySize,
|
|
DWORD *pdwInputIDs,
|
|
DWORD dwOutputIDArraySize,
|
|
DWORD *pdwOutputIDs
|
|
)
|
|
{
|
|
// Do not need to implement, because this MFT has a fixed number of
|
|
// streams and the stream IDs match the stream indexes.
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: GetInputStreamInfo
|
|
// Returns information about an input stream.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::GetInputStreamInfo(
|
|
DWORD dwInputStreamID,
|
|
MFT_INPUT_STREAM_INFO * pStreamInfo
|
|
)
|
|
{
|
|
TRACE((L"GetInputStreamInfo\n"));
|
|
|
|
AutoLock lock(m_critSec);
|
|
|
|
if (pStreamInfo == NULL)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (!IsValidInputStream(dwInputStreamID))
|
|
{
|
|
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;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: GetOutputStreamInfo
|
|
// Returns information about an output stream.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::GetOutputStreamInfo(
|
|
DWORD dwOutputStreamID,
|
|
MFT_OUTPUT_STREAM_INFO * pStreamInfo
|
|
)
|
|
{
|
|
AutoLock lock(m_critSec);
|
|
|
|
if (pStreamInfo == NULL)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (!IsValidOutputStream(dwOutputStreamID))
|
|
{
|
|
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;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: GetAttributes
|
|
// Returns the attributes for the MFT.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::GetAttributes(IMFAttributes** pAttributes)
|
|
{
|
|
// This MFT does not support any attributes, so the method is not implemented.
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: GetInputStreamAttributes
|
|
// Returns stream-level attributes for an input stream.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::GetInputStreamAttributes(
|
|
DWORD dwInputStreamID,
|
|
IMFAttributes **ppAttributes
|
|
)
|
|
{
|
|
// This MFT does not support any attributes, so the method is not implemented.
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: GetOutputStreamAttributes
|
|
// Returns stream-level attributes for an output stream.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::GetOutputStreamAttributes(
|
|
DWORD dwOutputStreamID,
|
|
IMFAttributes **ppAttributes
|
|
)
|
|
{
|
|
// This MFT does not support any attributes, so the method is not implemented.
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: DeleteInputStream
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::DeleteInputStream(DWORD dwStreamID)
|
|
{
|
|
// This MFT has a fixed number of input streams, so the method is not implemented.
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: AddInputStreams
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::AddInputStreams(
|
|
DWORD cStreams,
|
|
DWORD *adwStreamIDs
|
|
)
|
|
{
|
|
// This MFT has a fixed number of output streams, so the method is not implemented.
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: GetInputAvailableType
|
|
// Description: Return a preferred input type.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::GetInputAvailableType(
|
|
DWORD dwInputStreamID,
|
|
DWORD dwTypeIndex, // 0-based
|
|
IMFMediaType **ppType
|
|
)
|
|
{
|
|
TRACE((L"GetInputAvailableType (stream = %d, type index = %d)\n", dwInputStreamID, dwTypeIndex));
|
|
|
|
AutoLock lock(m_critSec);
|
|
|
|
if (ppType == NULL)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (!IsValidInputStream(dwInputStreamID))
|
|
{
|
|
return MF_E_INVALIDSTREAMNUMBER;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
// If the output type is set, return that type as our preferred input type.
|
|
if (this->m_pOutputType)
|
|
{
|
|
if (dwTypeIndex > 0)
|
|
{
|
|
return MF_E_NO_MORE_TYPES;
|
|
}
|
|
|
|
*ppType = m_pOutputType;
|
|
(*ppType)->AddRef();
|
|
}
|
|
else
|
|
{
|
|
// The output type is not set. Create a partial media type.
|
|
hr = OnGetPartialType(dwTypeIndex, ppType);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: GetOutputAvailableType
|
|
// Description: Return a preferred output type.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::GetOutputAvailableType(
|
|
DWORD dwOutputStreamID,
|
|
DWORD dwTypeIndex, // 0-based
|
|
IMFMediaType **ppType
|
|
)
|
|
{
|
|
TRACE((L"GetOutputAvailableType (stream = %d, type index = %d)\n", dwOutputStreamID, dwTypeIndex));
|
|
|
|
AutoLock lock(m_critSec);
|
|
|
|
if (ppType == NULL)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (!IsValidOutputStream(dwOutputStreamID))
|
|
{
|
|
return MF_E_INVALIDSTREAMNUMBER;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
// If the input type is set, return that type as our preferred output type.
|
|
if (this->m_pInputType)
|
|
{
|
|
if (dwTypeIndex > 0)
|
|
{
|
|
return MF_E_NO_MORE_TYPES;
|
|
}
|
|
|
|
*ppType = m_pInputType;
|
|
(*ppType)->AddRef();
|
|
}
|
|
else
|
|
{
|
|
// The input type is not set. Create a partial media type.
|
|
hr = OnGetPartialType(dwTypeIndex, ppType);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: SetInputType
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::SetInputType(
|
|
DWORD dwInputStreamID,
|
|
IMFMediaType *pType, // Can be NULL to clear the input type.
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
TRACE((L"CGrayscale::SetInputType\n"));
|
|
|
|
AutoLock lock(m_critSec);
|
|
|
|
if (!IsValidInputStream(dwInputStreamID))
|
|
{
|
|
return MF_E_INVALIDSTREAMNUMBER;
|
|
}
|
|
|
|
// Validate flags.
|
|
if (dwFlags & ~MFT_SET_TYPE_TEST_ONLY)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
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())
|
|
{
|
|
CHECK_HR(hr = MF_E_TRANSFORM_CANNOT_CHANGE_MEDIATYPE_WHILE_PROCESSING);
|
|
}
|
|
|
|
// Validate the type, if non-NULL.
|
|
if (pType)
|
|
{
|
|
CHECK_HR(hr = OnCheckInputType(pType));
|
|
}
|
|
|
|
// The type is OK.
|
|
// Set the type, unless the caller was just testing.
|
|
if (bReallySet)
|
|
{
|
|
CHECK_HR(hr = OnSetInputType(pType));
|
|
}
|
|
|
|
done:
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: SetOutputType
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::SetOutputType(
|
|
DWORD dwOutputStreamID,
|
|
IMFMediaType *pType, // Can be NULL to clear the output type.
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
TRACE((L"CGrayscale::SetOutputType\n"));
|
|
|
|
AutoLock lock(m_critSec);
|
|
|
|
if (!IsValidOutputStream(dwOutputStreamID))
|
|
{
|
|
return MF_E_INVALIDSTREAMNUMBER;
|
|
}
|
|
|
|
// Validate flags.
|
|
if (dwFlags & ~MFT_SET_TYPE_TEST_ONLY)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
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())
|
|
{
|
|
CHECK_HR(hr = MF_E_TRANSFORM_CANNOT_CHANGE_MEDIATYPE_WHILE_PROCESSING);
|
|
}
|
|
|
|
// Validate the type, if non-NULL.
|
|
if (pType)
|
|
{
|
|
CHECK_HR(hr = OnCheckOutputType(pType));
|
|
}
|
|
|
|
if (bReallySet)
|
|
{
|
|
// The type is OK.
|
|
// Set the type, unless the caller was just testing.
|
|
CHECK_HR(hr = OnSetOutputType(pType));
|
|
}
|
|
|
|
done:
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: GetInputCurrentType
|
|
// Description: Returns the current input type.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::GetInputCurrentType(
|
|
DWORD dwInputStreamID,
|
|
IMFMediaType **ppType
|
|
)
|
|
{
|
|
AutoLock lock(m_critSec);
|
|
|
|
if (ppType == NULL)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (!IsValidInputStream(dwInputStreamID))
|
|
{
|
|
return MF_E_INVALIDSTREAMNUMBER;
|
|
}
|
|
|
|
if (!m_pInputType)
|
|
{
|
|
return MF_E_TRANSFORM_TYPE_NOT_SET;
|
|
}
|
|
|
|
*ppType = m_pInputType;
|
|
(*ppType)->AddRef();
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: GetOutputCurrentType
|
|
// Description: Returns the current output type.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::GetOutputCurrentType(
|
|
DWORD dwOutputStreamID,
|
|
IMFMediaType **ppType
|
|
)
|
|
{
|
|
AutoLock lock(m_critSec);
|
|
|
|
if (ppType == NULL)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (!IsValidOutputStream(dwOutputStreamID))
|
|
{
|
|
return MF_E_INVALIDSTREAMNUMBER;
|
|
}
|
|
|
|
if (!m_pOutputType)
|
|
{
|
|
return MF_E_TRANSFORM_TYPE_NOT_SET;
|
|
}
|
|
|
|
*ppType = m_pOutputType;
|
|
(*ppType)->AddRef();
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: GetInputStatus
|
|
// Description: Query if the MFT is accepting more input.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::GetInputStatus(
|
|
DWORD dwInputStreamID,
|
|
DWORD *pdwFlags
|
|
)
|
|
{
|
|
TRACE((L"GetInputStatus\n"));
|
|
|
|
AutoLock lock(m_critSec);
|
|
|
|
if (pdwFlags == NULL)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (!IsValidInputStream(dwInputStreamID))
|
|
{
|
|
return MF_E_INVALIDSTREAMNUMBER;
|
|
}
|
|
|
|
// If we already have an input sample, we don't accept
|
|
// another one until the client calls ProcessOutput or Flush.
|
|
if (m_pSample == NULL)
|
|
{
|
|
*pdwFlags = MFT_INPUT_STATUS_ACCEPT_DATA;
|
|
}
|
|
else
|
|
{
|
|
*pdwFlags = 0;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: GetOutputStatus
|
|
// Description: Query if the MFT can produce output.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::GetOutputStatus(DWORD *pdwFlags)
|
|
{
|
|
TRACE((L"GetOutputStatus\n"));
|
|
|
|
AutoLock lock(m_critSec);
|
|
|
|
if (pdwFlags == NULL)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
// We can produce an output sample if (and only if)
|
|
// we have an input sample.
|
|
if (m_pSample != NULL)
|
|
{
|
|
*pdwFlags = MFT_OUTPUT_STATUS_SAMPLE_READY;
|
|
}
|
|
else
|
|
{
|
|
*pdwFlags = 0;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: SetOutputBounds
|
|
// Sets the range of time stamps that the MFT will output.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::SetOutputBounds(
|
|
LONGLONG hnsLowerBound,
|
|
LONGLONG hnsUpperBound
|
|
)
|
|
{
|
|
// Implementation of this method is optional.
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: ProcessEvent
|
|
// Sends an event to an input stream.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::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;
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: ProcessMessage
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::ProcessMessage(
|
|
MFT_MESSAGE_TYPE eMessage,
|
|
ULONG_PTR ulParam
|
|
)
|
|
{
|
|
AutoLock lock(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 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;
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: ProcessInput
|
|
// Description: Process an input sample.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::ProcessInput(
|
|
DWORD dwInputStreamID,
|
|
IMFSample *pSample,
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
AutoLock lock(m_critSec);
|
|
|
|
if (pSample == NULL)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (!IsValidInputStream(dwInputStreamID))
|
|
{
|
|
return MF_E_INVALIDSTREAMNUMBER;
|
|
}
|
|
|
|
if (dwFlags != 0)
|
|
{
|
|
return E_INVALIDARG; // dwFlags is reserved and must be zero.
|
|
}
|
|
|
|
if (!m_pInputType || !m_pOutputType)
|
|
{
|
|
return MF_E_NOTACCEPTING; // Client must set input and output types.
|
|
}
|
|
|
|
if (m_pSample != NULL)
|
|
{
|
|
return MF_E_NOTACCEPTING; // We already have an input sample.
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
DWORD dwBufferCount = 0;
|
|
|
|
// Validate the number of buffers. There should only be a single buffer to hold the video frame.
|
|
CHECK_HR(hr = pSample->GetBufferCount(&dwBufferCount));
|
|
|
|
if (dwBufferCount == 0)
|
|
{
|
|
CHECK_HR(hr = E_FAIL);
|
|
}
|
|
if (dwBufferCount > 1)
|
|
{
|
|
CHECK_HR(hr = MF_E_SAMPLE_HAS_TOO_MANY_BUFFERS);
|
|
}
|
|
|
|
// Cache the sample. We do the actual work in ProcessOutput.
|
|
m_pSample = pSample;
|
|
pSample->AddRef(); // Hold a reference count on the sample.
|
|
|
|
done:
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: ProcessOutput
|
|
// Description: Process an output sample.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::ProcessOutput(
|
|
DWORD dwFlags,
|
|
DWORD cOutputBufferCount,
|
|
MFT_OUTPUT_DATA_BUFFER *pOutputSamples, // one per stream
|
|
DWORD *pdwStatus
|
|
)
|
|
{
|
|
AutoLock lock(m_critSec);
|
|
|
|
// Check input parameters...
|
|
|
|
// There are no flags that we accept in this MFT.
|
|
// The only defined flag is MFT_PROCESS_OUTPUT_DISCARD_WHEN_NO_BUFFER. This
|
|
// flag only applies when the MFT marks an output stream as lazy or optional.
|
|
// However there are no lazy or optional streams on this MFT, so the flag is
|
|
// not valid.
|
|
if (dwFlags != 0)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (pOutputSamples == NULL || pdwStatus == NULL)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
// If we don't have an input sample, we need some input before
|
|
// we can generate any output.
|
|
if (m_pSample == NULL)
|
|
{
|
|
return MF_E_TRANSFORM_NEED_MORE_INPUT;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
IMFMediaBuffer *pInput = NULL;
|
|
IMFMediaBuffer *pOutput = NULL;
|
|
|
|
// Get the input buffer.
|
|
CHECK_HR(hr = m_pSample->ConvertToContiguousBuffer(&pInput));
|
|
|
|
// Get the output buffer.
|
|
CHECK_HR(hr = pOutputSamples[0].pSample->ConvertToContiguousBuffer(&pOutput));
|
|
|
|
CHECK_HR(hr = OnProcessOutput(pInput, pOutput));
|
|
|
|
// 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)))
|
|
{
|
|
CHECK_HR(hr = pOutputSamples[0].pSample->SetSampleDuration(hnsDuration));
|
|
}
|
|
|
|
if (SUCCEEDED(m_pSample->GetSampleTime(&hnsTime)))
|
|
{
|
|
CHECK_HR(hr = pOutputSamples[0].pSample->SetSampleTime(hnsTime));
|
|
}
|
|
|
|
done:
|
|
|
|
SAFE_RELEASE(m_pSample); // Release our input sample.
|
|
SAFE_RELEASE(pInput);
|
|
SAFE_RELEASE(pOutput);
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
/// PRIVATE METHODS
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: OnGetPartialType
|
|
// Description: Returns 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 CGrayscale::OnGetPartialType(DWORD dwTypeIndex, IMFMediaType **ppmt)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (dwTypeIndex >= g_cNumSubtypes)
|
|
{
|
|
return MF_E_NO_MORE_TYPES;
|
|
}
|
|
|
|
IMFMediaType *pmt = NULL;
|
|
|
|
CHECK_HR(hr = MFCreateMediaType(&pmt));
|
|
|
|
CHECK_HR(hr = pmt->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video));
|
|
|
|
CHECK_HR(hr = pmt->SetGUID(MF_MT_SUBTYPE, *g_MediaSubtypes[dwTypeIndex]));
|
|
|
|
*ppmt = pmt;
|
|
(*ppmt)->AddRef();
|
|
|
|
done:
|
|
SAFE_RELEASE(pmt);
|
|
return hr;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: OnCheckInputType
|
|
// Description: Validate an input media type.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::OnCheckInputType(IMFMediaType *pmt)
|
|
{
|
|
TRACE((L"OnCheckInputType\n"));
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: OnCheckOutputType
|
|
// Description: Validate an output media type.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::OnCheckOutputType(IMFMediaType *pmt)
|
|
{
|
|
TRACE((L"OnCheckOutputType\n"));
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: OnCheckMediaType
|
|
// Description: Validates a media type for this transform.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::OnCheckMediaType(IMFMediaType *pmt)
|
|
{
|
|
LogMediaType(pmt);
|
|
|
|
GUID major_type = GUID_NULL;
|
|
GUID subtype = GUID_NULL;
|
|
MFVideoInterlaceMode interlace = MFVideoInterlace_Unknown;
|
|
UINT32 val = 0;
|
|
BOOL bFoundMatchingSubtype = FALSE;
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
// Major type must be video.
|
|
CHECK_HR(hr = pmt->GetGUID(MF_MT_MAJOR_TYPE, &major_type));
|
|
|
|
if (major_type != MFMediaType_Video)
|
|
{
|
|
CHECK_HR(hr = MF_E_INVALIDMEDIATYPE);
|
|
}
|
|
|
|
// Subtype must be one of the subtypes in our global list.
|
|
|
|
// Get the subtype GUID.
|
|
CHECK_HR(hr = pmt->GetGUID(MF_MT_SUBTYPE, &subtype));
|
|
|
|
// Look for the subtype in our list of accepted types.
|
|
for (DWORD i = 0; i < g_cNumSubtypes; i++)
|
|
{
|
|
if (subtype == *g_MediaSubtypes[i])
|
|
{
|
|
bFoundMatchingSubtype = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!bFoundMatchingSubtype)
|
|
{
|
|
CHECK_HR(hr = MF_E_INVALIDMEDIATYPE);
|
|
}
|
|
|
|
// Video must be progressive frames.
|
|
CHECK_HR(hr = pmt->GetUINT32(MF_MT_INTERLACE_MODE, (UINT32*)&interlace));
|
|
if (interlace != MFVideoInterlace_Progressive)
|
|
{
|
|
CHECK_HR(hr = MF_E_INVALIDMEDIATYPE);
|
|
}
|
|
|
|
done:
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: OnSetInputType
|
|
// Description: Sets or clears the input media type.
|
|
//
|
|
// Prerequisite:
|
|
// The input type has already been validated.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::OnSetInputType(IMFMediaType *pmt)
|
|
{
|
|
TRACE((L"CGrayscale::OnSetInputType\n"));
|
|
|
|
// if pmt is NULL, clear the type.
|
|
// if pmt is non-NULL, set the type.
|
|
|
|
SAFE_RELEASE(m_pInputType);
|
|
m_pInputType = pmt;
|
|
if (m_pInputType)
|
|
{
|
|
m_pInputType->AddRef();
|
|
}
|
|
|
|
// Update the format information.
|
|
UpdateFormatInfo();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: OnSetOutputType
|
|
// Description: Sets or clears the output media type.
|
|
//
|
|
// Prerequisite:
|
|
// The output type has already been validated.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::OnSetOutputType(IMFMediaType *pmt)
|
|
{
|
|
TRACE((L"CGrayscale::OnSetOutputType\n"));
|
|
|
|
// if pmt is NULL, clear the type.
|
|
// if pmt is non-NULL, set the type.
|
|
|
|
SAFE_RELEASE(m_pOutputType);
|
|
m_pOutputType = pmt;
|
|
if (m_pOutputType)
|
|
{
|
|
m_pOutputType->AddRef();
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: OnProcessOutput
|
|
// Description: Generates output data.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::OnProcessOutput(IMFMediaBuffer *pIn, IMFMediaBuffer *pOut)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
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;
|
|
|
|
CHECK_HR(hr = GetDefaultStride(m_pInputType, &lDefaultStride));
|
|
|
|
// Lock the input buffer.
|
|
CHECK_HR(hr = inputLock.LockBuffer(lDefaultStride, this->m_imageHeightInPixels, &pSrc, &lSrcStride));
|
|
|
|
// Lock the output buffer.
|
|
CHECK_HR(hr = outputLock.LockBuffer(lDefaultStride, m_imageHeightInPixels, &pDest, &lDestStride));
|
|
|
|
// Invoke the image transform function.
|
|
assert (m_pTransformFn != NULL);
|
|
if (m_pTransformFn)
|
|
{
|
|
(*m_pTransformFn)( pDest, lDestStride, pSrc, lSrcStride,
|
|
m_imageWidthInPixels, m_imageHeightInPixels);
|
|
}
|
|
else
|
|
{
|
|
CHECK_HR(hr = E_UNEXPECTED);
|
|
}
|
|
|
|
|
|
// Set the data size on the output buffer.
|
|
CHECK_HR(hr = pOut->SetCurrentLength(m_cbImageSize));
|
|
|
|
// The VideoBufferLock class automatically unlocks the buffers.
|
|
done:
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: OnFlush
|
|
// Description: Flush the MFT.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::OnFlush()
|
|
{
|
|
// For this MFT, flushing just means releasing the input sample.
|
|
SAFE_RELEASE(m_pSample);
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: UpdateFormatInfo
|
|
// Description: After the input type is set, update our format
|
|
// information.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT CGrayscale::UpdateFormatInfo()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
GUID subtype = GUID_NULL;
|
|
|
|
m_imageWidthInPixels = 0;
|
|
m_imageHeightInPixels = 0;
|
|
m_videoFOURCC = 0;
|
|
m_cbImageSize = 0;
|
|
|
|
m_pTransformFn = NULL;
|
|
|
|
if (m_pInputType != NULL)
|
|
{
|
|
CHECK_HR(hr = m_pInputType->GetGUID(MF_MT_SUBTYPE, &subtype));
|
|
|
|
m_videoFOURCC = subtype.Data1;
|
|
|
|
switch (m_videoFOURCC)
|
|
{
|
|
case FOURCC_YUY2:
|
|
m_pTransformFn = TransformImage_YUY2;
|
|
break;
|
|
|
|
case FOURCC_UYVY:
|
|
m_pTransformFn = TransformImage_UYVY;
|
|
break;
|
|
|
|
case FOURCC_NV12:
|
|
m_pTransformFn = TransformImage_NV12;
|
|
break;
|
|
|
|
default:
|
|
CHECK_HR(hr = E_UNEXPECTED);
|
|
}
|
|
|
|
CHECK_HR(hr = MFGetAttributeSize(
|
|
m_pInputType,
|
|
MF_MT_FRAME_SIZE,
|
|
&m_imageWidthInPixels,
|
|
&m_imageHeightInPixels
|
|
));
|
|
|
|
TRACE((L"Frame size: %d x %d\n", m_imageWidthInPixels, m_imageHeightInPixels));
|
|
|
|
// Calculate the image size (not including padding)
|
|
CHECK_HR(hr = GetImageSize(m_videoFOURCC, m_imageWidthInPixels, m_imageHeightInPixels, &m_cbImageSize));
|
|
}
|
|
|
|
done:
|
|
return hr;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: GetImageSize
|
|
// Description:
|
|
// Calculates the buffer size needed, based on the video format.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT GetImageSize(FOURCC fcc, UINT32 width, UINT32 height, DWORD* pcbImage)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
switch (fcc)
|
|
{
|
|
case FOURCC_YUY2:
|
|
case FOURCC_UYVY:
|
|
// check overflow
|
|
if ((width > MAXDWORD / 2) ||
|
|
(width * 2 > MAXDWORD / height))
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
else
|
|
{
|
|
// 16 bpp
|
|
*pcbImage = width * height * 2;
|
|
}
|
|
break;
|
|
|
|
|
|
case FOURCC_NV12:
|
|
// check overflow
|
|
if ((height/2 > MAXDWORD - height) ||
|
|
((height + height/2) > MAXDWORD / width))
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
else
|
|
{
|
|
// 12 bpp
|
|
*pcbImage = width * (height + (height/2));
|
|
}
|
|
break;
|
|
|
|
default:
|
|
hr = E_FAIL; // Unsupported type.
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|