Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/samples/winrt/ImageManipulations/MediaExtensions/OcvTransform/OcvTransform.cpp
16344 views
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.hpp>
12
#include <opencv2\imgproc.hpp>
13
#include <opencv2\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>
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
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
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.
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
//-------------------------------------------------------------------
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
1269
HRESULT OcvImageManipulations::EndStreaming()
1270
{
1271
m_bStreamingInitialized = false;
1272
return S_OK;
1273
}
1274
1275
1276
1277
// Generate output data.
1278
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
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
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.
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
1501