C++ 使用媒体基金会从H.264字节流(附件B)创建MP4容器?

C++ 使用媒体基金会从H.264字节流(附件B)创建MP4容器?,c++,com,ms-media-foundation,C++,Com,Ms Media Foundation,基本上,我有一个整洁的H.264字节流,以I和p样本的形式。我可以使用MediaStreamSource和MediaElement播放这些示例,它们播放效果很好。我还需要将它们保存为MP4文件,以便以后可以使用媒体元素或VLC播放。这就是我使用媒体基金会的方式。 我从MFCreateMPEG4MediaSink创建IMFMediaSink;这是我的代码: IMFMediaType *pMediaType = NULL; IMFByteStream *pByteStream = NULL; HRE

基本上,我有一个整洁的H.264字节流,以I和p样本的形式。我可以使用MediaStreamSource和MediaElement播放这些示例,它们播放效果很好。我还需要将它们保存为MP4文件,以便以后可以使用媒体元素或VLC播放。这就是我使用媒体基金会的方式。 我从MFCreateMPEG4MediaSink创建IMFMediaSink;这是我的代码:

IMFMediaType *pMediaType = NULL;
IMFByteStream *pByteStream = NULL;
HRESULT hr = S_OK;
if (SUCCEEDED(hr))
{
    hr = MFCreateMediaType(&pMediaType);
}

pSeqHdr = reinterpret_cast<UINT8 *>(mSamplesQueue.SequenceHeader());
if (SUCCEEDED(hr))
{
    hr = pMediaType->SetBlob(MF_MT_MPEG_SEQUENCE_HEADER, pSeqHdr, 35);
}
UINT32 pcbBlobSize = {0};
hr = pMediaType->GetBlobSize(MF_MT_MPEG_SEQUENCE_HEADER, &pcbBlobSize);

/*if (SUCCEEDED(hr))
{
    hr = pMediaType->SetUINT32(MF_MPEG4SINK_SPSPPS_PASSTHROUGH, TRUE);
}*/
if (SUCCEEDED(hr))
{
    hr = pMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
}
if (SUCCEEDED(hr))
{
    hr = pMediaType->SetGUID(MF_MT_SUBTYPE, VIDEO_INPUT_FORMAT);
}
if (SUCCEEDED(hr))
{
    hr = MFSetAttributeRatio(pMediaType, MF_MT_FRAME_RATE, VIDEO_FPS, 1);
}
if (SUCCEEDED(hr))
{
    hr = pMediaType->SetUINT32(MF_MT_AVG_BITRATE, VIDEO_BIT_RATE);
}
if (SUCCEEDED(hr))
{
    hr = pMediaType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
}
if (SUCCEEDED(hr))
{
    hr = MFSetAttributeSize(pMediaType, MF_MT_FRAME_SIZE, VIDEO_WIDTH, VIDEO_HEIGHT);
}
if (SUCCEEDED(hr))
{
    // Pixel aspect ratio
    hr = MFSetAttributeRatio(pMediaType, MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
}
if (SUCCEEDED(hr))
{
    hr = MFCreateFile(
        MF_ACCESSMODE_READWRITE,
        MF_OPENMODE_DELETE_IF_EXIST,
        MF_FILEFLAGS_NONE,
        L"output1.mp4",
        &pByteStream);
}
if (SUCCEEDED(hr))
{
    hr = MFCreateMPEG4MediaSink(
        pByteStream,
        pMediaType,
        NULL,
        &pMediaSink);
}
if (SUCCEEDED(hr))
{
    hr = MFCreateSinkWriterFromMediaSink(pMediaSink, NULL, &pSinkWriter);
}
// Tell the sink writer to start accepting data.
if (SUCCEEDED(hr))
{
    hr = pSinkWriter->BeginWriting();
}

if (SUCCEEDED(hr))
{
    pSinkWriter->AddRef();
}
然后我使用IMFSinkWriter::WriteSample(0,IMFSample)将每个样本写入接收器编写器;这是我的代码: IMFSample*pSample=NULL; IMFMediaBuffer*pBuffer=NULL

const DWORD cbBuffer = mSamplesQueue.GetNextSampleSize();
UINT32 isIDR = mSamplesQueue.GetNextSampleIsIDR();
BYTE *pData = NULL;

// Create a new memory buffer.
HRESULT hr = MFCreateMemoryBuffer(cbBuffer, &pBuffer);

// Lock the buffer and copy the video frame to the buffer.
if (SUCCEEDED(hr))
{
    DWORD buffLen = cbBuffer;
    hr = pBuffer->Lock(&pData, &buffLen, 0);
}
if (SUCCEEDED(hr))
{
    hr = mSamplesQueue.Dequeu(&pData);
}
if (pBuffer)
{
    pBuffer->Unlock();
}

// Set the data length of the buffer.
if (SUCCEEDED(hr))
{
    hr = pBuffer->SetCurrentLength(cbBuffer);
}

// Create a media sample and add the buffer to the sample.
if (SUCCEEDED(hr))
{
    hr = MFCreateSample(&pSample);
}
if (SUCCEEDED(hr))
{
    hr = pSample->AddBuffer(pBuffer);
}

// Set the time stamp and the duration.
if (SUCCEEDED(hr))
{
    hr = pSample->SetSampleTime(rtStart);
}
if (SUCCEEDED(hr))
{
    hr = pSample->SetSampleDuration(rtDuration);
}
if (SUCCEEDED(hr))
{
    hr = pSample->SetUINT32(MFSampleExtension_CleanPoint, isIDR);
}
//pSample->
// Send the sample to the Sink Writer.
if (SUCCEEDED(hr))
{
    hr = pSinkWriter->WriteSample(0, pSample);
}

SafeRelease(&pSample);
SafeRelease(&pBuffer);
样本的编写是一个迭代代码,从我拥有的每个样本中调用(我使用1k I和p样本进行测试)。现在,当我调用IMFSinkWriter::Finalize()时,它告诉我“0xc00d4a45:接收器无法创建有效的输出文件,因为没有向接收器提供所需的头。”。它确实创建了一个大小非常有效的MP4文件(对于我的1k样本,4.6MB)。 如果它要求使用MF\u MT\u MPEG\u SEQUENCE\u头,那么我将使用IMFMediaType::SetBlob(MF\u MT\u MPEG\u SEQUENCE\u头,字节[],UINT32)设置它们 我用Elecard Video Format Analyzer检查了文件,文件头似乎不完整。 我能否得到一些帮助,找出我缺少的东西,或者是否有更好的/其他的方法来实现我正在努力实现的目标?
谢谢

对我来说,问题在于MF\u MT\u MPEG\u序列\u头blob的格式

与dwSequenceHeader和H.264上的MSDN文档不同,SPS和PPS应以起始代码(0x00,0x00,0x01)而不是2字节长度字段作为前缀


对此不确定,但不应:

hr = pMediaType->SetGUID(MF_MT_SUBTYPE, VIDEO_ENCODING_FORMAT);
而不是:

hr = pMediaType->SetGUID(MF_MT_SUBTYPE, VIDEO_INPUT_FORMAT);

您有描述h264流的SequenceParameterSet和PictureParameterSet吗?也许他们不见了。我拥有的所有MP4文件都在头中包含这两个。是的,我有,我在上面的代码中将它们设置为pMediaType->SetBlob(MF_mtu_MPEG_SEQUENCE_header,pSeqHdr,35)。要测试MP4的外观,可以使用mp4box并打包原始h264流。然后用elecard比较这两个文件并搜索差异。感谢@CPlusSharp的回复。我已经使用ffmpeg测试了我的样本。他们组成了一个好的mp4。唯一的差异,我可以注意到使用ErdARD是头,这是不成形的情况下,媒体基础。