2017-02-16 33 views
0

Я разрабатываю потоковое приложение для камеры USB с использованием MediaFoundation SourceReader. Камера имеет поддержку USB3.0 и дает 60 кадров в секунду для разрешения видеоформата MJPG 1080p.Асинхронный MFT не отправляет событие MFTransformHaveOutput (Intel Hardware MJPEG Decoder MFT)

Я использовал программное обеспечение MJPEG Decoder MFT для преобразования MJPG в кадры YUY2, а затем преобразован в рамку RGB32 для рисования на окне. Вместо 60fps я могу отображать только 30 кадров в секунду при использовании этого программного декодера. Я опубликовал вопрос на этом сайте и получил предложение использовать Intel MJPEG Decoder MFT для решения проблемы с кадром.

Для использования этого аппаратного декодера MJPEG я рассмотрел асинхронную модель обработки MFT и настроил асинхронный обратный вызов для IMFMediaEventGenerator через интерфейс IMFTransform.

После вызова MFT_MESSAGE_NOTIFY_START_OF_STREAM с использованием метода ProcessMessage я получил событие MFTransfromNeedInput дважды, но я не получил событие MFTransformHaveOutput из MFT.

я разделил свой код здесь для справки: метод

IMFTransform* m_pTransform = NULL; 

HRESULT EnumDecoderMFT() 
{ 
    HRESULT hr; 
    IMFActivate** ppActivate; 
    UINT32 numDecodersMJPG = 0; 
    LPWSTR lpMFTName = 0; 

    MFT_REGISTER_TYPE_INFO inputFilter = {MFMediaType_Video,MFVideoFormat_MJPG}; 
    MFT_REGISTER_TYPE_INFO outputFilter = {MFMediaType_Video,MFVideoFormat_YUY2}; 

    UINT32 unFlags = MFT_ENUM_FLAG_SYNCMFT | MFT_ENUM_FLAG_ASYNCMFT | MFT_ENUM_FLAG_LOCALMFT | MFT_ENUM_FLAG_HARDWARE | MFT_ENUM_FLAG_SORTANDFILTER; 

    hr = MFTEnumEx(MFT_CATEGORY_VIDEO_DECODER, unFlags, &inputFilter, &outputFilter, &ppActivate, &numDecodersMJPG); 
    if (FAILED(hr)) return hr; 

    hr = ppActivate[0]->GetAllocatedString(MFT_FRIENDLY_NAME_Attribute,&lpMFTName,0); 
    if (FAILED(hr)) return hr; 

    // Activate transform 
    hr = ppActivate[0]->ActivateObject(__uuidof(IMFTransform), (void**)&m_pTransform); 
    if (FAILED(hr)) return hr; 

    hr = hr = m_pTransform->GetAttributes(&pAttributes); 
    if (SUCCEEDED(hr)) 
    { 
     hr = pAttributes->SetUINT32(MF_TRANSFORM_ASYNC_UNLOCK, TRUE);   
     if(FAILED(hr)) return hr; 

     hr = pAttributes->SetUINT32(MFT_SUPPORT_DYNAMIC_FORMAT_CHANGE,TRUE);   
     if(FAILED(hr)) return hr; 

     hr = pAttributes->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, TRUE); 
     if(FAILED(hr)) return hr; 

     hr = m_pTransform->QueryInterface(IID_IMFMediaEventGenerator,(void**)&m_pEventGenerator);   
     if(FAILED(hr)) return hr; 

     hr = m_pEventGenerator->BeginGetEvent((IMFAsyncCallback*)this,NULL); 
     if(FAILED(hr)) return hr; 

     pAttributes->Release(); 
    } 

    SafeRelease(&ppActivate[0]); 

    CoTaskMemFree(ppActivate); 

    return hr;  
} 

HRESULT Invoke(IMFAsyncResult *pResult) 
{ 
    HRESULT hr = S_OK,hrStatus; 
    MediaEventType meType = MEUnknown; // Event type 
    IMFMediaEvent *pEvent = NULL; 

    // Get the event from the event queue. 
    hr = m_pEventGenerator->EndGetEvent(pResult, &pEvent);  //Completes an asynchronous request for the next event in the queue. 
    if(FAILED(hr)) return hr; 

    // Get the event type. 
    hr = pEvent->GetType(&meType); 
    if(FAILED(hr)) return hr; 

    hr = pEvent->GetStatus(&hrStatus); 
    if(FAILED(hr)) return hr; 

    if(SUCCEEDED(hrStatus)) 
    { 
     if(meType == METransformNeedInput) 
     {  
      SetEvent(m_hNeedInputEvent); 
     } 
     else if(meType == METransformHaveOutput) 
     {   
      SetEvent(m_hHaveOutputEvent); 
     } 
     else if(meType == METransformDrainComplete) 
     { 
      hr = m_pTransform->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH,0); 
      if(FAILED(hr)) return hr; 
     } 
     else if(meType == MEError) 
     { 
      PROPVARIANT pValue; 
      hr = pEvent->GetValue(&pValue);   
      if(FAILED(hr)) return hr; 
     } 

     hr = m_pEventGenerator->BeginGetEvent((IMFAsyncCallback*)this,NULL);  
     if(FAILED(hr)) return hr; 
    } 

done: 
    SafeRelease(&pEvent); 
    return S_OK; 
} 

HRESULT CMFSourceReader::OnReadSample(
    HRESULT hrStatus, 
    DWORD dwStreamIndex , 
    DWORD dwStreamFlags , 
    LONGLONG llTimestamp , 
    IMFSample *pSample  // Can be NULL 
    ) 
{ 
    HRESULT hr = S_OK; 
    IMFMediaBuffer *pBuffer = NULL; 
    DWORD dwcbTotLen = 0;   
    IMFSample *mftOutSample = NULL; 

    EnterCriticalSection(&m_critsec); 

    if (FAILED(hrStatus)) 
    { 
     hr = hrStatus; 
    } 

    if (SUCCEEDED(hr)) 
    { 
     if (pSample != NULL) 
     { 
      if(dwStreamIndex == 0)  //VideoStream 
      {     
       if(m_pTransform) 
       { 
        hr = m_pTransform->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0); 
        if(FAILED(hr)) return hr; 

        m_dwWaitObj = WaitForSingleObject(m_hNeedInputEvent,INFINITE); 
        if(m_dwWaitObj == WAIT_OBJECT_0) 
        {       
         hr = ProcessInputSample(pSample); 
         if(FAILED(hr)) return hr; 
        } 

        m_dwWaitObj = WaitForSingleObject(m_hHaveOutputEvent,INFINITE); 
        if(m_dwWaitObj == WAIT_OBJECT_0) 
        { 
         hr = ProcessOutputSample(&mftOutSample); 
         if(FAILED(hr)) return hr; 
        } 
       } 
      } 
     } 
    } 

    if(SUCCEEDED(hr)) 
    { 
     if(m_pReader != NULL) 
     { 
      hr = m_pReader->ReadSample(
       (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, 
       0, 
       NULL, // actual 
       NULL, // flags 
       NULL, // timestamp 
       NULL // sample 
       ); 
      if(FAILED(hr)) return hr; 
     } 
    } 

    SafeRelease(&mftOutSample); 

    LeaveCriticalSection(&m_critsec); 
    return hr; 
} 

HRESULT ProcessOutputSample(IMFSample **pOutSample) 
{ 
    HRESULT hr = S_OK; 
    MFT_OUTPUT_DATA_BUFFER outputDataBuffer; 
    DWORD processOutputStatus = 0,mftOutFlags = 0; 
    MFT_OUTPUT_STREAM_INFO StreamInfo; 
    IMFSample *mftOutSample = NULL; 
    IMFMediaBuffer *pOutBuffer = NULL; 

    if(m_pTransform != NULL) 
    { 
     hr = m_pTransform->GetOutputStreamInfo(0, &StreamInfo); 
     if(FAILED(hr)) return hr; 

     DWORD status = 0; 
     hr = m_pTransform->GetOutputStatus(&status); 
     if (FAILED(hr)) return hr; 

     hr = MFCreateSample(&mftOutSample); 
     if(FAILED(hr)) return hr; 

     hr = MFCreateMemoryBuffer(StreamInfo.cbSize, &pOutBuffer); 
     if(FAILED(hr)) return hr; 

     hr = mftOutSample->AddBuffer(pOutBuffer); 
     if(FAILED(hr)) return hr; 

     outputDataBuffer.dwStreamID = 0; 
     outputDataBuffer.dwStatus = 0; 
     outputDataBuffer.pEvents = NULL; 
     outputDataBuffer.pSample = mftOutSample; 

     hr = m_pTransform->ProcessOutput(0, 1, &outputDataBuffer, &processOutputStatus);    
     if(FAILED(hr)) return hr; 

     hr = m_pTransform->ProcessMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM, 0); 
     if (FAILED(hr)) return hr; 

     hr = m_pTransform->ProcessMessage(MFT_MESSAGE_COMMAND_DRAIN, 0); 
     if (FAILED(hr)) return hr; 

     if(mftOutSample) 
     { 
      *pOutSample = mftOutSample; 
      (*pOutSample)->AddRef(); 
     } 

     ResetEvent(m_hHaveOutputEvent); 
    } 

    SafeRelease(&mftOutSample); 
    SafeRelease(&pOutBuffer); 

    return hr; 
} 

HRESULT ProcessInputSample(IMFSample *pInputSample) 
{ 
    HRESULT hr; 

    if(m_pTransform != NULL) 
    {    
     hr = m_pTransform->ProcessInput(0, pInputSample, 0); 
     if(FAILED(hr)) return hr; 

     hr = m_pTransform->ProcessMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM,0); 
     if(FAILED(hr)) return hr; 

     ResetEvent(m_hNeedInputEvent); 
    } 

    return hr; 
} 

я прокомментировал ProcessOutputSample() в моем коде и проверил, непрерывно MFT отправить MFTransformNeedInput тип события. После примера ProcessInput у меня есть метод ProcessOutput, но он возвратил ошибку E_UNEXPECTED. Я прочитал об этой ошибке в MSDN, они упомянули, что я не должен вызывать метод IMFTransform :: ProcessOutput без получения события MFTransformHaveOutput.

Я что-то пропустил? Могу ли я использовать Intel MJPEG Decoder MFT внутри MediaFoundation? Кто-то предоставляет образец для использования этого декодера? За последние 4 дня я борюсь с этой проблемой.

Заранее спасибо.

+0

Какая веб-камера? (просто любопытно) – YePhIcK

+0

Я использую камеру, указанную в этой ссылке: https://www.e-consystems.com/13mp-autofocus-usb-camera.asp – Abi

ответ

0

Во-первых, вам не нужно называть это:

hr = pAttributes->SetUINT32(MFT_SUPPORT_DYNAMIC_FORMAT_CHANGE,TRUE); 

Mft несет ответственность за это, и потому что это асинхронный, вы можете предположить, что это имеет значение ИСТИНА. Вы можете проверить, действительно ли это ИСТИНА, вызывающий GetUINT32.

Второе:

hr = pAttributes->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, TRUE); 

Это не намерены MFT. Этот атрибут предназначен для считывателя источника или записывающего устройства: MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS attribute.

Из вашего кода проблема, которую я вижу, заключается в том, что вы всегда вызываете hr = m_pTransform->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0); внутри OnReadSample, и вам нужно позвонить один раз в начале.

То же самое применяется с ProcessInputSample и ProcessOutputSample, вы звоните hr = m_pTransform->ProcessMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM,0);, вы говорите, что MFT потоковую закончилось ...

Ваш код должен обрабатывать такие вещи, как это:

  • Начало расшифровывает
  • MFT_MESSAGE_NOTIFY_START_OF_STREAM
  • Ввод процесса при необходимости
  • Выходной сигнал процесса при необходимости
  • ...
  • ...
  • Процесс ввода в случае необходимости
  • процесса вывода по мере необходимости
  • MFT_MESSAGE_NOTIFY_END_OF_STREAM
  • Конец расшифровывает

Вы никогда не получите outputsample, потому что вы говорите в MFT, что поток завершился сразу после первого процесса ввода.

прочитать: MFT_MESSAGE_NOTIFY_START_OF_STREAM

Уведомляет преобразование фонд Медиа (MFT), что первый образец собирается быть обработан.

Да, первый образец, не все образцы.

EDIT

Существует другое проблема в CMFSourceReader :: OnReadSample:

m_dwWaitObj = WaitForSingleObject(m_hNeedInputEvent,INFINITE); 

if(m_dwWaitObj == WAIT_OBJECT_0) 
{       
    hr = ProcessInputSample(pSample); 
    if(FAILED(hr)) return hr; 
} 

m_dwWaitObj = WaitForSingleObject(m_hHaveOutputEvent,INFINITE); 

if(m_dwWaitObj == WAIT_OBJECT_0) 
{ 
    hr = ProcessOutputSample(&mftOutSample); 
    if(FAILED(hr)) return hr; 
} 

Вы первый ждать m_hNeedInputEvent, то для m_hHaveOutputEvent. Но что произойдет, если вы получите m_hNeedInputEvent дважды до m_hHaveOutputEvent. Этот код неверен. Вы неправильно обрабатываете Invoke. OnReadSample следует вызывать только тогда, когда ProcessInput завершен. Общий дизайн кажется неправильным.

UPDATE

При получении образца в CMFSourceReader :: OnReadSample, вам просто нужно стоять в очереди на выборку в списке (Queue (Sample)). Для управления списком образцов, вы можете использовать этот тип кода: SamplePool/ThreadSafeQueue

В CMFSourceReader :: Invoke, когда вы получаете METransformNeedInput, просто DEQUEUE (Sample) и вызвать ProcessInputSample.

В CMFSourceReader :: Invoke, когда вы получаете m_hHaveOutputEvent, вызовите ProcessOutputSample.

Две вещи:

  • вы можете позвонить m_pReader-> ReadSample три раза в начале программы, и ждать, чтобы иметь три образца в списке. Когда у вас есть три выборки, вы можете начать декодирование, например, вы будете уверены, что когда METransformNeedInput происходит, у вас есть один образец, готовый к обработке. Вы можете вызвать m_pReader-> ReadSample в этот момент, после того, как ProcessInputSample сохранит три образца в списке.
  • возможно, что процесс декодера слишком быстро, чем считыватель исходного считывателя, или наоборот. Поэтому проверьте, что когда METransformNeedInput, в списке всегда есть образцы. Стратегия заключается в том, чтобы в течение процесса декодирования вести обоснованный выборки, скажем, три.
+0

Благодарим вас за подробный ответ на мой запрос, Mofo. Прежде чем публиковать этот вопрос, я пробовал этот путь, но не повезло.Теперь я изменил свой код в соответствии с вашими комментариями и все еще выполнил двоичный код, столкнувшись с той же проблемой. Я добавил модифицированный код в следующий dropbox link.https: //www.dropbox.com/s/9pu2rddar2mx42x/HwMJPEGDecoderMFT_Code_Query.txt? Dl = 0. Не могли бы вы сообщить мне, когда я должен отправить этот MFT_MESSAGE_NOTIFY_END_OF_STREAM в MFT? Я устанавливаю этот флаг в MFT при изменении формата видео MJPG. Это правильный способ сказать MFT? Любая помощь? – Abi

+0

Я редактирую свое сообщение. По возможности отправьте полный код. – mofo77

+0

Спасибо, мофо. Да вы правы. Событие ввода вызывает вызов дважды. Я изменю код и отправлю вам код завтра, так как сегодня я в отпуске. Еще раз, спасибо. – Abi