Advertisement
Guest User

Media Foundation Video Decoder

a guest
Jan 19th, 2024
95
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 7.09 KB | Source Code | 0 0
  1.  
  2. // ResourcePointer is a smart pointer class for managing COM objects.
  3. // It has been used extensively for many years in other parts of our code.
  4.  
  5. std::deque<ResourcePointer<IMFSample>> inputQueue;    // Queue of encoded video samples waiting to be decoded.
  6. std::deque<ResourcePointer<IMFSample>> nv12Queue;     // Queue of NV12 frames waiting to be converted to RGB.
  7. std::deque<ResourcePointer<IMFSample>> outputQueue;   // Queue of RGB frames awaiting display.
  8.  
  9. ResourcePointer<IMFTransform> decoder;      // Decodes HEVC to NV12
  10. ResourcePointer<IMFTransform> processor;    // Converts NV12 to RGB
  11.  
  12. // Update is called once per frame...
  13. void update() {
  14.   pushSamples(processor, nv12Queue);
  15.   pullSamples(processor, outputQueue);
  16.  
  17.   pushSamples(decoder, inputQueue);
  18.   pullSamples(decoder, nv12Queue);
  19.  
  20.   updateTexture();
  21. }
  22.  
  23. // Queues incoming HEVC data for feeding to the decoder in update()...
  24. void streamVideoData(const VideoDecoderPayload& p) {
  25.   ResourcePointer<IMFSample>      sample;
  26.   ResourcePointer<IMFMediaBuffer> mediaBuffer;
  27.   BYTE*                           memory;
  28.  
  29.   MFCreateMemoryBuffer(static_cast<DWORD>(p.size), mediaBuffer.internalPointer());
  30.   mediaBuffer->Lock(&memory, nullptr, nullptr);
  31.   std::memcpy(memory, p.data, p.size);
  32.   mediaBuffer->Unlock();
  33.   mediaBuffer->SetCurrentLength(static_cast<DWORD>(p.size));
  34.  
  35.   MFCreateSample(sample.internalPointer());
  36.   sample->SetSampleTime(p.timestamp);
  37.   sample->AddBuffer(mediaBuffer);
  38.  
  39.   inputQueue.push_back(sample);
  40. }
  41.  
  42. // Initialises the decoder and video processor MF transforms...
  43. bool createDecoder() {
  44.   MFT_REGISTER_TYPE_INFO          inputInfo = {};
  45.   IMFActivate**                   activators = nullptr;
  46.   UINT32                          activatorCount = 0;
  47.   ResourcePointer<IMFMediaType>   inputType;
  48.   ResourcePointer<IMFAttributes>  attributes;
  49.  
  50.   MFCreateMediaType(inputType.internalPointer());
  51.   inputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
  52.   inputType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_HEVC_ES);
  53.  
  54.   inputInfo.guidMajorType = MFMediaType_Video;
  55.   inputInfo.guidSubtype = MFVideoFormat_HEVC_ES;
  56.  
  57.   MFTEnumEx(MFT_CATEGORY_VIDEO_DECODER,
  58.             MFT_ENUM_FLAG_SORTANDFILTER | MFT_ENUM_FLAG_SYNCMFT,
  59.             &inputInfo,
  60.             nullptr,
  61.             &activators,
  62.             &activatorCount);
  63.  
  64.   activators[0]->ActivateObject(IID_IMFTransform, decoder.internalVoidPointer());
  65.  
  66.   MFSetAttributeSize(inputType, MF_MT_FRAME_SIZE, outputWidth, outputHeight);
  67.   decoder->SetInputType(0, inputType, 0);
  68.  
  69.  
  70.   decoder->GetAttributes(attributes.internalPointer());
  71.   decoder->ProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER,
  72.                           reinterpret_cast<ULONG_PTR>(getDXGIDeviceManager()));
  73.   attributes->SetUINT32(CODECAPI_AVLowLatencyMode, TRUE);
  74.  
  75.   // <Clean up and release activators>
  76.  
  77.   inputInfo.guidSubtype = MFVideoFormat_NV12;
  78.   MFTEnumEx(MFT_CATEGORY_VIDEO_PROCESSOR,
  79.             MFT_ENUM_FLAG_SORTANDFILTER | MFT_ENUM_FLAG_SYNCMFT,
  80.             &inputInfo,
  81.             nullptr,
  82.             &activators,
  83.             &activatorCount));
  84.  
  85.   activators[0]->ActivateObject(IID_IMFTransform, processor.internalVoidPointer());
  86.   processor->ProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER,
  87.                             reinterpret_cast<ULONG_PTR>(getDXGIDeviceManager()));
  88.  
  89.   // <Clean up and release activators>
  90.  
  91.   configureIO();
  92.     decoder->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0);
  93.     decoder->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0);
  94.   return true;
  95. }
  96.  
  97. void updateTexture() {
  98.   int64_t                     now = std::chrono::nanoseconds(std::chrono::steady_clock().now().time_since_epoch()).count();
  99.   ResourcePointer<IMFSample>  displaySample;
  100.  
  101.   while (!outputQueue.empty()) {
  102.     LONGLONG sampleTime = 0;
  103.  
  104.     outputQueue.front()->GetSampleTime(&sampleTime);
  105.  
  106.     if (sampleTime <= now) {
  107.       displaySample = outputQueue.front();
  108.  
  109.       // pop_front will call ~ResourcePointer which calls Release.
  110.       outputQueue.pop_front();
  111.     }
  112.     else {
  113.       break;
  114.     }
  115.   }
  116.   if (displaySample) {
  117.     ResourcePointer<IMFMediaBuffer> buffer;
  118.     byte*                           data = nullptr;
  119.  
  120.     displaySample->GetBufferByIndex(0, buffer.internalPointer());
  121.  
  122.     buffer->Lock(&data, nullptr, nullptr);
  123.     // <Update a texture with the buffer's content.>
  124.     buffer->Unlock();
  125.      
  126.     // Calling buffer->Release() here will crash when the buffer destructor is called.
  127.     // I.e., buffer isn't being leaked.
  128.     // Ditto for displaySample->Release();
  129.   }
  130. }
  131.  
  132. // Configures the decoder output, and the processor input and output...
  133. void configureIO() {
  134.   ResourcePointer<IMFMediaType> type;
  135.  
  136.   MFCreateMediaType(type.internalPointer());
  137.  
  138.   type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
  139.   type->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_NV12);
  140.  
  141.   MFSetAttributeSize(type, MF_MT_FRAME_SIZE, outputWidth, outputHeight);
  142.   MFSetAttributeRatio(type, MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
  143.  
  144.   decoder->SetOutputType(0, type, 0);
  145.   processor->SetInputType(0, type, 0);
  146.  
  147.   type->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_ARGB32);
  148.   type->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1);
  149.  
  150.   processor->SetOutputType(0, type, 0);
  151. }
  152.  
  153. // Pushes data to the specified transform...
  154. void pushSamples(ResourcePointer<IMFTransform>& transform, std::deque<ResourcePointer<IMFSample>>& source) {
  155.   while (!source.empty()) {
  156.     if (transform->ProcessInput(0, source.front(), 0) != S_OK) {
  157.       return;
  158.     }
  159.     source.pop_front();
  160.   }
  161. }
  162.  
  163. // Fetches decoded/processed samples from the specified transform...
  164. void pullSamples(ResourcePointer<IMFTransform>& transform, std::deque<ResourcePointer<IMFSample>>& destination) {
  165.   while (true) {
  166.     DWORD flags = 0;
  167.  
  168.     transform->GetOutputStatus(&flags);
  169.     if (flags != MFT_OUTPUT_STATUS_SAMPLE_READY) {
  170.       return;
  171.     }
  172.  
  173.     // Code to call GetOutputStream and create samples if required was here.
  174.     // But for DX11 connected transforms, it's not necessary.
  175.     // streamInfo.dwFlags always has MFT_OUTPUT_STREAM_PROVIDES_SAMPLES set,
  176.     // and we have 100% control over the hardware and OS we deploy on.
  177.  
  178.     DWORD                   status = 0;
  179.     MFT_OUTPUT_DATA_BUFFER  outputDataBuffer;
  180.  
  181.     memset(&outputDataBuffer, 0, sizeof(outputDataBuffer));
  182.     outputDataBuffer.dwStreamID = 0;
  183.     outputDataBuffer.pSample = nullptr;
  184.     outputDataBuffer.dwStatus = 0;
  185.     outputDataBuffer.pEvents = nullptr;
  186.  
  187.     // This call will hang when called for the decoder after half a dozen frames have been processed
  188.     // in the Media Foundation function CDXVAFrameManager::WaitOnSampleReturnedByRenderer
  189.     HRESULT result = transform->ProcessOutput(0, 1, &outputDataBuffer, &status);
  190.  
  191.     if (result == S_OK) {
  192.       if (outputDataBuffer.dwStatus == 0) {
  193.         // This push_back does NOT call AddRef.
  194.         destination.push_back(outputDataBuffer.pSample);
  195.       }
  196.       else if (outputDataBuffer.dwStatus != MFT_OUTPUT_DATA_BUFFER_INCOMPLETE) {
  197.         return;
  198.       }
  199.     }
  200.     else if (result == MF_E_TRANSFORM_STREAM_CHANGE) {
  201.       configureIO();
  202.     }
  203.     else
  204.       return;
  205.     }
  206.   }
  207. }
  208.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement