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