2017-02-16 1 views
0

Je développe une application de bureau de streaming de caméra USB en utilisant la technique MediaFoundation SourceReader. L'appareil photo prend en charge USB3.0 et donne 60fps pour la résolution de format vidéo MJPG 1080p.MFT asynchrone n'envoie pas MFTransformHaveOutput Événement (Intel MFT MJPEG Decoder MFT)

J'ai utilisé le logiciel MJPEG Decoder MFT pour convertir MJPG en trames YUY2, puis converti dans la trame RGB32 pour dessiner sur la fenêtre. Au lieu de 60fps, je suis en mesure de rendre seulement 30fps sur la fenêtre lors de l'utilisation de ce décodeur logiciel. J'ai posté une question sur ce site et j'ai eu une suggestion d'utiliser Intel Hardware MJPEG Decoder MFT pour résoudre le problème de chute de trame.

Pour utiliser ce décodeur matériel MJPEG, j'ai adressé un modèle de traitement MFT asynchrone et configuré un rappel asynchrone pour IMFMediaEventGenerator via l'interface IMFTransform.

Après avoir appelé MFT_MESSAGE_NOTIFY_START_OF_STREAM en utilisant la méthode ProcessMessage, j'ai reçu deux fois l'événement MFTransfromNeedInput mais je n'ai pas reçu l'événement MFTransformHaveOutput de MFT.

J'ai partagé mon code ici pour référence: méthode

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

J'ai commenté de ProcessOutputSample() dans mon code et vérifié, MFT envoyer en continu type d'événement MFTransformNeedInput. Après l'exemple ProcessInput, j'ai la méthode ProcessOutput mais elle a renvoyé une erreur E_UNEXPECTED. J'ai lu à propos de cette erreur dans MSDN, ils ont mentionné que je ne devrais pas appeler la méthode IMFTransform :: ProcessOutput sans recevoir l'événement MFTransformHaveOutput.

Puis-je utiliser Intel Hardware MJPEG Decoder MFT dans MediaFoundation? Quelqu'un fournit un échantillon pour utiliser ce décodeur? Les 4 derniers jours, je suis aux prises avec ce problème.

Merci d'avance.

+0

Quelle webcam est-ce? (juste curieux) – YePhIcK

+0

J'utilise l'appareil photo qui est mentionné dans ce lien: https://www.e-consystems.com/13mp-autofocus-usb-camera.asp – Abi

Répondre

0

D'abord, vous n'avez pas besoin d'appeler ceci:

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

MFT est responsable de cela, et parce qu'il est asynchrone, vous pouvez supposer, il est vrai. Vous pouvez juste vérifier qu'il est vraiment vrai en appelant GetUINT32.

Deuxième:

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

Ceci est l'intention de ne pas MFT. Cet attribut est destiné au lecteur source ou au rédacteur de puits: MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS attribute.

De votre code, le problème que je peux voir est que vous appelez toujours hr = m_pTransform->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0); dans le OnReadSample, et vous devriez appeler une fois au début.

Le même appliquent avec ProcessInputSample et ProcessOutputSample, vous appelez hr = m_pTransform->ProcessMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM,0);, vous dites au MFT ce courant a pris fin ...

Votre code devrait gérer les choses comme ceci:

  • Begin decode
  • MFT_MESSAGE_NOTIFY_START_OFART_STREAM
  • Entrée de processus si nécessaire
  • Sortie de processus si nécessaire
  • ...
  • ...
  • Entrée process selon les besoins
  • sortie du processus selon les besoins
  • MFT_MESSAGE_NOTIFY_END_OF_STREAM
  • Fin decode

Vous ne recevez outputsample parce que vous dites au MFT que le flux est terminé juste après le premier processus d'entrée d'échantillon.

Lire ceci: MFT_MESSAGE_NOTIFY_START_OF_STREAM

Avise une Fondation pour les médias de transformation (MFT) que le premier échantillon est sur le point d'être traité.

Oui le premier échantillon, pas tous les échantillons.

EDIT

Il y a un autre problème dans 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; 
} 

Vous attendez d'abord m_hNeedInputEvent, puis pour m_hHaveOutputEvent. Mais que se passe-t-il si vous recevez deux fois m_hNeedInputEvent avant m_hHaveOutputEvent. Ce code n'est pas correct. Vous ne gérez pas Invoke correctement. OnReadSample ne doit être appelé que lorsque ProcessInput est terminé. La conception générale semble ne pas être correcte.

MISE À JOUR

Lorsque vous recevez un échantillon dans CMFSourceReader :: OnReadSample, vous avez juste besoin de faire la queue de l'échantillon dans une liste (File d'attente (échantillon)). Pour gérer la liste des échantillons, vous pouvez utiliser ce type de code: SamplePool/ThreadSafeQueue

Dans CMFSourceReader :: Invoke, lorsque vous recevez METransformNeedInput, juste DEQUEUE (échantillon) et appelez ProcessInputSample.

Dans CMFSourceReader :: Invoke, lorsque vous recevez m_hHaveOutputEvent, appelez ProcessOutputSample.

Deux choses:

  • vous pouvez appeler m_pReader-> ReadSample trois fois au début du programme, et attendre d'avoir trois échantillons dans la liste. Lorsque vous avez trois échantillons, vous pouvez commencer le décodage, comme cela, vous serez sûr que lorsque METransformNeedInput se produit, vous avez un échantillon prêt à traiter. Vous pouvez appeler m_pReader-> ReadSample à ce moment, après que ProcessInputSample conserve trois échantillons dans la liste.
  • il est possible que le processus de décodage soit trop rapide par rapport à l'exemple de lecture du lecteur source, ou l'inverse. Vérifiez donc que lorsque METransformNeedInput, il y a toujours des échantillons dans la liste. La stratégie consiste à maintenir un nombre d'échantillons raisonnable, disons trois, pendant le processus de décodage.
+0

Merci pour votre réponse détaillée à ma question, Mofo. Avant de poster cette question, j'ai essayé de cette façon mais pas de chance.Maintenant, j'ai modifié mon code selon vos commentaires et exécuté le binaire encore, je suis confronté au même problème. J'ai joint le code modifié dans le lien suivant dropbox.https: //www.dropbox.com/s/9pu2rddar2mx42x/HwMJPEGDecoderMFT_Code_Query.txt? Dl = 0. Pourriez-vous me faire savoir quand je dois envoyer ce MFT_MESSAGE_NOTIFY_END_OF_STREAM à MFT? Je mets ce drapeau dans MFT en changeant le format vidéo MJPG. Est-ce la bonne façon de dire MFT? De l'aide? – Abi

+0

Je modifie mon message. Publiez un code complet si possible. – mofo77

+0

Merci, mofo. Oui tu as raison. L'événement d'entrée appelle deux fois. Je vais changer le code et vous envoyer le code demain puisque je pars aujourd'hui. Merci une fois de plus. – Abi