Resolves https://bugs.winehq.org/show_bug.cgi?id=9127 . (Some of the named programs in that issue may require additional currently-missing functionality, I didn't check; but if so, that's separate issues.)
Tested with
- winetest on Windows 7 - winetest on Windows 10 - winetest in Wine, of course - microkiri https://bugs.winehq.org/show_bug.cgi?id=9127#c102 - Wagamama High Spec Trial Edition https://wagahigh.com/download_trial.php#normal (ダウンロード means download) - Ninki Seiyuu no Tsukurikata Trial https://archive.org/details/sayou_trial
(WMV files in microkiri and Wagamama don't work, but that's separate issues. Also, they need the LC_ALL=ja_JP env, or they throw various goofy errors.)
-- v15: quartz/tests: Add tests for CLSID_CMpegVideoCodec. quartz/tests: Add tests for new CLSID_MPEG1Splitter functionality. winegstreamer: Improve and clean up some debug logs. winegstreamer: Include the framerate when converting video format to GstCaps. winegstreamer: Don't read format from unparsed MPEG audio. winegstreamer: Seek to end of stream instead of to stream duration. winegstreamer: Implement a little more of IAMStreamSelect in CLSID_MPEG1Splitter. winegstreamer: Implement CLSID_CMpegVideoCodec. winegstreamer: Add program stream and video output support to CLSID_MPEG1Splitter.
From: Alfred Agrell floating@muncher.se
--- dlls/winegstreamer/gst_private.h | 2 +- dlls/winegstreamer/main.c | 3 ++- dlls/winegstreamer/media_source.c | 2 +- dlls/winegstreamer/quartz_parser.c | 12 ++++++------ dlls/winegstreamer/unixlib.h | 1 + dlls/winegstreamer/wg_parser.c | 4 +++- dlls/winegstreamer/wm_reader.c | 2 +- 7 files changed, 15 insertions(+), 11 deletions(-)
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 8cfadd10bfc..99e0044e77d 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -70,7 +70,7 @@ HRESULT wg_sample_queue_create(struct wg_sample_queue **out); void wg_sample_queue_destroy(struct wg_sample_queue *queue); void wg_sample_queue_flush(struct wg_sample_queue *queue, bool all);
-wg_parser_t wg_parser_create(enum wg_parser_type type); +wg_parser_t wg_parser_create(enum wg_parser_type type, bool output_compressed); void wg_parser_destroy(wg_parser_t parser);
HRESULT wg_parser_connect(wg_parser_t parser, uint64_t file_size); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 853907e1825..a5f77ce8d56 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -66,11 +66,12 @@ bool array_reserve(void **elements, size_t *capacity, size_t count, size_t size) return TRUE; }
-wg_parser_t wg_parser_create(enum wg_parser_type type) +wg_parser_t wg_parser_create(enum wg_parser_type type, bool output_compressed) { struct wg_parser_create_params params = { .type = type, + .output_compressed = output_compressed, .err_on = ERR_ON(quartz), .warn_on = WARN_ON(quartz), }; diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 8b9d42ea3f0..9cc54f8e146 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -1643,7 +1643,7 @@ static HRESULT media_source_create(struct object_context *context, IMFMediaSourc if (FAILED(hr = MFAllocateWorkQueue(&object->async_commands_queue))) goto fail;
- if (!(parser = wg_parser_create(WG_PARSER_DECODEBIN))) + if (!(parser = wg_parser_create(WG_PARSER_DECODEBIN, FALSE))) { hr = E_OUTOFMEMORY; goto fail; diff --git a/dlls/winegstreamer/quartz_parser.c b/dlls/winegstreamer/quartz_parser.c index f9d8d79b259..130f1c2a1d2 100644 --- a/dlls/winegstreamer/quartz_parser.c +++ b/dlls/winegstreamer/quartz_parser.c @@ -1444,14 +1444,14 @@ static HRESULT decodebin_parser_source_get_media_type(struct parser_source *pin, return VFW_S_NO_MORE_ITEMS; }
-static HRESULT parser_create(enum wg_parser_type type, struct parser **parser) +static HRESULT parser_create(enum wg_parser_type type, BOOL output_compressed, struct parser **parser) { struct parser *object;
if (!(object = calloc(1, sizeof(*object)))) return E_OUTOFMEMORY;
- if (!(object->wg_parser = wg_parser_create(type))) + if (!(object->wg_parser = wg_parser_create(type, output_compressed))) { free(object); return E_OUTOFMEMORY; @@ -1471,7 +1471,7 @@ HRESULT decodebin_parser_create(IUnknown *outer, IUnknown **out) struct parser *object; HRESULT hr;
- if (FAILED(hr = parser_create(WG_PARSER_DECODEBIN, &object))) + if (FAILED(hr = parser_create(WG_PARSER_DECODEBIN, FALSE, &object))) return hr;
strmbase_filter_init(&object->filter, outer, &CLSID_decodebin_parser, &filter_ops); @@ -2000,7 +2000,7 @@ HRESULT wave_parser_create(IUnknown *outer, IUnknown **out) struct parser *object; HRESULT hr;
- if (FAILED(hr = parser_create(WG_PARSER_WAVPARSE, &object))) + if (FAILED(hr = parser_create(WG_PARSER_WAVPARSE, FALSE, &object))) return hr;
strmbase_filter_init(&object->filter, outer, &CLSID_WAVEParser, &filter_ops); @@ -2078,7 +2078,7 @@ HRESULT avi_splitter_create(IUnknown *outer, IUnknown **out) struct parser *object; HRESULT hr;
- if (FAILED(hr = parser_create(WG_PARSER_AVIDEMUX, &object))) + if (FAILED(hr = parser_create(WG_PARSER_AVIDEMUX, FALSE, &object))) return hr;
strmbase_filter_init(&object->filter, outer, &CLSID_AviSplitter, &filter_ops); @@ -2175,7 +2175,7 @@ HRESULT mpeg_splitter_create(IUnknown *outer, IUnknown **out) struct parser *object; HRESULT hr;
- if (FAILED(hr = parser_create(WG_PARSER_MPEGAUDIOPARSE, &object))) + if (FAILED(hr = parser_create(WG_PARSER_MPEGAUDIOPARSE, FALSE, &object))) return hr;
strmbase_filter_init(&object->filter, outer, &CLSID_MPEG1Splitter, &mpeg_splitter_ops); diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index a3131e9f789..fc490d4c6e0 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -215,6 +215,7 @@ struct wg_parser_create_params { wg_parser_t parser; wg_parser_type type; + UINT8 output_compressed; UINT8 err_on; UINT8 warn_on; }; diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 299eea09c90..43220d1f86f 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -78,6 +78,7 @@ struct wg_parser pthread_mutex_t mutex;
pthread_cond_t init_cond; + bool output_compressed; bool no_more_pads, has_duration, error; bool err_on, warn_on;
@@ -979,7 +980,7 @@ static void pad_added_cb(GstElement *element, GstPad *pad, gpointer user) gst_caps_unref(caps);
/* For compressed stream, create an extra decodebin to decode it. */ - if (format_is_compressed(&stream->codec_format)) + if (!parser->output_compressed && format_is_compressed(&stream->codec_format)) { if (!stream_decodebin_create(stream)) { @@ -1879,6 +1880,7 @@ static NTSTATUS wg_parser_create(void *args) pthread_cond_init(&parser->read_cond, NULL); pthread_cond_init(&parser->read_done_cond, NULL); parser->init_gst = init_funcs[params->type]; + parser->output_compressed = params->output_compressed; parser->err_on = params->err_on; parser->warn_on = params->warn_on; GST_DEBUG("Created winegstreamer parser %p.", parser); diff --git a/dlls/winegstreamer/wm_reader.c b/dlls/winegstreamer/wm_reader.c index 6440f8dbb60..c8b98c7c769 100644 --- a/dlls/winegstreamer/wm_reader.c +++ b/dlls/winegstreamer/wm_reader.c @@ -1459,7 +1459,7 @@ static HRESULT init_stream(struct wm_reader *reader, QWORD file_size) HRESULT hr; WORD i;
- if (!(wg_parser = wg_parser_create(WG_PARSER_DECODEBIN))) + if (!(wg_parser = wg_parser_create(WG_PARSER_DECODEBIN, FALSE))) return E_OUTOFMEMORY;
reader->wg_parser = wg_parser;
From: Alfred Agrell floating@muncher.se
--- dlls/winegstreamer/quartz_parser.c | 2 +- dlls/winegstreamer/unixlib.h | 1 - dlls/winegstreamer/wg_parser.c | 26 -------------------------- 3 files changed, 1 insertion(+), 28 deletions(-)
diff --git a/dlls/winegstreamer/quartz_parser.c b/dlls/winegstreamer/quartz_parser.c index 130f1c2a1d2..cb18c951013 100644 --- a/dlls/winegstreamer/quartz_parser.c +++ b/dlls/winegstreamer/quartz_parser.c @@ -2175,7 +2175,7 @@ HRESULT mpeg_splitter_create(IUnknown *outer, IUnknown **out) struct parser *object; HRESULT hr;
- if (FAILED(hr = parser_create(WG_PARSER_MPEGAUDIOPARSE, FALSE, &object))) + if (FAILED(hr = parser_create(WG_PARSER_DECODEBIN, TRUE, &object))) return hr;
strmbase_filter_init(&object->filter, outer, &CLSID_MPEG1Splitter, &mpeg_splitter_ops); diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index fc490d4c6e0..5ae5a7776a8 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -202,7 +202,6 @@ enum wg_parser_type { WG_PARSER_DECODEBIN, WG_PARSER_AVIDEMUX, - WG_PARSER_MPEGAUDIOPARSE, WG_PARSER_WAVPARSE, };
diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 43220d1f86f..7679ec7925e 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1809,31 +1809,6 @@ static BOOL avi_parser_init_gst(struct wg_parser *parser) return TRUE; }
-static BOOL mpeg_audio_parser_init_gst(struct wg_parser *parser) -{ - struct wg_parser_stream *stream; - GstElement *element; - - if (!(element = create_element("mpegaudioparse", "good"))) - return FALSE; - - gst_bin_add(GST_BIN(parser->container), element); - - if (!link_src_to_element(parser->my_src, element)) - return FALSE; - - if (!(stream = create_stream(parser))) - return FALSE; - - if (!link_element_to_sink(element, stream->my_sink)) - return FALSE; - gst_pad_set_active(stream->my_sink, 1); - - parser->no_more_pads = true; - - return TRUE; -} - static BOOL wave_parser_init_gst(struct wg_parser *parser) { struct wg_parser_stream *stream; @@ -1865,7 +1840,6 @@ static NTSTATUS wg_parser_create(void *args) { [WG_PARSER_DECODEBIN] = decodebin_parser_init_gst, [WG_PARSER_AVIDEMUX] = avi_parser_init_gst, - [WG_PARSER_MPEGAUDIOPARSE] = mpeg_audio_parser_init_gst, [WG_PARSER_WAVPARSE] = wave_parser_init_gst, };
From: Alfred Agrell floating@muncher.se
--- dlls/winegstreamer/mfplat.c | 1 + dlls/winegstreamer/quartz_parser.c | 140 +++++++++++++++++++++-------- dlls/winegstreamer/unixlib.h | 6 ++ dlls/winegstreamer/wg_format.c | 55 ++++++++++++ dlls/winegstreamer/wg_transform.c | 2 + dlls/winegstreamer/wm_reader.c | 4 + 6 files changed, 173 insertions(+), 35 deletions(-)
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index c7906a8bc1f..0e1d489d8e5 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -565,6 +565,7 @@ IMFMediaType *mf_media_type_from_wg_format(const struct wg_format *format) case WG_MAJOR_TYPE_VIDEO_H264: case WG_MAJOR_TYPE_VIDEO_WMV: case WG_MAJOR_TYPE_VIDEO_INDEO: + case WG_MAJOR_TYPE_VIDEO_MPEG1: FIXME("Format %u not implemented!\n", format->major_type); /* fallthrough */ case WG_MAJOR_TYPE_UNKNOWN: diff --git a/dlls/winegstreamer/quartz_parser.c b/dlls/winegstreamer/quartz_parser.c index cb18c951013..d5af7b29bae 100644 --- a/dlls/winegstreamer/quartz_parser.c +++ b/dlls/winegstreamer/quartz_parser.c @@ -280,46 +280,50 @@ static bool amt_from_wg_format_audio_mpeg1(AM_MEDIA_TYPE *mt, const struct wg_fo
#define ALIGN(n, alignment) (((n) + (alignment) - 1) & ~((alignment) - 1))
+unsigned int wg_format_get_bytes_for_uncompressed(wg_video_format format, unsigned int width, unsigned int height) +{ + switch (format) + { + case WG_VIDEO_FORMAT_BGRA: + case WG_VIDEO_FORMAT_BGRx: + case WG_VIDEO_FORMAT_AYUV: + return width * height * 4; + + case WG_VIDEO_FORMAT_BGR: + return ALIGN(width * 3, 4) * height; + + case WG_VIDEO_FORMAT_RGB15: + case WG_VIDEO_FORMAT_RGB16: + case WG_VIDEO_FORMAT_UYVY: + case WG_VIDEO_FORMAT_YUY2: + case WG_VIDEO_FORMAT_YVYU: + return ALIGN(width * 2, 4) * height; + + case WG_VIDEO_FORMAT_I420: + case WG_VIDEO_FORMAT_YV12: + return ALIGN(width, 4) * ALIGN(height, 2) /* Y plane */ + + 2 * ALIGN((width + 1) / 2, 4) * ((height + 1) / 2); /* U and V planes */ + + case WG_VIDEO_FORMAT_NV12: + return ALIGN(width, 4) * ALIGN(height, 2) /* Y plane */ + + ALIGN(width, 4) * ((height + 1) / 2); /* U/V plane */ + + case WG_VIDEO_FORMAT_UNKNOWN: + FIXME("Cannot guess maximum sample size for unknown video format.\n"); + return 0; + } + + assert(0); + return 0; +} + unsigned int wg_format_get_max_size(const struct wg_format *format) { switch (format->major_type) { case WG_MAJOR_TYPE_VIDEO: - { - unsigned int width = format->u.video.width, height = abs(format->u.video.height); - - switch (format->u.video.format) - { - case WG_VIDEO_FORMAT_BGRA: - case WG_VIDEO_FORMAT_BGRx: - case WG_VIDEO_FORMAT_AYUV: - return width * height * 4; - - case WG_VIDEO_FORMAT_BGR: - return ALIGN(width * 3, 4) * height; - - case WG_VIDEO_FORMAT_RGB15: - case WG_VIDEO_FORMAT_RGB16: - case WG_VIDEO_FORMAT_UYVY: - case WG_VIDEO_FORMAT_YUY2: - case WG_VIDEO_FORMAT_YVYU: - return ALIGN(width * 2, 4) * height; - - case WG_VIDEO_FORMAT_I420: - case WG_VIDEO_FORMAT_YV12: - return ALIGN(width, 4) * ALIGN(height, 2) /* Y plane */ - + 2 * ALIGN((width + 1) / 2, 4) * ((height + 1) / 2); /* U and V planes */ - - case WG_VIDEO_FORMAT_NV12: - return ALIGN(width, 4) * ALIGN(height, 2) /* Y plane */ - + ALIGN(width, 4) * ((height + 1) / 2); /* U/V plane */ - - case WG_VIDEO_FORMAT_UNKNOWN: - FIXME("Cannot guess maximum sample size for unknown video format.\n"); - return 0; - } - break; - } + return wg_format_get_bytes_for_uncompressed(format->u.video.format, + format->u.video.width, abs(format->u.video.height));
case WG_MAJOR_TYPE_VIDEO_CINEPAK: /* Both ffmpeg's encoder and a Cinepak file seen in the wild report @@ -327,6 +331,13 @@ unsigned int wg_format_get_max_size(const struct wg_format *format) * but as long as every sample fits into our allocator, we're fine. */ return format->u.video_cinepak.width * format->u.video_cinepak.height * 3;
+ case WG_MAJOR_TYPE_VIDEO_MPEG1: + /* Estimated max size of a compressed video frame. + * There's no way to no way to know the real upper bound, + * so let's just use the decompressed size and hope it works. */ + return wg_format_get_bytes_for_uncompressed(WG_VIDEO_FORMAT_YV12, + format->u.video_mpeg1.width, format->u.video_mpeg1.height); + case WG_MAJOR_TYPE_AUDIO: { unsigned int rate = format->u.audio.rate, channels = format->u.audio.channels; @@ -595,6 +606,36 @@ static bool amt_from_wg_format_video_wmv(AM_MEDIA_TYPE *mt, const struct wg_form return true; }
+static bool amt_from_wg_format_video_mpeg1(AM_MEDIA_TYPE *mt, const struct wg_format *format) +{ + MPEG1VIDEOINFO *video_format; + uint32_t frame_time; + + if (!(video_format = CoTaskMemAlloc(sizeof(*video_format)))) + return false; + + mt->majortype = MEDIATYPE_Video; + mt->subtype = MEDIASUBTYPE_MPEG1Payload; + mt->bTemporalCompression = TRUE; + mt->lSampleSize = 1; + mt->formattype = FORMAT_MPEGVideo; + mt->cbFormat = sizeof(MPEG1VIDEOINFO); + mt->pbFormat = (BYTE *)video_format; + + memset(video_format, 0, sizeof(*video_format)); + if ((frame_time = MulDiv(10000000, format->u.video_mpeg1.fps_d, format->u.video_mpeg1.fps_n)) != -1) + video_format->hdr.AvgTimePerFrame = frame_time; + video_format->hdr.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + video_format->hdr.bmiHeader.biWidth = format->u.video_mpeg1.width; + video_format->hdr.bmiHeader.biHeight = format->u.video_mpeg1.height; + video_format->hdr.bmiHeader.biPlanes = 1; + video_format->hdr.bmiHeader.biBitCount = 12; + video_format->hdr.bmiHeader.biCompression = mt->subtype.Data1; + video_format->hdr.bmiHeader.biSizeImage = wg_format_get_max_size(format); + + return true; +} + bool amt_from_wg_format(AM_MEDIA_TYPE *mt, const struct wg_format *format, bool wm) { memset(mt, 0, sizeof(*mt)); @@ -624,6 +665,9 @@ bool amt_from_wg_format(AM_MEDIA_TYPE *mt, const struct wg_format *format, bool
case WG_MAJOR_TYPE_VIDEO_WMV: return amt_from_wg_format_video_wmv(mt, format); + + case WG_MAJOR_TYPE_VIDEO_MPEG1: + return amt_from_wg_format_video_mpeg1(mt, format); }
assert(0); @@ -838,6 +882,30 @@ static bool amt_to_wg_format_video_wmv(const AM_MEDIA_TYPE *mt, struct wg_format return true; }
+static bool amt_to_wg_format_video_mpeg1(const AM_MEDIA_TYPE *mt, struct wg_format *format) +{ + const MPEG1VIDEOINFO *video_format = (const MPEG1VIDEOINFO *)mt->pbFormat; + + if (!IsEqualGUID(&mt->formattype, &FORMAT_MPEGVideo)) + { + FIXME("Unknown format type %s.\n", debugstr_guid(&mt->formattype)); + return false; + } + if (mt->cbFormat < sizeof(VIDEOINFOHEADER) || !mt->pbFormat) + { + ERR("Unexpected format size %lu.\n", mt->cbFormat); + return false; + } + + format->major_type = WG_MAJOR_TYPE_VIDEO_MPEG1; + format->u.video_mpeg1.width = video_format->hdr.bmiHeader.biWidth; + format->u.video_mpeg1.height = video_format->hdr.bmiHeader.biHeight; + format->u.video_mpeg1.fps_n = 10000000; + format->u.video_mpeg1.fps_d = video_format->hdr.AvgTimePerFrame; + + return true; +} + bool amt_to_wg_format(const AM_MEDIA_TYPE *mt, struct wg_format *format) { memset(format, 0, sizeof(*format)); @@ -854,6 +922,8 @@ bool amt_to_wg_format(const AM_MEDIA_TYPE *mt, struct wg_format *format) || IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_WMV3) || IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_VC1S)) return amt_to_wg_format_video_wmv(mt, format); + if (IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_MPEG1Payload)) + return amt_to_wg_format_video_mpeg1(mt, format); return amt_to_wg_format_video(mt, format); } if (IsEqualGUID(&mt->majortype, &MEDIATYPE_Audio)) diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 5ae5a7776a8..3ffd6bf927a 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -42,6 +42,7 @@ enum wg_major_type WG_MAJOR_TYPE_VIDEO_H264, WG_MAJOR_TYPE_VIDEO_WMV, WG_MAJOR_TYPE_VIDEO_INDEO, + WG_MAJOR_TYPE_VIDEO_MPEG1, };
typedef UINT32 wg_audio_format; @@ -163,6 +164,11 @@ struct wg_format uint32_t fps_n, fps_d; uint32_t version; } video_indeo; + struct + { + int32_t width, height; + uint32_t fps_n, fps_d; + } video_mpeg1; } u; };
diff --git a/dlls/winegstreamer/wg_format.c b/dlls/winegstreamer/wg_format.c index 2353839bbc4..370c3722f9e 100644 --- a/dlls/winegstreamer/wg_format.c +++ b/dlls/winegstreamer/wg_format.c @@ -282,10 +282,39 @@ static void wg_format_from_caps_video_wmv(struct wg_format *format, const GstCap format->u.video_wmv.fps_d = fps_d; }
+static void wg_format_from_caps_video_mpeg1(struct wg_format *format, const GstCaps *caps) +{ + const GstStructure *structure = gst_caps_get_structure(caps, 0); + gint width, height, fps_n, fps_d; + + if (!gst_structure_get_int(structure, "width", &width)) + { + GST_WARNING("Missing "width" value in %" GST_PTR_FORMAT ".", caps); + return; + } + if (!gst_structure_get_int(structure, "height", &height)) + { + GST_WARNING("Missing "height" value in %" GST_PTR_FORMAT ".", caps); + return; + } + if (!gst_structure_get_fraction(structure, "framerate", &fps_n, &fps_d)) + { + fps_n = 0; + fps_d = 1; + } + + format->major_type = WG_MAJOR_TYPE_VIDEO_MPEG1; + format->u.video_mpeg1.width = width; + format->u.video_mpeg1.height = height; + format->u.video_mpeg1.fps_n = fps_n; + format->u.video_mpeg1.fps_d = fps_d; +} + void wg_format_from_caps(struct wg_format *format, const GstCaps *caps) { const GstStructure *structure = gst_caps_get_structure(caps, 0); const char *name = gst_structure_get_name(structure); + gboolean parsed;
memset(format, 0, sizeof(*format));
@@ -315,6 +344,10 @@ void wg_format_from_caps(struct wg_format *format, const GstCaps *caps) { wg_format_from_caps_video_wmv(format, caps); } + else if (!strcmp(name, "video/mpeg") && gst_structure_get_boolean(structure, "parsed", &parsed) && parsed) + { + wg_format_from_caps_video_mpeg1(format, caps); + } else { gchar *str = gst_caps_to_string(caps); @@ -699,6 +732,25 @@ static GstCaps *wg_format_to_caps_video_indeo(const struct wg_format *format) return caps; }
+static GstCaps *wg_format_to_caps_video_mpeg1(const struct wg_format *format) +{ + GstCaps *caps; + + if (!(caps = gst_caps_new_empty_simple("video/mpeg"))) + return NULL; + + gst_caps_set_simple(caps, "mpegversion", G_TYPE_INT, 1, NULL); + gst_caps_set_simple(caps, "systemstream", G_TYPE_BOOLEAN, FALSE, NULL); + gst_caps_set_simple(caps, "parsed", G_TYPE_BOOLEAN, TRUE, NULL); + if (format->u.video_mpeg1.width) + gst_caps_set_simple(caps, "width", G_TYPE_INT, format->u.video_mpeg1.width, NULL); + if (format->u.video_mpeg1.height) + gst_caps_set_simple(caps, "height", G_TYPE_INT, format->u.video_mpeg1.height, NULL); + if (format->u.video_mpeg1.fps_d || format->u.video_cinepak.fps_n) + gst_caps_set_simple(caps, "framerate", GST_TYPE_FRACTION, format->u.video_mpeg1.fps_n, format->u.video_mpeg1.fps_d, NULL); + return caps; +} + GstCaps *wg_format_to_caps(const struct wg_format *format) { switch (format->major_type) @@ -723,6 +775,8 @@ GstCaps *wg_format_to_caps(const struct wg_format *format) return wg_format_to_caps_video_wmv(format); case WG_MAJOR_TYPE_VIDEO_INDEO: return wg_format_to_caps_video_indeo(format); + case WG_MAJOR_TYPE_VIDEO_MPEG1: + return wg_format_to_caps_video_mpeg1(format); } assert(0); return NULL; @@ -741,6 +795,7 @@ bool wg_format_compare(const struct wg_format *a, const struct wg_format *b) case WG_MAJOR_TYPE_VIDEO_H264: case WG_MAJOR_TYPE_VIDEO_WMV: case WG_MAJOR_TYPE_VIDEO_INDEO: + case WG_MAJOR_TYPE_VIDEO_MPEG1: GST_FIXME("Format %u not implemented!", a->major_type); /* fallthrough */ case WG_MAJOR_TYPE_UNKNOWN: diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index e2b14527a20..5a2dae410bb 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -372,6 +372,7 @@ NTSTATUS wg_transform_create(void *args) case WG_MAJOR_TYPE_AUDIO: case WG_MAJOR_TYPE_VIDEO: break; + case WG_MAJOR_TYPE_VIDEO_MPEG1: case WG_MAJOR_TYPE_UNKNOWN: GST_FIXME("Format %u not implemented!", input_format.major_type); gst_caps_unref(raw_caps); @@ -425,6 +426,7 @@ NTSTATUS wg_transform_create(void *args) case WG_MAJOR_TYPE_VIDEO_H264: case WG_MAJOR_TYPE_VIDEO_INDEO: case WG_MAJOR_TYPE_VIDEO_WMV: + case WG_MAJOR_TYPE_VIDEO_MPEG1: GST_FIXME("Format %u not implemented!", output_format.major_type); goto out; } diff --git a/dlls/winegstreamer/wm_reader.c b/dlls/winegstreamer/wm_reader.c index c8b98c7c769..882b6df1bbb 100644 --- a/dlls/winegstreamer/wm_reader.c +++ b/dlls/winegstreamer/wm_reader.c @@ -1589,6 +1589,8 @@ static const char *get_major_type_string(enum wg_major_type type) return "wmv"; case WG_MAJOR_TYPE_VIDEO_INDEO: return "indeo"; + case WG_MAJOR_TYPE_VIDEO_MPEG1: + return "mpeg1-video"; case WG_MAJOR_TYPE_UNKNOWN: return "unknown"; } @@ -1948,6 +1950,7 @@ static HRESULT WINAPI reader_GetOutputFormat(IWMSyncReader2 *iface, case WG_MAJOR_TYPE_VIDEO_H264: case WG_MAJOR_TYPE_VIDEO_WMV: case WG_MAJOR_TYPE_VIDEO_INDEO: + case WG_MAJOR_TYPE_VIDEO_MPEG1: FIXME("Format %u not implemented!\n", format.major_type); break; case WG_MAJOR_TYPE_UNKNOWN: @@ -1990,6 +1993,7 @@ static HRESULT WINAPI reader_GetOutputFormatCount(IWMSyncReader2 *iface, DWORD o case WG_MAJOR_TYPE_VIDEO_H264: case WG_MAJOR_TYPE_VIDEO_WMV: case WG_MAJOR_TYPE_VIDEO_INDEO: + case WG_MAJOR_TYPE_VIDEO_MPEG1: FIXME("Format %u not implemented!\n", format.major_type); /* fallthrough */ case WG_MAJOR_TYPE_AUDIO:
From: Alfred Agrell floating@muncher.se
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=9127 --- dlls/winegstreamer/quartz_parser.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-)
diff --git a/dlls/winegstreamer/quartz_parser.c b/dlls/winegstreamer/quartz_parser.c index d5af7b29bae..bc2187124ea 100644 --- a/dlls/winegstreamer/quartz_parser.c +++ b/dlls/winegstreamer/quartz_parser.c @@ -2166,10 +2166,10 @@ static HRESULT mpeg_splitter_sink_query_accept(struct strmbase_pin *iface, const { if (!IsEqualGUID(&mt->majortype, &MEDIATYPE_Stream)) return S_FALSE; - if (IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_MPEG1Audio)) + if (IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_MPEG1Audio) + || IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_MPEG1System)) return S_OK; if (IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_MPEG1Video) - || IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_MPEG1System) || IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_MPEG1VideoCD)) FIXME("Unsupported subtype %s.\n", wine_dbgstr_guid(&mt->subtype)); return S_FALSE; @@ -2184,8 +2184,28 @@ static const struct strmbase_sink_ops mpeg_splitter_sink_ops =
static BOOL mpeg_splitter_filter_init_gst(struct parser *filter) { - if (!create_pin(filter, wg_parser_get_stream(filter->wg_parser, 0), L"Audio")) - return FALSE; + wg_parser_t parser = filter->wg_parser; + unsigned int i, stream_count; + wg_parser_stream_t stream; + struct wg_format fmt; + + stream_count = wg_parser_get_stream_count(parser); + for (i = 0; i < stream_count; ++i) + { + stream = wg_parser_get_stream(parser, i); + wg_parser_stream_get_preferred_format(stream, &fmt); + if (fmt.major_type == WG_MAJOR_TYPE_VIDEO_MPEG1) + { + if (!create_pin(filter, wg_parser_get_stream(parser, i), L"Video")) + return FALSE; + } + else if (fmt.major_type == WG_MAJOR_TYPE_AUDIO_MPEG1) + { + if (!create_pin(filter, wg_parser_get_stream(parser, i), L"Audio")) + return FALSE; + } + else FIXME("unexpected format %u\n", fmt.major_type); + }
return TRUE; }
From: Alfred Agrell floating@muncher.se
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=9127 --- dlls/winegstreamer/gst_private.h | 2 + dlls/winegstreamer/main.c | 38 +++++ dlls/winegstreamer/quartz_parser.c | 6 + dlls/winegstreamer/quartz_transform.c | 141 +++++++++++++++++++ dlls/winegstreamer/wg_transform.c | 2 +- dlls/winegstreamer/winegstreamer_classes.idl | 7 + 6 files changed, 195 insertions(+), 1 deletion(-)
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 99e0044e77d..ef7c316d6e3 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -113,11 +113,13 @@ HRESULT wg_transform_flush(wg_transform_t transform); HRESULT wg_muxer_create(const char *format, wg_muxer_t *muxer); void wg_muxer_destroy(wg_muxer_t muxer);
+unsigned int wg_format_get_bytes_for_uncompressed(wg_video_format format, unsigned int width, unsigned int height); unsigned int wg_format_get_max_size(const struct wg_format *format);
HRESULT avi_splitter_create(IUnknown *outer, IUnknown **out); HRESULT decodebin_parser_create(IUnknown *outer, IUnknown **out); HRESULT mpeg_audio_codec_create(IUnknown *outer, IUnknown **out); +HRESULT mpeg_video_codec_create(IUnknown *outer, IUnknown **out); HRESULT mpeg_layer3_decoder_create(IUnknown *outer, IUnknown **out); HRESULT mpeg_splitter_create(IUnknown *outer, IUnknown **out); HRESULT wave_parser_create(IUnknown *outer, IUnknown **out); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index a5f77ce8d56..54611afc3fb 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -632,6 +632,7 @@ static const IClassFactoryVtbl class_factory_vtbl = static struct class_factory avi_splitter_cf = {{&class_factory_vtbl}, avi_splitter_create}; static struct class_factory decodebin_parser_cf = {{&class_factory_vtbl}, decodebin_parser_create}; static struct class_factory mpeg_audio_codec_cf = {{&class_factory_vtbl}, mpeg_audio_codec_create}; +static struct class_factory mpeg_video_codec_cf = {{&class_factory_vtbl}, mpeg_video_codec_create}; static struct class_factory mpeg_layer3_decoder_cf = {{&class_factory_vtbl}, mpeg_layer3_decoder_create}; static struct class_factory mpeg_splitter_cf = {{&class_factory_vtbl}, mpeg_splitter_create}; static struct class_factory wave_parser_cf = {{&class_factory_vtbl}, wave_parser_create}; @@ -661,6 +662,8 @@ HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID iid, void **out) factory = &decodebin_parser_cf; else if (IsEqualGUID(clsid, &CLSID_CMpegAudioCodec)) factory = &mpeg_audio_codec_cf; + else if (IsEqualGUID(clsid, &CLSID_CMpegVideoCodec)) + factory = &mpeg_video_codec_cf; else if (IsEqualGUID(clsid, &CLSID_mpeg_layer3_decoder)) factory = &mpeg_layer3_decoder_cf; else if (IsEqualGUID(clsid, &CLSID_MPEG1Splitter)) @@ -774,6 +777,38 @@ static const REGFILTER2 reg_mpeg_audio_codec = .u.s2.rgPins2 = reg_mpeg_audio_codec_pins, };
+static const REGPINTYPES reg_mpeg_video_codec_sink_mts[2] = +{ + {&MEDIATYPE_Video, &MEDIASUBTYPE_MPEG1Packet}, + {&MEDIATYPE_Video, &MEDIASUBTYPE_MPEG1Payload}, +}; + +static const REGPINTYPES reg_mpeg_video_codec_source_mts[1] = +{ + {&MEDIATYPE_Video, &GUID_NULL}, +}; + +static const REGFILTERPINS2 reg_mpeg_video_codec_pins[2] = +{ + { + .nMediaTypes = 2, + .lpMediaType = reg_mpeg_video_codec_sink_mts, + }, + { + .dwFlags = REG_PINFLAG_B_OUTPUT, + .nMediaTypes = 1, + .lpMediaType = reg_mpeg_video_codec_source_mts, + }, +}; + +static const REGFILTER2 reg_mpeg_video_codec = +{ + .dwVersion = 2, + .dwMerit = 0x40000001, + .u.s2.cPins2 = 2, + .u.s2.rgPins2 = reg_mpeg_video_codec_pins, +}; + static const REGPINTYPES reg_mpeg_layer3_decoder_sink_mts[1] = { {&MEDIATYPE_Audio, &MEDIASUBTYPE_MP3}, @@ -1009,6 +1044,8 @@ HRESULT WINAPI DllRegisterServer(void) L"GStreamer splitter filter", NULL, NULL, NULL, ®_decodebin_parser); IFilterMapper2_RegisterFilter(mapper, &CLSID_CMpegAudioCodec, L"MPEG Audio Decoder", NULL, NULL, NULL, ®_mpeg_audio_codec); + IFilterMapper2_RegisterFilter(mapper, &CLSID_CMpegVideoCodec, + L"MPEG Video Decoder", NULL, NULL, NULL, ®_mpeg_video_codec); IFilterMapper2_RegisterFilter(mapper, &CLSID_mpeg_layer3_decoder, L"MPEG Layer-3 Decoder", NULL, NULL, NULL, ®_mpeg_layer3_decoder); IFilterMapper2_RegisterFilter(mapper, &CLSID_MPEG1Splitter, @@ -1050,6 +1087,7 @@ HRESULT WINAPI DllUnregisterServer(void) IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &CLSID_AviSplitter); IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &CLSID_decodebin_parser); IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &CLSID_CMpegAudioCodec); + IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &CLSID_CMpegVideoCodec); IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &CLSID_mpeg_layer3_decoder); IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &CLSID_MPEG1Splitter); IFilterMapper2_UnregisterFilter(mapper, NULL, NULL, &CLSID_WAVEParser); diff --git a/dlls/winegstreamer/quartz_parser.c b/dlls/winegstreamer/quartz_parser.c index bc2187124ea..db171ba0747 100644 --- a/dlls/winegstreamer/quartz_parser.c +++ b/dlls/winegstreamer/quartz_parser.c @@ -1894,6 +1894,12 @@ static HRESULT WINAPI GSTOutPin_DecideBufferSize(struct strmbase_source *iface, VIDEOINFOHEADER *format = (VIDEOINFOHEADER *)pin->pin.pin.mt.pbFormat; buffer_size = format->bmiHeader.biSizeImage; } + else if (IsEqualGUID(&pin->pin.pin.mt.formattype, &FORMAT_MPEGVideo)) + { + MPEG1VIDEOINFO *format = (MPEG1VIDEOINFO *)pin->pin.pin.mt.pbFormat; + buffer_size = format->hdr.bmiHeader.biSizeImage; + buffer_count = 8; + } else if (IsEqualGUID(&pin->pin.pin.mt.formattype, &FORMAT_WaveFormatEx) && (IsEqualGUID(&pin->pin.pin.mt.subtype, &MEDIASUBTYPE_PCM) || IsEqualGUID(&pin->pin.pin.mt.subtype, &MEDIASUBTYPE_IEEE_FLOAT))) diff --git a/dlls/winegstreamer/quartz_transform.c b/dlls/winegstreamer/quartz_transform.c index 1dce20d9cff..fee275bb1ee 100644 --- a/dlls/winegstreamer/quartz_transform.c +++ b/dlls/winegstreamer/quartz_transform.c @@ -738,6 +738,147 @@ HRESULT mpeg_audio_codec_create(IUnknown *outer, IUnknown **out) return hr; }
+static HRESULT mpeg_video_codec_sink_query_accept(struct transform *filter, const AM_MEDIA_TYPE *mt) +{ + if (!IsEqualGUID(&mt->majortype, &MEDIATYPE_Video) + || !IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_MPEG1Payload) + || !IsEqualGUID(&mt->formattype, &FORMAT_MPEGVideo) + || mt->cbFormat < sizeof(MPEG1VIDEOINFO)) + return S_FALSE; + + return S_OK; +} + +static HRESULT mpeg_video_codec_source_query_accept(struct transform *filter, const AM_MEDIA_TYPE *mt) +{ + if (!filter->sink.pin.peer) + return S_FALSE; + + if (!IsEqualGUID(&mt->majortype, &MEDIATYPE_Video) + || !IsEqualGUID(&mt->formattype, &FORMAT_VideoInfo) + || mt->cbFormat < sizeof(VIDEOINFOHEADER)) + return S_FALSE; + + if (!IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_YV12) + /* missing: MEDIASUBTYPE_Y41P, not supported by GStreamer */ + && !IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_YUY2) + && !IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_UYVY) + && !IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_RGB24) + && !IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_RGB32) + && !IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_RGB565) + && !IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_RGB555) + /* missing: MEDIASUBTYPE_RGB8, not supported by GStreamer */) + return S_FALSE; + + return S_OK; +} + +static HRESULT mpeg_video_codec_source_get_media_type(struct transform *filter, unsigned int index, AM_MEDIA_TYPE *mt) +{ + static const enum wg_video_format formats[] = { + WG_VIDEO_FORMAT_YV12, + WG_VIDEO_FORMAT_YUY2, + WG_VIDEO_FORMAT_UYVY, + WG_VIDEO_FORMAT_BGR, + WG_VIDEO_FORMAT_BGRx, + WG_VIDEO_FORMAT_RGB16, + WG_VIDEO_FORMAT_RGB15, + }; + + const MPEG1VIDEOINFO *input_format = (MPEG1VIDEOINFO*)filter->sink.pin.mt.pbFormat; + struct wg_format wg_format = {}; + VIDEOINFO *video_format; + + if (!filter->sink.pin.peer) + return VFW_S_NO_MORE_ITEMS; + + if (index >= ARRAY_SIZE(formats)) + return VFW_S_NO_MORE_ITEMS; + + input_format = (MPEG1VIDEOINFO*)filter->sink.pin.mt.pbFormat; + wg_format.major_type = WG_MAJOR_TYPE_VIDEO; + wg_format.u.video.format = formats[index]; + wg_format.u.video.width = input_format->hdr.bmiHeader.biWidth; + wg_format.u.video.height = input_format->hdr.bmiHeader.biHeight; + wg_format.u.video.fps_n = 10000000; + wg_format.u.video.fps_d = input_format->hdr.AvgTimePerFrame; + if (!amt_from_wg_format(mt, &wg_format, false)) + return E_OUTOFMEMORY; + + video_format = (VIDEOINFO*)mt->pbFormat; + video_format->bmiHeader.biHeight = abs(video_format->bmiHeader.biHeight); + SetRect(&video_format->rcSource, 0, 0, video_format->bmiHeader.biWidth, video_format->bmiHeader.biHeight); + + video_format->bmiHeader.biXPelsPerMeter = 2000; + video_format->bmiHeader.biYPelsPerMeter = 2000; + video_format->dwBitRate = MulDiv(video_format->bmiHeader.biSizeImage * 8, 10000000, video_format->AvgTimePerFrame); + mt->lSampleSize = video_format->bmiHeader.biSizeImage; + mt->bTemporalCompression = FALSE; + mt->bFixedSizeSamples = TRUE; + + return S_OK; +} + +static HRESULT mpeg_video_codec_source_decide_buffer_size(struct transform *filter, IMemAllocator *allocator, ALLOCATOR_PROPERTIES *props) +{ + VIDEOINFOHEADER *output_format = (VIDEOINFOHEADER *)filter->source.pin.mt.pbFormat; + ALLOCATOR_PROPERTIES ret_props; + + props->cBuffers = max(props->cBuffers, 1); + props->cbBuffer = max(props->cbBuffer, output_format->bmiHeader.biSizeImage); + props->cbAlign = max(props->cbAlign, 1); + + return IMemAllocator_SetProperties(allocator, props, &ret_props); +} + +static const struct transform_ops mpeg_video_codec_transform_ops = +{ + mpeg_video_codec_sink_query_accept, + mpeg_video_codec_source_query_accept, + mpeg_video_codec_source_get_media_type, + mpeg_video_codec_source_decide_buffer_size, +}; + +HRESULT mpeg_video_codec_create(IUnknown *outer, IUnknown **out) +{ + static const struct wg_format output_format = + { + .major_type = WG_MAJOR_TYPE_VIDEO, + .u.video = { + .format = WG_VIDEO_FORMAT_I420, + /* size doesn't matter, this one is only used to check if the GStreamer plugin exists */ + }, + }; + static const struct wg_format input_format = + { + .major_type = WG_MAJOR_TYPE_VIDEO_MPEG1, + .u.video_mpeg1 = {}, + }; + struct wg_transform_attrs attrs = {0}; + wg_transform_t transform; + struct transform *object; + HRESULT hr; + + transform = wg_transform_create(&input_format, &output_format, &attrs); + if (!transform) + { + ERR_(winediag)("GStreamer doesn't support MPEG-1 video decoding, please install appropriate plugins.\n"); + return E_FAIL; + } + wg_transform_destroy(transform); + + hr = transform_create(outer, &CLSID_CMpegVideoCodec, &mpeg_video_codec_transform_ops, &object); + if (FAILED(hr)) + return hr; + + wcscpy(object->sink.pin.name, L"Input"); + wcscpy(object->source.pin.name, L"Output"); + + TRACE("Created MPEG video decoder %p.\n", object); + *out = &object->filter.IUnknown_inner; + return hr; +} + static HRESULT mpeg_layer3_decoder_sink_query_accept(struct transform *filter, const AM_MEDIA_TYPE *mt) { const MPEGLAYER3WAVEFORMAT *format; diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 5a2dae410bb..1610f27b981 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -361,6 +361,7 @@ NTSTATUS wg_transform_create(void *args) case WG_MAJOR_TYPE_VIDEO_CINEPAK: case WG_MAJOR_TYPE_VIDEO_INDEO: case WG_MAJOR_TYPE_VIDEO_WMV: + case WG_MAJOR_TYPE_VIDEO_MPEG1: if (!(element = find_element(GST_ELEMENT_FACTORY_TYPE_DECODER, src_caps, raw_caps)) || !append_element(transform->container, element, &first, &last)) { @@ -372,7 +373,6 @@ NTSTATUS wg_transform_create(void *args) case WG_MAJOR_TYPE_AUDIO: case WG_MAJOR_TYPE_VIDEO: break; - case WG_MAJOR_TYPE_VIDEO_MPEG1: case WG_MAJOR_TYPE_UNKNOWN: GST_FIXME("Format %u not implemented!", input_format.major_type); gst_caps_unref(raw_caps); diff --git a/dlls/winegstreamer/winegstreamer_classes.idl b/dlls/winegstreamer/winegstreamer_classes.idl index 3e9b19c90e9..bb727ca8645 100644 --- a/dlls/winegstreamer/winegstreamer_classes.idl +++ b/dlls/winegstreamer/winegstreamer_classes.idl @@ -35,6 +35,13 @@ coclass AviSplitter {} ] coclass CMpegAudioCodec {}
+[ + helpstring("MPEG Video Decoder"), + threading(both), + uuid(feb50740-7bef-11ce-9bd9-0000e202599c) +] +coclass CMpegVideoCodec {} + [ helpstring("MPEG Layer-3 Decoder"), threading(both),
From: Alfred Agrell floating@muncher.se
--- dlls/winegstreamer/quartz_parser.c | 50 +++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 5 deletions(-)
diff --git a/dlls/winegstreamer/quartz_parser.c b/dlls/winegstreamer/quartz_parser.c index db171ba0747..ac4a5de5efa 100644 --- a/dlls/winegstreamer/quartz_parser.c +++ b/dlls/winegstreamer/quartz_parser.c @@ -1581,17 +1581,57 @@ static ULONG WINAPI stream_select_Release(IAMStreamSelect *iface)
static HRESULT WINAPI stream_select_Count(IAMStreamSelect *iface, DWORD *count) { - FIXME("iface %p, count %p, stub!\n", iface, count); - return E_NOTIMPL; + struct parser *filter = impl_from_IAMStreamSelect(iface); + TRACE("filter %p, count %p\n", filter, count); + if (filter->sink.pin.peer) + *count = wg_parser_get_stream_count(filter->wg_parser); + else + *count = 0; + return S_OK; }
static HRESULT WINAPI stream_select_Info(IAMStreamSelect *iface, LONG index, AM_MEDIA_TYPE **mt, DWORD *flags, LCID *lcid, DWORD *group, WCHAR **name, IUnknown **object, IUnknown **unknown) { - FIXME("iface %p, index %ld, mt %p, flags %p, lcid %p, group %p, name %p, object %p, unknown %p, stub!\n", - iface, index, mt, flags, lcid, group, name, object, unknown); - return E_NOTIMPL; + struct parser *filter = impl_from_IAMStreamSelect(iface); + wg_parser_stream_t stream = wg_parser_get_stream(filter->wg_parser, index); + + FIXME("filter %p, index %ld, mt %p, flags %p, lcid %p, group %p, name %p, object %p, unknown %p, semi-stub!\n", + filter, index, mt, flags, lcid, group, name, object, unknown); + + if (!filter->sink.pin.peer) + return VFW_E_NOT_CONNECTED; + if (index < 0 || index >= wg_parser_get_stream_count(filter->wg_parser)) + return S_FALSE; + + if (mt) + { + struct wg_format fmt; + wg_parser_stream_get_preferred_format(stream, &fmt); + if (!(*mt = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE)))) + return E_OUTOFMEMORY; + if (!amt_from_wg_format(*mt, &fmt, FALSE)) + { + CoTaskMemFree(*mt); + return E_OUTOFMEMORY; + } + } + + if (flags) /* todo */ + *flags = 0; + if (lcid) /* todo */ + *lcid = 0; + if (group) /* todo */ + *group = 0; + if (name) /* todo */ + *name = NULL; + if (object) /* todo */ + *object = NULL; + if (unknown) + *unknown = NULL; + + return S_OK; }
static HRESULT WINAPI stream_select_Enable(IAMStreamSelect *iface, LONG index, DWORD flags)
From: Alfred Agrell floating@muncher.se
--- dlls/winegstreamer/wg_parser.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 7679ec7925e..f8127168c2a 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -437,8 +437,11 @@ static NTSTATUS wg_parser_stream_seek(void *args) const struct wg_parser_stream_seek_params *params = args; DWORD start_flags = params->start_flags; DWORD stop_flags = params->stop_flags; + const struct wg_parser_stream *stream; GstSeekFlags flags = 0;
+ stream = get_stream(params->stream); + if (start_flags & AM_SEEKING_SeekToKeyFrame) flags |= GST_SEEK_FLAG_KEY_UNIT; if (start_flags & AM_SEEKING_Segment) @@ -451,8 +454,9 @@ static NTSTATUS wg_parser_stream_seek(void *args) if ((stop_flags & AM_SEEKING_PositioningBitsMask) == AM_SEEKING_NoPositioning) stop_type = GST_SEEK_TYPE_NONE;
- if (!push_event(get_stream(params->stream)->my_sink, gst_event_new_seek(params->rate, GST_FORMAT_TIME, - flags, start_type, params->start_pos * 100, stop_type, params->stop_pos * 100))) + if (!push_event(stream->my_sink, gst_event_new_seek(params->rate, GST_FORMAT_TIME, + flags, start_type, params->start_pos * 100, stop_type, + params->stop_pos == stream->duration ? -1 : params->stop_pos * 100))) GST_ERROR("Failed to seek.\n");
return S_OK;
From: Alfred Agrell floating@muncher.se
--- dlls/winegstreamer/wg_format.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/winegstreamer/wg_format.c b/dlls/winegstreamer/wg_format.c index 370c3722f9e..96b4e4b76e7 100644 --- a/dlls/winegstreamer/wg_format.c +++ b/dlls/winegstreamer/wg_format.c @@ -332,7 +332,7 @@ void wg_format_from_caps(struct wg_format *format, const GstCaps *caps) if (gst_video_info_from_caps(&info, caps)) wg_format_from_video_info(format, &info); } - else if (!strcmp(name, "audio/mpeg")) + else if (!strcmp(name, "audio/mpeg") && gst_structure_get_boolean(structure, "parsed", &parsed) && parsed) { wg_format_from_caps_audio_mpeg1(format, caps); }
From: Alfred Agrell floating@muncher.se
--- dlls/winegstreamer/wg_format.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/dlls/winegstreamer/wg_format.c b/dlls/winegstreamer/wg_format.c index 96b4e4b76e7..56e6d46466a 100644 --- a/dlls/winegstreamer/wg_format.c +++ b/dlls/winegstreamer/wg_format.c @@ -509,6 +509,11 @@ static GstCaps *wg_format_to_caps_video(const struct wg_format *format) return NULL;
gst_video_info_set_format(&info, video_format, format->u.video.width, abs(format->u.video.height)); + if (format->u.video.fps_d) + { + info.fps_n = format->u.video.fps_n; + info.fps_d = format->u.video.fps_d; + } if ((caps = gst_video_info_to_caps(&info))) { for (i = 0; i < gst_caps_get_size(caps); ++i)
From: Alfred Agrell floating@muncher.se
--- dlls/winegstreamer/unixlib.c | 9 +-------- dlls/winegstreamer/wg_format.c | 11 ++++------- dlls/winegstreamer/wg_parser.c | 27 ++++++++++----------------- dlls/winegstreamer/wg_transform.c | 10 ++-------- 4 files changed, 17 insertions(+), 40 deletions(-)
diff --git a/dlls/winegstreamer/unixlib.c b/dlls/winegstreamer/unixlib.c index 513ece95a90..46f2c79da4d 100644 --- a/dlls/winegstreamer/unixlib.c +++ b/dlls/winegstreamer/unixlib.c @@ -124,16 +124,9 @@ GstElement *find_element(GstElementFactoryListType type, GstCaps *src_caps, GstC
done: if (element) - { GST_DEBUG("Created %s element %p.", name, element); - } else - { - gchar *src_str = gst_caps_to_string(src_caps), *sink_str = gst_caps_to_string(sink_caps); - GST_WARNING("Failed to create element matching caps %s / %s.", src_str, sink_str); - g_free(sink_str); - g_free(src_str); - } + GST_WARNING("Failed to create element matching caps %" GST_PTR_FORMAT " / %" GST_PTR_FORMAT ".", src_caps, sink_caps);
return element; } diff --git a/dlls/winegstreamer/wg_format.c b/dlls/winegstreamer/wg_format.c index 56e6d46466a..7d4bc7a5f8c 100644 --- a/dlls/winegstreamer/wg_format.c +++ b/dlls/winegstreamer/wg_format.c @@ -181,17 +181,17 @@ static void wg_format_from_caps_audio_mpeg1(struct wg_format *format, const GstC
if (!gst_structure_get_int(structure, "layer", &layer)) { - GST_WARNING("Missing "layer" value."); + GST_WARNING("Missing "layer" value in %" GST_PTR_FORMAT ".", caps); return; } if (!gst_structure_get_int(structure, "channels", &channels)) { - GST_WARNING("Missing "channels" value."); + GST_WARNING("Missing "channels" value in %" GST_PTR_FORMAT ".", caps); return; } if (!gst_structure_get_int(structure, "rate", &rate)) { - GST_WARNING("Missing "rate" value."); + GST_WARNING("Missing "rate" value in %" GST_PTR_FORMAT ".", caps); return; }
@@ -350,10 +350,7 @@ void wg_format_from_caps(struct wg_format *format, const GstCaps *caps) } else { - gchar *str = gst_caps_to_string(caps); - - GST_FIXME("Unhandled caps %s.", str); - g_free(str); + GST_FIXME("Unhandled caps %" GST_PTR_FORMAT ".", caps); } }
diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index f8127168c2a..2ae083687b0 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -457,7 +457,7 @@ static NTSTATUS wg_parser_stream_seek(void *args) if (!push_event(stream->my_sink, gst_event_new_seek(params->rate, GST_FORMAT_TIME, flags, start_type, params->start_pos * 100, stop_type, params->stop_pos == stream->duration ? -1 : params->stop_pos * 100))) - GST_ERROR("Failed to seek.\n"); + GST_ERROR("Failed to seek.");
return S_OK; } @@ -478,13 +478,12 @@ static NTSTATUS wg_parser_stream_notify_qos(void *args) /* This can happen legitimately if the sample falls outside of the * segment bounds. GStreamer elements shouldn't present the sample in * that case, but DirectShow doesn't care. */ - GST_LOG("Ignoring QoS event.\n"); + GST_LOG("Ignoring QoS event."); return S_OK; } - if (!(event = gst_event_new_qos(params->underflow ? GST_QOS_TYPE_UNDERFLOW : GST_QOS_TYPE_OVERFLOW, params->proportion, params->diff * 100, stream_time))) - GST_ERROR("Failed to create QOS event.\n"); + GST_ERROR("Failed to create QOS event."); push_event(stream->my_sink, event);
return S_OK; @@ -677,6 +676,7 @@ static GstFlowReturn sink_chain_cb(GstPad *pad, GstObject *parent, GstBuffer *bu
if (!stream->enabled) { + GST_LOG("Stream is disabled; discarding buffer."); pthread_mutex_unlock(&parser->mutex); gst_buffer_unref(buffer); return GST_FLOW_OK; @@ -693,7 +693,7 @@ static GstFlowReturn sink_chain_cb(GstPad *pad, GstObject *parent, GstBuffer *bu if (!gst_buffer_map(buffer, &stream->map_info, GST_MAP_READ)) { pthread_mutex_unlock(&parser->mutex); - GST_ERROR("Failed to map buffer.\n"); + GST_ERROR("Failed to map buffer."); gst_buffer_unref(buffer); return GST_FLOW_ERROR; } @@ -723,7 +723,6 @@ static gboolean sink_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) case GST_QUERY_CAPS: { GstCaps *caps, *filter, *temp; - gchar *str; gsize i;
gst_query_parse_caps(query, &filter); @@ -740,9 +739,7 @@ static gboolean sink_query_cb(GstPad *pad, GstObject *parent, GstQuery *query) gst_structure_remove_fields(gst_caps_get_structure(caps, i), "framerate", "pixel-aspect-ratio", NULL);
- str = gst_caps_to_string(caps); - GST_LOG("Stream caps are "%s".", str); - g_free(str); + GST_LOG("Stream caps are "%" GST_PTR_FORMAT "".", caps);
if (filter) { @@ -777,12 +774,8 @@ static gboolean sink_query_cb(GstPad *pad, GstObject *parent, GstQuery *query)
pthread_mutex_unlock(&parser->mutex);
- if (!ret && gst_debug_category_get_threshold(GST_CAT_DEFAULT) >= GST_LEVEL_WARNING) - { - gchar *str = gst_caps_to_string(caps); - GST_WARNING("Rejecting caps "%s".", str); - g_free(str); - } + if (!ret) + GST_WARNING("Rejecting caps "%" GST_PTR_FORMAT "".", caps); gst_query_set_accept_caps_result(query, ret); return TRUE; } @@ -1586,7 +1579,7 @@ static NTSTATUS wg_parser_connect(void *args) ret = gst_element_get_state(parser->container, NULL, NULL, -1); if (ret == GST_STATE_CHANGE_FAILURE) { - GST_ERROR("Failed to play stream.\n"); + GST_ERROR("Failed to play stream."); goto out; }
@@ -1649,7 +1642,7 @@ static NTSTATUS wg_parser_connect(void *args) if (stream->eos) { stream->duration = 0; - GST_WARNING("Failed to query duration.\n"); + GST_WARNING("Failed to query duration."); break; }
diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 1610f27b981..6b2910740d3 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -196,7 +196,6 @@ static gboolean transform_sink_query_cb(GstPad *pad, GstObject *parent, GstQuery case GST_QUERY_CAPS: { GstCaps *caps, *filter, *temp; - gchar *str;
gst_query_parse_caps(query, &filter); if (!(caps = wg_format_to_caps(&transform->output_format))) @@ -209,9 +208,7 @@ static gboolean transform_sink_query_cb(GstPad *pad, GstObject *parent, GstQuery caps = temp; }
- str = gst_caps_to_string(caps); - GST_INFO("Returning caps %s", str); - g_free(str); + GST_INFO("Returning caps %" GST_PTR_FORMAT, caps);
gst_query_set_caps_result(query, caps); gst_caps_unref(caps); @@ -500,7 +497,6 @@ NTSTATUS wg_transform_set_output_format(void *args) const struct wg_format *format = params->format; GstSample *sample; GstCaps *caps; - gchar *str;
if (!(caps = wg_format_to_caps(format))) { @@ -539,9 +535,7 @@ NTSTATUS wg_transform_set_output_format(void *args) return STATUS_UNSUCCESSFUL; }
- str = gst_caps_to_string(caps); - GST_INFO("Configured new caps %s.", str); - g_free(str); + GST_INFO("Configured new caps %" GST_PTR_FORMAT ".", caps);
/* Ideally and to be fully compatible with native transform, the queued * output buffers will need to be converted to the new output format and
From: Alfred Agrell floating@muncher.se
--- dlls/quartz/tests/mpegsplit.c | 178 ++++++++++++++++++++++++++++++++-- 1 file changed, 168 insertions(+), 10 deletions(-)
diff --git a/dlls/quartz/tests/mpegsplit.c b/dlls/quartz/tests/mpegsplit.c index b2cced84c68..ad8f6636a61 100644 --- a/dlls/quartz/tests/mpegsplit.c +++ b/dlls/quartz/tests/mpegsplit.c @@ -60,7 +60,7 @@ static WCHAR *load_resource(const WCHAR *name) res = FindResourceW(NULL, name, (LPCWSTR)RT_RCDATA); ok(!!res, "Failed to load resource, error %lu.\n", GetLastError()); ptr = LockResource(LoadResource(GetModuleHandleA(NULL), res)); - WriteFile(file, ptr, SizeofResource( GetModuleHandleA(NULL), res), &written, NULL); + WriteFile(file, ptr, SizeofResource(GetModuleHandleA(NULL), res), &written, NULL); ok(written == SizeofResource(GetModuleHandleA(NULL), res), "Failed to write resource.\n"); CloseHandle(file);
@@ -609,7 +609,7 @@ static void test_media_types(void) todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); mt.subtype = MEDIASUBTYPE_MPEG1System; hr = IPin_QueryAccept(pin, &mt); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(hr == S_OK, "Got hr %#lx.\n", hr); mt.subtype = MEDIASUBTYPE_MPEG1AudioPayload; hr = IPin_QueryAccept(pin, &mt); ok(hr == S_FALSE, "Got hr %#lx.\n", hr); @@ -1097,8 +1097,8 @@ struct testfilter IAsyncReader IAsyncReader_iface, *reader; const AM_MEDIA_TYPE *mt; HANDLE eos_event; - unsigned int sample_count, eos_count, new_segment_count; - REFERENCE_TIME segment_start, segment_end, seek_start, seek_end; + unsigned int sample_count, eos_count, new_segment_count, byte_count; + REFERENCE_TIME segment_start, segment_end_min, segment_end_max, seek_start, seek_end; };
static inline struct testfilter *impl_from_strmbase_filter(struct strmbase_filter *iface) @@ -1221,7 +1221,7 @@ static HRESULT WINAPI testsink_Receive(struct strmbase_sink *iface, IMediaSample HRESULT hr;
hr = IMediaSample_GetTime(sample, &start, &end); - todo_wine_if (hr == VFW_S_NO_STOP_TIME) ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(hr == S_OK || (filter->sample_count > 0 && hr == VFW_E_SAMPLE_TIME_NOT_SET), "Got hr %#lx.\n", hr);
if (winetest_debug > 1) trace("%04lx: Got sample with timestamps %I64d-%I64d.\n", GetCurrentThreadId(), start, end); @@ -1237,6 +1237,7 @@ static HRESULT WINAPI testsink_Receive(struct strmbase_sink *iface, IMediaSample
ok(!filter->eos_count, "Got a sample after EOS.\n"); ++filter->sample_count; + filter->byte_count += IMediaSample_GetActualDataLength(sample); return S_OK; }
@@ -1271,7 +1272,8 @@ static HRESULT testsink_new_segment(struct strmbase_sink *iface, IMediaSeeking_Release(seeking);
ok(start == filter->segment_start, "Expected start %I64d, got %I64d.\n", filter->segment_start, start); - ok(end == filter->segment_end, "Expected end %I64d, got %I64d.\n", filter->segment_end, end); + ok(end >= filter->segment_end_min && end <= filter->segment_end_max, + "Expected end %I64d to %I64d, got %I64d.\n", filter->segment_end_min, filter->segment_end_max, end); ok(rate == 1.0, "Got rate %.16e.\n", rate);
return S_OK; @@ -1373,7 +1375,8 @@ static void testfilter_init(struct testfilter *filter) strmbase_sink_init(&filter->sink, &filter->filter, L"sink", &testsink_ops, NULL); filter->IAsyncReader_iface.lpVtbl = &async_reader_vtbl; filter->eos_event = CreateEventW(NULL, FALSE, FALSE, NULL); - filter->segment_end = 5392500; + filter->segment_end_min = 5000000; /* 5392500 on native */ + filter->segment_end_max = 5500000; }
static void test_connect_pin(void) @@ -1727,7 +1730,7 @@ static void test_seeking(void) duration = 0; hr = IMediaSeeking_GetDuration(seeking, &duration); ok(hr == S_OK, "Got hr %#lx.\n", hr); - ok(duration == 5392500, "Got duration %I64d.\n", duration); + ok(duration >= 5000000 && duration <= 5500000, "Got duration %I64d.\n", duration);
stop = current = 0xdeadbeef; hr = IMediaSeeking_GetStopPosition(seeking, &stop); @@ -1871,9 +1874,10 @@ static void test_streaming(void)
testsink.new_segment_count = testsink.sample_count = testsink.eos_count = 0; testsink.segment_start = 100 * 10000; - testsink.segment_end = 300 * 10000; + testsink.segment_end_min = 300 * 10000; + testsink.segment_end_max = 300 * 10000; hr = IMediaSeeking_SetPositions(seeking, &testsink.segment_start, AM_SEEKING_AbsolutePositioning, - &testsink.segment_end, AM_SEEKING_AbsolutePositioning); + &testsink.segment_end_min, AM_SEEKING_AbsolutePositioning); ok(hr == S_OK, "Got hr %#lx.\n", hr);
ok(!WaitForSingleObject(testsink.eos_event, 1000), "Did not receive EOS.\n"); @@ -1962,6 +1966,159 @@ static void test_large_file(void) ok(ret, "Failed to delete file, error %lu.\n", GetLastError()); }
+static void test_streamselect(IAMStreamSelect* sel) +{ + IBaseFilter *filter; + IUnknown *unk_video; + IUnknown *unk_audio; + DWORD stream_count; + AM_MEDIA_TYPE *mt; + IPin *pin_video; + IPin *pin_audio; + IUnknown *object; + IUnknown *unk; + DWORD flags; + DWORD group; + LPWSTR name; + HRESULT hr; + LCID lcid; + + IAMStreamSelect_QueryInterface(sel, &IID_IBaseFilter, (void **)&filter); + IBaseFilter_FindPin(filter, L"Video", &pin_video); + IBaseFilter_FindPin(filter, L"Audio", &pin_audio); + IPin_QueryInterface(pin_video, &IID_IUnknown, (void **)&unk_video); + IPin_QueryInterface(pin_audio, &IID_IUnknown, (void **)&unk_audio); + + hr = IAMStreamSelect_Count(sel, &stream_count); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(stream_count == 2, "Got %lu streams.\n", stream_count); + + hr = IAMStreamSelect_Info(sel, 0, &mt, &flags, &lcid, &group, &name, &object, &unk); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + todo_wine ok(flags == AMSTREAMSELECTINFO_ENABLED, "Got flags %lx.\n", flags); + ok(lcid == 0, "Got LCID %lx.\n", lcid); + ok(group == 0, "Got group %lx.\n", group); + todo_wine ok(name && !wcscmp(name, L"Stream(E0)"), "Got name %ls.\n", name ? name : L"(null)"); + todo_wine ok(object == unk_video, "Got object %p, expected %p.\n", object, unk_video); + ok(unk == NULL, "Got unknown %p.\n", unk); + + if (name) + CoTaskMemFree(name); + if (mt) + DeleteMediaType(mt); + if (object) + IUnknown_Release(object); + if (unk) + IUnknown_Release(unk); + + hr = IAMStreamSelect_Info(sel, 1, &mt, &flags, &lcid, &group, &name, &object, &unk); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + todo_wine ok(flags == AMSTREAMSELECTINFO_ENABLED, "Got flags %lx.\n", flags); + ok(lcid == 0, "Got LCID %lx.\n", lcid); + todo_wine ok(group == 1, "Got group %lx.\n", group); + todo_wine ok(name && !wcscmp(name, L"Stream(C0)"), "Got name %ls.\n", name ? name : L"(null)"); + todo_wine ok(object == unk_audio, "Got object %p, expected %p.\n", object, unk_audio); + ok(unk == NULL, "Got unknown %p.\n", unk); + + if (name) + CoTaskMemFree(name); + if (mt) + DeleteMediaType(mt); + if (object) + IUnknown_Release(object); + if (unk) + IUnknown_Release(unk); + + IBaseFilter_Release(filter); + IUnknown_Release(pin_video); + IUnknown_Release(pin_audio); + IUnknown_Release(unk_video); + IUnknown_Release(unk_audio); +} + +static void test_video_file(void) +{ + const WCHAR *filename = load_resource(L"test.mpg"); + IBaseFilter *filter = create_mpeg_splitter(); + struct testfilter testsink_video; + struct testfilter testsink_audio; + IPin *source_video = NULL; + IPin *source_audio = NULL; + IMediaControl *control; + IFilterGraph2 *graph; + IAMStreamSelect *sel; + DWORD stream_count; + HRESULT hr; + ULONG ref; + DWORD ret; + + IBaseFilter_QueryInterface(filter, &IID_IAMStreamSelect, (void **)&sel); + hr = IAMStreamSelect_Count(sel, &stream_count); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(stream_count == 0, "Got %lu streams.\n", stream_count); + + graph = connect_input(filter, filename); + hr = IBaseFilter_FindPin(filter, L"Video", &source_video); + ok(source_video != NULL, "No video pin, hr %#lx.\n", hr); + hr = IBaseFilter_FindPin(filter, L"Audio", &source_audio); + ok(source_audio != NULL, "No audio pin, hr %#lx.\n", hr); + + test_streamselect(sel); + + testfilter_init(&testsink_video); + testfilter_init(&testsink_audio); + testsink_video.segment_end_min = 1000000; /* 11232612 on native, 1197000 in Wine */ + testsink_video.segment_end_max = 20000000; + testsink_audio.segment_end_min = 1000000; + testsink_audio.segment_end_max = 20000000; + + hr = IFilterGraph2_AddFilter(graph, &testsink_video.filter.IBaseFilter_iface, L"sink_video"); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IFilterGraph2_AddFilter(graph, &testsink_audio.filter.IBaseFilter_iface, L"sink_audio"); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IFilterGraph2_ConnectDirect(graph, source_video, &testsink_video.sink.pin.IPin_iface, NULL); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IFilterGraph2_ConnectDirect(graph, source_audio, &testsink_audio.sink.pin.IPin_iface, NULL); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + ok(IsEqualGUID(&testsink_video.sink.pin.mt.majortype, &MEDIATYPE_Video), "Media types didn't match.\n"); + ok(IsEqualGUID(&testsink_video.sink.pin.mt.subtype, &MEDIASUBTYPE_MPEG1Payload), "Media types didn't match.\n"); + ok(IsEqualGUID(&testsink_video.sink.pin.mt.formattype, &FORMAT_MPEGVideo), "Media types didn't match.\n"); + + ok(IsEqualGUID(&testsink_audio.sink.pin.mt.majortype, &MEDIATYPE_Audio), "Media types didn't match.\n"); + ok(IsEqualGUID(&testsink_audio.sink.pin.mt.subtype, &MEDIASUBTYPE_MPEG1AudioPayload), "Media types didn't match.\n"); + ok(IsEqualGUID(&testsink_audio.sink.pin.mt.formattype, &FORMAT_WaveFormatEx), "Media types didn't match.\n"); + + testsink_video.new_segment_count = 0; + testsink_audio.new_segment_count = 0; + IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control); + hr = IMediaControl_Stop(control); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IMediaControl_Run(control); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + ok(!WaitForSingleObject(testsink_video.eos_event, 1000), "Video sink did not receive EOS.\n"); + ok(!WaitForSingleObject(testsink_audio.eos_event, 1000), "Audio sink did not receive EOS.\n"); + ok(testsink_video.new_segment_count == 1, "Video sink got %u segments.\n", testsink_video.new_segment_count); + ok(testsink_audio.new_segment_count == 1, "Audio sink got %u segments.\n", testsink_audio.new_segment_count); + + /* Native also supports subtype MEDIASUBTYPE_MPEG1Packet, yielding 1230 and 8828 bytes, respectively */ + ok(testsink_video.byte_count == 1214, "Video sink got %u bytes.\n", testsink_video.byte_count); + ok(testsink_audio.byte_count == 8777, "Audio sink got %u bytes.\n", testsink_audio.byte_count); + + IAMStreamSelect_Release(sel); + IPin_Release(source_video); + IPin_Release(source_audio); + IMediaControl_Release(control); + ref = IFilterGraph2_Release(graph); + ok(!ref, "Got outstanding refcount %ld.\n", ref); + ref = IBaseFilter_Release(filter); + ok(!ref, "Got outstanding refcount %ld.\n", ref); + ret = DeleteFileW(filename); + ok(ret, "Failed to delete file, error %lu.\n", GetLastError()); +} + START_TEST(mpegsplit) { IBaseFilter *filter; @@ -1988,6 +2145,7 @@ START_TEST(mpegsplit) test_seeking(); test_streaming(); test_large_file(); + test_video_file();
CoUninitialize(); }
From: Alfred Agrell floating@muncher.se
--- dlls/quartz/tests/Makefile.in | 1 + dlls/quartz/tests/mpegvideo.c | 1620 +++++++++++++++++++++++++++++++++ 2 files changed, 1621 insertions(+) create mode 100644 dlls/quartz/tests/mpegvideo.c
diff --git a/dlls/quartz/tests/Makefile.in b/dlls/quartz/tests/Makefile.in index 1613907424c..de7a1a6d914 100644 --- a/dlls/quartz/tests/Makefile.in +++ b/dlls/quartz/tests/Makefile.in @@ -13,6 +13,7 @@ C_SRCS = \ mpegaudio.c \ mpeglayer3.c \ mpegsplit.c \ + mpegvideo.c \ passthrough.c \ systemclock.c \ videorenderer.c \ diff --git a/dlls/quartz/tests/mpegvideo.c b/dlls/quartz/tests/mpegvideo.c new file mode 100644 index 00000000000..09156a87524 --- /dev/null +++ b/dlls/quartz/tests/mpegvideo.c @@ -0,0 +1,1620 @@ +/* + * MPEG video decoder filter unit tests + * + * Copyright 2022 Anton Baskanov + * Copyright 2018 Zebediah Figura + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define COBJMACROS +#include "dshow.h" +#include "mmreg.h" +#include "ks.h" +#include "ksmedia.h" +#include "wine/strmbase.h" +#include "wine/test.h" + +/* same as normal MPEG1VIDEOINFO, except bSequenceHeader is 12 bytes */ +typedef struct tagMPEG1VIDEOINFO_12 { + VIDEOINFOHEADER hdr; + DWORD dwStartTimeCode; + DWORD cbSequenceHeader; + BYTE bSequenceHeader[12]; +} MPEG1VIDEOINFO_12; + +static const MPEG1VIDEOINFO_12 mpg_format = +{ + .hdr.rcSource = { 0, 0, 32, 24 }, + .hdr.rcTarget = { 0, 0, 0, 0 }, + .hdr.dwBitRate = 0, + .hdr.dwBitErrorRate = 0, + .hdr.AvgTimePerFrame = 400000, /* 25fps, 40ms */ + .hdr.bmiHeader.biSize = sizeof(BITMAPINFOHEADER), + .hdr.bmiHeader.biWidth = 32, + .hdr.bmiHeader.biHeight = 24, + .hdr.bmiHeader.biPlanes = 0, + .hdr.bmiHeader.biBitCount = 0, + .hdr.bmiHeader.biCompression = 0, + .hdr.bmiHeader.biSizeImage = 0, + .hdr.bmiHeader.biXPelsPerMeter = 2000, + .hdr.bmiHeader.biYPelsPerMeter = 2000, + .hdr.bmiHeader.biClrUsed = 0, + .hdr.bmiHeader.biClrImportant = 0, + .dwStartTimeCode = 4096, + .cbSequenceHeader = 12, + .bSequenceHeader = { 0x00, 0x00, 0x01, 0xb3, 0x02, 0x00, 0x18, 0x13, 0xff, 0xff, 0xe0, 0x18 }, +}; + +static const AM_MEDIA_TYPE mpeg_mt = +{ + /* MEDIATYPE_Video, MEDIASUBTYPE_MPEG1Payload, FORMAT_MPEGVideo */ + .majortype = {0x73646976, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}, + .subtype = {0xe436eb81, 0x524f, 0x11ce, {0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}}, + .bFixedSizeSamples = TRUE, + .lSampleSize = 1, + .formattype = {0x05589f82, 0xc356, 0x11ce, {0xbf, 0x01, 0x00, 0xaa, 0x00, 0x55, 0x59, 0x5a}}, + .cbFormat = sizeof(MPEG1VIDEOINFO_12), + .pbFormat = (BYTE *)&mpg_format, +}; + +static const VIDEOINFOHEADER yuy2_format = +{ + .rcSource = { 0, 0, 32, 24 }, + .rcTarget = { 0, 0, 0, 0 }, + .dwBitRate = 32 * 24 * 16 * 25, + .dwBitErrorRate = 0, + .AvgTimePerFrame = 400000, + .bmiHeader.biSize = sizeof(BITMAPINFOHEADER), + .bmiHeader.biWidth = 32, + .bmiHeader.biHeight = 24, + .bmiHeader.biPlanes = 1, + .bmiHeader.biBitCount = 16, + .bmiHeader.biCompression = MAKEFOURCC('Y','U','Y','2'), + .bmiHeader.biSizeImage = 32 * 24 * 16 / 8, + .bmiHeader.biXPelsPerMeter = 2000, + .bmiHeader.biYPelsPerMeter = 2000, +}; + +static const AM_MEDIA_TYPE yuy2_mt = +{ + /* MEDIATYPE_Video, MEDIASUBTYPE_YUY2, FORMAT_VideoInfo */ + .majortype = {0x73646976, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}, + .subtype = {0x32595559, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}, + .bFixedSizeSamples = TRUE, + .lSampleSize = 32 * 24 * 16 / 8, + .formattype = {0x05589f80, 0xc356, 0x11ce, {0xbf, 0x01, 0x00, 0xaa, 0x00, 0x55, 0x59, 0x5a}}, + .cbFormat = sizeof(VIDEOINFOHEADER), + .pbFormat = (BYTE *)&yuy2_format, +}; + +static IBaseFilter *create_mpeg_video_codec(void) +{ + IBaseFilter *filter = NULL; + HRESULT hr = CoCreateInstance(&CLSID_CMpegVideoCodec, NULL, CLSCTX_INPROC_SERVER, + &IID_IBaseFilter, (void **)&filter); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + return filter; +} + +static inline BOOL compare_media_types(const AM_MEDIA_TYPE *a, const AM_MEDIA_TYPE *b) +{ + return !memcmp(a, b, offsetof(AM_MEDIA_TYPE, pbFormat)) + && !memcmp(a->pbFormat, b->pbFormat, a->cbFormat); +} + +static const struct +{ + const GUID *subtype; + int bits_per_pixel; + DWORD compression; + BOOL works_native; + BOOL works_wine; +} +video_types[] = +{ + /* native's EnumMediaTypes offers YV12, but that media type refuses to connect */ + /* Wine doesn't enumerate unsupported ones (Y41P and RGB8) at all */ + { &MEDIASUBTYPE_YV12, 12, MAKEFOURCC('Y','V','1','2'), FALSE, TRUE }, + { &MEDIASUBTYPE_Y41P, 12, MAKEFOURCC('Y','4','1','P'), TRUE, FALSE }, + { &MEDIASUBTYPE_YUY2, 16, MAKEFOURCC('Y','U','Y','2'), TRUE, TRUE }, + { &MEDIASUBTYPE_UYVY, 16, MAKEFOURCC('U','Y','V','Y'), TRUE, TRUE }, + { &MEDIASUBTYPE_RGB24, 24, BI_RGB, TRUE, TRUE }, + { &MEDIASUBTYPE_RGB32, 32, BI_RGB, TRUE, TRUE }, + { &MEDIASUBTYPE_RGB565, 16, BI_BITFIELDS, TRUE, TRUE }, + { &MEDIASUBTYPE_RGB555, 16, BI_RGB, TRUE, TRUE }, + { &MEDIASUBTYPE_RGB8, 8, BI_RGB, TRUE, FALSE }, +}; + +static void init_video_mt(AM_MEDIA_TYPE *mt, VIDEOINFOHEADER *format, unsigned type_idx) +{ + memcpy(mt, &yuy2_mt, sizeof(AM_MEDIA_TYPE)); + memcpy(format, &yuy2_format, sizeof(VIDEOINFOHEADER)); + mt->subtype = *video_types[type_idx].subtype; + mt->pbFormat = (BYTE*)format; + format->bmiHeader.biBitCount = video_types[type_idx].bits_per_pixel; + format->bmiHeader.biCompression = video_types[type_idx].compression; + format->bmiHeader.biSizeImage = format->bmiHeader.biBitCount * format->bmiHeader.biWidth * format->bmiHeader.biHeight / 8; + format->dwBitRate = format->bmiHeader.biSizeImage*8 * 25; + mt->lSampleSize = format->bmiHeader.biSizeImage; + if (IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_RGB8)) + format->bmiHeader.biClrUsed = 0x100; +} + +static ULONG get_refcount(void *iface) +{ + IUnknown *unknown = iface; + IUnknown_AddRef(unknown); + return IUnknown_Release(unknown); +} + +#define check_interface(a, b, c) check_interface_(__LINE__, a, b, c) +static void check_interface_(unsigned int line, void *iface_ptr, REFIID iid, BOOL supported) +{ + IUnknown *iface = iface_ptr; + HRESULT hr, expected_hr; + IUnknown *unk; + + expected_hr = supported ? S_OK : E_NOINTERFACE; + + hr = IUnknown_QueryInterface(iface, iid, (void **)&unk); + ok_(__FILE__, line)(hr == expected_hr, "Got hr %#lx, expected %#lx.\n", hr, expected_hr); + if (SUCCEEDED(hr)) + IUnknown_Release(unk); +} + +static void test_interfaces(void) +{ + IBaseFilter *filter = create_mpeg_video_codec(); + IPin *pin; + + check_interface(filter, &IID_IBaseFilter, TRUE); + check_interface(filter, &IID_IMediaFilter, TRUE); + check_interface(filter, &IID_IPersist, TRUE); + check_interface(filter, &IID_IUnknown, TRUE); + + check_interface(filter, &IID_IAMFilterMiscFlags, FALSE); + check_interface(filter, &IID_IBasicAudio, FALSE); + check_interface(filter, &IID_IBasicVideo, FALSE); + check_interface(filter, &IID_IKsPropertySet, FALSE); + check_interface(filter, &IID_IMediaPosition, FALSE); + check_interface(filter, &IID_IMediaSeeking, FALSE); + check_interface(filter, &IID_IPin, FALSE); + check_interface(filter, &IID_IQualityControl, FALSE); + check_interface(filter, &IID_IQualProp, FALSE); + check_interface(filter, &IID_IReferenceClock, FALSE); + check_interface(filter, &IID_IVideoWindow, FALSE); + check_interface(filter, &IID_IPersistPropertyBag, FALSE); + + IBaseFilter_FindPin(filter, L"In", &pin); + + check_interface(pin, &IID_IMemInputPin, TRUE); + check_interface(pin, &IID_IPin, TRUE); + check_interface(pin, &IID_IQualityControl, TRUE); + check_interface(pin, &IID_IUnknown, TRUE); + + check_interface(pin, &IID_IMediaPosition, FALSE); + check_interface(pin, &IID_IMediaSeeking, FALSE); + + IPin_Release(pin); + + IBaseFilter_FindPin(filter, L"Out", &pin); + + check_interface(pin, &IID_IPin, TRUE); + check_interface(pin, &IID_IMediaPosition, TRUE); + check_interface(pin, &IID_IMediaSeeking, TRUE); + check_interface(pin, &IID_IQualityControl, TRUE); + check_interface(pin, &IID_IUnknown, TRUE); + + check_interface(pin, &IID_IAsyncReader, FALSE); + + IPin_Release(pin); + + IBaseFilter_Release(filter); +} + +static const GUID test_iid = {0x33333333}; +static LONG outer_ref = 1; + +static HRESULT WINAPI outer_QueryInterface(IUnknown *iface, REFIID iid, void **out) +{ + if (IsEqualGUID(iid, &IID_IUnknown) + || IsEqualGUID(iid, &IID_IBaseFilter) + || IsEqualGUID(iid, &test_iid)) + { + *out = (IUnknown *)0xdeadbeef; + return S_OK; + } + ok(0, "unexpected call %s\n", wine_dbgstr_guid(iid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI outer_AddRef(IUnknown *iface) +{ + return InterlockedIncrement(&outer_ref); +} + +static ULONG WINAPI outer_Release(IUnknown *iface) +{ + return InterlockedDecrement(&outer_ref); +} + +static const IUnknownVtbl outer_vtbl = +{ + outer_QueryInterface, + outer_AddRef, + outer_Release, +}; + +static IUnknown test_outer = {&outer_vtbl}; + +static void test_aggregation(void) +{ + IBaseFilter *filter, *filter2; + IUnknown *unk, *unk2; + HRESULT hr; + ULONG ref; + + filter = (IBaseFilter *)0xdeadbeef; + hr = CoCreateInstance(&CLSID_CMpegVideoCodec, &test_outer, CLSCTX_INPROC_SERVER, + &IID_IBaseFilter, (void **)&filter); + ok(hr == E_NOINTERFACE, "Got hr %#lx.\n", hr); + ok(!filter, "Got interface %p.\n", filter); + + hr = CoCreateInstance(&CLSID_CMpegVideoCodec, &test_outer, CLSCTX_INPROC_SERVER, + &IID_IUnknown, (void **)&unk); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(outer_ref == 1, "Got unexpected refcount %ld.\n", outer_ref); + ok(unk != &test_outer, "Returned IUnknown should not be outer IUnknown.\n"); + ref = get_refcount(unk); + ok(ref == 1, "Got unexpected refcount %ld.\n", ref); + + ref = IUnknown_AddRef(unk); + ok(ref == 2, "Got unexpected refcount %ld.\n", ref); + ok(outer_ref == 1, "Got unexpected refcount %ld.\n", outer_ref); + + ref = IUnknown_Release(unk); + ok(ref == 1, "Got unexpected refcount %ld.\n", ref); + ok(outer_ref == 1, "Got unexpected refcount %ld.\n", outer_ref); + + hr = IUnknown_QueryInterface(unk, &IID_IUnknown, (void **)&unk2); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(unk2 == unk, "Got unexpected IUnknown %p.\n", unk2); + IUnknown_Release(unk2); + + hr = IUnknown_QueryInterface(unk, &IID_IBaseFilter, (void **)&filter); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IBaseFilter_QueryInterface(filter, &IID_IUnknown, (void **)&unk2); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(unk2 == (IUnknown *)0xdeadbeef, "Got unexpected IUnknown %p.\n", unk2); + + hr = IBaseFilter_QueryInterface(filter, &IID_IBaseFilter, (void **)&filter2); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(filter2 == (IBaseFilter *)0xdeadbeef, "Got unexpected IBaseFilter %p.\n", filter2); + + hr = IUnknown_QueryInterface(unk, &test_iid, (void **)&unk2); + ok(hr == E_NOINTERFACE, "Got hr %#lx.\n", hr); + ok(!unk2, "Got unexpected IUnknown %p.\n", unk2); + + hr = IBaseFilter_QueryInterface(filter, &test_iid, (void **)&unk2); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(unk2 == (IUnknown *)0xdeadbeef, "Got unexpected IUnknown %p.\n", unk2); + + IBaseFilter_Release(filter); + ref = IUnknown_Release(unk); + ok(!ref, "Got unexpected refcount %ld.\n", ref); + ok(outer_ref == 1, "Got unexpected refcount %ld.\n", outer_ref); +} + +static void test_unconnected_filter_state(void) +{ + IBaseFilter *filter = create_mpeg_video_codec(); + FILTER_STATE state; + HRESULT hr; + ULONG ref; + + hr = IBaseFilter_GetState(filter, 0, &state); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(state == State_Stopped, "Got state %u.\n", state); + + hr = IBaseFilter_Pause(filter); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IBaseFilter_GetState(filter, 0, &state); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(state == State_Paused, "Got state %u.\n", state); + + hr = IBaseFilter_Run(filter, 0); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IBaseFilter_GetState(filter, 0, &state); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(state == State_Running, "Got state %u.\n", state); + + hr = IBaseFilter_Pause(filter); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IBaseFilter_GetState(filter, 0, &state); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(state == State_Paused, "Got state %u.\n", state); + + hr = IBaseFilter_Stop(filter); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IBaseFilter_GetState(filter, 0, &state); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(state == State_Stopped, "Got state %u.\n", state); + + hr = IBaseFilter_Run(filter, 0); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IBaseFilter_GetState(filter, 0, &state); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(state == State_Running, "Got state %u.\n", state); + + hr = IBaseFilter_Stop(filter); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IBaseFilter_GetState(filter, 0, &state); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(state == State_Stopped, "Got state %u.\n", state); + + ref = IBaseFilter_Release(filter); + ok(!ref, "Got outstanding refcount %ld.\n", ref); +} + +static void test_enum_pins(void) +{ + IBaseFilter *filter = create_mpeg_video_codec(); + IEnumPins *enum1, *enum2; + ULONG count, ref; + IPin *pins[3]; + HRESULT hr; + + ref = get_refcount(filter); + ok(ref == 1, "Got unexpected refcount %ld.\n", ref); + + hr = IBaseFilter_EnumPins(filter, NULL); + ok(hr == E_POINTER, "Got hr %#lx.\n", hr); + + hr = IBaseFilter_EnumPins(filter, &enum1); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ref = get_refcount(filter); + ok(ref == 2, "Got unexpected refcount %ld.\n", ref); + ref = get_refcount(enum1); + ok(ref == 1, "Got unexpected refcount %ld.\n", ref); + + hr = IEnumPins_Next(enum1, 1, NULL, NULL); + ok(hr == E_POINTER, "Got hr %#lx.\n", hr); + + hr = IEnumPins_Next(enum1, 1, pins, NULL); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ref = get_refcount(filter); + ok(ref == 3, "Got unexpected refcount %ld.\n", ref); + ref = get_refcount(pins[0]); + ok(ref == 3, "Got unexpected refcount %ld.\n", ref); + ref = get_refcount(enum1); + ok(ref == 1, "Got unexpected refcount %ld.\n", ref); + IPin_Release(pins[0]); + ref = get_refcount(filter); + ok(ref == 2, "Got unexpected refcount %ld.\n", ref); + + hr = IEnumPins_Next(enum1, 1, pins, NULL); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ref = get_refcount(filter); + ok(ref == 3, "Got unexpected refcount %ld.\n", ref); + ref = get_refcount(pins[0]); + ok(ref == 3, "Got unexpected refcount %ld.\n", ref); + ref = get_refcount(enum1); + ok(ref == 1, "Got unexpected refcount %ld.\n", ref); + IPin_Release(pins[0]); + ref = get_refcount(filter); + ok(ref == 2, "Got unexpected refcount %ld.\n", ref); + + hr = IEnumPins_Next(enum1, 1, pins, NULL); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + hr = IEnumPins_Reset(enum1); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IEnumPins_Next(enum1, 1, pins, &count); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(count == 1, "Got count %lu.\n", count); + IPin_Release(pins[0]); + + hr = IEnumPins_Next(enum1, 1, pins, &count); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(count == 1, "Got count %lu.\n", count); + IPin_Release(pins[0]); + + hr = IEnumPins_Next(enum1, 1, pins, &count); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + ok(!count, "Got count %lu.\n", count); + + hr = IEnumPins_Reset(enum1); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IEnumPins_Next(enum1, 2, pins, NULL); + ok(hr == E_INVALIDARG, "Got hr %#lx.\n", hr); + + hr = IEnumPins_Next(enum1, 2, pins, &count); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(count == 2, "Got count %lu.\n", count); + IPin_Release(pins[0]); + IPin_Release(pins[1]); + + hr = IEnumPins_Next(enum1, 2, pins, &count); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + ok(!count, "Got count %lu.\n", count); + + hr = IEnumPins_Reset(enum1); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IEnumPins_Next(enum1, 3, pins, &count); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + ok(count == 2, "Got count %lu.\n", count); + IPin_Release(pins[0]); + IPin_Release(pins[1]); + + hr = IEnumPins_Reset(enum1); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IEnumPins_Clone(enum1, &enum2); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IEnumPins_Skip(enum1, 3); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + hr = IEnumPins_Skip(enum1, 2); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IEnumPins_Skip(enum1, 1); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + hr = IEnumPins_Next(enum1, 1, pins, NULL); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + hr = IEnumPins_Next(enum2, 1, pins, NULL); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + IPin_Release(pins[0]); + + IEnumPins_Release(enum2); + IEnumPins_Release(enum1); + ref = IBaseFilter_Release(filter); + ok(!ref, "Got outstanding refcount %ld.\n", ref); +} + +static void test_find_pin(void) +{ + IBaseFilter *filter = create_mpeg_video_codec(); + IEnumPins *enum_pins; + IPin *pin, *pin2; + HRESULT hr; + ULONG ref; + + hr = IBaseFilter_EnumPins(filter, &enum_pins); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IBaseFilter_FindPin(filter, L"In", &pin); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IEnumPins_Next(enum_pins, 1, &pin2, NULL); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(pin == pin2, "Pins didn't match.\n"); + IPin_Release(pin); + IPin_Release(pin2); + + hr = IBaseFilter_FindPin(filter, L"Out", &pin); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IEnumPins_Next(enum_pins, 1, &pin2, NULL); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(pin == pin2, "Pins didn't match.\n"); + IPin_Release(pin); + IPin_Release(pin2); + + hr = IBaseFilter_FindPin(filter, L"XForm In", &pin); + ok(hr == VFW_E_NOT_FOUND, "Got hr %#lx.\n", hr); + hr = IBaseFilter_FindPin(filter, L"XForm Out", &pin); + ok(hr == VFW_E_NOT_FOUND, "Got hr %#lx.\n", hr); + hr = IBaseFilter_FindPin(filter, L"input pin", &pin); + ok(hr == VFW_E_NOT_FOUND, "Got hr %#lx.\n", hr); + hr = IBaseFilter_FindPin(filter, L"output pin", &pin); + ok(hr == VFW_E_NOT_FOUND, "Got hr %#lx.\n", hr); + + IEnumPins_Release(enum_pins); + ref = IBaseFilter_Release(filter); + ok(!ref, "Got outstanding refcount %ld.\n", ref); +} + +static void test_pin_info(void) +{ + IBaseFilter *filter = create_mpeg_video_codec(); + PIN_DIRECTION dir; + PIN_INFO info; + HRESULT hr; + WCHAR *id; + ULONG ref; + IPin *pin; + + hr = IBaseFilter_FindPin(filter, L"In", &pin); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ref = get_refcount(filter); + ok(ref == 2, "Got unexpected refcount %ld.\n", ref); + ref = get_refcount(pin); + ok(ref == 2, "Got unexpected refcount %ld.\n", ref); + + hr = IPin_QueryPinInfo(pin, &info); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(info.pFilter == filter, "Expected filter %p, got %p.\n", filter, info.pFilter); + ok(info.dir == PINDIR_INPUT, "Got direction %d.\n", info.dir); + ok(!wcscmp(info.achName, L"Input"), "Got name %s.\n", debugstr_w(info.achName)); + ref = get_refcount(filter); + ok(ref == 3, "Got unexpected refcount %ld.\n", ref); + ref = get_refcount(pin); + ok(ref == 3, "Got unexpected refcount %ld.\n", ref); + IBaseFilter_Release(info.pFilter); + + hr = IPin_QueryDirection(pin, &dir); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(dir == PINDIR_INPUT, "Got direction %d.\n", dir); + + hr = IPin_QueryId(pin, &id); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(!wcscmp(id, L"In"), "Got id %s.\n", wine_dbgstr_w(id)); + CoTaskMemFree(id); + + hr = IPin_QueryInternalConnections(pin, NULL, NULL); + ok(hr == E_NOTIMPL, "Got hr %#lx.\n", hr); + + IPin_Release(pin); + + hr = IBaseFilter_FindPin(filter, L"Out", &pin); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IPin_QueryPinInfo(pin, &info); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(info.pFilter == filter, "Expected filter %p, got %p.\n", filter, info.pFilter); + ok(info.dir == PINDIR_OUTPUT, "Got direction %d.\n", info.dir); + ok(!wcscmp(info.achName, L"Output"), "Got name %s.\n", debugstr_w(info.achName)); + IBaseFilter_Release(info.pFilter); + + hr = IPin_QueryDirection(pin, &dir); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(dir == PINDIR_OUTPUT, "Got direction %d.\n", dir); + + hr = IPin_QueryId(pin, &id); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(!wcscmp(id, L"Out"), "Got id %s.\n", wine_dbgstr_w(id)); + CoTaskMemFree(id); + + hr = IPin_QueryInternalConnections(pin, NULL, NULL); + ok(hr == E_NOTIMPL, "Got hr %#lx.\n", hr); + + IPin_Release(pin); + + ref = IBaseFilter_Release(filter); + ok(!ref, "Got outstanding refcount %ld.\n", ref); +} + +static void test_enum_media_types(void) +{ + IBaseFilter *filter = create_mpeg_video_codec(); + IEnumMediaTypes *enum1, *enum2; + AM_MEDIA_TYPE *mts[1]; + ULONG ref, count; + HRESULT hr; + IPin *pin; + + IBaseFilter_FindPin(filter, L"In", &pin); + + hr = IPin_EnumMediaTypes(pin, &enum1); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IEnumMediaTypes_Next(enum1, 1, mts, NULL); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + hr = IEnumMediaTypes_Next(enum1, 1, mts, &count); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + ok(!count, "Got count %lu.\n", count); + + hr = IEnumMediaTypes_Reset(enum1); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IEnumMediaTypes_Next(enum1, 1, mts, NULL); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + hr = IEnumMediaTypes_Clone(enum1, &enum2); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IEnumMediaTypes_Skip(enum1, 1); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + hr = IEnumMediaTypes_Next(enum2, 1, mts, NULL); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + IEnumMediaTypes_Release(enum1); + IEnumMediaTypes_Release(enum2); + IPin_Release(pin); + + IBaseFilter_FindPin(filter, L"Out", &pin); + + hr = IPin_EnumMediaTypes(pin, &enum1); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IEnumMediaTypes_Next(enum1, 1, mts, NULL); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + hr = IEnumMediaTypes_Next(enum1, 1, mts, &count); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + ok(!count, "Got count %lu.\n", count); + + hr = IEnumMediaTypes_Reset(enum1); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IEnumMediaTypes_Next(enum1, 1, mts, NULL); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + hr = IEnumMediaTypes_Clone(enum1, &enum2); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IEnumMediaTypes_Skip(enum1, 1); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + hr = IEnumMediaTypes_Next(enum2, 1, mts, NULL); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + IEnumMediaTypes_Release(enum1); + IEnumMediaTypes_Release(enum2); + IPin_Release(pin); + + ref = IBaseFilter_Release(filter); + ok(!ref, "Got outstanding refcount %ld.\n", ref); +} + +static void test_media_types(void) +{ + IBaseFilter *filter = create_mpeg_video_codec(); + AM_MEDIA_TYPE mt; + HRESULT hr; + ULONG ref; + IPin *pin; + + IBaseFilter_FindPin(filter, L"In", &pin); + + hr = IPin_QueryAccept(pin, &mpeg_mt); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + mt = mpeg_mt; + mt.subtype = MEDIASUBTYPE_MPEG1Packet; + hr = IPin_QueryAccept(pin, &mt); + todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); + + mt = mpeg_mt; + mt.subtype = GUID_NULL; + hr = IPin_QueryAccept(pin, &mt); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + IPin_Release(pin); + + IBaseFilter_FindPin(filter, L"Out", &pin); + + hr = IPin_QueryAccept(pin, &yuy2_mt); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + IPin_Release(pin); + + ref = IBaseFilter_Release(filter); + ok(!ref, "Got outstanding refcount %ld.\n", ref); +} + +struct testfilter +{ + struct strmbase_filter filter; + struct strmbase_source source; + struct strmbase_sink sink; + const AM_MEDIA_TYPE *mt; + unsigned int got_sample, got_new_segment, got_eos, got_begin_flush, got_end_flush; + REFERENCE_TIME expected_start_time; + REFERENCE_TIME expected_stop_time; + BOOL todo_stop_time; +}; + +static inline struct testfilter *impl_from_strmbase_filter(struct strmbase_filter *iface) +{ + return CONTAINING_RECORD(iface, struct testfilter, filter); +} + +static struct strmbase_pin *testfilter_get_pin(struct strmbase_filter *iface, unsigned int index) +{ + struct testfilter *filter = impl_from_strmbase_filter(iface); + if (!index) + return &filter->source.pin; + return NULL; +} + +static void testfilter_destroy(struct strmbase_filter *iface) +{ + struct testfilter *filter = impl_from_strmbase_filter(iface); + strmbase_source_cleanup(&filter->source); + strmbase_sink_cleanup(&filter->sink); + strmbase_filter_cleanup(&filter->filter); +} + +static const struct strmbase_filter_ops testfilter_ops = +{ + .filter_get_pin = testfilter_get_pin, + .filter_destroy = testfilter_destroy, +}; + +static HRESULT testsource_query_interface(struct strmbase_pin *iface, REFIID iid, void **out) +{ + return E_NOINTERFACE; +} + +static HRESULT WINAPI testsource_DecideAllocator(struct strmbase_source *iface, + IMemInputPin *peer, IMemAllocator **allocator) +{ + return S_OK; +} + +static const struct strmbase_source_ops testsource_ops = +{ + .base.pin_query_interface = testsource_query_interface, + .pfnAttemptConnection = BaseOutputPinImpl_AttemptConnection, + .pfnDecideAllocator = testsource_DecideAllocator, +}; + +static HRESULT testsink_query_interface(struct strmbase_pin *iface, REFIID iid, void **out) +{ + struct testfilter *filter = impl_from_strmbase_filter(iface->filter); + + if (IsEqualGUID(iid, &IID_IMemInputPin)) + *out = &filter->sink.IMemInputPin_iface; + else + return E_NOINTERFACE; + + IUnknown_AddRef((IUnknown *)*out); + return S_OK; +} + +static HRESULT testsink_get_media_type(struct strmbase_pin *iface, unsigned int index, AM_MEDIA_TYPE *mt) +{ + struct testfilter *filter = impl_from_strmbase_filter(iface->filter); + if (!index && filter->mt) + { + CopyMediaType(mt, filter->mt); + return S_OK; + } + return VFW_S_NO_MORE_ITEMS; +} + +static HRESULT testsink_connect(struct strmbase_sink *iface, IPin *peer, const AM_MEDIA_TYPE *mt) +{ + struct testfilter *filter = impl_from_strmbase_filter(iface->pin.filter); + if (filter->mt && !IsEqualGUID(&mt->majortype, &filter->mt->majortype)) + return VFW_E_TYPE_NOT_ACCEPTED; + return S_OK; +} + +static HRESULT WINAPI testsink_Receive(struct strmbase_sink *iface, IMediaSample *sample) +{ + struct testfilter *filter = impl_from_strmbase_filter(iface->pin.filter); + REFERENCE_TIME start, stop; + AM_MEDIA_TYPE *mt = (AM_MEDIA_TYPE *)0xdeadbeef; + HRESULT hr; + LONG size; + + size = IMediaSample_GetSize(sample); + ok(size == filter->sink.pin.mt.lSampleSize, "Got size %lu, expected %lu.\n", size, filter->sink.pin.mt.lSampleSize); + size = IMediaSample_GetActualDataLength(sample); + ok(size == filter->sink.pin.mt.lSampleSize, "Got actual size %lu, expected %lu.\n", size, filter->sink.pin.mt.lSampleSize); + ok(IMediaSample_GetActualDataLength(sample) <= IMediaSample_GetSize(sample), "Buffer overflow"); + + start = 0xdeadbeef; + stop = 0xdeadbeef; + hr = IMediaSample_GetTime(sample, &start, &stop); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IMediaSample_GetMediaType(sample, &mt); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + if (mt) + DeleteMediaType(mt); + + if (filter->got_sample == 0 && filter->expected_start_time != (REFERENCE_TIME)-1) + { + ok(start == filter->expected_start_time, "Got start time %s, expected %s.\n", + wine_dbgstr_longlong(start), wine_dbgstr_longlong(filter->expected_start_time)); + todo_wine_if(filter->todo_stop_time) + ok(stop == filter->expected_stop_time, "Got stop time %s, expected %s.\n", + wine_dbgstr_longlong(stop), wine_dbgstr_longlong(filter->expected_stop_time)); + } + + ++filter->got_sample; + + return S_OK; +} + +static HRESULT testsink_new_segment(struct strmbase_sink *iface, + REFERENCE_TIME start, REFERENCE_TIME stop, double rate) +{ + struct testfilter *filter = impl_from_strmbase_filter(iface->pin.filter); + ++filter->got_new_segment; + ok(start == 10000, "Got start %s.\n", wine_dbgstr_longlong(start)); + ok(stop == 20000, "Got stop %s.\n", wine_dbgstr_longlong(stop)); + ok(rate == 1.0, "Got rate %.16e.\n", rate); + return S_OK; +} + +static HRESULT testsink_eos(struct strmbase_sink *iface) +{ + struct testfilter *filter = impl_from_strmbase_filter(iface->pin.filter); + ++filter->got_eos; + return S_OK; +} + +static HRESULT testsink_begin_flush(struct strmbase_sink *iface) +{ + struct testfilter *filter = impl_from_strmbase_filter(iface->pin.filter); + ++filter->got_begin_flush; + return S_OK; +} + +static HRESULT testsink_end_flush(struct strmbase_sink *iface) +{ + struct testfilter *filter = impl_from_strmbase_filter(iface->pin.filter); + ++filter->got_end_flush; + return S_OK; +} + +static const struct strmbase_sink_ops testsink_ops = +{ + .base.pin_query_interface = testsink_query_interface, + .base.pin_get_media_type = testsink_get_media_type, + .sink_connect = testsink_connect, + .pfnReceive = testsink_Receive, + .sink_new_segment = testsink_new_segment, + .sink_eos = testsink_eos, + .sink_begin_flush = testsink_begin_flush, + .sink_end_flush = testsink_end_flush, +}; + +static void testfilter_init(struct testfilter *filter) +{ + static const GUID clsid = {0xabacab}; + memset(filter, 0, sizeof(*filter)); + strmbase_filter_init(&filter->filter, NULL, &clsid, &testfilter_ops); + strmbase_source_init(&filter->source, &filter->filter, L"source", &testsource_ops); + strmbase_sink_init(&filter->sink, &filter->filter, L"sink", &testsink_ops, NULL); +} + +static void test_sink_allocator(IMemInputPin *input) +{ + IMemAllocator *req_allocator, *ret_allocator; + ALLOCATOR_PROPERTIES props, ret_props; + HRESULT hr; + + hr = IMemInputPin_GetAllocatorRequirements(input, &props); + ok(hr == E_NOTIMPL, "Got hr %#lx.\n", hr); + + hr = IMemInputPin_GetAllocator(input, &ret_allocator); + todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); + + if (hr == S_OK) + { + hr = IMemAllocator_GetProperties(ret_allocator, &props); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(!props.cBuffers, "Got %ld buffers.\n", props.cBuffers); + ok(!props.cbBuffer, "Got size %ld.\n", props.cbBuffer); + ok(!props.cbAlign, "Got alignment %ld.\n", props.cbAlign); + ok(!props.cbPrefix, "Got prefix %ld.\n", props.cbPrefix); + + hr = IMemInputPin_NotifyAllocator(input, ret_allocator, TRUE); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + IMemAllocator_Release(ret_allocator); + } + + hr = IMemInputPin_NotifyAllocator(input, NULL, TRUE); + ok(hr == E_POINTER, "Got hr %#lx.\n", hr); + + CoCreateInstance(&CLSID_MemoryAllocator, NULL, CLSCTX_INPROC_SERVER, + &IID_IMemAllocator, (void **)&req_allocator); + + props.cBuffers = 1; + props.cbBuffer = 256; + props.cbAlign = 1; + props.cbPrefix = 0; + hr = IMemAllocator_SetProperties(req_allocator, &props, &ret_props); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IMemInputPin_NotifyAllocator(input, req_allocator, TRUE); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IMemInputPin_GetAllocator(input, &ret_allocator); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(ret_allocator == req_allocator, "Allocators didn't match.\n"); + + IMemAllocator_Release(req_allocator); + IMemAllocator_Release(ret_allocator); +} + +static void test_source_allocator(IFilterGraph2 *graph, IMediaControl *control, + IPin *sink, IPin *source, struct testfilter *testsource, struct testfilter *testsink, + unsigned type_idx, BOOL should_work) +{ + ALLOCATOR_PROPERTIES props, req_props = {2, 30000, 32, 0}; + IMemAllocator *allocator; + IMediaSample *sample; + VIDEOINFOHEADER format; + AM_MEDIA_TYPE mt; + HRESULT hr; + + hr = IFilterGraph2_ConnectDirect(graph, &testsource->source.pin.IPin_iface, sink, &mpeg_mt); + ok(hr == S_OK, "(%u) Got hr %#lx.\n", type_idx, hr); + + init_video_mt(&mt, &format, type_idx); + hr = IFilterGraph2_ConnectDirect(graph, source, &testsink->sink.pin.IPin_iface, &mt); + if (!should_work) + { + ok(hr == VFW_E_TYPE_NOT_ACCEPTED, "(%u) Got hr %#lx.\n", type_idx, hr); + IFilterGraph2_Disconnect(graph, sink); + IFilterGraph2_Disconnect(graph, &testsource->source.pin.IPin_iface); + return; + } + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + ok(!!testsink->sink.pAllocator, "Expected an allocator.\n"); + hr = IMemAllocator_GetProperties(testsink->sink.pAllocator, &props); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(props.cBuffers == 1, "Got %ld buffers.\n", props.cBuffers); + ok(props.cbBuffer == mt.lSampleSize, "Got size %ld.\n", props.cbBuffer); + ok(props.cbAlign == 1, "Got alignment %ld.\n", props.cbAlign); + ok(!props.cbPrefix, "Got prefix %ld.\n", props.cbPrefix); + + hr = IMemAllocator_GetBuffer(testsink->sink.pAllocator, &sample, NULL, NULL, 0); + ok(hr == VFW_E_NOT_COMMITTED, "Got hr %#lx.\n", hr); + + hr = IMediaControl_Pause(control); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IMemAllocator_GetBuffer(testsink->sink.pAllocator, &sample, NULL, NULL, 0); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + if (hr == S_OK) + IMediaSample_Release(sample); + + hr = IMediaControl_Stop(control); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IMemAllocator_GetBuffer(testsink->sink.pAllocator, &sample, NULL, NULL, 0); + ok(hr == VFW_E_NOT_COMMITTED, "Got hr %#lx.\n", hr); + + IFilterGraph2_Disconnect(graph, source); + IFilterGraph2_Disconnect(graph, &testsink->sink.pin.IPin_iface); + + init_video_mt(&mt, &format, type_idx); + hr = IFilterGraph2_ConnectDirect(graph, source, &testsink->sink.pin.IPin_iface, &mt); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + ok(!!testsink->sink.pAllocator, "Expected an allocator.\n"); + hr = IMemAllocator_GetProperties(testsink->sink.pAllocator, &props); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(props.cBuffers == 1, "Got %ld buffers.\n", props.cBuffers); + ok(props.cbBuffer == mt.lSampleSize, "Got size %ld.\n", props.cbBuffer); + ok(props.cbAlign == 1, "Got alignment %ld.\n", props.cbAlign); + ok(!props.cbPrefix, "Got prefix %ld.\n", props.cbPrefix); + + IFilterGraph2_Disconnect(graph, source); + IFilterGraph2_Disconnect(graph, &testsink->sink.pin.IPin_iface); + + CoCreateInstance(&CLSID_MemoryAllocator, NULL, CLSCTX_INPROC_SERVER, + &IID_IMemAllocator, (void **)&allocator); + testsink->sink.pAllocator = allocator; + + hr = IMemAllocator_SetProperties(allocator, &req_props, &props); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + init_video_mt(&mt, &format, type_idx); + hr = IFilterGraph2_ConnectDirect(graph, source, &testsink->sink.pin.IPin_iface, &mt); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + ok(testsink->sink.pAllocator == allocator, "Expected an allocator.\n"); + hr = IMemAllocator_GetProperties(testsink->sink.pAllocator, &props); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(props.cBuffers == 1, "Got %ld buffers.\n", props.cBuffers); + ok(props.cbBuffer == mt.lSampleSize, "Got size %ld.\n", props.cbBuffer); + ok(props.cbAlign == 1, "Got alignment %ld.\n", props.cbAlign); + ok(!props.cbPrefix, "Got prefix %ld.\n", props.cbPrefix); + + IFilterGraph2_Disconnect(graph, source); + IFilterGraph2_Disconnect(graph, &testsink->sink.pin.IPin_iface); + + IFilterGraph2_Disconnect(graph, sink); + IFilterGraph2_Disconnect(graph, &testsource->source.pin.IPin_iface); +} + +static void test_send_sample(IMemInputPin *input, IMediaSample *sample, const BYTE *data, LONG len, BOOL todo) +{ + BYTE *target_data; + HRESULT hr; + LONG size; + hr = IMediaSample_GetPointer(sample, &target_data); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + size = IMediaSample_GetSize(sample); + ok(size >= len, "Got size %ld, expected at least %ld.\n", size, len); + + memcpy(target_data, data, len); + hr = IMediaSample_SetActualDataLength(sample, len); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IMemInputPin_Receive(input, sample); + todo_wine_if(todo) /* 0xc00d6d61 is MF_E_TRANSFORM_STREAM_CHANGE */ + ok(hr == S_OK, "Got hr %#lx.\n", hr); +} + +static void test_send_video(IMemInputPin *input, IMediaSample *sample) +{ + /* gst-launch-1.0 -v videotestsrc pattern=black num-buffers=10 ! video/x-raw,width=32,height=24 ! mpeg2enc ! filesink location=empty-es2.mpg */ + /* then truncate to taste */ + /* each 00 00 01 b3 or 00 00 01 00 starts a new frame, except the first 00 00 01 00 after a 00 00 01 b3 */ + static const BYTE empty_mpg_frame1[] = { + 0x00, 0x00, 0x01, 0xb3, 0x02, 0x00, 0x18, 0x15, 0x02, 0xbf, 0x60, 0x9c, + 0x00, 0x00, 0x01, 0xb8, 0x00, 0x08, 0x00, 0x40, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x0f, 0xff, 0xf8, + 0x00, 0x00, 0x01, 0x01, 0x0b, 0xf8, 0x7d, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x20, + 0x00, 0x00, 0x01, 0x02, 0x0b, 0xf8, 0x7d, 0x2e, 0x7d, 0x2f, 0xcf, 0xc1, 0x04, 0x03, 0xa0, 0x11, 0xb1, + 0x41, 0x28, 0x88, 0x13, 0xb9, 0x6f, 0xcf, 0xc1, 0x04, 0x03, 0xa0, 0x11, 0xb1, 0x41, 0x28, 0x88, + 0x13, 0xb9, 0x6f, 0xa1, 0x4b, 0x9f, 0x48, 0x04, 0x10, 0x0e, 0x80, 0x46, 0xc5, 0x04, 0xa2, + 0x20, 0x4e, 0xe5, 0x80, 0x41, 0x00, 0xe8, 0x04, 0x6c, 0x50, 0x4a, 0x22, 0x04, 0xee, 0x58, + }; + static const BYTE empty_mpg_frame2[] = { + 0x00, 0x00, 0x01, 0x00, 0x00, 0x57, 0xff, 0xf9, 0x80, + 0x00, 0x00, 0x01, 0x01, 0x0a, 0x79, 0xc0, + 0x00, 0x00, 0x01, 0x02, 0x0a, 0x79, 0xc0, + }; + static const BYTE empty_mpg_frame3[] = { + 0x00, 0x00, 0x01, 0x00, 0x00, 0x97, 0xff, 0xf9, 0x80, + 0x00, 0x00, 0x01, 0x01, 0x0a, 0x79, 0xc0, + 0x00, 0x00, 0x01, 0x02, 0x0a, 0x79, 0xc0, 0x00, 0x00, 0x01, 0xb7, + }; + HRESULT hr; + IPin *pin; + + /* frame 1 - it's a complete frame, but due to how MPEG framing works, the decoder doesn't know that */ + /* frame 2 - new frame starts, frame 1 can be emitted - but Wine gets confused by colorimetry and returns an error */ + /* frame 3 - Wine emits frames 1 and 2 */ + /* meanwhile, native won't emit anything until an unknown-sized internal buffer is filled, or EOS is announced */ + test_send_sample(input, sample, empty_mpg_frame1, ARRAY_SIZE(empty_mpg_frame1), FALSE); + test_send_sample(input, sample, empty_mpg_frame2, ARRAY_SIZE(empty_mpg_frame2), TRUE); + test_send_sample(input, sample, empty_mpg_frame3, ARRAY_SIZE(empty_mpg_frame3), FALSE); + + hr = IMemInputPin_QueryInterface(input, &IID_IPin, (void **)&pin); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IPin_EndOfStream(pin); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + IPin_Release(pin); +} + +static void test_sample_processing(IMediaControl *control, IMemInputPin *input, struct testfilter *sink) +{ + REFERENCE_TIME start, stop; + IMemAllocator *allocator; + IMediaSample *sample; + HRESULT hr; + IPin *pin; + LONG size; + + sink->got_sample = 0; + sink->got_eos = 0; + + hr = IMemInputPin_QueryInterface(input, &IID_IPin, (void **)&pin); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IMemInputPin_ReceiveCanBlock(input); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IMemInputPin_GetAllocator(input, &allocator); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IMediaControl_Pause(control); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IMemAllocator_GetBuffer(allocator, &sample, NULL, NULL, 0); + ok(hr == VFW_E_NOT_COMMITTED, "Got hr %#lx.\n", hr); + + hr = IMemAllocator_Commit(allocator); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IMemAllocator_GetBuffer(allocator, &sample, NULL, NULL, 0); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + size = IMediaSample_GetSize(sample); + ok(size == 256, "Got size %ld.\n", size); + + hr = IMediaSample_SetTime(sample, NULL, NULL); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + sink->expected_start_time = 0; + sink->expected_stop_time = 0; + sink->todo_stop_time = FALSE; + hr = IMediaSample_SetTime(sample, &sink->expected_start_time, &sink->expected_stop_time); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + test_send_video(input, sample); + ok(sink->got_sample >= 1, "Got %u calls to Receive().\n", sink->got_sample); + ok(sink->got_eos == 1, "Got %u calls to EndOfStream().\n", sink->got_eos); + sink->got_sample = 0; + sink->got_eos = 0; + + hr = IMediaControl_Stop(control); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IMediaControl_Pause(control); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + start = 22222; + hr = IMediaSample_SetTime(sample, &start, NULL); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + sink->expected_start_time = -1; /* native returns start and stop 0xff80000000000001 */ + test_send_video(input, sample); + ok(sink->got_sample >= 1, "Got %u calls to Receive().\n", sink->got_sample); + ok(sink->got_eos == 1, "Got %u calls to EndOfStream().\n", sink->got_eos); + sink->got_sample = 0; + sink->got_eos = 0; + + hr = IMediaControl_Stop(control); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IMediaControl_Pause(control); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + start = 22222; + stop = 33333; + hr = IMediaSample_SetTime(sample, &start, &stop); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + sink->expected_start_time = 22222; + sink->expected_stop_time = 22222; + sink->todo_stop_time = TRUE; + test_send_video(input, sample); + ok(sink->got_sample >= 1, "Got %u calls to Receive().\n", sink->got_sample); + ok(sink->got_eos == 1, "Got %u calls to EndOfStream().\n", sink->got_eos); + sink->got_sample = 0; + sink->got_eos = 0; + + hr = IMediaControl_Stop(control); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IMemInputPin_Receive(input, sample); + ok(hr == VFW_E_WRONG_STATE, "Got hr %#lx.\n", hr); + + hr=IMemAllocator_Decommit(allocator); + IPin_Release(pin); + IMediaSample_Release(sample); + IMemAllocator_Release(allocator); +} + +static void test_streaming_events(IMediaControl *control, IPin *sink, + IMemInputPin *input, struct testfilter *testsink) +{ + REFERENCE_TIME start, stop; + IMemAllocator *allocator; + IMediaSample *sample; + HRESULT hr; + IPin *pin; + + testsink->got_new_segment = 0; + testsink->got_sample = 0; + testsink->got_eos = 0; + testsink->got_begin_flush = 0; + testsink->got_end_flush = 0; + + hr = IMemInputPin_QueryInterface(input, &IID_IPin, (void **)&pin); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IMediaControl_Pause(control); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IMemInputPin_GetAllocator(input, &allocator); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IMemAllocator_Commit(allocator); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IMemAllocator_GetBuffer(allocator, &sample, NULL, NULL, 0); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + start = 0; + stop = 120000; + hr = IMediaSample_SetTime(sample, &start, &stop); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + ok(!testsink->got_new_segment, "Got %u calls to IPin::NewSegment().\n", testsink->got_new_segment); + hr = IPin_NewSegment(sink, 10000, 20000, 1.0); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(testsink->got_new_segment == 1, "Got %u calls to IPin::NewSegment().\n", testsink->got_new_segment); + + ok(!testsink->got_eos, "Got %u calls to IPin::EndOfStream().\n", testsink->got_eos); + hr = IPin_EndOfStream(sink); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(!testsink->got_sample, "Got %u calls to Receive().\n", testsink->got_sample); + ok(testsink->got_eos == 1, "Got %u calls to IPin::EndOfStream().\n", testsink->got_eos); + testsink->got_eos = 0; + + hr = IPin_EndOfStream(sink); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(testsink->got_eos == 1, "Got %u calls to IPin::EndOfStream().\n", testsink->got_eos); + + testsink->expected_start_time = 0; + testsink->expected_stop_time = 0; + testsink->todo_stop_time = TRUE; + test_send_video(input, sample); + ok(testsink->got_sample >= 1, "Got %u calls to Receive().\n", testsink->got_sample); + testsink->got_sample = 0; + + ok(!testsink->got_begin_flush, "Got %u calls to IPin::BeginFlush().\n", testsink->got_begin_flush); + hr = IPin_BeginFlush(sink); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(testsink->got_begin_flush == 1, "Got %u calls to IPin::BeginFlush().\n", testsink->got_begin_flush); + + hr = IMemInputPin_Receive(input, sample); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + hr = IPin_EndOfStream(sink); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + ok(!testsink->got_end_flush, "Got %u calls to IPin::EndFlush().\n", testsink->got_end_flush); + hr = IPin_EndFlush(sink); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(testsink->got_end_flush == 1, "Got %u calls to IPin::EndFlush().\n", testsink->got_end_flush); + + hr = IMediaControl_Stop(control); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IMediaControl_Pause(control); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + testsink->expected_start_time = 0; + testsink->expected_stop_time = 0; + testsink->todo_stop_time = TRUE; + test_send_video(input, sample); + ok(testsink->got_sample >= 1, "Got %u calls to Receive().\n", testsink->got_sample); + testsink->got_sample = 0; + + hr = IMediaControl_Stop(control); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + IMemAllocator_Decommit(allocator); + IPin_Release(pin); + IMediaSample_Release(sample); + IMemAllocator_Release(allocator); +} + +static void test_connect_pin(void) +{ + IBaseFilter *filter = create_mpeg_video_codec(); + struct testfilter testsource, testsink; + AM_MEDIA_TYPE mt, source_mt, *pmt; + IPin *sink, *source, *peer; + VIDEOINFOHEADER req_format; + IEnumMediaTypes *enummt; + IMediaControl *control; + IMemInputPin *meminput; + AM_MEDIA_TYPE req_mt; + IFilterGraph2 *graph; + unsigned int i; + HRESULT hr; + ULONG ref; + + CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, + &IID_IFilterGraph2, (void **)&graph); + testfilter_init(&testsource); + testfilter_init(&testsink); + IFilterGraph2_AddFilter(graph, &testsink.filter.IBaseFilter_iface, L"sink"); + IFilterGraph2_AddFilter(graph, &testsource.filter.IBaseFilter_iface, L"source"); + IFilterGraph2_AddFilter(graph, filter, L"MPEG video decoder"); + IBaseFilter_FindPin(filter, L"In", &sink); + IBaseFilter_FindPin(filter, L"Out", &source); + IPin_QueryInterface(sink, &IID_IMemInputPin, (void **)&meminput); + IFilterGraph2_QueryInterface(graph, &IID_IMediaControl, (void **)&control); + + for (i=0;i<ARRAY_SIZE(video_types);++i) + { + BOOL should_work; + if (winetest_platform_is_wine) should_work = video_types[i].works_wine; + else should_work = video_types[i].works_native; + test_source_allocator(graph, control, sink, source, &testsource, &testsink, i, should_work); + } + + /* Test sink connection. */ + + peer = (IPin *)0xdeadbeef; + hr = IPin_ConnectedTo(sink, &peer); + ok(hr == VFW_E_NOT_CONNECTED, "Got hr %#lx.\n", hr); + ok(!peer, "Got peer %p.\n", peer); + + hr = IPin_ConnectionMediaType(sink, &mt); + ok(hr == VFW_E_NOT_CONNECTED, "Got hr %#lx.\n", hr); + + hr = IMediaControl_Pause(control); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IFilterGraph2_ConnectDirect(graph, &testsource.source.pin.IPin_iface, sink, &mpeg_mt); + ok(hr == VFW_E_NOT_STOPPED, "Got hr %#lx.\n", hr); + hr = IMediaControl_Stop(control); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + req_mt = mpeg_mt; + req_mt.subtype = MEDIASUBTYPE_RGB24; + hr = IFilterGraph2_ConnectDirect(graph, &testsource.source.pin.IPin_iface, sink, &req_mt); + ok(hr == VFW_E_TYPE_NOT_ACCEPTED, "Got hr %#lx.\n", hr); + + hr = IFilterGraph2_ConnectDirect(graph, &testsource.source.pin.IPin_iface, sink, &mpeg_mt); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IPin_ConnectedTo(sink, &peer); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(peer == &testsource.source.pin.IPin_iface, "Got peer %p.\n", peer); + IPin_Release(peer); + + hr = IPin_ConnectionMediaType(sink, &mt); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(compare_media_types(&mt, &mpeg_mt), "Media types didn't match.\n"); + ok(compare_media_types(&testsource.source.pin.mt, &mpeg_mt), "Media types didn't match.\n"); + FreeMediaType(&mt); + + hr = IPin_QueryAccept(source, &yuy2_mt); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + req_mt = yuy2_mt; + req_mt.majortype = GUID_NULL; + hr = IPin_QueryAccept(source, &req_mt); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + req_mt = yuy2_mt; + req_mt.subtype = GUID_NULL; + hr = IPin_QueryAccept(source, &req_mt); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + for (i=0;i<ARRAY_SIZE(video_types);++i) + { + init_video_mt(&req_mt, &req_format, i); + hr = IPin_QueryAccept(source, &req_mt); + if (!winetest_platform_is_wine && !video_types[i].works_native) + continue; + todo_wine_if(!video_types[i].works_wine) + ok(hr == S_OK, "Got hr %#lx.\n", hr); + } + + req_mt = yuy2_mt; + req_mt.formattype = GUID_NULL; + hr = IPin_QueryAccept(source, &req_mt); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + + hr = IMediaControl_Pause(control); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IFilterGraph2_Disconnect(graph, sink); + ok(hr == VFW_E_NOT_STOPPED, "Got hr %#lx.\n", hr); + hr = IMediaControl_Stop(control); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + test_sink_allocator(meminput); + + hr = IPin_EnumMediaTypes(source, &enummt); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + i = 0; + while (i < ARRAY_SIZE(video_types)) + { + VIDEOINFOHEADER expect_format; + AM_MEDIA_TYPE expect_mt; + + hr = IEnumMediaTypes_Next(enummt, 1, &pmt, NULL); + ok(hr == S_OK, "%u: Got hr %#lx.\n", i, hr); + while (IsEqualGUID(&pmt->formattype, &FORMAT_VideoInfo2)) + { + DeleteMediaType(pmt); + hr = IEnumMediaTypes_Next(enummt, 1, &pmt, NULL); + ok(hr == S_OK, "%u: Got hr %#lx.\n", i, hr); + } + + init_video_mt(&expect_mt, &expect_format, i); + + ok(!memcmp(pmt, &expect_mt, offsetof(AM_MEDIA_TYPE, cbFormat)), + "%u: Media types didn't match.\n", i); + ok(!memcmp(pmt->pbFormat, &expect_format, sizeof(VIDEOINFOHEADER)), + "%u: Format blocks didn't match.\n", i); + DeleteMediaType(pmt); + + ++i; + while (winetest_platform_is_wine && !video_types[i].works_wine) + ++i; + } + + hr = IEnumMediaTypes_Next(enummt, 1, &pmt, NULL); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + IEnumMediaTypes_Release(enummt); + + /* Test source connection. */ + + peer = (IPin *)0xdeadbeef; + hr = IPin_ConnectedTo(source, &peer); + ok(hr == VFW_E_NOT_CONNECTED, "Got hr %#lx.\n", hr); + ok(!peer, "Got peer %p.\n", peer); + + hr = IPin_ConnectionMediaType(source, &mt); + ok(hr == VFW_E_NOT_CONNECTED, "Got hr %#lx.\n", hr); + + /* Exact connection. */ + + for (i=0;i<ARRAY_SIZE(video_types);i++) + { + if (!winetest_platform_is_wine && !video_types[i].works_native) + continue; + + hr = IMediaControl_Pause(control); + ok(hr == S_OK, "%u: Got hr %#lx.\n", i, hr); + init_video_mt(&req_mt, &req_format, i); + hr = IFilterGraph2_ConnectDirect(graph, source, &testsink.sink.pin.IPin_iface, &req_mt); + ok(hr == VFW_E_NOT_STOPPED, "%u: Got hr %#lx.\n", i, hr); + hr = IMediaControl_Stop(control); + ok(hr == S_OK, "%u: Got hr %#lx.\n", i, hr); + + init_video_mt(&req_mt, &req_format, i); + hr = IFilterGraph2_ConnectDirect(graph, source, &testsink.sink.pin.IPin_iface, &req_mt); + todo_wine_if(!video_types[i].works_wine) + ok(hr == S_OK, "%u: Got hr %#lx.\n", i, hr); + if (hr != S_OK) + continue; + + hr = IPin_ConnectedTo(source, &peer); + ok(hr == S_OK, "%u: Got hr %#lx.\n", i, hr); + ok(peer == &testsink.sink.pin.IPin_iface, "%u: Got peer %p.\n", i, peer); + IPin_Release(peer); + + hr = IPin_ConnectionMediaType(source, &mt); + ok(hr == S_OK, "%u: Got hr %#lx.\n", i, hr); + ok(compare_media_types(&mt, &req_mt), "%u: Media types didn't match.\n", i); + ok(compare_media_types(&testsink.sink.pin.mt, &req_mt), "%u: Media types didn't match.\n", i); + FreeMediaType(&mt); + + hr = IMediaControl_Pause(control); + ok(hr == S_OK, "%u: Got hr %#lx.\n", i, hr); + hr = IFilterGraph2_Disconnect(graph, source); + ok(hr == VFW_E_NOT_STOPPED, "%u: Got hr %#lx.\n", i, hr); + hr = IMediaControl_Stop(control); + ok(hr == S_OK, "%u: Got hr %#lx.\n", i, hr); + + test_sample_processing(control, meminput, &testsink); + test_streaming_events(control, sink, meminput, &testsink); + + hr = IFilterGraph2_Disconnect(graph, source); + ok(hr == S_OK, "%u: Got hr %#lx.\n", i, hr); + hr = IFilterGraph2_Disconnect(graph, source); + ok(hr == S_FALSE, "%u: Got hr %#lx.\n", i, hr); + ok(testsink.sink.pin.peer == source, "%u: Got peer %p.\n", i, testsink.sink.pin.peer); + IFilterGraph2_Disconnect(graph, &testsink.sink.pin.IPin_iface); + + init_video_mt(&req_mt, &req_format, i); + req_mt.lSampleSize = 999; + req_mt.bTemporalCompression = TRUE; + hr = IFilterGraph2_ConnectDirect(graph, source, &testsink.sink.pin.IPin_iface, &req_mt); + ok(hr == S_OK, "%u: Got hr %#lx.\n", i, hr); + ok(compare_media_types(&testsink.sink.pin.mt, &req_mt), "%u: Media types didn't match.\n", i); + IFilterGraph2_Disconnect(graph, source); + IFilterGraph2_Disconnect(graph, &testsink.sink.pin.IPin_iface); + } + + req_mt = yuy2_mt; + req_mt.formattype = FORMAT_None; + hr = IFilterGraph2_ConnectDirect(graph, source, &testsink.sink.pin.IPin_iface, &req_mt); + ok(hr == VFW_E_TYPE_NOT_ACCEPTED, "Got hr %#lx.\n", hr); + + /* Connection with wildcards. */ + + source_mt = yuy2_mt; + + hr = IFilterGraph2_ConnectDirect(graph, source, &testsink.sink.pin.IPin_iface, NULL); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(IsEqualGUID(&testsink.sink.pin.mt.majortype, &source_mt.majortype), "Media types didn't match.\n"); + /* don't worry too much about sub/format type */ + IFilterGraph2_Disconnect(graph, source); + IFilterGraph2_Disconnect(graph, &testsink.sink.pin.IPin_iface); + + req_mt = yuy2_mt; + req_format = yuy2_format; + req_mt.pbFormat = (BYTE *)&req_format; + req_mt.majortype = GUID_NULL; + hr = IFilterGraph2_ConnectDirect(graph, source, &testsink.sink.pin.IPin_iface, &req_mt); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(IsEqualGUID(&testsink.sink.pin.mt.majortype, &MEDIATYPE_Video), "Media types didn't match.\n"); + IFilterGraph2_Disconnect(graph, source); + IFilterGraph2_Disconnect(graph, &testsink.sink.pin.IPin_iface); + + req_mt = yuy2_mt; + req_format = yuy2_format; + req_mt.pbFormat = (BYTE *)&req_format; + req_mt.majortype = GUID_NULL; + req_format.dwBitRate = 0; /* native looks at this specific field if the major type is GUID_NULL */ + hr = IFilterGraph2_ConnectDirect(graph, source, &testsink.sink.pin.IPin_iface, &req_mt); + todo_wine ok(hr == VFW_E_NO_ACCEPTABLE_TYPES, "Got hr %#lx.\n", hr); + if (hr == S_OK) + { + IFilterGraph2_Disconnect(graph, source); + IFilterGraph2_Disconnect(graph, &testsink.sink.pin.IPin_iface); + } + + req_mt = yuy2_mt; + req_mt.majortype = MEDIATYPE_Audio; + req_mt.subtype = GUID_NULL; + req_mt.formattype = GUID_NULL; + hr = IFilterGraph2_ConnectDirect(graph, source, &testsink.sink.pin.IPin_iface, &req_mt); + ok(hr == VFW_E_NO_ACCEPTABLE_TYPES, "Got hr %#lx.\n", hr); + + /* Test enumeration of sink media types. */ + + req_mt = yuy2_mt; + req_mt.majortype = MEDIATYPE_Audio; + req_mt.subtype = GUID_NULL; + req_mt.formattype = GUID_NULL; + testsink.mt = &req_mt; + hr = IFilterGraph2_ConnectDirect(graph, source, &testsink.sink.pin.IPin_iface, NULL); + ok(hr == VFW_E_NO_ACCEPTABLE_TYPES, "Got hr %#lx.\n", hr); + + req_mt = yuy2_mt; + req_mt.lSampleSize = 444; + testsink.mt = &req_mt; + hr = IFilterGraph2_ConnectDirect(graph, source, &testsink.sink.pin.IPin_iface, NULL); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(compare_media_types(&testsink.sink.pin.mt, &req_mt), "Media types didn't match.\n"); + + hr = IFilterGraph2_Disconnect(graph, sink); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + hr = IFilterGraph2_Disconnect(graph, sink); + ok(hr == S_FALSE, "Got hr %#lx.\n", hr); + ok(testsource.source.pin.peer == sink, "Got peer %p.\n", testsource.source.pin.peer); + IFilterGraph2_Disconnect(graph, &testsource.source.pin.IPin_iface); + + IMemInputPin_Release(meminput); + IPin_Release(sink); + IPin_Release(source); + IMediaControl_Release(control); + ref = IFilterGraph2_Release(graph); + ok(!ref, "Got outstanding refcount %ld.\n", ref); + ref = IBaseFilter_Release(filter); + ok(!ref, "Got outstanding refcount %ld.\n", ref); + ref = IBaseFilter_Release(&testsource.filter.IBaseFilter_iface); + ok(!ref, "Got outstanding refcount %ld.\n", ref); + ref = IBaseFilter_Release(&testsink.filter.IBaseFilter_iface); + ok(!ref, "Got outstanding refcount %ld.\n", ref); +} + +START_TEST(mpegvideo) +{ + IBaseFilter *filter; + + CoInitialize(NULL); + + if (FAILED(CoCreateInstance(&CLSID_CMpegVideoCodec, NULL, CLSCTX_INPROC_SERVER, + &IID_IBaseFilter, (void **)&filter))) + { + skip("Failed to create MPEG video decoder instance.\n"); + return; + } + IBaseFilter_Release(filter); + + test_interfaces(); + test_aggregation(); + test_unconnected_filter_state(); + test_enum_pins(); + test_find_pin(); + test_pin_info(); + test_enum_media_types(); + test_media_types(); + test_connect_pin(); + + CoUninitialize(); +}
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=138915
Your paranoid android.
=== w11pro64 (32 bit report) ===
quartz: mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 0. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 0. mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 56ce. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 56ce. mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 0. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 0. mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 0. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 0. mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 0. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 0. mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 56ce. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 56ce. mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 0. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 0. mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 0. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 0. mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 0. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 0. mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 56ce. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 56ce. mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 0. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 0. mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 0. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 0. mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 0. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 0. mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 56ce. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 56ce. mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 0. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 0. mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 0. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 0. mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 0. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 0. mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 56ce. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 56ce. mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 0. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 0. mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 0. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 0. mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 0. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 0. mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 56ce. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 56ce. mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 0. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 0. mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 0. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 0. mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 0. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 0. mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 56ce. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 56ce. mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 0. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 0. mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 0. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 0. mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 0. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 0. mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 56ce. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 56ce. mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 0. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 0. mpegvideo.c:836: Test failed: Got start time ff80000000000001, expected 0. mpegvideo.c:839: Test failed: Got stop time 406a0000c06a01, expected 0.
=== w11pro64_amd (64 bit report) ===
quartz: mpegvideo.c:839: Test failed: Got stop time 1a473a0, expected 0. mpegvideo.c:839: Test failed: Got stop time 1a4ca6e, expected 56ce. mpegvideo.c:839: Test failed: Got stop time 1a473a0, expected 0. mpegvideo.c:839: Test failed: Got stop time 1a473a0, expected 0. mpegvideo.c:839: Test failed: Got stop time 1a473a0, expected 0. mpegvideo.c:839: Test failed: Got stop time 1a4ca6e, expected 56ce. mpegvideo.c:839: Test failed: Got stop time 1a473a0, expected 0. mpegvideo.c:839: Test failed: Got stop time 1a473a0, expected 0. mpegvideo.c:839: Test failed: Got stop time 1a473a0, expected 0. mpegvideo.c:839: Test failed: Got stop time 1a4ca6e, expected 56ce. mpegvideo.c:839: Test failed: Got stop time 1a473a0, expected 0. mpegvideo.c:839: Test failed: Got stop time 1a473a0, expected 0. mpegvideo.c:839: Test failed: Got stop time 1a473a0, expected 0. mpegvideo.c:839: Test failed: Got stop time 1a4ca6e, expected 56ce. mpegvideo.c:839: Test failed: Got stop time 1a473a0, expected 0. mpegvideo.c:839: Test failed: Got stop time 1a473a0, expected 0. mpegvideo.c:839: Test failed: Got stop time 1a473a0, expected 0. mpegvideo.c:839: Test failed: Got stop time 1a4ca6e, expected 56ce. mpegvideo.c:839: Test failed: Got stop time 1a473a0, expected 0. mpegvideo.c:839: Test failed: Got stop time 1a473a0, expected 0. mpegvideo.c:839: Test failed: Got stop time 1a473a0, expected 0. mpegvideo.c:839: Test failed: Got stop time 1a4ca6e, expected 56ce. mpegvideo.c:839: Test failed: Got stop time 1a473a0, expected 0. mpegvideo.c:839: Test failed: Got stop time 1a473a0, expected 0. mpegvideo.c:839: Test failed: Got stop time 1a473a0, expected 0. mpegvideo.c:839: Test failed: Got stop time 1a4ca6e, expected 56ce. mpegvideo.c:839: Test failed: Got stop time 1a473a0, expected 0. mpegvideo.c:839: Test failed: Got stop time 1a473a0, expected 0. mpegvideo.c:839: Test failed: Got stop time 1a473a0, expected 0. mpegvideo.c:839: Test failed: Got stop time 1a4ca6e, expected 56ce. mpegvideo.c:839: Test failed: Got stop time 1a473a0, expected 0. mpegvideo.c:839: Test failed: Got stop time 1a473a0, expected 0.
=== w10pro64 (32 bit report) ===
quartz: videorenderer.c:1098: Test failed: Wait failed. videorenderer.c:1100: Test failed: Got hr 0x103. videorenderer.c:1156: Test failed: Got hr 0x80004005.
=== debian11 (32 bit report) ===
quartz: mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0.
=== debian11 (32 bit ar:MA report) ===
quartz: mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0.
=== debian11 (32 bit de report) ===
quartz: mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0.
=== debian11 (32 bit fr report) ===
quartz: mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0.
=== debian11 (32 bit he:IL report) ===
quartz: mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0.
=== debian11 (32 bit hi:IN report) ===
quartz: mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0.
=== debian11 (32 bit ja:JP report) ===
quartz: mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0.
=== debian11 (32 bit zh:CN report) ===
quartz: mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0.
=== debian11b (32 bit WoW report) ===
quartz: mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0.
=== debian11b (64 bit WoW report) ===
quartz: mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0. mpegvideo.c:1061: Test succeeded inside todo block: Got hr 0.