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
4 // PARTICULAR PURPOSE.
5 //
6 // Copyright (c) Microsoft Corporation. All rights reserved.
7 
8 #include "OcvTransform.h"
9 #include "bufferlock.h"
10 
11 #include <opencv2\core\core.hpp>
12 #include <opencv2\imgproc\imgproc.hpp>
13 #include <opencv2\features2d\features2d.hpp>
14 
15 
16 
17 using namespace Microsoft::WRL;
18 
19 /*
20 
21 This sample implements a video effect as a Media Foundation transform (MFT).
22 
23 NOTES ON THE MFT IMPLEMENTATION
24 
25 1. The MFT has fixed streams: One input stream and one output stream.
26 
27 2. The MFT supports NV12 format only.
28 
29 3. If the MFT is holding an input sample, SetInputType and SetOutputType both fail.
30 
31 4. The input and output types must be identical.
32 
33 5. If both types are set, no type can be set until the current type is cleared.
34 
35 6. Preferred input types:
36 
37      (a) If the output type is set, that's the preferred type.
38      (b) Otherwise, the preferred types are partial types, constructed from the
39          list of supported subtypes.
40 
41 7. Preferred output types: As above.
42 
43 8. Streaming:
44 
45     The private BeingStreaming() method is called in response to the
46     MFT_MESSAGE_NOTIFY_BEGIN_STREAMING message.
47 
48     If the client does not send MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, the MFT calls
49     BeginStreaming inside the first call to ProcessInput or ProcessOutput.
50 
51     This is a good approach for allocating resources that your MFT requires for
52     streaming.
53 
54 9. The configuration attributes are applied in the BeginStreaming method. If the
55    client changes the attributes during streaming, the change is ignored until
56    streaming is stopped (either by changing the media types or by sending the
57    MFT_MESSAGE_NOTIFY_END_STREAMING message) and then restarted.
58 
59 */
60 
61 
62 // Static array of media types (preferred and accepted).
63 const GUID g_MediaSubtypes[] =
64 {
65     MFVideoFormat_NV12
66 };
67 
68 HRESULT GetDefaultStride(IMFMediaType *pType, LONG *plStride);
69 
70 template <typename T>
clamp(const T & val,const T & minVal,const T & maxVal)71 inline T clamp(const T& val, const T& minVal, const T& maxVal)
72 {
73     return (val < minVal ? minVal : (val > maxVal ? maxVal : val));
74 }
75 
OcvImageManipulations()76 OcvImageManipulations::OcvImageManipulations() :
77     m_pSample(NULL), m_pInputType(NULL), m_pOutputType(NULL),
78     m_imageWidthInPixels(0), m_imageHeightInPixels(0), m_cbImageSize(0),
79     m_TransformType(Preview), m_bStreamingInitialized(false),
80     m_pAttributes(NULL)
81 {
82     InitializeCriticalSectionEx(&m_critSec, 3000, 0);
83 }
84 
~OcvImageManipulations()85 OcvImageManipulations::~OcvImageManipulations()
86 {
87     SafeRelease(&m_pInputType);
88     SafeRelease(&m_pOutputType);
89     SafeRelease(&m_pSample);
90     SafeRelease(&m_pAttributes);
91     DeleteCriticalSection(&m_critSec);
92 }
93 
94 // Initialize the instance.
RuntimeClassInitialize()95 STDMETHODIMP OcvImageManipulations::RuntimeClassInitialize()
96 {
97     // Create the attribute store.
98     return MFCreateAttributes(&m_pAttributes, 3);
99 }
100 
101 // IMediaExtension methods
102 
103 //-------------------------------------------------------------------
104 // SetProperties
105 // Sets the configuration of the effect
106 //-------------------------------------------------------------------
SetProperties(ABI::Windows::Foundation::Collections::IPropertySet * pConfiguration)107 HRESULT OcvImageManipulations::SetProperties(ABI::Windows::Foundation::Collections::IPropertySet *pConfiguration)
108 {
109     HRESULT hr = S_OK;
110 
111     if (!pConfiguration)
112         return hr;
113 
114     HSTRING key;
115     WindowsCreateString(L"{698649BE-8EAE-4551-A4CB-3EC98FBD3D86}", 38, &key);
116     Microsoft::WRL::ComPtr<ABI::Windows::Foundation::Collections::IMap<HSTRING, IInspectable *>> spSetting;
117     pConfiguration->QueryInterface(IID_PPV_ARGS(&spSetting));
118     boolean found;
119     spSetting->HasKey(key, &found);
120 
121     if (found)
122     {
123         Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IPropertyValue> spPropVal;
124         Microsoft::WRL::ComPtr<IInspectable> spInsp;
125 
126         spSetting->Lookup(key, spInsp.ReleaseAndGetAddressOf());
127 
128         hr = spInsp.As(&spPropVal);
129         if (hr != S_OK)
130         {
131             return hr;
132         }
133 
134         INT32 effect;
135         hr = spPropVal->GetInt32(&effect);
136         if (hr != S_OK)
137         {
138             return hr;
139         }
140 
141         if ((effect >= 0) && (effect < InvalidEffect))
142         {
143             m_TransformType = (ProcessingType)effect;
144         }
145     }
146 
147     return hr;
148 }
149 
150 // IMFTransform methods. Refer to the Media Foundation SDK documentation for details.
151 
152 //-------------------------------------------------------------------
153 // GetStreamLimits
154 // Returns the minimum and maximum number of streams.
155 //-------------------------------------------------------------------
156 
GetStreamLimits(DWORD * pdwInputMinimum,DWORD * pdwInputMaximum,DWORD * pdwOutputMinimum,DWORD * pdwOutputMaximum)157 HRESULT OcvImageManipulations::GetStreamLimits(
158     DWORD   *pdwInputMinimum,
159     DWORD   *pdwInputMaximum,
160     DWORD   *pdwOutputMinimum,
161     DWORD   *pdwOutputMaximum
162 )
163 {
164     if ((pdwInputMinimum == NULL) ||
165         (pdwInputMaximum == NULL) ||
166         (pdwOutputMinimum == NULL) ||
167         (pdwOutputMaximum == NULL))
168     {
169         return E_POINTER;
170     }
171 
172     // This MFT has a fixed number of streams.
173     *pdwInputMinimum = 1;
174     *pdwInputMaximum = 1;
175     *pdwOutputMinimum = 1;
176     *pdwOutputMaximum = 1;
177     return S_OK;
178 }
179 
180 
181 //-------------------------------------------------------------------
182 // GetStreamCount
183 // Returns the actual number of streams.
184 //-------------------------------------------------------------------
185 
GetStreamCount(DWORD * pcInputStreams,DWORD * pcOutputStreams)186 HRESULT OcvImageManipulations::GetStreamCount(
187     DWORD   *pcInputStreams,
188     DWORD   *pcOutputStreams
189 )
190 {
191     if ((pcInputStreams == NULL) || (pcOutputStreams == NULL))
192 
193     {
194         return E_POINTER;
195     }
196 
197     // This MFT has a fixed number of streams.
198     *pcInputStreams = 1;
199     *pcOutputStreams = 1;
200     return S_OK;
201 }
202 
203 
204 
205 //-------------------------------------------------------------------
206 // GetStreamIDs
207 // Returns stream IDs for the input and output streams.
208 //-------------------------------------------------------------------
209 
GetStreamIDs(DWORD dwInputIDArraySize,DWORD * pdwInputIDs,DWORD dwOutputIDArraySize,DWORD * pdwOutputIDs)210 HRESULT OcvImageManipulations::GetStreamIDs(
211     DWORD   dwInputIDArraySize,
212     DWORD   *pdwInputIDs,
213     DWORD   dwOutputIDArraySize,
214     DWORD   *pdwOutputIDs
215 )
216 {
217     // It is not required to implement this method if the MFT has a fixed number of
218     // streams AND the stream IDs are numbered sequentially from zero (that is, the
219     // stream IDs match the stream indexes).
220 
221     // In that case, it is OK to return E_NOTIMPL.
222     return E_NOTIMPL;
223 }
224 
225 
226 //-------------------------------------------------------------------
227 // GetInputStreamInfo
228 // Returns information about an input stream.
229 //-------------------------------------------------------------------
230 
GetInputStreamInfo(DWORD dwInputStreamID,MFT_INPUT_STREAM_INFO * pStreamInfo)231 HRESULT OcvImageManipulations::GetInputStreamInfo(
232     DWORD                     dwInputStreamID,
233     MFT_INPUT_STREAM_INFO *   pStreamInfo
234 )
235 {
236     if (pStreamInfo == NULL)
237     {
238         return E_POINTER;
239     }
240 
241     EnterCriticalSection(&m_critSec);
242 
243     if (!IsValidInputStream(dwInputStreamID))
244     {
245         LeaveCriticalSection(&m_critSec);
246         return MF_E_INVALIDSTREAMNUMBER;
247     }
248 
249     // NOTE: This method should succeed even when there is no media type on the
250     //       stream. If there is no media type, we only need to fill in the dwFlags
251     //       member of MFT_INPUT_STREAM_INFO. The other members depend on having a
252     //       a valid media type.
253 
254     pStreamInfo->hnsMaxLatency = 0;
255     pStreamInfo->dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES | MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER;
256 
257     if (m_pInputType == NULL)
258     {
259         pStreamInfo->cbSize = 0;
260     }
261     else
262     {
263         pStreamInfo->cbSize = m_cbImageSize;
264     }
265 
266     pStreamInfo->cbMaxLookahead = 0;
267     pStreamInfo->cbAlignment = 0;
268 
269     LeaveCriticalSection(&m_critSec);
270     return S_OK;
271 }
272 
273 //-------------------------------------------------------------------
274 // GetOutputStreamInfo
275 // Returns information about an output stream.
276 //-------------------------------------------------------------------
277 
GetOutputStreamInfo(DWORD dwOutputStreamID,MFT_OUTPUT_STREAM_INFO * pStreamInfo)278 HRESULT OcvImageManipulations::GetOutputStreamInfo(
279     DWORD                     dwOutputStreamID,
280     MFT_OUTPUT_STREAM_INFO *  pStreamInfo
281 )
282 {
283     if (pStreamInfo == NULL)
284     {
285         return E_POINTER;
286     }
287 
288     EnterCriticalSection(&m_critSec);
289 
290     if (!IsValidOutputStream(dwOutputStreamID))
291     {
292         LeaveCriticalSection(&m_critSec);
293         return MF_E_INVALIDSTREAMNUMBER;
294     }
295 
296     // NOTE: This method should succeed even when there is no media type on the
297     //       stream. If there is no media type, we only need to fill in the dwFlags
298     //       member of MFT_OUTPUT_STREAM_INFO. The other members depend on having a
299     //       a valid media type.
300 
301     pStreamInfo->dwFlags =
302         MFT_OUTPUT_STREAM_WHOLE_SAMPLES |
303         MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER |
304         MFT_OUTPUT_STREAM_FIXED_SAMPLE_SIZE ;
305 
306     if (m_pOutputType == NULL)
307     {
308         pStreamInfo->cbSize = 0;
309     }
310     else
311     {
312         pStreamInfo->cbSize = m_cbImageSize;
313     }
314 
315     pStreamInfo->cbAlignment = 0;
316 
317     LeaveCriticalSection(&m_critSec);
318     return S_OK;
319 }
320 
321 
322 //-------------------------------------------------------------------
323 // GetAttributes
324 // Returns the attributes for the MFT.
325 //-------------------------------------------------------------------
326 
GetAttributes(IMFAttributes ** ppAttributes)327 HRESULT OcvImageManipulations::GetAttributes(IMFAttributes** ppAttributes)
328 {
329     if (ppAttributes == NULL)
330     {
331         return E_POINTER;
332     }
333 
334     EnterCriticalSection(&m_critSec);
335 
336     *ppAttributes = m_pAttributes;
337     (*ppAttributes)->AddRef();
338 
339     LeaveCriticalSection(&m_critSec);
340     return S_OK;
341 }
342 
343 
344 //-------------------------------------------------------------------
345 // GetInputStreamAttributes
346 // Returns stream-level attributes for an input stream.
347 //-------------------------------------------------------------------
348 
GetInputStreamAttributes(DWORD dwInputStreamID,IMFAttributes ** ppAttributes)349 HRESULT OcvImageManipulations::GetInputStreamAttributes(
350     DWORD           dwInputStreamID,
351     IMFAttributes   **ppAttributes
352 )
353 {
354     // This MFT does not support any stream-level attributes, so the method is not implemented.
355     return E_NOTIMPL;
356 }
357 
358 
359 //-------------------------------------------------------------------
360 // GetOutputStreamAttributes
361 // Returns stream-level attributes for an output stream.
362 //-------------------------------------------------------------------
363 
GetOutputStreamAttributes(DWORD dwOutputStreamID,IMFAttributes ** ppAttributes)364 HRESULT OcvImageManipulations::GetOutputStreamAttributes(
365     DWORD           dwOutputStreamID,
366     IMFAttributes   **ppAttributes
367 )
368 {
369     // This MFT does not support any stream-level attributes, so the method is not implemented.
370     return E_NOTIMPL;
371 }
372 
373 
374 //-------------------------------------------------------------------
375 // DeleteInputStream
376 //-------------------------------------------------------------------
377 
DeleteInputStream(DWORD dwStreamID)378 HRESULT OcvImageManipulations::DeleteInputStream(DWORD dwStreamID)
379 {
380     // This MFT has a fixed number of input streams, so the method is not supported.
381     return E_NOTIMPL;
382 }
383 
384 
385 //-------------------------------------------------------------------
386 // AddInputStreams
387 //-------------------------------------------------------------------
388 
AddInputStreams(DWORD cStreams,DWORD * adwStreamIDs)389 HRESULT OcvImageManipulations::AddInputStreams(
390     DWORD   cStreams,
391     DWORD   *adwStreamIDs
392 )
393 {
394     // This MFT has a fixed number of output streams, so the method is not supported.
395     return E_NOTIMPL;
396 }
397 
398 
399 //-------------------------------------------------------------------
400 // GetInputAvailableType
401 // Returns a preferred input type.
402 //-------------------------------------------------------------------
403 
GetInputAvailableType(DWORD dwInputStreamID,DWORD dwTypeIndex,IMFMediaType ** ppType)404 HRESULT OcvImageManipulations::GetInputAvailableType(
405     DWORD           dwInputStreamID,
406     DWORD           dwTypeIndex, // 0-based
407     IMFMediaType    **ppType
408 )
409 {
410     if (ppType == NULL)
411     {
412         return E_INVALIDARG;
413     }
414 
415     EnterCriticalSection(&m_critSec);
416 
417     if (!IsValidInputStream(dwInputStreamID))
418     {
419         LeaveCriticalSection(&m_critSec);
420         return MF_E_INVALIDSTREAMNUMBER;
421     }
422 
423     HRESULT hr = S_OK;
424 
425     // If the output type is set, return that type as our preferred input type.
426     if (m_pOutputType == NULL)
427     {
428         // The output type is not set. Create a partial media type.
429         hr = OnGetPartialType(dwTypeIndex, ppType);
430     }
431     else if (dwTypeIndex > 0)
432     {
433         hr = MF_E_NO_MORE_TYPES;
434     }
435     else
436     {
437         *ppType = m_pOutputType;
438         (*ppType)->AddRef();
439     }
440 
441     LeaveCriticalSection(&m_critSec);
442     return hr;
443 }
444 
445 
446 
447 //-------------------------------------------------------------------
448 // GetOutputAvailableType
449 // Returns a preferred output type.
450 //-------------------------------------------------------------------
451 
GetOutputAvailableType(DWORD dwOutputStreamID,DWORD dwTypeIndex,IMFMediaType ** ppType)452 HRESULT OcvImageManipulations::GetOutputAvailableType(
453     DWORD           dwOutputStreamID,
454     DWORD           dwTypeIndex, // 0-based
455     IMFMediaType    **ppType
456 )
457 {
458     if (ppType == NULL)
459     {
460         return E_INVALIDARG;
461     }
462 
463     EnterCriticalSection(&m_critSec);
464 
465     if (!IsValidOutputStream(dwOutputStreamID))
466     {
467         LeaveCriticalSection(&m_critSec);
468         return MF_E_INVALIDSTREAMNUMBER;
469     }
470 
471     HRESULT hr = S_OK;
472 
473     if (m_pInputType == NULL)
474     {
475         // The input type is not set. Create a partial media type.
476         hr = OnGetPartialType(dwTypeIndex, ppType);
477     }
478     else if (dwTypeIndex > 0)
479     {
480         hr = MF_E_NO_MORE_TYPES;
481     }
482     else
483     {
484         *ppType = m_pInputType;
485         (*ppType)->AddRef();
486     }
487 
488     LeaveCriticalSection(&m_critSec);
489     return hr;
490 }
491 
492 
493 //-------------------------------------------------------------------
494 // SetInputType
495 //-------------------------------------------------------------------
496 
SetInputType(DWORD dwInputStreamID,IMFMediaType * pType,DWORD dwFlags)497 HRESULT OcvImageManipulations::SetInputType(
498     DWORD           dwInputStreamID,
499     IMFMediaType    *pType, // Can be NULL to clear the input type.
500     DWORD           dwFlags
501 )
502 {
503     // Validate flags.
504     if (dwFlags & ~MFT_SET_TYPE_TEST_ONLY)
505     {
506         return E_INVALIDARG;
507     }
508 
509     EnterCriticalSection(&m_critSec);
510 
511     if (!IsValidInputStream(dwInputStreamID))
512     {
513         LeaveCriticalSection(&m_critSec);
514         return MF_E_INVALIDSTREAMNUMBER;
515     }
516 
517     HRESULT hr = S_OK;
518 
519     // Does the caller want us to set the type, or just test it?
520     BOOL bReallySet = ((dwFlags & MFT_SET_TYPE_TEST_ONLY) == 0);
521 
522     // If we have an input sample, the client cannot change the type now.
523     if (HasPendingOutput())
524     {
525         hr = MF_E_TRANSFORM_CANNOT_CHANGE_MEDIATYPE_WHILE_PROCESSING;
526         goto done;
527     }
528 
529     // Validate the type, if non-NULL.
530     if (pType)
531     {
532         hr = OnCheckInputType(pType);
533         if (FAILED(hr))
534         {
535             goto done;
536         }
537     }
538 
539     // The type is OK. Set the type, unless the caller was just testing.
540     if (bReallySet)
541     {
542         OnSetInputType(pType);
543 
544         // When the type changes, end streaming.
545         hr = EndStreaming();
546     }
547 
548 done:
549     LeaveCriticalSection(&m_critSec);
550     return hr;
551 }
552 
553 
554 
555 //-------------------------------------------------------------------
556 // SetOutputType
557 //-------------------------------------------------------------------
558 
SetOutputType(DWORD dwOutputStreamID,IMFMediaType * pType,DWORD dwFlags)559 HRESULT OcvImageManipulations::SetOutputType(
560     DWORD           dwOutputStreamID,
561     IMFMediaType    *pType, // Can be NULL to clear the output type.
562     DWORD           dwFlags
563 )
564 {
565     // Validate flags.
566     if (dwFlags & ~MFT_SET_TYPE_TEST_ONLY)
567     {
568         return E_INVALIDARG;
569     }
570 
571     EnterCriticalSection(&m_critSec);
572 
573     if (!IsValidOutputStream(dwOutputStreamID))
574     {
575         LeaveCriticalSection(&m_critSec);
576         return MF_E_INVALIDSTREAMNUMBER;
577     }
578 
579     HRESULT hr = S_OK;
580 
581     // Does the caller want us to set the type, or just test it?
582     BOOL bReallySet = ((dwFlags & MFT_SET_TYPE_TEST_ONLY) == 0);
583 
584     // If we have an input sample, the client cannot change the type now.
585     if (HasPendingOutput())
586     {
587         hr = MF_E_TRANSFORM_CANNOT_CHANGE_MEDIATYPE_WHILE_PROCESSING;
588         goto done;
589     }
590 
591     // Validate the type, if non-NULL.
592     if (pType)
593     {
594         hr = OnCheckOutputType(pType);
595         if (FAILED(hr))
596         {
597             goto done;
598         }
599     }
600 
601     // The type is OK. Set the type, unless the caller was just testing.
602     if (bReallySet)
603     {
604         OnSetOutputType(pType);
605 
606         // When the type changes, end streaming.
607         hr = EndStreaming();
608     }
609 
610 done:
611     LeaveCriticalSection(&m_critSec);
612     return hr;
613 }
614 
615 
616 //-------------------------------------------------------------------
617 // GetInputCurrentType
618 // Returns the current input type.
619 //-------------------------------------------------------------------
620 
GetInputCurrentType(DWORD dwInputStreamID,IMFMediaType ** ppType)621 HRESULT OcvImageManipulations::GetInputCurrentType(
622     DWORD           dwInputStreamID,
623     IMFMediaType    **ppType
624 )
625 {
626     if (ppType == NULL)
627     {
628         return E_POINTER;
629     }
630 
631     HRESULT hr = S_OK;
632 
633     EnterCriticalSection(&m_critSec);
634 
635     if (!IsValidInputStream(dwInputStreamID))
636     {
637         hr = MF_E_INVALIDSTREAMNUMBER;
638     }
639     else if (!m_pInputType)
640     {
641         hr = MF_E_TRANSFORM_TYPE_NOT_SET;
642     }
643     else
644     {
645         *ppType = m_pInputType;
646         (*ppType)->AddRef();
647     }
648     LeaveCriticalSection(&m_critSec);
649     return hr;
650 }
651 
652 
653 //-------------------------------------------------------------------
654 // GetOutputCurrentType
655 // Returns the current output type.
656 //-------------------------------------------------------------------
657 
GetOutputCurrentType(DWORD dwOutputStreamID,IMFMediaType ** ppType)658 HRESULT OcvImageManipulations::GetOutputCurrentType(
659     DWORD           dwOutputStreamID,
660     IMFMediaType    **ppType
661 )
662 {
663     if (ppType == NULL)
664     {
665         return E_POINTER;
666     }
667 
668     HRESULT hr = S_OK;
669 
670     EnterCriticalSection(&m_critSec);
671 
672     if (!IsValidOutputStream(dwOutputStreamID))
673     {
674         hr = MF_E_INVALIDSTREAMNUMBER;
675     }
676     else if (!m_pOutputType)
677     {
678         hr = MF_E_TRANSFORM_TYPE_NOT_SET;
679     }
680     else
681     {
682         *ppType = m_pOutputType;
683         (*ppType)->AddRef();
684     }
685 
686     LeaveCriticalSection(&m_critSec);
687     return hr;
688 }
689 
690 
691 //-------------------------------------------------------------------
692 // GetInputStatus
693 // Query if the MFT is accepting more input.
694 //-------------------------------------------------------------------
695 
GetInputStatus(DWORD dwInputStreamID,DWORD * pdwFlags)696 HRESULT OcvImageManipulations::GetInputStatus(
697     DWORD           dwInputStreamID,
698     DWORD           *pdwFlags
699 )
700 {
701     if (pdwFlags == NULL)
702     {
703         return E_POINTER;
704     }
705 
706     EnterCriticalSection(&m_critSec);
707 
708     if (!IsValidInputStream(dwInputStreamID))
709     {
710         LeaveCriticalSection(&m_critSec);
711         return MF_E_INVALIDSTREAMNUMBER;
712     }
713 
714     // If an input sample is already queued, do not accept another sample until the
715     // client calls ProcessOutput or Flush.
716 
717     // NOTE: It is possible for an MFT to accept more than one input sample. For
718     // example, this might be required in a video decoder if the frames do not
719     // arrive in temporal order. In the case, the decoder must hold a queue of
720     // samples. For the video effect, each sample is transformed independently, so
721     // there is no reason to queue multiple input samples.
722 
723     if (m_pSample == NULL)
724     {
725         *pdwFlags = MFT_INPUT_STATUS_ACCEPT_DATA;
726     }
727     else
728     {
729         *pdwFlags = 0;
730     }
731 
732     LeaveCriticalSection(&m_critSec);
733     return S_OK;
734 }
735 
736 
737 
738 //-------------------------------------------------------------------
739 // GetOutputStatus
740 // Query if the MFT can produce output.
741 //-------------------------------------------------------------------
742 
GetOutputStatus(DWORD * pdwFlags)743 HRESULT OcvImageManipulations::GetOutputStatus(DWORD *pdwFlags)
744 {
745     if (pdwFlags == NULL)
746     {
747         return E_POINTER;
748     }
749 
750     EnterCriticalSection(&m_critSec);
751 
752     // The MFT can produce an output sample if (and only if) there an input sample.
753     if (m_pSample != NULL)
754     {
755         *pdwFlags = MFT_OUTPUT_STATUS_SAMPLE_READY;
756     }
757     else
758     {
759         *pdwFlags = 0;
760     }
761 
762     LeaveCriticalSection(&m_critSec);
763     return S_OK;
764 }
765 
766 
767 //-------------------------------------------------------------------
768 // SetOutputBounds
769 // Sets the range of time stamps that the MFT will output.
770 //-------------------------------------------------------------------
771 
SetOutputBounds(LONGLONG hnsLowerBound,LONGLONG hnsUpperBound)772 HRESULT OcvImageManipulations::SetOutputBounds(
773     LONGLONG        hnsLowerBound,
774     LONGLONG        hnsUpperBound
775 )
776 {
777     // Implementation of this method is optional.
778     return E_NOTIMPL;
779 }
780 
781 
782 //-------------------------------------------------------------------
783 // ProcessEvent
784 // Sends an event to an input stream.
785 //-------------------------------------------------------------------
786 
ProcessEvent(DWORD dwInputStreamID,IMFMediaEvent * pEvent)787 HRESULT OcvImageManipulations::ProcessEvent(
788     DWORD              dwInputStreamID,
789     IMFMediaEvent      *pEvent
790 )
791 {
792     // This MFT does not handle any stream events, so the method can
793     // return E_NOTIMPL. This tells the pipeline that it can stop
794     // sending any more events to this MFT.
795     return E_NOTIMPL;
796 }
797 
798 
799 //-------------------------------------------------------------------
800 // ProcessMessage
801 //-------------------------------------------------------------------
802 
ProcessMessage(MFT_MESSAGE_TYPE eMessage,ULONG_PTR ulParam)803 HRESULT OcvImageManipulations::ProcessMessage(
804     MFT_MESSAGE_TYPE    eMessage,
805     ULONG_PTR           ulParam
806 )
807 {
808     EnterCriticalSection(&m_critSec);
809 
810     HRESULT hr = S_OK;
811 
812     switch (eMessage)
813     {
814     case MFT_MESSAGE_COMMAND_FLUSH:
815         // Flush the MFT.
816         hr = OnFlush();
817         break;
818 
819     case MFT_MESSAGE_COMMAND_DRAIN:
820         // Drain: Tells the MFT to reject further input until all pending samples are
821         // processed. That is our default behavior already, so there is nothing to do.
822         //
823         // For a decoder that accepts a queue of samples, the MFT might need to drain
824         // the queue in response to this command.
825     break;
826 
827     case MFT_MESSAGE_SET_D3D_MANAGER:
828         // Sets a pointer to the IDirect3DDeviceManager9 interface.
829 
830         // The pipeline should never send this message unless the MFT sets the MF_SA_D3D_AWARE
831         // attribute set to TRUE. Because this MFT does not set MF_SA_D3D_AWARE, it is an error
832         // to send the MFT_MESSAGE_SET_D3D_MANAGER message to the MFT. Return an error code in
833         // this case.
834 
835         // NOTE: If this MFT were D3D-enabled, it would cache the IDirect3DDeviceManager9
836         // pointer for use during streaming.
837 
838         hr = E_NOTIMPL;
839         break;
840 
841     case MFT_MESSAGE_NOTIFY_BEGIN_STREAMING:
842         hr = BeginStreaming();
843         break;
844 
845     case MFT_MESSAGE_NOTIFY_END_STREAMING:
846         hr = EndStreaming();
847         break;
848 
849     // The next two messages do not require any action from this MFT.
850 
851     case MFT_MESSAGE_NOTIFY_END_OF_STREAM:
852         break;
853 
854     case MFT_MESSAGE_NOTIFY_START_OF_STREAM:
855         break;
856     }
857 
858     LeaveCriticalSection(&m_critSec);
859     return hr;
860 }
861 
862 
863 //-------------------------------------------------------------------
864 // ProcessInput
865 // Process an input sample.
866 //-------------------------------------------------------------------
867 
ProcessInput(DWORD dwInputStreamID,IMFSample * pSample,DWORD dwFlags)868 HRESULT OcvImageManipulations::ProcessInput(
869     DWORD               dwInputStreamID,
870     IMFSample           *pSample,
871     DWORD               dwFlags
872 )
873 {
874     // Check input parameters.
875     if (pSample == NULL)
876     {
877         return E_POINTER;
878     }
879 
880     if (dwFlags != 0)
881     {
882         return E_INVALIDARG; // dwFlags is reserved and must be zero.
883     }
884 
885     HRESULT hr = S_OK;
886 
887     EnterCriticalSection(&m_critSec);
888 
889     // Validate the input stream number.
890     if (!IsValidInputStream(dwInputStreamID))
891     {
892         hr = MF_E_INVALIDSTREAMNUMBER;
893         goto done;
894     }
895 
896     // Check for valid media types.
897     // The client must set input and output types before calling ProcessInput.
898     if (!m_pInputType || !m_pOutputType)
899     {
900         hr = MF_E_NOTACCEPTING;
901         goto done;
902     }
903 
904     // Check if an input sample is already queued.
905     if (m_pSample != NULL)
906     {
907         hr = MF_E_NOTACCEPTING;   // We already have an input sample.
908         goto done;
909     }
910 
911     // Initialize streaming.
912     hr = BeginStreaming();
913     if (FAILED(hr))
914     {
915         goto done;
916     }
917 
918     // Cache the sample. We do the actual work in ProcessOutput.
919     m_pSample = pSample;
920     pSample->AddRef();  // Hold a reference count on the sample.
921 
922 done:
923     LeaveCriticalSection(&m_critSec);
924     return hr;
925 }
926 
927 
928 //-------------------------------------------------------------------
929 // ProcessOutput
930 // Process an output sample.
931 //-------------------------------------------------------------------
932 
ProcessOutput(DWORD dwFlags,DWORD cOutputBufferCount,MFT_OUTPUT_DATA_BUFFER * pOutputSamples,DWORD * pdwStatus)933 HRESULT OcvImageManipulations::ProcessOutput(
934     DWORD                   dwFlags,
935     DWORD                   cOutputBufferCount,
936     MFT_OUTPUT_DATA_BUFFER  *pOutputSamples, // one per stream
937     DWORD                   *pdwStatus
938 )
939 {
940     // Check input parameters...
941 
942     // This MFT does not accept any flags for the dwFlags parameter.
943 
944     // The only defined flag is MFT_PROCESS_OUTPUT_DISCARD_WHEN_NO_BUFFER. This flag
945     // applies only when the MFT marks an output stream as lazy or optional. But this
946     // MFT has no lazy or optional streams, so the flag is not valid.
947 
948     if (dwFlags != 0)
949     {
950         return E_INVALIDARG;
951     }
952 
953     if (pOutputSamples == NULL || pdwStatus == NULL)
954     {
955         return E_POINTER;
956     }
957 
958     // There must be exactly one output buffer.
959     if (cOutputBufferCount != 1)
960     {
961         return E_INVALIDARG;
962     }
963 
964     // It must contain a sample.
965     if (pOutputSamples[0].pSample == NULL)
966     {
967         return E_INVALIDARG;
968     }
969 
970     HRESULT hr = S_OK;
971 
972     IMFMediaBuffer *pInput = NULL;
973     IMFMediaBuffer *pOutput = NULL;
974 
975     EnterCriticalSection(&m_critSec);
976 
977     // There must be an input sample available for processing.
978     if (m_pSample == NULL)
979     {
980         hr = MF_E_TRANSFORM_NEED_MORE_INPUT;
981         goto done;
982     }
983 
984     // Initialize streaming.
985 
986     hr = BeginStreaming();
987     if (FAILED(hr))
988     {
989         goto done;
990     }
991 
992     // Get the input buffer.
993     hr = m_pSample->ConvertToContiguousBuffer(&pInput);
994     if (FAILED(hr))
995     {
996         goto done;
997     }
998 
999     // Get the output buffer.
1000     hr = pOutputSamples[0].pSample->ConvertToContiguousBuffer(&pOutput);
1001     if (FAILED(hr))
1002     {
1003         goto done;
1004     }
1005 
1006     hr = OnProcessOutput(pInput, pOutput);
1007     if (FAILED(hr))
1008     {
1009         goto done;
1010     }
1011 
1012     // Set status flags.
1013     pOutputSamples[0].dwStatus = 0;
1014     *pdwStatus = 0;
1015 
1016 
1017     // Copy the duration and time stamp from the input sample, if present.
1018 
1019     LONGLONG hnsDuration = 0;
1020     LONGLONG hnsTime = 0;
1021 
1022     if (SUCCEEDED(m_pSample->GetSampleDuration(&hnsDuration)))
1023     {
1024         hr = pOutputSamples[0].pSample->SetSampleDuration(hnsDuration);
1025         if (FAILED(hr))
1026         {
1027             goto done;
1028         }
1029     }
1030 
1031     if (SUCCEEDED(m_pSample->GetSampleTime(&hnsTime)))
1032     {
1033         hr = pOutputSamples[0].pSample->SetSampleTime(hnsTime);
1034     }
1035 
1036 done:
1037     SafeRelease(&m_pSample);   // Release our input sample.
1038     SafeRelease(&pInput);
1039     SafeRelease(&pOutput);
1040     LeaveCriticalSection(&m_critSec);
1041     return hr;
1042 }
1043 
1044 // PRIVATE METHODS
1045 
1046 // All methods that follow are private to this MFT and are not part of the IMFTransform interface.
1047 
1048 // Create a partial media type from our list.
1049 //
1050 // dwTypeIndex: Index into the list of peferred media types.
1051 // ppmt:        Receives a pointer to the media type.
1052 
OnGetPartialType(DWORD dwTypeIndex,IMFMediaType ** ppmt)1053 HRESULT OcvImageManipulations::OnGetPartialType(DWORD dwTypeIndex, IMFMediaType **ppmt)
1054 {
1055     if (dwTypeIndex >= ARRAYSIZE(g_MediaSubtypes))
1056     {
1057         return MF_E_NO_MORE_TYPES;
1058     }
1059 
1060     IMFMediaType *pmt = NULL;
1061 
1062     HRESULT hr = MFCreateMediaType(&pmt);
1063     if (FAILED(hr))
1064     {
1065         goto done;
1066     }
1067 
1068     hr = pmt->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
1069     if (FAILED(hr))
1070     {
1071         goto done;
1072     }
1073 
1074     hr = pmt->SetGUID(MF_MT_SUBTYPE, g_MediaSubtypes[dwTypeIndex]);
1075     if (FAILED(hr))
1076     {
1077         goto done;
1078     }
1079 
1080     *ppmt = pmt;
1081     (*ppmt)->AddRef();
1082 
1083 done:
1084     SafeRelease(&pmt);
1085     return hr;
1086 }
1087 
1088 
1089 // Validate an input media type.
1090 
OnCheckInputType(IMFMediaType * pmt)1091 HRESULT OcvImageManipulations::OnCheckInputType(IMFMediaType *pmt)
1092 {
1093     assert(pmt != NULL);
1094 
1095     HRESULT hr = S_OK;
1096 
1097     // If the output type is set, see if they match.
1098     if (m_pOutputType != NULL)
1099     {
1100         DWORD flags = 0;
1101         hr = pmt->IsEqual(m_pOutputType, &flags);
1102 
1103         // IsEqual can return S_FALSE. Treat this as failure.
1104         if (hr != S_OK)
1105         {
1106             hr = MF_E_INVALIDMEDIATYPE;
1107         }
1108     }
1109     else
1110     {
1111         // Output type is not set. Just check this type.
1112         hr = OnCheckMediaType(pmt);
1113     }
1114     return hr;
1115 }
1116 
1117 
1118 // Validate an output media type.
1119 
OnCheckOutputType(IMFMediaType * pmt)1120 HRESULT OcvImageManipulations::OnCheckOutputType(IMFMediaType *pmt)
1121 {
1122     assert(pmt != NULL);
1123 
1124     HRESULT hr = S_OK;
1125 
1126     // If the input type is set, see if they match.
1127     if (m_pInputType != NULL)
1128     {
1129         DWORD flags = 0;
1130         hr = pmt->IsEqual(m_pInputType, &flags);
1131 
1132         // IsEqual can return S_FALSE. Treat this as failure.
1133         if (hr != S_OK)
1134         {
1135             hr = MF_E_INVALIDMEDIATYPE;
1136         }
1137 
1138     }
1139     else
1140     {
1141         // Input type is not set. Just check this type.
1142         hr = OnCheckMediaType(pmt);
1143     }
1144     return hr;
1145 }
1146 
1147 
1148 // Validate a media type (input or output)
1149 
OnCheckMediaType(IMFMediaType * pmt)1150 HRESULT OcvImageManipulations::OnCheckMediaType(IMFMediaType *pmt)
1151 {
1152     BOOL bFoundMatchingSubtype = FALSE;
1153 
1154     // Major type must be video.
1155     GUID major_type;
1156     HRESULT hr = pmt->GetGUID(MF_MT_MAJOR_TYPE, &major_type);
1157     if (FAILED(hr))
1158     {
1159         goto done;
1160     }
1161 
1162     if (major_type != MFMediaType_Video)
1163     {
1164         hr = MF_E_INVALIDMEDIATYPE;
1165         goto done;
1166     }
1167 
1168     // Subtype must be one of the subtypes in our global list.
1169 
1170     // Get the subtype GUID.
1171     GUID subtype;
1172     hr = pmt->GetGUID(MF_MT_SUBTYPE, &subtype);
1173     if (FAILED(hr))
1174     {
1175         goto done;
1176     }
1177 
1178     // Look for the subtype in our list of accepted types.
1179     for (DWORD i = 0; i < ARRAYSIZE(g_MediaSubtypes); i++)
1180     {
1181         if (subtype == g_MediaSubtypes[i])
1182         {
1183             bFoundMatchingSubtype = TRUE;
1184             break;
1185         }
1186     }
1187 
1188     if (!bFoundMatchingSubtype)
1189     {
1190         hr = MF_E_INVALIDMEDIATYPE; // The MFT does not support this subtype.
1191         goto done;
1192     }
1193 
1194     // Reject single-field media types.
1195     UINT32 interlace = MFGetAttributeUINT32(pmt, MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
1196     if (interlace == MFVideoInterlace_FieldSingleUpper  || interlace == MFVideoInterlace_FieldSingleLower)
1197     {
1198         hr = MF_E_INVALIDMEDIATYPE;
1199     }
1200 
1201 done:
1202     return hr;
1203 }
1204 
1205 
1206 // Set or clear the input media type.
1207 //
1208 // Prerequisite: The input type was already validated.
1209 
OnSetInputType(IMFMediaType * pmt)1210 void OcvImageManipulations::OnSetInputType(IMFMediaType *pmt)
1211 {
1212     // if pmt is NULL, clear the type.
1213     // if pmt is non-NULL, set the type.
1214 
1215     SafeRelease(&m_pInputType);
1216     m_pInputType = pmt;
1217     if (m_pInputType)
1218     {
1219         m_pInputType->AddRef();
1220     }
1221 
1222     // Update the format information.
1223     UpdateFormatInfo();
1224 }
1225 
1226 
1227 // Set or clears the output media type.
1228 //
1229 // Prerequisite: The output type was already validated.
1230 
OnSetOutputType(IMFMediaType * pmt)1231 void OcvImageManipulations::OnSetOutputType(IMFMediaType *pmt)
1232 {
1233     // If pmt is NULL, clear the type. Otherwise, set the type.
1234 
1235     SafeRelease(&m_pOutputType);
1236     m_pOutputType = pmt;
1237     if (m_pOutputType)
1238     {
1239         m_pOutputType->AddRef();
1240     }
1241 }
1242 
1243 
1244 // Initialize streaming parameters.
1245 //
1246 // This method is called if the client sends the MFT_MESSAGE_NOTIFY_BEGIN_STREAMING
1247 // message, or when the client processes a sample, whichever happens first.
1248 
BeginStreaming()1249 HRESULT OcvImageManipulations::BeginStreaming()
1250 {
1251     HRESULT hr = S_OK;
1252 
1253     if (!m_bStreamingInitialized)
1254     {
1255         m_bStreamingInitialized = true;
1256         hr = S_OK;
1257     }
1258 
1259     return hr;
1260 }
1261 
1262 
1263 // End streaming.
1264 
1265 // This method is called if the client sends an MFT_MESSAGE_NOTIFY_END_STREAMING
1266 // message, or when the media type changes. In general, it should be called whenever
1267 // the streaming parameters need to be reset.
1268 
EndStreaming()1269 HRESULT OcvImageManipulations::EndStreaming()
1270 {
1271     m_bStreamingInitialized = false;
1272     return S_OK;
1273 }
1274 
1275 
1276 
1277 // Generate output data.
1278 
OnProcessOutput(IMFMediaBuffer * pIn,IMFMediaBuffer * pOut)1279 HRESULT OcvImageManipulations::OnProcessOutput(IMFMediaBuffer *pIn, IMFMediaBuffer *pOut)
1280 {
1281     BYTE *pDest = NULL;         // Destination buffer.
1282     LONG lDestStride = 0;       // Destination stride.
1283 
1284     BYTE *pSrc = NULL;          // Source buffer.
1285     LONG lSrcStride = 0;        // Source stride.
1286 
1287     // Helper objects to lock the buffers.
1288     VideoBufferLock inputLock(pIn);
1289     VideoBufferLock outputLock(pOut);
1290 
1291     // Stride if the buffer does not support IMF2DBuffer
1292     LONG lDefaultStride = 0;
1293 
1294     HRESULT hr = GetDefaultStride(m_pInputType, &lDefaultStride);
1295     if (FAILED(hr))
1296     {
1297         return hr;
1298     }
1299 
1300     // Lock the input buffer.
1301     hr = inputLock.LockBuffer(lDefaultStride, m_imageHeightInPixels, &pSrc, &lSrcStride);
1302     if (FAILED(hr))
1303     {
1304         return hr;
1305     }
1306 
1307     // Lock the output buffer.
1308     hr = outputLock.LockBuffer(lDefaultStride, m_imageHeightInPixels, &pDest, &lDestStride);
1309     if (FAILED(hr))
1310     {
1311         return hr;
1312     }
1313 
1314     cv::Mat InputFrame(m_imageHeightInPixels + m_imageHeightInPixels/2, m_imageWidthInPixels, CV_8UC1, pSrc, lSrcStride);
1315     cv::Mat InputGreyScale(InputFrame, cv::Range(0, m_imageHeightInPixels), cv::Range(0, m_imageWidthInPixels));
1316     cv::Mat OutputFrame(m_imageHeightInPixels + m_imageHeightInPixels/2, m_imageWidthInPixels, CV_8UC1, pDest, lDestStride);
1317 
1318     switch (m_TransformType)
1319     {
1320     case Preview:
1321         {
1322             InputFrame.copyTo(OutputFrame);
1323         } break;
1324     case GrayScale:
1325         {
1326             OutputFrame.setTo(cv::Scalar(128));
1327             cv::Mat OutputGreyScale(OutputFrame, cv::Range(0, m_imageHeightInPixels), cv::Range(0, m_imageWidthInPixels));
1328             InputGreyScale.copyTo(OutputGreyScale);
1329         } break;
1330     case Canny:
1331         {
1332             OutputFrame.setTo(cv::Scalar(128));
1333             cv::Mat OutputGreyScale(OutputFrame, cv::Range(0, m_imageHeightInPixels), cv::Range(0, m_imageWidthInPixels));
1334             cv::Canny(InputGreyScale, OutputGreyScale, 80, 90);
1335 
1336         } break;
1337     case Sobel:
1338         {
1339             OutputFrame.setTo(cv::Scalar(128));
1340             cv::Mat OutputGreyScale(OutputFrame, cv::Range(0, m_imageHeightInPixels), cv::Range(0, m_imageWidthInPixels));
1341             cv::Sobel(InputGreyScale, OutputGreyScale, CV_8U, 1, 1);
1342         } break;
1343     case Histogram:
1344         {
1345             const int mHistSizeNum = 25;
1346             const int channels[3][1] = {{0}, {1}, {2}};
1347             const int mHistSize[] = {25};
1348             const float baseRabge[] = {0.f,256.f};
1349             const float* ranges[] = {baseRabge};
1350 
1351             const cv::Scalar mColorsY[] = { cv::Scalar(76), cv::Scalar(149), cv::Scalar(29) };
1352             const cv::Scalar mColorsUV[] = { cv::Scalar(84, 255), cv::Scalar(43, 21), cv::Scalar(255, 107) };
1353 
1354             cv::Mat OutputY(m_imageHeightInPixels, m_imageWidthInPixels, CV_8UC1, pDest, lDestStride);
1355             cv::Mat OutputUV(m_imageHeightInPixels/2, m_imageWidthInPixels/2,
1356                              CV_8UC2, pDest+m_imageHeightInPixels*lDestStride, lDestStride);
1357             cv::Mat BgrFrame;
1358 
1359             InputFrame.copyTo(OutputFrame);
1360 
1361             cv::cvtColor(InputFrame, BgrFrame, cv::COLOR_YUV420sp2BGR);
1362             int thikness = (int) (BgrFrame.cols / (mHistSizeNum + 10) / 5);
1363             if(thikness > 5) thikness = 5;
1364             int offset = (int) ((BgrFrame.cols - (5*mHistSizeNum + 4*10)*thikness)/2);
1365 
1366             // RGB
1367             for (int c=0; c<3; c++)
1368             {
1369                 cv::Mat hist;
1370                 cv::calcHist(&BgrFrame, 1, channels[c], cv::Mat(), hist, 1, mHistSize, ranges);
1371                 cv::normalize(hist, hist, BgrFrame.rows/2, 0, cv::NORM_INF);
1372                 for(int h=0; h<mHistSizeNum; h++) {
1373                     cv::Point mP1, mP2;
1374                     // Draw on Y plane
1375                     mP1.x = mP2.x = offset + (c * (mHistSizeNum + 10) + h) * thikness;
1376                     mP1.y = BgrFrame.rows-1;
1377                     mP2.y = mP1.y - 2 - (int)hist.at<float>(h);
1378                     cv::line(OutputY, mP1, mP2, mColorsY[c], thikness);
1379 
1380                     // Draw on UV planes
1381                     mP1.x /= 2;
1382                     mP1.y /= 2;
1383                     mP2.x /= 2;
1384                     mP2.y /= 2;
1385                     cv::line(OutputUV, mP1, mP2, mColorsUV[c], thikness/2);
1386                 }
1387             }
1388         } break;
1389     default:
1390         break;
1391     }
1392 
1393     // Set the data size on the output buffer.
1394     hr = pOut->SetCurrentLength(m_cbImageSize);
1395 
1396     return hr;
1397 }
1398 
1399 
1400 // Flush the MFT.
1401 
OnFlush()1402 HRESULT OcvImageManipulations::OnFlush()
1403 {
1404     // For this MFT, flushing just means releasing the input sample.
1405     SafeRelease(&m_pSample);
1406     return S_OK;
1407 }
1408 
1409 
1410 // Update the format information. This method is called whenever the
1411 // input type is set.
1412 
UpdateFormatInfo()1413 HRESULT OcvImageManipulations::UpdateFormatInfo()
1414 {
1415     HRESULT hr = S_OK;
1416 
1417     GUID subtype = GUID_NULL;
1418 
1419     m_imageWidthInPixels = 0;
1420     m_imageHeightInPixels = 0;
1421     m_cbImageSize = 0;
1422 
1423     if (m_pInputType != NULL)
1424     {
1425         hr = m_pInputType->GetGUID(MF_MT_SUBTYPE, &subtype);
1426         if (FAILED(hr))
1427         {
1428             goto done;
1429         }
1430         if (subtype != MFVideoFormat_NV12)
1431         {
1432             hr = E_UNEXPECTED;
1433             goto done;
1434         }
1435 
1436         hr = MFGetAttributeSize(m_pInputType, MF_MT_FRAME_SIZE, &m_imageWidthInPixels, &m_imageHeightInPixels);
1437         if (FAILED(hr))
1438         {
1439             goto done;
1440         }
1441 
1442         // Calculate the image size for YUV NV12 image(not including padding)
1443         m_cbImageSize = (m_imageHeightInPixels + m_imageHeightInPixels/2)*m_imageWidthInPixels;
1444     }
1445 
1446 done:
1447     return hr;
1448 }
1449 
1450 
1451 // Get the default stride for a video format.
GetDefaultStride(IMFMediaType * pType,LONG * plStride)1452 HRESULT GetDefaultStride(IMFMediaType *pType, LONG *plStride)
1453 {
1454     LONG lStride = 0;
1455 
1456     // Try to get the default stride from the media type.
1457     HRESULT hr = pType->GetUINT32(MF_MT_DEFAULT_STRIDE, (UINT32*)&lStride);
1458     if (FAILED(hr))
1459     {
1460         // Attribute not set. Try to calculate the default stride.
1461         GUID subtype = GUID_NULL;
1462 
1463         UINT32 width = 0;
1464         UINT32 height = 0;
1465 
1466         // Get the subtype and the image size.
1467         hr = pType->GetGUID(MF_MT_SUBTYPE, &subtype);
1468         if (SUCCEEDED(hr))
1469         {
1470             hr = MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &width, &height);
1471         }
1472         if (SUCCEEDED(hr))
1473         {
1474             if (subtype == MFVideoFormat_NV12)
1475             {
1476                 lStride = width;
1477             }
1478             else if (subtype == MFVideoFormat_YUY2 || subtype == MFVideoFormat_UYVY)
1479             {
1480                 lStride = ((width * 2) + 3) & ~3;
1481             }
1482             else
1483             {
1484                 hr = E_INVALIDARG;
1485             }
1486         }
1487 
1488         // Set the attribute for later reference.
1489         if (SUCCEEDED(hr))
1490         {
1491             (void)pType->SetUINT32(MF_MT_DEFAULT_STRIDE, UINT32(lStride));
1492         }
1493     }
1494     if (SUCCEEDED(hr))
1495     {
1496         *plStride = lStride;
1497     }
1498     return hr;
1499 }
1500