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