2017-01-09 27 views
5

У меня есть решение, в котором я кодирую видео (YUY2) с веб-камеры с помощью кодировщика h264 Media Foundation. Затем я отправляю его через TCP-соединение в другое приложение, которое декодирует поток с декодером h264 Media Foundation обратно в формат YUY2. После декодирования образцы/изображения видео представлены на экране с использованием DirectX.Видеокамера Media Foundation видео H264 encode/decode производит артефакты при воспроизведении

Проблема в том, что между ключевыми кадрами видеоизображение увеличивает количество артефактов. Артефакты исчезают при получении ключевого кадра.

Я удалил TCP-соединение из области действия и сделал декодирование сразу после кодирования, но все же у меня есть артефакты, беспокоящие меня.

Вот метод обратного вызова, который принимает выборки из веб-камеры:

//------------------------------------------------------------------- 
// OnReadSample 
// 
// Called when the IMFMediaSource::ReadSample method completes. 
//------------------------------------------------------------------- 

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

    EnterCriticalSection(&m_critsec); 

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

    if (SUCCEEDED(hr)) 
    { 
     if (pSample) 
     { 
      IMFSample *pEncodedSample = NULL; 
      hr = m_pCodec->EncodeSample(pSample, &pEncodedSample); 
      if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT || pEncodedSample == NULL) 
      { 
       hr = m_pReader->ReadSample((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, NULL, NULL, NULL, NULL); 
       LeaveCriticalSection(&m_critsec); 
       return S_OK; 
      } 

      LONGLONG llEncodedSampleTimeStamp = 0; 
      LONGLONG llEncodedSampleDuration = 0; 
      pEncodedSample->GetSampleTime(&llEncodedSampleTimeStamp); 
      pEncodedSample->GetSampleDuration(&llEncodedSampleDuration); 

      pBuffer = NULL; 
      hr = pEncodedSample->GetBufferByIndex(0, &pBuffer); 
      if (hr != S_OK) 
      { 
       hr = m_pReader->ReadSample((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, NULL, NULL, NULL, NULL); 
       LeaveCriticalSection(&m_critsec); 
       return hr; 
      } 

      BYTE *pOutBuffer = NULL; 
      DWORD dwMaxLength, dwCurrentLength; 
      hr = pBuffer->Lock(&pOutBuffer, &dwMaxLength, &dwCurrentLength); 
      if (hr != S_OK) 
      { 
       hr = m_pReader->ReadSample((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, NULL, NULL, NULL, NULL); 
       LeaveCriticalSection(&m_critsec); 
       return hr; 
      } 
      // Send encoded webcam data to connected clients 
      //SendData(pOutBuffer, dwCurrentLength, llEncodedSampleTimeStamp, llEncodedSampleDuration); 

      pBuffer->Unlock(); 
      SafeRelease(&pBuffer); 

      IMFSample *pDecodedSample = NULL;   
      m_pCodec->DecodeSample(pEncodedSample, &pDecodedSample); 
      if (pDecodedSample != NULL) 
      { 
       pDecodedSample->SetSampleTime(llTimestamp); 
       pDecodedSample->SetSampleTime(llTimestamp - llLastSampleTimeStamp); 
       llLastSampleTimeStamp = llTimestamp; 
       hr = pDecodedSample->GetBufferByIndex(0, &pBuffer); 
       //hr = pSample->GetBufferByIndex(0, &pBuffer); 

       // Draw the frame. 
       if (SUCCEEDED(hr)) 
       { 
        hr = m_draw.DrawFrame(pBuffer); 
       } 
       SafeRelease(&pDecodedSample); 
      } 

      SafeRelease(&pBuffer); 
      SafeRelease(&pEncodedSample);   
     } 
    } 

    // Request the next frame. 
    if (SUCCEEDED(hr)) 
    { 
     hr = m_pReader->ReadSample(
      (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, 
      0, 
      NULL, // actual 
      NULL, // flags 
      NULL, // timestamp 
      NULL // sample 
      ); 
    } 

    if (FAILED(hr)) 
    { 
     NotifyError(hr); 
    } 
    SafeRelease(&pBuffer); 

    LeaveCriticalSection(&m_critsec); 
    return hr; 
} 

А вот кодер/декодер кода инициализации:

HRESULT Codec::InitializeEncoder() 
    { 
     IMFMediaType *pMFTInputMediaType = NULL, *pMFTOutputMediaType = NULL; 
     IUnknown *spTransformUnk = NULL;  
     DWORD mftStatus = 0; 
     UINT8 blob[] = { 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0xc0, 0x1e, 0x96, 0x54, 0x05, 0x01, 
      0xe9, 0x80, 0x80, 0x40, 0x00, 0x00, 0x00, 0x01, 0x68, 0xce, 0x3c, 0x80 }; 

     CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); 
     MFStartup(MF_VERSION); 

     // Create H.264 encoder. 
     CHECK_HR(CoCreateInstance(CLSID_CMSH264EncoderMFT, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&spTransformUnk), "Failed to create H264 encoder MFT.\n"); 

     CHECK_HR(spTransformUnk->QueryInterface(IID_PPV_ARGS(&pEncoderTransform)), "Failed to get IMFTransform interface from H264 encoder MFT object.\n"); 

     // Transform output type 
     MFCreateMediaType(&pMFTOutputMediaType); 
     pMFTOutputMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); 
     pMFTOutputMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264); 
     pMFTOutputMediaType->SetUINT32(MF_MT_AVG_BITRATE, 500000); 
     CHECK_HR(MFSetAttributeSize(pMFTOutputMediaType, MF_MT_FRAME_SIZE, 640, 480), "Failed to set frame size on H264 MFT out type.\n"); 
     CHECK_HR(MFSetAttributeRatio(pMFTOutputMediaType, MF_MT_FRAME_RATE, 30, 1), "Failed to set frame rate on H264 MFT out type.\n"); 
     CHECK_HR(MFSetAttributeRatio(pMFTOutputMediaType, MF_MT_PIXEL_ASPECT_RATIO, 1, 1), "Failed to set aspect ratio on H264 MFT out type.\n"); 
     pMFTOutputMediaType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_MixedInterlaceOrProgressive); 
     pMFTOutputMediaType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE); 

     // Special attributes for H264 transform, if needed 
     /*CHECK_HR(pMFTOutputMediaType->SetUINT32(MF_MT_MPEG2_PROFILE, eAVEncH264VProfile_Base), "Failed to set profile on H264 MFT out type.\n"); 
     CHECK_HR(pMFTOutputMediaType->SetUINT32(MF_MT_MPEG2_LEVEL, eAVEncH264VLevel4), "Failed to set level on H264 MFT out type.\n"); 
     CHECK_HR(pMFTOutputMediaType->SetUINT32(MF_MT_MAX_KEYFRAME_SPACING, 10), "Failed to set key frame interval on H264 MFT out type.\n"); 
     CHECK_HR(pMFTOutputMediaType->SetUINT32(CODECAPI_AVEncCommonQuality, 100), "Failed to set H264 codec qulaity.\n"); 
     CHECK_HR(pMFTOutputMediaType->SetUINT32(CODECAPI_AVEncMPVGOPSize, 1), "Failed to set CODECAPI_AVEncMPVGOPSize = 1\n");*/ 
     CHECK_HR(pEncoderTransform->SetOutputType(0, pMFTOutputMediaType, 0), "Failed to set output media type on H.264 encoder MFT.\n"); 

     // Transform input type 
     MFCreateMediaType(&pMFTInputMediaType); 
     pMFTInputMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); 
     pMFTInputMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_YUY2); 
     CHECK_HR(MFSetAttributeSize(pMFTInputMediaType, MF_MT_FRAME_SIZE, 640, 480), "Failed to set frame size on H264 MFT out type.\n"); 
     CHECK_HR(MFSetAttributeRatio(pMFTInputMediaType, MF_MT_FRAME_RATE, 30, 1), "Failed to set frame rate on H264 MFT out type.\n"); 
     CHECK_HR(MFSetAttributeRatio(pMFTInputMediaType, MF_MT_PIXEL_ASPECT_RATIO, 1, 1), "Failed to set aspect ratio on H264 MFT out type.\n"); 
     CHECK_HR(pEncoderTransform->SetInputType(0, pMFTInputMediaType, 0), "Failed to set input media type on H.264 encoder MFT.\n"); 

     CHECK_HR(pEncoderTransform->GetInputStatus(0, &mftStatus), "Failed to get input status from H.264 MFT.\n"); 
     if (MFT_INPUT_STATUS_ACCEPT_DATA != mftStatus) 
     { 
      printf("E: pEncoderTransform->GetInputStatus() not accept data.\n"); 
      goto done; 
     } 

     CHECK_HR(pEncoderTransform->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, NULL), "Failed to process FLUSH command on H.264 MFT.\n"); 
     CHECK_HR(pEncoderTransform->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, NULL), "Failed to process BEGIN_STREAMING command on H.264 MFT.\n"); 
     CHECK_HR(pEncoderTransform->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, NULL), "Failed to process START_OF_STREAM command on H.264 MFT.\n"); 

     return S_OK; 

    done: 

     SafeRelease(&pMFTInputMediaType); 
     SafeRelease(&pMFTOutputMediaType); 

     return S_FALSE; 
    } 

    HRESULT Codec::InitializeDecoder() 
    { 
     IUnknown *spTransformUnk = NULL; 
     IMFMediaType *pMFTOutputMediaType = NULL; 
     IMFMediaType *pMFTInputMediaType = NULL; 
     DWORD mftStatus = 0; 

     // Create H.264 decoder. 
     CHECK_HR(CoCreateInstance(CLSID_CMSH264DecoderMFT, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&spTransformUnk), "Failed to create H264 decoder MFT.\n"); 

     // Query for the IMFTransform interface 
     CHECK_HR(spTransformUnk->QueryInterface(IID_PPV_ARGS(&pDecoderTransform)), "Failed to get IMFTransform interface from H264 decoder MFT object.\n"); 

     // Create input mediatype for the decoder 
     MFCreateMediaType(&pMFTInputMediaType); 
     pMFTInputMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); 
     pMFTInputMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264); 
     CHECK_HR(MFSetAttributeSize(pMFTInputMediaType, MF_MT_FRAME_SIZE, 640, 480), "Failed to set frame size on H264 MFT out type.\n"); 
     CHECK_HR(MFSetAttributeRatio(pMFTInputMediaType, MF_MT_FRAME_RATE, 30, 1), "Failed to set frame rate on H264 MFT out type.\n"); 
     CHECK_HR(MFSetAttributeRatio(pMFTInputMediaType, MF_MT_PIXEL_ASPECT_RATIO, 1, 1), "Failed to set aspect ratio on H264 MFT out type.\n"); 
     pMFTInputMediaType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_MixedInterlaceOrProgressive); 
     pMFTInputMediaType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE); 
     CHECK_HR(pDecoderTransform->SetInputType(0, pMFTInputMediaType, 0), "Failed to set input media type on H.264 encoder MFT.\n"); 

     CHECK_HR(pDecoderTransform->GetInputStatus(0, &mftStatus), "Failed to get input status from H.264 MFT.\n"); 
     if (MFT_INPUT_STATUS_ACCEPT_DATA != mftStatus) 
     { 
      printf("E: pDecoderTransform->GetInputStatus() not accept data.\n"); 
      goto done; 
     } 

     // Create outmedia type for the decoder 
     MFCreateMediaType(&pMFTOutputMediaType); 
     pMFTOutputMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); 
     pMFTOutputMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_YUY2); 
     CHECK_HR(MFSetAttributeSize(pMFTOutputMediaType, MF_MT_FRAME_SIZE, 640, 480), "Failed to set frame size on H264 MFT out type.\n"); 
     CHECK_HR(MFSetAttributeRatio(pMFTOutputMediaType, MF_MT_FRAME_RATE, 30, 1), "Failed to set frame rate on H264 MFT out type.\n"); 
     CHECK_HR(MFSetAttributeRatio(pMFTOutputMediaType, MF_MT_PIXEL_ASPECT_RATIO, 1, 1), "Failed to set aspect ratio on H264 MFT out type.\n"); 
     CHECK_HR(pDecoderTransform->SetOutputType(0, pMFTOutputMediaType, 0), "Failed to set output media type on H.264 decoder MFT.\n"); 

     CHECK_HR(pDecoderTransform->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, NULL), "Failed to process FLUSH command on H.264 MFT.\n"); 
     CHECK_HR(pDecoderTransform->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, NULL), "Failed to process BEGIN_STREAMING command on H.264 MFT.\n"); 
     CHECK_HR(pDecoderTransform->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, NULL), "Failed to process START_OF_STREAM command on H.264 MFT.\n"); 

     return S_OK; 

    done: 

     SafeRelease(&pMFTInputMediaType); 
     SafeRelease(&pMFTOutputMediaType); 

     return S_FALSE; 
    } 

Вот фактическое расшифровывает/кодировщик часть:

HRESULT Codec::EncodeSample(IMFSample *pSample, IMFSample **ppEncodedSample) 
{ 
    return TransformSample(pEncoderTransform, pSample, ppEncodedSample); 
} 

HRESULT Codec::DecodeSample(IMFSample *pSample, IMFSample **ppEncodedSample) 
{ 
    return TransformSample(pDecoderTransform, pSample, ppEncodedSample); 
} 

HRESULT Codec::TransformSample(IMFTransform *pTransform, IMFSample *pSample, IMFSample **ppSampleOut) 
{ 
    IMFSample *pOutSample = NULL; 
    IMFMediaBuffer *pBuffer = NULL; 
    DWORD mftOutFlags; 
    pTransform->ProcessInput(0, pSample, 0); 
    CHECK_HR(pTransform->GetOutputStatus(&mftOutFlags), "H264 MFT GetOutputStatus failed.\n"); 

    // Note: Decoder does not return MFT flag MFT_OUTPUT_STATUS_SAMPLE_READY, so we just need to rely on S_OK return 
    if (pTransform == pEncoderTransform && mftOutFlags == S_OK) 
    { 
     return S_OK; 
    } 
    else if (pTransform == pEncoderTransform && mftOutFlags == MFT_OUTPUT_STATUS_SAMPLE_READY || 
     pTransform == pDecoderTransform && mftOutFlags == S_OK) 
    { 
     DWORD processOutputStatus = 0; 
     MFT_OUTPUT_DATA_BUFFER outputDataBuffer; 
     MFT_OUTPUT_STREAM_INFO StreamInfo; 
     pTransform->GetOutputStreamInfo(0, &StreamInfo); 

     CHECK_HR(MFCreateSample(&pOutSample), "Failed to create MF sample.\n"); 
     CHECK_HR(MFCreateMemoryBuffer(StreamInfo.cbSize, &pBuffer), "Failed to create memory buffer.\n"); 
     if (pTransform == pEncoderTransform) 
      CHECK_HR(pBuffer->SetCurrentLength(StreamInfo.cbSize), "Failed SetCurrentLength.\n"); 
     CHECK_HR(pOutSample->AddBuffer(pBuffer), "Failed to add sample to buffer.\n");  
     outputDataBuffer.dwStreamID = 0; 
     outputDataBuffer.dwStatus = 0; 
     outputDataBuffer.pEvents = NULL; 
     outputDataBuffer.pSample = pOutSample; 

     HRESULT hr = pTransform->ProcessOutput(0, 1, &outputDataBuffer, &processOutputStatus); 
     if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) 
     { 
      SafeRelease(&pBuffer); 
      SafeRelease(&pOutSample); 
      return hr; 
     } 

     LONGLONG llVideoTimeStamp, llSampleDuration; 
     pSample->GetSampleTime(&llVideoTimeStamp); 
     pSample->GetSampleDuration(&llSampleDuration); 
     CHECK_HR(outputDataBuffer.pSample->SetSampleTime(llVideoTimeStamp), "Error setting MFT sample time.\n"); 
     CHECK_HR(outputDataBuffer.pSample->SetSampleDuration(llSampleDuration), "Error setting MFT sample duration.\n");   
     if (pTransform == pEncoderTransform) 
     { 
      IMFMediaBuffer *pMediaBuffer = NULL; 
      DWORD dwBufLength; 
      CHECK_HR(pOutSample->ConvertToContiguousBuffer(&pMediaBuffer), "ConvertToContiguousBuffer failed.\n"); 
      CHECK_HR(pMediaBuffer->GetCurrentLength(&dwBufLength), "Get buffer length failed.\n"); 

      WCHAR *strDebug = new WCHAR[256]; 
      wsprintf(strDebug, L"Encoded sample ready: time %I64d, sample duration %I64d, sample size %i.\n", llVideoTimeStamp, llSampleDuration, dwBufLength); 
      OutputDebugString(strDebug); 
      SafeRelease(&pMediaBuffer); 
     } 
     else if (pTransform == pDecoderTransform) 
     { 
      IMFMediaBuffer *pMediaBuffer = NULL; 
      DWORD dwBufLength; 
      CHECK_HR(pOutSample->ConvertToContiguousBuffer(&pMediaBuffer), "ConvertToContiguousBuffer failed.\n"); 
      CHECK_HR(pMediaBuffer->GetCurrentLength(&dwBufLength), "Get buffer length failed.\n"); 

      WCHAR *strDebug = new WCHAR[256]; 
      wsprintf(strDebug, L"Decoded sample ready: time %I64d, sample duration %I64d, sample size %i.\n", llVideoTimeStamp, llSampleDuration, dwBufLength); 
      OutputDebugString(strDebug); 
      SafeRelease(&pMediaBuffer); 
     } 

     // Decoded sample out 
     *ppSampleOut = pOutSample; 

     //SafeRelease(&pMediaBuffer); 
     SafeRelease(&pBuffer); 

     return S_OK; 
    } 

done: 
    SafeRelease(&pBuffer); 
    SafeRelease(&pOutSample); 

    return S_FALSE; 
} 

Я искал решение для этого довольно долго и нашел один que который определен совершенно так же, как и моя проблема, но, как и для другого API, это не помогло мне. FFMPEG decoding artifacts between keyframes

С наилучшими пожеланиями, Toni Riikonen

+0

Я заметил, что, если я жду около 30-60 секунд после начала потока, артефакты исчезли. Может ли это быть чем-то вроде проблемы с буферизацией, или мне нужно немного сместить образцы, прежде чем позволить декодеру овладеть ими. Или что-то не так с моими метками? –

+1

pMFTOutputMediaType-> SetUINT32 (MF_MT_AVG_BITRATE, 500000); 500 Кбит/с - слишком низкое значение для битрейта. В этом случае качество кодирования будет плохим и приведет к артефактам. Попробуйте вместо 5000000 (5 Мбит/с) или даже более высокого значения. – VuVirt

+0

Пробовали более крупные битрейты, артефакты уменьшаются по размеру, но не исчезают. Я не говорю об обычных артефактах, вызванных алгоритмом кодирования с потерями, но артефактами, которые мне кажутся, это сброс данных, или образцы не соответствуют порядку. Возможно ли, что в методе обратного вызова OnReadSample, поскольку мне нужно несколько входных выборок для первого кодированного образца, выпущены IMFS-образцы, которые передаются этой функции обратного вызова? Я думал, что, возможно, копия предоставленных образцов будет функцией обратного вызова, а затем освободит их после того, как я закончил с ними. –

ответ

1

Это звучит как качество/битрейтом проблемы.

pMFTOutputMediaType->SetUINT32(MF_MT_AVG_BITRATE, 500000); 

500 кб является слишком низкое значение битрейта, вы можете попробовать с чем-то большим, как 5, 10 или 20 Мбит.

Я могу предложить:

  1. Поскольку вы создаете H264 кодировщик самостоятельно, вы можете запросить его для ICodecAPI и попробуйте различные настройки. А именно: CODECAPI_AVEncCommonRateControlMode, CODECAPI_AVEncCommonQuality, CODECAPI_AVEncAdaptiveMode, CODECAPI_AVEncCommonQualityVsSpeed, CODECAPI_AVEncVideoEncodeQP.

  2. Вы также можете попробовать создать H264 кодировщик аппаратных средств и использовать IMFDXGIDeviceManager с ним (Windows 8 и выше?)

0

Это звучит как IP (B) проблемы упорядочения кадров.

Закодированный порядок кадров не совпадает с порядком декодированного кадра. Я не тестировал ваш код, но я думаю, что кодер обеспечивает кадр в закодированном порядке и что вам нужно переупорядочить кадр перед рендерингом.

+0

Кажется, что даже с использованием образцов Microsoft API и сохранения в качестве MP4 артефакты все еще присутствуют. Поэтому я предполагаю, что кодировщик/декодер H264 от Intel плохо работает при обработке шума на изображении веб-камеры (так как самая дешевая веб-камера создает шумное изображение), или кодер/декодер несколько сломан. Я решил переключиться на использование WMV3. Не уверен, какой ответ будет правильным ответом, так как фактическая проблема не будет полностью решена и может быть проблемой, которая выходит из рук разработчика. –

0

Этот вопрос, кажется, имеет ответ, но я все еще хочу поделиться своим опытом. Надеюсь, что это поможет тем, кто встретил подобную проблему.

Я также столкнулся с аналогичной проблемой артефактов при декодировании H264. Однако в моем случае поток поступает из устройства захвата видео, и артефакты не исчезают через 30-60 секунд от начала потока.

Полагаю, что декодер с нормальной настройкой не может декодировать потоковое вещание из-за низкой латентности. Таким образом, я пытаюсь включить CODECAPI_AVLowLatencyMode, который может установить режим декодирования/кодирования в Low-latency для обмена в реальном времени или в режиме реального времени. (Чтобы получить более подробную информацию, пожалуйста, обратитесь по следующей ссылке от MS https://msdn.microsoft.com/zh-tw/library/windows/desktop/hh447590(v=vs.85).aspx ) К счастью, проблема решена, и декодер работает нормально.

Несмотря на то, что наша проблема немного отличается от нашей проблемы, вы можете попытаться включить/отключить CODECAPI_AVLowLatencyMode в вашем случае, и я надеюсь, что у вас также могут быть хорошие новости.