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 | 156 +++++++++++++++++++ dlls/winegstreamer/wg_format.c | 4 + dlls/winegstreamer/wg_transform.c | 2 +- dlls/winegstreamer/winegstreamer_classes.idl | 7 + 7 files changed, 214 insertions(+), 1 deletion(-)
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 8cfadd10bfc..465a7074cbe 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 853907e1825..afe4b5ee413 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -631,6 +631,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}; @@ -660,6 +661,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)) @@ -773,6 +776,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}, @@ -1008,6 +1043,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, @@ -1049,6 +1086,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 a74d9baee69..32521be893f 100644 --- a/dlls/winegstreamer/quartz_parser.c +++ b/dlls/winegstreamer/quartz_parser.c @@ -1892,6 +1892,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..0066cd7131c 100644 --- a/dlls/winegstreamer/quartz_transform.c +++ b/dlls/winegstreamer/quartz_transform.c @@ -738,6 +738,162 @@ 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) +{ + struct { + const GUID *subtype; + wg_video_format wg_format; + int bits_per_pixel; + DWORD compression; + } static const video_types[] = { + { &MEDIASUBTYPE_YV12, WG_VIDEO_FORMAT_YV12, 12, MAKEFOURCC('Y','V','1','2') }, + { &MEDIASUBTYPE_YUY2, WG_VIDEO_FORMAT_YUY2, 16, MAKEFOURCC('Y','U','Y','2') }, + { &MEDIASUBTYPE_UYVY, WG_VIDEO_FORMAT_UYVY, 16, MAKEFOURCC('U','Y','V','Y') }, + { &MEDIASUBTYPE_RGB24, WG_VIDEO_FORMAT_BGR, 24, BI_RGB }, + { &MEDIASUBTYPE_RGB32, WG_VIDEO_FORMAT_BGRx, 32, BI_RGB }, + { &MEDIASUBTYPE_RGB565, WG_VIDEO_FORMAT_RGB16, 16, BI_BITFIELDS }, + { &MEDIASUBTYPE_RGB555, WG_VIDEO_FORMAT_RGB15, 16, BI_RGB }, + }; + + MPEG1VIDEOINFO *input_format; + VIDEOINFO *output_format; + + if (!filter->sink.pin.peer) + return VFW_S_NO_MORE_ITEMS; + + if (index >= 7) + return VFW_S_NO_MORE_ITEMS; + + input_format = (MPEG1VIDEOINFO*)filter->sink.pin.mt.pbFormat; + + output_format = CoTaskMemAlloc(sizeof(*output_format)); + if (!output_format) + return E_OUTOFMEMORY; + + memcpy(output_format, &input_format->hdr, sizeof(VIDEOINFOHEADER)); + output_format->bmiHeader.biPlanes = 1; + output_format->bmiHeader.biBitCount = video_types[index].bits_per_pixel; + output_format->bmiHeader.biCompression = video_types[index].compression; + output_format->bmiHeader.biSizeImage = + wg_format_get_bytes_for_uncompressed(video_types[index].wg_format, + output_format->bmiHeader.biWidth, output_format->bmiHeader.biHeight); + output_format->dwBitRate = MulDiv(output_format->bmiHeader.biSizeImage * 8, 10000000, output_format->AvgTimePerFrame); + + memset(mt, 0, sizeof(*mt)); + mt->majortype = MEDIATYPE_Video; + mt->subtype = *video_types[index].subtype; + mt->bFixedSizeSamples = TRUE; + mt->lSampleSize = output_format->bmiHeader.biSizeImage; + mt->formattype = FORMAT_VideoInfo; + mt->cbFormat = sizeof(VIDEOINFOHEADER); + mt->pbFormat = (BYTE *)output_format; + + if (video_types[index].wg_format == WG_VIDEO_FORMAT_RGB16) + { + mt->cbFormat = offsetof(VIDEOINFO, dwBitMasks[3]); + output_format->dwBitMasks[iRED] = 0xf800; + output_format->dwBitMasks[iGREEN] = 0x07e0; + output_format->dwBitMasks[iBLUE] = 0x001f; + } + + 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_format.c b/dlls/winegstreamer/wg_format.c index 96b4e4b76e7..4969b2564d4 100644 --- a/dlls/winegstreamer/wg_format.c +++ b/dlls/winegstreamer/wg_format.c @@ -509,6 +509,10 @@ 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)); + info.fps_n = format->u.video.fps_n; + info.fps_d = format->u.video.fps_d; + if (!format->u.video.fps_d && !format->u.video.fps_n) + info.fps_d = 1; if ((caps = gst_video_info_to_caps(&info))) { for (i = 0; i < gst_caps_get_size(caps); ++i) 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),