Creating an MP4 container from an h.264 byte stream (Annex B), using media foundation?

3

Basically, I have a neat H.264 byte stream in the form of I and P samples. I can play these samples using MediaStreamSource and MediaElement and they play good. I also need to save them as an MP4 file so that the same can be played later using Media Element or VLC. This is how I am trying to do it, using Media Foundation; I create an IMFMediaSink from MFCreateMPEG4MediaSink; this is my code:

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

Then I create an IMFSinkWriter from this media sink using MFCreateSinkWriterFromMediaSink; this is my code:

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

And then I write every sample to the sink writer with IMFSinkWriter::WriteSample(0, IMFSample); this is my code: 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);

The writing of samples is an iterative code that is called from every sample that I have (I am testing with 1k I and P samples). Now when I call the IMFSinkWriter::Finalize(), it tells me that "0xc00d4a45 : Sink could not create valid output file because required headers were not provided to the sink.". It does create an MP4 file with a very valid size (for my 1k samples, 4.6 MB). This is the link to the trace from MFTrace. If it is asking for MF_MT_MPEG_SEQUENCE_HEADER then I am setting them with IMFMediaType::SetBlob(MF_MT_MPEG_SEQUENCE_HEADER, BYTE[], UINT32) I checked the file with Elecard Video Format Analyzer and the header seems incomplete. Could I get some help finding out what I am missing or whether there is some better/other way of doing what I am trying to achieve? Thanks!

c++
com
ms-media-foundation
asked on Stack Overflow Feb 27, 2014 by Manish Verma

2 Answers

4

For me the problem was the format of the MF_MT_MPEG_SEQUENCE_HEADER blob.

In contrast to the MSDN docs on dwSequenceHeader and H.264 the SPS and PPS should be prepended with start codes (0x00,0x00,0x01) instead of the 2-byte length fields.

http://msdn.microsoft.com/en-us/library/dd757808%28VS.85%29.aspx

answered on Stack Overflow Apr 28, 2014 by user3569107
0

Not sure about that but, shouldnt be:

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

rather than:

hr = pMediaType->SetGUID(MF_MT_SUBTYPE, VIDEO_INPUT_FORMAT);
answered on Stack Overflow Jul 17, 2014 by Nox

User contributions licensed under CC BY-SA 3.0