microsoft / media-foundation

Repository for Windows Media Foundation related tools and samples
MIT License
145 stars 31 forks source link

MF_MPEG4SINK_MOOV_BEFORE_MDAT = TRUE : invalid output file #59

Open persskog opened 1 year ago

persskog commented 1 year ago

Hi,

I am trying to encode a mp4-file with "fast start" using a IMFSourceReader and a IMFSinkWriter. If a use the default settings, with no extra stuff the output file is ok:

If I then add the attribute: sinkAttr->SetUINT32(MF_MPEG4SINK_MOOV_BEFORE_MDAT, TRUE) the output file gets corrupted, the mdat box is gone and the file is not playable.

// output file
// stream gets the following caps: 
// MFBYTESTREAM_IS_READABLE | MFBYTESTREAM_IS_WRITABLE | MFBYTESTREAM_IS_SEEKABLE | MFBYTESTREAM_DOES_NOT_USE_NETWORK
winrt::com_ptr<IMFByteStream> stream;
::MFCreateFile(MF_ACCESSMODE_READWRITE, MF_OPENMODE_DELETE_IF_EXIST, MF_FILEFLAGS_NONE, L"movie.mp4", stream.put());

"In order for the mpeg4 sink to use this attribute, the byte stream passed in must not be slow seek or remote for" ✅

// Media Sink creation
winrt::com_ptr<IMFMediaSink> sink;
::MFCreateMPEG4MediaSink(stream.get(), h264.get(), nullptr, sink.put());
auto sinkAttr = sink.as<IMFAttributes>();
sinkAttr->SetUINT32(MF_MPEG4SINK_MOOV_BEFORE_MDAT, TRUE);
// SinkWriter configuration
winrt::com_ptr<IMFSinkWriter> writer;
::MFCreateSinkWriterFromMediaSink(sink.get(), attr.get(), writer.put());
writer->SetInputMediaType(0, inputType.get(), nullptr);
writer->BeginWriting()
while (framesToEncode > 0)
{
    winrt::com_ptr<IMFSample> sample;
    DWORD flags{};
    int64_t timestamp{};
    HRESULT hr = reader->ReadSample(0, 0, nullptr, &flags, &timestamp, sample.put());
    if (sample)
    {
        int64_t duration{};
        sample->GetSampleDuration(&duration);
        sample->SetSampleTime(time);
        winrt::check_hresult(writer->WriteSample(0, sample.get()));
        time += duration;
        --framesToEncode;
    }
    else
    {
        if (flags & MF_SOURCE_READERF_ERROR)
        {
            throw winrt::hresult_error(hr);
        }
        else if (flags & MF_SOURCE_READERF_NATIVEMEDIATYPECHANGED)
        {
           ...
        }
        else if (flags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED)
        {
            ...
        }
        else if (flags & MF_SOURCE_READERF_STREAMTICK)
        {
            ...
        }
        else
        {
            ...
        }
    }
}
writer->NotifyEndOfSegment(0);
writer->Finalize();

Is this feature working? Or am I doing something wrong?

I am running this on the following system: