[PATCH 3/7] winegstreamer: Implement pausing the media source.

Giovanni Mascellani gmascellani at codeweavers.com
Fri Sep 17 10:11:40 CDT 2021


Hi,

this patch implements the possibility to pause the media source provided 
by winegstreamer. This feature is, for example, required by Deep Rock 
Galactic (the game uses it to pause and restart the preview videos in 
the character selection screen, which you can reach from the computer on 
your right in the spawn pod, just after loading the game; without this 
patch the videos will not restart after you switch between them).

Since the media source does not work synchronously (the caller can 
request a sample at any time, independently of the timestamps at which 
they should presented), the paused state is not very different from the 
playing state. The most important difference is that RequestSample 
commands are not executed when the media source is paused, but stored in 
an internal queue (via the enqueue_token helper). As soon as the source 
is restarted, they will be immediately issued (if, instead, the source 
is later stopped, they are dropped), which happens in the 
flush_token_queue helper. This internal queue just needs to store the 
token associated with that RequestSample command, because that's the 
only state RequestSample has.

Besides that, this patch ensures that the MESourcePaused and 
MEStreamPaused are emitted when there is a transition to paused state 
and that the MFMEDIASOURCE_CAN_PAUSE characteristic is exposed.

Again, you can use patches 4/7 through 7/7 to test this one. See my 
explanation for 1/7.

Thanks, Giovanni.


Il 06/09/21 17:11, Giovanni Mascellani ha scritto:
> Signed-off-by: Giovanni Mascellani <gmascellani at codeweavers.com>
> ---
> This patch was already signed off by Derek Lesho in an earlier
> submission.
> 
>   dlls/winegstreamer/media_source.c | 102 ++++++++++++++++++++++++++++--
>   1 file changed, 97 insertions(+), 5 deletions(-)
> 
> diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c
> index 51ef84bf88f..2066051765e 100644
> --- a/dlls/winegstreamer/media_source.c
> +++ b/dlls/winegstreamer/media_source.c
> @@ -37,6 +37,10 @@ struct media_stream
>   
>       struct wg_parser_stream *wg_stream;
>   
> +    IUnknown **token_queue;
> +    LONG token_queue_count;
> +    LONG token_queue_cap;
> +
>       enum
>       {
>           STREAM_INACTIVE,
> @@ -50,6 +54,7 @@ struct media_stream
>   enum source_async_op
>   {
>       SOURCE_ASYNC_START,
> +    SOURCE_ASYNC_PAUSE,
>       SOURCE_ASYNC_STOP,
>       SOURCE_ASYNC_REQUEST_SAMPLE,
>   };
> @@ -96,6 +101,7 @@ struct media_source
>       {
>           SOURCE_OPENING,
>           SOURCE_STOPPED,
> +        SOURCE_PAUSED,
>           SOURCE_RUNNING,
>           SOURCE_SHUTDOWN,
>       } state;
> @@ -260,6 +266,54 @@ static IMFStreamDescriptor *stream_descriptor_from_id(IMFPresentationDescriptor
>       return NULL;
>   }
>   
> +static BOOL enqueue_token(struct media_stream *stream, IUnknown *token)
> +{
> +    if (stream->token_queue_count == stream->token_queue_cap)
> +    {
> +        IUnknown **buf;
> +        stream->token_queue_cap = stream->token_queue_cap * 2 + 1;
> +        buf = realloc(stream->token_queue, stream->token_queue_cap * sizeof(*buf));
> +        if (buf)
> +            stream->token_queue = buf;
> +        else
> +        {
> +            stream->token_queue_cap = stream->token_queue_count;
> +            return FALSE;
> +        }
> +    }
> +    stream->token_queue[stream->token_queue_count++] = token;
> +    return TRUE;
> +}
> +
> +static void flush_token_queue(struct media_stream *stream, BOOL send)
> +{
> +    LONG i;
> +
> +    for (i = 0; i < stream->token_queue_count; i++)
> +    {
> +        if (send)
> +        {
> +            HRESULT hr;
> +            struct source_async_command *command;
> +            if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_REQUEST_SAMPLE, &command)))
> +            {
> +                command->u.request_sample.stream = stream;
> +                command->u.request_sample.token = stream->token_queue[i];
> +
> +                hr = MFPutWorkItem(stream->parent_source->async_commands_queue, &stream->parent_source->async_commands_callback, &command->IUnknown_iface);
> +            }
> +            if (FAILED(hr))
> +                WARN("Could not enqueue sample request, hr %#x\n", hr);
> +        }
> +        else if (stream->token_queue[i])
> +            IUnknown_Release(stream->token_queue[i]);
> +    }
> +    free(stream->token_queue);
> +    stream->token_queue = NULL;
> +    stream->token_queue_count = 0;
> +    stream->token_queue_cap = 0;
> +}
> +
>   static void start_pipeline(struct media_source *source, struct source_async_command *command)
>   {
>       PROPVARIANT *position = &command->u.start.position;
> @@ -333,6 +387,27 @@ static void start_pipeline(struct media_source *source, struct source_async_comm
>           unix_funcs->wg_parser_stream_seek(source->streams[0]->wg_stream, 1.0,
>                   position->hVal.QuadPart, 0, AM_SEEKING_AbsolutePositioning, AM_SEEKING_NoPositioning);
>       unix_funcs->wg_parser_end_flush(source->wg_parser);
> +
> +    for (i = 0; i < source->stream_count; i++)
> +        flush_token_queue(source->streams[i], position->vt == VT_EMPTY);
> +}
> +
> +static void pause_pipeline(struct media_source *source)
> +{
> +    unsigned int i;
> +
> +    for (i = 0; i < source->stream_count; i++)
> +    {
> +        struct media_stream *stream = source->streams[i];
> +        if (stream->state != STREAM_INACTIVE)
> +        {
> +            IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, MEStreamPaused, &GUID_NULL, S_OK, NULL);
> +        }
> +    }
> +
> +    IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MESourcePaused, &GUID_NULL, S_OK, NULL);
> +
> +    source->state = SOURCE_PAUSED;
>   }
>   
>   static void stop_pipeline(struct media_source *source)
> @@ -354,6 +429,9 @@ static void stop_pipeline(struct media_source *source)
>       IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MESourceStopped, &GUID_NULL, S_OK, NULL);
>   
>       source->state = SOURCE_STOPPED;
> +
> +    for (i = 0; i < source->stream_count; i++)
> +        flush_token_queue(source->streams[i], FALSE);
>   }
>   
>   static void dispatch_end_of_presentation(struct media_source *source)
> @@ -502,11 +580,17 @@ static HRESULT WINAPI source_async_commands_Invoke(IMFAsyncCallback *iface, IMFA
>           case SOURCE_ASYNC_START:
>               start_pipeline(source, command);
>               break;
> +        case SOURCE_ASYNC_PAUSE:
> +            pause_pipeline(source);
> +            break;
>           case SOURCE_ASYNC_STOP:
>               stop_pipeline(source);
>               break;
>           case SOURCE_ASYNC_REQUEST_SAMPLE:
> -            wait_on_sample(command->u.request_sample.stream, command->u.request_sample.token);
> +            if (source->state == SOURCE_PAUSED)
> +                enqueue_token(command->u.request_sample.stream, command->u.request_sample.token);
> +            else
> +                wait_on_sample(command->u.request_sample.stream, command->u.request_sample.token);
>               break;
>       }
>   
> @@ -597,6 +681,7 @@ static ULONG WINAPI media_stream_Release(IMFMediaStream *iface)
>       {
>           if (stream->event_queue)
>               IMFMediaEventQueue_Release(stream->event_queue);
> +        flush_token_queue(stream, FALSE);
>           free(stream);
>       }
>   
> @@ -699,7 +784,6 @@ static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown
>               IUnknown_AddRef(token);
>           command->u.request_sample.token = token;
>   
> -        /* Once pause support is added, this will need to put into a stream queue, and synchronization will need to be added*/
>           hr = MFPutWorkItem(stream->parent_source->async_commands_queue, &stream->parent_source->async_commands_callback, &command->IUnknown_iface);
>       }
>   
> @@ -1124,7 +1208,7 @@ static HRESULT WINAPI media_source_GetCharacteristics(IMFMediaSource *iface, DWO
>       if (source->state == SOURCE_SHUTDOWN)
>           return MF_E_SHUTDOWN;
>   
> -    *characteristics = MFMEDIASOURCE_CAN_SEEK;
> +    *characteristics = MFMEDIASOURCE_CAN_SEEK | MFMEDIASOURCE_CAN_PAUSE;
>   
>       return S_OK;
>   }
> @@ -1188,13 +1272,21 @@ static HRESULT WINAPI media_source_Stop(IMFMediaSource *iface)
>   static HRESULT WINAPI media_source_Pause(IMFMediaSource *iface)
>   {
>       struct media_source *source = impl_from_IMFMediaSource(iface);
> +    struct source_async_command *command;
> +    HRESULT hr;
>   
> -    FIXME("%p: stub\n", iface);
> +    TRACE("%p.\n", iface);
>   
>       if (source->state == SOURCE_SHUTDOWN)
>           return MF_E_SHUTDOWN;
>   
> -    return E_NOTIMPL;
> +    if (source->state != SOURCE_RUNNING)
> +        return MF_E_INVALID_STATE_TRANSITION;
> +
> +    if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_PAUSE, &command)))
> +        hr = MFPutWorkItem(source->async_commands_queue, &source->async_commands_callback, &command->IUnknown_iface);
> +
> +    return S_OK;
>   }
>   
>   static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface)
> 



More information about the wine-devel mailing list