Media Foundation 网络摄像头视频 H264 encode/decode 在播放时产生伪像
Media Foundation webcam video H264 encode/decode produces artifacts when played back
我有一个解决方案,我使用 Media Foundation 的 h264 编码器对来自网络摄像头的视频 (YUY2) 样本进行编码。然后我通过 TCP 连接将它发送到另一个应用程序,该应用程序使用 Media Foundation 的 h264 解码器将流解码回 YUY2 格式。解码后,视频 samples/images 使用 DirectX 呈现在屏幕上。
问题是在关键帧之间,视频图像出现越来越多的伪像。收到关键帧时,伪影消失。
我将 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;
}
这里是 encoder/decoder 初始化代码:
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;
}
这是实际的 decode/encoder 部分:
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;
}
我已经为此搜索了很长一段时间的解决方案,发现了一个与我的问题定义非常相似的问题,但因为它是针对不同的 API,所以对我没有帮助. FFMPEG decoding artifacts between keyframes
此致,
托尼·里科宁
听起来像是 quality/bitrate 问题。
pMFTOutputMediaType->SetUINT32(MF_MT_AVG_BITRATE, 500000);
500kbps 的比特率值太低,您可以尝试更大的比特率,例如 5、10 或 20Mbps。
我可以建议:
由于您是自己创建 H264 编码器,您可以查询它的 ICodecAPI 并尝试不同的设置。即,CODECAPI_AVEncCommonRateControlMode、CODECAPI_AVEncCommonQuality、CODECAPI_AVEncAdaptiveMode、CODECAPI_AVEncCommonQualityVsSpeed、CODECAPI_AVEncVideoEncodeQP。
您也可以尝试创建硬件 H264 编码器并使用 IMFDXGIDeviceManager(Windows8 及以上?)
听起来像是 IP(B) 帧排序问题。
编码帧顺序与解码帧顺序不同。我没有测试你的代码,但我认为编码器按编码顺序提供帧,你需要在渲染前重新排序帧。
这个问题好像有答案了,不过还是想分享一下我的经验。希望对遇到类似问题的朋友有所帮助。
我在解码 H264 时也遇到了类似的伪影问题。但是,就我而言,流来自视频捕获设备,流开始后 30-60 秒后伪像不会消失。
在我看来,我猜正常设置的解码器由于延迟低而无法解码直播。因此,我尝试启用 CODECAPI_AVLowLatencyMode 可以将 decode/encode 模式设置为低延迟以进行实时通信或实时捕获。 (要获得更多详细信息,请参阅以下来自 MS 的 link
https://msdn.microsoft.com/zh-tw/library/windows/desktop/hh447590(v=vs.85).aspx
) 还好问题已经解决,解码器正常工作。
虽然我们的问题有点不同,你可以尝试enable/disableCODECAPI_AVLowLatencyMode你的情况,希望你也能有好消息。
我来晚了一点,但我可以确认主页上的答案是正确的解决方案。我也遇到了同样的问题,但我只使用了这个示例代码的解码器部分。我在阅读 MP4 文件时发现关键帧之间的伪像越来越多。我一收到关键帧,图像看起来不错,然后逐渐变差。这是我在 Codec::InitializeDecoder():
中添加的代码
// Set CODECAPI_AVLowLatencyMode
ICodecAPI *mpCodecAPI = NULL;
hr = pDecoderTransform->QueryInterface(IID_PPV_ARGS(&mpCodecAPI));
CHECK_HR(hr, "Failed to get ICodecAPI.\n");
VARIANT var;
var.vt = VT_BOOL;
var.boolVal = VARIANT_TRUE;
hr = mpCodecAPI->SetValue(&CODECAPI_AVLowLatencyMode, &var);
CHECK_HR(hr, "Failed to enable low latency mode.\n");
添加这些更改后,程序运行得更好!感谢 GitHub 上的这个软件给了我必要的代码:
https://github.com/GameTechDev/ChatHeads/blob/master/VideoStreaming/EncodeTransform.cpp
我有一个解决方案,我使用 Media Foundation 的 h264 编码器对来自网络摄像头的视频 (YUY2) 样本进行编码。然后我通过 TCP 连接将它发送到另一个应用程序,该应用程序使用 Media Foundation 的 h264 解码器将流解码回 YUY2 格式。解码后,视频 samples/images 使用 DirectX 呈现在屏幕上。
问题是在关键帧之间,视频图像出现越来越多的伪像。收到关键帧时,伪影消失。
我将 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;
}
这里是 encoder/decoder 初始化代码:
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;
}
这是实际的 decode/encoder 部分:
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;
}
我已经为此搜索了很长一段时间的解决方案,发现了一个与我的问题定义非常相似的问题,但因为它是针对不同的 API,所以对我没有帮助. FFMPEG decoding artifacts between keyframes
此致, 托尼·里科宁
听起来像是 quality/bitrate 问题。
pMFTOutputMediaType->SetUINT32(MF_MT_AVG_BITRATE, 500000);
500kbps 的比特率值太低,您可以尝试更大的比特率,例如 5、10 或 20Mbps。
我可以建议:
由于您是自己创建 H264 编码器,您可以查询它的 ICodecAPI 并尝试不同的设置。即,CODECAPI_AVEncCommonRateControlMode、CODECAPI_AVEncCommonQuality、CODECAPI_AVEncAdaptiveMode、CODECAPI_AVEncCommonQualityVsSpeed、CODECAPI_AVEncVideoEncodeQP。
您也可以尝试创建硬件 H264 编码器并使用 IMFDXGIDeviceManager(Windows8 及以上?)
听起来像是 IP(B) 帧排序问题。
编码帧顺序与解码帧顺序不同。我没有测试你的代码,但我认为编码器按编码顺序提供帧,你需要在渲染前重新排序帧。
这个问题好像有答案了,不过还是想分享一下我的经验。希望对遇到类似问题的朋友有所帮助。
我在解码 H264 时也遇到了类似的伪影问题。但是,就我而言,流来自视频捕获设备,流开始后 30-60 秒后伪像不会消失。
在我看来,我猜正常设置的解码器由于延迟低而无法解码直播。因此,我尝试启用 CODECAPI_AVLowLatencyMode 可以将 decode/encode 模式设置为低延迟以进行实时通信或实时捕获。 (要获得更多详细信息,请参阅以下来自 MS 的 link https://msdn.microsoft.com/zh-tw/library/windows/desktop/hh447590(v=vs.85).aspx ) 还好问题已经解决,解码器正常工作。
虽然我们的问题有点不同,你可以尝试enable/disableCODECAPI_AVLowLatencyMode你的情况,希望你也能有好消息。
我来晚了一点,但我可以确认主页上的答案是正确的解决方案。我也遇到了同样的问题,但我只使用了这个示例代码的解码器部分。我在阅读 MP4 文件时发现关键帧之间的伪像越来越多。我一收到关键帧,图像看起来不错,然后逐渐变差。这是我在 Codec::InitializeDecoder():
中添加的代码// Set CODECAPI_AVLowLatencyMode
ICodecAPI *mpCodecAPI = NULL;
hr = pDecoderTransform->QueryInterface(IID_PPV_ARGS(&mpCodecAPI));
CHECK_HR(hr, "Failed to get ICodecAPI.\n");
VARIANT var;
var.vt = VT_BOOL;
var.boolVal = VARIANT_TRUE;
hr = mpCodecAPI->SetValue(&CODECAPI_AVLowLatencyMode, &var);
CHECK_HR(hr, "Failed to enable low latency mode.\n");
添加这些更改后,程序运行得更好!感谢 GitHub 上的这个软件给了我必要的代码: https://github.com/GameTechDev/ChatHeads/blob/master/VideoStreaming/EncodeTransform.cpp