Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/mfreadwrite/main.c | 167 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 163 insertions(+), 4 deletions(-)
diff --git a/dlls/mfreadwrite/main.c b/dlls/mfreadwrite/main.c index 56ec036089..34b394f77d 100644 --- a/dlls/mfreadwrite/main.c +++ b/dlls/mfreadwrite/main.c @@ -81,6 +81,7 @@ struct sample enum media_stream_state { STREAM_STATE_READY = 0, + STREAM_STATE_PLAYING, STREAM_STATE_EOS, };
@@ -94,6 +95,7 @@ struct media_stream { IMFMediaStream *stream; IMFMediaType *current; + IMFTransform *decoder; DWORD id; CRITICAL_SECTION cs; CONDITION_VARIABLE sample_event; @@ -404,6 +406,7 @@ static HRESULT source_reader_media_sample_handler(struct source_reader *reader, IMFSample_AddRef(pending_sample->sample);
EnterCriticalSection(&reader->streams[i].cs); + reader->streams[i].state = STREAM_STATE_PLAYING; list_add_tail(&reader->streams[i].samples, &pending_sample->entry); LeaveCriticalSection(&reader->streams[i].cs);
@@ -576,6 +579,8 @@ static ULONG WINAPI src_reader_Release(IMFSourceReader *iface) IMFMediaStream_Release(stream->stream); if (stream->current) IMFMediaType_Release(stream->current); + if (stream->decoder) + IMFTransform_Release(stream->decoder); DeleteCriticalSection(&stream->cs);
LIST_FOR_EACH_ENTRY_SAFE(ptr, next, &stream->samples, struct sample, entry) @@ -755,6 +760,14 @@ static HRESULT WINAPI src_reader_SetCurrentMediaType(IMFSourceReader *iface, DWO IMFMediaType *type) { struct source_reader *reader = impl_from_IMFSourceReader(iface); + DWORD equal_flags; + GUID major_type, target_subtype, decoder_category; + IMFStreamDescriptor *stream_descriptor = NULL; + BOOL selected; + IMFMediaTypeHandler *stream_type_handler = NULL; + IMFMediaType *native_stream_type = NULL, *potential_out_stream_type = NULL; + CLSID decoder_id; + IMFTransform *decoder = NULL; HRESULT hr;
TRACE("%p, %#x, %p, %p.\n", iface, index, reserved, type); @@ -773,15 +786,161 @@ static HRESULT WINAPI src_reader_SetCurrentMediaType(IMFSourceReader *iface, DWO
if (index >= reader->stream_count) return MF_E_INVALIDSTREAMNUMBER; - - /* FIXME: validate passed type and current presentation state. */ + /* FIXME: validate current presentation state. */
EnterCriticalSection(&reader->cs);
- hr = IMFMediaType_CopyAllItems(type, (IMFAttributes *)reader->streams[index].current); + if (FAILED(hr = IMFMediaType_IsEqual(type, reader->streams[index].current, &equal_flags))) + goto done;
- LeaveCriticalSection(&reader->cs); + if (!(equal_flags & MF_MEDIATYPE_EQUAL_MAJOR_TYPES)) + { + hr = MF_E_INVALIDMEDIATYPE; + goto done; + } + + /* Testing reveals that setting the output type while streaming triggers a flush */ + if (reader->streams[index].state != STREAM_STATE_READY) + { + if (FAILED(hr = IMFSourceReader_Flush(iface, index))) + goto done; + } + + if (equal_flags & MF_MEDIATYPE_EQUAL_FORMAT_TYPES) + { + /* Similar enough not to need to refind a decoder */ + goto done; + } + + if (FAILED(hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(reader->descriptor, index, &selected, &stream_descriptor))) + goto done; + + if (FAILED(hr = IMFStreamDescriptor_GetMediaTypeHandler(stream_descriptor, &stream_type_handler))) + goto done; + + /* first, check if it's a native type */ + if (SUCCEEDED(hr = IMFMediaTypeHandler_IsMediaTypeSupported(stream_type_handler, type, &native_stream_type))) + { + if (FAILED(hr = IMFMediaTypeHandler_SetCurrentMediaType(stream_type_handler, native_stream_type))) + goto done; + hr = IMFMediaType_CopyAllItems(native_stream_type, (IMFAttributes *)reader->streams[index].current); + goto done; + + IMFMediaType_Release(native_stream_type); + } + + /* TODO: should we check if the source type is compressed? */ + + if (IsEqualGUID(&major_type, &MFMediaType_Video)) + { + decoder_category = MFT_CATEGORY_VIDEO_DECODER; + } + else if (IsEqualGUID(&major_type, &MFMediaType_Audio)) + { + decoder_category = MFT_CATEGORY_AUDIO_DECODER; + } + else + { + hr = MF_E_TOPO_CODEC_NOT_FOUND; + goto done; + } + + if (FAILED(hr = IMFMediaType_GetMajorType(type, &major_type))) + goto done; + + if (FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &target_subtype))) + goto done; + + for (unsigned int i = 0; hr != MF_E_NO_MORE_TYPES; i++) + { + GUID source_subtype; + MFT_REGISTER_TYPE_INFO in_type, out_type; + CLSID *decoder_ids; + ULONG decoder_count; + + if (FAILED(hr = IMFSourceReader_GetNativeMediaType(iface, index, i, &native_stream_type))) + continue;
+ if (FAILED(hr = IMFMediaType_GetGUID(native_stream_type, &MF_MT_SUBTYPE, &source_subtype))) + goto done; + + in_type.guidMajorType = major_type; + in_type.guidSubtype = source_subtype; + + out_type.guidMajorType = major_type; + out_type.guidSubtype = target_subtype; + + if (FAILED(hr = MFTEnum(decoder_category, 0, &in_type, &out_type, NULL, &decoder_ids, &decoder_count)) || decoder_count == 0) + { + IMFMediaType_Release(native_stream_type); + native_stream_type = NULL; + continue; + } + + decoder_id = decoder_ids[0]; + CoTaskMemFree(decoder_ids); + + if (FAILED(hr = IMFMediaTypeHandler_SetCurrentMediaType(stream_type_handler, native_stream_type))) + goto done; + + break; + } + + if (!native_stream_type) + { + ERR("Failed to find decoder matching request\n"); + hr = MF_E_INVALIDREQUEST; + goto done; + } + + if (FAILED(hr = CoCreateInstance(&decoder_id, NULL, CLSCTX_INPROC_SERVER, &IID_IMFTransform, (void**)&decoder))) + goto done; + + if (FAILED(hr = IMFTransform_SetInputType(decoder, 0, native_stream_type, 0))) + goto done; + + /* find the relevant output type */ + for (unsigned int i = 0;;i++) + { + if (FAILED(hr = IMFTransform_GetOutputAvailableType(decoder, 0, i, &potential_out_stream_type))) + goto done; + + if (FAILED(hr = IMFMediaType_IsEqual(type, potential_out_stream_type, &equal_flags))) + goto done; + + if (equal_flags & MF_MEDIATYPE_EQUAL_FORMAT_TYPES) + { + if (FAILED(hr = IMFTransform_SetOutputType(decoder, 0, potential_out_stream_type, 0))) + goto done; + + if (FAILED(hr = IMFMediaType_CopyAllItems(potential_out_stream_type, (IMFAttributes *)reader->streams[index].current))) + goto done; + + break; + } + IMFMediaType_Release(potential_out_stream_type); + potential_out_stream_type = NULL; + } + + if (reader->streams[index].decoder) + { + IMFTransform_Release(reader->streams[index].decoder); + } + reader->streams[index].decoder = decoder; + decoder = NULL; + + done: + LeaveCriticalSection(&reader->cs); + if (stream_descriptor) + IMFStreamDescriptor_Release(stream_descriptor); + if (stream_type_handler) + IMFMediaTypeHandler_Release(stream_type_handler); + if (native_stream_type) + IMFMediaType_Release(native_stream_type); + if (decoder) + IMFTransform_Release(decoder); + if (potential_out_stream_type) + IMFMediaType_Release(potential_out_stream_type); return hr; }
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/mfreadwrite/main.c | 114 +++++++++++++++++++++++++++++++--------- 1 file changed, 90 insertions(+), 24 deletions(-)
diff --git a/dlls/mfreadwrite/main.c b/dlls/mfreadwrite/main.c index 34b394f77d..d69daddc65 100644 --- a/dlls/mfreadwrite/main.c +++ b/dlls/mfreadwrite/main.c @@ -963,7 +963,7 @@ static HRESULT WINAPI src_reader_SetCurrentPosition(IMFSourceReader *iface, REFG return IMFMediaSource_Start(reader->source, reader->descriptor, format, position); }
-static IMFSample *media_stream_pop_sample(struct media_stream *stream, DWORD *stream_flags) +static IMFSample *media_stream_pop_sample(struct media_stream *stream) { IMFSample *ret = NULL; struct list *head; @@ -976,8 +976,6 @@ static IMFSample *media_stream_pop_sample(struct media_stream *stream, DWORD *st heap_free(pending_sample); }
- *stream_flags = stream->state == STREAM_STATE_EOS ? MF_SOURCE_READERF_ENDOFSTREAM : 0; - return ret; }
@@ -1016,6 +1014,91 @@ static HRESULT source_reader_start_source(struct source_reader *reader) return hr; }
+static IMFSample *next_source_sample(struct media_stream *stream) +{ + IMFSample *ret; + HRESULT hr; + + EnterCriticalSection(&stream->cs); + + while (list_empty(&stream->samples) && stream->state != STREAM_STATE_EOS) + { + if (stream->stream) + { + if (FAILED(hr = IMFMediaStream_RequestSample(stream->stream, NULL))) + WARN("Sample request failed, hr %#x.\n", hr); + } + SleepConditionVariableCS(&stream->sample_event, &stream->cs, INFINITE); + } + + ret = media_stream_pop_sample(stream); + + LeaveCriticalSection(&stream->cs); + + return ret; +} + +static HRESULT next_sample(struct media_stream *stream, IMFSample **out_sample, DWORD *stream_flags, BOOL drain) +{ + HRESULT hr = S_OK; + + *stream_flags = 0; + *out_sample = NULL; + + if (stream->decoder) + { + DWORD status; + MFT_OUTPUT_DATA_BUFFER out_buffer = {}; + + out_buffer.dwStreamID = 0; + for(;;) + { + if (list_empty(&stream->samples) && (stream->state == STREAM_STATE_EOS || drain)) + { + hr = IMFTransform_ProcessMessage(stream->decoder, MFT_MESSAGE_COMMAND_DRAIN, 0); + if (FAILED(hr)) + return hr; + } + + hr = IMFTransform_ProcessOutput(stream->decoder, 0, 1, &out_buffer, &status); + if (hr == S_OK) + { + *out_sample = out_buffer.pSample; + return hr; + } + else if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) + { + IMFSample *next_sample = drain ? media_stream_pop_sample(stream) : next_source_sample(stream); + if (!next_sample) + { + *stream_flags = MF_SOURCE_READERF_ENDOFSTREAM; + return S_OK; + } + + hr = IMFTransform_ProcessInput(stream->decoder, 0, next_sample, 0); + if (hr == MF_E_NOTACCEPTING) + { + ERR("ProcessInput returned MF_E_NOTACCEPTING after ProcessOutput returned MF_E_TRANSFORM_NEED_MORE_INPUT\n"); + IMFSample_Release(next_sample); + } + if (hr != S_OK) + return hr; + } + else + { + return hr; + } + } + } + else + { + *out_sample = drain ? media_stream_pop_sample(stream) : next_source_sample(stream); + if (stream->state == STREAM_STATE_EOS) + *stream_flags = MF_SOURCE_READERF_ENDOFSTREAM; + return S_OK; + } +} + static HRESULT source_reader_read_sample(struct source_reader *reader, DWORD index, DWORD flags, DWORD *actual_index, DWORD *stream_flags, LONGLONG *timestamp, IMFSample **sample) { @@ -1062,31 +1145,14 @@ static HRESULT source_reader_read_sample(struct source_reader *reader, DWORD ind *actual_index = stream_index;
stream = &reader->streams[stream_index]; - - EnterCriticalSection(&stream->cs); - if (SUCCEEDED(hr = source_reader_start_source(reader))) { - if (!(flags & MF_SOURCE_READER_CONTROLF_DRAIN)) - { - while (list_empty(&stream->samples) && stream->state != STREAM_STATE_EOS) - { - if (stream->stream) - { - if (FAILED(hr = IMFMediaStream_RequestSample(stream->stream, NULL))) - WARN("Sample request failed, hr %#x.\n", hr); - } - SleepConditionVariableCS(&stream->sample_event, &stream->cs, INFINITE); - } - } - - *sample = media_stream_pop_sample(stream, stream_flags); + if (FAILED(hr = next_sample(stream, sample, stream_flags, flags & MF_SOURCE_READER_CONTROLF_DRAIN))) + WARN("Failed to get sample, hr = %#x.\n", hr); + else + TRACE("Got sample %p.\n", *sample); }
- LeaveCriticalSection(&stream->cs); - - TRACE("Got sample %p.\n", *sample); - if (timestamp) { /* TODO: it's possible timestamp has to be set for some events.