[vlc-devel] [PATCH] [WIP] codec: implementation of Media Foundation Transform video decoding

Denis Charmet typx at dinauz.org
Fri Apr 18 11:27:42 CEST 2014


Hi,

Le jeudi 17 avril 2014 à 05:21:58, Felix Abecassis a écrit :
> +vlc_module_begin()
> +    set_description(N_("Media Foundation Transform decoder"))
> +    add_shortcut("mft")
> +    set_capability("decoder", 0)
why 0?
> +    set_callbacks(Open, Close)
> +    set_category(CAT_INPUT)
> +    set_subcategory(SUBCAT_INPUT_VCODEC)
> +vlc_module_end()
> +
> +typedef struct
> +{
> +    HINSTANCE mfplat_dll;
> +    HRESULT (STDCALL *MFTEnumEx)(GUID guidCategory, UINT32 Flags,
> +                                 const MFT_REGISTER_TYPE_INFO *pInputType,
> +                                 const MFT_REGISTER_TYPE_INFO *pOutputType,
> +                                 IMFActivate ***pppMFTActivate, UINT32 *pcMFTActivate);
> +    HRESULT (STDCALL *MFCreateMediaType)(IMFMediaType **ppMFType);
> +    HRESULT (STDCALL *MFCreateSample)(IMFSample **ppIMFSample);
> +    HRESULT (STDCALL *MFCreateMemoryBuffer)(DWORD cbMaxLength, IMFMediaBuffer **ppBuffer);
> +    HRESULT (STDCALL *MFCreateAlignedMemoryBuffer)(DWORD cbMaxLength, DWORD fAlignmentFlags, IMFMediaBuffer **ppBuffer);
> +} MFHandle;
> +
> +struct decoder_sys_t
> +{
> +    MFHandle mf_handle;
> +
> +    IMFTransform *mft;
> +
> +    /* For asynchronous MFT */
> +    bool is_async;
> +    IMFMediaEventGenerator *event_generator;
> +    int pending_input_events;
> +    int pending_output_events;
> +
> +    /* Input stream */
> +    DWORD input_stream_id;
> +    IMFMediaType *input_type;
> +
> +    /* Output stream */
> +    DWORD output_stream_id;
> +    IMFSample *output_sample;
> +    IMFMediaType *output_type;
> +
> +    /* H264 only. */
> +    uint32_t nal_size;
> +};
> +
> +/* Possibly missing from mingw headers */
> +#ifndef MF_E_TRANSFORM_NEED_MORE_INPUT
> +# define MF_E_TRANSFORM_NEED_MORE_INPUT _HRESULT_TYPEDEF_(0xc00d6d72)
> +#endif
> +
> +#ifndef MF_E_TRANSFORM_STREAM_CHANGE
> +# define MF_E_TRANSFORM_STREAM_CHANGE _HRESULT_TYPEDEF_(0xc00d6d61)
> +#endif
> +
> +#ifndef MF_E_NO_EVENTS_AVAILABLE
> +# define MF_E_NO_EVENTS_AVAILABLE _HRESULT_TYPEDEF_(0xC00D3E80L)
> +#endif
> +
> +#ifndef MF_EVENT_FLAG_NO_WAIT
> +# define MF_EVENT_FLAG_NO_WAIT 0x00000001
> +#endif
> +
> +/*
> + * The MFTransformXXX values might not be defined in mingw headers,
> + * thus we use our own enum with the VLC prefix.
> + */
> +enum
> +{
> +    VLC_METransformUnknown = 600,
> +    VLC_METransformNeedInput,
> +    VLC_METransformHaveOutput,
> +    VLC_METransformDrainComplete,
> +    VLC_METransformMarker,
> +};
> +
> +typedef struct
> +{
> +    vlc_fourcc_t fourcc;
> +    const GUID   *guid;
> +} pair_format_guid;
> +
> +/*
> + * We need this table since the FOURCC used for GUID is not the same
> + * as the FOURCC used by VLC, for instance h264 vs H264.
> + */
> +static const pair_format_guid video_format_table[] =
> +{
> +    { VLC_CODEC_H264, &MFVideoFormat_H264 },
> +    { VLC_CODEC_MJPG, &MFVideoFormat_MJPG },
> +    { VLC_CODEC_WMV1, &MFVideoFormat_WMV1 },
> +    { VLC_CODEC_WMV2, &MFVideoFormat_WMV2 },
> +    { VLC_CODEC_WMV3, &MFVideoFormat_WMV3 },
> +    { VLC_CODEC_VC1,  &MFVideoFormat_WVC1 },
> +    { 0, 0 }
this is not really usefull since you can iterate with a:
i < sizeof(video_format_table)/sizeof(pair_format_guid)
but OK...
> +};
> +
> +static const GUID *FormatToGUID(const pair_format_guid table[], vlc_fourcc_t fourcc)
> +{
> +    for (int i = 0; table[i].fourcc; ++i)
> +        if (table[i].fourcc == fourcc)
> +            return table[i].guid;
> +
> +    return NULL;
> +}
> +
> +/*
> + * GUIDs were found using the mediasdk_sys_analyzer tool from Intel
> + * Media SDK 2014 for Clients.
> + */
> +DEFINE_GUID(INTEL_HW_H264_DECODER, 0x45E5CE07, 0x5AC7, 0x4509, 0x94, 0xE9, 0x62, 0xDB, 0x27, 0xCF, 0x8F, 0x96);
> +DEFINE_GUID(INTEL_HW_MJPEG_DECODER, 0x00C69F81, 0x0524, 0x48C0, 0xA3, 0x53, 0x4D, 0xD9, 0xD5, 0x4F, 0x9A, 0x6E);
> +DEFINE_GUID(INTEL_HW_VC1_DECODER, 0x059A5BAE, 0x5D7A, 0x4C5E, 0x8F, 0x7A, 0xBF, 0xD5, 0x7D, 0x1D, 0x6A, 0xAA);
> +DEFINE_GUID(INTEL_HW_MPEG2_DECODER, 0xCD5BA7FF, 0x9071, 0x40E9, 0xA4, 0x62, 0x8D, 0xC5, 0x15, 0x2B, 0x17, 0x76);
> +
> +/*
> + * Low latency mode for Windows 8. Without this option, the decoder
> + * will fill *all* its internal buffers before returning a
> + * frame. Because of this behavior, the decoder might return no frame
> + * for more than 500 ms, making it unusable for playback.
> + */
> +DEFINE_GUID(CODECAPI_AVLowLatencyMode, 0x9c27891a, 0xed7a, 0x40e1, 0x88, 0xe8, 0xb2, 0x27, 0x27, 0xa0, 0x24, 0xee);
> +
> +
> +/*
> + * Additional MFT decoders than might be available on the system but
> + * cannot be enumerated. Several entries are possible for each format
> + * but should be sorted by priority.
> + */
> +static const pair_format_guid extra_mft_table[] =
> +{
> +    { VLC_CODEC_H264, &INTEL_HW_H264_DECODER },
> +    { VLC_CODEC_MJPG, &INTEL_HW_MJPEG_DECODER },
> +    { VLC_CODEC_VC1,  &INTEL_HW_VC1_DECODER },
> +    { VLC_CODEC_MPGV, &INTEL_HW_MPEG2_DECODER },
> +    { VLC_CODEC_MP2V, &INTEL_HW_MPEG2_DECODER },
> +    { 0, 0 }
> +};
> +
> +static int SetInputType(decoder_t *p_dec, DWORD stream_id, IMFMediaType **result)
> +{
> +    decoder_sys_t *p_sys = p_dec->p_sys;
> +    HRESULT hr;
> +
> +    if (!result)
> +        return VLC_EGENERIC;
It is your static function and you never call it with a NULL result, you
don't need that test.

> +    *result = NULL;
> +
> +    IMFMediaType *input_media_type = NULL;
> +    vlc_fourcc_t input_type = vlc_fourcc_GetCodec(p_dec->fmt_in.i_cat, p_dec->fmt_in.i_codec);
> +
> +    /* Search a suitable input type for the MFT. */
> +    int input_type_index = 0;
> +    for (int i = 0; true; ++i)
> +    {
> +        hr = IMFTransform_GetInputAvailableType(p_sys->mft, stream_id, i, &input_media_type);
> +        if (hr == MF_E_NO_MORE_TYPES)
> +            break;
> +        else if (hr == MF_E_TRANSFORM_TYPE_NOT_SET)
> +        {
> +            /* The output type must be set before setting the input type for this MFT. */
> +            return VLC_SUCCESS;
> +        }
> +        else if (FAILED(hr))
> +            goto error;
> +
> +        GUID subtype;
> +        hr = IMFMediaType_GetGUID(input_media_type, &MF_MT_SUBTYPE, &subtype);
> +        if (FAILED(hr))
> +            goto error;
> +        vlc_fourcc_t mft_type = vlc_fourcc_GetCodec(p_dec->fmt_in.i_cat, subtype.Data1);
> +        if (input_type == mft_type)
> +            input_type_index = i;
Does this case happen only once or it doesn't matter if input_type_index
is overriden?
> +
> +        IMFMediaType_Release(input_media_type);
> +        input_media_type = NULL;
> +    }
> +
> +    hr = IMFTransform_GetInputAvailableType(p_sys->mft, stream_id, input_type_index, &input_media_type);
> +    if (FAILED(hr))
> +        goto error;
> +
> +    UINT64 width = p_dec->fmt_in.video.i_width;
> +    UINT64 height = p_dec->fmt_in.video.i_height;
> +    UINT64 frame_size = (width << 32) | height;
> +    hr = IMFMediaType_SetUINT64(input_media_type, &MF_MT_FRAME_SIZE, frame_size);
> +    if (FAILED(hr))
> +        goto error;
> +
> +    hr = IMFTransform_SetInputType(p_sys->mft, stream_id, input_media_type, 0);
> +    if (FAILED(hr))
> +        goto error;
> +
> +    *result = input_media_type;
> +
> +    return VLC_SUCCESS;
> +
> +error:
> +    msg_Err(p_dec, "Error in SetInputType()");
> +    if (input_media_type)
> +        IMFMediaType_Release(input_media_type);
> +    return VLC_EGENERIC;
> +}
> +
> +static int SetOutputType(decoder_t *p_dec, DWORD stream_id, IMFMediaType **result)
> +{
> +    decoder_sys_t *p_sys = p_dec->p_sys;
> +    HRESULT hr;
> +
> +    if (!result)
> +        return VLC_EGENERIC;
same remark
> +    *result = NULL;
> +
> +    IMFMediaType *output_media_type = NULL;
> +
> +    /*
> +     * Enumerate available output types. The list is ordered by
> +     * preference thus we will use the first one unless YV12/I420 is
> +     * available.
> +     */
> +    int output_type_index = 0;
> +    for (int i = 0; true; ++i)
> +    {
> +        hr = IMFTransform_GetOutputAvailableType(p_sys->mft, stream_id, i, &output_media_type);
> +        if (hr == MF_E_NO_MORE_TYPES)
> +            break;
> +        else if (hr == MF_E_TRANSFORM_TYPE_NOT_SET)
> +        {
> +            /* The input type must be set before setting the output type for this MFT. */
> +            return VLC_SUCCESS;
> +        }
> +        else if (FAILED(hr))
> +            goto error;
> +
> +        GUID subtype;
> +        hr = IMFMediaType_GetGUID(output_media_type, &MF_MT_SUBTYPE, &subtype);
> +        if (FAILED(hr))
> +            goto error;
> +
> +        if (IsEqualGUID(&subtype, &MFVideoFormat_YV12) || IsEqualGUID(&subtype, &MFVideoFormat_I420))
> +            output_type_index = i;
> +
> +        IMFMediaType_Release(output_media_type);
> +        output_media_type = NULL;
> +    }
> +
> +    hr = IMFTransform_GetOutputAvailableType(p_sys->mft, stream_id, output_type_index, &output_media_type);
> +    if (FAILED(hr))
> +        goto error;
> +
> +    hr = IMFTransform_SetOutputType(p_sys->mft, stream_id, output_media_type, 0);
> +    if (FAILED(hr))
> +        goto error;
> +
> +    GUID subtype;
> +    hr = IMFMediaType_GetGUID(output_media_type, &MF_MT_SUBTYPE, &subtype);
> +    if (FAILED(hr))
> +        goto error;
> +    p_dec->fmt_out.i_codec = vlc_fourcc_GetCodec(p_dec->fmt_in.i_cat, subtype.Data1);
> +
> +    *result = output_media_type;
> +
> +    return VLC_SUCCESS;
> +
> +error:
> +    msg_Err(p_dec, "Error in SetOutputType()");
> +    if (output_media_type)
> +        IMFMediaType_Release(output_media_type);
> +    return VLC_EGENERIC;
> +}
> +
> +static int AllocateInputSample(decoder_t *p_dec, DWORD stream_id, IMFSample** result, DWORD size)
> +{
> +    decoder_sys_t *p_sys = p_dec->p_sys;
> +    MFHandle *mf = &p_sys->mf_handle;
> +    HRESULT hr;
> +
> +    if (!result)
> +        return VLC_EGENERIC;
> +    *result = NULL;
> +
> +    IMFSample *input_sample = NULL;
> +
> +    MFT_INPUT_STREAM_INFO input_info;
> +    hr = IMFTransform_GetInputStreamInfo(p_sys->mft, stream_id, &input_info);
> +    if (FAILED(hr))
> +        goto error;
> +
> +    hr = mf->MFCreateSample(&input_sample);
> +    if (FAILED(hr))
> +        goto error;
> +
> +    IMFMediaBuffer *input_media_buffer = NULL;
> +    DWORD allocation_size = __MAX(input_info.cbSize, size);
> +    hr = mf->MFCreateMemoryBuffer(allocation_size, &input_media_buffer);
> +    if (FAILED(hr))
> +        goto error;
> +
> +    hr = IMFSample_AddBuffer(input_sample, input_media_buffer);
> +    if (FAILED(hr))
leak input_media_buffer?
> +        goto error;
> +
> +    *result = input_sample;
> +
> +    return VLC_SUCCESS;
> +
> +error:
> +    msg_Err(p_dec, "Error in AllocateInputSample()");
> +    if (input_sample)
> +        IMFSample_Release(input_sample);
> +    return VLC_EGENERIC;
> +}
> +
> +static int AllocateOutputSample(decoder_t *p_dec, DWORD stream_id, IMFSample **result)
> +{
> +    decoder_sys_t *p_sys = p_dec->p_sys;
> +    MFHandle *mf = &p_sys->mf_handle;
> +    HRESULT hr;
> +
> +    if (!result)
> +        return VLC_EGENERIC;
> +    *result = NULL;
> +
> +    IMFSample *output_sample = NULL;
> +
> +    MFT_OUTPUT_STREAM_INFO output_info;
> +    hr = IMFTransform_GetOutputStreamInfo(p_sys->mft, stream_id, &output_info);
> +    if (FAILED(hr))
> +        goto error;
> +
> +    if (output_info.dwFlags & (MFT_OUTPUT_STREAM_PROVIDES_SAMPLES | MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES))
> +    {
> +        /* The MFT will provide an allocated sample. */
> +        return VLC_SUCCESS;
> +    }
> +    DWORD expected_flags = MFT_OUTPUT_STREAM_WHOLE_SAMPLES
> +        | MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER
> +        | MFT_OUTPUT_STREAM_FIXED_SAMPLE_SIZE;
> +    if ((output_info.dwFlags & expected_flags) != expected_flags)
> +        goto error;
> +
> +    hr = mf->MFCreateSample(&output_sample);
> +    if (FAILED(hr))
> +        goto error;
> +
> +    IMFMediaBuffer *output_media_buffer = NULL;
> +    DWORD allocation_size = output_info.cbSize;
> +    DWORD alignment = output_info.cbAlignment;
> +    if (alignment > 0)
> +        hr = mf->MFCreateAlignedMemoryBuffer(allocation_size, alignment - 1, &output_media_buffer);
> +    else
> +        hr = mf->MFCreateMemoryBuffer(allocation_size, &output_media_buffer);
> +    if (FAILED(hr))
> +        goto error;
> +
> +    hr = IMFSample_AddBuffer(output_sample, output_media_buffer);
> +    if (FAILED(hr))
leak?
> +        goto error;
> +
> +    *result = output_sample;
> +
> +    return VLC_SUCCESS;
> +
> +error:
> +    msg_Err(p_dec, "Error in AllocateOutputSample()");
> +    if (output_sample)
> +        IMFSample_Release(output_sample);
> +    return VLC_EGENERIC;
> +}
> +
> +static int ProcessInputStream(decoder_t *p_dec, DWORD stream_id, block_t *p_block)
> +{
> +    decoder_sys_t *p_sys = p_dec->p_sys;
> +    HRESULT hr;
> +    IMFSample *input_sample = NULL;
> +
> +    if (AllocateInputSample(p_dec, stream_id, &input_sample, p_block->i_buffer))
> +        goto error;
> +
> +    IMFMediaBuffer *input_media_buffer = NULL;
> +    hr = IMFSample_GetBufferByIndex(input_sample, stream_id, &input_media_buffer);
> +    if (FAILED(hr))
> +        goto error;
> +
> +    BYTE *buffer_start;
> +    hr = IMFMediaBuffer_Lock(input_media_buffer, &buffer_start, NULL, NULL);
> +    if (FAILED(hr))
> +        goto error;
> +
> +    memcpy(buffer_start, p_block->p_buffer, p_block->i_buffer);
> +
> +    if (p_dec->fmt_in.i_codec == VLC_CODEC_H264)
> +    {
> +        /* in-place NAL to annex B conversion. */
> +        struct H264ConvertState convert_state = { 0, 0 };
> +        convert_h264_to_annexb(buffer_start, p_block->i_buffer, p_sys->nal_size, &convert_state);
> +    }
> +
> +    hr = IMFMediaBuffer_Unlock(input_media_buffer);
> +    if (FAILED(hr))
> +        goto error;
> +
> +    hr = IMFMediaBuffer_SetCurrentLength(input_media_buffer, p_block->i_buffer);
> +    if (FAILED(hr))
> +        goto error;
> +
> +    LONGLONG ts = p_block->i_pts;
> +    if (!ts && p_block->i_dts)
> +        ts = p_block->i_dts;
> +
> +    /* Convert from microseconds to 100 nanoseconds unit. */
> +    hr = IMFSample_SetSampleTime(input_sample, ts * 10);
Ideally you should use CLOCK_FREQ since it may change one day :)
> +    if (FAILED(hr))
> +        goto error;
> +
> +    hr = IMFTransform_ProcessInput(p_sys->mft, stream_id, input_sample, 0);
> +    if (FAILED(hr))
> +        goto error;
> +
> +    IMFSample_Release(input_sample);
> +
> +    return VLC_SUCCESS;
> +
> +error:
> +    msg_Err(p_dec, "Error in ProcessInputStream()");
> +    if (input_sample)
> +        IMFSample_Release(input_sample);
> +    return VLC_EGENERIC;
> +}
> +
> +/* Copy a packed buffer (no padding) to a picture_t */
> +static void CopyPackedBufferToPicture(picture_t *p_pic, const uint8_t *p_src)
> +{
> +    for (int i = 0; i < p_pic->i_planes; ++i)
> +    {
> +        uint8_t *p_dst = p_pic->p[i].p_pixels;
> +
> +        if (p_pic->p[i].i_visible_pitch == p_pic->p[i].i_pitch)
> +        {
> +            /* Plane is packed, only one memcpy is needed. */
> +            uint32_t plane_size = p_pic->p[i].i_pitch * p_pic->p[i].i_visible_lines;
> +            memcpy(p_dst, p_src, plane_size);
> +            p_src += plane_size;
> +            continue;
> +        }
> +
> +        for (int i_line = 0; i_line < p_pic->p[i].i_visible_lines; i_line++)
> +        {
> +            memcpy(p_dst, p_src, p_pic->p[i].i_visible_pitch);
> +            p_src += p_pic->p[i].i_visible_pitch;
> +            p_dst += p_pic->p[i].i_pitch;
> +        }
> +    }
> +}
> +
> +static int ProcessOutputStream(decoder_t *p_dec, DWORD stream_id, picture_t **result)
> +{
> +    decoder_sys_t *p_sys = p_dec->p_sys;
> +    HRESULT hr;
> +    picture_t *picture = NULL;
> +
> +    if (!result)
> +        return VLC_EGENERIC;
> +    *result = NULL;
> +
> +    DWORD output_status = 0;
> +    MFT_OUTPUT_DATA_BUFFER output_buffer = { stream_id, p_sys->output_sample, 0, NULL };
> +    hr = IMFTransform_ProcessOutput(p_sys->mft, 0, 1, &output_buffer, &output_status);
> +    /* Use the returned sample since it can be provided by the MFT. */
> +    IMFSample *output_sample = output_buffer.pSample;
> +
> +    if (hr == S_OK)
> +    {
> +        if (!output_sample)
> +            return VLC_SUCCESS;
> +        picture = decoder_NewPicture(p_dec);
> +        if (!picture)
> +            return VLC_SUCCESS;
> +
> +        UINT32 interlaced = false;
> +        hr = IMFSample_GetUINT32(output_sample, &MFSampleExtension_Interlaced, &interlaced);
> +        picture->b_progressive = !interlaced;
> +
> +        IMFMediaBuffer *output_media_buffer = NULL;
> +        hr = IMFSample_GetBufferByIndex(output_sample, 0, &output_media_buffer);
> +
> +        BYTE *buffer_start;
> +        hr = IMFMediaBuffer_Lock(output_media_buffer, &buffer_start, NULL, NULL);
> +        if (FAILED(hr))
> +            goto error;
> +
> +        CopyPackedBufferToPicture(picture, buffer_start);
> +
> +        LONGLONG sample_time;
> +        hr = IMFSample_GetSampleTime(output_sample, &sample_time);
> +        if (FAILED(hr))
unlock buffer?
> +            goto error;
> +        /* Convert from 100 nanoseconds unit to microseconds. */
> +        picture->date = sample_time / 10;
> +
> +        hr = IMFMediaBuffer_Unlock(output_media_buffer);
> +        if (FAILED(hr))
> +            goto error;
> +
> +        if (p_sys->output_sample)
> +        {
> +            /* Sample is not provided by the MFT: clear its content. */
> +            hr = IMFMediaBuffer_SetCurrentLength(output_media_buffer, 0);
> +            if (FAILED(hr))
> +                goto error;
> +        }
> +        else
> +        {
> +            /* Sample is provided by the MFT: decrease refcount. */
> +            IMFSample_Release(output_sample);
> +        }
> +    }
> +    else if (hr == MF_E_TRANSFORM_STREAM_CHANGE || hr == MF_E_TRANSFORM_TYPE_NOT_SET)
> +    {
> +        if (p_sys->output_type)
> +            IMFMediaType_Release(p_sys->output_type);
> +        if (SetOutputType(p_dec, p_sys->output_stream_id, &p_sys->output_type))
> +            goto error;
> +    }
> +    else if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT)
> +    {
> +        return VLC_SUCCESS;
> +    }
> +    else /* An error not listed above occurred */
> +    {
> +        msg_Err(p_dec, "Unexpected error in IMFTransform::ProcessOutput");
> +        goto error;
> +    }
> +
> +    *result = picture;
> +
> +    return VLC_SUCCESS;
> +
> +error:
> +    msg_Err(p_dec, "Error in ProcessOutputStream()");
> +    if (picture)
> +        picture_Release(picture);
> +    return VLC_EGENERIC;
> +}
> +
> +static picture_t *DecodeSync(decoder_t *p_dec, block_t **pp_block)
> +{
> +    decoder_sys_t *p_sys = p_dec->p_sys;
> +    HRESULT hr;
> +
> +    if (!pp_block || !*pp_block)
> +        return NULL;
> +
> +    block_t *p_block = *pp_block;
> +    if (p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY | BLOCK_FLAG_CORRUPTED))
> +    {
> +        block_Release(p_block);
> +
> +        hr = IMFTransform_ProcessMessage(p_sys->mft, MFT_MESSAGE_COMMAND_FLUSH, 0);
> +        if (FAILED(hr))
> +            msg_Err(p_dec, "COMMAND_FLUSH: error.");
> +
> +        return NULL;
> +    }
> +
> +    /* Drain the output stream before sending the input packet. */
> +    picture_t *picture = NULL;
> +    if (ProcessOutputStream(p_dec, p_sys->output_stream_id, &picture))
> +        goto error;
> +    if (picture)
> +        return picture;
why returning the picture here and not after ProcessInputStream?
> +
> +    if (ProcessInputStream(p_dec, p_sys->input_stream_id, p_block))
> +        goto error;
> +
> +    block_Release(p_block);
> +    *pp_block = NULL;
> +
> +    return NULL;
> +
> +error:
> +    msg_Err(p_dec, "Error in DecodeSync()");
> +    if (p_block)
> +        block_Release(p_block);
> +    return NULL;
> +}
> +
> +static HRESULT DequeueMediaEvent(decoder_t *p_dec)
> +{
> +    decoder_sys_t *p_sys = p_dec->p_sys;
> +    HRESULT hr;
> +
> +    IMFMediaEvent *event = NULL;
> +    hr = IMFMediaEventGenerator_GetEvent(p_sys->event_generator, MF_EVENT_FLAG_NO_WAIT, &event);
> +    if (FAILED(hr))
> +        return hr;
> +    MediaEventType event_type;
> +    hr = IMFMediaEvent_GetType(event, &event_type);
> +    IMFMediaEvent_Release(event);
> +    if (FAILED(hr))
> +        return hr;
> +
> +    if (event_type == VLC_METransformNeedInput)
> +        p_sys->pending_input_events += 1;
> +    else if (event_type == VLC_METransformHaveOutput)
> +        p_sys->pending_output_events += 1;
> +    else
> +        msg_Err(p_dec, "UNKNOWN EVENT");
> +
> +    return S_OK;
> +}
> +
> +static picture_t *DecodeAsync(decoder_t *p_dec, block_t **pp_block)
> +{
> +    decoder_sys_t *p_sys = p_dec->p_sys;
> +    HRESULT hr;
> +
> +    if (!pp_block || !*pp_block)
> +        return NULL;
> +
> +    block_t *p_block = *pp_block;
> +    if (p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY | BLOCK_FLAG_CORRUPTED))
> +    {
> +        block_Release(p_block);
> +
> +        /*
> +         * The following code should be used according to the MSDN
> +         * documentation, however it currently causes the Intel MFT
> +         * decoder to freeze after a seek.
> +        */
> +#if 0
> +        p_sys->pending_input_events = p_sys->pending_output_events = 0;
> +
> +        hr = IMFTransform_ProcessMessage(p_sys->mft, MFT_MESSAGE_COMMAND_FLUSH, 0);
> +        if (FAILED(hr))
> +            msg_Err(p_dec, "COMMAND_FLUSH: error.");
> +
> +         hr = IMFTransform_ProcessMessage(p_sys->mft, MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0);
> +        if (FAILED(hr))
> +            msg_Err(p_dec, "NOTIFY_START_OF_STREAM: error.");
> +#endif
Is it dead code or for future use?
> +
> +        return NULL;
> +    }
> +
> +    /* Dequeue all pending media events. */
> +    while ((hr = DequeueMediaEvent(p_dec)) == S_OK)
> +        continue;
useless continue

> +    if (hr != MF_E_NO_EVENTS_AVAILABLE && FAILED(hr))
> +        goto error;
> +
> +    /* Drain the output stream of the MFT before sending the input packet. */
> +    if (p_sys->pending_output_events > 0)
> +    {
> +        p_sys->pending_output_events -= 1;
> +        picture_t *picture = NULL;
> +        if (ProcessOutputStream(p_dec, p_sys->output_stream_id, &picture))
> +            goto error;
> +        return picture;
> +    }
> +
> +    /* Poll the MFT and return decoded frames until the input stream is ready. */
> +    while (p_sys->pending_input_events == 0)
> +    {
> +        hr = DequeueMediaEvent(p_dec);
> +        if (hr == MF_E_NO_EVENTS_AVAILABLE)
> +        {
> +            /* Sleep for 1 ms to avoid excessive polling. */
> +            Sleep(1);
maybe DequeueMediaEvent should take a wait param, it would avoid the use
of a Sleep after a poll.
> +            continue;
> +        }
> +        if (FAILED(hr))
> +            goto error;
> +
> +        if (p_sys->pending_output_events > 0)
> +        {
> +            p_sys->pending_output_events -= 1;
> +            picture_t *picture = NULL;
> +            if (ProcessOutputStream(p_dec, p_sys->output_stream_id, &picture))
> +                goto error;
> +            return picture;
> +        }
> +    }
> +
> +    p_sys->pending_input_events -= 1;
> +    if (ProcessInputStream(p_dec, p_sys->input_stream_id, p_block))
> +        goto error;
> +
> +    block_Release(p_block);
> +    *pp_block = NULL;
> +
> +    return NULL;
> +
> +error:
> +    msg_Err(p_dec, "Error in DecodeAsync()");
> +    block_Release(p_block);
> +    return NULL;
> +}
> +
> +static void DestroyMFT(decoder_t *p_dec);
> +
> +static int InitializeMFT(decoder_t *p_dec)
> +{
> +    decoder_sys_t *p_sys = p_dec->p_sys;
> +    HRESULT hr;
> +
> +    IMFAttributes *attributes;
> +    IMFTransform_GetAttributes(p_sys->mft, &attributes);
> +    UINT32 is_async = false;
> +    hr = IMFAttributes_GetUINT32(attributes, &MF_TRANSFORM_ASYNC, &is_async);
> +    if (hr != MF_E_ATTRIBUTENOTFOUND && FAILED(hr))
> +        goto error;
> +    p_sys->is_async = is_async;
> +    if (p_sys->is_async)
> +    {
> +        hr = IMFAttributes_SetUINT32(attributes, &MF_TRANSFORM_ASYNC_UNLOCK, true);
> +        if (FAILED(hr))
> +            goto error;
> +        hr = IMFTransform_QueryInterface(p_sys->mft, &IID_IMFMediaEventGenerator, (void**)&p_sys->event_generator);
> +        if (FAILED(hr))
> +            goto error;
> +    }
> +
> +    DWORD input_streams_count;
> +    DWORD output_streams_count;
> +    hr = IMFTransform_GetStreamCount(p_sys->mft, &input_streams_count, &output_streams_count);
> +    if (FAILED(hr))
> +        goto error;
> +    if (input_streams_count != 1 || output_streams_count != 1)
> +    {
> +        msg_Err(p_dec, "MFT decoder should have 1 input stream and 1 output stream.");
> +        goto error;
> +    }
> +
> +    hr = IMFTransform_GetStreamIDs(p_sys->mft, 1, &p_sys->input_stream_id, 1, &p_sys->output_stream_id);
> +    if (hr == E_NOTIMPL)
> +    {
> +        /*
> +         * This is not an error, it happens if:
> +         * - there is a fixed number of streams.
> +         * AND
> +         * - streams are numbered consecutively from O to N-1.
> +         */
> +        p_sys->input_stream_id = 0;
> +        p_sys->output_stream_id = 0;
> +    }
> +    else if (FAILED(hr))
> +        goto error;
> +
> +    if (SetInputType(p_dec, p_sys->input_stream_id, &p_sys->input_type))
> +        goto error;
> +
> +    if (SetOutputType(p_dec, p_sys->output_stream_id, &p_sys->output_type))
> +        goto error;
> +
> +    /*
> +     * The input type was not set by the previous call to
> +     * SetInputType, try again after setting the output type.
> +     */
> +    if (!p_sys->input_type)
> +        if (SetInputType(p_dec, p_sys->input_stream_id, &p_sys->input_type) || !p_sys->input_type)
> +            goto error;
> +
> +    /* This call can be a no-op for some MFT decoders, but it can potentially reduce starting time. */
> +    hr = IMFTransform_ProcessMessage(p_sys->mft, MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, (ULONG_PTR)0);
> +    if (FAILED(hr))
> +        goto error;
> +
> +    /* This event is required for asynchronous MFTs, optional otherwise. */
> +    hr = IMFTransform_ProcessMessage(p_sys->mft, MFT_MESSAGE_NOTIFY_START_OF_STREAM, (ULONG_PTR)0);
> +    if (FAILED(hr))
> +        goto error;
> +
> +    if (p_dec->fmt_in.i_codec == VLC_CODEC_H264)
> +    {
> +        /* It's not an error if the following call fails. */
> +        IMFAttributes_SetUINT32(attributes, &CODECAPI_AVLowLatencyMode, true);
> +
> +        if (p_dec->fmt_in.i_extra)
> +        {
> +            int buf_size = p_dec->fmt_in.i_extra + 20;
> +            uint32_t size = p_dec->fmt_in.i_extra;
> +            uint8_t *buf = malloc(buf_size);
> +            if (((uint8_t*)p_dec->fmt_in.p_extra)[0] == 1)
> +            {
> +                convert_sps_pps(p_dec, p_dec->fmt_in.p_extra, p_dec->fmt_in.i_extra,
> +                                buf, buf_size,
> +                                &size, &p_sys->nal_size);
> +            }
> +            free(buf);
> +        }
> +    }
Doesn't the packetizer already outputs annexb frames?
> +    return VLC_SUCCESS;
> +
> +error:
> +    msg_Err(p_dec, "Error in InitializeMFT()");
> +    DestroyMFT(p_dec);
> +    return VLC_EGENERIC;
> +}
> +
> +static void DestroyMFT(decoder_t *p_dec)
> +{
> +    decoder_sys_t *p_sys = p_dec->p_sys;
> +
> +    if (p_sys->event_generator)
> +        IMFMediaEventGenerator_Release(p_sys->event_generator);
> +    if (p_sys->input_type)
> +        IMFMediaType_Release(p_sys->input_type);
> +    if (p_sys->output_sample)
> +        IMFSample_Release(p_sys->output_sample);
> +    if (p_sys->output_type)
> +        IMFMediaType_Release(p_sys->output_type);
> +    if (p_sys->mft)
> +        IMFTransform_Release(p_sys->mft);
> +
> +    p_sys->event_generator = NULL;
> +    p_sys->input_type = NULL;
> +    p_sys->output_sample = NULL;
> +    p_sys->output_type = NULL;
> +    p_sys->mft = NULL;
> +}
> +
> +static int FindMFT(decoder_t *p_dec)
> +{
> +    decoder_sys_t *p_sys = p_dec->p_sys;
> +    MFHandle *mf = &p_sys->mf_handle;
> +    HRESULT hr;
> +
> +    /*
> +     * Try to initialize a MFT decoder using the list of extra MFT
> +     * (MFTs that are not available through MFTEnumEx)
> +     */
> +    for (int i = 0; extra_mft_table[i].fourcc; ++i)
> +    {
> +        if (extra_mft_table[i].fourcc == p_dec->fmt_in.i_codec)
> +        {
> +            const GUID *guid = extra_mft_table[i].guid;
> +            hr = CoCreateInstance(guid, NULL, CLSCTX_INPROC, &IID_IMFTransform, (void**)&p_sys->mft);
> +            if (FAILED(hr))
> +                continue;
> +            if (InitializeMFT(p_dec) == VLC_SUCCESS)
> +                return VLC_SUCCESS;
> +
> +            IMFTransform_Release(p_sys->mft);
> +            p_sys->mft = NULL;
> +        }
> +    }
> +
> +    /* Try to create a MFT using MFTEnumEx. */
> +    GUID category = MFT_CATEGORY_VIDEO_DECODER;
> +    UINT32 flags = MFT_ENUM_FLAG_SORTANDFILTER | MFT_ENUM_FLAG_LOCALMFT
> +                 | MFT_ENUM_FLAG_SYNCMFT | MFT_ENUM_FLAG_ASYNCMFT
> +                 | MFT_ENUM_FLAG_HARDWARE;
> +    GUID major_type = MFMediaType_Video;
> +    const GUID *subtype = FormatToGUID(video_format_table, p_dec->fmt_in.i_codec);
> +    if (!subtype)
> +    {
> +        msg_Err(p_dec, "FOURCC not supported.");
> +        return VLC_EGENERIC;
> +    }
> +    MFT_REGISTER_TYPE_INFO input_type = { major_type, *subtype };
> +    IMFActivate **activate_objects = NULL;
> +    UINT32 activate_objects_count = 0;
> +    hr = mf->MFTEnumEx(category, flags, &input_type, NULL, &activate_objects, &activate_objects_count);
> +    if (FAILED(hr))
> +        return VLC_EGENERIC;
> +
> +    msg_Dbg(p_dec, "Found %d available MFT module(s)", activate_objects_count);
> +    if (activate_objects_count == 0)
> +        return VLC_EGENERIC;
> +
> +    for (UINT32 i = 0; i < activate_objects_count; ++i)
> +    {
> +        hr = IMFActivate_ActivateObject(activate_objects[i], &IID_IMFTransform, (void**)&p_sys->mft);
> +        IMFActivate_Release(activate_objects[i]);
> +        if (FAILED(hr))
> +            continue;
> +
> +        if (InitializeMFT(p_dec) == VLC_SUCCESS)
> +        {
> +            CoTaskMemFree(activate_objects);
> +            return VLC_SUCCESS;
> +        }
> +    }
> +    CoTaskMemFree(activate_objects);
> +
> +    return VLC_EGENERIC;
> +}
> +
> +static int LoadMFTLibrary(MFHandle *mf)
> +{
> +    mf->mfplat_dll = LoadLibrary(TEXT("mfplat.dll"));
> +    if (!mf->mfplat_dll)
> +        return VLC_EGENERIC;
> +
> +    mf->MFTEnumEx = (void*)GetProcAddress(mf->mfplat_dll, "MFTEnumEx");
> +    mf->MFCreateMediaType = (void*)GetProcAddress(mf->mfplat_dll, "MFCreateMediaType");
> +    mf->MFCreateSample = (void*)GetProcAddress(mf->mfplat_dll, "MFCreateSample");
> +    mf->MFCreateMemoryBuffer = (void*)GetProcAddress(mf->mfplat_dll, "MFCreateMemoryBuffer");
> +    mf->MFCreateAlignedMemoryBuffer = (void*)GetProcAddress(mf->mfplat_dll, "MFCreateAlignedMemoryBuffer");
> +    if (!mf->MFTEnumEx || !mf->MFCreateMediaType || !mf->MFCreateSample
> +        || !mf->MFCreateMemoryBuffer || !mf->MFCreateAlignedMemoryBuffer)
> +        return VLC_EGENERIC;
> +
> +    return VLC_SUCCESS;
> +}
> +
> +int Open(vlc_object_t *p_this)
> +{
> +    decoder_t *p_dec = (decoder_t *)p_this;
> +    decoder_sys_t *p_sys;
> +
> +    /* Audio is not yet supported. */
> +    if (p_dec->fmt_in.i_cat != VIDEO_ES)
> +        return VLC_EGENERIC;
> +
> +    p_sys = p_dec->p_sys = calloc(1, sizeof(*p_sys));
> +    if (!p_sys)
> +        return VLC_ENOMEM;
> +
> +    if (LoadMFTLibrary(&p_sys->mf_handle))
> +    {
> +        msg_Err(p_dec, "Failed to load MFT library.");
> +        goto error;
> +    }
> +
> +    if (FindMFT(p_dec))
> +    {
> +        msg_Err(p_dec, "Could not find suitable MFT decoder");
> +        goto error;
> +    }
> +
> +    /* Only one output sample is needed, we can allocate one and reuse it. */
> +    if (AllocateOutputSample(p_dec, 0, &p_sys->output_sample))
> +    {
> +        msg_Err(p_dec, "Failed to allocated output sample.");
> +        goto error;
> +    }
> +
> +    if (p_sys->is_async)
> +        p_dec->pf_decode_video = DecodeAsync;
> +    else
> +        p_dec->pf_decode_video = DecodeSync;
> +
> +    p_dec->fmt_out.i_cat = p_dec->fmt_in.i_cat;
> +    p_dec->fmt_out.video = p_dec->fmt_in.video;
> +    p_dec->fmt_out.audio = p_dec->fmt_in.audio;
> +    p_dec->b_need_packetized = true;
> +
> +    return VLC_SUCCESS;
> +
> +error:
> +    Close(p_this);
> +    return VLC_EGENERIC;
> +}
> +
> +void Close(vlc_object_t *p_this)
> +{
> +    decoder_t *p_dec = (decoder_t *)p_this;
> +    decoder_sys_t *p_sys = p_dec->p_sys;
> +    MFHandle *mf = &p_sys->mf_handle;
> +
> +    DestroyMFT(p_dec);
> +
> +    if (mf->mfplat_dll)
> +        FreeLibrary(mf->mfplat_dll);
> +
> +    free(p_sys);
> +}
> -- 
> 1.7.10.4
> 
> _______________________________________________
> vlc-devel mailing list
> To unsubscribe or modify your subscription options:
> https://mailman.videolan.org/listinfo/vlc-devel

Regards,
-- 
Denis Charmet - TypX
Le mauvais esprit est un art de vivre



More information about the vlc-devel mailing list