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> 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