При использовании Media Foundation Source Reader с камерой UV C иногда OnReadSample никогда не вызывается. - PullRequest
1 голос
/ 05 мая 2020

Начну с того, что этот вопрос очень похож на этот другой - основное отличие будет, я надеюсь, я предоставлю достаточно контекста, чтобы иметь возможность решить проблему :)

Я пишу библиотеку C ++ для проприетарного устройства камеры UV C, которое использует MS Media Foundation для захвата кадров для обработки и отображения с помощью специального приложения C ++ / CUDA / C#. Камера иногда присоединяется и отключается от устройства во время обычного использования , и примерно в 15% случаев это происходит:

После успешного создания объекта IMFSourceReader , выполняется последующий асинхронный вызов ReadSample. Функция обратного вызова сначала вызывается с "hrStatus", установленным на S_OK, и dwStreamFlags, установленным на указанное MF_SOURCE_READERF_STREAMTICK. После этого любые последующие вызовы функции ReadSample не вызывают вызова зарегистрированного обратного вызова OnReadSample.

Ниже приведены фрагменты кода инициализации SourceReader, вызов ReadSample и реализация OnReadSample.

Код инициализации для объекта Source Reader

      // Create the media source
      res = SUCCEEDED(CreateVideoDeviceSource(&cap_mediasrc_des));
      if (!res)
      {
         std::cout << "Failed to create video device source" << std::endl;
      }

      // Get the stream and presentation descriptors to set data type
      .
      . excluded this code for brevity
      .

      // Create source reader object and get handle to it
      if (res)
      {
         // Create attributes store for the source reader
         // to indicate that it should be used asynchronously
         HRESULT hr = S_OK;
         IMFAttributes *src_rdr_attr = NULL;

         res = SUCCEEDED(hr = MFCreateAttributes(&src_rdr_attr, 1));

         if (res)
         {
            res = SUCCEEDED(hr = src_rdr_attr->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, &frame_src_reader_cb));
         }

         if (res)
         {
            res = SUCCEEDED(MFCreateSourceReaderFromMediaSource(cap_mediasrc_des, src_rdr_attr, &cap_src_rdr_h));
            if (!res)
            {
               std::cout << "Failed to create source reader from media source" << std::endl;
            }
         }
      }

Вызов ReadSample - выполняется в al oop, который выполняется постоянно

      res = SUCCEEDED(capture_result = cap_src_rdr_h->ReadSample(
         MF_SOURCE_READER_FIRST_VIDEO_STREAM,
         0,
         NULL,
         NULL,
         NULL,
         NULL
      ));

      if (res)
      {
         // wait for async call to finish or to timeout, whichever comes first
         std::uint32_t timeout_cnt = 0U;
         while (!read_sample_available && (timeout_cnt < 100U)) 
         {
            Sleep(1);
            timeout_cnt++;
         }

         if (timeout_cnt >= 100U)
         {
            read_time_expired = true;
         }
      } 

и, наконец, реализация OnReadSample (с некоторым кодом обработки изображений исключен для краткости)

   HRESULT FrameSource::FrameSourceReaderCallback::OnReadSample(
      HRESULT   hrStatus,
      DWORD     dwStreamIndex,
      DWORD     dwStreamFlags,
      LONGLONG  llTimestamp,
      IMFSample *pSample
   )
   {
      HRESULT res = hrStatus;

      // Check the flags
      if (((dwStreamFlags & MF_SOURCE_READERF_STREAMTICK) != MF_SOURCE_READERF_STREAMTICK) && (dwStreamFlags > 0))
      {
         // If the flags indicate something other than just MF_SOURCE_READERF_STREAMTICK, we've run into a problem
         // Retain hrStatus if possible
         res = SUCCEEDED(hrStatus) ? S_FALSE : hrStatus; 

         FrameSource::GetInstance().read_sample_available = false;
      }
      else if ((dwStreamFlags & MF_SOURCE_READERF_STREAMTICK) == MF_SOURCE_READERF_STREAMTICK)
      {
         // There isn't a new frame to grab
         FrameSource::GetInstance().read_sample_available = false;
         }
      }
      else if (SUCCEEDED(res))
      {
         // Good frame available
         IMFMediaBuffer* in_buff_h = nullptr;
         IMF2DBuffer2* in_buff2d_h = nullptr;
         IMFMediaBuffer* out_buff_h = nullptr;
         IMF2DBuffer2* out_buff2d_h = nullptr;

         ... here we convert the sample to a contiguous buffer, read some data out of it,
         ... run it through a color conversion MFT, and copy it into a CUDA GPU resident buffer
         ... excluded for brevity

         if (SUCCEEDED(res))
         {
            frm_src_ref.read_sample_available = true;
         }

         // Make sure to release everything that we allocated
         if (in_buff_h != nullptr)
         {
            in_buff_h->Release();
         }

         if (in_buff2d_h != nullptr)
         {
            in_buff2d_h->Release();
         }

         if (out_buff_h != nullptr)
         {
            out_buff_h->Release();
         }

         if (out_buff2d_h != nullptr)
         {
            out_buff2d_h->Release();
         }
      }
      else
      {
         // res failed, but no flags?
         std::cout << "OnReadSample failed, but there are no error flags" << std::endl;
      }
      return res;
   }

Я в тупике и довольно долгое время был в тупике. Я бы предположил, что если бы была какая-то проблема на стороне UV C, то первый вызов ReadSample указывал бы на это, и я мог бы просто диагностировать эту проблему. Первоначально у меня была синхронная реализация ReadSample, которая столкнулась с проблемой, представленной таким же образом (первый вызов дал MF_SOURCE_READERF_STREAMTICK, следующий никогда не вернулся). Мне остается только гадать, есть ли какой-нибудь другой способ проверки состояния медиаисточника, чтобы я мог отладить его дальше?

Что вы думаете? Я использую Windows 10 Pro 64-Bit (10.0, сборка 18363) для справки. Любая помощь очень ценится. Спасибо!

...