With this the FFmpeg based demuxers should work, although there's still various things to fix related to MF pipelines elsewhere.
-- v2: mfsrcsnk: Read samples from the media source demuxer. winedmo: Read sample flags, timestamps and duration. winedmo: Export a new winedmo_demuxer_read function.
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mfsrcsnk/media_source.c | 220 ++++++++++++++++++++++++++++++++++- 1 file changed, 218 insertions(+), 2 deletions(-)
diff --git a/dlls/mfsrcsnk/media_source.c b/dlls/mfsrcsnk/media_source.c index 2b59ee89f3e..e1d313e5baf 100644 --- a/dlls/mfsrcsnk/media_source.c +++ b/dlls/mfsrcsnk/media_source.c @@ -72,6 +72,84 @@ WINE_DEFAULT_DEBUG_CHANNEL(mfplat); #define DEFINE_MF_ASYNC_CALLBACK(type, name, base_iface) \ DEFINE_MF_ASYNC_CALLBACK_(type, name, type##_from_##name, type##_##name, name##_iface, &object->base_iface)
+struct async_start_params +{ + IUnknown IUnknown_iface; + LONG refcount; + IMFPresentationDescriptor *descriptor; + GUID format; + PROPVARIANT position; +}; + +static struct async_start_params *async_start_params_from_IUnknown(IUnknown *iface) +{ + return CONTAINING_RECORD(iface, struct async_start_params, IUnknown_iface); +} + +static HRESULT WINAPI async_start_params_QueryInterface(IUnknown *iface, REFIID riid, void **obj) +{ + struct async_start_params *params = async_start_params_from_IUnknown(iface); + + if (IsEqualIID(riid, &IID_IUnknown)) + { + IUnknown_AddRef(¶ms->IUnknown_iface); + *obj = ¶ms->IUnknown_iface; + return S_OK; + } + + WARN("Unsupported interface %s\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI async_start_params_AddRef(IUnknown *iface) +{ + struct async_start_params *params = async_start_params_from_IUnknown(iface); + return InterlockedIncrement(¶ms->refcount); +} + +static ULONG WINAPI async_start_params_Release(IUnknown *iface) +{ + struct async_start_params *params = async_start_params_from_IUnknown(iface); + ULONG refcount = InterlockedDecrement(¶ms->refcount); + + if (!refcount) + { + IMFPresentationDescriptor_Release(params->descriptor); + PropVariantClear(¶ms->position); + free(params); + } + + return refcount; +} + +static const IUnknownVtbl async_start_params_vtbl = +{ + async_start_params_QueryInterface, + async_start_params_AddRef, + async_start_params_Release, +}; + +static HRESULT async_start_params_create(IMFPresentationDescriptor *descriptor, const GUID *time_format, + const PROPVARIANT *position, IUnknown **out) +{ + struct async_start_params *params; + + if (!(params = calloc(1, sizeof(*params)))) + return E_OUTOFMEMORY; + + params->IUnknown_iface.lpVtbl = &async_start_params_vtbl; + params->refcount = 1; + + params->descriptor = descriptor; + IMFPresentationDescriptor_AddRef(descriptor); + params->format = *time_format; + PropVariantCopy(¶ms->position, position); + + *out = ¶ms->IUnknown_iface; + return S_OK; +} + struct media_stream { IMFMediaStream IMFMediaStream_iface; @@ -91,6 +169,7 @@ struct media_source IMFRateSupport IMFRateSupport_iface; IMFRateControl IMFRateControl_iface; IMFAsyncCallback async_create_iface; + IMFAsyncCallback async_start_iface; LONG refcount;
CRITICAL_SECTION cs; @@ -112,8 +191,10 @@ struct media_source enum { SOURCE_STOPPED, + SOURCE_RUNNING, SOURCE_SHUTDOWN, } state; + UINT pending_state; };
static struct media_source *media_source_from_IMFMediaSource(IMFMediaSource *iface) @@ -121,6 +202,121 @@ static struct media_source *media_source_from_IMFMediaSource(IMFMediaSource *ifa return CONTAINING_RECORD(iface, struct media_source, IMFMediaSource_iface); }
+static void queue_media_event_object(IMFMediaEventQueue *queue, MediaEventType type, IUnknown *object) +{ + HRESULT hr; + if (FAILED(hr = IMFMediaEventQueue_QueueEventParamUnk(queue, type, &GUID_NULL, S_OK, object))) + ERR("Failed to queue event of type %#lx to queue %p, hr %#lx\n", type, queue, hr); +} + +static void queue_media_event_value(IMFMediaEventQueue *queue, MediaEventType type, const PROPVARIANT *value) +{ + HRESULT hr; + if (FAILED(hr = IMFMediaEventQueue_QueueEventParamVar(queue, type, &GUID_NULL, S_OK, value))) + ERR("Failed to queue event of type %#lx to queue %p, hr %#lx\n", type, queue, hr); +} + +static void media_stream_start(struct media_stream *stream, UINT index, const PROPVARIANT *position) +{ + struct media_source *source = media_source_from_IMFMediaSource(stream->source); + BOOL starting = source->state == SOURCE_STOPPED, seeking = !starting && position->vt != VT_EMPTY; + BOOL was_active = !starting && stream->active; + + TRACE("stream %p, index %u, position %s\n", stream, index, debugstr_propvar(position)); + + if (stream->active) + { + queue_media_event_object(source->queue, was_active ? MEUpdatedStream : MENewStream, + (IUnknown *)&stream->IMFMediaStream_iface); + queue_media_event_value(stream->queue, seeking ? MEStreamSeeked : MEStreamStarted, position); + } +} + +static HRESULT media_source_start(struct media_source *source, IMFPresentationDescriptor *descriptor, + GUID *format, PROPVARIANT *position) +{ + BOOL starting = source->state == SOURCE_STOPPED, seeking = !starting && position->vt != VT_EMPTY; + DWORD i, count; + HRESULT hr; + + TRACE("source %p, descriptor %p, format %s, position %s\n", source, descriptor, + debugstr_guid(format), debugstr_propvar(position)); + + if (source->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + + if (source->state == SOURCE_STOPPED && position->vt == VT_EMPTY) + { + position->vt = VT_I8; + position->hVal.QuadPart = 0; + } + + for (i = 0; i < source->stream_count; i++) + { + struct media_stream *stream = source->streams[i]; + stream->active = FALSE; + } + + if (FAILED(hr = IMFPresentationDescriptor_GetStreamDescriptorCount(descriptor, &count))) + WARN("Failed to get presentation descriptor stream count, hr %#lx\n", hr); + + for (i = 0; i < count; i++) + { + IMFStreamDescriptor *stream_descriptor; + BOOL selected; + DWORD id; + + if (FAILED(hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(descriptor, i, + &selected, &stream_descriptor))) + break; + + if (FAILED(hr = IMFStreamDescriptor_GetStreamIdentifier(stream_descriptor, &id))) + WARN("Failed to get stream descriptor id, hr %#lx\n", hr); + else if (id > source->stream_count) + WARN("Invalid stream descriptor id %lu, hr %#lx\n", id, hr); + else + { + struct media_stream *stream = source->streams[id - 1]; + stream->active = selected; + } + + IMFStreamDescriptor_Release(stream_descriptor); + } + + for (i = 0; i < source->stream_count; i++) + { + struct media_stream *stream = source->streams[i]; + media_stream_start(stream, i, position); + } + + source->state = SOURCE_RUNNING; + + queue_media_event_value(source->queue, seeking ? MESourceSeeked : MESourceStarted, position); + return hr; +} + +static HRESULT media_source_async_start(struct media_source *source, IMFAsyncResult *result) +{ + struct async_start_params *params; + IUnknown *state; + HRESULT hr; + + if (!(state = IMFAsyncResult_GetStateNoAddRef(result))) return E_INVALIDARG; + params = async_start_params_from_IUnknown(state); + + EnterCriticalSection(&source->cs); + source->pending_state--; + + if (FAILED(hr = media_source_start(source, params->descriptor, ¶ms->format, ¶ms->position))) + WARN("Failed to start source %p, hr %#lx\n", source, hr); + + LeaveCriticalSection(&source->cs); + + return hr; +} + +DEFINE_MF_ASYNC_CALLBACK(media_source, async_start, IMFMediaSource_iface) + static struct media_stream *media_stream_from_IMFMediaStream(IMFMediaStream *iface) { return CONTAINING_RECORD(iface, struct media_stream, IMFMediaStream_iface); @@ -657,9 +853,28 @@ static HRESULT WINAPI media_source_Start(IMFMediaSource *iface, IMFPresentationD const PROPVARIANT *position) { struct media_source *source = media_source_from_IMFMediaSource(iface); - FIXME("source %p, descriptor %p, format %s, position %s, stub!\n", source, descriptor, + IUnknown *op; + HRESULT hr; + + TRACE("source %p, descriptor %p, format %s, position %s\n", source, descriptor, debugstr_guid(format), debugstr_propvar(position)); - return E_NOTIMPL; + + EnterCriticalSection(&source->cs); + + if (source->state == SOURCE_SHUTDOWN) + hr = MF_E_SHUTDOWN; + else if (!IsEqualIID(format, &GUID_NULL)) + hr = MF_E_UNSUPPORTED_TIME_FORMAT; + else if (SUCCEEDED(hr = async_start_params_create(descriptor, format, position, &op))) + { + if (SUCCEEDED(hr = MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_STANDARD, &source->async_start_iface, op))) + source->pending_state++; + IUnknown_Release(op); + } + + LeaveCriticalSection(&source->cs); + + return hr; }
static HRESULT WINAPI media_source_Stop(IMFMediaSource *iface) @@ -1072,6 +1287,7 @@ static HRESULT media_source_create(const WCHAR *url, IMFByteStream *stream, IMFM source->IMFRateSupport_iface.lpVtbl = &media_source_IMFRateSupport_vtbl; source->IMFRateControl_iface.lpVtbl = &media_source_IMFRateControl_vtbl; source->async_create_iface.lpVtbl = &media_source_async_create_vtbl; + source->async_start_iface.lpVtbl = &media_source_async_start_vtbl; source->refcount = 1;
if (FAILED(hr = MFCreateEventQueue(&source->queue)))
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mfsrcsnk/media_source.c | 64 ++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-)
diff --git a/dlls/mfsrcsnk/media_source.c b/dlls/mfsrcsnk/media_source.c index e1d313e5baf..caef4c0d218 100644 --- a/dlls/mfsrcsnk/media_source.c +++ b/dlls/mfsrcsnk/media_source.c @@ -170,6 +170,7 @@ struct media_source IMFRateControl IMFRateControl_iface; IMFAsyncCallback async_create_iface; IMFAsyncCallback async_start_iface; + IMFAsyncCallback async_stop_iface; LONG refcount;
CRITICAL_SECTION cs; @@ -317,6 +318,51 @@ static HRESULT media_source_async_start(struct media_source *source, IMFAsyncRes
DEFINE_MF_ASYNC_CALLBACK(media_source, async_start, IMFMediaSource_iface)
+static void media_stream_stop(struct media_stream *stream) +{ + TRACE("stream %p\n", stream); + + if (stream->active) + queue_media_event_value(stream->queue, MEStreamStopped, NULL); +} + +static HRESULT media_source_stop(struct media_source *source) +{ + unsigned int i; + + TRACE("source %p\n", source); + + if (source->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + + for (i = 0; i < source->stream_count; i++) + { + struct media_stream *stream = source->streams[i]; + media_stream_stop(stream); + } + + source->state = SOURCE_STOPPED; + queue_media_event_value(source->queue, MESourceStopped, NULL); + return S_OK; +} + +static HRESULT media_source_async_stop(struct media_source *source, IMFAsyncResult *result) +{ + HRESULT hr; + + EnterCriticalSection(&source->cs); + source->pending_state--; + + if (FAILED(hr = media_source_stop(source))) + WARN("Failed to stop source %p, hr %#lx\n", source, hr); + + LeaveCriticalSection(&source->cs); + + return hr; +} + +DEFINE_MF_ASYNC_CALLBACK(media_source, async_stop, IMFMediaSource_iface) + static struct media_stream *media_stream_from_IMFMediaStream(IMFMediaStream *iface) { return CONTAINING_RECORD(iface, struct media_stream, IMFMediaStream_iface); @@ -360,6 +406,7 @@ static ULONG WINAPI media_stream_Release(IMFMediaStream *iface) if (!refcount) { stream->active = FALSE; + media_stream_stop(stream); IMFMediaSource_Release(stream->source); IMFStreamDescriptor_Release(stream->descriptor); IMFMediaEventQueue_Release(stream->queue); @@ -880,8 +927,20 @@ static HRESULT WINAPI media_source_Start(IMFMediaSource *iface, IMFPresentationD static HRESULT WINAPI media_source_Stop(IMFMediaSource *iface) { struct media_source *source = media_source_from_IMFMediaSource(iface); - FIXME("source %p, stub!\n", source); - return E_NOTIMPL; + HRESULT hr; + + TRACE("source %p\n", source); + + EnterCriticalSection(&source->cs); + + if (source->state == SOURCE_SHUTDOWN) + hr = MF_E_SHUTDOWN; + else if (SUCCEEDED(hr = MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_STANDARD, &source->async_stop_iface, NULL))) + source->pending_state++; + + LeaveCriticalSection(&source->cs); + + return hr; }
static HRESULT WINAPI media_source_Pause(IMFMediaSource *iface) @@ -1288,6 +1347,7 @@ static HRESULT media_source_create(const WCHAR *url, IMFByteStream *stream, IMFM source->IMFRateControl_iface.lpVtbl = &media_source_IMFRateControl_vtbl; source->async_create_iface.lpVtbl = &media_source_async_create_vtbl; source->async_start_iface.lpVtbl = &media_source_async_start_vtbl; + source->async_stop_iface.lpVtbl = &media_source_async_stop_vtbl; source->refcount = 1;
if (FAILED(hr = MFCreateEventQueue(&source->queue)))
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mfsrcsnk/media_source.c | 66 ++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-)
diff --git a/dlls/mfsrcsnk/media_source.c b/dlls/mfsrcsnk/media_source.c index caef4c0d218..5cc769b93b0 100644 --- a/dlls/mfsrcsnk/media_source.c +++ b/dlls/mfsrcsnk/media_source.c @@ -171,6 +171,7 @@ struct media_source IMFAsyncCallback async_create_iface; IMFAsyncCallback async_start_iface; IMFAsyncCallback async_stop_iface; + IMFAsyncCallback async_pause_iface; LONG refcount;
CRITICAL_SECTION cs; @@ -192,6 +193,7 @@ struct media_source enum { SOURCE_STOPPED, + SOURCE_PAUSED, SOURCE_RUNNING, SOURCE_SHUTDOWN, } state; @@ -363,6 +365,51 @@ static HRESULT media_source_async_stop(struct media_source *source, IMFAsyncResu
DEFINE_MF_ASYNC_CALLBACK(media_source, async_stop, IMFMediaSource_iface)
+static void media_stream_pause(struct media_stream *stream) +{ + TRACE("stream %p\n", stream); + + if (stream->active) + queue_media_event_value(stream->queue, MEStreamPaused, NULL); +} + +static HRESULT media_source_pause(struct media_source *source) +{ + unsigned int i; + + TRACE("source %p\n", source); + + if (source->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + + for (i = 0; i < source->stream_count; i++) + { + struct media_stream *stream = source->streams[i]; + media_stream_pause(stream); + } + + source->state = SOURCE_PAUSED; + queue_media_event_value(source->queue, MESourcePaused, NULL); + return S_OK; +} + +static HRESULT media_source_async_pause(struct media_source *source, IMFAsyncResult *result) +{ + HRESULT hr; + + EnterCriticalSection(&source->cs); + source->pending_state--; + + if (FAILED(hr = media_source_pause(source))) + WARN("Failed to pause source %p, hr %#lx\n", source, hr); + + LeaveCriticalSection(&source->cs); + + return hr; +} + +DEFINE_MF_ASYNC_CALLBACK(media_source, async_pause, IMFMediaSource_iface) + static struct media_stream *media_stream_from_IMFMediaStream(IMFMediaStream *iface) { return CONTAINING_RECORD(iface, struct media_stream, IMFMediaStream_iface); @@ -946,8 +993,22 @@ static HRESULT WINAPI media_source_Stop(IMFMediaSource *iface) static HRESULT WINAPI media_source_Pause(IMFMediaSource *iface) { struct media_source *source = media_source_from_IMFMediaSource(iface); - FIXME("source %p, stub!\n", source); - return E_NOTIMPL; + HRESULT hr; + + TRACE("source %p\n", source); + + EnterCriticalSection(&source->cs); + + if (source->state == SOURCE_SHUTDOWN) + hr = MF_E_SHUTDOWN; + else if (source->state != SOURCE_RUNNING) + hr = MF_E_INVALID_STATE_TRANSITION; + else if (SUCCEEDED(hr = MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_STANDARD, &source->async_pause_iface, NULL))) + source->pending_state++; + + LeaveCriticalSection(&source->cs); + + return hr; }
static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) @@ -1348,6 +1409,7 @@ static HRESULT media_source_create(const WCHAR *url, IMFByteStream *stream, IMFM source->async_create_iface.lpVtbl = &media_source_async_create_vtbl; source->async_start_iface.lpVtbl = &media_source_async_start_vtbl; source->async_stop_iface.lpVtbl = &media_source_async_stop_vtbl; + source->async_pause_iface.lpVtbl = &media_source_async_pause_vtbl; source->refcount = 1;
if (FAILED(hr = MFCreateEventQueue(&source->queue)))
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winedmo/main.c | 16 ++++++++++++++++ dlls/winedmo/unix_demuxer.c | 18 ++++++++++++++++++ dlls/winedmo/unix_private.h | 1 + dlls/winedmo/unixlib.c | 2 ++ dlls/winedmo/unixlib.h | 7 +++++++ dlls/winedmo/winedmo.spec | 1 + include/wine/winedmo.h | 1 + 7 files changed, 46 insertions(+)
diff --git a/dlls/winedmo/main.c b/dlls/winedmo/main.c index 381211b396f..ccb8a4eb95d 100644 --- a/dlls/winedmo/main.c +++ b/dlls/winedmo/main.c @@ -180,6 +180,22 @@ NTSTATUS CDECL winedmo_demuxer_destroy( struct winedmo_demuxer *demuxer ) return status; }
+NTSTATUS CDECL winedmo_demuxer_seek( struct winedmo_demuxer demuxer, INT64 timestamp ) +{ + struct demuxer_seek_params params = {.demuxer = demuxer, .timestamp = timestamp}; + NTSTATUS status; + + TRACE( "demuxer %#I64x, timestamp %I64d\n", demuxer.handle, timestamp ); + + if ((status = UNIX_CALL( demuxer_seek, ¶ms ))) + { + WARN( "Failed to set position, status %#lx\n", status ); + return status; + } + + return STATUS_SUCCESS; +} + NTSTATUS CDECL winedmo_demuxer_stream_lang( struct winedmo_demuxer demuxer, UINT stream, WCHAR *buffer, UINT len ) { struct demuxer_stream_lang_params params = {.demuxer = demuxer, .stream = stream}; diff --git a/dlls/winedmo/unix_demuxer.c b/dlls/winedmo/unix_demuxer.c index 70f9e0715f6..e14431e6d9c 100644 --- a/dlls/winedmo/unix_demuxer.c +++ b/dlls/winedmo/unix_demuxer.c @@ -159,6 +159,24 @@ NTSTATUS demuxer_destroy( void *arg ) return STATUS_SUCCESS; }
+NTSTATUS demuxer_seek( void *arg ) +{ + struct demuxer_seek_params *params = arg; + AVFormatContext *ctx = get_demuxer( params->demuxer ); + int64_t timestamp = params->timestamp * AV_TIME_BASE / 10000000; + int ret; + + TRACE( "context %p, timestamp 0x%s\n", ctx, wine_dbgstr_longlong( params->timestamp ) ); + + if ((ret = av_seek_frame( ctx, -1, timestamp, AVSEEK_FLAG_ANY )) < 0) + { + ERR( "Failed to seek context %p, error %s.\n", ctx, debugstr_averr(ret) ); + return STATUS_UNSUCCESSFUL; + } + + return STATUS_SUCCESS; +} + NTSTATUS demuxer_stream_lang( void *arg ) { struct demuxer_stream_lang_params *params = arg; diff --git a/dlls/winedmo/unix_private.h b/dlls/winedmo/unix_private.h index 8a282c23148..77aa9058350 100644 --- a/dlls/winedmo/unix_private.h +++ b/dlls/winedmo/unix_private.h @@ -43,6 +43,7 @@ extern int unix_read_callback( void *opaque, uint8_t *buffer, int size ); extern NTSTATUS demuxer_check( void * ); extern NTSTATUS demuxer_create( void * ); extern NTSTATUS demuxer_destroy( void * ); +extern NTSTATUS demuxer_seek( void * ); extern NTSTATUS demuxer_stream_lang( void * ); extern NTSTATUS demuxer_stream_name( void * ); extern NTSTATUS demuxer_stream_type( void * ); diff --git a/dlls/winedmo/unixlib.c b/dlls/winedmo/unixlib.c index 8398c8f143a..119853ec457 100644 --- a/dlls/winedmo/unixlib.c +++ b/dlls/winedmo/unixlib.c @@ -132,6 +132,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = X( demuxer_check ), X( demuxer_create ), X( demuxer_destroy ), + X( demuxer_seek ), X( demuxer_stream_lang ), X( demuxer_stream_name ), X( demuxer_stream_type ), @@ -193,6 +194,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = X( demuxer_check ), X64( demuxer_create ), X64( demuxer_destroy ), + X( demuxer_seek ), X( demuxer_stream_lang ), X( demuxer_stream_name ), X( demuxer_stream_type ), diff --git a/dlls/winedmo/unixlib.h b/dlls/winedmo/unixlib.h index d7dc1a2e2fd..fd250e58209 100644 --- a/dlls/winedmo/unixlib.h +++ b/dlls/winedmo/unixlib.h @@ -98,6 +98,12 @@ struct demuxer_destroy_params struct stream_context *context; };
+struct demuxer_seek_params +{ + struct winedmo_demuxer demuxer; + INT64 timestamp; +}; + struct demuxer_stream_lang_params { struct winedmo_demuxer demuxer; @@ -127,6 +133,7 @@ enum unix_funcs unix_demuxer_check, unix_demuxer_create, unix_demuxer_destroy, + unix_demuxer_seek, unix_demuxer_stream_lang, unix_demuxer_stream_name, unix_demuxer_stream_type, diff --git a/dlls/winedmo/winedmo.spec b/dlls/winedmo/winedmo.spec index 0da65562406..77772b1f472 100644 --- a/dlls/winedmo/winedmo.spec +++ b/dlls/winedmo/winedmo.spec @@ -1,6 +1,7 @@ @ cdecl winedmo_demuxer_check(str) @ cdecl winedmo_demuxer_create(wstr ptr int64 ptr ptr ptr ptr) @ cdecl winedmo_demuxer_destroy(ptr) +@ cdecl winedmo_demuxer_seek(int64 int64) @ cdecl winedmo_demuxer_stream_lang(int64 long ptr long) @ cdecl winedmo_demuxer_stream_name(int64 long ptr long) @ cdecl winedmo_demuxer_stream_type(int64 long ptr ptr) diff --git a/include/wine/winedmo.h b/include/wine/winedmo.h index 843640c8107..f4f9d1371d4 100644 --- a/include/wine/winedmo.h +++ b/include/wine/winedmo.h @@ -46,6 +46,7 @@ NTSTATUS CDECL winedmo_demuxer_check( const char *mime_type ); NTSTATUS CDECL winedmo_demuxer_create( const WCHAR *url, struct winedmo_stream *stream, UINT64 stream_size, INT64 *duration, UINT *stream_count, WCHAR *mime_type, struct winedmo_demuxer *demuxer ); NTSTATUS CDECL winedmo_demuxer_destroy( struct winedmo_demuxer *demuxer ); +NTSTATUS CDECL winedmo_demuxer_seek( struct winedmo_demuxer demuxer, INT64 timestamp ); NTSTATUS CDECL winedmo_demuxer_stream_lang( struct winedmo_demuxer demuxer, UINT stream, WCHAR *buffer, UINT len ); NTSTATUS CDECL winedmo_demuxer_stream_name( struct winedmo_demuxer demuxer, UINT stream, WCHAR *buffer, UINT len ); NTSTATUS CDECL winedmo_demuxer_stream_type( struct winedmo_demuxer demuxer, UINT stream,
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winedmo/Makefile.in | 1 + dlls/winedmo/main.c | 51 +++++++++++++++++++++++++++++++++++++ dlls/winedmo/unix_demuxer.c | 38 +++++++++++++++++++++++++++ dlls/winedmo/unix_private.h | 1 + dlls/winedmo/unixlib.c | 2 ++ dlls/winedmo/unixlib.h | 16 ++++++++++++ dlls/winedmo/winedmo.spec | 1 + include/wine/winedmo.h | 1 + 8 files changed, 111 insertions(+)
diff --git a/dlls/winedmo/Makefile.in b/dlls/winedmo/Makefile.in index 2d829deb07b..9b33d859d6a 100644 --- a/dlls/winedmo/Makefile.in +++ b/dlls/winedmo/Makefile.in @@ -1,6 +1,7 @@ MODULE = winedmo.dll UNIXLIB = winedmo.so IMPORTLIB = winedmo +IMPORTS = mfuuid UNIX_CFLAGS = $(FFMPEG_CFLAGS) UNIX_LIBS = $(FFMPEG_LIBS) $(PTHREAD_LIBS)
diff --git a/dlls/winedmo/main.c b/dlls/winedmo/main.c index ccb8a4eb95d..7536566fe75 100644 --- a/dlls/winedmo/main.c +++ b/dlls/winedmo/main.c @@ -16,8 +16,10 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#define COBJMACROS #include "unixlib.h" #include "winnls.h" +#include "mfidl.h"
#include "wine/debug.h"
@@ -114,6 +116,32 @@ BOOL WINAPI DllMain( HINSTANCE instance, DWORD reason, void *reserved ) }
+static void buffer_lock( DMO_OUTPUT_DATA_BUFFER *buffer, struct sample *sample ) +{ + BYTE *data; + HRESULT hr; + DWORD size; + + if (FAILED(hr = IMediaBuffer_GetBufferAndLength( buffer->pBuffer, &data, &size ))) + ERR( "Failed to get media buffer data %p, hr %#lx\n", buffer, hr ); + if (FAILED(hr = IMediaBuffer_GetMaxLength( buffer->pBuffer, &size ))) + ERR( "Failed to get media buffer max length %p, hr %#lx\n", buffer, hr ); + + sample->data = (UINT_PTR)data; + sample->size = size; +} + +static void buffer_unlock( DMO_OUTPUT_DATA_BUFFER *buffer, struct sample *sample ) +{ + HRESULT hr; + + if (FAILED(hr = IMediaBuffer_SetLength( buffer->pBuffer, sample->size ))) + ERR( "Failed to update buffer length, hr %#lx\n", hr ); + + buffer->dwStatus = 0; +} + + NTSTATUS CDECL winedmo_demuxer_check( const char *mime_type ) { struct demuxer_check_params params = {0}; @@ -180,6 +208,29 @@ NTSTATUS CDECL winedmo_demuxer_destroy( struct winedmo_demuxer *demuxer ) return status; }
+NTSTATUS CDECL winedmo_demuxer_read( struct winedmo_demuxer demuxer, UINT *stream, DMO_OUTPUT_DATA_BUFFER *buffer, UINT *buffer_size ) +{ + struct demuxer_read_params params = {.demuxer = demuxer}; + NTSTATUS status; + + TRACE( "demuxer %#I64x, stream %p, buffer %p, buffer_size %p\n", demuxer.handle, stream, buffer, buffer_size ); + + buffer_lock( buffer, ¶ms.sample ); + status = UNIX_CALL( demuxer_read, ¶ms ); + buffer_unlock( buffer, ¶ms.sample ); + *buffer_size = params.sample.size; + *stream = params.stream; + + if (status) + { + ERR( "Failed to read sample, status %#lx\n", status ); + return status; + } + + TRACE( "Got buffer %p, buffer_size %#x on stream %u\n", buffer->pBuffer, *buffer_size, *stream ); + return status; +} + NTSTATUS CDECL winedmo_demuxer_seek( struct winedmo_demuxer demuxer, INT64 timestamp ) { struct demuxer_seek_params params = {.demuxer = demuxer, .timestamp = timestamp}; diff --git a/dlls/winedmo/unix_demuxer.c b/dlls/winedmo/unix_demuxer.c index e14431e6d9c..d3e804aa6eb 100644 --- a/dlls/winedmo/unix_demuxer.c +++ b/dlls/winedmo/unix_demuxer.c @@ -159,6 +159,44 @@ NTSTATUS demuxer_destroy( void *arg ) return STATUS_SUCCESS; }
+NTSTATUS demuxer_read( void *arg ) +{ + struct demuxer_read_params *params = arg; + AVFormatContext *ctx = get_demuxer( params->demuxer ); + struct sample *sample = ¶ms->sample; + UINT capacity = params->sample.size; + AVPacket *packet; + int ret; + + TRACE( "context %p, capacity %#x\n", ctx, capacity ); + + if (!(packet = ctx->opaque)) + { + if (!(packet = av_packet_alloc())) return STATUS_NO_MEMORY; + if ((ret = av_read_frame( ctx, packet )) < 0) + { + TRACE( "Failed to read context %p, error %s.\n", ctx, debugstr_averr( ret ) ); + av_packet_free( &packet ); + if (ret == AVERROR_EOF) return STATUS_END_OF_FILE; + return STATUS_UNSUCCESSFUL; + } + } + + params->sample.size = packet->size; + if ((capacity < packet->size)) + { + ctx->opaque = packet; + return STATUS_BUFFER_TOO_SMALL; + } + + memcpy( (void *)(UINT_PTR)sample->data, packet->data, packet->size ); + params->stream = packet->stream_index; + av_packet_free( &packet ); + ctx->opaque = NULL; + + return STATUS_SUCCESS; +} + NTSTATUS demuxer_seek( void *arg ) { struct demuxer_seek_params *params = arg; diff --git a/dlls/winedmo/unix_private.h b/dlls/winedmo/unix_private.h index 77aa9058350..bc45f2c6db2 100644 --- a/dlls/winedmo/unix_private.h +++ b/dlls/winedmo/unix_private.h @@ -43,6 +43,7 @@ extern int unix_read_callback( void *opaque, uint8_t *buffer, int size ); extern NTSTATUS demuxer_check( void * ); extern NTSTATUS demuxer_create( void * ); extern NTSTATUS demuxer_destroy( void * ); +extern NTSTATUS demuxer_read( void * ); extern NTSTATUS demuxer_seek( void * ); extern NTSTATUS demuxer_stream_lang( void * ); extern NTSTATUS demuxer_stream_name( void * ); diff --git a/dlls/winedmo/unixlib.c b/dlls/winedmo/unixlib.c index 119853ec457..15500ee10b4 100644 --- a/dlls/winedmo/unixlib.c +++ b/dlls/winedmo/unixlib.c @@ -132,6 +132,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = X( demuxer_check ), X( demuxer_create ), X( demuxer_destroy ), + X( demuxer_read ), X( demuxer_seek ), X( demuxer_stream_lang ), X( demuxer_stream_name ), @@ -194,6 +195,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = X( demuxer_check ), X64( demuxer_create ), X64( demuxer_destroy ), + X( demuxer_read ), X( demuxer_seek ), X( demuxer_stream_lang ), X( demuxer_stream_name ), diff --git a/dlls/winedmo/unixlib.h b/dlls/winedmo/unixlib.h index fd250e58209..4e3fef9fa82 100644 --- a/dlls/winedmo/unixlib.h +++ b/dlls/winedmo/unixlib.h @@ -21,6 +21,7 @@
#include <stddef.h> #include <stdarg.h> +#include <stdint.h>
#include "ntstatus.h" #define WIN32_NO_STATUS @@ -63,6 +64,13 @@ struct read_callback_params };
+struct sample +{ + UINT64 size; + UINT64 data; /* pointer to user memory */ +}; + + struct media_type { GUID major; @@ -98,6 +106,13 @@ struct demuxer_destroy_params struct stream_context *context; };
+struct demuxer_read_params +{ + struct winedmo_demuxer demuxer; + UINT32 stream; + struct sample sample; +}; + struct demuxer_seek_params { struct winedmo_demuxer demuxer; @@ -133,6 +148,7 @@ enum unix_funcs unix_demuxer_check, unix_demuxer_create, unix_demuxer_destroy, + unix_demuxer_read, unix_demuxer_seek, unix_demuxer_stream_lang, unix_demuxer_stream_name, diff --git a/dlls/winedmo/winedmo.spec b/dlls/winedmo/winedmo.spec index 77772b1f472..90eff030dfd 100644 --- a/dlls/winedmo/winedmo.spec +++ b/dlls/winedmo/winedmo.spec @@ -1,6 +1,7 @@ @ cdecl winedmo_demuxer_check(str) @ cdecl winedmo_demuxer_create(wstr ptr int64 ptr ptr ptr ptr) @ cdecl winedmo_demuxer_destroy(ptr) +@ cdecl winedmo_demuxer_read(int64 ptr ptr ptr) @ cdecl winedmo_demuxer_seek(int64 int64) @ cdecl winedmo_demuxer_stream_lang(int64 long ptr long) @ cdecl winedmo_demuxer_stream_name(int64 long ptr long) diff --git a/include/wine/winedmo.h b/include/wine/winedmo.h index f4f9d1371d4..c068dfa1de8 100644 --- a/include/wine/winedmo.h +++ b/include/wine/winedmo.h @@ -46,6 +46,7 @@ NTSTATUS CDECL winedmo_demuxer_check( const char *mime_type ); NTSTATUS CDECL winedmo_demuxer_create( const WCHAR *url, struct winedmo_stream *stream, UINT64 stream_size, INT64 *duration, UINT *stream_count, WCHAR *mime_type, struct winedmo_demuxer *demuxer ); NTSTATUS CDECL winedmo_demuxer_destroy( struct winedmo_demuxer *demuxer ); +NTSTATUS CDECL winedmo_demuxer_read( struct winedmo_demuxer demuxer, UINT *stream, DMO_OUTPUT_DATA_BUFFER *buffer, UINT *buffer_size ); NTSTATUS CDECL winedmo_demuxer_seek( struct winedmo_demuxer demuxer, INT64 timestamp ); NTSTATUS CDECL winedmo_demuxer_stream_lang( struct winedmo_demuxer demuxer, UINT stream, WCHAR *buffer, UINT len ); NTSTATUS CDECL winedmo_demuxer_stream_name( struct winedmo_demuxer demuxer, UINT stream, WCHAR *buffer, UINT len );
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winedmo/main.c | 14 ++++++++++++++ dlls/winedmo/unix_demuxer.c | 6 ++++++ dlls/winedmo/unixlib.h | 10 ++++++++++ 3 files changed, 30 insertions(+)
diff --git a/dlls/winedmo/main.c b/dlls/winedmo/main.c index 7536566fe75..dc9829f25b2 100644 --- a/dlls/winedmo/main.c +++ b/dlls/winedmo/main.c @@ -133,12 +133,26 @@ static void buffer_lock( DMO_OUTPUT_DATA_BUFFER *buffer, struct sample *sample )
static void buffer_unlock( DMO_OUTPUT_DATA_BUFFER *buffer, struct sample *sample ) { + IMFSample *object; HRESULT hr;
if (FAILED(hr = IMediaBuffer_SetLength( buffer->pBuffer, sample->size ))) ERR( "Failed to update buffer length, hr %#lx\n", hr );
buffer->dwStatus = 0; + if (SUCCEEDED(hr = IMediaBuffer_QueryInterface( buffer->pBuffer, &IID_IMFSample, (void **)&object ))) + { + if (sample->dts != INT64_MIN) IMFSample_SetUINT64( object, &MFSampleExtension_DecodeTimestamp, sample->dts ); + if (sample->pts != INT64_MIN) IMFSample_SetSampleTime( object, sample->pts ); + if (sample->duration != INT64_MIN) IMFSample_SetSampleDuration( object, sample->duration ); + if (sample->flags & SAMPLE_FLAG_SYNC_POINT) IMFSample_SetUINT32( object, &MFSampleExtension_CleanPoint, 1 ); + IMFSample_Release( object ); + } + + if ((buffer->rtTimestamp = sample->pts) != INT64_MIN) buffer->dwStatus |= DMO_OUTPUT_DATA_BUFFERF_TIME; + if ((buffer->rtTimelength = sample->duration) != INT64_MIN) buffer->dwStatus |= DMO_OUTPUT_DATA_BUFFERF_TIMELENGTH; + if (sample->flags & SAMPLE_FLAG_SYNC_POINT) buffer->dwStatus |= DMO_OUTPUT_DATA_BUFFERF_SYNCPOINT; + if (sample->flags & SAMPLE_FLAG_INCOMPLETE) buffer->dwStatus |= DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE; }
diff --git a/dlls/winedmo/unix_demuxer.c b/dlls/winedmo/unix_demuxer.c index d3e804aa6eb..50be8688397 100644 --- a/dlls/winedmo/unix_demuxer.c +++ b/dlls/winedmo/unix_demuxer.c @@ -165,6 +165,7 @@ NTSTATUS demuxer_read( void *arg ) AVFormatContext *ctx = get_demuxer( params->demuxer ); struct sample *sample = ¶ms->sample; UINT capacity = params->sample.size; + AVStream *stream; AVPacket *packet; int ret;
@@ -189,6 +190,11 @@ NTSTATUS demuxer_read( void *arg ) return STATUS_BUFFER_TOO_SMALL; }
+ stream = ctx->streams[packet->stream_index]; + sample->pts = get_stream_time( stream, packet->pts ); + sample->dts = get_stream_time( stream, packet->dts ); + sample->duration = get_stream_time( stream, packet->duration ); + if (packet->flags & AV_PKT_FLAG_KEY) sample->flags |= SAMPLE_FLAG_SYNC_POINT; memcpy( (void *)(UINT_PTR)sample->data, packet->data, packet->size ); params->stream = packet->stream_index; av_packet_free( &packet ); diff --git a/dlls/winedmo/unixlib.h b/dlls/winedmo/unixlib.h index 4e3fef9fa82..cf37bd5342a 100644 --- a/dlls/winedmo/unixlib.h +++ b/dlls/winedmo/unixlib.h @@ -64,8 +64,18 @@ struct read_callback_params };
+enum sample_flag +{ + SAMPLE_FLAG_SYNC_POINT = 1, + SAMPLE_FLAG_INCOMPLETE = 2, +}; + struct sample { + UINT32 flags; + INT64 dts; + INT64 pts; + UINT64 duration; UINT64 size; UINT64 data; /* pointer to user memory */ };
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mfsrcsnk/media_source.c | 278 ++++++++++++++++++++++++++++++++++- 1 file changed, 276 insertions(+), 2 deletions(-)
diff --git a/dlls/mfsrcsnk/media_source.c b/dlls/mfsrcsnk/media_source.c index 5cc769b93b0..1ecf3ed1902 100644 --- a/dlls/mfsrcsnk/media_source.c +++ b/dlls/mfsrcsnk/media_source.c @@ -72,6 +72,62 @@ WINE_DEFAULT_DEBUG_CHANNEL(mfplat); #define DEFINE_MF_ASYNC_CALLBACK(type, name, base_iface) \ DEFINE_MF_ASYNC_CALLBACK_(type, name, type##_from_##name, type##_##name, name##_iface, &object->base_iface)
+struct object_entry +{ + struct list entry; + IUnknown *object; +}; + +static void object_entry_destroy(struct object_entry *entry) +{ + if (entry->object) IUnknown_AddRef( entry->object ); + free(entry); +} + +static HRESULT object_entry_create(IUnknown *object, struct object_entry **out) +{ + struct object_entry *entry; + + if (!(entry = calloc(1, sizeof(*entry)))) return E_OUTOFMEMORY; + if ((entry->object = object)) IUnknown_AddRef( entry->object ); + + *out = entry; + return S_OK; +} + +static HRESULT object_queue_push(struct list *queue, IUnknown *object) +{ + struct object_entry *entry; + HRESULT hr; + + if (SUCCEEDED(hr = object_entry_create(object, &entry))) + list_add_tail(queue, &entry->entry); + + return hr; +} + +static HRESULT object_queue_pop(struct list *queue, IUnknown **object) +{ + struct object_entry *entry; + struct list *ptr; + + if (!(ptr = list_head(queue))) return E_PENDING; + entry = LIST_ENTRY(ptr, struct object_entry, entry); + + if ((*object = entry->object)) IUnknown_AddRef(*object); + list_remove(&entry->entry); + object_entry_destroy(entry); + + return S_OK; +} + +static void object_queue_clear(struct list *queue) +{ + IUnknown *object; + while (object_queue_pop(queue, &object) != E_PENDING) + if (object) IUnknown_Release(object); +} + struct async_start_params { IUnknown IUnknown_iface; @@ -158,8 +214,11 @@ struct media_stream IMFMediaSource *source; IMFMediaEventQueue *queue; IMFStreamDescriptor *descriptor; + struct list samples; + struct list tokens;
BOOL active; + BOOL eos; };
struct media_source @@ -172,6 +231,7 @@ struct media_source IMFAsyncCallback async_start_iface; IMFAsyncCallback async_stop_iface; IMFAsyncCallback async_pause_iface; + IMFAsyncCallback async_read_iface; LONG refcount;
CRITICAL_SECTION cs; @@ -205,6 +265,51 @@ static struct media_source *media_source_from_IMFMediaSource(IMFMediaSource *ifa return CONTAINING_RECORD(iface, struct media_source, IMFMediaSource_iface); }
+static HRESULT media_stream_send_sample(struct media_stream *stream, IMFSample *sample, IUnknown *token) +{ + HRESULT hr = S_OK; + + if (!token || SUCCEEDED(hr = IMFSample_SetUnknown(sample, &MFSampleExtension_Token, token))) + hr = IMFMediaEventQueue_QueueEventParamUnk(stream->queue, MEMediaSample, + &GUID_NULL, S_OK, (IUnknown *)sample); + + return hr; +} + +static struct media_stream *media_stream_from_index(struct media_source *source, UINT index) +{ + UINT i; + + for (i = 0; i < source->stream_count; i++) + if (source->stream_map[i] == index) + return source->streams[i]; + + WARN("Failed to find stream with index %u\n", index); + return NULL; +} + +static HRESULT media_source_send_sample(struct media_source *source, UINT index, IMFSample *sample) +{ + struct media_stream *stream; + IUnknown *token; + HRESULT hr; + + if (!(stream = media_stream_from_index(source, index)) || !stream->active) + return S_FALSE; + + if (SUCCEEDED(hr = object_queue_pop(&stream->tokens, &token))) + { + hr = media_stream_send_sample(stream, sample, token); + if (token) IUnknown_Release(token); + return hr; + } + + if (FAILED(hr = object_queue_push(&stream->samples, (IUnknown *)sample))) + return hr; + + return S_FALSE; +} + static void queue_media_event_object(IMFMediaEventQueue *queue, MediaEventType type, IUnknown *object) { HRESULT hr; @@ -219,6 +324,16 @@ static void queue_media_event_value(IMFMediaEventQueue *queue, MediaEventType ty ERR("Failed to queue event of type %#lx to queue %p, hr %#lx\n", type, queue, hr); }
+static void queue_media_source_read(struct media_source *source) +{ + HRESULT hr; + + if (source->pending_state) + return; + if (FAILED(hr = MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_STANDARD, &source->async_read_iface, NULL))) + ERR("Failed to queue async read for source %p, hr %#lx\n", source, hr); +} + static void media_stream_start(struct media_stream *stream, UINT index, const PROPVARIANT *position) { struct media_source *source = media_source_from_IMFMediaSource(stream->source); @@ -227,18 +342,42 @@ static void media_stream_start(struct media_stream *stream, UINT index, const PR
TRACE("stream %p, index %u, position %s\n", stream, index, debugstr_propvar(position));
+ if (position->vt != VT_EMPTY) + { + object_queue_clear(&stream->tokens); + object_queue_clear(&stream->samples); + } + if (stream->active) { queue_media_event_object(source->queue, was_active ? MEUpdatedStream : MENewStream, (IUnknown *)&stream->IMFMediaStream_iface); queue_media_event_value(stream->queue, seeking ? MEStreamSeeked : MEStreamStarted, position); } + + if (position->vt == VT_EMPTY && stream->active) + { + struct list samples = LIST_INIT(samples); + struct object_entry *object, *next; + IMFSample *sample; + + list_move_head(&samples, &stream->samples); + while (object_queue_pop(&samples, (IUnknown **)&sample) != E_PENDING) + { + media_source_send_sample(source, index, sample); + IMFSample_Release(sample); + } + + LIST_FOR_EACH_ENTRY_SAFE(object, next, &stream->tokens, struct object_entry, entry) + queue_media_source_read(source); + } }
static HRESULT media_source_start(struct media_source *source, IMFPresentationDescriptor *descriptor, GUID *format, PROPVARIANT *position) { BOOL starting = source->state == SOURCE_STOPPED, seeking = !starting && position->vt != VT_EMPTY; + NTSTATUS status; DWORD i, count; HRESULT hr;
@@ -257,6 +396,8 @@ static HRESULT media_source_start(struct media_source *source, IMFPresentationDe for (i = 0; i < source->stream_count; i++) { struct media_stream *stream = source->streams[i]; + if (position->vt != VT_EMPTY) + stream->eos = FALSE; stream->active = FALSE; }
@@ -292,6 +433,8 @@ static HRESULT media_source_start(struct media_source *source, IMFPresentationDe media_stream_start(stream, i, position); }
+ if (position->vt == VT_I8 && (status = winedmo_demuxer_seek(source->winedmo_demuxer, position->hVal.QuadPart))) + WARN("Failed to seek to %I64d, status %#lx\n", position->hVal.QuadPart, status); source->state = SOURCE_RUNNING;
queue_media_event_value(source->queue, seeking ? MESourceSeeked : MESourceStarted, position); @@ -324,6 +467,9 @@ static void media_stream_stop(struct media_stream *stream) { TRACE("stream %p\n", stream);
+ object_queue_clear(&stream->tokens); + object_queue_clear(&stream->samples); + if (stream->active) queue_media_event_value(stream->queue, MEStreamStopped, NULL); } @@ -410,6 +556,110 @@ static HRESULT media_source_async_pause(struct media_source *source, IMFAsyncRes
DEFINE_MF_ASYNC_CALLBACK(media_source, async_pause, IMFMediaSource_iface)
+static HRESULT create_media_buffer_sample(UINT buffer_size, IMFSample **sample, IMediaBuffer **media_buffer) +{ + IMFMediaBuffer *buffer; + HRESULT hr; + + if (FAILED(hr = MFCreateSample(sample))) + return hr; + if (SUCCEEDED(hr = MFCreateMemoryBuffer( buffer_size, &buffer ))) + { + if (SUCCEEDED(hr = IMFSample_AddBuffer(*sample, buffer))) + hr = MFCreateLegacyMediaBufferOnMFMediaBuffer(*sample, buffer, 0, media_buffer); + IMFMediaBuffer_Release(buffer); + } + if (FAILED(hr)) + { + IMFSample_Release(*sample); + *sample = NULL; + } + return hr; +} + +static HRESULT demuxer_read_sample(struct winedmo_demuxer demuxer, UINT *index, IMFSample **out) +{ + UINT buffer_size = 0x1000; + IMFSample *sample; + HRESULT hr; + + do + { + DMO_OUTPUT_DATA_BUFFER output = {0}; + NTSTATUS status; + + if (FAILED(hr = create_media_buffer_sample(buffer_size, &sample, &output.pBuffer))) + return hr; + if ((status = winedmo_demuxer_read(demuxer, index, &output, &buffer_size))) + { + if (status == STATUS_BUFFER_TOO_SMALL) hr = E_PENDING; + else if (status == STATUS_END_OF_FILE) hr = MF_E_END_OF_STREAM; + else hr = HRESULT_FROM_NT(status); + IMFSample_Release(sample); + sample = NULL; + } + IMediaBuffer_Release(output.pBuffer); + } + while (hr == E_PENDING); + + *out = sample; + return hr; +} + +static HRESULT media_source_read(struct media_source *source) +{ + IMFSample *sample; + UINT i, index; + HRESULT hr; + + if (source->state != SOURCE_RUNNING) + return S_OK; + + if (FAILED(hr = demuxer_read_sample(source->winedmo_demuxer, &index, &sample)) && hr != MF_E_END_OF_STREAM) + { + WARN("Failed to read stream %u data, hr %#lx\n", index, hr); + return hr; + } + + if (hr == MF_E_END_OF_STREAM) + { + PROPVARIANT empty = {.vt = VT_EMPTY}; + + for (i = 0; i < source->stream_count; i++) + { + struct media_stream *stream = source->streams[i]; + if (stream->active) queue_media_event_value(stream->queue, MEEndOfStream, &empty); + stream->eos = TRUE; + } + + queue_media_event_value(source->queue, MEEndOfPresentation, &empty); + source->state = SOURCE_STOPPED; + return S_OK; + } + + if ((hr = media_source_send_sample(source, index, sample)) == S_FALSE) + queue_media_source_read(source); + IMFSample_Release(sample); + + return hr; +} + +static HRESULT media_source_async_read(struct media_source *source, IMFAsyncResult *result) +{ + HRESULT hr; + + EnterCriticalSection(&source->cs); + + if (FAILED(hr = media_source_read(source))) + WARN("Failed to request sample, hr %#lx\n", hr); + + LeaveCriticalSection(&source->cs); + + return S_OK; +} + +DEFINE_MF_ASYNC_CALLBACK(media_source, async_read, IMFMediaSource_iface) + static struct media_stream *media_stream_from_IMFMediaStream(IMFMediaStream *iface) { return CONTAINING_RECORD(iface, struct media_stream, IMFMediaStream_iface); @@ -541,8 +791,28 @@ static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IM static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token) { struct media_stream *stream = media_stream_from_IMFMediaStream(iface); - FIXME("stream %p, token %p, stub!\n", stream, token); - return E_NOTIMPL; + struct media_source *source = media_source_from_IMFMediaSource(stream->source); + IMFSample *sample; + HRESULT hr; + + TRACE("stream %p, token %p\n", stream, token); + + EnterCriticalSection(&source->cs); + + if (source->state == SOURCE_SHUTDOWN) + hr = MF_E_SHUTDOWN; + else if (!stream->active) + hr = MF_E_MEDIA_SOURCE_WRONGSTATE; + else if (stream->eos) + hr = MF_E_END_OF_STREAM; + else if (source->state == SOURCE_RUNNING && SUCCEEDED(hr = object_queue_pop(&stream->samples, (IUnknown **)&sample))) + hr = media_stream_send_sample(stream, sample, token); + else if (SUCCEEDED(hr = object_queue_push(&stream->tokens, token))) + queue_media_source_read(source); + + LeaveCriticalSection(&source->cs); + + return hr; }
static const IMFMediaStreamVtbl media_stream_vtbl = @@ -581,6 +851,9 @@ static HRESULT media_stream_create(IMFMediaSource *source, IMFStreamDescriptor * IMFMediaSource_AddRef((object->source = source)); IMFStreamDescriptor_AddRef((object->descriptor = descriptor));
+ list_init(&object->tokens); + list_init(&object->samples); + TRACE("Created stream object %p\n", object);
*out = object; @@ -1410,6 +1683,7 @@ static HRESULT media_source_create(const WCHAR *url, IMFByteStream *stream, IMFM source->async_start_iface.lpVtbl = &media_source_async_start_vtbl; source->async_stop_iface.lpVtbl = &media_source_async_stop_vtbl; source->async_pause_iface.lpVtbl = &media_source_async_pause_vtbl; + source->async_read_iface.lpVtbl = &media_source_async_read_vtbl; source->refcount = 1;
if (FAILED(hr = MFCreateEventQueue(&source->queue)))
So I could file BUG with this apply ? I have tested a game that video is original working in gstreamer backend, but it not working in winedmo
On Wed Sep 18 08:11:30 2024 +0000, Chunhao Hung wrote:
So I could file BUG with this apply ? I have tested a game that video is original working in gstreamer backend, but it not working in winedmo
Yes, please.
On Wed Sep 18 08:11:30 2024 +0000, Rémi Bernon wrote:
Yes, please.
here or bugzilla?
On Wed Sep 18 08:24:05 2024 +0000, Chunhao Hung wrote:
here or bugzilla?
On Bugzilla rather, except if this MR specifically is causing issues.
On Wed Sep 18 08:27:52 2024 +0000, Rémi Bernon wrote:
On Bugzilla rather, except if this MR specifically is causing issues.
I am actually testing winedmo feature not winegstreamer, still post on bugzilla? by the way, there are no component call winedmo on Bugzilla now.
On Wed Sep 18 08:31:00 2024 +0000, Chunhao Hung wrote:
I am actually testing winedmo feature not winegstreamer, still post on bugzilla? by the way, there are no component call winedmo on Bugzilla now.
Feel free to create bugs for things that are currently working and are not working with the `DisableGstByteStreamHandler = 1` registry setting. With this MR applied, although it would probably be better to wait until it is actually merged, but please don't use any other extra patches.
You can probably use mfplat for the component.
On Wed Sep 18 08:37:45 2024 +0000, Rémi Bernon wrote:
Feel free to create bugs for things that are currently working and are not working with the `DisableGstByteStreamHandler = 1` registry setting. With this MR applied, although it would probably be better to wait until it is actually merged, but please don't use any other extra patches. You can probably use mfplat for the component.
OK 👏👏👏
Emil Velikov (@xexaxo) commented about dlls/winedmo/unix_demuxer.c:
{
TRACE( "Failed to read context %p, error %s.\n", ctx, debugstr_averr( ret ) );
av_packet_free( &packet );
if (ret == AVERROR_EOF) return STATUS_END_OF_FILE;
return STATUS_UNSUCCESSFUL;
}
- }
- params->sample.size = packet->size;
- if ((capacity < packet->size))
- {
ctx->opaque = packet;
return STATUS_BUFFER_TOO_SMALL;
- }
- memcpy( (void *)(UINT_PTR)sample->data, packet->data, packet->size );
Assuming this is going to be a hot path, would it make sense to wrap sample->data instead of copying? Not sure if libav has API (flexible) for this though.
On Wed Sep 18 13:15:47 2024 +0000, Emil Velikov wrote:
Assuming this is going to be a hot path, would it make sense to wrap sample->data instead of copying? Not sure if libav has API (flexible) for this though. Edit: at a glance it seems like av_packet_from_data() could work
Maybe, but I don't think it matters so much here. It's just reading compressed samples here, so copy size stays relatively small.