1 // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
2 // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
3 // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
6 // Copyright (c) Microsoft Corporation. All rights reserved.
8 #include "OcvTransform.h"
9 #include "bufferlock.h"
11 #include "opencv2\core\core.hpp"
12 #include "opencv2\imgproc\imgproc.hpp"
14 using namespace Microsoft::WRL;
18 This sample implements a video effect as a Media Foundation transform (MFT).
20 NOTES ON THE MFT IMPLEMENTATION
22 1. The MFT has fixed streams: One input stream and one output stream.
24 2. The MFT supports NV12 format only.
26 3. If the MFT is holding an input sample, SetInputType and SetOutputType both fail.
28 4. The input and output types must be identical.
30 5. If both types are set, no type can be set until the current type is cleared.
32 6. Preferred input types:
34 (a) If the output type is set, that's the preferred type.
35 (b) Otherwise, the preferred types are partial types, constructed from the
36 list of supported subtypes.
38 7. Preferred output types: As above.
42 The private BeingStreaming() method is called in response to the
43 MFT_MESSAGE_NOTIFY_BEGIN_STREAMING message.
45 If the client does not send MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, the MFT calls
46 BeginStreaming inside the first call to ProcessInput or ProcessOutput.
48 This is a good approach for allocating resources that your MFT requires for
51 9. The configuration attributes are applied in the BeginStreaming method. If the
52 client changes the attributes during streaming, the change is ignored until
53 streaming is stopped (either by changing the media types or by sending the
54 MFT_MESSAGE_NOTIFY_END_STREAMING message) and then restarted.
59 // Static array of media types (preferred and accepted).
60 const GUID g_MediaSubtypes[] =
65 HRESULT GetDefaultStride(IMFMediaType *pType, LONG *plStride);
68 inline T clamp(const T& val, const T& minVal, const T& maxVal)
70 return (val < minVal ? minVal : (val > maxVal ? maxVal : val));
73 OcvImageManipulations::OcvImageManipulations() :
74 m_pSample(NULL), m_pInputType(NULL), m_pOutputType(NULL),
75 m_imageWidthInPixels(0), m_imageHeightInPixels(0), m_cbImageSize(0),
76 m_TransformType(Preview), m_bStreamingInitialized(false),
79 InitializeCriticalSectionEx(&m_critSec, 3000, 0);
82 OcvImageManipulations::~OcvImageManipulations()
84 SafeRelease(&m_pInputType);
85 SafeRelease(&m_pOutputType);
86 SafeRelease(&m_pSample);
87 SafeRelease(&m_pAttributes);
88 DeleteCriticalSection(&m_critSec);
91 // Initialize the instance.
92 STDMETHODIMP OcvImageManipulations::RuntimeClassInitialize()
94 // Create the attribute store.
95 return MFCreateAttributes(&m_pAttributes, 3);
98 // IMediaExtension methods
100 //-------------------------------------------------------------------
102 // Sets the configuration of the effect
103 //-------------------------------------------------------------------
104 HRESULT OcvImageManipulations::SetProperties(ABI::Windows::Foundation::Collections::IPropertySet *pConfiguration)
112 WindowsCreateString(L"{698649BE-8EAE-4551-A4CB-3EC98FBD3D86}", 38, &key);
113 Microsoft::WRL::ComPtr<ABI::Windows::Foundation::Collections::IMap<HSTRING, IInspectable *>> spSetting;
114 pConfiguration->QueryInterface(IID_PPV_ARGS(&spSetting));
116 spSetting->HasKey(key, &found);
121 spSetting->Lookup(key, &value);
123 Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IReference<int>> ref;
124 hr = value->QueryInterface(IID_PPV_ARGS(&ref));
125 int effect = InvalidEffect;
126 hr = ref->get_Value(&effect);
127 if ((effect >= 0) && (effect < InvalidEffect))
129 m_TransformType = (ProcessingType)effect;
136 // IMFTransform methods. Refer to the Media Foundation SDK documentation for details.
138 //-------------------------------------------------------------------
140 // Returns the minimum and maximum number of streams.
141 //-------------------------------------------------------------------
143 HRESULT OcvImageManipulations::GetStreamLimits(
144 DWORD *pdwInputMinimum,
145 DWORD *pdwInputMaximum,
146 DWORD *pdwOutputMinimum,
147 DWORD *pdwOutputMaximum
150 if ((pdwInputMinimum == NULL) ||
151 (pdwInputMaximum == NULL) ||
152 (pdwOutputMinimum == NULL) ||
153 (pdwOutputMaximum == NULL))
158 // This MFT has a fixed number of streams.
159 *pdwInputMinimum = 1;
160 *pdwInputMaximum = 1;
161 *pdwOutputMinimum = 1;
162 *pdwOutputMaximum = 1;
167 //-------------------------------------------------------------------
169 // Returns the actual number of streams.
170 //-------------------------------------------------------------------
172 HRESULT OcvImageManipulations::GetStreamCount(
173 DWORD *pcInputStreams,
174 DWORD *pcOutputStreams
177 if ((pcInputStreams == NULL) || (pcOutputStreams == NULL))
183 // This MFT has a fixed number of streams.
185 *pcOutputStreams = 1;
191 //-------------------------------------------------------------------
193 // Returns stream IDs for the input and output streams.
194 //-------------------------------------------------------------------
196 HRESULT OcvImageManipulations::GetStreamIDs(
197 DWORD dwInputIDArraySize,
199 DWORD dwOutputIDArraySize,
203 // It is not required to implement this method if the MFT has a fixed number of
204 // streams AND the stream IDs are numbered sequentially from zero (that is, the
205 // stream IDs match the stream indexes).
207 // In that case, it is OK to return E_NOTIMPL.
212 //-------------------------------------------------------------------
213 // GetInputStreamInfo
214 // Returns information about an input stream.
215 //-------------------------------------------------------------------
217 HRESULT OcvImageManipulations::GetInputStreamInfo(
218 DWORD dwInputStreamID,
219 MFT_INPUT_STREAM_INFO * pStreamInfo
222 if (pStreamInfo == NULL)
227 EnterCriticalSection(&m_critSec);
229 if (!IsValidInputStream(dwInputStreamID))
231 LeaveCriticalSection(&m_critSec);
232 return MF_E_INVALIDSTREAMNUMBER;
235 // NOTE: This method should succeed even when there is no media type on the
236 // stream. If there is no media type, we only need to fill in the dwFlags
237 // member of MFT_INPUT_STREAM_INFO. The other members depend on having a
238 // a valid media type.
240 pStreamInfo->hnsMaxLatency = 0;
241 pStreamInfo->dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES | MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER;
243 if (m_pInputType == NULL)
245 pStreamInfo->cbSize = 0;
249 pStreamInfo->cbSize = m_cbImageSize;
252 pStreamInfo->cbMaxLookahead = 0;
253 pStreamInfo->cbAlignment = 0;
255 LeaveCriticalSection(&m_critSec);
259 //-------------------------------------------------------------------
260 // GetOutputStreamInfo
261 // Returns information about an output stream.
262 //-------------------------------------------------------------------
264 HRESULT OcvImageManipulations::GetOutputStreamInfo(
265 DWORD dwOutputStreamID,
266 MFT_OUTPUT_STREAM_INFO * pStreamInfo
269 if (pStreamInfo == NULL)
274 EnterCriticalSection(&m_critSec);
276 if (!IsValidOutputStream(dwOutputStreamID))
278 LeaveCriticalSection(&m_critSec);
279 return MF_E_INVALIDSTREAMNUMBER;
282 // NOTE: This method should succeed even when there is no media type on the
283 // stream. If there is no media type, we only need to fill in the dwFlags
284 // member of MFT_OUTPUT_STREAM_INFO. The other members depend on having a
285 // a valid media type.
287 pStreamInfo->dwFlags =
288 MFT_OUTPUT_STREAM_WHOLE_SAMPLES |
289 MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER |
290 MFT_OUTPUT_STREAM_FIXED_SAMPLE_SIZE ;
292 if (m_pOutputType == NULL)
294 pStreamInfo->cbSize = 0;
298 pStreamInfo->cbSize = m_cbImageSize;
301 pStreamInfo->cbAlignment = 0;
303 LeaveCriticalSection(&m_critSec);
308 //-------------------------------------------------------------------
310 // Returns the attributes for the MFT.
311 //-------------------------------------------------------------------
313 HRESULT OcvImageManipulations::GetAttributes(IMFAttributes** ppAttributes)
315 if (ppAttributes == NULL)
320 EnterCriticalSection(&m_critSec);
322 *ppAttributes = m_pAttributes;
323 (*ppAttributes)->AddRef();
325 LeaveCriticalSection(&m_critSec);
330 //-------------------------------------------------------------------
331 // GetInputStreamAttributes
332 // Returns stream-level attributes for an input stream.
333 //-------------------------------------------------------------------
335 HRESULT OcvImageManipulations::GetInputStreamAttributes(
336 DWORD dwInputStreamID,
337 IMFAttributes **ppAttributes
340 // This MFT does not support any stream-level attributes, so the method is not implemented.
345 //-------------------------------------------------------------------
346 // GetOutputStreamAttributes
347 // Returns stream-level attributes for an output stream.
348 //-------------------------------------------------------------------
350 HRESULT OcvImageManipulations::GetOutputStreamAttributes(
351 DWORD dwOutputStreamID,
352 IMFAttributes **ppAttributes
355 // This MFT does not support any stream-level attributes, so the method is not implemented.
360 //-------------------------------------------------------------------
362 //-------------------------------------------------------------------
364 HRESULT OcvImageManipulations::DeleteInputStream(DWORD dwStreamID)
366 // This MFT has a fixed number of input streams, so the method is not supported.
371 //-------------------------------------------------------------------
373 //-------------------------------------------------------------------
375 HRESULT OcvImageManipulations::AddInputStreams(
380 // This MFT has a fixed number of output streams, so the method is not supported.
385 //-------------------------------------------------------------------
386 // GetInputAvailableType
387 // Returns a preferred input type.
388 //-------------------------------------------------------------------
390 HRESULT OcvImageManipulations::GetInputAvailableType(
391 DWORD dwInputStreamID,
392 DWORD dwTypeIndex, // 0-based
393 IMFMediaType **ppType
401 EnterCriticalSection(&m_critSec);
403 if (!IsValidInputStream(dwInputStreamID))
405 LeaveCriticalSection(&m_critSec);
406 return MF_E_INVALIDSTREAMNUMBER;
411 // If the output type is set, return that type as our preferred input type.
412 if (m_pOutputType == NULL)
414 // The output type is not set. Create a partial media type.
415 hr = OnGetPartialType(dwTypeIndex, ppType);
417 else if (dwTypeIndex > 0)
419 hr = MF_E_NO_MORE_TYPES;
423 *ppType = m_pOutputType;
427 LeaveCriticalSection(&m_critSec);
433 //-------------------------------------------------------------------
434 // GetOutputAvailableType
435 // Returns a preferred output type.
436 //-------------------------------------------------------------------
438 HRESULT OcvImageManipulations::GetOutputAvailableType(
439 DWORD dwOutputStreamID,
440 DWORD dwTypeIndex, // 0-based
441 IMFMediaType **ppType
449 EnterCriticalSection(&m_critSec);
451 if (!IsValidOutputStream(dwOutputStreamID))
453 LeaveCriticalSection(&m_critSec);
454 return MF_E_INVALIDSTREAMNUMBER;
459 if (m_pInputType == NULL)
461 // The input type is not set. Create a partial media type.
462 hr = OnGetPartialType(dwTypeIndex, ppType);
464 else if (dwTypeIndex > 0)
466 hr = MF_E_NO_MORE_TYPES;
470 *ppType = m_pInputType;
474 LeaveCriticalSection(&m_critSec);
479 //-------------------------------------------------------------------
481 //-------------------------------------------------------------------
483 HRESULT OcvImageManipulations::SetInputType(
484 DWORD dwInputStreamID,
485 IMFMediaType *pType, // Can be NULL to clear the input type.
490 if (dwFlags & ~MFT_SET_TYPE_TEST_ONLY)
495 EnterCriticalSection(&m_critSec);
497 if (!IsValidInputStream(dwInputStreamID))
499 LeaveCriticalSection(&m_critSec);
500 return MF_E_INVALIDSTREAMNUMBER;
505 // Does the caller want us to set the type, or just test it?
506 BOOL bReallySet = ((dwFlags & MFT_SET_TYPE_TEST_ONLY) == 0);
508 // If we have an input sample, the client cannot change the type now.
509 if (HasPendingOutput())
511 hr = MF_E_TRANSFORM_CANNOT_CHANGE_MEDIATYPE_WHILE_PROCESSING;
515 // Validate the type, if non-NULL.
518 hr = OnCheckInputType(pType);
525 // The type is OK. Set the type, unless the caller was just testing.
528 OnSetInputType(pType);
530 // When the type changes, end streaming.
535 LeaveCriticalSection(&m_critSec);
541 //-------------------------------------------------------------------
543 //-------------------------------------------------------------------
545 HRESULT OcvImageManipulations::SetOutputType(
546 DWORD dwOutputStreamID,
547 IMFMediaType *pType, // Can be NULL to clear the output type.
552 if (dwFlags & ~MFT_SET_TYPE_TEST_ONLY)
557 EnterCriticalSection(&m_critSec);
559 if (!IsValidOutputStream(dwOutputStreamID))
561 LeaveCriticalSection(&m_critSec);
562 return MF_E_INVALIDSTREAMNUMBER;
567 // Does the caller want us to set the type, or just test it?
568 BOOL bReallySet = ((dwFlags & MFT_SET_TYPE_TEST_ONLY) == 0);
570 // If we have an input sample, the client cannot change the type now.
571 if (HasPendingOutput())
573 hr = MF_E_TRANSFORM_CANNOT_CHANGE_MEDIATYPE_WHILE_PROCESSING;
577 // Validate the type, if non-NULL.
580 hr = OnCheckOutputType(pType);
587 // The type is OK. Set the type, unless the caller was just testing.
590 OnSetOutputType(pType);
592 // When the type changes, end streaming.
597 LeaveCriticalSection(&m_critSec);
602 //-------------------------------------------------------------------
603 // GetInputCurrentType
604 // Returns the current input type.
605 //-------------------------------------------------------------------
607 HRESULT OcvImageManipulations::GetInputCurrentType(
608 DWORD dwInputStreamID,
609 IMFMediaType **ppType
619 EnterCriticalSection(&m_critSec);
621 if (!IsValidInputStream(dwInputStreamID))
623 hr = MF_E_INVALIDSTREAMNUMBER;
625 else if (!m_pInputType)
627 hr = MF_E_TRANSFORM_TYPE_NOT_SET;
631 *ppType = m_pInputType;
634 LeaveCriticalSection(&m_critSec);
639 //-------------------------------------------------------------------
640 // GetOutputCurrentType
641 // Returns the current output type.
642 //-------------------------------------------------------------------
644 HRESULT OcvImageManipulations::GetOutputCurrentType(
645 DWORD dwOutputStreamID,
646 IMFMediaType **ppType
656 EnterCriticalSection(&m_critSec);
658 if (!IsValidOutputStream(dwOutputStreamID))
660 hr = MF_E_INVALIDSTREAMNUMBER;
662 else if (!m_pOutputType)
664 hr = MF_E_TRANSFORM_TYPE_NOT_SET;
668 *ppType = m_pOutputType;
672 LeaveCriticalSection(&m_critSec);
677 //-------------------------------------------------------------------
679 // Query if the MFT is accepting more input.
680 //-------------------------------------------------------------------
682 HRESULT OcvImageManipulations::GetInputStatus(
683 DWORD dwInputStreamID,
687 if (pdwFlags == NULL)
692 EnterCriticalSection(&m_critSec);
694 if (!IsValidInputStream(dwInputStreamID))
696 LeaveCriticalSection(&m_critSec);
697 return MF_E_INVALIDSTREAMNUMBER;
700 // If an input sample is already queued, do not accept another sample until the
701 // client calls ProcessOutput or Flush.
703 // NOTE: It is possible for an MFT to accept more than one input sample. For
704 // example, this might be required in a video decoder if the frames do not
705 // arrive in temporal order. In the case, the decoder must hold a queue of
706 // samples. For the video effect, each sample is transformed independently, so
707 // there is no reason to queue multiple input samples.
709 if (m_pSample == NULL)
711 *pdwFlags = MFT_INPUT_STATUS_ACCEPT_DATA;
718 LeaveCriticalSection(&m_critSec);
724 //-------------------------------------------------------------------
726 // Query if the MFT can produce output.
727 //-------------------------------------------------------------------
729 HRESULT OcvImageManipulations::GetOutputStatus(DWORD *pdwFlags)
731 if (pdwFlags == NULL)
736 EnterCriticalSection(&m_critSec);
738 // The MFT can produce an output sample if (and only if) there an input sample.
739 if (m_pSample != NULL)
741 *pdwFlags = MFT_OUTPUT_STATUS_SAMPLE_READY;
748 LeaveCriticalSection(&m_critSec);
753 //-------------------------------------------------------------------
755 // Sets the range of time stamps that the MFT will output.
756 //-------------------------------------------------------------------
758 HRESULT OcvImageManipulations::SetOutputBounds(
759 LONGLONG hnsLowerBound,
760 LONGLONG hnsUpperBound
763 // Implementation of this method is optional.
768 //-------------------------------------------------------------------
770 // Sends an event to an input stream.
771 //-------------------------------------------------------------------
773 HRESULT OcvImageManipulations::ProcessEvent(
774 DWORD dwInputStreamID,
775 IMFMediaEvent *pEvent
778 // This MFT does not handle any stream events, so the method can
779 // return E_NOTIMPL. This tells the pipeline that it can stop
780 // sending any more events to this MFT.
785 //-------------------------------------------------------------------
787 //-------------------------------------------------------------------
789 HRESULT OcvImageManipulations::ProcessMessage(
790 MFT_MESSAGE_TYPE eMessage,
794 EnterCriticalSection(&m_critSec);
800 case MFT_MESSAGE_COMMAND_FLUSH:
805 case MFT_MESSAGE_COMMAND_DRAIN:
806 // Drain: Tells the MFT to reject further input until all pending samples are
807 // processed. That is our default behavior already, so there is nothing to do.
809 // For a decoder that accepts a queue of samples, the MFT might need to drain
810 // the queue in response to this command.
813 case MFT_MESSAGE_SET_D3D_MANAGER:
814 // Sets a pointer to the IDirect3DDeviceManager9 interface.
816 // The pipeline should never send this message unless the MFT sets the MF_SA_D3D_AWARE
817 // attribute set to TRUE. Because this MFT does not set MF_SA_D3D_AWARE, it is an error
818 // to send the MFT_MESSAGE_SET_D3D_MANAGER message to the MFT. Return an error code in
821 // NOTE: If this MFT were D3D-enabled, it would cache the IDirect3DDeviceManager9
822 // pointer for use during streaming.
827 case MFT_MESSAGE_NOTIFY_BEGIN_STREAMING:
828 hr = BeginStreaming();
831 case MFT_MESSAGE_NOTIFY_END_STREAMING:
835 // The next two messages do not require any action from this MFT.
837 case MFT_MESSAGE_NOTIFY_END_OF_STREAM:
840 case MFT_MESSAGE_NOTIFY_START_OF_STREAM:
844 LeaveCriticalSection(&m_critSec);
849 //-------------------------------------------------------------------
851 // Process an input sample.
852 //-------------------------------------------------------------------
854 HRESULT OcvImageManipulations::ProcessInput(
855 DWORD dwInputStreamID,
860 // Check input parameters.
868 return E_INVALIDARG; // dwFlags is reserved and must be zero.
873 EnterCriticalSection(&m_critSec);
875 // Validate the input stream number.
876 if (!IsValidInputStream(dwInputStreamID))
878 hr = MF_E_INVALIDSTREAMNUMBER;
882 // Check for valid media types.
883 // The client must set input and output types before calling ProcessInput.
884 if (!m_pInputType || !m_pOutputType)
886 hr = MF_E_NOTACCEPTING;
890 // Check if an input sample is already queued.
891 if (m_pSample != NULL)
893 hr = MF_E_NOTACCEPTING; // We already have an input sample.
897 // Initialize streaming.
898 hr = BeginStreaming();
904 // Cache the sample. We do the actual work in ProcessOutput.
906 pSample->AddRef(); // Hold a reference count on the sample.
909 LeaveCriticalSection(&m_critSec);
914 //-------------------------------------------------------------------
916 // Process an output sample.
917 //-------------------------------------------------------------------
919 HRESULT OcvImageManipulations::ProcessOutput(
921 DWORD cOutputBufferCount,
922 MFT_OUTPUT_DATA_BUFFER *pOutputSamples, // one per stream
926 // Check input parameters...
928 // This MFT does not accept any flags for the dwFlags parameter.
930 // The only defined flag is MFT_PROCESS_OUTPUT_DISCARD_WHEN_NO_BUFFER. This flag
931 // applies only when the MFT marks an output stream as lazy or optional. But this
932 // MFT has no lazy or optional streams, so the flag is not valid.
939 if (pOutputSamples == NULL || pdwStatus == NULL)
944 // There must be exactly one output buffer.
945 if (cOutputBufferCount != 1)
950 // It must contain a sample.
951 if (pOutputSamples[0].pSample == NULL)
958 IMFMediaBuffer *pInput = NULL;
959 IMFMediaBuffer *pOutput = NULL;
961 EnterCriticalSection(&m_critSec);
963 // There must be an input sample available for processing.
964 if (m_pSample == NULL)
966 hr = MF_E_TRANSFORM_NEED_MORE_INPUT;
970 // Initialize streaming.
972 hr = BeginStreaming();
978 // Get the input buffer.
979 hr = m_pSample->ConvertToContiguousBuffer(&pInput);
985 // Get the output buffer.
986 hr = pOutputSamples[0].pSample->ConvertToContiguousBuffer(&pOutput);
992 hr = OnProcessOutput(pInput, pOutput);
999 pOutputSamples[0].dwStatus = 0;
1003 // Copy the duration and time stamp from the input sample, if present.
1005 LONGLONG hnsDuration = 0;
1006 LONGLONG hnsTime = 0;
1008 if (SUCCEEDED(m_pSample->GetSampleDuration(&hnsDuration)))
1010 hr = pOutputSamples[0].pSample->SetSampleDuration(hnsDuration);
1017 if (SUCCEEDED(m_pSample->GetSampleTime(&hnsTime)))
1019 hr = pOutputSamples[0].pSample->SetSampleTime(hnsTime);
1023 SafeRelease(&m_pSample); // Release our input sample.
1024 SafeRelease(&pInput);
1025 SafeRelease(&pOutput);
1026 LeaveCriticalSection(&m_critSec);
1032 // All methods that follow are private to this MFT and are not part of the IMFTransform interface.
1034 // Create a partial media type from our list.
1036 // dwTypeIndex: Index into the list of peferred media types.
1037 // ppmt: Receives a pointer to the media type.
1039 HRESULT OcvImageManipulations::OnGetPartialType(DWORD dwTypeIndex, IMFMediaType **ppmt)
1041 if (dwTypeIndex >= ARRAYSIZE(g_MediaSubtypes))
1043 return MF_E_NO_MORE_TYPES;
1046 IMFMediaType *pmt = NULL;
1048 HRESULT hr = MFCreateMediaType(&pmt);
1054 hr = pmt->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
1060 hr = pmt->SetGUID(MF_MT_SUBTYPE, g_MediaSubtypes[dwTypeIndex]);
1075 // Validate an input media type.
1077 HRESULT OcvImageManipulations::OnCheckInputType(IMFMediaType *pmt)
1079 assert(pmt != NULL);
1083 // If the output type is set, see if they match.
1084 if (m_pOutputType != NULL)
1087 hr = pmt->IsEqual(m_pOutputType, &flags);
1089 // IsEqual can return S_FALSE. Treat this as failure.
1092 hr = MF_E_INVALIDMEDIATYPE;
1097 // Output type is not set. Just check this type.
1098 hr = OnCheckMediaType(pmt);
1104 // Validate an output media type.
1106 HRESULT OcvImageManipulations::OnCheckOutputType(IMFMediaType *pmt)
1108 assert(pmt != NULL);
1112 // If the input type is set, see if they match.
1113 if (m_pInputType != NULL)
1116 hr = pmt->IsEqual(m_pInputType, &flags);
1118 // IsEqual can return S_FALSE. Treat this as failure.
1121 hr = MF_E_INVALIDMEDIATYPE;
1127 // Input type is not set. Just check this type.
1128 hr = OnCheckMediaType(pmt);
1134 // Validate a media type (input or output)
1136 HRESULT OcvImageManipulations::OnCheckMediaType(IMFMediaType *pmt)
1138 BOOL bFoundMatchingSubtype = FALSE;
1140 // Major type must be video.
1142 HRESULT hr = pmt->GetGUID(MF_MT_MAJOR_TYPE, &major_type);
1148 if (major_type != MFMediaType_Video)
1150 hr = MF_E_INVALIDMEDIATYPE;
1154 // Subtype must be one of the subtypes in our global list.
1156 // Get the subtype GUID.
1158 hr = pmt->GetGUID(MF_MT_SUBTYPE, &subtype);
1164 // Look for the subtype in our list of accepted types.
1165 for (DWORD i = 0; i < ARRAYSIZE(g_MediaSubtypes); i++)
1167 if (subtype == g_MediaSubtypes[i])
1169 bFoundMatchingSubtype = TRUE;
1174 if (!bFoundMatchingSubtype)
1176 hr = MF_E_INVALIDMEDIATYPE; // The MFT does not support this subtype.
1180 // Reject single-field media types.
1181 UINT32 interlace = MFGetAttributeUINT32(pmt, MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
1182 if (interlace == MFVideoInterlace_FieldSingleUpper || interlace == MFVideoInterlace_FieldSingleLower)
1184 hr = MF_E_INVALIDMEDIATYPE;
1192 // Set or clear the input media type.
1194 // Prerequisite: The input type was already validated.
1196 void OcvImageManipulations::OnSetInputType(IMFMediaType *pmt)
1198 // if pmt is NULL, clear the type.
1199 // if pmt is non-NULL, set the type.
1201 SafeRelease(&m_pInputType);
1205 m_pInputType->AddRef();
1208 // Update the format information.
1213 // Set or clears the output media type.
1215 // Prerequisite: The output type was already validated.
1217 void OcvImageManipulations::OnSetOutputType(IMFMediaType *pmt)
1219 // If pmt is NULL, clear the type. Otherwise, set the type.
1221 SafeRelease(&m_pOutputType);
1222 m_pOutputType = pmt;
1225 m_pOutputType->AddRef();
1230 // Initialize streaming parameters.
1232 // This method is called if the client sends the MFT_MESSAGE_NOTIFY_BEGIN_STREAMING
1233 // message, or when the client processes a sample, whichever happens first.
1235 HRESULT OcvImageManipulations::BeginStreaming()
1239 if (!m_bStreamingInitialized)
1241 m_bStreamingInitialized = true;
1251 // This method is called if the client sends an MFT_MESSAGE_NOTIFY_END_STREAMING
1252 // message, or when the media type changes. In general, it should be called whenever
1253 // the streaming parameters need to be reset.
1255 HRESULT OcvImageManipulations::EndStreaming()
1257 m_bStreamingInitialized = false;
1263 // Generate output data.
1265 HRESULT OcvImageManipulations::OnProcessOutput(IMFMediaBuffer *pIn, IMFMediaBuffer *pOut)
1267 BYTE *pDest = NULL; // Destination buffer.
1268 LONG lDestStride = 0; // Destination stride.
1270 BYTE *pSrc = NULL; // Source buffer.
1271 LONG lSrcStride = 0; // Source stride.
1273 // Helper objects to lock the buffers.
1274 VideoBufferLock inputLock(pIn);
1275 VideoBufferLock outputLock(pOut);
1277 // Stride if the buffer does not support IMF2DBuffer
1278 LONG lDefaultStride = 0;
1280 HRESULT hr = GetDefaultStride(m_pInputType, &lDefaultStride);
1286 // Lock the input buffer.
1287 hr = inputLock.LockBuffer(lDefaultStride, m_imageHeightInPixels, &pSrc, &lSrcStride);
1293 // Lock the output buffer.
1294 hr = outputLock.LockBuffer(lDefaultStride, m_imageHeightInPixels, &pDest, &lDestStride);
1300 cv::Mat InputFrame(m_imageHeightInPixels + m_imageHeightInPixels/2, m_imageWidthInPixels, CV_8UC1, pSrc, lSrcStride);
1301 cv::Mat InputGreyScale(InputFrame, cv::Range(0, m_imageHeightInPixels), cv::Range(0, m_imageWidthInPixels));
1302 cv::Mat OutputFrame(m_imageHeightInPixels + m_imageHeightInPixels/2, m_imageWidthInPixels, CV_8UC1, pDest, lDestStride);
1304 switch (m_TransformType)
1308 InputFrame.copyTo(OutputFrame);
1312 OutputFrame.setTo(cv::Scalar(128));
1313 cv::Mat OutputGreyScale(OutputFrame, cv::Range(0, m_imageHeightInPixels), cv::Range(0, m_imageWidthInPixels));
1314 InputGreyScale.copyTo(OutputGreyScale);
1318 OutputFrame.setTo(cv::Scalar(128));
1319 cv::Mat OutputGreyScale(OutputFrame, cv::Range(0, m_imageHeightInPixels), cv::Range(0, m_imageWidthInPixels));
1320 cv::Canny(InputGreyScale, OutputGreyScale, 80, 90);
1325 OutputFrame.setTo(cv::Scalar(128));
1326 cv::Mat OutputGreyScale(OutputFrame, cv::Range(0, m_imageHeightInPixels), cv::Range(0, m_imageWidthInPixels));
1327 cv::Sobel(InputGreyScale, OutputGreyScale, CV_8U, 1, 1);
1331 const int mHistSizeNum = 25;
1332 const int channels[3][1] = {{0}, {1}, {2}};
1333 const int mHistSize[] = {25};
1334 const float baseRabge[] = {0.f,256.f};
1335 const float* ranges[] = {baseRabge};
1337 const cv::Scalar mColorsY[] = { cv::Scalar(76), cv::Scalar(149), cv::Scalar(29) };
1338 const cv::Scalar mColorsUV[] = { cv::Scalar(84, 255), cv::Scalar(43, 21), cv::Scalar(255, 107) };
1340 cv::Mat OutputY(m_imageHeightInPixels, m_imageWidthInPixels, CV_8UC1, pDest, lDestStride);
1341 cv::Mat OutputUV(m_imageHeightInPixels/2, m_imageWidthInPixels/2,
1342 CV_8UC2, pDest+m_imageHeightInPixels*lDestStride, lDestStride);
1345 InputFrame.copyTo(OutputFrame);
1347 cv::cvtColor(InputFrame, BgrFrame, cv::COLOR_YUV420sp2BGR);
1348 int thikness = (int) (BgrFrame.cols / (mHistSizeNum + 10) / 5);
1349 if(thikness > 5) thikness = 5;
1350 int offset = (int) ((BgrFrame.cols - (5*mHistSizeNum + 4*10)*thikness)/2);
1353 for (int c=0; c<3; c++)
1356 cv::calcHist(&BgrFrame, 1, channels[c], cv::Mat(), hist, 1, mHistSize, ranges);
1357 cv::normalize(hist, hist, BgrFrame.rows/2, 0, cv::NORM_INF);
1358 for(int h=0; h<mHistSizeNum; h++) {
1361 mP1.x = mP2.x = offset + (c * (mHistSizeNum + 10) + h) * thikness;
1362 mP1.y = BgrFrame.rows-1;
1363 mP2.y = mP1.y - 2 - (int)hist.at<float>(h);
1364 cv::line(OutputY, mP1, mP2, mColorsY[c], thikness);
1366 // Draw on UV planes
1371 cv::line(OutputUV, mP1, mP2, mColorsUV[c], thikness/2);
1379 // Set the data size on the output buffer.
1380 hr = pOut->SetCurrentLength(m_cbImageSize);
1388 HRESULT OcvImageManipulations::OnFlush()
1390 // For this MFT, flushing just means releasing the input sample.
1391 SafeRelease(&m_pSample);
1396 // Update the format information. This method is called whenever the
1397 // input type is set.
1399 HRESULT OcvImageManipulations::UpdateFormatInfo()
1403 GUID subtype = GUID_NULL;
1405 m_imageWidthInPixels = 0;
1406 m_imageHeightInPixels = 0;
1409 if (m_pInputType != NULL)
1411 hr = m_pInputType->GetGUID(MF_MT_SUBTYPE, &subtype);
1416 if (subtype != MFVideoFormat_NV12)
1422 hr = MFGetAttributeSize(m_pInputType, MF_MT_FRAME_SIZE, &m_imageWidthInPixels, &m_imageHeightInPixels);
1428 // Calculate the image size for YUV NV12 image(not including padding)
1429 m_cbImageSize = (m_imageHeightInPixels + m_imageHeightInPixels/2)*m_imageWidthInPixels;
1437 // Get the default stride for a video format.
1438 HRESULT GetDefaultStride(IMFMediaType *pType, LONG *plStride)
1442 // Try to get the default stride from the media type.
1443 HRESULT hr = pType->GetUINT32(MF_MT_DEFAULT_STRIDE, (UINT32*)&lStride);
1446 // Attribute not set. Try to calculate the default stride.
1447 GUID subtype = GUID_NULL;
1452 // Get the subtype and the image size.
1453 hr = pType->GetGUID(MF_MT_SUBTYPE, &subtype);
1456 hr = MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &width, &height);
1460 if (subtype == MFVideoFormat_NV12)
1464 else if (subtype == MFVideoFormat_YUY2 || subtype == MFVideoFormat_UYVY)
1466 lStride = ((width * 2) + 3) & ~3;
1474 // Set the attribute for later reference.
1477 (void)pType->SetUINT32(MF_MT_DEFAULT_STRIDE, UINT32(lStride));
1482 *plStride = lStride;