MFTransform encoder->ProcessInput returns E_FAIL

早过忘川 提交于 2019-12-08 08:22:09

问题


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://docs.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


回答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.




回答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.



来源:https://stackoverflow.com/questions/55131516/mftransform-encoder-processinput-returns-e-fail

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!