From: Akihiro Sagawa Subject: [RFC PATCH 4/5] winegstreamer: Add MPEG video parser to MPEG splitter. Message-Id: <20200621154028.872D.375B48EC@gmail.com> Date: Sun, 21 Jun 2020 15:41:02 +0900 Signed-off-by: Akihiro Sagawa --- dlls/quartz/tests/mpegsplit.c | 2 +- dlls/winegstreamer/gstdemux.c | 150 ++++++++++++++++++++++++++++++++-- 2 files changed, 143 insertions(+), 9 deletions(-) diff --git a/dlls/quartz/tests/mpegsplit.c b/dlls/quartz/tests/mpegsplit.c index 0932351bc7..2553742e56 100644 --- a/dlls/quartz/tests/mpegsplit.c +++ b/dlls/quartz/tests/mpegsplit.c @@ -587,7 +587,7 @@ static void test_sink_media_types(void) mt.subtype = MEDIASUBTYPE_MPEG1Video; hr = IPin_QueryAccept(pin, &mt); - todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(hr == S_OK, "Got hr %#x.\n", hr); mt.subtype = MEDIASUBTYPE_MPEG1VideoCD; hr = IPin_QueryAccept(pin, &mt); todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr); diff --git a/dlls/winegstreamer/gstdemux.c b/dlls/winegstreamer/gstdemux.c index c5096c5d32..b67d62fca5 100644 --- a/dlls/winegstreamer/gstdemux.c +++ b/dlls/winegstreamer/gstdemux.c @@ -309,6 +309,117 @@ static gboolean amt_from_gst_caps_audio_mpeg(const GstCaps *caps, AM_MEDIA_TYPE return TRUE; } +static gboolean amt_from_gst_caps_video_mpeg(const GstCaps *caps, AM_MEDIA_TYPE *mt) +{ + const DWORD aspect_ratio_table[16] = { + 0, 2000, 1347, 1406, 1523, 1611, 1687, 1787, + 1831, 1963, 2051, 2139, 2190, 2315, 2403, 2000 + }; + GstStructure *structure = gst_caps_get_structure(caps, 0); + MPEG1VIDEOINFO *mph; + BITMAPINFOHEADER *bih; + GstVideoInfo info; + const GValue *val; + GstBuffer *buffer = NULL; + DWORD bit_rate, pel_aspect_ratio; + gboolean system_stream; + gsize seq_header_len = 0; + gint version; + + TRACE("()\n"); + if (!gst_video_info_from_caps(&info, caps)) + return FALSE; + + mt->majortype = MEDIATYPE_Video; + mt->subtype = MEDIASUBTYPE_MPEG1Payload; + mt->bFixedSizeSamples = TRUE; + mt->bTemporalCompression = FALSE; + mt->lSampleSize = 1; + mt->formattype = FORMAT_MPEGVideo; + mt->pUnk = NULL; + + if (!gst_structure_get_int(structure, "mpegversion", &version)) + { + WARN("Missing 'mpegversion' value.\n"); + return FALSE; + } + if (version != 1) + { + WARN("Can't handle MPEG version %d, yet.\n", version); + return FALSE; + } + if (!gst_structure_get_boolean(structure, "systemstream", &system_stream)) + { + WARN("Missing 'systemstream' value.\n"); + return FALSE; + } + if (system_stream) + { + WARN("Can't handle system stream.\n"); + return FALSE; + } + val = gst_structure_get_value(structure, "codec_data"); + if (val) + { + buffer = (GstBuffer *)g_value_get_boxed(val); + seq_header_len = gst_buffer_get_size(buffer); + } + if (seq_header_len != 12 && seq_header_len != 140 && seq_header_len != 76) + { + WARN("Invalid sequence header length %d.\n", (int)seq_header_len); + return FALSE; + } + + mt->cbFormat = offsetof(MPEG1VIDEOINFO, bSequenceHeader) + seq_header_len; + mph = CoTaskMemAlloc(mt->cbFormat); + if (!mph) + return FALSE; + memset(mph, 0, mt->cbFormat); + mt->pbFormat = (BYTE *)mph; + + if ((mph->hdr.AvgTimePerFrame = (REFERENCE_TIME)MulDiv(10000000, + GST_VIDEO_INFO_FPS_D(&info), GST_VIDEO_INFO_FPS_N(&info))) == -1) + mph->hdr.AvgTimePerFrame = 0; /* zero division or integer overflow */ + SetRect(&mph->hdr.rcSource, 0, 0, + GST_VIDEO_INFO_WIDTH(&info), GST_VIDEO_INFO_HEIGHT(&info)); + mph->dwStartTimeCode = 1 << 12; /* FIXME: the first frame's time code */ + + mph->cbSequenceHeader = seq_header_len; + gst_buffer_extract(buffer, 0, mph->bSequenceHeader, seq_header_len); + if (memcmp(mph->bSequenceHeader, "\0\0\1\xb3", 4)) + goto error; + bit_rate = (mph->bSequenceHeader[8] << 10) + | (mph->bSequenceHeader[9] << 2) + | (mph->bSequenceHeader[10] >> 6); + TRACE("bit_rate=0x%08x, header[8]=%02x, [9]=%02x, [10]=%02x\n", + bit_rate, mph->bSequenceHeader[8], mph->bSequenceHeader[9], mph->bSequenceHeader[10]); + if (bit_rate == 0x3ffff) + /* variable bit rate */ + mph->hdr.dwBitRate = 0; + else if (bit_rate) + mph->hdr.dwBitRate = bit_rate * 400; + else + goto error; + + bih = &mph->hdr.bmiHeader; + bih->biSize = sizeof(*bih); + bih->biWidth = GST_VIDEO_INFO_WIDTH(&info); + bih->biHeight = GST_VIDEO_INFO_HEIGHT(&info); + pel_aspect_ratio = mph->bSequenceHeader[7] >> 4; + if (!pel_aspect_ratio) + goto error; + bih->biXPelsPerMeter = aspect_ratio_table[pel_aspect_ratio]; + bih->biYPelsPerMeter = 2000; + + strmbase_dump_media_type(mt); + + return TRUE; + +error: + FreeMediaType(mt); + return FALSE; +} + static gboolean amt_from_gst_caps(const GstCaps *caps, AM_MEDIA_TYPE *mt) { const char *type = gst_structure_get_name(gst_caps_get_structure(caps, 0)); @@ -326,6 +437,8 @@ static gboolean amt_from_gst_caps(const GstCaps *caps, AM_MEDIA_TYPE *mt) } else if (!strcmp(type, "audio/mpeg")) return amt_from_gst_caps_audio_mpeg(caps, mt); + else if (!strcmp(type, "video/mpeg")) + return amt_from_gst_caps_video_mpeg(caps, mt); else if (!strcmp(type, "video/x-cinepak")) { VIDEOINFOHEADER *vih; @@ -2544,11 +2657,11 @@ 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_MPEG1Video)) return S_OK; - if (IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_MPEG1Video) - || IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_MPEG1System) - || IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_MPEG1VideoCD)) + if (IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_MPEG1System) + || IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_MPEG1VideoCD)) FIXME("Unsupported subtype %s.\n", wine_dbgstr_guid(&mt->subtype)); return S_FALSE; } @@ -2588,19 +2701,40 @@ static const struct strmbase_sink_ops mpeg_splitter_sink_ops = static BOOL mpeg_splitter_init_gst(struct gstdemux *filter, const AM_MEDIA_TYPE *mt) { - static const WCHAR source_name[] = {'A','u','d','i','o',0}; + static const WCHAR AudioW[] = {'A','u','d','i','o',0}; + static const WCHAR VideoW[] = {'V','i','d','e','o',0}; struct gstdemux_source *pin; + const WCHAR *source_name; GstElement *element; HANDLE events[3]; DWORD res; int ret; - if (!(element = gst_element_factory_make("mpegaudioparse", NULL))) + TRACE("(%p, %p)\n", filter, mt); + strmbase_dump_media_type(mt); + + if (IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_MPEG1Audio)) { - ERR("Failed to create mpegaudioparse; are %u-bit GStreamer \"good\" plugins installed?\n", + if (!(element = gst_element_factory_make("mpegaudioparse", NULL))) + { + ERR("Failed to create mpegaudioparse; are %u-bit GStreamer \"good\" plugins installed?\n", 8 * (int)sizeof(void*)); - return FALSE; + return FALSE; + } + source_name = AudioW; } + else if (IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_MPEG1Video)) + { + if (!(element = gst_element_factory_make("mpegvideoparse", NULL))) + { + ERR("Failed to create mpegvideoparse; are %u-bit GStreamer \"bad\" plugins installed?\n", + 8 * (int)sizeof(void*)); + return FALSE; + } + source_name = VideoW; + } + else + return FALSE; gst_bin_add(GST_BIN(filter->container), element);