2

When I run encoder->ProcessInput(stream_id, sample.Get(), 0) I am getting a E_FAIL ("Unspecified error") error which isn't very helpful.

I am either trying to (1) Figure out what the real error is and/or (2) get past this unspecified error.

Ultimately, my goal is achieving this: http://alax.info/blog/1716

Here's the gist of what I am doing:

(Error occurs in this block)

void encode_frame(ComPtr<ID3D11Texture2D> texture) {
    _com_error error = NULL;
    IMFTransform *encoder = nullptr;
    encoder = get_encoder();

    if (!encoder) {
        cout << "Did not get a valid encoder to utilize\n";
        return;
    }

    cout << "Making it Direct3D aware...\n";
    setup_D3_aware_mft(encoder);

    cout << "Setting up input/output media types...\n";
    setup_media_types(encoder);

    error = encoder->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, NULL); // flush all stored data
    error = encoder->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, NULL);
    error = encoder->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, NULL); // first sample is about to be processed, req for async
    cout << "Encoding image...\n";

    IMFMediaEventGenerator *event_generator = nullptr;
    error = encoder->QueryInterface(&event_generator);

    while (true) {

        IMFMediaEvent *event = nullptr;
        MediaEventType type;

        error = event_generator->GetEvent(0, &event);
        error = event->GetType(&type);

        uint32_t stream_id = get_stream_id(encoder); // Likely just going to be 0
        uint32_t frame = 1;

        uint64_t sample_duration = 0;
        ComPtr<IMFSample> sample = nullptr;
        IMFMediaBuffer *mbuffer = nullptr;
        DWORD length = 0;
        uint32_t img_size = 0;

        MFCalculateImageSize(desktop_info.input_sub_type, desktop_info.width, desktop_info.height, &img_size);

        switch (type) {
        case METransformNeedInput:
            ThrowIfFailed(MFCreateDXGISurfaceBuffer(__uuidof(ID3D11Texture2D), texture.Get(), 0, false, &mbuffer),
                mbuffer, "Failed to generate a media buffer");

            ThrowIfFailed(MFCreateSample(&sample), sample.Get(), "Couldn't create sample buffer");
            ThrowIfFailed(sample->AddBuffer(mbuffer), sample.Get(), "Couldn't add buffer");

            // Test (delete this) - fake buffer
            /*byte *buffer_data;
            MFCreateMemoryBuffer(img_size, &mbuffer);
            mbuffer->Lock(&buffer_data, NULL, NULL);
            mbuffer->GetCurrentLength(&length);
            memset(buffer_data, 0, img_size);
            mbuffer->Unlock();
            mbuffer->SetCurrentLength(img_size);
            sample->AddBuffer(mbuffer);*/

            MFFrameRateToAverageTimePerFrame(desktop_info.fps, 1, &sample_duration);
            sample->SetSampleDuration(sample_duration);

            // ERROR
            ThrowIfFailed(encoder->ProcessInput(stream_id, sample.Get(), 0), sample.Get(), "ProcessInput failed.");

I setup my media types like this:

void setup_media_types(IMFTransform *encoder) {
    IMFMediaType *output_type = nullptr;
    IMFMediaType *input_type = nullptr;

    ThrowIfFailed(MFCreateMediaType(&output_type), output_type, "Failed to create output type");
    ThrowIfFailed(MFCreateMediaType(&input_type), input_type, "Failed to create input type");

    /*
        List of all MF types: 
        https://learn.microsoft.com/en-us/windows/desktop/medfound/alphabetical-list-of-media-foundation-attributes
    */

    _com_error error = NULL;
    int stream_id = get_stream_id(encoder);

    error = output_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
    error = output_type->SetGUID(MF_MT_SUBTYPE, desktop_info.output_sub_type);
    error = output_type->SetUINT32(MF_MT_AVG_BITRATE, desktop_info.bitrate); 
    error = MFSetAttributeSize(output_type, MF_MT_FRAME_SIZE, desktop_info.width, desktop_info.height);
    error = MFSetAttributeRatio(output_type, MF_MT_FRAME_RATE, desktop_info.fps, 1);
    error = output_type->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive); // motion will be smoother, fewer artifacts
    error = output_type->SetUINT32(MF_MT_MPEG2_PROFILE, eAVEncH264VProfile_High);
    error = output_type->SetUINT32(MF_MT_MPEG2_LEVEL, eAVEncH264VLevel3_1);
    error = output_type->SetUINT32(CODECAPI_AVEncCommonRateControlMode, eAVEncCommonRateControlMode_CBR); // probably will change this

    ThrowIfFailed(encoder->SetOutputType(stream_id, output_type, 0), output_type, "Couldn't set output type");

    error = input_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
    error = input_type->SetGUID(MF_MT_SUBTYPE, desktop_info.input_sub_type);
    error = input_type->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
    error = MFSetAttributeSize(input_type, MF_MT_FRAME_SIZE, desktop_info.width, desktop_info.height);
    error = MFSetAttributeRatio(input_type, MF_MT_FRAME_RATE, desktop_info.fps, 1);
    error = MFSetAttributeRatio(input_type, MF_MT_PIXEL_ASPECT_RATIO, 1, 1);

    ThrowIfFailed(encoder->SetInputType(stream_id, input_type, 0), input_type, "Couldn't set input type");
}

My desktop_info struct is:

struct desktop_info {
    int fps = 30;
    int width = 2560;
    int height = 1440;
    uint32_t bitrate = 10 * 1000000; // 10Mb
    GUID input_sub_type = MFVideoFormat_ARGB32;
    GUID output_sub_type = MFVideoFormat_H264;
} desktop_info;

Output of my program prior to reaching ProcessInput:

Hello World!
Number of devices: 3
Device #0
Adapter: Intel(R) HD Graphics 630
Got some information about the device:
        \\.\DISPLAY2
        Attached to desktop : 1
Got some information about the device:
        \\.\DISPLAY1
        Attached to desktop : 1
Did not find another adapter. Index higher than the # of outputs.
Successfully duplicated output from IDXGIOutput1
Accumulated frames: 0
Created a 2D texture...
Number of encoders/processors available: 1
Encoder name: Intel« Quick Sync Video H.264 Encoder MFT
Making it Direct3D aware...
Setting up input/output media types...

If you're curious what my Locals were right before ProcessInput: http://prntscr.com/mx1i9t

Suhail Doshi
  • 716
  • 1
  • 10
  • 23
  • I don't think it's going to work without `ISample.SetSampleTime` call. Anyway even though `E_FAIL` is not descriptive enough, the problem is likely to be with your arguments and media sample instance in particular. – Roman R. Mar 13 '19 at 07:55
  • were you able to solve the problem? I'm also stuck with similar issue in intel hardware encoders. No issues with Nvidia hardware encoders & MS software MFTs though. – iamrameshkumar Nov 09 '19 at 19:14

2 Answers2

2

This may be an "unpopular" answer since it doesn't provide a solution for MFT specifically but after 8 months of working heavily on this stuff, I would highly recommend not using MFT and implementing encoders directly.

My solution was implementing an HW encoder like NVENC/QSV and you could fall back on a software encoder like x264 if the client doesn't have HW acceleration available.

The reason for this is that MFT is far more opaque and not well documented/supported by Microsoft. I think you'll find you want more control over the settings & parameter tuning of the encoder's as well wherein each encoder implementation is subtly different.

Suhail Doshi
  • 716
  • 1
  • 10
  • 23
  • For me, Media foundation works fine for Nvidia MFT. I face issues only with intel graphics. https://stackoverflow.com/questions/58779958/intel-graphics-hardware-h264-mft-processinput-call-fails-after-feeding-few-input – iamrameshkumar Nov 11 '19 at 06:16
1

We have seen this error coming from the Intel graphics driver. (The H.264 encoder MFT uses the Intel GPU to do the encode the video into H.264 format.)

In our case, I think the bug was triggered by configuring the encoder to a very high bit rate and then configuring to a low bit rate. In your sample code, it does not look like you are changing the bit rate, so I am not sure if it is the same bug.

Intel just released a new driver about two weeks ago, that is supposed to have the fix for the bug that we were seeing. So, you may want to give that new driver a try -- hopefully it will fix the problem that you are having.

The new driver is version 25.20.100.6519. You can get it from the Intel web site: https://downloadcenter.intel.com/download/28566/Intel-Graphics-Driver-for-Windows-10

If the new driver does not fix the problem, you could try running your program on a different PC that uses a NVidia or AMD graphics card, to see if the problem only happens on PCs that have Intel graphics.

  • I am not changing the bitrate anywhere & only explicitly setting it on the output type. – Suhail Doshi Mar 13 '19 at 06:59
  • I tried updating my drivers but I am still getting the same error. – Suhail Doshi Mar 15 '19 at 18:54
  • @SuhailDoshi were you able to solve the problem? I'm also stuck with similar issue in intel hardware encoders. No issues with Nvidia hardware encoders & MS software MFTs though. – iamrameshkumar Nov 09 '19 at 19:13
  • 1
    I highly recommend not using MFT because of how opaque it is. I've contacted engineers at Microsoft who can't even figure this out and it's their part of the codebase. A better solution is to implement the kind of encoder you want directly: whether that's QSV, NVENC, x264, etc. You will be happier and it'll faster. – Suhail Doshi Nov 10 '19 at 17:47