winegstreamer: Implement IMFMediaStream::RequestSample.
Signed-off-by: Derek Lesho <dlesho@codeweavers.com> Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
12f1afbffe
commit
03eaa2cc93
|
@ -615,19 +615,15 @@ todo_wine
|
||||||
get_event((IMFMediaEventGenerator *)video_stream, MEStreamStarted, NULL);
|
get_event((IMFMediaEventGenerator *)video_stream, MEStreamStarted, NULL);
|
||||||
sample_count = 10;
|
sample_count = 10;
|
||||||
|
|
||||||
/* Request one beyond EOS, otherwise EndOfStream isn't queued. */
|
for (i = 0; i < sample_count; ++i)
|
||||||
for (i = 0; i <= sample_count; ++i)
|
|
||||||
{
|
{
|
||||||
hr = IMFMediaStream_RequestSample(video_stream, NULL);
|
hr = IMFMediaStream_RequestSample(video_stream, NULL);
|
||||||
if (i == sample_count)
|
if (i == sample_count)
|
||||||
break;
|
break;
|
||||||
todo_wine
|
|
||||||
ok(hr == S_OK, "Failed to request sample %u, hr %#x.\n", i + 1, hr);
|
ok(hr == S_OK, "Failed to request sample %u, hr %#x.\n", i + 1, hr);
|
||||||
if (hr != S_OK)
|
if (hr != S_OK)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (FAILED(hr))
|
|
||||||
goto skip_source_tests;
|
|
||||||
|
|
||||||
for (i = 0; i < sample_count; ++i)
|
for (i = 0; i < sample_count; ++i)
|
||||||
{
|
{
|
||||||
|
@ -661,14 +657,25 @@ todo_wine
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i == sample_count)
|
if (i == sample_count)
|
||||||
|
{
|
||||||
|
IMFMediaEvent *event;
|
||||||
|
|
||||||
|
/* MEEndOfStream isn't queued until after a one request beyond the last frame is submitted */
|
||||||
|
Sleep(100);
|
||||||
|
hr = IMFMediaEventGenerator_GetEvent((IMFMediaEventGenerator *)video_stream, MF_EVENT_FLAG_NO_WAIT, &event);
|
||||||
|
ok (hr == MF_E_NO_EVENTS_AVAILABLE, "Unexpected hr %#x.\n", hr);
|
||||||
|
|
||||||
|
hr = IMFMediaStream_RequestSample(video_stream, NULL);
|
||||||
|
ok (hr == S_OK || hr == MF_E_END_OF_STREAM, "Unexpected hr %#x.\n", hr);
|
||||||
get_event((IMFMediaEventGenerator *)video_stream, MEEndOfStream, NULL);
|
get_event((IMFMediaEventGenerator *)video_stream, MEEndOfStream, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
hr = IMFMediaStream_RequestSample(video_stream, NULL);
|
hr = IMFMediaStream_RequestSample(video_stream, NULL);
|
||||||
ok(hr == MF_E_END_OF_STREAM, "Unexpected hr %#x.\n", hr);
|
ok(hr == MF_E_END_OF_STREAM, "Unexpected hr %#x.\n", hr);
|
||||||
|
|
||||||
get_event((IMFMediaEventGenerator *)mediasource, MEEndOfPresentation, NULL);
|
get_event((IMFMediaEventGenerator *)mediasource, MEEndOfPresentation, NULL);
|
||||||
|
|
||||||
skip_source_tests:
|
|
||||||
IMFMediaStream_Release(video_stream);
|
IMFMediaStream_Release(video_stream);
|
||||||
IMFMediaTypeHandler_Release(handler);
|
IMFMediaTypeHandler_Release(handler);
|
||||||
IMFPresentationDescriptor_Release(descriptor);
|
IMFPresentationDescriptor_Release(descriptor);
|
||||||
|
|
|
@ -727,7 +727,6 @@ static void test_source_reader(void)
|
||||||
|
|
||||||
hr = IMFSourceReader_ReadSample(reader, MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, &actual_index, &stream_flags,
|
hr = IMFSourceReader_ReadSample(reader, MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, &actual_index, &stream_flags,
|
||||||
×tamp, &sample);
|
×tamp, &sample);
|
||||||
todo_wine
|
|
||||||
ok(hr == S_OK, "Failed to get a sample, hr %#x.\n", hr);
|
ok(hr == S_OK, "Failed to get a sample, hr %#x.\n", hr);
|
||||||
if (hr != S_OK)
|
if (hr != S_OK)
|
||||||
goto skip_read_sample;
|
goto skip_read_sample;
|
||||||
|
@ -753,8 +752,23 @@ todo_wine
|
||||||
×tamp, &sample);
|
×tamp, &sample);
|
||||||
ok(hr == S_OK, "Failed to get a sample, hr %#x.\n", hr);
|
ok(hr == S_OK, "Failed to get a sample, hr %#x.\n", hr);
|
||||||
ok(actual_index == 0, "Unexpected stream index %u\n", actual_index);
|
ok(actual_index == 0, "Unexpected stream index %u\n", actual_index);
|
||||||
|
/* TODO: gstreamer outputs .wav sample in increments of 4096, instead of 4410 */
|
||||||
|
todo_wine
|
||||||
|
{
|
||||||
ok(stream_flags == MF_SOURCE_READERF_ENDOFSTREAM, "Unexpected stream flags %#x.\n", stream_flags);
|
ok(stream_flags == MF_SOURCE_READERF_ENDOFSTREAM, "Unexpected stream flags %#x.\n", stream_flags);
|
||||||
ok(!sample, "Unexpected sample object.\n");
|
ok(!sample, "Unexpected sample object.\n");
|
||||||
|
}
|
||||||
|
if(!stream_flags)
|
||||||
|
{
|
||||||
|
IMFSample_Release(sample);
|
||||||
|
|
||||||
|
hr = IMFSourceReader_ReadSample(reader, MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, &actual_index, &stream_flags,
|
||||||
|
×tamp, &sample);
|
||||||
|
ok(hr == S_OK, "Failed to get a sample, hr %#x.\n", hr);
|
||||||
|
ok(actual_index == 0, "Unexpected stream index %u\n", actual_index);
|
||||||
|
ok(stream_flags == MF_SOURCE_READERF_ENDOFSTREAM, "Unexpected stream flags %#x.\n", stream_flags);
|
||||||
|
ok(!sample, "Unexpected sample object.\n");
|
||||||
|
}
|
||||||
|
|
||||||
hr = IMFSourceReader_ReadSample(reader, MF_SOURCE_READER_FIRST_AUDIO_STREAM, MF_SOURCE_READER_CONTROLF_DRAIN,
|
hr = IMFSourceReader_ReadSample(reader, MF_SOURCE_READER_FIRST_AUDIO_STREAM, MF_SOURCE_READER_CONTROLF_DRAIN,
|
||||||
&actual_index, &stream_flags, ×tamp, &sample);
|
&actual_index, &stream_flags, ×tamp, &sample);
|
||||||
|
|
|
@ -80,6 +80,7 @@ extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj)
|
||||||
HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj) DECLSPEC_HIDDEN;
|
HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj) DECLSPEC_HIDDEN;
|
||||||
IMFMediaType *mf_media_type_from_caps(const GstCaps *caps) DECLSPEC_HIDDEN;
|
IMFMediaType *mf_media_type_from_caps(const GstCaps *caps) DECLSPEC_HIDDEN;
|
||||||
GstCaps *caps_from_mf_media_type(IMFMediaType *type) DECLSPEC_HIDDEN;
|
GstCaps *caps_from_mf_media_type(IMFMediaType *type) DECLSPEC_HIDDEN;
|
||||||
|
IMFSample *mf_sample_from_gst_buffer(GstBuffer *in) DECLSPEC_HIDDEN;
|
||||||
|
|
||||||
HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj) DECLSPEC_HIDDEN;
|
HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj) DECLSPEC_HIDDEN;
|
||||||
|
|
||||||
|
|
|
@ -58,11 +58,13 @@ struct media_stream
|
||||||
STREAM_RUNNING,
|
STREAM_RUNNING,
|
||||||
} state;
|
} state;
|
||||||
DWORD stream_id;
|
DWORD stream_id;
|
||||||
|
BOOL eos;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum source_async_op
|
enum source_async_op
|
||||||
{
|
{
|
||||||
SOURCE_ASYNC_START,
|
SOURCE_ASYNC_START,
|
||||||
|
SOURCE_ASYNC_REQUEST_SAMPLE,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct source_async_command
|
struct source_async_command
|
||||||
|
@ -78,6 +80,11 @@ struct source_async_command
|
||||||
GUID format;
|
GUID format;
|
||||||
PROPVARIANT position;
|
PROPVARIANT position;
|
||||||
} start;
|
} start;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
struct media_stream *stream;
|
||||||
|
IUnknown *token;
|
||||||
|
} request_sample;
|
||||||
} u;
|
} u;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -311,6 +318,8 @@ static void start_pipeline(struct media_source *source, struct source_async_comm
|
||||||
GST_SEEK_TYPE_SET, position->u.hVal.QuadPart / 100, GST_SEEK_TYPE_NONE, 0);
|
GST_SEEK_TYPE_SET, position->u.hVal.QuadPart / 100, GST_SEEK_TYPE_NONE, 0);
|
||||||
|
|
||||||
gst_pad_push_event(stream->my_sink, seek_event);
|
gst_pad_push_event(stream->my_sink, seek_event);
|
||||||
|
|
||||||
|
stream->eos = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selected)
|
if (selected)
|
||||||
|
@ -334,6 +343,61 @@ static void start_pipeline(struct media_source *source, struct source_async_comm
|
||||||
gst_element_set_state(source->container, GST_STATE_PLAYING);
|
gst_element_set_state(source->container, GST_STATE_PLAYING);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void dispatch_end_of_presentation(struct media_source *source)
|
||||||
|
{
|
||||||
|
PROPVARIANT empty = {.vt = VT_EMPTY};
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
/* A stream has ended, check whether all have */
|
||||||
|
for (i = 0; i < source->stream_count; i++)
|
||||||
|
{
|
||||||
|
struct media_stream *stream = source->streams[i];
|
||||||
|
|
||||||
|
if (stream->state != STREAM_INACTIVE && !stream->eos)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MEEndOfPresentation, &GUID_NULL, S_OK, &empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wait_on_sample(struct media_stream *stream, IUnknown *token)
|
||||||
|
{
|
||||||
|
PROPVARIANT empty_var = {.vt = VT_EMPTY};
|
||||||
|
GstSample *gst_sample;
|
||||||
|
GstBuffer *buffer;
|
||||||
|
IMFSample *sample;
|
||||||
|
|
||||||
|
TRACE("%p, %p\n", stream, token);
|
||||||
|
|
||||||
|
g_signal_emit_by_name(stream->appsink, "pull-sample", &gst_sample);
|
||||||
|
if (gst_sample)
|
||||||
|
{
|
||||||
|
buffer = gst_sample_get_buffer(gst_sample);
|
||||||
|
|
||||||
|
TRACE("PTS = %llu\n", (unsigned long long int) GST_BUFFER_PTS(buffer));
|
||||||
|
|
||||||
|
sample = mf_sample_from_gst_buffer(buffer);
|
||||||
|
gst_sample_unref(gst_sample);
|
||||||
|
|
||||||
|
if (token)
|
||||||
|
IMFSample_SetUnknown(sample, &MFSampleExtension_Token, token);
|
||||||
|
|
||||||
|
IMFMediaEventQueue_QueueEventParamUnk(stream->event_queue, MEMediaSample, &GUID_NULL, S_OK, (IUnknown *)sample);
|
||||||
|
IMFSample_Release(sample);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_object_get(stream->appsink, "eos", &stream->eos, NULL);
|
||||||
|
if (stream->eos)
|
||||||
|
{
|
||||||
|
if (token)
|
||||||
|
IUnknown_Release(token);
|
||||||
|
IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, MEEndOfStream, &GUID_NULL, S_OK, &empty_var);
|
||||||
|
dispatch_end_of_presentation(stream->parent_source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static HRESULT WINAPI source_async_commands_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
|
static HRESULT WINAPI source_async_commands_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
|
||||||
{
|
{
|
||||||
struct media_source *source = impl_from_async_commands_callback_IMFAsyncCallback(iface);
|
struct media_source *source = impl_from_async_commands_callback_IMFAsyncCallback(iface);
|
||||||
|
@ -353,6 +417,9 @@ static HRESULT WINAPI source_async_commands_Invoke(IMFAsyncCallback *iface, IMFA
|
||||||
case SOURCE_ASYNC_START:
|
case SOURCE_ASYNC_START:
|
||||||
start_pipeline(source, command);
|
start_pipeline(source, command);
|
||||||
break;
|
break;
|
||||||
|
case SOURCE_ASYNC_REQUEST_SAMPLE:
|
||||||
|
wait_on_sample(command->u.request_sample.stream, command->u.request_sample.token);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
IUnknown_Release(state);
|
IUnknown_Release(state);
|
||||||
|
@ -640,13 +707,37 @@ static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IM
|
||||||
static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token)
|
static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token)
|
||||||
{
|
{
|
||||||
struct media_stream *stream = impl_from_IMFMediaStream(iface);
|
struct media_stream *stream = impl_from_IMFMediaStream(iface);
|
||||||
|
struct source_async_command *command;
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
TRACE("(%p)->(%p)\n", iface, token);
|
TRACE("(%p)->(%p)\n", iface, token);
|
||||||
|
|
||||||
if (stream->state == STREAM_SHUTDOWN)
|
if (stream->state == STREAM_SHUTDOWN)
|
||||||
return MF_E_SHUTDOWN;
|
return MF_E_SHUTDOWN;
|
||||||
|
|
||||||
return E_NOTIMPL;
|
if (stream->state == STREAM_INACTIVE)
|
||||||
|
{
|
||||||
|
WARN("Stream isn't active\n");
|
||||||
|
return MF_E_MEDIA_SOURCE_WRONGSTATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stream->eos)
|
||||||
|
{
|
||||||
|
return MF_E_END_OF_STREAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_REQUEST_SAMPLE, &command)))
|
||||||
|
{
|
||||||
|
command->u.request_sample.stream = stream;
|
||||||
|
if (token)
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const IMFMediaStreamVtbl media_stream_vtbl =
|
static const IMFMediaStreamVtbl media_stream_vtbl =
|
||||||
|
@ -729,6 +820,7 @@ static HRESULT new_media_stream(struct media_source *source, GstPad *pad, DWORD
|
||||||
object->stream_id = stream_id;
|
object->stream_id = stream_id;
|
||||||
|
|
||||||
object->state = STREAM_INACTIVE;
|
object->state = STREAM_INACTIVE;
|
||||||
|
object->eos = FALSE;
|
||||||
|
|
||||||
if (FAILED(hr = MFCreateEventQueue(&object->event_queue)))
|
if (FAILED(hr = MFCreateEventQueue(&object->event_queue)))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
|
@ -731,3 +731,72 @@ GstCaps *caps_from_mf_media_type(IMFMediaType *type)
|
||||||
FIXME("Unrecognized major type %s\n", debugstr_guid(&major_type));
|
FIXME("Unrecognized major type %s\n", debugstr_guid(&major_type));
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* IMFSample = GstBuffer
|
||||||
|
IMFBuffer = GstMemory */
|
||||||
|
|
||||||
|
/* TODO: Future optimization could be to create a custom
|
||||||
|
IMFMediaBuffer wrapper around GstMemory, and to utilize
|
||||||
|
gst_memory_new_wrapped on IMFMediaBuffer data. However,
|
||||||
|
this wouldn't work if we allow the callers to allocate
|
||||||
|
the buffers. */
|
||||||
|
|
||||||
|
IMFSample* mf_sample_from_gst_buffer(GstBuffer *gst_buffer)
|
||||||
|
{
|
||||||
|
IMFMediaBuffer *mf_buffer = NULL;
|
||||||
|
GstMapInfo map_info = {0};
|
||||||
|
LONGLONG duration, time;
|
||||||
|
BYTE *mapped_buf = NULL;
|
||||||
|
IMFSample *out = NULL;
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
if (FAILED(hr = MFCreateSample(&out)))
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
duration = GST_BUFFER_DURATION(gst_buffer);
|
||||||
|
time = GST_BUFFER_PTS(gst_buffer);
|
||||||
|
|
||||||
|
if (FAILED(hr = IMFSample_SetSampleDuration(out, duration / 100)))
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
if (FAILED(hr = IMFSample_SetSampleTime(out, time / 100)))
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
if (!gst_buffer_map(gst_buffer, &map_info, GST_MAP_READ))
|
||||||
|
{
|
||||||
|
hr = E_FAIL;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FAILED(hr = MFCreateMemoryBuffer(map_info.maxsize, &mf_buffer)))
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
if (FAILED(hr = IMFMediaBuffer_Lock(mf_buffer, &mapped_buf, NULL, NULL)))
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
memcpy(mapped_buf, map_info.data, map_info.size);
|
||||||
|
|
||||||
|
if (FAILED(hr = IMFMediaBuffer_Unlock(mf_buffer)))
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
if (FAILED(hr = IMFMediaBuffer_SetCurrentLength(mf_buffer, map_info.size)))
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
if (FAILED(hr = IMFSample_AddBuffer(out, mf_buffer)))
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (mf_buffer)
|
||||||
|
IMFMediaBuffer_Release(mf_buffer);
|
||||||
|
if (map_info.data)
|
||||||
|
gst_buffer_unmap(gst_buffer, &map_info);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
ERR("Failed to copy IMFSample to GstBuffer, hr = %#x\n", hr);
|
||||||
|
if (out)
|
||||||
|
IMFSample_Release(out);
|
||||||
|
out = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue