[PATCH v2 4/5] winegstreamer: Implement IMFMediaSource::Start.

Zebediah Figura zfigura at codeweavers.com
Tue Oct 6 21:58:30 CDT 2020


On 10/6/20 10:59 AM, Derek Lesho wrote:
> Signed-off-by: Derek Lesho <dlesho at codeweavers.com>
> ---
> v2:
> - Squashed prior commit for IMFMediaType conversion in.
> - Replaced usage of gst_bus_poll with better (not perfect) alternative.
> - Addressed comments.
> ---
>  dlls/mfplat/tests/mfplat.c        |   8 +-
>  dlls/winegstreamer/gst_private.h  |   1 +
>  dlls/winegstreamer/media_source.c | 316 +++++++++++++++++++++++++++++-
>  dlls/winegstreamer/mfplat.c       | 149 ++++++++++++++
>  4 files changed, 466 insertions(+), 8 deletions(-)
> 
> diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c
> index 15d5bcba3d6..bffce2bc114 100644
> --- a/dlls/mfplat/tests/mfplat.c
> +++ b/dlls/mfplat/tests/mfplat.c
> @@ -605,10 +605,7 @@ todo_wine
>  
>      var.vt = VT_EMPTY;
>      hr = IMFMediaSource_Start(mediasource, descriptor, &GUID_NULL, &var);
> -todo_wine
>      ok(hr == S_OK, "Failed to start media source, hr %#x.\n", hr);
> -    if (FAILED(hr))
> -        goto skip_source_tests;
>  
>      get_event((IMFMediaEventGenerator *)mediasource, MENewStream, &var);
>      ok(var.vt == VT_UNKNOWN, "Unexpected value type %u from MENewStream event.\n", var.vt);
> @@ -626,10 +623,13 @@ todo_wine
>          hr = IMFMediaStream_RequestSample(video_stream, NULL);
>          if (i == sample_count)
>              break;
> +todo_wine
>          ok(hr == S_OK, "Failed to request sample %u, hr %#x.\n", i + 1, hr);
>          if (hr != S_OK)
>              break;
>      }
> +    if (FAILED(hr))
> +        goto skip_source_tests;
>  
>      for (i = 0; i < sample_count; ++i)
>      {
> @@ -667,11 +667,11 @@ todo_wine
>  
>      hr = IMFMediaStream_RequestSample(video_stream, NULL);
>      ok(hr == MF_E_END_OF_STREAM, "Unexpected hr %#x.\n", hr);
> -    IMFMediaStream_Release(video_stream);
>  
>      get_event((IMFMediaEventGenerator *)mediasource, MEEndOfPresentation, NULL);
>  
>  skip_source_tests:
> +    IMFMediaStream_Release(video_stream);
>      IMFMediaTypeHandler_Release(handler);
>      IMFPresentationDescriptor_Release(descriptor);
>  
> diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h
> index 60b38a48f5a..07556802a51 100644
> --- a/dlls/winegstreamer/gst_private.h
> +++ b/dlls/winegstreamer/gst_private.h
> @@ -57,6 +57,7 @@ extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj)
>  
>  HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj) DECLSPEC_HIDDEN;
>  IMFMediaType *mf_media_type_from_caps(const GstCaps *caps) DECLSPEC_HIDDEN;
> +GstCaps *caps_from_mf_media_type(IMFMediaType *type) DECLSPEC_HIDDEN;
>  
>  HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj) DECLSPEC_HIDDEN;
>  
> diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c
> index c70de184f0b..5eb4465fea6 100644
> --- a/dlls/winegstreamer/media_source.c
> +++ b/dlls/winegstreamer/media_source.c
> @@ -55,14 +55,39 @@ struct media_stream
>      {
>          STREAM_INACTIVE,
>          STREAM_SHUTDOWN,
> +        STREAM_RUNNING,
>      } state;
>      DWORD stream_id;
> +    BOOL eos;
> +};
> +
> +enum source_async_op
> +{
> +    SOURCE_ASYNC_START,
> +};
> +
> +struct source_async_command
> +{
> +    IUnknown IUnknown_iface;
> +    LONG refcount;
> +    enum source_async_op op;
> +    union
> +    {
> +        struct
> +        {
> +            IMFPresentationDescriptor *descriptor;
> +            GUID format;
> +            PROPVARIANT position;
> +        } start;
> +    } u;
>  };
>  
>  struct media_source
>  {
>      IMFMediaSource IMFMediaSource_iface;
> +    IMFAsyncCallback async_commands_callback;
>      LONG ref;
> +    DWORD async_commands_queue;
>      IMFMediaEventQueue *event_queue;
>      IMFByteStream *byte_stream;
>      struct media_stream **streams;
> @@ -76,6 +101,7 @@ struct media_source
>      {
>          SOURCE_OPENING,
>          SOURCE_STOPPED,
> +        SOURCE_RUNNING,
>          SOURCE_SHUTDOWN,
>      } state;
>      HANDLE no_more_pads_event;
> @@ -91,7 +117,266 @@ static inline struct media_source *impl_from_IMFMediaSource(IMFMediaSource *ifac
>      return CONTAINING_RECORD(iface, struct media_source, IMFMediaSource_iface);
>  }
>  
> -static GstFlowReturn bytestream_wrapper_pull(GstPad *pad, GstObject *parent, guint64 ofs, guint len,
> +static inline struct media_source *impl_from_async_commands_callback_IMFAsyncCallback(IMFAsyncCallback *iface)
> +{
> +    return CONTAINING_RECORD(iface, struct media_source, async_commands_callback);
> +}
> +
> +static inline struct source_async_command *impl_from_async_command_IUnknown(IUnknown *iface)
> +{
> +    return CONTAINING_RECORD(iface, struct source_async_command, IUnknown_iface);
> +}
> +
> +static HRESULT WINAPI source_async_command_QueryInterface(IUnknown *iface, REFIID riid, void **obj)
> +{
> +    if (IsEqualIID(riid, &IID_IUnknown))
> +    {
> +        *obj = iface;
> +        IUnknown_AddRef(iface);
> +        return S_OK;
> +    }
> +
> +    WARN("Unsupported interface %s.\n", debugstr_guid(riid));
> +    *obj = NULL;
> +    return E_NOINTERFACE;
> +}
> +
> +static ULONG WINAPI source_async_command_AddRef(IUnknown *iface)
> +{
> +    struct source_async_command *command = impl_from_async_command_IUnknown(iface);
> +    return InterlockedIncrement(&command->refcount);
> +}
> +
> +static ULONG WINAPI source_async_command_Release(IUnknown *iface)
> +{
> +    struct source_async_command *command = impl_from_async_command_IUnknown(iface);
> +    ULONG refcount = InterlockedDecrement(&command->refcount);
> +
> +    if (!refcount)
> +    {
> +        if (command->op == SOURCE_ASYNC_START)
> +            PropVariantClear(&command->u.start.position);
> +        heap_free(command);
> +    }
> +
> +    return refcount;
> +}
> +
> +static const IUnknownVtbl source_async_command_vtbl =
> +{
> +    source_async_command_QueryInterface,
> +    source_async_command_AddRef,
> +    source_async_command_Release,
> +};
> +
> +static HRESULT source_create_async_op(enum source_async_op op, struct source_async_command **ret)
> +{
> +    struct source_async_command *command;
> +
> +    if (!(command = heap_alloc_zero(sizeof(*command))))
> +        return E_OUTOFMEMORY;
> +
> +    command->IUnknown_iface.lpVtbl = &source_async_command_vtbl;
> +    command->op = op;
> +
> +    *ret = command;
> +
> +    return S_OK;
> +}
> +
> +static HRESULT WINAPI callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj)
> +{
> +    TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
> +
> +    if (IsEqualIID(riid, &IID_IMFAsyncCallback) ||
> +            IsEqualIID(riid, &IID_IUnknown))
> +    {
> +        *obj = iface;
> +        IMFAsyncCallback_AddRef(iface);
> +        return S_OK;
> +    }
> +
> +    WARN("Unsupported %s.\n", debugstr_guid(riid));
> +    *obj = NULL;
> +    return E_NOINTERFACE;
> +}
> +
> +static HRESULT WINAPI callback_GetParameters(IMFAsyncCallback *iface,
> +        DWORD *flags, DWORD *queue)
> +{
> +    return E_NOTIMPL;
> +}
> +
> +static ULONG WINAPI source_async_commands_callback_AddRef(IMFAsyncCallback *iface)
> +{
> +    struct media_source *source = impl_from_async_commands_callback_IMFAsyncCallback(iface);
> +    return IMFMediaSource_AddRef(&source->IMFMediaSource_iface);
> +}
> +
> +static ULONG WINAPI source_async_commands_callback_Release(IMFAsyncCallback *iface)
> +{
> +    struct media_source *source = impl_from_async_commands_callback_IMFAsyncCallback(iface);
> +    return IMFMediaSource_Release(&source->IMFMediaSource_iface);
> +}
> +
> +static IMFStreamDescriptor *stream_descriptor_from_id(IMFPresentationDescriptor *pres_desc, DWORD id, BOOL *selected)
> +{
> +    ULONG sd_count;
> +    IMFStreamDescriptor *ret;
> +    unsigned int i;
> +
> +    if (FAILED(IMFPresentationDescriptor_GetStreamDescriptorCount(pres_desc, &sd_count)))
> +        return NULL;
> +
> +    for (i = 0; i < sd_count; i++)
> +    {
> +        DWORD stream_id;
> +
> +        if (FAILED(IMFPresentationDescriptor_GetStreamDescriptorByIndex(pres_desc, i, selected, &ret)))
> +            return NULL;
> +
> +        if (SUCCEEDED(IMFStreamDescriptor_GetStreamIdentifier(ret, &stream_id)) && stream_id == id)
> +            return ret;
> +
> +        IMFStreamDescriptor_Release(ret);
> +    }
> +    return NULL;
> +}
> +
> +static void start_pipeline(struct media_source *source, struct source_async_command *command)
> +{
> +    PROPVARIANT *position = &command->u.start.position;
> +    BOOL seek_message = source->state != SOURCE_STOPPED && position->vt != VT_EMPTY;
> +    GstStateChangeReturn ret;
> +    unsigned int i;
> +
> +    gst_element_set_state(source->container, GST_STATE_PAUSED);
> +    ret = gst_element_get_state(source->container, NULL, NULL, -1);
> +    assert(ret == GST_STATE_CHANGE_SUCCESS);
> +
> +    /* seek to beginning on stop->play */
> +    if (source->state == SOURCE_STOPPED && position->vt == VT_EMPTY)
> +    {
> +        position->vt = VT_I8;
> +        position->u.hVal.QuadPart = 0;
> +    }
> +
> +    for (i = 0; i < source->stream_count; i++)
> +    {
> +        struct media_stream *stream;
> +        IMFStreamDescriptor *sd;
> +        IMFMediaTypeHandler *mth;
> +        IMFMediaType *current_mt;
> +        GstCaps *current_caps;
> +        GstCaps *prev_caps;
> +        DWORD stream_id;
> +        BOOL was_active;
> +        BOOL selected;
> +
> +        stream = source->streams[i];
> +
> +        IMFStreamDescriptor_GetStreamIdentifier(stream->descriptor, &stream_id);
> +
> +        sd = stream_descriptor_from_id(command->u.start.descriptor, stream_id, &selected);
> +        IMFStreamDescriptor_Release(sd);
> +
> +        was_active = stream->state != STREAM_INACTIVE;
> +
> +        stream->state = selected ? STREAM_RUNNING : STREAM_INACTIVE;
> +
> +        if (selected)
> +        {
> +            IMFStreamDescriptor_GetMediaTypeHandler(stream->descriptor, &mth);
> +            IMFMediaTypeHandler_GetCurrentMediaType(mth, &current_mt);
> +            current_caps = caps_from_mf_media_type(current_mt);
> +            g_object_get(stream->appsink, "caps", &prev_caps, NULL);
> +            if (!gst_caps_is_equal(prev_caps, current_caps))
> +            {
> +                GstEvent *reconfigure_event = gst_event_new_reconfigure();
> +                g_object_set(stream->appsink, "caps", current_caps, NULL);
> +                gst_pad_push_event(gst_element_get_static_pad(stream->appsink, "sink"), reconfigure_event);
> +            }
> +
> +            gst_caps_unref(current_caps);
> +            gst_caps_unref(prev_caps);
> +            IMFMediaType_Release(current_mt);
> +            IMFMediaTypeHandler_Release(mth);
> +        }
> +
> +        if (position->vt != VT_EMPTY)
> +        {
> +            GstEvent *seek_event = gst_event_new_seek(1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
> +                    GST_SEEK_TYPE_SET, position->u.hVal.QuadPart / 100, GST_SEEK_TYPE_NONE, 0);
> +            GstSample *preroll;
> +
> +            gst_pad_push_event(stream->my_sink, seek_event);
> +
> +            /* this works because the pre-seek preroll is already removed by media_source_constructor */
> +            g_signal_emit_by_name(stream->appsink, "pull-preroll", &preroll);
> +            if (preroll)
> +                gst_sample_unref(preroll);

What is the point of doing this?

> +
> +            stream->eos = FALSE;
> +        }
> +
> +        if (selected)
> +        {
> +            TRACE("Stream %u (%p) selected\n", i, stream);
> +            IMFMediaEventQueue_QueueEventParamUnk(source->event_queue,
> +            was_active ? MEUpdatedStream : MENewStream, &GUID_NULL,
> +            S_OK, (IUnknown*) &stream->IMFMediaStream_iface);

Please apply at least some indentation to line continuations.

> +
> +            IMFMediaEventQueue_QueueEventParamVar(stream->event_queue,
> +                seek_message ? MEStreamSeeked : MEStreamStarted, &GUID_NULL, S_OK, position);
> +        }
> +    }
> +
> +    IMFMediaEventQueue_QueueEventParamVar(source->event_queue,
> +        seek_message ? MESourceSeeked : MESourceStarted,
> +        &GUID_NULL, S_OK, position);
> +
> +    source->state = SOURCE_RUNNING;
> +
> +    gst_element_set_state(source->container, GST_STATE_PLAYING);
> +    gst_element_get_state(source->container, NULL, NULL, -1);

And if this method is asynchronous, do you need this
gst_element_get_state() call?

> +}
> +
> +static HRESULT WINAPI source_async_commands_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
> +{
> +    struct media_source *source = impl_from_async_commands_callback_IMFAsyncCallback(iface);
> +    struct source_async_command *command;
> +    IUnknown *state;
> +    HRESULT hr;
> +
> +    if (source->state == SOURCE_SHUTDOWN)
> +        return S_OK;
> +
> +    if (FAILED(hr = IMFAsyncResult_GetState(result, &state)))
> +        return hr;
> +
> +    command = impl_from_async_command_IUnknown(state);
> +    switch (command->op)
> +    {
> +        case SOURCE_ASYNC_START:
> +            start_pipeline(source, command);
> +            break;
> +    }
> +
> +    IUnknown_Release(state);
> +
> +    return S_OK;
> +}
> +
> +static const IMFAsyncCallbackVtbl source_async_commands_callback_vtbl =
> +{
> +    callback_QueryInterface,
> +    source_async_commands_callback_AddRef,
> +    source_async_commands_callback_Release,
> +    callback_GetParameters,
> +    source_async_commands_Invoke,
> +};
> +
> +GstFlowReturn bytestream_wrapper_pull(GstPad *pad, GstObject *parent, guint64 ofs, guint len,
>          GstBuffer **buf)
>  {
>      struct media_source *source = gst_pad_get_element_private(pad);
> @@ -432,6 +717,8 @@ static HRESULT media_stream_connect_to_sink(struct media_stream *stream)
>      if (gst_pad_link(stream->their_src, stream->my_sink) != GST_PAD_LINK_OK)
>          return E_FAIL;
>  
> +    g_object_set(stream->appsink, "caps", source_caps, NULL);
> +

Again, what's the point of this?

>      return S_OK;
>  }
>  
> @@ -682,16 +969,30 @@ static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource *
>  }
>  
>  static HRESULT WINAPI media_source_Start(IMFMediaSource *iface, IMFPresentationDescriptor *descriptor,
> -                                     const GUID *time_format, const PROPVARIANT *start_position)
> +                                     const GUID *time_format, const PROPVARIANT *position)
>  {
>      struct media_source *source = impl_from_IMFMediaSource(iface);
> +    struct source_async_command *command;
> +    HRESULT hr;
>  
> -    FIXME("(%p)->(%p, %p, %p): stub\n", source, descriptor, time_format, start_position);
> +    TRACE("(%p)->(%p, %p, %p)\n", source, descriptor, time_format, position);
>  
>      if (source->state == SOURCE_SHUTDOWN)
>          return MF_E_SHUTDOWN;
>  
> -    return E_NOTIMPL;
> +    if (!(IsEqualIID(time_format, &GUID_NULL)))
> +        return MF_E_UNSUPPORTED_TIME_FORMAT;
> +
> +    if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_START, &command)))
> +    {
> +        command->u.start.descriptor = descriptor;
> +        command->u.start.format = *time_format;
> +        PropVariantCopy(&command->u.start.position, position);
> +
> +        hr = MFPutWorkItem(source->async_commands_queue, &source->async_commands_callback, &command->IUnknown_iface);
> +    }

There are likely mfplat pecularities I'm not aware of here, but does
this need to be asynchronous? I know that the documentation says "this
method is asynchronous", but it's not immediately obvious to me that
there isn't already a level of asynchronicity between this call and any
of its effects.

> +
> +    return hr;
>  }
>  
>  static HRESULT WINAPI media_source_Stop(IMFMediaSource *iface)
> @@ -772,6 +1073,9 @@ static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface)
>      if (source->no_more_pads_event)
>          CloseHandle(source->no_more_pads_event);
>  
> +    if (source->async_commands_queue)
> +        MFUnlockWorkQueue(source->async_commands_queue);
> +
>      return S_OK;
>  }
>  
> @@ -852,6 +1156,7 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, struct media_
>          return E_OUTOFMEMORY;
>  
>      object->IMFMediaSource_iface.lpVtbl = &IMFMediaSource_vtbl;
> +    object->async_commands_callback.lpVtbl = &source_async_commands_callback_vtbl;
>      object->ref = 1;
>      object->byte_stream = bytestream;
>      IMFByteStream_AddRef(bytestream);
> @@ -860,6 +1165,9 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, struct media_
>      if (FAILED(hr = MFCreateEventQueue(&object->event_queue)))
>          goto fail;
>  
> +    if (FAILED(hr = MFAllocateWorkQueue(&object->async_commands_queue)))
> +        goto fail;
> +
>      object->container = gst_bin_new(NULL);
>      object->bus = gst_bus_new();
>      gst_bus_set_sync_handler(object->bus, mf_src_bus_watch_wrapper, object, NULL);
> diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c
> index 2e8b0978648..9aa17ad00ab 100644
> --- a/dlls/winegstreamer/mfplat.c
> +++ b/dlls/winegstreamer/mfplat.c
> @@ -601,3 +601,152 @@ IMFMediaType *mf_media_type_from_caps(const GstCaps *caps)
>  
>      return media_type;
>  }
> +
> +GstCaps *caps_from_mf_media_type(IMFMediaType *type)
> +{
> +    GUID major_type;
> +    GUID subtype;
> +    GstCaps *output = NULL;
> +
> +    if (FAILED(IMFMediaType_GetMajorType(type, &major_type)))
> +        return NULL;
> +    if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype)))
> +        return NULL;
> +
> +    if (IsEqualGUID(&major_type, &MFMediaType_Video))
> +    {
> +        UINT64 frame_rate = 0, frame_size = 0;
> +        DWORD width, height, framerate_num, framerate_den;
> +        UINT32 unused;
> +
> +        if (FAILED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &frame_size)))
> +            return NULL;
> +        width = frame_size >> 32;
> +        height = frame_size;
> +        if (FAILED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_RATE, &frame_rate)))
> +        {
> +            frame_rate = TRUE;
> +            framerate_num = 0;
> +            framerate_den = 1;
> +        }
> +        else
> +        {
> +            framerate_num = frame_rate >> 32;
> +            framerate_den = frame_rate;
> +        }
> +
> +        /* Check if type is uncompressed */
> +        if (SUCCEEDED(MFCalculateImageSize(&subtype, 100, 100, &unused)))

Early return could save a level of indentation.

I also feel like there's an easier way to do this check. Maybe something
like:

for (i = 0; i < ARRAY_SIZE(uncompressed_video_formats); i++)
{
    ...
}

if (format == GST_VIDEO_FORMAT_UNKNOWN
        && !memcmp(&subtype, &MFVideoFormat_Base.Data2, ...))
    format = gst_video_format_from_fourcc(subtype.Data1);

if (format == GST_VIDEO_FORMAT_UNKNOWN)
{
    FIXME("Unrecognized subtype %s.\n", debugstr_guid(&subtype));
    return NULL;
}

> +        {
> +            GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN;
> +            unsigned int i;
> +
> +            output = gst_caps_new_empty_simple("video/x-raw");
> +
> +            for (i = 0; i < ARRAY_SIZE(uncompressed_video_formats); i++)
> +            {
> +                if (IsEqualGUID(uncompressed_video_formats[i].subtype, &subtype))
> +                {
> +                    format = uncompressed_video_formats[i].format;
> +                    break;
> +                }
> +            }
> +
> +            if (format == GST_VIDEO_FORMAT_UNKNOWN)
> +            {
> +                format = gst_video_format_from_fourcc(subtype.Data1);
> +            }
> +
> +            if (format == GST_VIDEO_FORMAT_UNKNOWN)
> +            {
> +                FIXME("Unrecognized format %s\n", debugstr_guid(&subtype));
> +                return NULL;
> +            }
> +            else

This "else" is redundant.

> +            {
> +                GstVideoInfo info;
> +
> +                gst_video_info_set_format(&info, format, width, height);
> +                output = gst_video_info_to_caps(&info);
> +            }
> +
> +        }
> +        else {

Inconsistent braces.

> +            FIXME("Unrecognized subtype %s\n", debugstr_guid(&subtype));
> +            return NULL;
> +        }
> +
> +
> +        if (frame_size)
> +        {
> +            gst_caps_set_simple(output, "width", G_TYPE_INT, width, NULL);
> +            gst_caps_set_simple(output, "height", G_TYPE_INT, height, NULL);
> +        }
> +        if (frame_rate)

Why do you need to check this is nonzero? Can the frame rate really be 0/0?

For that matter, do we need to give GStreamer the framerate at all? It
should only ever matter to sink elements (or those that respect a clock).

> +            gst_caps_set_simple(output, "framerate", GST_TYPE_FRACTION, framerate_num, framerate_den, NULL);
> +        return output;
> +    }
> +    else if (IsEqualGUID(&major_type, &MFMediaType_Audio))
> +    {
> +        DWORD rate, channels, channel_mask, bitrate;
> +
> +        if (IsEqualGUID(&subtype, &MFAudioFormat_Float))
> +        {
> +            output = gst_caps_new_empty_simple("audio/x-raw");
> +
> +            gst_caps_set_simple(output, "format", G_TYPE_STRING, "F32LE", NULL);
> +            gst_caps_set_simple(output, "layout", G_TYPE_STRING, "interleaved", NULL);
> +        }
> +        else if (IsEqualGUID(&subtype, &MFAudioFormat_PCM))
> +        {
> +            DWORD bits_per_sample;
> +
> +            if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_BITS_PER_SAMPLE, &bits_per_sample)))
> +            {
> +                char format[6];
> +                char type;
> +
> +                type = bits_per_sample > 8 ? 'S' : 'U';
> +
> +                output = gst_caps_new_empty_simple("audio/x-raw");
> +
> +                sprintf(format, "%c%u%s", type, bits_per_sample, bits_per_sample > 8 ? "LE" : "");
> +
> +                gst_caps_set_simple(output, "format", G_TYPE_STRING, format, NULL);
> +            }
> +            else
> +            {
> +                ERR("Bits per sample not set.\n");
> +                return NULL;
> +            }
> +        }
> +        else
> +        {
> +            FIXME("Unrecognized subtype %s\n", debugstr_guid(&subtype));
> +            return NULL;
> +        }
> +
> +        if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &rate)))
> +        {
> +            gst_caps_set_simple(output, "rate", G_TYPE_INT, rate, NULL);
> +        }
> +        if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_NUM_CHANNELS, &channels)))
> +        {
> +            gst_caps_set_simple(output, "channels", G_TYPE_INT, channels, NULL);
> +        }
> +        if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_CHANNEL_MASK, &channel_mask)))
> +        {
> +            gst_caps_set_simple(output, "channel-mask", GST_TYPE_BITMASK, (guint64) channel_mask, NULL);
> +        }
> +
> +        if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_AVG_BITRATE, &bitrate)))
> +        {
> +            gst_caps_set_simple(output, "bitrate", G_TYPE_INT, bitrate, NULL);
> +        }
> +
> +        return output;
> +    }
> +
> +    FIXME("Unrecognized major type %s\n", debugstr_guid(&major_type));
> +    return NULL;
> +}
> 


-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 488 bytes
Desc: OpenPGP digital signature
URL: <http://www.winehq.org/pipermail/wine-devel/attachments/20201006/6f1aed12/attachment-0001.sig>


More information about the wine-devel mailing list