Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/winegstreamer/gst_private.h | 3 ++ dlls/winegstreamer/mfplat.c | 81 ++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+)
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index e6fb841fc8..7d693ced9a 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -36,6 +36,7 @@ #include "winuser.h" #include "dshow.h" #include "strmif.h" +#include "mfobjects.h" #include "wine/heap.h" #include "wine/strmbase.h"
@@ -54,4 +55,6 @@ void start_dispatch_thread(void) DECLSPEC_HIDDEN;
extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) DECLSPEC_HIDDEN;
+IMFMediaType* media_type_from_caps(GstCaps *caps); + #endif /* __GST_PRIVATE_INCLUDED__ */ diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 55b9b08876..4af50624e0 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -16,6 +16,11 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#include "config.h" +#include <gst/gst.h> + +#include "gst_private.h" + #include <stdarg.h>
#include "gst_private.h" @@ -433,3 +438,79 @@ HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj)
return CLASS_E_CLASSNOTAVAILABLE; } + +/* IMPORTANT: caps will be modified to represent the exact type needed for the format */ +IMFMediaType* media_type_from_caps(GstCaps *caps) +{ + IMFMediaType *media_type; + GstStructure *info; + const char *mime_type; + gchar *human_readable; + + human_readable = gst_caps_to_string(caps); + TRACE("caps = %s\n", human_readable); + g_free(human_readable); + + if (FAILED(MFCreateMediaType(&media_type))) + { + return NULL; + } + + info = gst_caps_get_structure(caps, 0); + mime_type = gst_structure_get_name(info); + + if (!(strncmp(mime_type, "video", 5))) + { + gint width, height, framerate_num, framerate_den; + + IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video); + + if (gst_structure_get_int(info, "width", &width) && gst_structure_get_int(info, "height", &height)) + { + IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_SIZE, ((UINT64)width << 32) | height); + } + if (gst_structure_get_fraction(info, "framerate", &framerate_num, &framerate_den)) + { + IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_RATE, ((UINT64)framerate_num << 32) | framerate_den); + } + + if (!(strcmp(mime_type, "video/x-raw"))) + { + const char *fourcc = gst_structure_get_string(info, "stream-format"); + IMFMediaType_SetUINT32(media_type, &MF_MT_COMPRESSED, FALSE); + if (fourcc && (strlen(fourcc) == 4)) + { + GUID fourcc_subtype = MFVideoFormat_Base; + fourcc_subtype.Data1 = MAKEFOURCC( + toupper(fourcc[0]), toupper(fourcc[1]), toupper(fourcc[2]), toupper(fourcc[3])); + IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &fourcc_subtype); + } + else + ERR("uncompressed video has no stream-format\n"); + } + else + ERR("Unrecognized video format %s\n", mime_type); + } + else if (!(strncmp(mime_type, "audio", 5))) + { + IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio); + + if (!(strcmp(mime_type, "audio/x-raw"))) + { + IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_Float); + + gst_caps_set_simple(caps, "format", G_TYPE_STRING, "F32LE", NULL); + } + else + ERR("Unrecognized audio format %s\n", mime_type); + } + else + { + goto fail; + } + + return media_type; + fail: + IMFMediaType_Release(media_type); + return NULL; +}
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/winegstreamer/mfplat.c | 64 +++++++++++++++++++++++++++++++++++++ include/codecapi.h | 38 ++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 include/codecapi.h
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 4af50624e0..2ac0f7fd13 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -26,6 +26,7 @@ #include "gst_private.h" #include "mfapi.h" #include "mfidl.h" +#include "codecapi.h"
#include "wine/debug.h" #include "wine/heap.h" @@ -488,6 +489,69 @@ IMFMediaType* media_type_from_caps(GstCaps *caps) else ERR("uncompressed video has no stream-format\n"); } + else if (!(strcmp(mime_type, "video/x-h264"))) + { + const char *profile, *level; + + IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_H264); + IMFMediaType_SetUINT32(media_type, &MF_MT_COMPRESSED, TRUE); + + if ((profile = gst_structure_get_string(info, "profile"))) + { + if (!(strcmp(profile, "main"))) + IMFMediaType_SetUINT32(media_type, &MF_MT_MPEG2_PROFILE, eAVEncH264VProfile_Main); + else if (!(strcmp(profile, "high"))) + IMFMediaType_SetUINT32(media_type, &MF_MT_MPEG2_PROFILE, eAVEncH264VProfile_High); + else if (!(strcmp(profile, "high-4:4:4"))) + IMFMediaType_SetUINT32(media_type, &MF_MT_MPEG2_PROFILE, eAVEncH264VProfile_444); + else + ERR("Unrecognized profile %s\n", profile); + } + if ((level = gst_structure_get_string(info, "level"))) + { + if (!(strcmp(level, "1"))) + IMFMediaType_SetUINT32(media_type, &MF_MT_MPEG2_LEVEL, eAVEncH264VLevel1); + else if (!(strcmp(level, "1.1"))) + IMFMediaType_SetUINT32(media_type, &MF_MT_MPEG2_LEVEL, eAVEncH264VLevel1_1); + else if (!(strcmp(level, "1.2"))) + IMFMediaType_SetUINT32(media_type, &MF_MT_MPEG2_LEVEL, eAVEncH264VLevel1_2); + else if (!(strcmp(level, "1.3"))) + IMFMediaType_SetUINT32(media_type, &MF_MT_MPEG2_LEVEL, eAVEncH264VLevel1_3); + else if (!(strcmp(level, "2"))) + IMFMediaType_SetUINT32(media_type, &MF_MT_MPEG2_LEVEL, eAVEncH264VLevel2); + else if (!(strcmp(level, "2.1"))) + IMFMediaType_SetUINT32(media_type, &MF_MT_MPEG2_LEVEL, eAVEncH264VLevel2_1); + else if (!(strcmp(level, "2.2"))) + IMFMediaType_SetUINT32(media_type, &MF_MT_MPEG2_LEVEL, eAVEncH264VLevel2_2); + else if (!(strcmp(level, "3"))) + IMFMediaType_SetUINT32(media_type, &MF_MT_MPEG2_LEVEL, eAVEncH264VLevel3); + else if (!(strcmp(level, "3.1"))) + IMFMediaType_SetUINT32(media_type, &MF_MT_MPEG2_LEVEL, eAVEncH264VLevel3_1); + else if (!(strcmp(level, "3.2"))) + IMFMediaType_SetUINT32(media_type, &MF_MT_MPEG2_LEVEL, eAVEncH264VLevel3_2); + else if (!(strcmp(level, "4"))) + IMFMediaType_SetUINT32(media_type, &MF_MT_MPEG2_LEVEL, eAVEncH264VLevel4); + else if (!(strcmp(level, "4.1"))) + IMFMediaType_SetUINT32(media_type, &MF_MT_MPEG2_LEVEL, eAVEncH264VLevel4_1); + else if (!(strcmp(level, "4.2"))) + IMFMediaType_SetUINT32(media_type, &MF_MT_MPEG2_LEVEL, eAVEncH264VLevel4_2); + else if (!(strcmp(level, "5"))) + IMFMediaType_SetUINT32(media_type, &MF_MT_MPEG2_LEVEL, eAVEncH264VLevel5); + else if (!(strcmp(level, "5.1"))) + IMFMediaType_SetUINT32(media_type, &MF_MT_MPEG2_LEVEL, eAVEncH264VLevel5_1); + else if (!(strcmp(level, "5.2"))) + IMFMediaType_SetUINT32(media_type, &MF_MT_MPEG2_LEVEL, eAVEncH264VLevel5_2); + else + ERR("Unrecognized level %s\n", level); + } + gst_caps_set_simple(caps, "stream-format", G_TYPE_STRING, "byte-stream", NULL); + gst_caps_set_simple(caps, "alignment", G_TYPE_STRING, "au", NULL); + for (unsigned int i = 0; i < gst_caps_get_size(caps); i++) + { + GstStructure *structure = gst_caps_get_structure (caps, i); + gst_structure_remove_field(structure, "codec_data"); + } + } else ERR("Unrecognized video format %s\n", mime_type); } diff --git a/include/codecapi.h b/include/codecapi.h new file mode 100644 index 0000000000..307f40cb27 --- /dev/null +++ b/include/codecapi.h @@ -0,0 +1,38 @@ +#ifndef __WINE_CODECAPI_H +#define __WINE_CODECAPI_H + +enum eAVEncH264VProfile +{ + eAVEncH264VProfile_unknown = 0, + eAVEncH264VProfile_Simple = 66, + eAVEncH264VProfile_Base = 66, + eAVEncH264VProfile_Main = 77, + eAVEncH264VProfile_High = 100, + eAVEncH264VProfile_422 = 122, + eAVEncH264VProfile_High10 = 110, + eAVEncH264VProfile_444 = 244, + eAVEncH264VProfile_Extended = 88, +}; + +enum eAVEncH264VLevel +{ + eAVEncH264VLevel1 = 10, + eAVEncH264VLevel1_b = 11, + eAVEncH264VLevel1_1 = 11, + eAVEncH264VLevel1_2 = 12, + eAVEncH264VLevel1_3 = 13, + eAVEncH264VLevel2 = 20, + eAVEncH264VLevel2_1 = 21, + eAVEncH264VLevel2_2 = 22, + eAVEncH264VLevel3 = 30, + eAVEncH264VLevel3_1 = 31, + eAVEncH264VLevel3_2 = 32, + eAVEncH264VLevel4 = 40, + eAVEncH264VLevel4_1 = 41, + eAVEncH264VLevel4_2 = 42, + eAVEncH264VLevel5 = 50, + eAVEncH264VLevel5_1 = 51, + eAVEncH264VLevel5_2 = 52 +}; + +#endif \ No newline at end of file
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/winegstreamer/mfplat.c | 42 +++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+)
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 2ac0f7fd13..a56ec1d6e6 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -552,6 +552,48 @@ IMFMediaType* media_type_from_caps(GstCaps *caps) gst_structure_remove_field(structure, "codec_data"); } } + else if (!(strcmp(mime_type, "video/x-wmv"))) + { + gint wmv_version; + const char *format; + const GValue *codec_data; + + if (gst_structure_get_int(info, "wmvversion", &wmv_version)) + { + switch (wmv_version) + { + case 1: + IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_WMV1); + break; + case 2: + IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_WMV2); + break; + case 3: + IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_WMV3); + break; + default: + ERR("Unrecognized wmvversion %d\n", wmv_version); + } + } + + if ((format = gst_structure_get_string(info, "format"))) + { + if (!(strcmp(format, "WVC1"))) + IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_WVC1); + } + + if ((codec_data = gst_structure_get_value(info, "codec_data"))) + { + GstBuffer *codec_data_buffer = gst_value_get_buffer(codec_data); + if (codec_data_buffer) + { + gsize codec_data_size = gst_buffer_get_size(codec_data_buffer); + gpointer codec_data_raw = heap_alloc(codec_data_size); + gst_buffer_extract(codec_data_buffer, 0, codec_data_raw, codec_data_size); + IMFMediaType_SetBlob(media_type, &MF_MT_USER_DATA, codec_data_raw, codec_data_size); + } + } + } else ERR("Unrecognized video format %s\n", mime_type); }
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/winegstreamer/mfplat.c | 119 ++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+)
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index a56ec1d6e6..c98c8b897a 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -440,6 +440,15 @@ HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) return CLASS_E_CLASSNOTAVAILABLE; }
+struct aac_user_data +{ + WORD payload_type; + WORD profile_level_indication; + WORD struct_type; + WORD reserved; + /*BYTE audio_specific_config;*/ +}; + /* IMPORTANT: caps will be modified to represent the exact type needed for the format */ IMFMediaType* media_type_from_caps(GstCaps *caps) { @@ -607,6 +616,116 @@ IMFMediaType* media_type_from_caps(GstCaps *caps)
gst_caps_set_simple(caps, "format", G_TYPE_STRING, "F32LE", NULL); } + else if (!(strcmp(mime_type, "audio/mpeg"))) + { + int mpeg_version = -1; + + IMFMediaType_SetUINT32(media_type, &MF_MT_COMPRESSED, TRUE); + + if (!(gst_structure_get_int(info, "mpegversion", &mpeg_version))) + ERR("Failed to get mpegversion\n"); + switch (mpeg_version) + { + case 1: + { + IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_MPEG); + break; + } + case 2: + case 4: + { + const char *format, *profile, *level; + DWORD profile_level_indication = 0; + const GValue *codec_data; + DWORD asc_size = 0; + struct aac_user_data *user_data = NULL; + + IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_AAC); + + codec_data = gst_structure_get_value(info, "codec_data"); + if (codec_data) + { + GstBuffer *codec_data_buffer = gst_value_get_buffer(codec_data); + if (codec_data_buffer) + { + if ((asc_size = gst_buffer_get_size(codec_data_buffer)) >= 2) + { + user_data = heap_alloc_zero(sizeof(*user_data)+asc_size); + gst_buffer_extract(codec_data_buffer, 0, (gpointer)(user_data + 1), asc_size); + } + else + ERR("Unexpected buffer size\n"); + } + else + ERR("codec_data not a buffer\n"); + } + else + ERR("codec_data not found\n"); + if (!user_data) + user_data = heap_alloc_zero(sizeof(*user_data)); + + { + int rate; + if (gst_structure_get_int(info, "rate", &rate)) + IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, rate); + } + { + int channels; + if (gst_structure_get_int(info, "channels", &channels)) + IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_NUM_CHANNELS, channels); + } + + if ((format = gst_structure_get_string(info, "stream-format"))) + { + DWORD payload_type = -1; + if (!(strcmp(format, "raw"))) + payload_type = 0; + else if (!(strcmp(format, "adts"))) + payload_type = 1; + else + ERR("Unrecognized stream-format\n"); + if (payload_type != -1) + { + IMFMediaType_SetUINT32(media_type, &MF_MT_AAC_PAYLOAD_TYPE, payload_type); + user_data->payload_type = payload_type; + } + } + else + { + ERR("Stream format not present\n"); + } + + profile = gst_structure_get_string(info, "profile"); + level = gst_structure_get_string(info, "level"); + /* Data from https://docs.microsoft.com/en-us/windows/win32/medfound/aac-encoder#output-t... */ + if (profile && level) + { + if (!(strcmp(profile, "lc")) && !(strcmp(level, "2"))) + profile_level_indication = 0x29; + else if (!(strcmp(profile, "lc")) && !(strcmp(level, "4"))) + profile_level_indication = 0x2A; + else if (!(strcmp(profile, "lc")) && !(strcmp(level, "5"))) + profile_level_indication = 0x2B; + else + ERR("Unhandled profile/level combo\n"); + } + else + ERR("Profile or level not present\n"); + + if (profile_level_indication) + { + IMFMediaType_SetUINT32(media_type, &MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION, profile_level_indication); + user_data->profile_level_indication = profile_level_indication; + } + + IMFMediaType_SetBlob(media_type, &MF_MT_USER_DATA, (BYTE *)user_data, sizeof(user_data) + asc_size); + heap_free(user_data); + break; + } + default: + ERR("Unhandled mpegversion %d\n", mpeg_version); + } + } else ERR("Unrecognized audio format %s\n", mime_type); }
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/winegstreamer/mfplat.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index c98c8b897a..16c1f3c6f9 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -603,6 +603,11 @@ IMFMediaType* media_type_from_caps(GstCaps *caps) } } } + else if (!(strcmp(mime_type, "video/mpeg"))) + { + IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_M4S2); + IMFMediaType_SetUINT32(media_type, &MF_MT_COMPRESSED, TRUE); + } else ERR("Unrecognized video format %s\n", mime_type); }
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/mfplat.c | 90 ++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+)
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 7d693ced9a..1e5c5afbd4 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -56,5 +56,6 @@ void start_dispatch_thread(void) DECLSPEC_HIDDEN; extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) DECLSPEC_HIDDEN;
IMFMediaType* media_type_from_caps(GstCaps *caps); +IMFSample* mf_sample_from_gst_buffer(GstBuffer *in);
#endif /* __GST_PRIVATE_INCLUDED__ */ diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 16c1f3c6f9..156f2578b3 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -744,3 +744,93 @@ IMFMediaType* media_type_from_caps(GstCaps *caps) IMFMediaType_Release(media_type); 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) +{ + IMFSample *out = NULL; + LONGLONG duration, time; + int buffer_count; + HRESULT hr; + + if (FAILED(hr = MFCreateSample(&out))) + goto fail; + + duration = GST_BUFFER_DURATION(gst_buffer); + time = GST_BUFFER_PTS(gst_buffer); + + if (FAILED(IMFSample_SetSampleDuration(out, duration / 100))) + goto fail; + + if (FAILED(IMFSample_SetSampleTime(out, time / 100))) + goto fail; + + buffer_count = gst_buffer_n_memory(gst_buffer); + + for (unsigned int i = 0; i < buffer_count; i++) + { + GstMemory *memory = gst_buffer_get_memory(gst_buffer, i); + IMFMediaBuffer *mf_buffer = NULL; + GstMapInfo map_info; + BYTE *buf_data; + + if (!memory) + { + hr = E_FAIL; + goto loop_done; + } + + if (!(gst_memory_map(memory, &map_info, GST_MAP_READ))) + { + hr = E_FAIL; + goto loop_done; + } + + if (FAILED(hr = MFCreateMemoryBuffer(map_info.maxsize, &mf_buffer))) + { + gst_memory_unmap(memory, &map_info); + goto loop_done; + } + + if (FAILED(hr = IMFMediaBuffer_Lock(mf_buffer, &buf_data, NULL, NULL))) + { + gst_memory_unmap(memory, &map_info); + goto loop_done; + } + + memcpy(buf_data, map_info.data, map_info.size); + + gst_memory_unmap(memory, &map_info); + + if (FAILED(hr = IMFMediaBuffer_Unlock(mf_buffer))) + goto loop_done; + + if (FAILED(hr = IMFMediaBuffer_SetCurrentLength(mf_buffer, map_info.size))) + goto loop_done; + + if (FAILED(hr = IMFSample_AddBuffer(out, mf_buffer))) + goto loop_done; + + loop_done: + if (mf_buffer) + IMFMediaBuffer_Release(mf_buffer); + if (memory) + gst_memory_unref(memory); + if (FAILED(hr)) + goto fail; + } + + return out; + fail: + ERR("Failed to copy IMFSample to GstBuffer, hr = %#x\n", hr); + IMFSample_Release(out); + return NULL; +}
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/mfplat/tests/mfplat.c | 6 +- dlls/winegstreamer/Makefile.in | 1 + dlls/winegstreamer/gst_private.h | 6 + dlls/winegstreamer/media_source.c | 679 +++++++++++++++++++ dlls/winegstreamer/mfplat.c | 6 + dlls/winegstreamer/winegstreamer.rgs | 32 + dlls/winegstreamer/winegstreamer_classes.idl | 7 + include/mfidl.idl | 1 + 8 files changed, 737 insertions(+), 1 deletion(-) create mode 100644 dlls/winegstreamer/media_source.c
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 497c451f72..851b049a4b 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -493,7 +493,10 @@ static void test_source_resolver(void) ok(obj_type == MF_OBJECT_MEDIASOURCE, "got %d\n", obj_type);
hr = IMFMediaSource_CreatePresentationDescriptor(mediasource, &descriptor); +todo_wine ok(hr == S_OK, "Failed to get presentation descriptor, hr %#x.\n", hr); + if (FAILED(hr)) + goto skip_source_tests; ok(descriptor != NULL, "got %p\n", descriptor);
hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(descriptor, 0, &selected, &sd); @@ -597,7 +600,8 @@ todo_wine
skip_source_tests:
- IMFPresentationDescriptor_Release(descriptor); + if (descriptor) + IMFPresentationDescriptor_Release(descriptor); IMFMediaSource_Release(mediasource); IMFByteStream_Release(stream);
diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index 2364ac84dc..f4f72c6e96 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -9,6 +9,7 @@ C_SRCS = \ gst_cbs.c \ gstdemux.c \ main.c \ + media_source.c \ mediatype.c \ mfplat.c \ pin.c \ diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 1e5c5afbd4..49691d1fbe 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -58,4 +58,10 @@ extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) IMFMediaType* media_type_from_caps(GstCaps *caps); IMFSample* mf_sample_from_gst_buffer(GstBuffer *in);
+enum source_type +{ + SOURCE_TYPE_MPEG_4, +}; +HRESULT container_stream_handler_construct(REFIID riid, void **obj, enum source_type); + #endif /* __GST_PRIVATE_INCLUDED__ */ diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c new file mode 100644 index 0000000000..13a8db9d16 --- /dev/null +++ b/dlls/winegstreamer/media_source.c @@ -0,0 +1,679 @@ +#include "gst_private.h" + +#include <stdarg.h> + +#define COBJMACROS +#define NONAMELESSUNION + +#include "mfapi.h" +#include "mferror.h" +#include "mfidl.h" + +#include "wine/debug.h" +#include "wine/heap.h" +#include "wine/list.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mfplat); + +struct media_source +{ + IMFMediaSource IMFMediaSource_iface; + LONG ref; + IMFMediaEventQueue *event_queue; +}; + +/* source */ + +static inline struct media_source *impl_from_IMFMediaSource(IMFMediaSource *iface) +{ + return CONTAINING_RECORD(iface, struct media_source, IMFMediaSource_iface); +} + +static HRESULT WINAPI media_source_QueryInterface(IMFMediaSource *iface, REFIID riid, void **out) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + + TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), out); + + if (IsEqualIID(riid, &IID_IMFMediaSource) || + IsEqualIID(riid, &IID_IMFMediaEventGenerator) || + IsEqualIID(riid, &IID_IUnknown)) + { + *out = &This->IMFMediaSource_iface; + } + else + { + FIXME("(%s, %p)\n", debugstr_guid(riid), out); + *out = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*out); + return S_OK; +} + +static ULONG WINAPI media_source_AddRef(IMFMediaSource *iface) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + ULONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%u\n", This, ref); + + return ref; +} + +static ULONG WINAPI media_source_Release(IMFMediaSource *iface) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%u\n", This, ref); + + if (!ref) + { + heap_free(This); + } + + return ref; +} + +static HRESULT WINAPI media_source_GetEvent(IMFMediaSource *iface, DWORD flags, IMFMediaEvent **event) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + + TRACE("(%p)->(%#x, %p)\n", This, flags, event); + + return IMFMediaEventQueue_GetEvent(This->event_queue, flags, event); +} + +static HRESULT WINAPI media_source_BeginGetEvent(IMFMediaSource *iface, IMFAsyncCallback *callback, IUnknown *state) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + + TRACE("(%p)->(%p, %p)\n", This, callback, state); + + return IMFMediaEventQueue_BeginGetEvent(This->event_queue, callback, state); +} + +static HRESULT WINAPI media_source_EndGetEvent(IMFMediaSource *iface, IMFAsyncResult *result, IMFMediaEvent **event) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + + TRACE("(%p)->(%p, %p)\n", This, result, event); + + return IMFMediaEventQueue_EndGetEvent(This->event_queue, result, event); +} + +static HRESULT WINAPI media_source_QueueEvent(IMFMediaSource *iface, MediaEventType event_type, REFGUID ext_type, + HRESULT hr, const PROPVARIANT *value) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + + TRACE("(%p)->(%d, %s, %#x, %p)\n", This, event_type, debugstr_guid(ext_type), hr, value); + + return IMFMediaEventQueue_QueueEventParamVar(This->event_queue, event_type, ext_type, hr, value); +} + +static HRESULT WINAPI media_source_GetCharacteristics(IMFMediaSource *iface, DWORD *characteristics) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + + FIXME("(%p)->(%p): stub\n", This, characteristics); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource *iface, IMFPresentationDescriptor **descriptor) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + + FIXME("(%p)->(%p): stub\n", This, descriptor); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_source_Start(IMFMediaSource *iface, IMFPresentationDescriptor *descriptor, + const GUID *time_format, const PROPVARIANT *start_position) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + + FIXME("(%p)->(%p, %p, %p): stub\n", This, descriptor, time_format, start_position); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_source_Stop(IMFMediaSource *iface) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + + FIXME("(%p): stub\n", This); + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_source_Pause(IMFMediaSource *iface) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + + FIXME("(%p): stub\n", This); + + return E_NOTIMPL; +} + +static HRESULT media_source_teardown(struct media_source *This) +{ + if (This->event_queue) + IMFMediaEventQueue_Release(This->event_queue); + + return S_OK; +} + +static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) +{ + struct media_source *This = impl_from_IMFMediaSource(iface); + + FIXME("(%p): stub\n", This); + + return E_NOTIMPL; +} + +static const IMFMediaSourceVtbl IMFMediaSource_vtbl = +{ + media_source_QueryInterface, + media_source_AddRef, + media_source_Release, + media_source_GetEvent, + media_source_BeginGetEvent, + media_source_EndGetEvent, + media_source_QueueEvent, + media_source_GetCharacteristics, + media_source_CreatePresentationDescriptor, + media_source_Start, + media_source_Stop, + media_source_Pause, + media_source_Shutdown, +}; + +static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_type type, struct media_source **out_media_source) +{ + struct media_source *This = heap_alloc_zero(sizeof(*This)); + HRESULT hr; + + if (!This) + return E_OUTOFMEMORY; + + if (FAILED(hr = MFCreateEventQueue(&This->event_queue))) + goto fail; + + This->IMFMediaSource_iface.lpVtbl = &IMFMediaSource_vtbl; + This->ref = 1; + + *out_media_source = This; + return S_OK; + + fail: + WARN("Failed to construct MFMediaSource, hr %#x.\n", hr); + + media_source_teardown(This); + heap_free(This); + return hr; +} + +/* IMFByteStreamHandler */ + +struct container_stream_handler_result +{ + struct list entry; + IMFAsyncResult *result; + MF_OBJECT_TYPE obj_type; + IUnknown *object; +}; + +struct container_stream_handler +{ + IMFByteStreamHandler IMFByteStreamHandler_iface; + IMFAsyncCallback IMFAsyncCallback_iface; + LONG refcount; + enum source_type type; + struct list results; + CRITICAL_SECTION cs; +}; + +static struct container_stream_handler *impl_from_IMFByteStreamHandler(IMFByteStreamHandler *iface) +{ + return CONTAINING_RECORD(iface, struct container_stream_handler, IMFByteStreamHandler_iface); +} + +static struct container_stream_handler *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct container_stream_handler, IMFAsyncCallback_iface); +} + +static HRESULT WINAPI container_stream_handler_QueryInterface(IMFByteStreamHandler *iface, REFIID riid, void **obj) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IMFByteStreamHandler) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFByteStreamHandler_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI container_stream_handler_AddRef(IMFByteStreamHandler *iface) +{ + struct container_stream_handler *handler = impl_from_IMFByteStreamHandler(iface); + ULONG refcount = InterlockedIncrement(&handler->refcount); + + TRACE("%p, refcount %u.\n", handler, refcount); + + return refcount; +} + +static ULONG WINAPI container_stream_handler_Release(IMFByteStreamHandler *iface) +{ + struct container_stream_handler *handler = impl_from_IMFByteStreamHandler(iface); + ULONG refcount = InterlockedDecrement(&handler->refcount); + struct container_stream_handler_result *result, *next; + + TRACE("%p, refcount %u.\n", iface, refcount); + + if (!refcount) + { + LIST_FOR_EACH_ENTRY_SAFE(result, next, &handler->results, struct container_stream_handler_result, entry) + { + list_remove(&result->entry); + IMFAsyncResult_Release(result->result); + if (result->object) + IUnknown_Release(result->object); + heap_free(result); + } + DeleteCriticalSection(&handler->cs); + heap_free(handler); + } + + return refcount; +} + +struct create_object_context +{ + IUnknown IUnknown_iface; + LONG refcount; + + IPropertyStore *props; + IMFByteStream *stream; + WCHAR *url; + DWORD flags; +}; + +static struct create_object_context *impl_from_IUnknown(IUnknown *iface) +{ + return CONTAINING_RECORD(iface, struct create_object_context, IUnknown_iface); +} + +static HRESULT WINAPI create_object_context_QueryInterface(IUnknown *iface, REFIID riid, void **obj) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IUnknown_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI create_object_context_AddRef(IUnknown *iface) +{ + struct create_object_context *context = impl_from_IUnknown(iface); + ULONG refcount = InterlockedIncrement(&context->refcount); + + TRACE("%p, refcount %u.\n", iface, refcount); + + return refcount; +} + +static ULONG WINAPI create_object_context_Release(IUnknown *iface) +{ + struct create_object_context *context = impl_from_IUnknown(iface); + ULONG refcount = InterlockedDecrement(&context->refcount); + + TRACE("%p, refcount %u.\n", iface, refcount); + + if (!refcount) + { + if (context->props) + IPropertyStore_Release(context->props); + if (context->stream) + IMFByteStream_Release(context->stream); + if (context->url) + heap_free(context->url); + heap_free(context); + } + + return refcount; +} + +static const IUnknownVtbl create_object_context_vtbl = +{ + create_object_context_QueryInterface, + create_object_context_AddRef, + create_object_context_Release, +}; + +static WCHAR *heap_strdupW(const WCHAR *str) +{ + WCHAR *ret = NULL; + + if (str) + { + unsigned int size; + + size = (lstrlenW(str) + 1) * sizeof(WCHAR); + ret = heap_alloc(size); + if (ret) + memcpy(ret, str, size); + } + + return ret; +} + +static HRESULT WINAPI container_stream_handler_BeginCreateObject(IMFByteStreamHandler *iface, IMFByteStream *stream, const WCHAR *url, DWORD flags, + IPropertyStore *props, IUnknown **cancel_cookie, IMFAsyncCallback *callback, IUnknown *state) +{ + struct container_stream_handler *this = impl_from_IMFByteStreamHandler(iface); + struct create_object_context *context; + IMFAsyncResult *caller, *item; + HRESULT hr; + + TRACE("%p, %s, %#x, %p, %p, %p, %p.\n", iface, debugstr_w(url), flags, props, cancel_cookie, callback, state); + + if (cancel_cookie) + *cancel_cookie = NULL; + + if (FAILED(hr = MFCreateAsyncResult(NULL, callback, state, &caller))) + return hr; + + context = heap_alloc(sizeof(*context)); + if (!context) + { + IMFAsyncResult_Release(caller); + return E_OUTOFMEMORY; + } + + context->IUnknown_iface.lpVtbl = &create_object_context_vtbl; + context->refcount = 1; + context->props = props; + if (context->props) + IPropertyStore_AddRef(context->props); + context->flags = flags; + context->stream = stream; + if (context->stream) + IMFByteStream_AddRef(context->stream); + if (url) + context->url = heap_strdupW(url); + if (!context->stream) + { + IMFAsyncResult_Release(caller); + IUnknown_Release(&context->IUnknown_iface); + return E_OUTOFMEMORY; + } + + hr = MFCreateAsyncResult(&context->IUnknown_iface, &this->IMFAsyncCallback_iface, (IUnknown *)caller, &item); + IUnknown_Release(&context->IUnknown_iface); + IMFAsyncResult_Release(caller); + if (SUCCEEDED(hr)) + { + if (SUCCEEDED(hr = MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_IO, item))) + { + if (cancel_cookie) + IMFAsyncResult_GetState(item, cancel_cookie); + } + + IMFAsyncResult_Release(item); + } + + return hr; +} + +static HRESULT WINAPI container_stream_handler_EndCreateObject(IMFByteStreamHandler *iface, IMFAsyncResult *result, + MF_OBJECT_TYPE *obj_type, IUnknown **object) +{ + struct container_stream_handler *this = impl_from_IMFByteStreamHandler(iface); + struct container_stream_handler_result *found = NULL, *cur; + HRESULT hr; + + TRACE("%p, %p, %p, %p.\n", iface, result, obj_type, object); + + EnterCriticalSection(&this->cs); + + LIST_FOR_EACH_ENTRY(cur, &this->results, struct container_stream_handler_result, entry) + { + if (result == cur->result) + { + list_remove(&cur->entry); + found = cur; + break; + } + } + + LeaveCriticalSection(&this->cs); + + if (found) + { + *obj_type = found->obj_type; + *object = found->object; + hr = IMFAsyncResult_GetStatus(found->result); + IMFAsyncResult_Release(found->result); + heap_free(found); + } + else + { + *obj_type = MF_OBJECT_INVALID; + *object = NULL; + hr = MF_E_UNEXPECTED; + } + + return hr; +} + +static HRESULT WINAPI container_stream_handler_CancelObjectCreation(IMFByteStreamHandler *iface, IUnknown *cancel_cookie) +{ + struct container_stream_handler *this = impl_from_IMFByteStreamHandler(iface); + struct container_stream_handler_result *found = NULL, *cur; + + TRACE("%p, %p.\n", iface, cancel_cookie); + + EnterCriticalSection(&this->cs); + + LIST_FOR_EACH_ENTRY(cur, &this->results, struct container_stream_handler_result, entry) + { + if (cancel_cookie == (IUnknown *)cur->result) + { + list_remove(&cur->entry); + found = cur; + break; + } + } + + LeaveCriticalSection(&this->cs); + + if (found) + { + IMFAsyncResult_Release(found->result); + if (found->object) + IUnknown_Release(found->object); + heap_free(found); + } + + return found ? S_OK : MF_E_UNEXPECTED; +} + +static HRESULT WINAPI container_stream_handler_GetMaxNumberOfBytesRequiredForResolution(IMFByteStreamHandler *iface, QWORD *bytes) +{ + FIXME("stub (%p %p)\n", iface, bytes); + return E_NOTIMPL; +} + +static const IMFByteStreamHandlerVtbl container_stream_handler_vtbl = +{ + container_stream_handler_QueryInterface, + container_stream_handler_AddRef, + container_stream_handler_Release, + container_stream_handler_BeginCreateObject, + container_stream_handler_EndCreateObject, + container_stream_handler_CancelObjectCreation, + container_stream_handler_GetMaxNumberOfBytesRequiredForResolution, +}; + +static HRESULT WINAPI container_stream_handler_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **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 ULONG WINAPI container_stream_handler_callback_AddRef(IMFAsyncCallback *iface) +{ + struct container_stream_handler *handler = impl_from_IMFAsyncCallback(iface); + return IMFByteStreamHandler_AddRef(&handler->IMFByteStreamHandler_iface); +} + +static ULONG WINAPI container_stream_handler_callback_Release(IMFAsyncCallback *iface) +{ + struct container_stream_handler *handler = impl_from_IMFAsyncCallback(iface); + return IMFByteStreamHandler_Release(&handler->IMFByteStreamHandler_iface); +} + +static HRESULT WINAPI container_stream_handler_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) +{ + return E_NOTIMPL; +} + +static HRESULT container_stream_handler_create_object(struct container_stream_handler *This, WCHAR *url, IMFByteStream *stream, DWORD flags, + IPropertyStore *props, IUnknown **out_object, MF_OBJECT_TYPE *out_obj_type) +{ + TRACE("(%p %s %p %u %p %p %p)\n", This, debugstr_w(url), stream, flags, props, out_object, out_obj_type); + + if (flags & MF_RESOLUTION_MEDIASOURCE) + { + HRESULT hr; + struct media_source *new_source; + + if (FAILED(hr = media_source_constructor(stream, This->type, &new_source))) + return hr; + + TRACE("->(%p)\n", new_source); + + *out_object = (IUnknown*)&new_source->IMFMediaSource_iface; + *out_obj_type = MF_OBJECT_MEDIASOURCE; + + return S_OK; + } + else + { + FIXME("flags = %08x\n", flags); + return E_NOTIMPL; + } +} + +static HRESULT WINAPI container_stream_handler_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct container_stream_handler *handler = impl_from_IMFAsyncCallback(iface); + struct container_stream_handler_result *handler_result; + MF_OBJECT_TYPE obj_type = MF_OBJECT_INVALID; + IUnknown *object = NULL, *context_object; + struct create_object_context *context; + IMFAsyncResult *caller; + HRESULT hr; + + caller = (IMFAsyncResult *)IMFAsyncResult_GetStateNoAddRef(result); + + if (FAILED(hr = IMFAsyncResult_GetObject(result, &context_object))) + { + WARN("Expected context set for callee result.\n"); + return hr; + } + + context = impl_from_IUnknown(context_object); + + hr = container_stream_handler_create_object(handler, context->url, context->stream, context->flags, context->props, &object, &obj_type); + + handler_result = heap_alloc(sizeof(*handler_result)); + if (handler_result) + { + handler_result->result = caller; + IMFAsyncResult_AddRef(handler_result->result); + handler_result->obj_type = obj_type; + handler_result->object = object; + + EnterCriticalSection(&handler->cs); + list_add_tail(&handler->results, &handler_result->entry); + LeaveCriticalSection(&handler->cs); + } + else + { + if (object) + IUnknown_Release(object); + hr = E_OUTOFMEMORY; + } + + IUnknown_Release(&context->IUnknown_iface); + + IMFAsyncResult_SetStatus(caller, hr); + MFInvokeCallback(caller); + + return S_OK; +} + +static const IMFAsyncCallbackVtbl container_stream_handler_callback_vtbl = +{ + container_stream_handler_callback_QueryInterface, + container_stream_handler_callback_AddRef, + container_stream_handler_callback_Release, + container_stream_handler_callback_GetParameters, + container_stream_handler_callback_Invoke, +}; + +HRESULT container_stream_handler_construct(REFIID riid, void **obj, enum source_type type) +{ + struct container_stream_handler *this; + HRESULT hr; + + TRACE("%s, %p.\n", debugstr_guid(riid), obj); + + this = heap_alloc_zero(sizeof(*this)); + if (!this) + return E_OUTOFMEMORY; + + list_init(&this->results); + InitializeCriticalSection(&this->cs); + + this->type = type; + this->IMFByteStreamHandler_iface.lpVtbl = &container_stream_handler_vtbl; + this->IMFAsyncCallback_iface.lpVtbl = &container_stream_handler_callback_vtbl; + this->refcount = 1; + + hr = IMFByteStreamHandler_QueryInterface(&this->IMFByteStreamHandler_iface, riid, obj); + IMFByteStreamHandler_Release(&this->IMFByteStreamHandler_iface); + + return hr; +} \ No newline at end of file diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 156f2578b3..54b611b130 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -404,6 +404,11 @@ failed: return hr; }
+static HRESULT mp4_stream_handler_create(REFIID riid, void **ret) +{ + return container_stream_handler_construct(riid, ret, SOURCE_TYPE_MPEG_4); +} + static const struct class_object { const GUID *clsid; @@ -412,6 +417,7 @@ static const struct class_object class_objects[] = { { &CLSID_VideoProcessorMFT, &video_processor_create }, + { &CLSID_MPEG4ByteStreamHandler, &mp4_stream_handler_create }, };
HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) diff --git a/dlls/winegstreamer/winegstreamer.rgs b/dlls/winegstreamer/winegstreamer.rgs index 923ba673f8..0bc26761e9 100644 --- a/dlls/winegstreamer/winegstreamer.rgs +++ b/dlls/winegstreamer/winegstreamer.rgs @@ -12,3 +12,35 @@ HKCR } } } + +HKLM +{ + NoRemove 'Software' + { + NoRemove 'Microsoft' + { + NoRemove 'Windows Media Foundation' + { + NoRemove 'ByteStreamHandlers' + { + '.m4v' + { + val '{271C3902-6095-4c45-A22F-20091816EE9E}' = s 'MPEG4 Byte Stream Handler' + } + '.mp4' + { + val '{271C3902-6095-4c45-A22F-20091816EE9E}' = s 'MPEG4 Byte Stream Handler' + } + 'video/m4v' + { + val '{271C3902-6095-4c45-A22F-20091816EE9E}' = s 'MPEG4 Byte Stream Handler' + } + 'video/mp4' + { + val '{271C3902-6095-4c45-A22F-20091816EE9E}' = s 'MPEG4 Byte Stream Handler' + } + } + } + } + } +} \ No newline at end of file diff --git a/dlls/winegstreamer/winegstreamer_classes.idl b/dlls/winegstreamer/winegstreamer_classes.idl index fa0e178405..997a28b052 100644 --- a/dlls/winegstreamer/winegstreamer_classes.idl +++ b/dlls/winegstreamer/winegstreamer_classes.idl @@ -54,3 +54,10 @@ coclass Gstreamer_Splitter {} uuid(88753b26-5b24-49bd-b2e7-0c445c78c982) ] coclass VideoProcessorMFT {} + +[ + helpstring("MP4 Byte Stream Handler"), + threading(both), + uuid(271c3902-6095-4c45-a22f-20091816ee9e) +] +coclass MPEG4ByteStreamHandler {} diff --git a/include/mfidl.idl b/include/mfidl.idl index a5fb8bc0bd..15a68a4253 100644 --- a/include/mfidl.idl +++ b/include/mfidl.idl @@ -1066,3 +1066,4 @@ cpp_quote("EXTERN_GUID(MF_SESSION_APPROX_EVENT_OCCURRENCE_TIME, 0x190e852f, 0x62 cpp_quote("EXTERN_GUID(MF_PMP_SERVER_CONTEXT, 0x2f00c910, 0xd2cf, 0x4278, 0x8b, 0x6a, 0xd0, 0x77, 0xfa, 0xc3, 0xa2, 0x5f);")
cpp_quote("EXTERN_GUID(CLSID_VideoProcessorMFT, 0x88753b26, 0x5b24, 0x49bd, 0xb2, 0xe7, 0xc, 0x44, 0x5c, 0x78, 0xc9, 0x82);") +cpp_quote("EXTERN_GUID(CLSID_MPEG4ByteStreamHandler, 0x271c3902, 0x6095, 0x4c45, 0xa2, 0x2f, 0x20, 0x09, 0x18, 0x16, 0xee, 0x9e);") \ No newline at end of file
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=68146
Your paranoid android.
=== debiant (32 bit report) ===
mfplat: mfplat.c:497: Test succeeded inside todo block: Failed to get presentation descriptor, hr 0.
=== debiant (32 bit French report) ===
mfplat: mfplat.c:497: Test succeeded inside todo block: Failed to get presentation descriptor, hr 0.
=== debiant (32 bit Japanese:Japan report) ===
mfplat: mfplat.c:497: Test succeeded inside todo block: Failed to get presentation descriptor, hr 0.
=== debiant (32 bit Chinese:China report) ===
mfplat: mfplat.c:497: Test succeeded inside todo block: Failed to get presentation descriptor, hr 0.
=== debiant (32 bit WoW report) ===
mfplat: mfplat.c:497: Test succeeded inside todo block: Failed to get presentation descriptor, hr 0.
=== debiant (64 bit WoW report) ===
mfplat: mfplat.c:497: Test succeeded inside todo block: Failed to get presentation descriptor, hr 0.
On 3/26/20 3:12 AM, Derek Lesho wrote:
+static ULONG WINAPI media_source_Release(IMFMediaSource *iface) +{
- struct media_source *This = impl_from_IMFMediaSource(iface);
- ULONG ref = InterlockedDecrement(&This->ref);
- TRACE("(%p) ref=%u\n", This, ref);
- if (!ref)
- {
heap_free(This);
- }
- return ref;
+}
This does not release event queue. It should also shutdown it, just in case. It could be important if client subscribed to source events, but didn't call Start/Shutdown.
+static HRESULT media_source_teardown(struct media_source *This) +{
- if (This->event_queue)
IMFMediaEventQueue_Release(This->event_queue);
- return S_OK;
+}
It could as well be void, you can't do much with return value of it.
diff --git a/include/mfidl.idl b/include/mfidl.idl index a5fb8bc0bd..15a68a4253 100644 --- a/include/mfidl.idl +++ b/include/mfidl.idl @@ -1066,3 +1066,4 @@ cpp_quote("EXTERN_GUID(MF_SESSION_APPROX_EVENT_OCCURRENCE_TIME, 0x190e852f, 0x62 cpp_quote("EXTERN_GUID(MF_PMP_SERVER_CONTEXT, 0x2f00c910, 0xd2cf, 0x4278, 0x8b, 0x6a, 0xd0, 0x77, 0xfa, 0xc3, 0xa2, 0x5f);")
cpp_quote("EXTERN_GUID(CLSID_VideoProcessorMFT, 0x88753b26, 0x5b24, 0x49bd, 0xb2, 0xe7, 0xc, 0x44, 0x5c, 0x78, 0xc9, 0x82);") +cpp_quote("EXTERN_GUID(CLSID_MPEG4ByteStreamHandler, 0x271c3902, 0x6095, 0x4c45, 0xa2, 0x2f, 0x20, 0x09, 0x18, 0x16, 0xee, 0x9e);")
I don't see this name (or guid) in SDK. Is it going to be used from outside winegstreamer?
P.S. it would be nice to have formatting and naming conventions matching other MF modules for the sake of consistency and more uniform traces, but I'll leave that to gstreamer part reviewers to decide, so ignore it if you disagree.
On 3/26/20 12:06 AM, Nikolay Sivov wrote:
On 3/26/20 3:12 AM, Derek Lesho wrote:
+static ULONG WINAPI media_source_Release(IMFMediaSource *iface) +{ +Â Â Â struct media_source *This = impl_from_IMFMediaSource(iface); +Â Â Â ULONG ref = InterlockedDecrement(&This->ref);
+Â Â Â TRACE("(%p) ref=%u\n", This, ref);
+Â Â Â if (!ref) +Â Â Â { +Â Â Â Â Â Â Â heap_free(This); +Â Â Â }
+Â Â Â return ref; +}
This does not release event queue. It should also shutdown it, just in case. It could be important if client subscribed to source events, but didn't call Start/Shutdown.
+static HRESULT media_source_teardown(struct media_source *This) +{ +Â Â Â if (This->event_queue) +Â Â Â Â Â Â Â IMFMediaEventQueue_Release(This->event_queue);
+Â Â Â return S_OK; +}
It could as well be void, you can't do much with return value of it.
diff --git a/include/mfidl.idl b/include/mfidl.idl index a5fb8bc0bd..15a68a4253 100644 --- a/include/mfidl.idl +++ b/include/mfidl.idl @@ -1066,3 +1066,4 @@ cpp_quote("EXTERN_GUID(MF_SESSION_APPROX_EVENT_OCCURRENCE_TIME, 0x190e852f, 0x62 Â cpp_quote("EXTERN_GUID(MF_PMP_SERVER_CONTEXT, 0x2f00c910, 0xd2cf, 0x4278, 0x8b, 0x6a, 0xd0, 0x77, 0xfa, 0xc3, 0xa2, 0x5f);") Â cpp_quote("EXTERN_GUID(CLSID_VideoProcessorMFT, 0x88753b26, 0x5b24, 0x49bd, 0xb2, 0xe7, 0xc, 0x44, 0x5c, 0x78, 0xc9, 0x82);") +cpp_quote("EXTERN_GUID(CLSID_MPEG4ByteStreamHandler, 0x271c3902, 0x6095, 0x4c45, 0xa2, 0x2f, 0x20, 0x09, 0x18, 0x16, 0xee, 0x9e);")
I don't see this name (or guid) in SDK. Is it going to be used from outside winegstreamer?
P.S. it would be nice to have formatting and naming conventions matching other MF modules for the sake of consistency and more uniform traces, but I'll leave that to gstreamer part reviewers to decide, so ignore it if you disagree.
Either MF style or quartz style (i.e. D3D style) is fine with me, but I'd definitely prefer it match at least one of those.
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/mfplat/tests/mfplat.c | 11 +- dlls/winegstreamer/gst_cbs.c | 125 +++ dlls/winegstreamer/gst_cbs.h | 33 +- dlls/winegstreamer/media_source.c | 1201 ++++++++++++++++++++++++++++- 4 files changed, 1349 insertions(+), 21 deletions(-)
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 851b049a4b..aca0fac130 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -493,10 +493,7 @@ static void test_source_resolver(void) ok(obj_type == MF_OBJECT_MEDIASOURCE, "got %d\n", obj_type);
hr = IMFMediaSource_CreatePresentationDescriptor(mediasource, &descriptor); -todo_wine ok(hr == S_OK, "Failed to get presentation descriptor, hr %#x.\n", hr); - if (FAILED(hr)) - goto skip_source_tests; ok(descriptor != NULL, "got %p\n", descriptor);
hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(descriptor, 0, &selected, &sd); @@ -507,10 +504,7 @@ todo_wine IMFStreamDescriptor_Release(sd);
hr = IMFMediaTypeHandler_GetMajorType(handler, &guid); -todo_wine ok(hr == S_OK, "Failed to get stream major type, hr %#x.\n", hr); - if (FAILED(hr)) - goto skip_source_tests;
/* Check major/minor type for the test media. */ ok(IsEqualGUID(&guid, &MFMediaType_Video), "Unexpected major type %s.\n", debugstr_guid(&guid)); @@ -598,10 +592,7 @@ todo_wine hr = IMFMediaSource_CreatePresentationDescriptor(mediasource, NULL); ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr);
-skip_source_tests: - - if (descriptor) - IMFPresentationDescriptor_Release(descriptor); + IMFPresentationDescriptor_Release(descriptor); IMFMediaSource_Release(mediasource); IMFByteStream_Release(stream);
diff --git a/dlls/winegstreamer/gst_cbs.c b/dlls/winegstreamer/gst_cbs.c index a0618798d8..9c27c3490d 100644 --- a/dlls/winegstreamer/gst_cbs.c +++ b/dlls/winegstreamer/gst_cbs.c @@ -49,6 +49,8 @@ static void CALLBACK perform_cb(TP_CALLBACK_INSTANCE *instance, void *user)
if (cbdata->type < GSTDEMUX_MAX) perform_cb_gstdemux(cbdata); + else if (cbdata->type < MEDIA_SOURCE_MAX) + perform_cb_media_source(cbdata);
pthread_mutex_lock(&cbdata->lock); cbdata->finished = 1; @@ -305,3 +307,126 @@ gboolean query_sink_wrapper(GstPad *pad, GstObject *parent, GstQuery *query)
return cbdata.u.query_sink_data.ret; } + +GstFlowReturn pull_from_bytestream_wrapper(GstPad *pad, GstObject *parent, guint64 ofs, guint len, + GstBuffer **buf) +{ + struct cb_data cbdata = { PULL_FROM_BYTESTREAM }; + + cbdata.u.getrange_data.pad = pad; + cbdata.u.getrange_data.parent = parent; + cbdata.u.getrange_data.ofs = ofs; + cbdata.u.getrange_data.len = len; + cbdata.u.getrange_data.buf = buf; + + call_cb(&cbdata); + + return cbdata.u.getrange_data.ret; +} + +gboolean query_bytestream_wrapper(GstPad *pad, GstObject *parent, GstQuery *query) +{ + struct cb_data cbdata = { QUERY_BYTESTREAM }; + + cbdata.u.query_function_data.pad = pad; + cbdata.u.query_function_data.parent = parent; + cbdata.u.query_function_data.query = query; + + call_cb(&cbdata); + + return cbdata.u.query_function_data.ret; +} + +gboolean activate_bytestream_pad_mode_wrapper(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate) +{ + struct cb_data cbdata = { ACTIVATE_BYTESTREAM_PAD_MODE }; + + cbdata.u.activate_mode_data.pad = pad; + cbdata.u.activate_mode_data.parent = parent; + cbdata.u.activate_mode_data.mode = mode; + cbdata.u.activate_mode_data.activate = activate; + + call_cb(&cbdata); + + return cbdata.u.query_function_data.ret; +} + +gboolean process_bytestream_pad_event_wrapper(GstPad *pad, GstObject *parent, GstEvent *event) +{ + struct cb_data cbdata = { PROCESS_BYTESTREAM_PAD_EVENT }; + + cbdata.u.event_src_data.pad = pad; + cbdata.u.event_src_data.parent = parent; + cbdata.u.event_src_data.event = event; + + call_cb(&cbdata); + + return cbdata.u.event_src_data.ret; +} + +void source_stream_added_wrapper(GstElement *bin, GstPad *pad, gpointer user) +{ + struct cb_data cbdata = { SOURCE_STREAM_ADDED }; + + cbdata.u.pad_added_data.element = bin; + cbdata.u.pad_added_data.pad = pad; + cbdata.u.pad_added_data.user = user; + + call_cb(&cbdata); +} + +void source_stream_removed_wrapper(GstElement *element, GstPad *pad, gpointer user) +{ + struct cb_data cbdata = { SOURCE_STREAM_REMOVED }; + + cbdata.u.pad_removed_data.element = element; + cbdata.u.pad_removed_data.pad = pad; + cbdata.u.pad_removed_data.user = user; + + call_cb(&cbdata); +} + +void source_all_streams_wrapper(GstElement *element, gpointer user) +{ + struct cb_data cbdata = { SOURCE_ALL_STREAMS }; + + cbdata.u.no_more_pads_data.element = element; + cbdata.u.no_more_pads_data.user = user; + + call_cb(&cbdata); +} + +GstFlowReturn stream_new_sample_wrapper(GstElement *appsink, gpointer user) +{ + struct cb_data cbdata = { STREAM_NEW_SAMPLE }; + + cbdata.u.new_sample_data.appsink = appsink; + cbdata.u.new_sample_data.user = user; + + call_cb(&cbdata); + + return cbdata.u.new_sample_data.ret; +} + +void stream_eos_wrapper(GstElement *appsink, gpointer user) +{ + struct cb_data cbdata = { STREAM_EOS }; + + cbdata.u.eos_data.appsink = appsink; + cbdata.u.eos_data.user = user; + + call_cb(&cbdata); +} + +GstBusSyncReply watch_source_bus_wrapper(GstBus *bus, GstMessage *message, gpointer user) +{ + struct cb_data cbdata = { WATCH_SOURCE_BUS }; + + cbdata.u.watch_bus_data.bus = bus; + cbdata.u.watch_bus_data.msg = message; + cbdata.u.watch_bus_data.user = user; + + call_cb(&cbdata); + + return cbdata.u.watch_bus_data.ret; +} \ No newline at end of file diff --git a/dlls/winegstreamer/gst_cbs.h b/dlls/winegstreamer/gst_cbs.h index 4725f23ad1..2c83f294f4 100644 --- a/dlls/winegstreamer/gst_cbs.h +++ b/dlls/winegstreamer/gst_cbs.h @@ -43,7 +43,18 @@ enum CB_TYPE { AUTOPLUG_BLACKLIST, UNKNOWN_TYPE, QUERY_SINK, - GSTDEMUX_MAX + GSTDEMUX_MAX, + PULL_FROM_BYTESTREAM, + QUERY_BYTESTREAM, + ACTIVATE_BYTESTREAM_PAD_MODE, + PROCESS_BYTESTREAM_PAD_EVENT, + SOURCE_STREAM_ADDED, + SOURCE_STREAM_REMOVED, + SOURCE_ALL_STREAMS, + STREAM_NEW_SAMPLE, + STREAM_EOS, + WATCH_SOURCE_BUS, + MEDIA_SOURCE_MAX, };
struct cb_data { @@ -128,6 +139,15 @@ struct cb_data { GstQuery *query; gboolean ret; } query_sink_data; + struct new_sample_data { + GstElement *appsink; + gpointer user; + GstFlowReturn ret; + } new_sample_data; + struct eos_data { + GstElement *appsink; + gpointer user; + } eos_data; } u;
int finished; @@ -138,6 +158,7 @@ struct cb_data {
void mark_wine_thread(void) DECLSPEC_HIDDEN; void perform_cb_gstdemux(struct cb_data *data) DECLSPEC_HIDDEN; +void perform_cb_media_source(struct cb_data *data) DECLSPEC_HIDDEN;
GstBusSyncReply watch_bus_wrapper(GstBus *bus, GstMessage *msg, gpointer user) DECLSPEC_HIDDEN; void existing_new_pad_wrapper(GstElement *bin, GstPad *pad, gpointer user) DECLSPEC_HIDDEN; @@ -154,5 +175,15 @@ GstAutoplugSelectResult autoplug_blacklist_wrapper(GstElement *bin, GstPad *pad, void unknown_type_wrapper(GstElement *bin, GstPad *pad, GstCaps *caps, gpointer user) DECLSPEC_HIDDEN; void Gstreamer_transform_pad_added_wrapper(GstElement *filter, GstPad *pad, gpointer user) DECLSPEC_HIDDEN; gboolean query_sink_wrapper(GstPad *pad, GstObject *parent, GstQuery *query) DECLSPEC_HIDDEN; +GstFlowReturn pull_from_bytestream_wrapper(GstPad *pad, GstObject *parent, guint64 ofs, guint len, GstBuffer **buf) DECLSPEC_HIDDEN; +gboolean query_bytestream_wrapper(GstPad *pad, GstObject *parent, GstQuery *query) DECLSPEC_HIDDEN; +gboolean activate_bytestream_pad_mode_wrapper(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate) DECLSPEC_HIDDEN; +gboolean process_bytestream_pad_event_wrapper(GstPad *pad, GstObject *parent, GstEvent *event) DECLSPEC_HIDDEN; +void source_stream_added_wrapper(GstElement *bin, GstPad *pad, gpointer user) DECLSPEC_HIDDEN; +void source_stream_removed_wrapper(GstElement *element, GstPad *pad, gpointer user) DECLSPEC_HIDDEN; +void source_all_streams_wrapper(GstElement *element, gpointer user) DECLSPEC_HIDDEN; +GstFlowReturn stream_new_sample_wrapper(GstElement *appsink, gpointer user) DECLSPEC_HIDDEN; +void stream_eos_wrapper(GstElement *appsink, gpointer user) DECLSPEC_HIDDEN; +GstBusSyncReply watch_source_bus_wrapper(GstBus *bus, GstMessage *message, gpointer user) DECLSPEC_HIDDEN;
#endif diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 13a8db9d16..e7710dbd08 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -1,4 +1,9 @@ +#include "config.h" + +#include <gst/gst.h> + #include "gst_private.h" +#include "gst_cbs.h"
#include <stdarg.h>
@@ -8,6 +13,7 @@ #include "mfapi.h" #include "mferror.h" #include "mfidl.h" +#include "mfobjects.h"
#include "wine/debug.h" #include "wine/heap.h" @@ -15,13 +21,559 @@
WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
+static struct source_desc +{ + GstStaticCaps bytestream_caps; +} source_descs[] = +{ + {/*SOURCE_TYPE_MPEG_4*/ + GST_STATIC_CAPS("video/quicktime"), + } +}; + +struct sample_request +{ + struct list entry; + IUnknown *token; +}; + +struct media_source; + +struct media_stream +{ + IMFMediaStream IMFMediaStream_iface; + LONG ref; + struct media_source *parent_source; + IMFMediaEventQueue *event_queue; + IMFStreamDescriptor *descriptor; + GstElement *parser, *appsink; + GstPad *their_src, *my_sink; + /* usually reflects state of source */ + enum + { + STREAM_INACTIVE, + STREAM_ENABLED, + STREAM_PAUSED, + STREAM_RUNNING, + STREAM_SHUTDOWN, + } state; + BOOL eos; + CRITICAL_SECTION dispatch_samples_cs; + struct list sample_requests; + unsigned int pending_samples; +}; + struct media_source { IMFMediaSource IMFMediaSource_iface; LONG ref; + enum source_type type; IMFMediaEventQueue *event_queue; + IMFByteStream *byte_stream; + struct media_stream **streams; + ULONG stream_count; + IMFPresentationDescriptor *pres_desc; + GstBus *bus; + GstElement *container; + GstElement *demuxer; + GstPad *my_src, *their_sink; + enum + { + SOURCE_OPENING, + SOURCE_STOPPED, /* (READY) */ + SOURCE_PAUSED, + SOURCE_RUNNING, + SOURCE_SHUTDOWN, + } state; + CRITICAL_SECTION streams_cs; + HANDLE init_complete_event; +}; + +/* stream */ + +static void media_source_notify_stream_ended(struct media_source *source); +static void stream_dispatch_samples(struct media_stream *This) +{ + struct sample_request *req, *cursor2; + unsigned int fufilled_counter = 0; + + if (This->state != STREAM_RUNNING && This->state != STREAM_SHUTDOWN) + return; + + EnterCriticalSection(&This->dispatch_samples_cs); + + LIST_FOR_EACH_ENTRY_SAFE(req, cursor2, &This->sample_requests, struct sample_request, entry) + { + IMFSample *sample; + + if (This->state == STREAM_SHUTDOWN + /* Not sure if this is correct: */ + || (!(This->pending_samples) && This->eos)) + { + if (req->token) + { + IUnknown_Release(req->token); + } + list_remove(&req->entry); + continue; + } + + if (!(This->pending_samples)) + { + break; + } + + /* Get the sample from the appsink, then construct an IMFSample */ + /* We do this in the dispatch function so we can have appsink buffer for us */ + { + GstSample *gst_sample; + + g_signal_emit_by_name(This->appsink, "pull-sample", &gst_sample); + if (!gst_sample) + { + ERR("Appsink has no samples and pending_samples != 0\n"); + break; + } + + sample = mf_sample_from_gst_buffer(gst_sample_get_buffer(gst_sample)); + + gst_sample_unref(gst_sample); + } + + if (req->token) + { + IMFSample_SetUnknown(sample, &MFSampleExtension_Token, req->token); + } + + IMFMediaEventQueue_QueueEventParamUnk(This->event_queue, MEMediaSample, &GUID_NULL, S_OK, (IUnknown *)sample); + + if (req->token) + { + IUnknown_Release(req->token); + } + + list_remove(&req->entry); + + This->pending_samples--; + fufilled_counter++; + } + + TRACE("Fufilled %u sample requests. %u pending samples. %s more requests.\n", + fufilled_counter, This->pending_samples, list_empty(&This->sample_requests) ? "no" : ""); + + if (This->eos && !This->pending_samples && This->state == STREAM_RUNNING) + { + PROPVARIANT empty; + empty.vt = VT_EMPTY; + + IMFMediaEventQueue_QueueEventParamVar(This->event_queue, MEEndOfStream, &GUID_NULL, S_OK, &empty); + media_source_notify_stream_ended(This->parent_source); + } + LeaveCriticalSection(&This->dispatch_samples_cs); +} + +static inline struct media_stream *impl_from_IMFMediaStream(IMFMediaStream *iface) +{ + return CONTAINING_RECORD(iface, struct media_stream, IMFMediaStream_iface); +} + +static HRESULT WINAPI media_stream_QueryInterface(IMFMediaStream *iface, REFIID riid, void **out) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), out); + + if (IsEqualIID(riid, &IID_IMFMediaStream) || + IsEqualIID(riid, &IID_IMFMediaEventGenerator) || + IsEqualIID(riid, &IID_IUnknown)) + { + *out = &This->IMFMediaStream_iface; + } + else + { + FIXME("(%s, %p)\n", debugstr_guid(riid), out); + *out = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*out); + return S_OK; +} + +static ULONG WINAPI media_stream_AddRef(IMFMediaStream *iface) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + ULONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%u\n", This, ref); + + return ref; +} + +static ULONG WINAPI media_stream_Release(IMFMediaStream *iface) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%u\n", This, ref); + + if (!ref) + { + if (This->state != STREAM_SHUTDOWN) + ERR("incomplete cleanup\n"); + heap_free(This); + } + + return ref; +} + +static HRESULT WINAPI media_stream_GetEvent(IMFMediaStream *iface, DWORD flags, IMFMediaEvent **event) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%#x, %p)\n", This, flags, event); + + if (This->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + return IMFMediaEventQueue_GetEvent(This->event_queue, flags, event); +} + +static HRESULT WINAPI media_stream_BeginGetEvent(IMFMediaStream *iface, IMFAsyncCallback *callback, IUnknown *state) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%p, %p)\n", This, callback, state); + + if (This->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + return IMFMediaEventQueue_BeginGetEvent(This->event_queue, callback, state); +} + +static HRESULT WINAPI media_stream_EndGetEvent(IMFMediaStream *iface, IMFAsyncResult *result, IMFMediaEvent **event) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%p, %p)\n", This, result, event); + + if (This->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + return IMFMediaEventQueue_EndGetEvent(This->event_queue, result, event); +} + +static HRESULT WINAPI media_stream_QueueEvent(IMFMediaStream *iface, MediaEventType event_type, REFGUID ext_type, + HRESULT hr, const PROPVARIANT *value) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%d, %s, %#x, %p)\n", This, event_type, debugstr_guid(ext_type), hr, value); + + if (This->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + return IMFMediaEventQueue_QueueEventParamVar(This->event_queue, event_type, ext_type, hr, value); +} + +static HRESULT WINAPI media_stream_GetMediaSource(IMFMediaStream *iface, IMFMediaSource **source) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + + FIXME("stub (%p)->(%p)\n", This, source); + + if (This->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IMFStreamDescriptor **descriptor) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%p)\n", This, descriptor); + + if (This->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + IMFStreamDescriptor_AddRef(This->descriptor); + *descriptor = This->descriptor; + + return S_OK; +} + +static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + struct sample_request *req; + + TRACE("(%p)->(%p)\n", iface, token); + + if (This->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + if (This->state == STREAM_INACTIVE || This->state == STREAM_ENABLED) + { + WARN("Stream isn't active\n"); + return MF_E_MEDIA_SOURCE_WRONGSTATE; + } + + if (This->eos && !This->pending_samples) + return MF_E_END_OF_STREAM; + + req = heap_alloc(sizeof(*req)); + if (token) + IUnknown_AddRef(token); + req->token = token; + list_add_tail(&This->sample_requests, &req->entry); + + stream_dispatch_samples(This); + + return S_OK; +} + +static const IMFMediaStreamVtbl media_stream_vtbl = +{ + media_stream_QueryInterface, + media_stream_AddRef, + media_stream_Release, + media_stream_GetEvent, + media_stream_BeginGetEvent, + media_stream_EndGetEvent, + media_stream_QueueEvent, + media_stream_GetMediaSource, + media_stream_GetStreamDescriptor, + media_stream_RequestSample };
+static GstFlowReturn stream_new_sample(GstElement *appsink, gpointer user) +{ + struct media_stream *This = (struct media_stream *) user; + + TRACE("(%p) got sample\n", This); + + if (This->state == STREAM_INACTIVE) + { + GstSample *discard_sample; + g_signal_emit_by_name(This->appsink, "pull-sample", &discard_sample); + gst_sample_unref(discard_sample); + return GST_FLOW_OK; + } + + This->pending_samples++; + stream_dispatch_samples(This); + return GST_FLOW_OK; +} + +void stream_eos(GstElement *appsink, gpointer user) +{ + struct media_stream *This = (struct media_stream *) user; + + TRACE("(%p) EOS\n", This); + + This->eos = TRUE; + + stream_dispatch_samples(This); +} + +GstBusSyncReply watch_source_bus(GstBus *bus, GstMessage *message, gpointer user) +{ + struct media_stream *This = (struct media_stream *) user; + GError *err = NULL; + gchar *dbg_info = NULL; + + TRACE("source %p message type %s\n", This, GST_MESSAGE_TYPE_NAME(message)); + + switch (message->type) + { + case GST_MESSAGE_ERROR: + gst_message_parse_error(message, &err, &dbg_info); + ERR("%s: %s\n", GST_OBJECT_NAME(message->src), err->message); + ERR("%s\n", dbg_info); + g_error_free(err); + g_free(dbg_info); + break; + case GST_MESSAGE_WARNING: + gst_message_parse_warning(message, &err, &dbg_info); + WARN("%s: %s\n", GST_OBJECT_NAME(message->src), err->message); + WARN("%s\n", dbg_info); + g_error_free(err); + g_free(dbg_info); + break; + default: + break; + } + + return GST_BUS_DROP; +} + +static void media_stream_teardown(struct media_stream *This) +{ + TRACE("(%p)\n", This); + + This->state = STREAM_SHUTDOWN; + + if (This->their_src) + gst_object_unref(GST_OBJECT(This->their_src)); + if (This->my_sink) + gst_object_unref(GST_OBJECT(This->my_sink)); + + /* Frees pending requests and samples when state == STREAM_SHUTDOWN */ + stream_dispatch_samples(This); + + if (This->descriptor) + IMFStreamDescriptor_Release(This->descriptor); + if (This->event_queue) + IMFMediaEventQueue_Release(This->event_queue); + if (This->parent_source) + IMFMediaSource_Release(&This->parent_source->IMFMediaSource_iface); + + DeleteCriticalSection(&This->dispatch_samples_cs); +} + +static HRESULT media_stream_constructor(struct media_source *source, GstPad *pad, DWORD stream_id, struct media_stream **out_stream) +{ + HRESULT hr; + GstCaps *caps = NULL, *desired_caps = NULL; + IMFMediaType *media_type; + IMFMediaTypeHandler *type_handler; + struct media_stream *This = heap_alloc_zero(sizeof(*This)); + + TRACE("(%p %p)->(%p)\n", source, pad, out_stream); + + This->state = STREAM_INACTIVE; + This->pending_samples = 0; + list_init(&This->sample_requests); + This->eos = FALSE; + InitializeCriticalSection(&This->dispatch_samples_cs); + + if (FAILED(hr = IMFMediaSource_AddRef(&source->IMFMediaSource_iface))) + { + goto fail; + } + This->parent_source = source; + + if (FAILED(hr = MFCreateEventQueue(&This->event_queue))) + { + goto fail; + } + + caps = gst_pad_query_caps(pad, NULL); + + if (!(caps)) + { + goto fail; + } + + if (FAILED(hr = MFCreateMediaType(&media_type))) + { + goto fail; + } + + if (!(This->appsink = gst_element_factory_make("appsink", NULL))) + { + hr = E_OUTOFMEMORY; + goto fail; + } + gst_bin_add(GST_BIN(This->parent_source->container), This->appsink); + + gst_caps_ref(caps); + desired_caps = gst_caps_make_writable(caps); + media_type = media_type_from_caps(desired_caps); + TRACE("caps %s desired_caps %s\n", gst_caps_to_string(caps), gst_caps_to_string(desired_caps)); + if (!(gst_caps_is_equal(caps, desired_caps))) + { + GList *parser_list_one, *parser_list_two; + GstElementFactory *parser_factory; + + parser_list_one = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_PARSER, 1); + + parser_list_two = gst_element_factory_list_filter(parser_list_one, caps, GST_PAD_SINK, 0); + gst_plugin_feature_list_free(parser_list_one); + parser_list_one = parser_list_two; + + parser_list_two = gst_element_factory_list_filter(parser_list_one, desired_caps, GST_PAD_SRC, 0); + gst_plugin_feature_list_free(parser_list_one); + parser_list_one = parser_list_two; + + if (!(g_list_length(parser_list_one))) + { + gst_plugin_feature_list_free(parser_list_one); + ERR("Failed to find parser for stream\n"); + hr = E_FAIL; + goto fail; + } + + parser_factory = g_list_first(parser_list_one)->data; + TRACE("Found parser %s.\n", GST_ELEMENT_NAME(parser_factory)); + + if (!(This->parser = gst_element_factory_create(parser_factory, NULL))) + { + hr = E_FAIL; + goto fail; + } + gst_bin_add(GST_BIN(This->parent_source->container), This->parser); + if (!(gst_element_link(This->parser, This->appsink))) + { + hr = E_FAIL; + goto fail; + } + + g_object_set(This->appsink, "caps", desired_caps, NULL); + + gst_plugin_feature_list_free(parser_list_one); + } + gst_caps_unref(caps); + gst_caps_unref(desired_caps); + caps = NULL; + + MFCreateStreamDescriptor(stream_id, 1, &media_type, &This->descriptor); + + IMFStreamDescriptor_GetMediaTypeHandler(This->descriptor, &type_handler); + IMFMediaTypeHandler_SetCurrentMediaType(type_handler, media_type); + IMFMediaTypeHandler_Release(type_handler); + IMFMediaType_Release(media_type); + media_type = NULL; + + g_object_set(This->appsink, "emit-signals", TRUE, NULL); + g_object_set(This->appsink, "sync", FALSE, NULL); + g_signal_connect(This->appsink, "new-sample", G_CALLBACK(stream_new_sample_wrapper), This); + g_signal_connect(This->appsink, "eos", G_CALLBACK(stream_eos_wrapper), This); + + This->their_src = pad; + This->my_sink = gst_element_get_static_pad(This->parser ? This->parser : This->appsink, "sink"); + gst_pad_set_element_private(pad, This); + + gst_pad_link(This->their_src, This->my_sink); + + gst_element_sync_state_with_parent(This->appsink); + if (This->parser) + gst_element_sync_state_with_parent(This->parser); + + This->IMFMediaStream_iface.lpVtbl = &media_stream_vtbl; + This->ref = 1; + + TRACE("->(%p)\n", This); + + *out_stream = This; + return S_OK; + + fail: + WARN("Failed to construct media stream, hr %#x.\n", hr); + + /* Destroy temporary objects */ + if (caps) + gst_caps_unref(caps); + if (media_type) + IMFMediaType_Release(media_type); + + media_stream_teardown(This); + heap_free(This); + return hr; +} + /* source */
static inline struct media_source *impl_from_IMFMediaSource(IMFMediaSource *iface) @@ -71,6 +623,8 @@ static ULONG WINAPI media_source_Release(IMFMediaSource *iface)
if (!ref) { + if (This->state != SOURCE_SHUTDOWN) + ERR("Application has freed media source without calling ::Shutdown\n"); heap_free(This); }
@@ -83,6 +637,9 @@ static HRESULT WINAPI media_source_GetEvent(IMFMediaSource *iface, DWORD flags,
TRACE("(%p)->(%#x, %p)\n", This, flags, event);
+ if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + return IMFMediaEventQueue_GetEvent(This->event_queue, flags, event); }
@@ -92,6 +649,9 @@ static HRESULT WINAPI media_source_BeginGetEvent(IMFMediaSource *iface, IMFAsync
TRACE("(%p)->(%p, %p)\n", This, callback, state);
+ if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + return IMFMediaEventQueue_BeginGetEvent(This->event_queue, callback, state); }
@@ -101,6 +661,9 @@ static HRESULT WINAPI media_source_EndGetEvent(IMFMediaSource *iface, IMFAsyncRe
TRACE("(%p)->(%p, %p)\n", This, result, event);
+ if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + return IMFMediaEventQueue_EndGetEvent(This->event_queue, result, event); }
@@ -111,6 +674,9 @@ static HRESULT WINAPI media_source_QueueEvent(IMFMediaSource *iface, MediaEventT
TRACE("(%p)->(%d, %s, %#x, %p)\n", This, event_type, debugstr_guid(ext_type), hr, value);
+ if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + return IMFMediaEventQueue_QueueEventParamVar(This->event_queue, event_type, ext_type, hr, value); }
@@ -118,28 +684,102 @@ static HRESULT WINAPI media_source_GetCharacteristics(IMFMediaSource *iface, DWO { struct media_source *This = impl_from_IMFMediaSource(iface);
- FIXME("(%p)->(%p): stub\n", This, characteristics); + TRACE("(%p)->(%p)\n", This, characteristics);
- return E_NOTIMPL; + if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + + *characteristics = 0; + + return S_OK; }
static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource *iface, IMFPresentationDescriptor **descriptor) { struct media_source *This = impl_from_IMFMediaSource(iface);
- FIXME("(%p)->(%p): stub\n", This, descriptor); + TRACE("(%p)->(%p)\n", This, descriptor);
- return E_NOTIMPL; + if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + + if (!(This->pres_desc)) + { + return MF_E_NOT_INITIALIZED; + } + + IMFPresentationDescriptor_Clone(This->pres_desc, descriptor); + + return S_OK; }
static HRESULT WINAPI media_source_Start(IMFMediaSource *iface, IMFPresentationDescriptor *descriptor, const GUID *time_format, const PROPVARIANT *start_position) { struct media_source *This = impl_from_IMFMediaSource(iface); + int ret; + PROPVARIANT empty_var; + empty_var.vt = VT_EMPTY;
- FIXME("(%p)->(%p, %p, %p): stub\n", This, descriptor, time_format, start_position); + TRACE("(%p)->(%p, %p, %p)\n", This, descriptor, time_format, start_position);
- return E_NOTIMPL; + if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + + /* Find out which streams are active */ + for (unsigned int i = 0; i < This->stream_count; i++) + { + IMFStreamDescriptor *stream_desc; + DWORD in_stream_id; + BOOL selected; + + IMFPresentationDescriptor_GetStreamDescriptorByIndex(descriptor, i, &selected, &stream_desc); + IMFStreamDescriptor_GetStreamIdentifier(stream_desc, &in_stream_id); + + for (unsigned int k = 0; k < This->stream_count; k++) + { + DWORD cur_stream_id; + + IMFStreamDescriptor_GetStreamIdentifier(This->streams[k]->descriptor, &cur_stream_id); + + if (in_stream_id == cur_stream_id) + { + BOOL was_active = This->streams[k]->state != STREAM_INACTIVE; + This->streams[k]->state = selected ? STREAM_RUNNING : STREAM_INACTIVE; + if (selected) + { + IMFMediaEventQueue_QueueEventParamUnk(This->event_queue, + was_active ? MEUpdatedStream : MENewStream, &GUID_NULL, + S_OK, (IUnknown*) &This->streams[k]->IMFMediaStream_iface); + IMFMediaEventQueue_QueueEventParamVar(This->streams[k]->event_queue, + MEStreamStarted, &GUID_NULL, S_OK, &empty_var); + stream_dispatch_samples(This->streams[k]); + } + } + } + + IMFStreamDescriptor_Release(stream_desc); + } + + if (!(IsEqualIID(time_format, &GUID_NULL) && + (start_position->vt == VT_EMPTY || (start_position->vt == VT_I8 && start_position->u.hVal.QuadPart == 0)))) + { + ERR("unhandled start time\n"); + return MF_E_UNSUPPORTED_TIME_FORMAT; + } + + This->state = SOURCE_RUNNING; + gst_element_set_state(This->container, GST_STATE_PLAYING); + ret = gst_element_get_state(This->container, NULL, NULL, -1); + if (ret == GST_STATE_CHANGE_FAILURE) + { + ERR("Failed to play source.\n"); + return E_FAIL; + } + + IMFMediaEventQueue_QueueEventParamVar(This->event_queue, MESourceStarted, &GUID_NULL, S_OK, &empty_var); + + return S_OK; }
static HRESULT WINAPI media_source_Stop(IMFMediaSource *iface) @@ -148,6 +788,9 @@ static HRESULT WINAPI media_source_Stop(IMFMediaSource *iface)
FIXME("(%p): stub\n", This);
+ if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + return E_NOTIMPL; }
@@ -157,13 +800,42 @@ static HRESULT WINAPI media_source_Pause(IMFMediaSource *iface)
FIXME("(%p): stub\n", This);
+ if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + return E_NOTIMPL; }
static HRESULT media_source_teardown(struct media_source *This) { + if (This->my_src) + gst_object_unref(GST_OBJECT(This->my_src)); + if (This->their_sink) + gst_object_unref(GST_OBJECT(This->their_sink)); + if (This->container) + { + gst_element_set_state(This->container, GST_STATE_NULL); + gst_object_unref(GST_OBJECT(This->container)); + } + if (This->pres_desc) + IMFPresentationDescriptor_Release(This->pres_desc); if (This->event_queue) IMFMediaEventQueue_Release(This->event_queue); + if (This->byte_stream) + IMFByteStream_Release(This->byte_stream); + + for (unsigned int i = 0; i < This->stream_count; i++) + { + media_stream_teardown(This->streams[i]); + IMFMediaStream_Release(&This->streams[i]->IMFMediaStream_iface); + } + + if (This->stream_count) + heap_free(This->streams); + + if (This->init_complete_event) + CloseHandle(This->init_complete_event); + DeleteCriticalSection(&This->streams_cs);
return S_OK; } @@ -172,9 +844,10 @@ static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) { struct media_source *This = impl_from_IMFMediaSource(iface);
- FIXME("(%p): stub\n", This); + TRACE("(%p)\n", This);
- return E_NOTIMPL; + This->state = SOURCE_SHUTDOWN; + return media_source_teardown(This); }
static const IMFMediaSourceVtbl IMFMediaSource_vtbl = @@ -194,28 +867,459 @@ static const IMFMediaSourceVtbl IMFMediaSource_vtbl = media_source_Shutdown, };
+GstFlowReturn pull_from_bytestream(GstPad *pad, GstObject *parent, guint64 ofs, guint len, + GstBuffer **buf) +{ + struct media_source *This = gst_pad_get_element_private(pad); + IMFByteStream *byte_stream = This->byte_stream; + BOOL is_eof; + GstMapInfo info; + ULONG bytes_read; + HRESULT hr; + + TRACE("gstreamer requesting %u bytes at %s from source %p into buffer %p\n", len, wine_dbgstr_longlong(ofs), This, buf); + + if (ofs != GST_BUFFER_OFFSET_NONE) + { + if (FAILED(IMFByteStream_SetCurrentPosition(byte_stream, ofs))) + return GST_FLOW_ERROR; + } + + if (FAILED(IMFByteStream_IsEndOfStream(byte_stream, &is_eof))) + return GST_FLOW_ERROR; + if (is_eof) + return GST_FLOW_EOS; + + *buf = gst_buffer_new_and_alloc(len); + gst_buffer_map(*buf, &info, GST_MAP_WRITE); + hr = IMFByteStream_Read(byte_stream, info.data, len, &bytes_read); + gst_buffer_unmap(*buf, &info); + + gst_buffer_set_size(*buf, bytes_read); + + if (FAILED(hr)) + { + return GST_FLOW_ERROR; + } + GST_BUFFER_OFFSET(*buf) = ofs; + return GST_FLOW_OK; +} + +static gboolean query_bytestream(GstPad *pad, GstObject *parent, GstQuery *query) +{ + struct media_source *This = gst_pad_get_element_private(pad); + GstFormat format; + QWORD bytestream_len; + + TRACE("GStreamer queries source %p for %s\n", This, GST_QUERY_TYPE_NAME(query)); + + if (FAILED(IMFByteStream_GetLength(This->byte_stream, &bytestream_len))) + return FALSE; + + switch (GST_QUERY_TYPE(query)) + { + case GST_QUERY_DURATION: + { + gst_query_parse_duration (query, &format, NULL); + if (format == GST_FORMAT_PERCENT) { + gst_query_set_duration (query, GST_FORMAT_PERCENT, GST_FORMAT_PERCENT_MAX); + return TRUE; + } + return FALSE; + } + case GST_QUERY_SEEKING: + { + gst_query_parse_seeking (query, &format, NULL, NULL, NULL); + if (format != GST_FORMAT_BYTES) + { + WARN("Cannot seek using format "%s".\n", gst_format_get_name(format)); + return FALSE; + } + gst_query_set_seeking(query, GST_FORMAT_BYTES, 1, 0, bytestream_len); + return TRUE; + } + case GST_QUERY_SCHEDULING: + { + gst_query_set_scheduling(query, GST_SCHEDULING_FLAG_SEEKABLE, 1, -1, 0); + gst_query_add_scheduling_mode(query, GST_PAD_MODE_PULL); + return TRUE; + } + case GST_QUERY_CAPS: + { + GstCaps *caps, *filter; + + gst_query_parse_caps(query, &filter); + + caps = gst_static_caps_get(&source_descs[This->type].bytestream_caps); + + if (filter) { + GstCaps* filtered; + filtered = gst_caps_intersect_full( + filter, caps, GST_CAPS_INTERSECT_FIRST); + gst_caps_unref(caps); + caps = filtered; + } + gst_query_set_caps_result(query, caps); + gst_caps_unref(caps); + return TRUE; + } + default: + { + WARN("Unhandled query type %s\n", GST_QUERY_TYPE_NAME(query)); + return FALSE; + } + } +} + +static gboolean activate_bytestream_pad_mode(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate) +{ + struct media_source *source = gst_pad_get_element_private(pad); + + TRACE("%s source pad for mediasource %p in %s mode.\n", + activate ? "Activating" : "Deactivating", source, gst_pad_mode_get_name(mode)); + + switch (mode) { + case GST_PAD_MODE_PULL: + return TRUE; + default: + return FALSE; + } + return FALSE; +} + +static gboolean process_bytestream_pad_event(GstPad *pad, GstObject *parent, GstEvent *event) +{ + struct media_source *This = gst_pad_get_element_private(pad); + + TRACE("filter %p, type "%s".\n", This, GST_EVENT_TYPE_NAME(event)); + + switch (event->type) { + default: + WARN("Ignoring "%s" event.\n", GST_EVENT_TYPE_NAME(event)); + case GST_EVENT_TAG: + case GST_EVENT_QOS: + case GST_EVENT_RECONFIGURE: + return gst_pad_event_default(pad, parent, event); + } + return TRUE; +} + +static void source_stream_added(GstElement *element, GstPad *pad, gpointer user) +{ + struct media_stream *stream; + struct media_source *source = (struct media_source *) user; + struct media_stream **new_stream_array; + gchar *g_stream_id; + const char *stream_id_string; + DWORD stream_id; + + EnterCriticalSection(&source->streams_cs); + + g_stream_id = gst_pad_get_stream_id(pad); + stream_id_string = strstr(g_stream_id, "/"); + sscanf(stream_id_string, "/%03u", &stream_id); + TRACE("stream-id: %u\n", stream_id); + g_free(g_stream_id); + + /* find existing stream */ + for (unsigned int i = 0; i < source->stream_count; i++) + { + DWORD existing_stream_id; + IMFStreamDescriptor *descriptor = source->streams[i]->descriptor; + + if (FAILED(IMFStreamDescriptor_GetStreamIdentifier(descriptor, &existing_stream_id))) + goto leave; + + if (existing_stream_id == stream_id) + { + struct media_stream *existing_stream = source->streams[i]; + + TRACE("Found existing stream %p\n", existing_stream); + + if (!existing_stream->my_sink) + { + ERR("Couldn't find our sink\n"); + goto leave; + } + + existing_stream->their_src = pad; + gst_pad_set_element_private(pad, existing_stream); + + if (existing_stream->state != STREAM_INACTIVE) + { + GstPadLinkReturn err = gst_pad_link(existing_stream->their_src, existing_stream->my_sink); + if (err != GST_PAD_LINK_OK) + { + ERR("Error linking demuxer to stream %p, err = %d\n", existing_stream, err); + } + gst_element_sync_state_with_parent(existing_stream->appsink); + if (existing_stream->parser) + gst_element_sync_state_with_parent(existing_stream->parser); + } + goto leave; + } + } + + if (FAILED(media_stream_constructor(source, pad, stream_id, &stream))) + { + goto leave; + } + + if (!(new_stream_array = heap_realloc(source->streams, (source->stream_count + 1) * (sizeof(*new_stream_array))))) + { + ERR("Failed to add stream to source\n"); + goto leave; + } + + source->streams = new_stream_array; + source->streams[source->stream_count++] = stream; + + leave: + LeaveCriticalSection(&source->streams_cs); + return; +} + +static void source_stream_removed(GstElement *element, GstPad *pad, gpointer user) +{ + struct media_stream *stream; + + if (gst_pad_get_direction(pad) != GST_PAD_SRC) + { + return; + } + + stream = (struct media_stream *) gst_pad_get_element_private(pad); + + if (stream) + { + TRACE("Stream %p of Source %p removed\n", stream, stream->parent_source); + + if (stream->their_src != pad) + { + ERR("assert: unexpected pad/user combination!!!"); + return; + } + + gst_pad_unlink(stream->their_src, stream->my_sink); + + stream->their_src = NULL; + gst_pad_set_element_private(pad, NULL); + } +} + +static void source_all_streams(GstElement *element, gpointer user) +{ + IMFStreamDescriptor **descriptors; + struct media_source *source = (struct media_source *) user; + + EnterCriticalSection(&source->streams_cs); + if (source->state != SOURCE_OPENING) + goto leave; + + /* Init presentation descriptor */ + + descriptors = heap_alloc(source->stream_count * sizeof(IMFStreamDescriptor*)); + for (unsigned int i = 0; i < source->stream_count; i++) + { + IMFMediaStream_GetStreamDescriptor(&source->streams[i]->IMFMediaStream_iface, &descriptors[i]); + } + + if (FAILED(MFCreatePresentationDescriptor(source->stream_count, descriptors, &source->pres_desc))) + goto leave; + + for (unsigned int i = 0; i < source->stream_count; i++) + { + IMFStreamDescriptor_Release(descriptors[i]); + } + heap_free(descriptors); + + SetEvent(source->init_complete_event); + + leave: + LeaveCriticalSection(&source->streams_cs); +} + +static void media_source_notify_stream_ended(struct media_source *This) +{ + PROPVARIANT empty; + empty.vt = VT_EMPTY; + + /* A stream has ended, check whether all have */ + for (unsigned int i = 0; i < This->stream_count; i++) + { + struct media_stream *stream = This->streams[i]; + + if (!stream->eos) + return; + } + + IMFMediaEventQueue_QueueEventParamVar(This->event_queue, MEEndOfPresentation, &GUID_NULL, S_OK, &empty); +} + static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_type type, struct media_source **out_media_source) { + GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE( + "mf_src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + source_descs[type].bytestream_caps); + struct media_source *This = heap_alloc_zero(sizeof(*This)); + GList *demuxer_list_one, *demuxer_list_two; + GstElementFactory *demuxer_factory = NULL; + int ret; HRESULT hr;
if (!This) return E_OUTOFMEMORY;
- if (FAILED(hr = MFCreateEventQueue(&This->event_queue))) + This->container = gst_bin_new(NULL); + This->bus = gst_bus_new(); + gst_bus_set_sync_handler(This->bus, watch_source_bus_wrapper, This, NULL); + gst_element_set_bus(This->container, This->bus); + + /* Find demuxer */ + demuxer_list_one = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_DEMUXER, 1); + + demuxer_list_two = gst_element_factory_list_filter(demuxer_list_one, gst_static_caps_get(&source_descs[type].bytestream_caps), GST_PAD_SINK, 0); + gst_plugin_feature_list_free(demuxer_list_one); + + if (!(g_list_length(demuxer_list_two))) + { + ERR("Failed to find demuxer for source.\n"); + gst_plugin_feature_list_free(demuxer_list_two); + hr = E_FAIL; goto fail; + }
+ demuxer_factory = g_list_first(demuxer_list_two)->data; + gst_object_ref(demuxer_factory); + TRACE("Found demuxer %s.\n", GST_ELEMENT_NAME(demuxer_factory)); + + gst_plugin_feature_list_free(demuxer_list_two); + + This->type = type; + This->state = SOURCE_OPENING; + InitializeCriticalSection(&This->streams_cs); + This->init_complete_event = CreateEventA(NULL, TRUE, FALSE, NULL); + + /* Setup interface early as the streams interact with us during initialization */ This->IMFMediaSource_iface.lpVtbl = &IMFMediaSource_vtbl; This->ref = 1;
+ if (FAILED(hr = IMFByteStream_QueryInterface(bytestream, &IID_IMFByteStream, (void **)&This->byte_stream))) + { + goto fail; + } + + if (FAILED(hr = MFCreateEventQueue(&This->event_queue))) + goto fail; + + /* create demuxer */ + + This->my_src = gst_pad_new_from_static_template(&src_template, "mf-src"); + gst_pad_set_element_private(This->my_src, This); + gst_pad_set_getrange_function(This->my_src, pull_from_bytestream_wrapper); + gst_pad_set_query_function(This->my_src, query_bytestream_wrapper); + gst_pad_set_activatemode_function(This->my_src, activate_bytestream_pad_mode_wrapper); + gst_pad_set_event_function(This->my_src, process_bytestream_pad_event_wrapper); + + This->demuxer = gst_element_factory_create(demuxer_factory, NULL); + if (!(This->demuxer)) + { + WARN("Failed to create demuxer for source\n"); + hr = E_OUTOFMEMORY; + goto fail; + } + gst_bin_add(GST_BIN(This->container), This->demuxer); + + This->their_sink = gst_element_get_static_pad(This->demuxer, "sink"); + + if ((ret = gst_pad_link(This->my_src, This->their_sink)) < 0) + { + WARN("Failed to link our bytestream pad to the demuxer input\n"); + hr = E_OUTOFMEMORY; + goto fail; + } + + g_signal_connect(This->demuxer, "pad-added", G_CALLBACK(source_stream_added_wrapper), This); + g_signal_connect(This->demuxer, "pad-removed", G_CALLBACK(source_stream_removed_wrapper), This); + g_signal_connect(This->demuxer, "no-more-pads", G_CALLBACK(source_all_streams_wrapper), This); + + gst_element_set_state(This->container, GST_STATE_PAUSED); + ret = gst_element_get_state(This->container, NULL, NULL, -1); + if (ret == GST_STATE_CHANGE_FAILURE) + { + ERR("Failed to play source.\n"); + hr = E_OUTOFMEMORY; + goto fail; + } + + WaitForSingleObject(This->init_complete_event, INFINITE); + CloseHandle(This->init_complete_event); + This->init_complete_event = NULL; + + /* miscelaneous presentation descriptor setup */ + { + IMFAttributes *byte_stream_attributes; + gint64 total_pres_time = 0; + + if (SUCCEEDED(IMFByteStream_QueryInterface(This->byte_stream, &IID_IMFAttributes, (void **)&byte_stream_attributes))) + { + WCHAR *mimeW = NULL; + DWORD length; + if (SUCCEEDED(IMFAttributes_GetAllocatedString(byte_stream_attributes, &MF_BYTESTREAM_CONTENT_TYPE, &mimeW, &length))) + { + IMFPresentationDescriptor_SetString(This->pres_desc, &MF_PD_MIME_TYPE, mimeW); + CoTaskMemFree(mimeW); + } + IMFAttributes_Release(byte_stream_attributes); + } + + for (unsigned int i = 0; i < This->stream_count; i++) + { + GstQuery *query = gst_query_new_duration(GST_FORMAT_TIME); + if (gst_pad_query(This->streams[i]->their_src, query)) + { + gint64 stream_pres_time; + gst_query_parse_duration(query, NULL, &stream_pres_time); + + TRACE("Stream %u has duration %lu\n", i, stream_pres_time); + + if (stream_pres_time > total_pres_time) + total_pres_time = stream_pres_time; + } + else + { + WARN("Unable to get presentation time of stream %u\n", i); + } + } + + if (This->stream_count) + IMFPresentationDescriptor_SetUINT64(This->pres_desc, &MF_PD_DURATION, total_pres_time / 100); + } + + gst_pad_set_active(This->my_src, 1); + gst_element_set_state(This->container, GST_STATE_READY); + if (!(This->pres_desc)) + { + hr = E_FAIL; + goto fail; + } + + This->state = SOURCE_STOPPED; + *out_media_source = This; return S_OK;
fail: WARN("Failed to construct MFMediaSource, hr %#x.\n", hr);
+ if (demuxer_factory) + gst_object_unref(demuxer_factory); media_source_teardown(This); heap_free(This); + *out_media_source = NULL; return hr; }
@@ -573,6 +1677,9 @@ static HRESULT container_stream_handler_create_object(struct container_stream_ha { TRACE("(%p %s %p %u %p %p %p)\n", This, debugstr_w(url), stream, flags, props, out_object, out_obj_type);
+ if (!(init_gstreamer())) + return E_FAIL; + if (flags & MF_RESOLUTION_MEDIASOURCE) { HRESULT hr; @@ -676,4 +1783,78 @@ HRESULT container_stream_handler_construct(REFIID riid, void **obj, enum source_ IMFByteStreamHandler_Release(&this->IMFByteStreamHandler_iface);
return hr; -} \ No newline at end of file +} + +/* helper for callback forwarding */ +void perform_cb_media_source(struct cb_data *cbdata) +{ + switch(cbdata->type) + { + case PULL_FROM_BYTESTREAM: + { + struct getrange_data *data = &cbdata->u.getrange_data; + cbdata->u.getrange_data.ret = pull_from_bytestream(data->pad, data->parent, + data->ofs, data->len, data->buf); + break; + } + case QUERY_BYTESTREAM: + { + struct query_function_data *data = &cbdata->u.query_function_data; + cbdata->u.query_function_data.ret = query_bytestream(data->pad, data->parent, data->query); + break; + } + case ACTIVATE_BYTESTREAM_PAD_MODE: + { + struct activate_mode_data *data = &cbdata->u.activate_mode_data; + cbdata->u.activate_mode_data.ret = activate_bytestream_pad_mode(data->pad, data->parent, data->mode, data->activate); + break; + } + case PROCESS_BYTESTREAM_PAD_EVENT: + { + struct event_src_data *data = &cbdata->u.event_src_data; + cbdata->u.event_src_data.ret = process_bytestream_pad_event(data->pad, data->parent, data->event); + break; + } + case SOURCE_STREAM_ADDED: + { + struct pad_added_data *data = &cbdata->u.pad_added_data; + source_stream_added(data->element, data->pad, data->user); + break; + } + case SOURCE_STREAM_REMOVED: + { + struct pad_removed_data *data = &cbdata->u.pad_removed_data; + source_stream_removed(data->element, data->pad, data->user); + break; + } + case SOURCE_ALL_STREAMS: + { + struct no_more_pads_data *data = &cbdata->u.no_more_pads_data; + source_all_streams(data->element, data->user); + break; + } + case STREAM_NEW_SAMPLE: + { + struct new_sample_data *data = &cbdata->u.new_sample_data; + cbdata->u.new_sample_data.ret = stream_new_sample(data->appsink, data->user); + break; + } + case STREAM_EOS: + { + struct eos_data *data = &cbdata->u.eos_data; + stream_eos(data->appsink, data->user); + break; + } + case WATCH_SOURCE_BUS: + { + struct watch_bus_data *data = &cbdata->u.watch_bus_data; + cbdata->u.watch_bus_data.ret = watch_source_bus(data->bus, data->msg, data->user); + break; + } + default: + { + ERR("Wrong callback forwarder called\n"); + return; + } + } +}
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=68147
Your paranoid android.
=== debiant (32 bit report) ===
mfplat: mfplat.c:507: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:510: Test failed: Unexpected major type {0063fb28-fb20-0063-0400-000020fb6300}. mfplat.c:513: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on read access to 0x00000004 in 32-bit code (0x0040eca1).
=== debiant (32 bit French report) ===
mfplat: mfplat.c:507: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:510: Test failed: Unexpected major type {0063fb28-fb20-0063-0400-000020fb6300}. mfplat.c:513: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on read access to 0x00000004 in 32-bit code (0x0040eca1).
=== debiant (32 bit Japanese:Japan report) ===
mfplat: mfplat.c:507: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:510: Test failed: Unexpected major type {0063fb28-fb20-0063-0400-000020fb6300}. mfplat.c:513: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on read access to 0x00000004 in 32-bit code (0x0040eca1).
Report validation errors: mfplat:mfplat crashed (c0000005)
=== debiant (32 bit Chinese:China report) ===
mfplat: mfplat.c:507: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:510: Test failed: Unexpected major type {0063fb28-fb20-0063-0400-000020fb6300}. mfplat.c:513: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on read access to 0x00000004 in 32-bit code (0x0040eca1).
=== debiant (32 bit WoW report) ===
mfplat: mfplat.c:507: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:510: Test failed: Unexpected major type {0063fb28-fb20-0063-0400-000020fb6300}. mfplat.c:513: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on read access to 0x00000004 in 32-bit code (0x0040eca1).
=== debiant (64 bit WoW report) ===
mfplat: mfplat.c:507: Test failed: Failed to get stream major type, hr 0xc00d36e6. mfplat.c:510: Test failed: Unexpected major type {0063fb28-fb20-0063-0400-000020fb6300}. mfplat.c:513: Test failed: Failed to get current media type, hr 0xc00d36b6. Unhandled exception: page fault on read access to 0x00000004 in 32-bit code (0x0040eca1).
Report validation errors: mfplat:mfplat crashed (c0000005)
On 3/26/20 3:12 AM, Derek Lesho wrote:
+static ULONG WINAPI media_stream_Release(IMFMediaStream *iface) +{
- struct media_stream *This = impl_from_IMFMediaStream(iface);
- ULONG ref = InterlockedDecrement(&This->ref);
- TRACE("(%p) ref=%u\n", This, ref);
- if (!ref)
- {
if (This->state != STREAM_SHUTDOWN)
ERR("incomplete cleanup\n");
heap_free(This);
- }
- return ref;
+}
Error message here is redundant I think. The point of shutdown is to break circular references, where source holds streams references and every stream hold source reference. So in theory you shouldn't be able to release unless you shut it down.
+static HRESULT WINAPI media_stream_GetEvent(IMFMediaStream *iface, DWORD flags, IMFMediaEvent **event) +{
- struct media_stream *This = impl_from_IMFMediaStream(iface);
- TRACE("(%p)->(%#x, %p)\n", This, flags, event);
- if (This->state == STREAM_SHUTDOWN)
return MF_E_SHUTDOWN;
- return IMFMediaEventQueue_GetEvent(This->event_queue, flags, event);
+}
Same as with the source, you only need to shutdown the queue and it will return MF_E_SHUTDOWN for you.
+static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IMFStreamDescriptor **descriptor) +{
- struct media_stream *This = impl_from_IMFMediaStream(iface);
- TRACE("(%p)->(%p)\n", This, descriptor);
- if (This->state == STREAM_SHUTDOWN)
return MF_E_SHUTDOWN;
- IMFStreamDescriptor_AddRef(This->descriptor);
- *descriptor = This->descriptor;
- return S_OK;
+}
It's not obvious that it's correct but could be problematic to test properly, because it could differ between implementations.
- req = heap_alloc(sizeof(*req));
- if (token)
IUnknown_AddRef(token);
- req->token = token;
- list_add_tail(&This->sample_requests, &req->entry);
This should be protected, there is no guarantee client is using the same thread to make requests, or serializes them.
@@ -118,28 +684,102 @@ static HRESULT WINAPI media_source_GetCharacteristics(IMFMediaSource *iface, DWO { struct media_source *This = impl_from_IMFMediaSource(iface);
- FIXME("(%p)->(%p): stub\n", This, characteristics);
- TRACE("(%p)->(%p)\n", This, characteristics);
- return E_NOTIMPL;
- if (This->state == SOURCE_SHUTDOWN)
return MF_E_SHUTDOWN;
- *characteristics = 0;
- return S_OK; }
Returning 0 flags will prevent seeking. It should be derived from bytestream caps + gstreamer object constraints if any. There is a number of flags, but CAN_PAUSE/CAN_SEEK are probably most important ones.
static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource *iface, IMFPresentationDescriptor **descriptor) { struct media_source *This = impl_from_IMFMediaSource(iface);
- FIXME("(%p)->(%p): stub\n", This, descriptor);
- TRACE("(%p)->(%p)\n", This, descriptor);
- return E_NOTIMPL;
- if (This->state == SOURCE_SHUTDOWN)
return MF_E_SHUTDOWN;
- if (!(This->pres_desc))
- {
return MF_E_NOT_INITIALIZED;
- }
- IMFPresentationDescriptor_Clone(This->pres_desc, descriptor);
- return S_OK; }
Is NOT_INITIALIZED path reachable? I think it's generally assumed that once you got a source you can get its descriptor, even if stream configuration can change dynamically later.
On 3/26/20 12:23 AM, Nikolay Sivov wrote:
On 3/26/20 3:12 AM, Derek Lesho wrote:
+static ULONG WINAPI media_stream_Release(IMFMediaStream *iface) +{ +Â Â Â struct media_stream *This = impl_from_IMFMediaStream(iface);
+Â Â Â ULONG ref = InterlockedDecrement(&This->ref);
+Â Â Â TRACE("(%p) ref=%u\n", This, ref);
+Â Â Â if (!ref) +Â Â Â { +Â Â Â Â Â Â Â if (This->state != STREAM_SHUTDOWN) +Â Â Â Â Â Â Â Â Â Â Â ERR("incomplete cleanup\n"); +Â Â Â Â Â Â Â heap_free(This); +Â Â Â }
+Â Â Â return ref; +}
Error message here is redundant I think. The point of shutdown is to break circular references, where source holds streams references and every stream hold source reference. So in theory you shouldn't be able to release unless you shut it down.
+static HRESULT WINAPI media_stream_GetEvent(IMFMediaStream *iface, DWORD flags, IMFMediaEvent **event) +{ +Â Â Â struct media_stream *This = impl_from_IMFMediaStream(iface);
+Â Â Â TRACE("(%p)->(%#x, %p)\n", This, flags, event);
+Â Â Â if (This->state == STREAM_SHUTDOWN) +Â Â Â Â Â Â Â return MF_E_SHUTDOWN;
+Â Â Â return IMFMediaEventQueue_GetEvent(This->event_queue, flags, event); +}
Same as with the source, you only need to shutdown the queue and it will return MF_E_SHUTDOWN for you.
+static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IMFStreamDescriptor **descriptor) +{ +Â Â Â struct media_stream *This = impl_from_IMFMediaStream(iface);
+Â Â Â TRACE("(%p)->(%p)\n", This, descriptor);
+Â Â Â if (This->state == STREAM_SHUTDOWN) +Â Â Â Â Â Â Â return MF_E_SHUTDOWN;
+Â Â Â IMFStreamDescriptor_AddRef(This->descriptor); +Â Â Â *descriptor = This->descriptor;
+Â Â Â return S_OK; +}
It's not obvious that it's correct but could be problematic to test properly, because it could differ between implementations.
What do you mean? What might not be correct?
+Â Â Â req = heap_alloc(sizeof(*req)); +Â Â Â if (token) +Â Â Â Â Â Â Â IUnknown_AddRef(token); +Â Â Â req->token = token; +Â Â Â list_add_tail(&This->sample_requests, &req->entry);
This should be protected, there is no guarantee client is using the same thread to make requests, or serializes them.
@@ -118,28 +684,102 @@ static HRESULT WINAPI media_source_GetCharacteristics(IMFMediaSource *iface, DWO Â { Â Â Â Â Â struct media_source *This = impl_from_IMFMediaSource(iface); Â -Â Â Â FIXME("(%p)->(%p): stub\n", This, characteristics); +Â Â Â TRACE("(%p)->(%p)\n", This, characteristics); Â -Â Â Â return E_NOTIMPL; +Â Â Â if (This->state == SOURCE_SHUTDOWN) +Â Â Â Â Â Â Â return MF_E_SHUTDOWN;
+Â Â Â *characteristics = 0;
+Â Â Â return S_OK; Â }
Returning 0 flags will prevent seeking. It should be derived from bytestream caps + gstreamer object constraints if any. There is a number of flags, but CAN_PAUSE/CAN_SEEK are probably most important ones.
Right now I haven't implemented pausing/seeking, shouldn't we wait until we support it before adding the characteristics?
static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource *iface, IMFPresentationDescriptor **descriptor) Â { Â Â Â Â Â struct media_source *This = impl_from_IMFMediaSource(iface); Â -Â Â Â FIXME("(%p)->(%p): stub\n", This, descriptor); +Â Â Â TRACE("(%p)->(%p)\n", This, descriptor); Â -Â Â Â return E_NOTIMPL; +Â Â Â if (This->state == SOURCE_SHUTDOWN) +Â Â Â Â Â Â Â return MF_E_SHUTDOWN;
+Â Â Â if (!(This->pres_desc)) +Â Â Â { +Â Â Â Â Â Â Â return MF_E_NOT_INITIALIZED; +Â Â Â }
+Â Â Â IMFPresentationDescriptor_Clone(This->pres_desc, descriptor);
+Â Â Â return S_OK; Â }
Is NOT_INITIALIZED path reachable? I think it's generally assumed that once you got a source you can get its descriptor, even if stream configuration can change dynamically later.
On 3/26/20 5:13 PM, Derek Lesho wrote:
On 3/26/20 12:23 AM, Nikolay Sivov wrote:
On 3/26/20 3:12 AM, Derek Lesho wrote:
+static ULONG WINAPI media_stream_Release(IMFMediaStream *iface) +{ +Â Â Â struct media_stream *This = impl_from_IMFMediaStream(iface);
+Â Â Â ULONG ref = InterlockedDecrement(&This->ref);
+Â Â Â TRACE("(%p) ref=%u\n", This, ref);
+Â Â Â if (!ref) +Â Â Â { +Â Â Â Â Â Â Â if (This->state != STREAM_SHUTDOWN) +Â Â Â Â Â Â Â Â Â Â Â ERR("incomplete cleanup\n"); +Â Â Â Â Â Â Â heap_free(This); +Â Â Â }
+Â Â Â return ref; +}
Error message here is redundant I think. The point of shutdown is to break circular references, where source holds streams references and every stream hold source reference. So in theory you shouldn't be able to release unless you shut it down.
+static HRESULT WINAPI media_stream_GetEvent(IMFMediaStream *iface, DWORD flags, IMFMediaEvent **event) +{ +Â Â Â struct media_stream *This = impl_from_IMFMediaStream(iface);
+Â Â Â TRACE("(%p)->(%#x, %p)\n", This, flags, event);
+Â Â Â if (This->state == STREAM_SHUTDOWN) +Â Â Â Â Â Â Â return MF_E_SHUTDOWN;
+Â Â Â return IMFMediaEventQueue_GetEvent(This->event_queue, flags, event); +}
Same as with the source, you only need to shutdown the queue and it will return MF_E_SHUTDOWN for you.
+static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IMFStreamDescriptor **descriptor) +{ +Â Â Â struct media_stream *This = impl_from_IMFMediaStream(iface);
+Â Â Â TRACE("(%p)->(%p)\n", This, descriptor);
+Â Â Â if (This->state == STREAM_SHUTDOWN) +Â Â Â Â Â Â Â return MF_E_SHUTDOWN;
+Â Â Â IMFStreamDescriptor_AddRef(This->descriptor); +Â Â Â *descriptor = This->descriptor;
+Â Â Â return S_OK; +}
It's not obvious that it's correct but could be problematic to test properly, because it could differ between implementations.
What do you mean? What might not be correct?
Failing to return descriptor.
+Â Â Â req = heap_alloc(sizeof(*req)); +Â Â Â if (token) +Â Â Â Â Â Â Â IUnknown_AddRef(token); +Â Â Â req->token = token; +Â Â Â list_add_tail(&This->sample_requests, &req->entry);
This should be protected, there is no guarantee client is using the same thread to make requests, or serializes them.
@@ -118,28 +684,102 @@ static HRESULT WINAPI media_source_GetCharacteristics(IMFMediaSource *iface, DWO Â { Â Â Â Â Â struct media_source *This = impl_from_IMFMediaSource(iface); Â -Â Â Â FIXME("(%p)->(%p): stub\n", This, characteristics); +Â Â Â TRACE("(%p)->(%p)\n", This, characteristics); Â -Â Â Â return E_NOTIMPL; +Â Â Â if (This->state == SOURCE_SHUTDOWN) +Â Â Â Â Â Â Â return MF_E_SHUTDOWN;
+Â Â Â *characteristics = 0;
+Â Â Â return S_OK; Â }
Returning 0 flags will prevent seeking. It should be derived from bytestream caps + gstreamer object constraints if any. There is a number of flags, but CAN_PAUSE/CAN_SEEK are probably most important ones.
Right now I haven't implemented pausing/seeking, shouldn't we wait until we support it before adding the characteristics?
Sure, I don't know why I assumed seeking was supposed to work.
static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource *iface, IMFPresentationDescriptor **descriptor) Â { Â Â Â Â Â struct media_source *This = impl_from_IMFMediaSource(iface); Â -Â Â Â FIXME("(%p)->(%p): stub\n", This, descriptor); +Â Â Â TRACE("(%p)->(%p)\n", This, descriptor); Â -Â Â Â return E_NOTIMPL; +Â Â Â if (This->state == SOURCE_SHUTDOWN) +Â Â Â Â Â Â Â return MF_E_SHUTDOWN;
+Â Â Â if (!(This->pres_desc)) +Â Â Â { +Â Â Â Â Â Â Â return MF_E_NOT_INITIALIZED; +Â Â Â }
+Â Â Â IMFPresentationDescriptor_Clone(This->pres_desc, descriptor);
+Â Â Â return S_OK; Â }
Is NOT_INITIALIZED path reachable? I think it's generally assumed that once you got a source you can get its descriptor, even if stream configuration can change dynamically later.
On 3/25/20 7:12 PM, Derek Lesho wrote:
Signed-off-by: Derek Lesho dlesho@codeweavers.com
dlls/mfplat/tests/mfplat.c | 11 +- dlls/winegstreamer/gst_cbs.c | 125 +++ dlls/winegstreamer/gst_cbs.h | 33 +- dlls/winegstreamer/media_source.c | 1201 ++++++++++++++++++++++++++++- 4 files changed, 1349 insertions(+), 21 deletions(-)
This is a huge amount of code in one patch, and subsequently difficult to review.
If you're having trouble splitting it, I'd suggest implementing IMFMediaSource functions one at a time, and e.g. handling GStreamer events one at a time. Individual discretion is advised, of course.
On 3/26/20 3:12 AM, Derek Lesho wrote:
- TRACE("Fufilled %u sample requests. %u pending samples. %s more requests.\n",
fufilled_counter, This->pending_samples, list_empty(&This->sample_requests) ? "no" : "");
I was testing these winegstreamer patches today, in context of media session, and noticed that right after Start() call and before I had a chance to make any sample requests, I get trace above showing hundreds of pending samples already. Does it mean source buffers whole media unconditionally, or is there a limit to it?
Even if requests will normally come soon after start, it will at least take time to deliver source/stream state events, which takes arbitrary time, or may never happen. If it's really a case of excessive buffering we'll have to limit it somehow.
On 3/27/20 6:28 AM, Nikolay Sivov wrote:
On 3/26/20 3:12 AM, Derek Lesho wrote:
+   TRACE("Fufilled %u sample requests. %u pending samples. %s more requests.\n", +           fufilled_counter, This->pending_samples, list_empty(&This->sample_requests) ? "no" : "");
I was testing these winegstreamer patches today, in context of media session, and noticed that right after Start() call and before I had a chance to make any sample requests, I get trace above showing hundreds of pending samples already. Does it mean source buffers whole media unconditionally, or is there a limit to it?
Even if requests will normally come soon after start, it will at least take time to deliver source/stream state events, which takes arbitrary time, or may never happen. If it's really a case of excessive buffering we'll have to limit it somehow.
Yeah, by default appsink infinitely buffers, we can change this by setting the max-buffers property of the element.
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- tools/make_makefiles | 45 +++++++++++++++++++++++++++----------------- tools/makedep.c | 30 ++++++++++++++++++++--------- 2 files changed, 49 insertions(+), 26 deletions(-)
diff --git a/tools/make_makefiles b/tools/make_makefiles index c18fa90e2d..1e34a280a4 100755 --- a/tools/make_makefiles +++ b/tools/make_makefiles @@ -231,14 +231,14 @@ sub parse_makefile($) { die "Configure substitution is not allowed in $file" unless $file eq "Makefile"; } - if (/^\s*(MODULE|IMPORTLIB|TESTDLL|PARENTSRC|APPMODE|EXTRADLLFLAGS)\s*=\s*(.*)/) + if (/^\s*(MODULE|IMPORTLIB|TESTDLL|APPMODE|EXTRADLLFLAGS)\s*=\s*(.*)/) { my $var = $1; $make{$var} = $2; next; } my $source_vars_regexp = join "|", @source_vars; - if (/^\s*($source_vars_regexp|PROGRAMS|EXTRA_TARGETS|EXTRA_OBJS|INSTALL_LIB|INSTALL_DEV)\s*=\s*(.*)/) + if (/^\s*($source_vars_regexp|PROGRAMS|EXTRA_TARGETS|EXTRA_OBJS|INSTALL_LIB|INSTALL_DEV|PARENTSRC)\s*=\s*(.*)/) { my $var = $1; my @list = split(/\s+/, $2); @@ -293,19 +293,27 @@ sub get_makedep_flags($) return %flags; }
-sub get_parent_makefile($) +sub get_parent_makefiles($) { my $file = shift; my %make = %{$makefiles{$file}}; - my $reldir = $make{"PARENTSRC"} || ""; - return "" unless $reldir; - (my $path = $file) =~ s//Makefile$///; - while ($reldir =~ /^..//) + my $pointer = $make{"PARENTSRC"} || (); + return () unless $pointer; + my @reldirs = @{$pointer}; + my @makefiles = (); + foreach my $reldir (@reldirs) { - $reldir =~ s/^..///; - $path =~ s/[^/]+/$//; + my $length = @reldirs; + (my $path = $file) =~ s//Makefile$///; + while ($reldir =~ /^..//) + { + $reldir =~ s/^..///; + $path =~ s/[^/]+/$//; + } + push @makefiles, "$path$reldir/Makefile"; } - return "$path$reldir/Makefile"; + + return @makefiles }
# preserve shared source files that are listed in the existing makefile @@ -410,13 +418,16 @@ sub assign_sources_to_makefiles(@) foreach my $file (@makefiles) { my $make = $makefiles{$file}; - my $parent = get_parent_makefile( $file ); - next unless $parent; - preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "C_SRCS" ); - preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "RC_SRCS" ); - preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "IDL_SRCS" ); - preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "LEX_SRCS" ); - preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "BISON_SRCS" ); + my @parents = get_parent_makefiles( $file ); + next unless @parents; + foreach my $parent (@parents) + { + preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "C_SRCS" ); + preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "RC_SRCS" ); + preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "IDL_SRCS" ); + preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "LEX_SRCS" ); + preserve_shared_source_files( $makefiles{$file}, $makefiles{$parent}, "BISON_SRCS" ); + } } }
diff --git a/tools/makedep.c b/tools/makedep.c index 4f19231e7c..31f577d49c 100644 --- a/tools/makedep.c +++ b/tools/makedep.c @@ -182,6 +182,7 @@ struct makefile struct strarray install_lib; struct strarray install_dev; struct strarray extra_targets; + struct strarray parent_dirs; struct list sources; struct list includes; const char *base_dir; @@ -189,7 +190,6 @@ struct makefile const char *obj_dir; const char *top_src_dir; const char *top_obj_dir; - const char *parent_dir; const char *module; const char *testdll; const char *sharedlib; @@ -1373,15 +1373,24 @@ static struct file *open_local_file( const struct makefile *make, const char *pa { char *src_path = root_dir_path( base_dir_path( make, path )); struct file *ret = load_file( src_path ); + unsigned int i;
- /* if not found, try parent dir */ - if (!ret && make->parent_dir) + /* if not found, try parent dirs */ + for (i = 0; !ret && i < make->parent_dirs.count; i++) { + char *new_path; + free( src_path ); - path = strmake( "%s/%s", make->parent_dir, path ); - src_path = root_dir_path( base_dir_path( make, path )); + new_path = strmake( "%s/%s", make->parent_dirs.str[i], path ); + src_path = root_dir_path( base_dir_path( make, new_path )); ret = load_file( src_path ); - if (ret) ret->flags |= FLAG_PARENTDIR; + if (ret) + { + ret->flags |= FLAG_PARENTDIR; + path = new_path; + } + else + free(new_path); }
if (ret) *filename = src_dir_path( make, path ); @@ -4186,13 +4195,13 @@ static void load_sources( struct makefile *make ) strarray_set_value( &make->vars, "top_srcdir", top_src_dir_path( make, "" )); strarray_set_value( &make->vars, "srcdir", src_dir_path( make, "" ));
- make->parent_dir = get_expanded_make_variable( make, "PARENTSRC" ); make->module = get_expanded_make_variable( make, "MODULE" ); make->testdll = get_expanded_make_variable( make, "TESTDLL" ); make->sharedlib = get_expanded_make_variable( make, "SHAREDLIB" ); make->staticlib = get_expanded_make_variable( make, "STATICLIB" ); make->importlib = get_expanded_make_variable( make, "IMPORTLIB" );
+ make->parent_dirs = get_expanded_make_var_array( make, "PARENTSRC" ); make->programs = get_expanded_make_var_array( make, "PROGRAMS" ); make->scripts = get_expanded_make_var_array( make, "SCRIPTS" ); make->imports = get_expanded_make_var_array( make, "IMPORTS" ); @@ -4237,8 +4246,11 @@ static void load_sources( struct makefile *make ) strarray_add( &make->include_args, strmake( "-I%s", obj_dir_path( make, "" ))); if (make->src_dir) strarray_add( &make->include_args, strmake( "-I%s", make->src_dir )); - if (make->parent_dir) - strarray_add( &make->include_args, strmake( "-I%s", src_dir_path( make, make->parent_dir ))); + if (make->parent_dirs.count) + { + for (i = 0; i < make->parent_dirs.count; i++) + strarray_add( &make->include_args, strmake( "-I%s", src_dir_path( make, make->parent_dirs.str[i] ))); + } strarray_add( &make->include_args, strmake( "-I%s", top_obj_dir_path( make, "include" ))); if (make->top_src_dir) strarray_add( &make->include_args, strmake( "-I%s", top_src_dir_path( make, "include" )));
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/mf/Makefile.in | 1 + dlls/mf/handler.c | 357 ++++++++++++++++++++++++++++ dlls/mf/handler.h | 30 +++ dlls/mf/main.c | 370 ++++-------------------------- dlls/winegstreamer/Makefile.in | 3 +- dlls/winegstreamer/media_source.c | 332 ++------------------------- 6 files changed, 444 insertions(+), 649 deletions(-) create mode 100644 dlls/mf/handler.c create mode 100644 dlls/mf/handler.h
diff --git a/dlls/mf/Makefile.in b/dlls/mf/Makefile.in index fe156e43ab..5c69ac38e5 100644 --- a/dlls/mf/Makefile.in +++ b/dlls/mf/Makefile.in @@ -5,6 +5,7 @@ IMPORTS = advapi32 mfplat ole32 uuid mfuuid EXTRADLLFLAGS = -mno-cygwin
C_SRCS = \ + handler.c \ main.c \ samplegrabber.c \ sar.c \ diff --git a/dlls/mf/handler.c b/dlls/mf/handler.c new file mode 100644 index 0000000000..db8783a7cd --- /dev/null +++ b/dlls/mf/handler.c @@ -0,0 +1,357 @@ +#include <stdarg.h> + +#define COBJMACROS + +#include "windef.h" +#include "winbase.h" +#include "mfidl.h" + +#include "mfapi.h" +#include "mferror.h" + +#include "handler.h" + +#include "wine/debug.h" +#include "wine/heap.h" +#include "wine/list.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mfplat); + +struct create_object_context +{ + IUnknown IUnknown_iface; + LONG refcount; + + IPropertyStore *props; + IMFByteStream *stream; + WCHAR *url; + DWORD flags; +}; + +struct handler_result +{ + struct list entry; + IMFAsyncResult *result; + MF_OBJECT_TYPE obj_type; + IUnknown *object; +}; + +static struct create_object_context *impl_from_IUnknown(IUnknown *iface) +{ + return CONTAINING_RECORD(iface, struct create_object_context, IUnknown_iface); +} + +static HRESULT WINAPI create_object_context_QueryInterface(IUnknown *iface, REFIID riid, void **obj) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IUnknown_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI create_object_context_AddRef(IUnknown *iface) +{ + struct create_object_context *context = impl_from_IUnknown(iface); + ULONG refcount = InterlockedIncrement(&context->refcount); + + TRACE("%p, refcount %u.\n", iface, refcount); + + return refcount; +} + +static ULONG WINAPI create_object_context_Release(IUnknown *iface) +{ + struct create_object_context *context = impl_from_IUnknown(iface); + ULONG refcount = InterlockedDecrement(&context->refcount); + + TRACE("%p, refcount %u.\n", iface, refcount); + + if (!refcount) + { + if (context->props) + IPropertyStore_Release(context->props); + if (context->stream) + IMFByteStream_Release(context->stream); + if (context->url) + heap_free(context->url); + heap_free(context); + } + + return refcount; +} + +static const IUnknownVtbl create_object_context_vtbl = +{ + create_object_context_QueryInterface, + create_object_context_AddRef, + create_object_context_Release, +}; + +/* Start async methods */ +static struct handler *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct handler, IMFAsyncCallback_iface); +} + +static HRESULT WINAPI handler_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **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; +} + +/* lifetime not managed with COM */ +static ULONG WINAPI handler_callback_AddRef(IMFAsyncCallback *iface) +{ + return 2; +} + +static ULONG WINAPI handler_callback_Release(IMFAsyncCallback *iface) +{ + return 1; +} + +static HRESULT WINAPI handler_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI handler_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct handler *handler = impl_from_IMFAsyncCallback(iface); + struct handler_result *handler_result; + MF_OBJECT_TYPE obj_type = MF_OBJECT_INVALID; + IUnknown *object = NULL, *context_object; + struct create_object_context *context; + IMFAsyncResult *caller; + HRESULT hr; + + caller = (IMFAsyncResult *)IMFAsyncResult_GetStateNoAddRef(result); + + if (FAILED(hr = IMFAsyncResult_GetObject(result, &context_object))) + { + WARN("Expected context set for callee result.\n"); + return hr; + } + + context = impl_from_IUnknown(context_object); + + hr = handler->create_object(handler, context->url, context->stream, context->flags, context->props, &object, &obj_type); + + handler_result = heap_alloc(sizeof(*handler_result)); + if (handler_result) + { + handler_result->result = caller; + IMFAsyncResult_AddRef(handler_result->result); + handler_result->obj_type = obj_type; + handler_result->object = object; + + EnterCriticalSection(&handler->cs); + list_add_tail(&handler->results, &handler_result->entry); + LeaveCriticalSection(&handler->cs); + } + else + { + if (object) + IUnknown_Release(object); + hr = E_OUTOFMEMORY; + } + + IUnknown_Release(&context->IUnknown_iface); + + IMFAsyncResult_SetStatus(caller, hr); + MFInvokeCallback(caller); + + return S_OK; +} + +static const IMFAsyncCallbackVtbl handler_callback_vtbl = +{ + handler_callback_QueryInterface, + handler_callback_AddRef, + handler_callback_Release, + handler_callback_GetParameters, + handler_callback_Invoke, +}; + +/* Start handler helpers */ + +static WCHAR *heap_strdupW(const WCHAR *str) +{ + WCHAR *ret = NULL; + + if (str) + { + unsigned int size; + + size = (lstrlenW(str) + 1) * sizeof(WCHAR); + ret = heap_alloc(size); + if (ret) + memcpy(ret, str, size); + } + + return ret; +} + +HRESULT handler_begin_create_object(struct handler *handler, IMFByteStream *stream, + const WCHAR *url, DWORD flags, IPropertyStore *props, IUnknown **cancel_cookie, + IMFAsyncCallback *callback, IUnknown *state) +{ + struct create_object_context *context; + IMFAsyncResult *caller, *item; + HRESULT hr; + + if (cancel_cookie) + *cancel_cookie = NULL; + + if (FAILED(hr = MFCreateAsyncResult(NULL, callback, state, &caller))) + return hr; + + context = heap_alloc(sizeof(*context)); + if (!context) + { + IMFAsyncResult_Release(caller); + return E_OUTOFMEMORY; + } + + context->IUnknown_iface.lpVtbl = &create_object_context_vtbl; + context->refcount = 1; + context->props = props; + if (context->props) + IPropertyStore_AddRef(context->props); + context->flags = flags; + context->stream = stream; + if (context->stream) + IMFByteStream_AddRef(context->stream); + if (url) + context->url = heap_strdupW(url); + if (!context->url && !context->stream) + { + IMFAsyncResult_Release(caller); + IUnknown_Release(&context->IUnknown_iface); + return E_OUTOFMEMORY; + } + + hr = MFCreateAsyncResult(&context->IUnknown_iface, &handler->IMFAsyncCallback_iface, (IUnknown *)caller, &item); + IUnknown_Release(&context->IUnknown_iface); + IMFAsyncResult_Release(caller); + if (SUCCEEDED(hr)) + { + if (SUCCEEDED(hr = MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_IO, item))) + { + if (cancel_cookie) + IMFAsyncResult_GetState(item, cancel_cookie); + } + + IMFAsyncResult_Release(item); + } + + return hr; +} + +HRESULT handler_end_create_object(struct handler *handler, IMFAsyncResult *result, + MF_OBJECT_TYPE *obj_type, IUnknown **object) +{ + struct handler_result *found = NULL, *cur; + HRESULT hr; + + EnterCriticalSection(&handler->cs); + + LIST_FOR_EACH_ENTRY(cur, &handler->results, struct handler_result, entry) + { + if (result == cur->result) + { + list_remove(&cur->entry); + found = cur; + break; + } + } + + LeaveCriticalSection(&handler->cs); + + if (found) + { + *obj_type = found->obj_type; + *object = found->object; + hr = IMFAsyncResult_GetStatus(found->result); + IMFAsyncResult_Release(found->result); + heap_free(found); + } + else + { + *obj_type = MF_OBJECT_INVALID; + *object = NULL; + hr = MF_E_UNEXPECTED; + } + + return hr; +} + +HRESULT handler_cancel_object_creation(struct handler *handler, IUnknown *cancel_cookie) +{ + struct handler_result *found = NULL, *cur; + + EnterCriticalSection(&handler->cs); + + LIST_FOR_EACH_ENTRY(cur, &handler->results, struct handler_result, entry) + { + if (cancel_cookie == (IUnknown *)cur->result) + { + list_remove(&cur->entry); + found = cur; + break; + } + } + + LeaveCriticalSection(&handler->cs); + + if (found) + { + IMFAsyncResult_Release(found->result); + if (found->object) + IUnknown_Release(found->object); + heap_free(found); + } + + return found ? S_OK : MF_E_UNEXPECTED; +} + +void handler_construct(struct handler *handler, p_create_object_callback create_object_callback) +{ + handler->IMFAsyncCallback_iface.lpVtbl = &handler_callback_vtbl; + handler->create_object = create_object_callback; + + list_init(&handler->results); + InitializeCriticalSection(&handler->cs); +} + +void handler_destruct(struct handler *handler) +{ + struct handler_result *result, *next; + + LIST_FOR_EACH_ENTRY_SAFE(result, next, &handler->results, struct handler_result, entry) + { + list_remove(&result->entry); + IMFAsyncResult_Release(result->result); + if (result->object) + IUnknown_Release(result->object); + heap_free(result); + } + DeleteCriticalSection(&handler->cs); +} \ No newline at end of file diff --git a/dlls/mf/handler.h b/dlls/mf/handler.h new file mode 100644 index 0000000000..fbebd26e8d --- /dev/null +++ b/dlls/mf/handler.h @@ -0,0 +1,30 @@ +#include "windef.h" + +#include "mfidl.h" +#include "mfapi.h" +#include "mfobjects.h" + +#include "wine/list.h" + +/* helper sub-object that handles ansyncronous nature of handlers */ + +struct handler; +typedef HRESULT (*p_create_object_callback)(struct handler *handler, WCHAR *url, IMFByteStream *stream, DWORD flags, IPropertyStore *props, + IUnknown **out_object, MF_OBJECT_TYPE *out_obj_type); + +struct handler +{ + IMFAsyncCallback IMFAsyncCallback_iface; + struct list results; + CRITICAL_SECTION cs; + p_create_object_callback create_object; +}; + +void handler_construct(struct handler *handler, p_create_object_callback create_object_callback); +void handler_destruct(struct handler *handler); +HRESULT handler_begin_create_object(struct handler *handler, IMFByteStream *stream, + const WCHAR *url, DWORD flags, IPropertyStore *props, IUnknown **cancel_cookie, + IMFAsyncCallback *callback, IUnknown *state); +HRESULT handler_end_create_object(struct handler *handler, IMFAsyncResult *result, + MF_OBJECT_TYPE *obj_type, IUnknown **object); +HRESULT handler_cancel_object_creation(struct handler *handler, IUnknown *cancel_cookie); \ No newline at end of file diff --git a/dlls/mf/main.c b/dlls/mf/main.c index 20b587e413..e2d1ef1ea0 100644 --- a/dlls/mf/main.c +++ b/dlls/mf/main.c @@ -35,6 +35,7 @@ #include "mferror.h"
#include "mf_private.h" +#include "handler.h"
#include "wine/debug.h" #include "wine/heap.h" @@ -556,22 +557,12 @@ static const IClassFactoryVtbl class_factory_vtbl = class_factory_LockServer, };
-struct file_scheme_handler_result -{ - struct list entry; - IMFAsyncResult *result; - MF_OBJECT_TYPE obj_type; - IUnknown *object; -}; - struct file_scheme_handler { IMFSchemeHandler IMFSchemeHandler_iface; - IMFAsyncCallback IMFAsyncCallback_iface; LONG refcount; IMFSourceResolver *resolver; - struct list results; - CRITICAL_SECTION cs; + struct handler handler; };
static struct file_scheme_handler *impl_from_IMFSchemeHandler(IMFSchemeHandler *iface) @@ -579,11 +570,6 @@ static struct file_scheme_handler *impl_from_IMFSchemeHandler(IMFSchemeHandler * return CONTAINING_RECORD(iface, struct file_scheme_handler, IMFSchemeHandler_iface); }
-static struct file_scheme_handler *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface) -{ - return CONTAINING_RECORD(iface, struct file_scheme_handler, IMFAsyncCallback_iface); -} - static HRESULT WINAPI file_scheme_handler_QueryInterface(IMFSchemeHandler *iface, REFIID riid, void **obj) { TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); @@ -613,239 +599,45 @@ static ULONG WINAPI file_scheme_handler_AddRef(IMFSchemeHandler *iface)
static ULONG WINAPI file_scheme_handler_Release(IMFSchemeHandler *iface) { - struct file_scheme_handler *handler = impl_from_IMFSchemeHandler(iface); - ULONG refcount = InterlockedDecrement(&handler->refcount); - struct file_scheme_handler_result *result, *next; - - TRACE("%p, refcount %u.\n", iface, refcount); - - if (!refcount) - { - LIST_FOR_EACH_ENTRY_SAFE(result, next, &handler->results, struct file_scheme_handler_result, entry) - { - list_remove(&result->entry); - IMFAsyncResult_Release(result->result); - if (result->object) - IUnknown_Release(result->object); - heap_free(result); - } - DeleteCriticalSection(&handler->cs); - if (handler->resolver) - IMFSourceResolver_Release(handler->resolver); - heap_free(handler); - } - - return refcount; -} - -struct create_object_context -{ - IUnknown IUnknown_iface; - LONG refcount; - - IPropertyStore *props; - WCHAR *url; - DWORD flags; -}; - -static struct create_object_context *impl_from_IUnknown(IUnknown *iface) -{ - return CONTAINING_RECORD(iface, struct create_object_context, IUnknown_iface); -} - -static HRESULT WINAPI create_object_context_QueryInterface(IUnknown *iface, REFIID riid, void **obj) -{ - TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); - - if (IsEqualIID(riid, &IID_IUnknown)) - { - *obj = iface; - IUnknown_AddRef(iface); - return S_OK; - } - - WARN("Unsupported %s.\n", debugstr_guid(riid)); - *obj = NULL; - return E_NOINTERFACE; -} - -static ULONG WINAPI create_object_context_AddRef(IUnknown *iface) -{ - struct create_object_context *context = impl_from_IUnknown(iface); - ULONG refcount = InterlockedIncrement(&context->refcount); - - TRACE("%p, refcount %u.\n", iface, refcount); - - return refcount; -} - -static ULONG WINAPI create_object_context_Release(IUnknown *iface) -{ - struct create_object_context *context = impl_from_IUnknown(iface); - ULONG refcount = InterlockedDecrement(&context->refcount); + struct file_scheme_handler *this = impl_from_IMFSchemeHandler(iface); + ULONG refcount = InterlockedDecrement(&this->refcount);
TRACE("%p, refcount %u.\n", iface, refcount);
if (!refcount) { - if (context->props) - IPropertyStore_Release(context->props); - heap_free(context->url); - heap_free(context); + if (this->resolver) + IMFSourceResolver_Release(this->resolver); + handler_destruct(&this->handler); }
return refcount; }
-static const IUnknownVtbl create_object_context_vtbl = -{ - create_object_context_QueryInterface, - create_object_context_AddRef, - create_object_context_Release, -}; - -static WCHAR *heap_strdupW(const WCHAR *str) -{ - WCHAR *ret = NULL; - - if (str) - { - unsigned int size; - - size = (lstrlenW(str) + 1) * sizeof(WCHAR); - ret = heap_alloc(size); - if (ret) - memcpy(ret, str, size); - } - - return ret; -} - static HRESULT WINAPI file_scheme_handler_BeginCreateObject(IMFSchemeHandler *iface, const WCHAR *url, DWORD flags, IPropertyStore *props, IUnknown **cancel_cookie, IMFAsyncCallback *callback, IUnknown *state) { - struct file_scheme_handler *handler = impl_from_IMFSchemeHandler(iface); - struct create_object_context *context; - IMFAsyncResult *caller, *item; - HRESULT hr; + struct file_scheme_handler *this = impl_from_IMFSchemeHandler(iface);
TRACE("%p, %s, %#x, %p, %p, %p, %p.\n", iface, debugstr_w(url), flags, props, cancel_cookie, callback, state); - - if (cancel_cookie) - *cancel_cookie = NULL; - - if (FAILED(hr = MFCreateAsyncResult(NULL, callback, state, &caller))) - return hr; - - context = heap_alloc(sizeof(*context)); - if (!context) - { - IMFAsyncResult_Release(caller); - return E_OUTOFMEMORY; - } - - context->IUnknown_iface.lpVtbl = &create_object_context_vtbl; - context->refcount = 1; - context->props = props; - if (context->props) - IPropertyStore_AddRef(context->props); - context->flags = flags; - context->url = heap_strdupW(url); - if (!context->url) - { - IMFAsyncResult_Release(caller); - IUnknown_Release(&context->IUnknown_iface); - return E_OUTOFMEMORY; - } - - hr = MFCreateAsyncResult(&context->IUnknown_iface, &handler->IMFAsyncCallback_iface, (IUnknown *)caller, &item); - IUnknown_Release(&context->IUnknown_iface); - IMFAsyncResult_Release(caller); - if (SUCCEEDED(hr)) - { - if (SUCCEEDED(hr = MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_IO, item))) - { - if (cancel_cookie) - IMFAsyncResult_GetState(item, cancel_cookie); - } - - IMFAsyncResult_Release(item); - } - - return hr; + return handler_begin_create_object(&this->handler, NULL, url, flags, props, cancel_cookie, callback, state); }
static HRESULT WINAPI file_scheme_handler_EndCreateObject(IMFSchemeHandler *iface, IMFAsyncResult *result, MF_OBJECT_TYPE *obj_type, IUnknown **object) { - struct file_scheme_handler *handler = impl_from_IMFSchemeHandler(iface); - struct file_scheme_handler_result *found = NULL, *cur; - HRESULT hr; + struct file_scheme_handler *this = impl_from_IMFSchemeHandler(iface);
TRACE("%p, %p, %p, %p.\n", iface, result, obj_type, object); - - EnterCriticalSection(&handler->cs); - - LIST_FOR_EACH_ENTRY(cur, &handler->results, struct file_scheme_handler_result, entry) - { - if (result == cur->result) - { - list_remove(&cur->entry); - found = cur; - break; - } - } - - LeaveCriticalSection(&handler->cs); - - if (found) - { - *obj_type = found->obj_type; - *object = found->object; - hr = IMFAsyncResult_GetStatus(found->result); - IMFAsyncResult_Release(found->result); - heap_free(found); - } - else - { - *obj_type = MF_OBJECT_INVALID; - *object = NULL; - hr = MF_E_UNEXPECTED; - } - - return hr; + return handler_end_create_object(&this->handler, result, obj_type, object); }
static HRESULT WINAPI file_scheme_handler_CancelObjectCreation(IMFSchemeHandler *iface, IUnknown *cancel_cookie) { - struct file_scheme_handler *handler = impl_from_IMFSchemeHandler(iface); - struct file_scheme_handler_result *found = NULL, *cur; + struct file_scheme_handler *this = impl_from_IMFSchemeHandler(iface);
TRACE("%p, %p.\n", iface, cancel_cookie); - - EnterCriticalSection(&handler->cs); - - LIST_FOR_EACH_ENTRY(cur, &handler->results, struct file_scheme_handler_result, entry) - { - if (cancel_cookie == (IUnknown *)cur->result) - { - list_remove(&cur->entry); - found = cur; - break; - } - } - - LeaveCriticalSection(&handler->cs); - - if (found) - { - IMFAsyncResult_Release(found->result); - if (found->object) - IUnknown_Release(found->object); - heap_free(found); - } - - return found ? S_OK : MF_E_UNEXPECTED; + return handler_cancel_object_creation(&this->handler, cancel_cookie); }
static const IMFSchemeHandlerVtbl file_scheme_handler_vtbl = @@ -858,38 +650,6 @@ static const IMFSchemeHandlerVtbl file_scheme_handler_vtbl = file_scheme_handler_CancelObjectCreation, };
-static HRESULT WINAPI file_scheme_handler_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **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 ULONG WINAPI file_scheme_handler_callback_AddRef(IMFAsyncCallback *iface) -{ - struct file_scheme_handler *handler = impl_from_IMFAsyncCallback(iface); - return IMFSchemeHandler_AddRef(&handler->IMFSchemeHandler_iface); -} - -static ULONG WINAPI file_scheme_handler_callback_Release(IMFAsyncCallback *iface) -{ - struct file_scheme_handler *handler = impl_from_IMFAsyncCallback(iface); - return IMFSchemeHandler_Release(&handler->IMFSchemeHandler_iface); -} - -static HRESULT WINAPI file_scheme_handler_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) -{ - return E_NOTIMPL; -} - static HRESULT file_scheme_handler_get_resolver(struct file_scheme_handler *handler, IMFSourceResolver **resolver) { HRESULT hr; @@ -911,111 +671,63 @@ static HRESULT file_scheme_handler_get_resolver(struct file_scheme_handler *hand return S_OK; }
-static HRESULT WINAPI file_scheme_handler_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +static HRESULT file_scheme_handler_create_object(struct handler *handler, WCHAR *url, IMFByteStream *stream, DWORD flags, + IPropertyStore *props, IUnknown **out_object, MF_OBJECT_TYPE *out_obj_type) { static const WCHAR schemeW[] = {'f','i','l','e',':','/','/'}; - struct file_scheme_handler *handler = impl_from_IMFAsyncCallback(iface); - struct file_scheme_handler_result *handler_result; - MF_OBJECT_TYPE obj_type = MF_OBJECT_INVALID; - IUnknown *object = NULL, *context_object; - struct create_object_context *context; + HRESULT hr = S_OK; + WCHAR *path; + IMFByteStream *file_byte_stream; + struct file_scheme_handler *this = CONTAINING_RECORD(handler, struct file_scheme_handler, handler); IMFSourceResolver *resolver; - IMFAsyncResult *caller; - IMFByteStream *stream; - const WCHAR *url; - HRESULT hr; - - caller = (IMFAsyncResult *)IMFAsyncResult_GetStateNoAddRef(result); - - if (FAILED(hr = IMFAsyncResult_GetObject(result, &context_object))) - { - WARN("Expected context set for callee result.\n"); - return hr; - } - - context = impl_from_IUnknown(context_object);
/* Strip from scheme, MFCreateFile() won't be expecting it. */ - url = context->url; - if (!wcsnicmp(context->url, schemeW, ARRAY_SIZE(schemeW))) - url += ARRAY_SIZE(schemeW); + path = url; + if (!wcsnicmp(url, schemeW, ARRAY_SIZE(schemeW))) + path += ARRAY_SIZE(schemeW);
- hr = MFCreateFile(context->flags & MF_RESOLUTION_WRITE ? MF_ACCESSMODE_READWRITE : MF_ACCESSMODE_READ, - MF_OPENMODE_FAIL_IF_NOT_EXIST, MF_FILEFLAGS_NONE, url, &stream); + hr = MFCreateFile(flags & MF_RESOLUTION_WRITE ? MF_ACCESSMODE_READWRITE : MF_ACCESSMODE_READ, + MF_OPENMODE_FAIL_IF_NOT_EXIST, MF_FILEFLAGS_NONE, path, &file_byte_stream); if (SUCCEEDED(hr)) { - if (context->flags & MF_RESOLUTION_MEDIASOURCE) + if (flags & MF_RESOLUTION_MEDIASOURCE) { - if (SUCCEEDED(hr = file_scheme_handler_get_resolver(handler, &resolver))) + if (SUCCEEDED(hr = file_scheme_handler_get_resolver(this, &resolver))) { - hr = IMFSourceResolver_CreateObjectFromByteStream(resolver, stream, context->url, context->flags, - context->props, &obj_type, &object); + hr = IMFSourceResolver_CreateObjectFromByteStream(resolver, file_byte_stream, url, flags, + props, out_obj_type, out_object); IMFSourceResolver_Release(resolver); - IMFByteStream_Release(stream); + IMFByteStream_Release(file_byte_stream); } } else { - object = (IUnknown *)stream; - obj_type = MF_OBJECT_BYTESTREAM; + *out_object = (IUnknown *)file_byte_stream; + *out_obj_type = MF_OBJECT_BYTESTREAM; } }
- handler_result = heap_alloc(sizeof(*handler_result)); - if (handler_result) - { - handler_result->result = caller; - IMFAsyncResult_AddRef(handler_result->result); - handler_result->obj_type = obj_type; - handler_result->object = object; - - EnterCriticalSection(&handler->cs); - list_add_tail(&handler->results, &handler_result->entry); - LeaveCriticalSection(&handler->cs); - } - else - { - if (object) - IUnknown_Release(object); - hr = E_OUTOFMEMORY; - } - - IUnknown_Release(&context->IUnknown_iface); - - IMFAsyncResult_SetStatus(caller, hr); - MFInvokeCallback(caller); - - return S_OK; + return hr; }
-static const IMFAsyncCallbackVtbl file_scheme_handler_callback_vtbl = -{ - file_scheme_handler_callback_QueryInterface, - file_scheme_handler_callback_AddRef, - file_scheme_handler_callback_Release, - file_scheme_handler_callback_GetParameters, - file_scheme_handler_callback_Invoke, -}; - static HRESULT file_scheme_handler_construct(REFIID riid, void **obj) { - struct file_scheme_handler *handler; + struct file_scheme_handler *this; HRESULT hr;
TRACE("%s, %p.\n", debugstr_guid(riid), obj);
- handler = heap_alloc_zero(sizeof(*handler)); - if (!handler) + this = heap_alloc_zero(sizeof(*this)); + if (!this) return E_OUTOFMEMORY;
- handler->IMFSchemeHandler_iface.lpVtbl = &file_scheme_handler_vtbl; - handler->IMFAsyncCallback_iface.lpVtbl = &file_scheme_handler_callback_vtbl; - handler->refcount = 1; - list_init(&handler->results); - InitializeCriticalSection(&handler->cs); + handler_construct(&this->handler, file_scheme_handler_create_object); + + this->IMFSchemeHandler_iface.lpVtbl = &file_scheme_handler_vtbl; + this->refcount = 1;
- hr = IMFSchemeHandler_QueryInterface(&handler->IMFSchemeHandler_iface, riid, obj); - IMFSchemeHandler_Release(&handler->IMFSchemeHandler_iface); + hr = IMFSchemeHandler_QueryInterface(&this->IMFSchemeHandler_iface, riid, obj); + IMFSchemeHandler_Release(&this->IMFSchemeHandler_iface);
return hr; } diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index f4f72c6e96..61789884b8 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -2,12 +2,13 @@ MODULE = winegstreamer.dll IMPORTS = strmiids uuid winmm msacm32 msvfw32 ole32 oleaut32 user32 gdi32 advapi32 mfplat mfuuid EXTRAINCL = $(GSTREAMER_CFLAGS) EXTRALIBS = $(GSTREAMER_LIBS) $(PTHREAD_LIBS) -PARENTSRC = ../strmbase +PARENTSRC = ../strmbase ../mf
C_SRCS = \ filter.c \ gst_cbs.c \ gstdemux.c \ + handler.c \ main.c \ media_source.c \ mediatype.c \ diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index e7710dbd08..01314f1da7 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -4,6 +4,7 @@
#include "gst_private.h" #include "gst_cbs.h" +#include "handler.h"
#include <stdarg.h>
@@ -1325,22 +1326,12 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t
/* IMFByteStreamHandler */
-struct container_stream_handler_result -{ - struct list entry; - IMFAsyncResult *result; - MF_OBJECT_TYPE obj_type; - IUnknown *object; -}; - struct container_stream_handler { IMFByteStreamHandler IMFByteStreamHandler_iface; - IMFAsyncCallback IMFAsyncCallback_iface; LONG refcount; enum source_type type; - struct list results; - CRITICAL_SECTION cs; + struct handler handler; };
static struct container_stream_handler *impl_from_IMFByteStreamHandler(IMFByteStreamHandler *iface) @@ -1348,11 +1339,6 @@ static struct container_stream_handler *impl_from_IMFByteStreamHandler(IMFByteSt return CONTAINING_RECORD(iface, struct container_stream_handler, IMFByteStreamHandler_iface); }
-static struct container_stream_handler *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface) -{ - return CONTAINING_RECORD(iface, struct container_stream_handler, IMFAsyncCallback_iface); -} - static HRESULT WINAPI container_stream_handler_QueryInterface(IMFByteStreamHandler *iface, REFIID riid, void **obj) { TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); @@ -1382,245 +1368,44 @@ static ULONG WINAPI container_stream_handler_AddRef(IMFByteStreamHandler *iface)
static ULONG WINAPI container_stream_handler_Release(IMFByteStreamHandler *iface) { - struct container_stream_handler *handler = impl_from_IMFByteStreamHandler(iface); - ULONG refcount = InterlockedDecrement(&handler->refcount); - struct container_stream_handler_result *result, *next; - - TRACE("%p, refcount %u.\n", iface, refcount); - - if (!refcount) - { - LIST_FOR_EACH_ENTRY_SAFE(result, next, &handler->results, struct container_stream_handler_result, entry) - { - list_remove(&result->entry); - IMFAsyncResult_Release(result->result); - if (result->object) - IUnknown_Release(result->object); - heap_free(result); - } - DeleteCriticalSection(&handler->cs); - heap_free(handler); - } - - return refcount; -} - -struct create_object_context -{ - IUnknown IUnknown_iface; - LONG refcount; - - IPropertyStore *props; - IMFByteStream *stream; - WCHAR *url; - DWORD flags; -}; - -static struct create_object_context *impl_from_IUnknown(IUnknown *iface) -{ - return CONTAINING_RECORD(iface, struct create_object_context, IUnknown_iface); -} - -static HRESULT WINAPI create_object_context_QueryInterface(IUnknown *iface, REFIID riid, void **obj) -{ - TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); - - if (IsEqualIID(riid, &IID_IUnknown)) - { - *obj = iface; - IUnknown_AddRef(iface); - return S_OK; - } - - WARN("Unsupported %s.\n", debugstr_guid(riid)); - *obj = NULL; - return E_NOINTERFACE; -} - -static ULONG WINAPI create_object_context_AddRef(IUnknown *iface) -{ - struct create_object_context *context = impl_from_IUnknown(iface); - ULONG refcount = InterlockedIncrement(&context->refcount); - - TRACE("%p, refcount %u.\n", iface, refcount); - - return refcount; -} - -static ULONG WINAPI create_object_context_Release(IUnknown *iface) -{ - struct create_object_context *context = impl_from_IUnknown(iface); - ULONG refcount = InterlockedDecrement(&context->refcount); + struct container_stream_handler *this = impl_from_IMFByteStreamHandler(iface); + ULONG refcount = InterlockedDecrement(&this->refcount);
TRACE("%p, refcount %u.\n", iface, refcount);
if (!refcount) { - if (context->props) - IPropertyStore_Release(context->props); - if (context->stream) - IMFByteStream_Release(context->stream); - if (context->url) - heap_free(context->url); - heap_free(context); + handler_destruct(&this->handler); + heap_free(this); }
return refcount; }
-static const IUnknownVtbl create_object_context_vtbl = -{ - create_object_context_QueryInterface, - create_object_context_AddRef, - create_object_context_Release, -}; - -static WCHAR *heap_strdupW(const WCHAR *str) -{ - WCHAR *ret = NULL; - - if (str) - { - unsigned int size; - - size = (lstrlenW(str) + 1) * sizeof(WCHAR); - ret = heap_alloc(size); - if (ret) - memcpy(ret, str, size); - } - - return ret; -} - static HRESULT WINAPI container_stream_handler_BeginCreateObject(IMFByteStreamHandler *iface, IMFByteStream *stream, const WCHAR *url, DWORD flags, IPropertyStore *props, IUnknown **cancel_cookie, IMFAsyncCallback *callback, IUnknown *state) { struct container_stream_handler *this = impl_from_IMFByteStreamHandler(iface); - struct create_object_context *context; - IMFAsyncResult *caller, *item; - HRESULT hr;
TRACE("%p, %s, %#x, %p, %p, %p, %p.\n", iface, debugstr_w(url), flags, props, cancel_cookie, callback, state); - - if (cancel_cookie) - *cancel_cookie = NULL; - - if (FAILED(hr = MFCreateAsyncResult(NULL, callback, state, &caller))) - return hr; - - context = heap_alloc(sizeof(*context)); - if (!context) - { - IMFAsyncResult_Release(caller); - return E_OUTOFMEMORY; - } - - context->IUnknown_iface.lpVtbl = &create_object_context_vtbl; - context->refcount = 1; - context->props = props; - if (context->props) - IPropertyStore_AddRef(context->props); - context->flags = flags; - context->stream = stream; - if (context->stream) - IMFByteStream_AddRef(context->stream); - if (url) - context->url = heap_strdupW(url); - if (!context->stream) - { - IMFAsyncResult_Release(caller); - IUnknown_Release(&context->IUnknown_iface); - return E_OUTOFMEMORY; - } - - hr = MFCreateAsyncResult(&context->IUnknown_iface, &this->IMFAsyncCallback_iface, (IUnknown *)caller, &item); - IUnknown_Release(&context->IUnknown_iface); - IMFAsyncResult_Release(caller); - if (SUCCEEDED(hr)) - { - if (SUCCEEDED(hr = MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_IO, item))) - { - if (cancel_cookie) - IMFAsyncResult_GetState(item, cancel_cookie); - } - - IMFAsyncResult_Release(item); - } - - return hr; + return handler_begin_create_object(&this->handler, stream, url, flags, props, cancel_cookie, callback, state); }
static HRESULT WINAPI container_stream_handler_EndCreateObject(IMFByteStreamHandler *iface, IMFAsyncResult *result, MF_OBJECT_TYPE *obj_type, IUnknown **object) { struct container_stream_handler *this = impl_from_IMFByteStreamHandler(iface); - struct container_stream_handler_result *found = NULL, *cur; - HRESULT hr;
TRACE("%p, %p, %p, %p.\n", iface, result, obj_type, object); - - EnterCriticalSection(&this->cs); - - LIST_FOR_EACH_ENTRY(cur, &this->results, struct container_stream_handler_result, entry) - { - if (result == cur->result) - { - list_remove(&cur->entry); - found = cur; - break; - } - } - - LeaveCriticalSection(&this->cs); - - if (found) - { - *obj_type = found->obj_type; - *object = found->object; - hr = IMFAsyncResult_GetStatus(found->result); - IMFAsyncResult_Release(found->result); - heap_free(found); - } - else - { - *obj_type = MF_OBJECT_INVALID; - *object = NULL; - hr = MF_E_UNEXPECTED; - } - - return hr; + return handler_end_create_object(&this->handler, result, obj_type, object); }
static HRESULT WINAPI container_stream_handler_CancelObjectCreation(IMFByteStreamHandler *iface, IUnknown *cancel_cookie) { struct container_stream_handler *this = impl_from_IMFByteStreamHandler(iface); - struct container_stream_handler_result *found = NULL, *cur;
TRACE("%p, %p.\n", iface, cancel_cookie); - - EnterCriticalSection(&this->cs); - - LIST_FOR_EACH_ENTRY(cur, &this->results, struct container_stream_handler_result, entry) - { - if (cancel_cookie == (IUnknown *)cur->result) - { - list_remove(&cur->entry); - found = cur; - break; - } - } - - LeaveCriticalSection(&this->cs); - - if (found) - { - IMFAsyncResult_Release(found->result); - if (found->object) - IUnknown_Release(found->object); - heap_free(found); - } - - return found ? S_OK : MF_E_UNEXPECTED; + return handler_cancel_object_creation(&this->handler, cancel_cookie); }
static HRESULT WINAPI container_stream_handler_GetMaxNumberOfBytesRequiredForResolution(IMFByteStreamHandler *iface, QWORD *bytes) @@ -1640,42 +1425,10 @@ static const IMFByteStreamHandlerVtbl container_stream_handler_vtbl = container_stream_handler_GetMaxNumberOfBytesRequiredForResolution, };
-static HRESULT WINAPI container_stream_handler_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **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 ULONG WINAPI container_stream_handler_callback_AddRef(IMFAsyncCallback *iface) -{ - struct container_stream_handler *handler = impl_from_IMFAsyncCallback(iface); - return IMFByteStreamHandler_AddRef(&handler->IMFByteStreamHandler_iface); -} - -static ULONG WINAPI container_stream_handler_callback_Release(IMFAsyncCallback *iface) -{ - struct container_stream_handler *handler = impl_from_IMFAsyncCallback(iface); - return IMFByteStreamHandler_Release(&handler->IMFByteStreamHandler_iface); -} - -static HRESULT WINAPI container_stream_handler_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) -{ - return E_NOTIMPL; -} - -static HRESULT container_stream_handler_create_object(struct container_stream_handler *This, WCHAR *url, IMFByteStream *stream, DWORD flags, +static HRESULT container_stream_handler_create_object(struct handler *handler, WCHAR *url, IMFByteStream *stream, DWORD flags, IPropertyStore *props, IUnknown **out_object, MF_OBJECT_TYPE *out_obj_type) { - TRACE("(%p %s %p %u %p %p %p)\n", This, debugstr_w(url), stream, flags, props, out_object, out_obj_type); + TRACE("(%p %s %p %u %p %p %p)\n", handler, debugstr_w(url), stream, flags, props, out_object, out_obj_type);
if (!(init_gstreamer())) return E_FAIL; @@ -1684,6 +1437,7 @@ static HRESULT container_stream_handler_create_object(struct container_stream_ha { HRESULT hr; struct media_source *new_source; + struct container_stream_handler *This = CONTAINING_RECORD(handler, struct container_stream_handler, handler);
if (FAILED(hr = media_source_constructor(stream, This->type, &new_source))) return hr; @@ -1702,64 +1456,6 @@ static HRESULT container_stream_handler_create_object(struct container_stream_ha } }
-static HRESULT WINAPI container_stream_handler_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) -{ - struct container_stream_handler *handler = impl_from_IMFAsyncCallback(iface); - struct container_stream_handler_result *handler_result; - MF_OBJECT_TYPE obj_type = MF_OBJECT_INVALID; - IUnknown *object = NULL, *context_object; - struct create_object_context *context; - IMFAsyncResult *caller; - HRESULT hr; - - caller = (IMFAsyncResult *)IMFAsyncResult_GetStateNoAddRef(result); - - if (FAILED(hr = IMFAsyncResult_GetObject(result, &context_object))) - { - WARN("Expected context set for callee result.\n"); - return hr; - } - - context = impl_from_IUnknown(context_object); - - hr = container_stream_handler_create_object(handler, context->url, context->stream, context->flags, context->props, &object, &obj_type); - - handler_result = heap_alloc(sizeof(*handler_result)); - if (handler_result) - { - handler_result->result = caller; - IMFAsyncResult_AddRef(handler_result->result); - handler_result->obj_type = obj_type; - handler_result->object = object; - - EnterCriticalSection(&handler->cs); - list_add_tail(&handler->results, &handler_result->entry); - LeaveCriticalSection(&handler->cs); - } - else - { - if (object) - IUnknown_Release(object); - hr = E_OUTOFMEMORY; - } - - IUnknown_Release(&context->IUnknown_iface); - - IMFAsyncResult_SetStatus(caller, hr); - MFInvokeCallback(caller); - - return S_OK; -} - -static const IMFAsyncCallbackVtbl container_stream_handler_callback_vtbl = -{ - container_stream_handler_callback_QueryInterface, - container_stream_handler_callback_AddRef, - container_stream_handler_callback_Release, - container_stream_handler_callback_GetParameters, - container_stream_handler_callback_Invoke, -}; - HRESULT container_stream_handler_construct(REFIID riid, void **obj, enum source_type type) { struct container_stream_handler *this; @@ -1771,12 +1467,10 @@ HRESULT container_stream_handler_construct(REFIID riid, void **obj, enum source_ if (!this) return E_OUTOFMEMORY;
- list_init(&this->results); - InitializeCriticalSection(&this->cs); + handler_construct(&this->handler, container_stream_handler_create_object);
this->type = type; this->IMFByteStreamHandler_iface.lpVtbl = &container_stream_handler_vtbl; - this->IMFAsyncCallback_iface.lpVtbl = &container_stream_handler_callback_vtbl; this->refcount = 1;
hr = IMFByteStreamHandler_QueryInterface(&this->IMFByteStreamHandler_iface, riid, obj);
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/mfplat.c | 83 ++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+)
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 49691d1fbe..e594ed1419 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -56,6 +56,7 @@ void start_dispatch_thread(void) DECLSPEC_HIDDEN; extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) DECLSPEC_HIDDEN;
IMFMediaType* media_type_from_caps(GstCaps *caps); +GstCaps *caps_from_media_type(IMFMediaType *type); IMFSample* mf_sample_from_gst_buffer(GstBuffer *in);
enum source_type diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 54b611b130..b7aef27645 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -751,6 +751,89 @@ IMFMediaType* media_type_from_caps(GstCaps *caps) return NULL; }
+static const char *fourcc_str(DWORD fourcc) +{ + if (!fourcc) return NULL; + return wine_dbg_sprintf ("%c%c%c%c", + (char)(fourcc), (char)(fourcc >> 8), + (char)(fourcc >> 16), (char)(fourcc >> 24)); +} + +GstCaps *caps_from_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 *framerate_num = ((DWORD*)&frame_rate) + 1; + DWORD *framerate_den = ((DWORD*)&frame_rate); + DWORD *width = ((DWORD*)&frame_size) + 1; + DWORD *height = ((DWORD*)&frame_size); + UINT32 unused; + + /* Check if type is uncompressed */ + if (SUCCEEDED(MFCalculateImageSize(&subtype, 100, 100, &unused))) + { + output = gst_caps_new_empty_simple("video/x-raw"); + gst_caps_set_simple(output, "format", G_TYPE_STRING, fourcc_str(subtype.Data1), NULL); + } + else { + ERR("Unrecognized subtype %s\n", debugstr_guid(&subtype)); + return NULL; + } + + IMFMediaType_GetUINT64(type, &MF_MT_FRAME_RATE, &frame_rate); + IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &frame_size); + + if (frame_rate) + gst_caps_set_simple(output, "framerate", GST_TYPE_FRACTION, *framerate_num, *framerate_den, 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); + } + return output; + } + else if (IsEqualGUID(&major_type, &MFMediaType_Audio)) + { + DWORD rate, channels; + + 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); + } + else + { + ERR("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); + } + + return output; + } + + ERR("Unrecognized major type %s\n", debugstr_guid(&major_type)); + return NULL; +} + /* IMFSample = GstBuffer IMFBuffer = GstMemory */
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/winegstreamer/mfplat.c | 48 +++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+)
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index b7aef27645..ffef9d4c08 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -785,6 +785,54 @@ GstCaps *caps_from_media_type(IMFMediaType *type) output = gst_caps_new_empty_simple("video/x-raw"); gst_caps_set_simple(output, "format", G_TYPE_STRING, fourcc_str(subtype.Data1), NULL); } + else if (IsEqualGUID(&subtype, &MFVideoFormat_H264)) + { + enum eAVEncH264VProfile h264_profile; + enum eAVEncH264VLevel h264_level; + output = gst_caps_new_empty_simple("video/x-h264"); + gst_caps_set_simple(output, "stream-format", G_TYPE_STRING, "byte-stream", NULL); + gst_caps_set_simple(output, "alignment", G_TYPE_STRING, "au", NULL); + + if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_MPEG2_PROFILE, &h264_profile))) + { + const char *profile = NULL; + switch (h264_profile) + { + case eAVEncH264VProfile_Main: profile = "main"; break; + case eAVEncH264VProfile_High: profile = "high"; break; + case eAVEncH264VProfile_444: profile = "high-4:4:4"; break; + default: ERR("Unknown profile %u\n", h264_profile); + } + if (profile) + gst_caps_set_simple(output, "profile", G_TYPE_STRING, profile, NULL); + } + if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_MPEG2_LEVEL, &h264_level))) + { + const char *level = NULL; + switch (h264_level) + { + case eAVEncH264VLevel1: level = "1"; break; + case eAVEncH264VLevel1_1: level = "1.1"; break; + case eAVEncH264VLevel1_2: level = "1.2"; break; + case eAVEncH264VLevel1_3: level = "1.3"; break; + case eAVEncH264VLevel2: level = "2"; break; + case eAVEncH264VLevel2_1: level = "2.1"; break; + case eAVEncH264VLevel2_2: level = "2.2"; break; + case eAVEncH264VLevel3: level = "3"; break; + case eAVEncH264VLevel3_1: level = "3.1"; break; + case eAVEncH264VLevel3_2: level = "3.2"; break; + case eAVEncH264VLevel4: level = "4"; break; + case eAVEncH264VLevel4_1: level = "4.1"; break; + case eAVEncH264VLevel4_2: level = "4.2"; break; + case eAVEncH264VLevel5: level = "5"; break; + case eAVEncH264VLevel5_1: level = "5.1"; break; + case eAVEncH264VLevel5_2: level = "5.2"; break; + default: ERR("Unknown level %u\n", h264_level); + } + if (level) + gst_caps_set_simple(output, "level", G_TYPE_STRING, level, NULL); + } + } else { ERR("Unrecognized subtype %s\n", debugstr_guid(&subtype)); return NULL;
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/winegstreamer/mfplat.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index ffef9d4c08..62f10a13b9 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -833,6 +833,24 @@ GstCaps *caps_from_media_type(IMFMediaType *type) gst_caps_set_simple(output, "level", G_TYPE_STRING, level, NULL); } } + else if (IsEqualGUID(&subtype, &MFVideoFormat_WVC1)) + { + BYTE *user_data; + DWORD user_data_size; + output = gst_caps_new_empty_simple("video/x-wmv"); + gst_caps_set_simple(output, "format", G_TYPE_STRING, "WVC1", NULL); + + gst_caps_set_simple(output, "wmvversion", G_TYPE_INT, 3, NULL); + + if (SUCCEEDED(IMFMediaType_GetAllocatedBlob(type, &MF_MT_USER_DATA, &user_data, &user_data_size))) + { + GstBuffer *codec_data_buffer = gst_buffer_new_allocate(NULL, user_data_size, NULL); + gst_buffer_fill(codec_data_buffer, 0, user_data, user_data_size); + gst_caps_set_simple(output, "codec_data", GST_TYPE_BUFFER, codec_data_buffer, NULL); + gst_buffer_unref(codec_data_buffer); + CoTaskMemFree(user_data); + } + } else { ERR("Unrecognized subtype %s\n", debugstr_guid(&subtype)); return NULL;
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/winegstreamer/mfplat.c | 68 +++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+)
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 62f10a13b9..aa042a59d3 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -878,6 +878,74 @@ GstCaps *caps_from_media_type(IMFMediaType *type)
gst_caps_set_simple(output, "format", G_TYPE_STRING, "F32LE", NULL); } + else if (IsEqualGUID(&subtype, &MFAudioFormat_AAC)) + { + DWORD payload_type, indication; + struct aac_user_data *user_data; + UINT32 user_data_size; + output = gst_caps_new_empty_simple("audio/mpeg"); + + /* TODO */ + gst_caps_set_simple(output, "framed", G_TYPE_BOOLEAN, TRUE, NULL); + gst_caps_set_simple(output, "mpegversion", G_TYPE_INT, 4, NULL); + + if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_AAC_PAYLOAD_TYPE, &payload_type))) + { + switch (payload_type) + { + case 0: + gst_caps_set_simple(output, "stream-format", G_TYPE_STRING, "raw", NULL); + break; + case 1: + gst_caps_set_simple(output, "stream-format", G_TYPE_STRING, "adts", NULL); + break; + default: + gst_caps_set_simple(output, "stream-format", G_TYPE_STRING, "raw", NULL); + } + } + else + gst_caps_set_simple(output, "stream-format", G_TYPE_STRING, "raw", NULL); + + if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION, &indication))) + { + switch (indication) + { + case 0x29: + { + gst_caps_set_simple(output, "profile", G_TYPE_STRING, "lc", NULL); + gst_caps_set_simple(output, "level", G_TYPE_STRING, "2", NULL); + break; + } + case 0x2A: + { + gst_caps_set_simple(output, "profile", G_TYPE_STRING, "lc", NULL); + gst_caps_set_simple(output, "level", G_TYPE_STRING, "4", NULL); + break; + } + case 0x2B: + { + gst_caps_set_simple(output, "profile", G_TYPE_STRING, "lc", NULL); + gst_caps_set_simple(output, "level", G_TYPE_STRING, "5", NULL); + break; + } + default: + ERR("Unrecognized profile-level-indication %u\n", indication); + } + } + + if (SUCCEEDED(IMFMediaType_GetAllocatedBlob(type, &MF_MT_USER_DATA, (BYTE **) &user_data, &user_data_size))) + { + if (user_data_size > sizeof(sizeof(*user_data))) + { + GstBuffer *audio_specific_config = gst_buffer_new_allocate(NULL, user_data_size - sizeof(*user_data), NULL); + gst_buffer_fill(audio_specific_config, 0, user_data + 1, user_data_size - sizeof(*user_data)); + + gst_caps_set_simple(output, "codec_data", GST_TYPE_BUFFER, audio_specific_config, NULL); + gst_buffer_unref(audio_specific_config); + } + CoTaskMemFree(user_data); + } + } else { ERR("Unrecognized subtype %s\n", debugstr_guid(&subtype));
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/mfplat.c | 74 ++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+)
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index e594ed1419..489b543738 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -58,6 +58,7 @@ extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) IMFMediaType* media_type_from_caps(GstCaps *caps); GstCaps *caps_from_media_type(IMFMediaType *type); IMFSample* mf_sample_from_gst_buffer(GstBuffer *in); +GstBuffer* gst_buffer_from_mf_sample(IMFSample *in);
enum source_type { diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index aa042a59d3..5fbb331c89 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -1057,3 +1057,77 @@ IMFSample* mf_sample_from_gst_buffer(GstBuffer *gst_buffer) IMFSample_Release(out); return NULL; } + +GstBuffer* gst_buffer_from_mf_sample(IMFSample *mf_sample) +{ + GstBuffer *out = gst_buffer_new(); + IMFMediaBuffer *mf_buffer = NULL; + LONGLONG duration, time; + DWORD buffer_count; + HRESULT hr; + + if (FAILED(hr = IMFSample_GetSampleDuration(mf_sample, &duration))) + goto fail; + + if (FAILED(hr = IMFSample_GetSampleTime(mf_sample, &time))) + goto fail; + + GST_BUFFER_DURATION(out) = duration; + GST_BUFFER_PTS(out) = time * 100; + + if (FAILED(hr = IMFSample_GetBufferCount(mf_sample, &buffer_count))) + goto fail; + + for (unsigned int i = 0; i < buffer_count; i++) + { + DWORD buffer_max_size, buffer_size; + GstMapInfo map_info; + GstMemory *memory; + BYTE *buf_data; + + if (FAILED(hr = IMFSample_GetBufferByIndex(mf_sample, i, &mf_buffer))) + goto fail; + + if (FAILED(hr = IMFMediaBuffer_GetMaxLength(mf_buffer, &buffer_max_size))) + goto fail; + + if (FAILED(hr = IMFMediaBuffer_GetCurrentLength(mf_buffer, &buffer_size))) + goto fail; + + memory = gst_allocator_alloc(NULL, buffer_size, NULL); + gst_memory_resize(memory, 0, buffer_size); + + if (!(gst_memory_map(memory, &map_info, GST_MAP_WRITE))) + { + hr = E_FAIL; + goto fail; + } + + if (FAILED(hr = IMFMediaBuffer_Lock(mf_buffer, &buf_data, NULL, NULL))) + goto fail; + + memcpy(map_info.data, buf_data, buffer_size); + + if (FAILED(hr = IMFMediaBuffer_Unlock(mf_buffer))) + goto fail; + + if (FAILED(hr = IMFMediaBuffer_SetCurrentLength(mf_buffer, buffer_size))) + goto fail; + + gst_memory_unmap(memory, &map_info); + + gst_buffer_append_memory(out, memory); + + IMFMediaBuffer_Release(mf_buffer); + mf_buffer = NULL; + } + + return out; + + fail: + ERR("Failed to copy IMFSample to GstBuffer, hr = %#x\n", hr); + if (mf_buffer) + IMFMediaBuffer_Release(mf_buffer); + gst_buffer_unref(out); + return NULL; +}
--- dlls/winegstreamer/Makefile.in | 1 + dlls/winegstreamer/gst_cbs.c | 65 + dlls/winegstreamer/gst_cbs.h | 18 + dlls/winegstreamer/gst_private.h | 7 + dlls/winegstreamer/main.c | 3 +- dlls/winegstreamer/mf_decode.c | 1210 ++++++++++++++++++ dlls/winegstreamer/mfplat.c | 143 ++- dlls/winegstreamer/winegstreamer_classes.idl | 12 + include/mfidl.idl | 4 +- 9 files changed, 1460 insertions(+), 3 deletions(-) create mode 100644 dlls/winegstreamer/mf_decode.c
diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index 61789884b8..9ca63e926c 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -12,6 +12,7 @@ C_SRCS = \ main.c \ media_source.c \ mediatype.c \ + mf_decode.c \ mfplat.c \ pin.c \ qualitycontrol.c \ diff --git a/dlls/winegstreamer/gst_cbs.c b/dlls/winegstreamer/gst_cbs.c index 9c27c3490d..4ba58e87b0 100644 --- a/dlls/winegstreamer/gst_cbs.c +++ b/dlls/winegstreamer/gst_cbs.c @@ -51,6 +51,8 @@ static void CALLBACK perform_cb(TP_CALLBACK_INSTANCE *instance, void *user) perform_cb_gstdemux(cbdata); else if (cbdata->type < MEDIA_SOURCE_MAX) perform_cb_media_source(cbdata); + else if (cbdata->type < MF_DECODE_MAX) + perform_cb_mf_decode(cbdata);
pthread_mutex_lock(&cbdata->lock); cbdata->finished = 1; @@ -429,4 +431,67 @@ GstBusSyncReply watch_source_bus_wrapper(GstBus *bus, GstMessage *message, gpoin call_cb(&cbdata);
return cbdata.u.watch_bus_data.ret; +} + +gboolean activate_push_mode_wrapper(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate) +{ + struct cb_data cbdata = { ACTIVATE_PUSH_MODE }; + + cbdata.u.activate_mode_data.pad = pad; + cbdata.u.activate_mode_data.parent = parent; + cbdata.u.activate_mode_data.mode = mode; + cbdata.u.activate_mode_data.activate = activate; + + call_cb(&cbdata); + + return cbdata.u.query_function_data.ret; +} + +gboolean query_input_src_wrapper(GstPad *pad, GstObject *parent, GstQuery *query) +{ + struct cb_data cbdata = { QUERY_INPUT_SRC }; + + cbdata.u.query_function_data.pad = pad; + cbdata.u.query_function_data.parent = parent; + cbdata.u.query_function_data.query = query; + + call_cb(&cbdata); + + return cbdata.u.query_function_data.ret; +} + +GstBusSyncReply watch_decoder_bus_wrapper(GstBus *bus, GstMessage *message, gpointer user) +{ + struct cb_data cbdata = { WATCH_DECODER_BUS }; + + cbdata.u.watch_bus_data.bus = bus; + cbdata.u.watch_bus_data.msg = message; + cbdata.u.watch_bus_data.user = user; + + call_cb(&cbdata); + + return cbdata.u.watch_bus_data.ret; +} + +void decoder_pad_added_wrapper(GstElement *element, GstPad *pad, gpointer user) +{ + struct cb_data cbdata = { DECODER_PAD_ADDED }; + + cbdata.u.pad_added_data.element = element; + cbdata.u.pad_added_data.pad = pad; + cbdata.u.pad_added_data.user = user; + + call_cb(&cbdata); +} + +GstFlowReturn decoder_new_sample_wrapper(GstElement *appsink, gpointer user) +{ + struct cb_data cbdata = {DECODER_NEW_SAMPLE}; + + cbdata.u.new_sample_data.appsink = appsink; + cbdata.u.new_sample_data.user = user; + + call_cb(&cbdata); + + return cbdata.u.new_sample_data.ret; } \ No newline at end of file diff --git a/dlls/winegstreamer/gst_cbs.h b/dlls/winegstreamer/gst_cbs.h index 2c83f294f4..e1e018fa46 100644 --- a/dlls/winegstreamer/gst_cbs.h +++ b/dlls/winegstreamer/gst_cbs.h @@ -55,6 +55,12 @@ enum CB_TYPE { STREAM_EOS, WATCH_SOURCE_BUS, MEDIA_SOURCE_MAX, + ACTIVATE_PUSH_MODE, + QUERY_INPUT_SRC, + DECODER_NEW_SAMPLE, + WATCH_DECODER_BUS, + DECODER_PAD_ADDED, + MF_DECODE_MAX, };
struct cb_data { @@ -148,6 +154,12 @@ struct cb_data { GstElement *appsink; gpointer user; } eos_data; + struct chain_data { + GstPad *pad; + GstObject *parent; + GstBuffer *buffer; + GstFlowReturn ret; + } chain_data; } u;
int finished; @@ -159,6 +171,7 @@ struct cb_data { void mark_wine_thread(void) DECLSPEC_HIDDEN; void perform_cb_gstdemux(struct cb_data *data) DECLSPEC_HIDDEN; void perform_cb_media_source(struct cb_data *data) DECLSPEC_HIDDEN; +void perform_cb_mf_decode(struct cb_data *data) DECLSPEC_HIDDEN;
GstBusSyncReply watch_bus_wrapper(GstBus *bus, GstMessage *msg, gpointer user) DECLSPEC_HIDDEN; void existing_new_pad_wrapper(GstElement *bin, GstPad *pad, gpointer user) DECLSPEC_HIDDEN; @@ -185,5 +198,10 @@ void source_all_streams_wrapper(GstElement *element, gpointer user) DECLSPEC_HID GstFlowReturn stream_new_sample_wrapper(GstElement *appsink, gpointer user) DECLSPEC_HIDDEN; void stream_eos_wrapper(GstElement *appsink, gpointer user) DECLSPEC_HIDDEN; GstBusSyncReply watch_source_bus_wrapper(GstBus *bus, GstMessage *message, gpointer user) DECLSPEC_HIDDEN; +gboolean activate_push_mode_wrapper(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate) DECLSPEC_HIDDEN; +gboolean query_input_src_wrapper(GstPad *pad, GstObject *parent, GstQuery *query) DECLSPEC_HIDDEN; +GstBusSyncReply watch_decoder_bus_wrapper(GstBus *bus, GstMessage *message, gpointer user) DECLSPEC_HIDDEN; +GstFlowReturn decoder_new_sample_wrapper(GstElement *appsink, gpointer user) DECLSPEC_HIDDEN; +void decoder_pad_added_wrapper(GstElement *element, GstPad *Pad, gpointer user) DECLSPEC_HIDDEN;
#endif diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 489b543738..4e9cc53a18 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -53,6 +53,7 @@ BOOL init_gstreamer(void) DECLSPEC_HIDDEN;
void start_dispatch_thread(void) DECLSPEC_HIDDEN;
+extern HRESULT mfplat_DllRegisterServer(void) DECLSPEC_HIDDEN; extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) DECLSPEC_HIDDEN;
IMFMediaType* media_type_from_caps(GstCaps *caps); @@ -60,6 +61,12 @@ GstCaps *caps_from_media_type(IMFMediaType *type); IMFSample* mf_sample_from_gst_buffer(GstBuffer *in); GstBuffer* gst_buffer_from_mf_sample(IMFSample *in);
+enum decoder_type +{ + DECODER_TYPE_H264, + DECODER_TYPE_AAC, +}; +HRESULT generic_decoder_construct(REFIID riid, void **obj, enum decoder_type); enum source_type { SOURCE_TYPE_MPEG_4, diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 2872710b3e..12ca11fa6c 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -365,7 +365,8 @@ HRESULT WINAPI DllRegisterServer(void) IFilterMapper2_RegisterFilter(mapper, &CLSID_WAVEParser, wave_parserW, NULL, NULL, NULL, ®_wave_parser);
IFilterMapper2_Release(mapper); - return S_OK; + + return mfplat_DllRegisterServer(); }
HRESULT WINAPI DllUnregisterServer(void) diff --git a/dlls/winegstreamer/mf_decode.c b/dlls/winegstreamer/mf_decode.c new file mode 100644 index 0000000000..9c36f94fdc --- /dev/null +++ b/dlls/winegstreamer/mf_decode.c @@ -0,0 +1,1210 @@ +#include "config.h" + +#include <gst/gst.h> + +#include "gst_private.h" +#include "gst_cbs.h" + +#include <stdarg.h> + +#define COBJMACROS +#define NONAMELESSUNION + +#include "mfapi.h" +#include "mferror.h" +#include "mfobjects.h" +#include "mftransform.h" + +#include "wine/debug.h" +#include "wine/heap.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mfplat); + +const GUID *h264_input_types[] = {&MFVideoFormat_H264}; +const GUID *h264_output_types[] = {&MFVideoFormat_I420, &MFVideoFormat_IYUV, &MFVideoFormat_NV12, &MFVideoFormat_YUY2, &MFVideoFormat_YV12}; + +const GUID *aac_input_types[] = {&MFAudioFormat_AAC}; +const GUID *aac_output_types[] = {&MFAudioFormat_Float}; + +static struct decoder_desc +{ + const GUID *major_type; + const GUID **input_types; + unsigned int input_types_count; + const GUID **output_types; + unsigned int output_types_count; +} decoder_descs[] = +{ + { /* DECODER_TYPE_H264 */ + &MFMediaType_Video, + h264_input_types, + ARRAY_SIZE(h264_input_types), + h264_output_types, + ARRAY_SIZE(h264_output_types), + }, + { /* DECODER_TYPE_AAC */ + &MFMediaType_Audio, + aac_input_types, + ARRAY_SIZE(aac_input_types), + aac_output_types, + ARRAY_SIZE(aac_output_types), + } +}; + +struct mf_decoder +{ + IMFTransform IMFTransform_iface; + IMFAsyncCallback process_message_callback; + LONG refcount; + enum decoder_type type; + BOOL video; + IMFMediaType *input_type, *output_type; + BOOL valid_state; + GstBus *bus; + GstElement *container; + GstElement *parser, *decoder, *converter, *appsink; + GstPad *input_src, *their_sink; + unsigned int output_counter; + BOOL draining, eos; + CRITICAL_SECTION state_cs; + CONDITION_VARIABLE state_cv; +}; + +static struct mf_decoder *impl_mf_decoder_from_IMFTransform(IMFTransform *iface) +{ + return CONTAINING_RECORD(iface, struct mf_decoder, IMFTransform_iface); +} + +static struct mf_decoder *impl_from_message_callback_IMFAsyncCallback(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct mf_decoder, process_message_callback); +} + +static HRESULT WINAPI mf_decoder_QueryInterface (IMFTransform *iface, REFIID riid, void **out) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), out); + + if (IsEqualIID(riid, &IID_IMFTransform) || + IsEqualIID(riid, &IID_IUnknown)) + { + *out = iface; + IMFTransform_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI mf_decoder_AddRef(IMFTransform *iface) +{ + struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface); + ULONG refcount = InterlockedIncrement(&This->refcount); + + TRACE("%p, refcount %u.\n", iface, refcount); + + return refcount; +} + +static void mf_decoder_destroy(struct mf_decoder *decoder); +static ULONG WINAPI mf_decoder_Release(IMFTransform *iface) +{ + struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface); + ULONG refcount = InterlockedDecrement(&This->refcount); + + TRACE("%p, refcount %u.\n", iface, refcount); + + if (!refcount) + { + mf_decoder_destroy(This); + } + + return refcount; +} + +static HRESULT WINAPI mf_decoder_GetStreamLimits(IMFTransform *iface, DWORD *input_minimum, DWORD *input_maximum, + DWORD *output_minimum, DWORD *output_maximum) +{ + TRACE("%p, %p, %p, %p, %p.\n", iface, input_minimum, input_maximum, output_minimum, output_maximum); + + *input_minimum = *input_maximum = *output_minimum = *output_maximum = 1; + + return S_OK; +} + +static HRESULT WINAPI mf_decoder_GetStreamCount(IMFTransform *iface, DWORD *inputs, DWORD *outputs) +{ + TRACE("%p %p %p.\n", iface, inputs, outputs); + + *inputs = *outputs = 1; + + return S_OK; +} + +static HRESULT WINAPI mf_decoder_GetStreamIDs(IMFTransform *iface, DWORD input_size, DWORD *inputs, + DWORD output_size, DWORD *outputs) +{ + TRACE("%p %u %p %u %p.\n", iface, input_size, inputs, output_size, outputs); + + return E_NOTIMPL; +} + + +static HRESULT WINAPI mf_decoder_GetInputStreamInfo(IMFTransform *iface, DWORD id, MFT_INPUT_STREAM_INFO *info) +{ + struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface); + + TRACE("%p %u %p\n", This, id, info); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + /* If we create a wrapped GstBuffer, remove MFT_INPUT_STREAM_DOES_NOT_ADDREF */ + info->dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES | MFT_INPUT_STREAM_DOES_NOT_ADDREF; + info->cbMaxLookahead = 0; + info->cbAlignment = 0; + /* this is incorrect */ + info->hnsMaxLatency = 0; + return S_OK; +} + +static HRESULT WINAPI mf_decoder_GetOutputStreamInfo(IMFTransform *iface, DWORD id, MFT_OUTPUT_STREAM_INFO *info) +{ + struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface); + MFT_OUTPUT_STREAM_INFO stream_info = {}; + + TRACE("%p %u %p\n", This, id, info); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + stream_info.dwFlags = MFT_OUTPUT_STREAM_PROVIDES_SAMPLES; + stream_info.cbSize = 0; + stream_info.cbAlignment = 0; + + *info = stream_info; + + return S_OK; +} + +static HRESULT WINAPI mf_decoder_GetAttributes(IMFTransform *iface, IMFAttributes **attributes) +{ + FIXME("%p, %p. stub!\n", iface, attributes); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mf_decoder_GetInputStreamAttributes(IMFTransform *iface, DWORD id, + IMFAttributes **attributes) +{ + FIXME("%p, %u, %p. stub!\n", iface, id, attributes); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mf_decoder_GetOutputStreamAttributes(IMFTransform *iface, DWORD id, + IMFAttributes **attributes) +{ + FIXME("%p, %u, %p. stub!\n", iface, id, attributes); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mf_decoder_DeleteInputStream(IMFTransform *iface, DWORD id) +{ + FIXME("%p, %u. stub!\n", iface, id); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mf_decoder_AddInputStreams(IMFTransform *iface, DWORD streams, DWORD *ids) +{ + FIXME("%p, %u, %p. stub!\n", iface, streams, ids); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mf_decoder_GetInputAvailableType(IMFTransform *iface, DWORD id, DWORD index, + IMFMediaType **type) +{ + struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface); + IMFMediaType *input_type; + HRESULT hr; + + TRACE("%p, %u, %u, %p\n", This, id, index, type); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (index >= decoder_descs[This->type].input_types_count) + return MF_E_NO_MORE_TYPES; + + if (FAILED(hr = MFCreateMediaType(&input_type))) + return hr; + + if (FAILED(hr = IMFMediaType_SetGUID(input_type, &MF_MT_MAJOR_TYPE, decoder_descs[This->type].major_type))) + { + IMFMediaType_Release(input_type); + return hr; + } + + if (FAILED(hr = IMFMediaType_SetGUID(input_type, &MF_MT_SUBTYPE, decoder_descs[This->type].input_types[index]))) + { + IMFMediaType_Release(input_type); + return hr; + } + + *type = input_type; + + return S_OK; +} + +static HRESULT WINAPI mf_decoder_GetOutputAvailableType(IMFTransform *iface, DWORD id, DWORD index, + IMFMediaType **type) +{ + struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface); + IMFMediaType *output_type; + HRESULT hr; + + TRACE("%p, %u, %u, %p\n", This, id, index, type); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (!(This->input_type)) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + if (index >= decoder_descs[This->type].output_types_count) + return MF_E_NO_MORE_TYPES; + + if (FAILED(hr = MFCreateMediaType(&output_type))) + return hr; + + /* TODO: This may need to be more fine tuned */ + if (FAILED(hr = IMFMediaType_CopyAllItems(This->input_type, (IMFAttributes*) output_type))) + { + IMFMediaType_Release(output_type); + return hr; + } + + if (FAILED(hr = IMFMediaType_SetGUID(output_type, &MF_MT_MAJOR_TYPE, decoder_descs[This->type].major_type))) + { + IMFMediaType_Release(output_type); + return hr; + } + + if (FAILED(hr = IMFMediaType_SetGUID(output_type, &MF_MT_SUBTYPE, decoder_descs[This->type].output_types[index]))) + { + IMFMediaType_Release(output_type); + return hr; + } + + *type = output_type; + + return S_OK; +} + +static gboolean activate_push_mode(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate) +{ + TRACE("%s mft input pad in %s mode.\n", + activate ? "Activating" : "Deactivating", gst_pad_mode_get_name(mode)); + + switch (mode) { + case GST_PAD_MODE_PUSH: + return TRUE; + default: + return FALSE; + } +} + +static gboolean query_input_src(GstPad *pad, GstObject *parent, GstQuery *query) +{ + struct mf_decoder *This = gst_pad_get_element_private(pad); + + TRACE("GStreamer queries MFT Input Pad %p for %s\n", This, GST_QUERY_TYPE_NAME(query)); + + switch (GST_QUERY_TYPE(query)) + { + case GST_QUERY_CAPS: + { + gst_query_set_caps_result(query, caps_from_media_type(This->input_type)); + return TRUE; + } + case GST_QUERY_SCHEDULING: + { + gst_query_add_scheduling_mode(query, GST_PAD_MODE_PUSH); + return TRUE; + } + case GST_QUERY_SEEKING: + { + GstFormat format; + gboolean seekable; + gint64 segment_start, segment_end; + + gst_query_parse_seeking(query, &format, &seekable, &segment_start, &segment_end); + gst_query_set_seeking(query, format, 0, segment_start, segment_end); + return TRUE; + } + case GST_QUERY_DURATION: + { + return FALSE; + } + case GST_QUERY_LATENCY: + { + return FALSE; + } + default: + { + ERR("Unhandled query type %s on MFT Input Pad %p\n", GST_QUERY_TYPE_NAME(query), This); + return gst_pad_query_default (pad, parent, query); + } + } +} + +static GstFlowReturn decoder_new_sample(GstElement *appsink, gpointer user) +{ + struct mf_decoder *This = (struct mf_decoder *) user; + + This->output_counter++; + + return GST_FLOW_OK; +} + +static BOOL find_decoder_from_caps(GstCaps *input_caps, GstElement **decoder, GstElement **parser) +{ + GList *parser_list_one, *parser_list_two; + GList *walk; + BOOL ret = TRUE; + + TRACE("input caps: %s\n", gst_caps_to_string(input_caps)); + + parser_list_one = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_PARSER, 1); + parser_list_two = gst_element_factory_list_filter(parser_list_one, input_caps, GST_PAD_SINK, 0); + gst_plugin_feature_list_free(parser_list_one); + parser_list_one = parser_list_two; + if (!(g_list_length(parser_list_one))) + { + GList *decoder_list_one, *decoder_list_two; + decoder_list_one = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_DECODER, 1); + decoder_list_two = gst_element_factory_list_filter(decoder_list_one, input_caps, GST_PAD_SINK, 0); + gst_plugin_feature_list_free(decoder_list_one); + decoder_list_one = decoder_list_two; + if (!(g_list_length(decoder_list_one)) || + !(*decoder = gst_element_factory_create(g_list_first(decoder_list_one)->data, NULL))) + { + gst_plugin_feature_list_free(decoder_list_one); + ERR("Failed to create decoder\n"); + ret = FALSE; + goto done; + } + TRACE("Found decoder %s\n", GST_ELEMENT_NAME(g_list_first(decoder_list_one)->data)); + } + else + { + for (walk = (GList *) parser_list_one; walk; walk = g_list_next(walk)) + { + GstElementFactory *parser_factory = walk->data; + const GList *templates, *walk_templ; + + templates = gst_element_factory_get_static_pad_templates(parser_factory); + + for (walk_templ = (GList *)templates; walk_templ; walk_templ = g_list_next(walk_templ)) + { + GList *decoder_list_one, *decoder_list_two; + GstStaticPadTemplate *templ = walk_templ->data; + GstCaps *templ_caps; + + if (templ->direction != GST_PAD_SRC) + continue; + + templ_caps = gst_static_pad_template_get_caps(templ); + + TRACE("Matching parser src caps %s to decoder.\n", gst_caps_to_string(templ_caps)); + + decoder_list_one = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_DECODER, 1); + decoder_list_two = gst_element_factory_list_filter(decoder_list_one, templ_caps, GST_PAD_SINK, 0); + gst_plugin_feature_list_free(decoder_list_one); + decoder_list_one = decoder_list_two; + gst_caps_unref(templ_caps); + + if (!(g_list_length(decoder_list_one))) + continue; + + if (!(*parser = gst_element_factory_create(parser_factory, NULL))) + { + gst_plugin_feature_list_free(decoder_list_one); + ERR("Failed to create parser\n"); + ret = FALSE; + goto done; + } + + if (!(*decoder = gst_element_factory_create(g_list_first(decoder_list_one)->data, NULL))) + { + gst_plugin_feature_list_free(decoder_list_one); + ERR("Failed to create decoder\n"); + ret = FALSE; + goto done; + } + + TRACE("Found decoder %s parser %s\n", + GST_ELEMENT_NAME(g_list_first(decoder_list_one)->data), GST_ELEMENT_NAME(parser_factory)); + gst_plugin_feature_list_free(decoder_list_one); + + goto done; + } + } + } + done: + gst_plugin_feature_list_free(parser_list_one); + return ret; +} + +static void decoder_update_pipeline(struct mf_decoder *This) +{ + GstSegment *segment; + GstCaps *input_caps = NULL; + + This->valid_state = FALSE; + + /* tear down current pipeline */ + gst_element_set_state(This->container, GST_STATE_READY); + if (gst_element_get_state(This->container, NULL, NULL, -1) == GST_STATE_CHANGE_FAILURE) + { + ERR("Failed to stop container\n"); + } + + g_object_set(This->appsink, "caps", gst_caps_new_empty(), NULL); + + if (This->input_src) + { + gst_pad_unlink(This->input_src, This->their_sink); + gst_object_unref(G_OBJECT(This->input_src)); + This->input_src = NULL; + } + + if (This->their_sink) + { + gst_object_unref(G_OBJECT(This->their_sink)); + This->their_sink = NULL; + } + + if (This->parser) + { + gst_element_unlink(This->parser, This->decoder); + gst_bin_remove(GST_BIN(This->container), This->parser); + This->parser = NULL; + } + if (This->decoder) + { + gst_element_unlink(This->decoder, This->converter); + gst_bin_remove(GST_BIN(This->container), This->decoder); + This->decoder = NULL; + } + + /* we can only have a valid state if an input and output type is present */ + if (!This->input_type || !This->output_type) + return; + + /* We do leave a lot of unfreed objects here when we failure, + but it will be cleaned up on the next call */ + + input_caps = caps_from_media_type(This->input_type); + + if (!(This->input_src = gst_pad_new_from_template(gst_pad_template_new( + "mf_src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + input_caps + ), "input_src"))) + { + ERR("Failed to create input source\n"); + goto done; + } + + gst_pad_set_activatemode_function(This->input_src, activate_push_mode_wrapper); + gst_pad_set_query_function(This->input_src, query_input_src_wrapper); + gst_pad_set_element_private(This->input_src, This); + + if (!(find_decoder_from_caps(input_caps, &This->decoder, &This->parser))) + { + goto done; + } + + gst_bin_add(GST_BIN(This->container), This->decoder); + if (This->parser) + { + gst_bin_add(GST_BIN(This->container), This->parser); + } + + if (!(This->their_sink = gst_element_get_static_pad(This->parser ? This->parser : This->decoder, "sink"))) + { + goto done; + } + + g_object_set(This->appsink, "caps", caps_from_media_type(This->output_type), NULL); + + if (gst_pad_link(This->input_src, This->their_sink) != GST_PAD_LINK_OK) + { + ERR("Failed to link input source to decoder sink\n"); + return; + } + + if (This->parser && !(gst_element_link(This->parser, This->decoder))) + { + ERR("Failed to link parser to decoder\n"); + goto done; + } + + if (!(gst_element_link(This->decoder, This->converter))) + { + ERR("Failed to link decoder to converter\n"); + goto done; + } + + gst_element_set_state(This->container, GST_STATE_PLAYING); + + gst_pad_set_active(This->input_src, 1); + gst_pad_push_event(This->input_src, gst_event_new_stream_start("decoder-stream")); + gst_pad_push_event(This->input_src, gst_event_new_caps(caps_from_media_type(This->input_type))); + segment = gst_segment_new(); + gst_segment_init(segment, GST_FORMAT_DEFAULT); + gst_pad_push_event(This->input_src, gst_event_new_segment(segment)); + + This->valid_state = TRUE; + done: + if (input_caps) + gst_caps_unref(input_caps); + return; +} + +static HRESULT WINAPI mf_decoder_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) +{ + struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface); + HRESULT hr = S_OK; + + TRACE("%p, %u, %p, %#x\n", This, id, type, flags); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (type) + { + GUID major_type, subtype; + BOOL found = FALSE; + + if (FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_MAJOR_TYPE, &major_type))) + return hr; + if (FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype))) + return hr; + + for (unsigned int i = 0; i < decoder_descs[This->type].input_types_count; i++) + { + UINT64 unused; + + if (IsEqualGUID(&major_type, decoder_descs[This->type].major_type) && + IsEqualGUID(&subtype, decoder_descs[This->type].input_types[i])) + { + if (This->video) + { + if (FAILED(hr = IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &unused))) + return hr; + + if (FAILED(hr = IMFMediaType_GetUINT64(type, &MF_MT_FRAME_RATE, &unused))) + return hr; + } + + found = TRUE; + break; + } + } + + if (!found) + return MF_E_INVALIDTYPE; + } + + if (flags & MFT_SET_TYPE_TEST_ONLY) + { + return S_OK; + } + + EnterCriticalSection(&This->state_cs); + + if (type) + { + if (!This->input_type) + if (FAILED(hr = MFCreateMediaType(&This->input_type))) + goto done; + + if (FAILED(hr = IMFMediaType_CopyAllItems(type, (IMFAttributes*) This->input_type))) + goto done; + } + else if (This->input_type) + { + IMFMediaType_Release(This->input_type); + This->input_type = NULL; + } + + decoder_update_pipeline(This); + + done: + LeaveCriticalSection(&This->state_cs); + WakeAllConditionVariable(&This->state_cv); + return hr; +} + +static HRESULT WINAPI mf_decoder_SetOutputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) +{ + struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface); + HRESULT hr = S_OK; + + TRACE("%p, %u, %p, %#x\n", This, id, type, flags); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (type) + { + /* validate the type */ + + for (unsigned int i = 0; i < decoder_descs[This->type].output_types_count; i++) + { + GUID major_type, subtype; + UINT64 unused; + + if (FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_MAJOR_TYPE, &major_type))) + return hr; + if (FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype))) + return hr; + + if (IsEqualGUID(&major_type, decoder_descs[This->type].major_type) && + IsEqualGUID(&subtype, decoder_descs[This->type].output_types[i])) + { + if (This->video) + { + if (FAILED(hr = IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &unused))) + return hr; + + if (FAILED(hr = IMFMediaType_GetUINT64(type, &MF_MT_FRAME_RATE, &unused))) + return hr; + } + + break; + } + } + } + + if (flags & MFT_SET_TYPE_TEST_ONLY) + { + return S_OK; + } + + EnterCriticalSection(&This->state_cs); + if (type) + { + if (!This->output_type) + if (FAILED(hr = MFCreateMediaType(&This->output_type))) + goto done; + + if (FAILED(hr = IMFMediaType_CopyAllItems(type, (IMFAttributes*) This->output_type))) + goto done; + } + else if (This->output_type) + { + IMFMediaType_Release(This->output_type); + This->output_type = NULL; + } + + decoder_update_pipeline(This); + + done: + LeaveCriticalSection(&This->state_cs); + WakeAllConditionVariable(&This->state_cv); + return hr; +} + +static HRESULT WINAPI mf_decoder_GetInputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) +{ + FIXME("%p, %u, %p. stub!\n", iface, id, type); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mf_decoder_GetOutputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) +{ + FIXME("%p, %u, %p. stub!\n", iface, id, type); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mf_decoder_GetInputStatus(IMFTransform *iface, DWORD id, DWORD *flags) +{ + struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface); + + TRACE("%p, %u, %p\n", This, id, flags); + + *flags = This->output_counter ? MFT_INPUT_STATUS_ACCEPT_DATA : 0; + + return S_OK; +} + +static HRESULT WINAPI mf_decoder_GetOutputStatus(IMFTransform *iface, DWORD *flags) +{ + struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface); + + TRACE("%p, %p.\n", This, flags); + + *flags = This->output_counter ? MFT_OUTPUT_STATUS_SAMPLE_READY : 0; + + return S_OK; +} + +static HRESULT WINAPI mf_decoder_SetOutputBounds(IMFTransform *iface, LONGLONG lower, LONGLONG upper) +{ + FIXME("%p, %s, %s. stub!\n", iface, wine_dbgstr_longlong(lower), wine_dbgstr_longlong(upper)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mf_decoder_ProcessEvent(IMFTransform *iface, DWORD id, IMFMediaEvent *event) +{ + FIXME("%p, %u, %p. stub!\n", iface, id, event); + + return E_NOTIMPL; +} + +static HRESULT WINAPI decoder_process_message_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 ULONG WINAPI decoder_process_message_callback_AddRef(IMFAsyncCallback *iface) +{ + struct mf_decoder *decoder = impl_from_message_callback_IMFAsyncCallback(iface); + return IMFTransform_AddRef(&decoder->IMFTransform_iface); +} + +static ULONG WINAPI decoder_process_message_callback_Release(IMFAsyncCallback *iface) +{ + struct mf_decoder *decoder = impl_from_message_callback_IMFAsyncCallback(iface); + return IMFTransform_Release(&decoder->IMFTransform_iface); +} + +static HRESULT WINAPI decoder_process_message_callback_GetParameters(IMFAsyncCallback *iface, + DWORD *flags, DWORD *queue) +{ + return E_NOTIMPL; +} + +const GUID WINE_MFT_MESSAGE_TYPE = {0xd09998bf, 0x102f, 0x4efa, {0x8f,0x84,0x06,0x1f,0xa4,0x10,0xf2,0x64}}; + +static HRESULT WINAPI decoder_process_message_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct mf_decoder *This = impl_from_message_callback_IMFAsyncCallback(iface); + IUnknown *state; + IMFAttributes *async_param; + MFT_MESSAGE_TYPE message_type; + HRESULT hr; + + state = IMFAsyncResult_GetStateNoAddRef(result); + if (!state) + return E_FAIL; + if (FAILED(hr = IUnknown_QueryInterface(state, &IID_IMFAttributes, (void **)&async_param))) + return hr; + if (FAILED(hr = IMFAttributes_GetUINT32(async_param, &WINE_MFT_MESSAGE_TYPE, &message_type))) + { + IMFAttributes_Release(async_param); + return hr; + } + IMFAttributes_Release(async_param); + + switch (message_type) + { + case MFT_MESSAGE_COMMAND_DRAIN: + { + EnterCriticalSection(&This->state_cs); + This->draining = TRUE; + LeaveCriticalSection(&This->state_cs); + WakeAllConditionVariable(&This->state_cv); + gst_pad_push_event(This->input_src, gst_event_new_eos()); + + EnterCriticalSection(&This->state_cs); + while(This->eos == FALSE) + { + SleepConditionVariableCS(&This->state_cv, &This->state_cs, INFINITE); + } + gst_pad_push_event(This->input_src, gst_event_new_flush_stop(0)); + This->draining = FALSE; + LeaveCriticalSection(&This->state_cs); + WakeAllConditionVariable(&This->state_cv); + return S_OK; + } + default: + return E_FAIL; + } +} + +static const IMFAsyncCallbackVtbl process_message_callback_vtbl = +{ + decoder_process_message_callback_QueryInterface, + decoder_process_message_callback_AddRef, + decoder_process_message_callback_Release, + decoder_process_message_callback_GetParameters, + decoder_process_message_callback_Invoke, +}; + +static HRESULT WINAPI mf_decoder_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) +{ + struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface); + IMFAttributes *async_param; + HRESULT hr; + + TRACE("%p, %u %lu.\n", This, message, param); + + if (FAILED(hr = MFCreateAttributes(&async_param, 1))) + return hr; + EnterCriticalSection(&This->state_cs); + + switch (message) + { + case MFT_MESSAGE_COMMAND_DRAIN: + { + if (This->draining) + { + hr = S_OK; + break; + } + + IMFAttributes_SetUINT32(async_param, &WINE_MFT_MESSAGE_TYPE, message); + + MFPutWorkItem(MF_MULTITHREADED_WORKQUEUE, &This->process_message_callback, (IUnknown *)async_param); + while (!This->draining) + SleepConditionVariableCS(&This->state_cv, &This->state_cs, INFINITE); + + hr = S_OK; + break; + } + case MFT_MESSAGE_NOTIFY_BEGIN_STREAMING: + { + hr = S_OK; + break; + } + default: + { + ERR("Unhandled message type %u.\n", message); + hr = E_FAIL; + break; + } + } + + LeaveCriticalSection(&This->state_cs); + IMFAttributes_Release(async_param); + return hr; +} + +static HRESULT WINAPI mf_decoder_ProcessInput(IMFTransform *iface, DWORD id, IMFSample *sample, DWORD flags) +{ + struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface); + GstBuffer *gst_buffer; + GstFlowReturn ret; + HRESULT hr = S_OK; + + TRACE("%p, %u, %p, %#x\n", This, id, sample, flags); + + if (flags) + WARN("Unsupported flags %#x\n", flags); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (!This->valid_state) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + EnterCriticalSection(&This->state_cs); + + if (This->output_counter || This->draining) + { + hr = MF_E_NOTACCEPTING; + goto done; + } + + if (!(gst_buffer = gst_buffer_from_mf_sample(sample))) + { + hr = E_FAIL; + goto done; + } + + ret = gst_pad_push(This->input_src, gst_buffer); + if (ret != GST_FLOW_OK) + { + ERR("Couldn't process input ret = %d\n", ret); + hr = E_FAIL; + goto done; + } + + done: + LeaveCriticalSection(&This->state_cs); + return hr; +} + +static HRESULT WINAPI mf_decoder_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count, + MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status) +{ + struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface); + GstSample *buffer; + MFT_OUTPUT_DATA_BUFFER *relevant_buffer = NULL; + + TRACE("%p, %#x, %u, %p, %p,\n", iface, flags, count, samples, status); + + if (flags) + { + WARN("Unsupported flags %#x\n", flags); + } + + if (!This->valid_state) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + for (unsigned int i = 0; i < count; i++) + { + MFT_OUTPUT_DATA_BUFFER *out_buffer = &samples[i]; + + if (out_buffer->dwStreamID != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (relevant_buffer) + return MF_E_INVALIDSTREAMNUMBER; + + relevant_buffer = out_buffer; + } + + if (!relevant_buffer) + return S_OK; + + EnterCriticalSection(&This->state_cs); + if (This->draining) + { + if (This->eos) + buffer = NULL; + else + g_signal_emit_by_name(This->appsink, "pull-sample", &buffer); + } + else + g_signal_emit_by_name(This->appsink, "try-pull-sample", 0, &buffer); + LeaveCriticalSection(&This->state_cs); + + if (!buffer) + return MF_E_TRANSFORM_NEED_MORE_INPUT; + This->output_counter--; + + relevant_buffer->pSample = mf_sample_from_gst_buffer(gst_sample_get_buffer(buffer)); + gst_sample_unref(buffer); + relevant_buffer->dwStatus = S_OK; + relevant_buffer->pEvents = NULL; + *status = 0; + return S_OK; +} + +static const IMFTransformVtbl mf_decoder_vtbl = +{ + mf_decoder_QueryInterface, + mf_decoder_AddRef, + mf_decoder_Release, + mf_decoder_GetStreamLimits, + mf_decoder_GetStreamCount, + mf_decoder_GetStreamIDs, + mf_decoder_GetInputStreamInfo, + mf_decoder_GetOutputStreamInfo, + mf_decoder_GetAttributes, + mf_decoder_GetInputStreamAttributes, + mf_decoder_GetOutputStreamAttributes, + mf_decoder_DeleteInputStream, + mf_decoder_AddInputStreams, + mf_decoder_GetInputAvailableType, + mf_decoder_GetOutputAvailableType, + mf_decoder_SetInputType, + mf_decoder_SetOutputType, + mf_decoder_GetInputCurrentType, + mf_decoder_GetOutputCurrentType, + mf_decoder_GetInputStatus, + mf_decoder_GetOutputStatus, + mf_decoder_SetOutputBounds, + mf_decoder_ProcessEvent, + mf_decoder_ProcessMessage, + mf_decoder_ProcessInput, + mf_decoder_ProcessOutput, +}; + +GstBusSyncReply watch_decoder_bus(GstBus *bus, GstMessage *message, gpointer user_data) +{ + struct mf_decoder *This = user_data; + GError *err = NULL; + gchar *dbg_info = NULL; + + TRACE("decoder %p message type %s\n", This, GST_MESSAGE_TYPE_NAME(message)); + + switch (message->type) + { + case GST_MESSAGE_ERROR: + gst_message_parse_error(message, &err, &dbg_info); + ERR("%s: %s\n", GST_OBJECT_NAME(message->src), err->message); + ERR("%s\n", dbg_info); + g_error_free(err); + g_free(dbg_info); + break; + case GST_MESSAGE_WARNING: + gst_message_parse_warning(message, &err, &dbg_info); + WARN("%s: %s\n", GST_OBJECT_NAME(message->src), err->message); + WARN("%s\n", dbg_info); + g_error_free(err); + g_free(dbg_info); + break; + case GST_MESSAGE_EOS: + This->eos = TRUE; + WakeAllConditionVariable(&This->state_cv); + break; + default: + break; + } + + return GST_BUS_DROP; +} + +static void mf_decoder_destroy(struct mf_decoder *This) +{ + if (This->input_type) + { + IMFMediaType_Release(This->input_type); + This->input_type = NULL; + } + + if (This->output_type) + { + IMFMediaType_Release(This->output_type); + This->output_type = NULL; + } + + decoder_update_pipeline(This); + + if (This->their_sink) + gst_object_unref(G_OBJECT(This->their_sink)); + + if (This->container) + gst_object_unref(G_OBJECT(This->container)); + + if (This->bus) + gst_object_unref(G_OBJECT(This->bus)); + + DeleteCriticalSection(&This->state_cs); + + heap_free(This); +} + +HRESULT generic_decoder_construct(REFIID riid, void **obj, enum decoder_type type) +{ + struct mf_decoder *This; + HRESULT hr = S_OK; + + TRACE("%s, %p %u.\n", debugstr_guid(riid), obj, type); + + if (!(This = heap_alloc_zero(sizeof(*This)))) + return E_OUTOFMEMORY; + This->type = type; + This->video = decoder_descs[type].major_type == &MFMediaType_Video; + + InitializeCriticalSection(&This->state_cs); + InitializeConditionVariable(&This->state_cv); + + This->container = gst_bin_new(NULL); + This->bus = gst_bus_new(); + gst_bus_set_sync_handler(This->bus, watch_decoder_bus_wrapper, This, NULL); + gst_element_set_bus(This->container, This->bus); + + if (!(This->converter = gst_element_factory_make(This->video ? "videoconvert" : "audioconvert", NULL))) + { + ERR("Failed to create videoconvert\n"); + hr = E_FAIL; + goto fail; + } + if (!(This->appsink = gst_element_factory_make("appsink", NULL))) + { + ERR("Failed to create appsink\n"); + hr = E_FAIL; + goto fail; + } + + gst_bin_add(GST_BIN(This->container), This->converter); + gst_bin_add(GST_BIN(This->container), This->appsink); + + if (!(gst_element_link(This->converter, This->appsink))) + { + ERR("Failed to link converter to appsink\n"); + hr = E_FAIL; + goto fail; + } + + g_object_set(This->appsink, "emit-signals", TRUE, NULL); + g_object_set(This->appsink, "sync", FALSE, NULL); + g_signal_connect(This->appsink, "new-sample", G_CALLBACK(decoder_new_sample_wrapper), This); + + This->process_message_callback.lpVtbl = &process_message_callback_vtbl; + + This->IMFTransform_iface.lpVtbl = &mf_decoder_vtbl; + This->refcount = 1; + + *obj = This; + return S_OK; + + fail: + ERR("Failed to create Decoder MFT type %u, hr = %#x\n", type, hr); + mf_decoder_destroy(This); + return hr; +} + +void perform_cb_mf_decode(struct cb_data *cbdata) +{ + switch (cbdata->type) + { + case ACTIVATE_PUSH_MODE: + { + struct activate_mode_data *data = &cbdata->u.activate_mode_data; + cbdata->u.activate_mode_data.ret = activate_push_mode(data->pad, data->parent, data->mode, data->activate); + break; + } + case QUERY_INPUT_SRC: + { + struct query_function_data *data = &cbdata->u.query_function_data; + cbdata->u.query_function_data.ret = query_input_src(data->pad, data->parent, data->query); + break; + } + case DECODER_NEW_SAMPLE: + { + struct new_sample_data *data = &cbdata->u.new_sample_data; + cbdata->u.new_sample_data.ret = decoder_new_sample(data->appsink, data->user); + break; + } + case WATCH_DECODER_BUS: + { + struct watch_bus_data *data = &cbdata->u.watch_bus_data; + cbdata->u.watch_bus_data.ret = watch_decoder_bus(data->bus, data->msg, data->user); + break; + } + default: + { + ERR("Wrong callback forwarder called\n"); + return; + } + } +} \ No newline at end of file diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 5fbb331c89..b7bab5e572 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -404,6 +404,16 @@ failed: return hr; }
+static HRESULT h264_decoder_create(REFIID riid, void **ret) +{ + return generic_decoder_construct(riid, ret, DECODER_TYPE_H264); +} + +static HRESULT aac_decoder_create(REFIID riid, void **ret) +{ + return generic_decoder_construct(riid, ret, DECODER_TYPE_AAC); +} + static HRESULT mp4_stream_handler_create(REFIID riid, void **ret) { return container_stream_handler_construct(riid, ret, SOURCE_TYPE_MPEG_4); @@ -417,6 +427,8 @@ static const struct class_object class_objects[] = { { &CLSID_VideoProcessorMFT, &video_processor_create }, + { &CLSID_CMSH264DecoderMFT, &h264_decoder_create }, + { &CLSID_CMSAACDecMFT, &aac_decoder_create }, { &CLSID_MPEG4ByteStreamHandler, &mp4_stream_handler_create }, };
@@ -446,11 +458,140 @@ HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) return CLASS_E_CLASSNOTAVAILABLE; }
+struct register_type_info +{ + const GUID *major_type; + const GUID *sub_type; +}; + +static WCHAR h264decoderW[] = {'H','.','2','6','4',' ','D','e','c','o','d','e','r',0}; +const struct register_type_info h264_decoder_input_types[] = +{ + { + &MFMediaType_Video, + &MFVideoFormat_H264 + } +}; +const struct register_type_info h264_decoder_output_types[] = +{ + { + &MFMediaType_Video, + &MFVideoFormat_I420 + }, + { + &MFMediaType_Video, + &MFVideoFormat_IYUV + }, + { + &MFMediaType_Video, + &MFVideoFormat_NV12 + }, + { + &MFMediaType_Video, + &MFVideoFormat_YUY2, + }, + { + &MFMediaType_Video, + &MFVideoFormat_YV12, + } +}; + +static WCHAR aacdecoderW[] = {'A','A','C',' ','D','e','c','o','d','e','r',0}; +const struct register_type_info aac_decoder_input_types[] = +{ + { + &MFMediaType_Audio, + &MFAudioFormat_AAC + } +}; + +const struct register_type_info aac_decoder_output_types[] = +{ + { + &MFMediaType_Audio, + &MFAudioFormat_Float + } +}; + +static const struct mft +{ + const GUID *clsid; + const GUID *category; + LPWSTR name; + const UINT32 flags; + const UINT32 input_types_count; + const struct register_type_info *input_types; + const UINT32 output_types_count; + const struct register_type_info *output_types; + IMFAttributes *attributes; +} +mfts[] = +{ + { + &CLSID_CMSH264DecoderMFT, + &MFT_CATEGORY_VIDEO_DECODER, + h264decoderW, + MFT_ENUM_FLAG_SYNCMFT, + ARRAY_SIZE(h264_decoder_input_types), + h264_decoder_input_types, + ARRAY_SIZE(h264_decoder_output_types), + h264_decoder_output_types, + NULL + }, + { + &CLSID_CMSAACDecMFT, + &MFT_CATEGORY_AUDIO_DECODER, + aacdecoderW, + MFT_ENUM_FLAG_SYNCMFT, + ARRAY_SIZE(aac_decoder_input_types), + aac_decoder_input_types, + ARRAY_SIZE(aac_decoder_output_types), + aac_decoder_output_types, + NULL + } +}; + +HRESULT mfplat_DllRegisterServer(void) +{ + HRESULT hr; + + for (unsigned int i = 0; i < ARRAY_SIZE(mfts); i++) + { + const struct mft *cur = &mfts[i]; + + MFT_REGISTER_TYPE_INFO *input_types, *output_types; + input_types = heap_alloc(cur->input_types_count * sizeof(input_types[0])); + output_types = heap_alloc(cur->output_types_count * sizeof(output_types[0])); + for (unsigned int i = 0; i < cur->input_types_count; i++) + { + input_types[i].guidMajorType = *(cur->input_types[i].major_type); + input_types[i].guidSubtype = *(cur->input_types[i].sub_type); + } + for (unsigned int i = 0; i < cur->output_types_count; i++) + { + output_types[i].guidMajorType = *(cur->output_types[i].major_type); + output_types[i].guidSubtype = *(cur->output_types[i].sub_type); + } + + hr = MFTRegister(*(cur->clsid), *(cur->category), cur->name, cur->flags, cur->input_types_count, + input_types, cur->output_types_count, output_types, cur->attributes); + + heap_free(input_types); + heap_free(output_types); + + if (FAILED(hr)) + { + FIXME("Failed to register MFT, hr %#x\n", hr); + return hr; + } + } + return S_OK; +} + struct aac_user_data { WORD payload_type; WORD profile_level_indication; - WORD struct_type; WORD reserved; /*BYTE audio_specific_config;*/ }; diff --git a/dlls/winegstreamer/winegstreamer_classes.idl b/dlls/winegstreamer/winegstreamer_classes.idl index 997a28b052..ee8090cf4a 100644 --- a/dlls/winegstreamer/winegstreamer_classes.idl +++ b/dlls/winegstreamer/winegstreamer_classes.idl @@ -61,3 +61,15 @@ coclass VideoProcessorMFT {} uuid(271c3902-6095-4c45-a22f-20091816ee9e) ] coclass MPEG4ByteStreamHandler {} + +[ + threading(both), + uuid(62ce7e72-4c71-4d20-b15d-452831a87d9d) +] +coclass CMSH264DecoderMFT { } + +[ + threading(both), + uuid(32d186a7-218f-4c75-8876-dd77273a8999) +] +coclass CMSAACDecMFT { } diff --git a/include/mfidl.idl b/include/mfidl.idl index 15a68a4253..d07852e444 100644 --- a/include/mfidl.idl +++ b/include/mfidl.idl @@ -1066,4 +1066,6 @@ cpp_quote("EXTERN_GUID(MF_SESSION_APPROX_EVENT_OCCURRENCE_TIME, 0x190e852f, 0x62 cpp_quote("EXTERN_GUID(MF_PMP_SERVER_CONTEXT, 0x2f00c910, 0xd2cf, 0x4278, 0x8b, 0x6a, 0xd0, 0x77, 0xfa, 0xc3, 0xa2, 0x5f);")
cpp_quote("EXTERN_GUID(CLSID_VideoProcessorMFT, 0x88753b26, 0x5b24, 0x49bd, 0xb2, 0xe7, 0xc, 0x44, 0x5c, 0x78, 0xc9, 0x82);") -cpp_quote("EXTERN_GUID(CLSID_MPEG4ByteStreamHandler, 0x271c3902, 0x6095, 0x4c45, 0xa2, 0x2f, 0x20, 0x09, 0x18, 0x16, 0xee, 0x9e);") \ No newline at end of file +cpp_quote("EXTERN_GUID(CLSID_MPEG4ByteStreamHandler, 0x271c3902, 0x6095, 0x4c45, 0xa2, 0x2f, 0x20, 0x09, 0x18, 0x16, 0xee, 0x9e);") +cpp_quote("EXTERN_GUID(CLSID_CMSH264DecoderMFT, 0x62ce7e72, 0x4c71, 0x4d20, 0xb1, 0x5d, 0x45, 0x28, 0x31, 0xa8, 0x7d, 0x9d);") +cpp_quote("EXTERN_GUID(CLSID_CMSAACDecMFT, 0x32d186a7, 0x218f, 0x4c75, 0x88, 0x76, 0xdd, 0x77, 0x27, 0x3a, 0x89, 0x99);")
On 3/25/20 7:12 PM, Derek Lesho wrote:
dlls/winegstreamer/Makefile.in | 1 + dlls/winegstreamer/gst_cbs.c | 65 + dlls/winegstreamer/gst_cbs.h | 18 + dlls/winegstreamer/gst_private.h | 7 + dlls/winegstreamer/main.c | 3 +- dlls/winegstreamer/mf_decode.c | 1210 ++++++++++++++++++ dlls/winegstreamer/mfplat.c | 143 ++- dlls/winegstreamer/winegstreamer_classes.idl | 12 + include/mfidl.idl | 4 +- 9 files changed, 1460 insertions(+), 3 deletions(-) create mode 100644 dlls/winegstreamer/mf_decode.c
Even as an RFC, this is too much code to review all at once. Surely this can be split?