[PATCH 2/4] winegstreamer: Introduce an intermediate media format structure.

Zebediah Figura z.figura12 at gmail.com
Mon Feb 8 15:06:59 CST 2021


The fundamental idea here is to provide a type which can be used in the unixlib
interface. Obviously GstCaps can't be used from PE, and while AM_MEDIA_TYPE can
in theory be used from the Unix library, allocation of the format block makes
things a little tricky. Moreover, we'd ideally like to use the same backend for
DirectShow and Media Foundation, and while it wouldn't be a problem currently,
in general AM_MEDIA_TYPE is not quite expressive enough to translate from
GstCaps to IMFMediaType, and the latter can't be used from the Unix library.

Signed-off-by: Zebediah Figura <z.figura12 at gmail.com>
---
 dlls/winegstreamer/gst_private.h |  60 ++++
 dlls/winegstreamer/gstdemux.c    | 516 +++++++++++++++++++++++++------
 2 files changed, 486 insertions(+), 90 deletions(-)

diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h
index b6942e5ae77..adef709d8fa 100644
--- a/dlls/winegstreamer/gst_private.h
+++ b/dlls/winegstreamer/gst_private.h
@@ -66,6 +66,66 @@ static inline const char *debugstr_time(REFERENCE_TIME time)
 
 #define MEDIATIME_FROM_BYTES(x) ((LONGLONG)(x) * 10000000)
 
+struct wg_format
+{
+    enum wg_major_type
+    {
+        WG_MAJOR_TYPE_UNKNOWN,
+        WG_MAJOR_TYPE_VIDEO,
+        WG_MAJOR_TYPE_AUDIO,
+    } major_type;
+
+    union
+    {
+        struct
+        {
+            enum wg_video_format
+            {
+                WG_VIDEO_FORMAT_UNKNOWN,
+
+                WG_VIDEO_FORMAT_BGRA,
+                WG_VIDEO_FORMAT_BGRx,
+                WG_VIDEO_FORMAT_BGR,
+                WG_VIDEO_FORMAT_RGB15,
+                WG_VIDEO_FORMAT_RGB16,
+
+                WG_VIDEO_FORMAT_AYUV,
+                WG_VIDEO_FORMAT_I420,
+                WG_VIDEO_FORMAT_NV12,
+                WG_VIDEO_FORMAT_UYVY,
+                WG_VIDEO_FORMAT_YUY2,
+                WG_VIDEO_FORMAT_YV12,
+                WG_VIDEO_FORMAT_YVYU,
+
+                WG_VIDEO_FORMAT_CINEPAK,
+            } format;
+            uint32_t width, height;
+            uint32_t fps_n, fps_d;
+        } video;
+        struct
+        {
+            enum wg_audio_format
+            {
+                WG_AUDIO_FORMAT_UNKNOWN,
+
+                WG_AUDIO_FORMAT_U8,
+                WG_AUDIO_FORMAT_S16LE,
+                WG_AUDIO_FORMAT_S24LE,
+                WG_AUDIO_FORMAT_S32LE,
+                WG_AUDIO_FORMAT_F32LE,
+                WG_AUDIO_FORMAT_F64LE,
+
+                WG_AUDIO_FORMAT_MPEG1_LAYER1,
+                WG_AUDIO_FORMAT_MPEG1_LAYER2,
+                WG_AUDIO_FORMAT_MPEG1_LAYER3,
+            } format;
+
+            uint32_t channels;
+            uint32_t rate;
+        } audio;
+    } u;
+};
+
 extern LONG object_locks;
 
 HRESULT avi_splitter_create(IUnknown *outer, IUnknown **out) DECLSPEC_HIDDEN;
diff --git a/dlls/winegstreamer/gstdemux.c b/dlls/winegstreamer/gstdemux.c
index c05b1e24018..96405f89e53 100644
--- a/dlls/winegstreamer/gstdemux.c
+++ b/dlls/winegstreamer/gstdemux.c
@@ -45,6 +45,7 @@ GST_DEBUG_CATEGORY_STATIC(wine);
 #define GST_CAT_DEFAULT wine
 
 static const GUID MEDIASUBTYPE_CVID = {mmioFOURCC('c','v','i','d'), 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
+static const GUID MEDIASUBTYPE_MP3  = {WAVE_FORMAT_MPEGLAYER3, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
 
 struct wg_parser
 {
@@ -317,133 +318,468 @@ static gboolean amt_from_gst_video_info(const GstVideoInfo *info, AM_MEDIA_TYPE
     return TRUE;
 }
 
-static gboolean amt_from_gst_caps_audio_mpeg(const GstCaps *caps, AM_MEDIA_TYPE *mt)
+static enum wg_audio_format wg_audio_format_from_gst(GstAudioFormat format)
 {
-    GstStructure *structure = gst_caps_get_structure(caps, 0);
-    gint layer, channels, rate;
+    switch (format)
+    {
+        case GST_AUDIO_FORMAT_U8:
+            return WG_AUDIO_FORMAT_U8;
+        case GST_AUDIO_FORMAT_S16LE:
+            return WG_AUDIO_FORMAT_S16LE;
+        case GST_AUDIO_FORMAT_S24LE:
+            return WG_AUDIO_FORMAT_S24LE;
+        case GST_AUDIO_FORMAT_S32LE:
+            return WG_AUDIO_FORMAT_S32LE;
+        case GST_AUDIO_FORMAT_F32LE:
+            return WG_AUDIO_FORMAT_F32LE;
+        case GST_AUDIO_FORMAT_F64LE:
+            return WG_AUDIO_FORMAT_F64LE;
+        default:
+            return WG_AUDIO_FORMAT_UNKNOWN;
+    }
+}
 
-    mt->majortype = MEDIATYPE_Audio;
-    mt->subtype = MEDIASUBTYPE_MPEG1AudioPayload;
-    mt->bFixedSizeSamples = FALSE;
-    mt->bTemporalCompression = FALSE;
-    mt->lSampleSize = 0;
-    mt->formattype = FORMAT_WaveFormatEx;
-    mt->pUnk = NULL;
+static void wg_format_from_audio_info(struct wg_format *format, const GstAudioInfo *info)
+{
+    format->major_type = WG_MAJOR_TYPE_AUDIO;
+    format->u.audio.format = wg_audio_format_from_gst(GST_AUDIO_INFO_FORMAT(info));
+    format->u.audio.channels = GST_AUDIO_INFO_CHANNELS(info);
+    format->u.audio.rate = GST_AUDIO_INFO_RATE(info);
+}
+
+static enum wg_video_format wg_video_format_from_gst(GstVideoFormat format)
+{
+    switch (format)
+    {
+        case GST_VIDEO_FORMAT_BGRA:
+            return WG_VIDEO_FORMAT_BGRA;
+        case GST_VIDEO_FORMAT_BGRx:
+            return WG_VIDEO_FORMAT_BGRx;
+        case GST_VIDEO_FORMAT_BGR:
+            return WG_VIDEO_FORMAT_BGR;
+        case GST_VIDEO_FORMAT_RGB15:
+            return WG_VIDEO_FORMAT_RGB15;
+        case GST_VIDEO_FORMAT_AYUV:
+            return WG_VIDEO_FORMAT_AYUV;
+        case GST_VIDEO_FORMAT_I420:
+            return WG_VIDEO_FORMAT_I420;
+        case GST_VIDEO_FORMAT_NV12:
+            return WG_VIDEO_FORMAT_NV12;
+        case GST_VIDEO_FORMAT_UYVY:
+            return WG_VIDEO_FORMAT_UYVY;
+        case GST_VIDEO_FORMAT_YUY2:
+            return WG_VIDEO_FORMAT_YUY2;
+        case GST_VIDEO_FORMAT_YV12:
+            return WG_VIDEO_FORMAT_YV12;
+        case GST_VIDEO_FORMAT_YVYU:
+            return WG_VIDEO_FORMAT_YVYU;
+        default:
+            return WG_VIDEO_FORMAT_UNKNOWN;
+    }
+}
+
+static void wg_format_from_video_info(struct wg_format *format, const GstVideoInfo *info)
+{
+    format->major_type = WG_MAJOR_TYPE_VIDEO;
+    format->u.video.format = wg_video_format_from_gst(GST_VIDEO_INFO_FORMAT(info));
+    format->u.video.width = GST_VIDEO_INFO_WIDTH(info);
+    format->u.video.height = GST_VIDEO_INFO_HEIGHT(info);
+    format->u.video.fps_n = GST_VIDEO_INFO_FPS_N(info);
+    format->u.video.fps_d = GST_VIDEO_INFO_FPS_D(info);
+}
+
+static void wg_format_from_caps_audio_mpeg(struct wg_format *format, const GstCaps *caps)
+{
+    const GstStructure *structure = gst_caps_get_structure(caps, 0);
+    gint layer, channels, rate;
 
     if (!gst_structure_get_int(structure, "layer", &layer))
     {
-        WARN("Missing 'layer' value.\n");
-        return FALSE;
+        GST_WARNING("Missing \"layer\" value.");
+        return;
     }
     if (!gst_structure_get_int(structure, "channels", &channels))
     {
-        WARN("Missing 'channels' value.\n");
-        return FALSE;
+        GST_WARNING("Missing \"channels\" value.");
+        return;
     }
     if (!gst_structure_get_int(structure, "rate", &rate))
     {
-        WARN("Missing 'rate' value.\n");
-        return FALSE;
+        GST_WARNING("Missing \"rate\" value.");
+        return;
+    }
+
+    format->major_type = WG_MAJOR_TYPE_AUDIO;
+
+    if (layer == 1)
+        format->u.audio.format = WG_AUDIO_FORMAT_MPEG1_LAYER1;
+    else if (layer == 2)
+        format->u.audio.format = WG_AUDIO_FORMAT_MPEG1_LAYER2;
+    else if (layer == 3)
+        format->u.audio.format = WG_AUDIO_FORMAT_MPEG1_LAYER3;
+
+    format->u.audio.channels = channels;
+    format->u.audio.rate = rate;
+}
+
+static void wg_format_from_caps_video_cinepak(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.");
+        return;
+    }
+    if (!gst_structure_get_int(structure, "height", &height))
+    {
+        GST_WARNING("Missing \"height\" value.");
+        return;
+    }
+    if (!gst_structure_get_fraction(structure, "framerate", &fps_n, &fps_d))
+    {
+        fps_n = 0;
+        fps_d = 1;
     }
 
-    if (layer == 3)
+    format->major_type = WG_MAJOR_TYPE_VIDEO;
+    format->u.video.format = WG_VIDEO_FORMAT_CINEPAK;
+    format->u.video.width = width;
+    format->u.video.height = height;
+    format->u.video.fps_n = fps_n;
+    format->u.video.fps_d = fps_d;
+}
+
+static 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);
+
+    memset(format, 0, sizeof(*format));
+
+    if (!strcmp(name, "audio/x-raw"))
     {
-        MPEGLAYER3WAVEFORMAT *wfx = CoTaskMemAlloc(sizeof(*wfx));
-        memset(wfx, 0, sizeof(*wfx));
+        GstAudioInfo info;
 
-        mt->subtype.Data1 = WAVE_FORMAT_MPEGLAYER3;
-        mt->cbFormat = sizeof(*wfx);
-        mt->pbFormat = (BYTE *)wfx;
-        wfx->wfx.wFormatTag = WAVE_FORMAT_MPEGLAYER3;
-        wfx->wfx.nChannels = channels;
-        wfx->wfx.nSamplesPerSec = rate;
-        /* FIXME: We can't get most of the MPEG data from the caps. We may have
-         * to manually parse the header. */
-        wfx->wfx.cbSize = sizeof(*wfx) - sizeof(WAVEFORMATEX);
-        wfx->wID = MPEGLAYER3_ID_MPEG;
-        wfx->fdwFlags = MPEGLAYER3_FLAG_PADDING_ON;
-        wfx->nFramesPerBlock = 1;
-        wfx->nCodecDelay = 1393;
+        if (gst_audio_info_from_caps(&info, caps))
+            wg_format_from_audio_info(format, &info);
+    }
+    else if (!strcmp(name, "video/x-raw"))
+    {
+        GstVideoInfo info;
+
+        if (gst_video_info_from_caps(&info, caps))
+            wg_format_from_video_info(format, &info);
+    }
+    else if (!strcmp(name, "audio/mpeg"))
+    {
+        wg_format_from_caps_audio_mpeg(format, caps);
+    }
+    else if (!strcmp(name, "video/x-cinepak"))
+    {
+        wg_format_from_caps_video_cinepak(format, caps);
     }
     else
     {
-        MPEG1WAVEFORMAT *wfx = CoTaskMemAlloc(sizeof(*wfx));
-        memset(wfx, 0, sizeof(*wfx));
+        gchar *str = gst_caps_to_string(caps);
 
-        mt->subtype.Data1 = WAVE_FORMAT_MPEG;
-        mt->cbFormat = sizeof(*wfx);
-        mt->pbFormat = (BYTE *)wfx;
-        wfx->wfx.wFormatTag = WAVE_FORMAT_MPEG;
-        wfx->wfx.nChannels = channels;
-        wfx->wfx.nSamplesPerSec = rate;
-        wfx->wfx.cbSize = sizeof(*wfx) - sizeof(WAVEFORMATEX);
-        wfx->fwHeadLayer = layer;
+        GST_FIXME("Unhandled caps %s.", str);
+        g_free(str);
     }
+}
 
-    return TRUE;
+static DWORD channel_mask_from_count(uint32_t count)
+{
+    switch (count)
+    {
+        case 1: return KSAUDIO_SPEAKER_MONO;
+        case 2: return KSAUDIO_SPEAKER_STEREO;
+        case 4: return KSAUDIO_SPEAKER_SURROUND;
+        case 5: return KSAUDIO_SPEAKER_5POINT1 & ~SPEAKER_LOW_FREQUENCY;
+        case 6: return KSAUDIO_SPEAKER_5POINT1;
+        case 8: return KSAUDIO_SPEAKER_7POINT1;
+        default: return 0;
+    }
 }
 
-static gboolean amt_from_gst_caps(const GstCaps *caps, AM_MEDIA_TYPE *mt)
+static bool amt_from_wg_format_audio(AM_MEDIA_TYPE *mt, const struct wg_format *format)
 {
-    const char *type = gst_structure_get_name(gst_caps_get_structure(caps, 0));
-    GstStructure *structure = gst_caps_get_structure(caps, 0);
+    mt->majortype = MEDIATYPE_Audio;
+    mt->formattype = FORMAT_WaveFormatEx;
 
-    memset(mt, 0, sizeof(AM_MEDIA_TYPE));
+    switch (format->u.audio.format)
+    {
+    case WG_AUDIO_FORMAT_UNKNOWN:
+        return false;
 
-    if (!strcmp(type, "audio/x-raw"))
+    case WG_AUDIO_FORMAT_MPEG1_LAYER1:
+    case WG_AUDIO_FORMAT_MPEG1_LAYER2:
     {
-        GstAudioInfo info;
+        MPEG1WAVEFORMAT *wave_format;
 
-        if (!(gst_audio_info_from_caps(&info, caps)))
-            return FALSE;
-        return amt_from_gst_audio_info(&info, mt);
+        if (!(wave_format = CoTaskMemAlloc(sizeof(*wave_format))))
+            return false;
+        memset(wave_format, 0, sizeof(*wave_format));
+
+        mt->subtype = MEDIASUBTYPE_MPEG1AudioPayload;
+        mt->cbFormat = sizeof(*wave_format);
+        mt->pbFormat = (BYTE *)wave_format;
+        wave_format->wfx.wFormatTag = WAVE_FORMAT_MPEG;
+        wave_format->wfx.nChannels = format->u.audio.channels;
+        wave_format->wfx.nSamplesPerSec = format->u.audio.rate;
+        wave_format->wfx.cbSize = sizeof(*wave_format) - sizeof(WAVEFORMATEX);
+        wave_format->fwHeadLayer = (format->u.audio.format == WG_AUDIO_FORMAT_MPEG1_LAYER1 ? 1 : 2);
+        return true;
     }
-    else if (!strcmp(type, "video/x-raw"))
+
+    case WG_AUDIO_FORMAT_MPEG1_LAYER3:
     {
-        GstVideoInfo info;
+        MPEGLAYER3WAVEFORMAT *wave_format;
 
-        if (!gst_video_info_from_caps(&info, caps))
-            return FALSE;
-        return amt_from_gst_video_info(&info, mt);
+        if (!(wave_format = CoTaskMemAlloc(sizeof(*wave_format))))
+            return false;
+        memset(wave_format, 0, sizeof(*wave_format));
+
+        mt->subtype = MEDIASUBTYPE_MP3;
+        mt->cbFormat = sizeof(*wave_format);
+        mt->pbFormat = (BYTE *)wave_format;
+        wave_format->wfx.wFormatTag = WAVE_FORMAT_MPEGLAYER3;
+        wave_format->wfx.nChannels = format->u.audio.channels;
+        wave_format->wfx.nSamplesPerSec = format->u.audio.rate;
+        wave_format->wfx.cbSize = sizeof(*wave_format) - sizeof(WAVEFORMATEX);
+        /* FIXME: We can't get most of the MPEG data from the caps. We may have
+         * to manually parse the header. */
+        wave_format->wID = MPEGLAYER3_ID_MPEG;
+        wave_format->fdwFlags = MPEGLAYER3_FLAG_PADDING_ON;
+        wave_format->nFramesPerBlock = 1;
+        wave_format->nCodecDelay = 1393;
+        return true;
+    }
+
+    case WG_AUDIO_FORMAT_U8:
+    case WG_AUDIO_FORMAT_S16LE:
+    case WG_AUDIO_FORMAT_S24LE:
+    case WG_AUDIO_FORMAT_S32LE:
+    case WG_AUDIO_FORMAT_F32LE:
+    case WG_AUDIO_FORMAT_F64LE:
+    {
+        static const struct
+        {
+            bool is_float;
+            WORD depth;
+        }
+        format_table[] =
+        {
+            {0},
+            {false, 8},
+            {false, 16},
+            {false, 24},
+            {false, 32},
+            {true, 32},
+            {true, 64},
+        };
+
+        bool is_float;
+        WORD depth;
+
+        assert(format->u.audio.format < ARRAY_SIZE(format_table));
+        is_float = format_table[format->u.audio.format].is_float;
+        depth = format_table[format->u.audio.format].depth;
+
+        if (is_float || format->u.audio.channels > 2)
+        {
+            WAVEFORMATEXTENSIBLE *wave_format;
+
+            if (!(wave_format = CoTaskMemAlloc(sizeof(*wave_format))))
+                return false;
+            memset(wave_format, 0, sizeof(*wave_format));
+
+            mt->subtype = is_float ? MEDIASUBTYPE_IEEE_FLOAT : MEDIASUBTYPE_PCM;
+            mt->bFixedSizeSamples = TRUE;
+            mt->pbFormat = (BYTE *)wave_format;
+            mt->cbFormat = sizeof(*wave_format);
+            wave_format->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+            wave_format->Format.nChannels = format->u.audio.channels;
+            wave_format->Format.nSamplesPerSec = format->u.audio.rate;
+            wave_format->Format.nAvgBytesPerSec = format->u.audio.rate * format->u.audio.channels * depth / 8;
+            wave_format->Format.nBlockAlign = format->u.audio.channels * depth / 8;
+            wave_format->Format.wBitsPerSample = depth;
+            wave_format->Format.cbSize = sizeof(*wave_format) - sizeof(WAVEFORMATEX);
+            wave_format->Samples.wValidBitsPerSample = depth;
+            wave_format->dwChannelMask = channel_mask_from_count(format->u.audio.channels);
+            wave_format->SubFormat = is_float ? KSDATAFORMAT_SUBTYPE_IEEE_FLOAT : KSDATAFORMAT_SUBTYPE_PCM;
+            mt->lSampleSize = wave_format->Format.nBlockAlign;
+        }
+        else
+        {
+            WAVEFORMATEX *wave_format;
+
+            if (!(wave_format = CoTaskMemAlloc(sizeof(*wave_format))))
+                return false;
+            memset(wave_format, 0, sizeof(*wave_format));
+
+            mt->subtype = MEDIASUBTYPE_PCM;
+            mt->bFixedSizeSamples = TRUE;
+            mt->pbFormat = (BYTE *)wave_format;
+            mt->cbFormat = sizeof(*wave_format);
+            wave_format->wFormatTag = WAVE_FORMAT_PCM;
+            wave_format->nChannels = format->u.audio.channels;
+            wave_format->nSamplesPerSec = format->u.audio.rate;
+            wave_format->nAvgBytesPerSec = format->u.audio.rate * format->u.audio.channels * depth / 8;
+            wave_format->nBlockAlign = format->u.audio.channels * depth / 8;
+            wave_format->wBitsPerSample = depth;
+            wave_format->cbSize = 0;
+            mt->lSampleSize = wave_format->nBlockAlign;
+        }
+        return true;
+    }
     }
-    else if (!strcmp(type, "audio/mpeg"))
-        return amt_from_gst_caps_audio_mpeg(caps, mt);
-    else if (!strcmp(type, "video/x-cinepak"))
+
+    assert(0);
+    return false;
+}
+
+#define ALIGN(n, alignment) (((n) + (alignment) - 1) & ~((alignment) - 1))
+
+static unsigned int get_image_size(const struct wg_format *format)
+{
+    unsigned int width = format->u.video.width, height = format->u.video.height;
+
+    switch (format->u.video.format)
     {
-        VIDEOINFOHEADER *vih;
-        gint i;
+        case WG_VIDEO_FORMAT_BGRA:
+        case WG_VIDEO_FORMAT_BGRx:
+        case WG_VIDEO_FORMAT_AYUV:
+            return width * height * 4;
 
-        mt->majortype = MEDIATYPE_Video;
-        mt->subtype = MEDIASUBTYPE_CVID;
-        mt->bTemporalCompression = TRUE;
-        mt->lSampleSize = 1;
-        mt->formattype = FORMAT_VideoInfo;
-        if (!(vih = CoTaskMemAlloc(sizeof(VIDEOINFOHEADER))))
-            return FALSE;
-        mt->cbFormat = sizeof(VIDEOINFOHEADER);
-        mt->pbFormat = (BYTE *)vih;
-
-        memset(vih, 0, sizeof(VIDEOINFOHEADER));
-        vih->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
-        if (gst_structure_get_int(structure, "width", &i))
-            vih->bmiHeader.biWidth = i;
-        if (gst_structure_get_int(structure, "height", &i))
-            vih->bmiHeader.biHeight = i;
-        vih->bmiHeader.biPlanes = 1;
-        /* Both ffmpeg's encoder and a Cinepak file seen in the wild report
-         * 24 bpp. ffmpeg sets biSizeImage as below; others may be smaller, but
-         * as long as every sample fits into our allocator, we're fine. */
-        vih->bmiHeader.biBitCount = 24;
-        vih->bmiHeader.biCompression = mmioFOURCC('c','v','i','d');
-        vih->bmiHeader.biSizeImage = vih->bmiHeader.biWidth
-                * vih->bmiHeader.biHeight * vih->bmiHeader.biBitCount / 8;
-        return TRUE;
+        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_CINEPAK:
+            /* Both ffmpeg's encoder and a Cinepak file seen in the wild report
+             * 24 bpp. ffmpeg sets biSizeImage as below; others may be smaller,
+             * but as long as every sample fits into our allocator, we're fine. */
+            return width * height * 3;
+
+        case WG_VIDEO_FORMAT_UNKNOWN:
+            break;
     }
-    else
+
+    assert(0);
+    return 0;
+}
+
+static bool amt_from_wg_format_video(AM_MEDIA_TYPE *mt, const struct wg_format *format)
+{
+    static const struct
     {
-        FIXME("Unhandled type %s.\n", debugstr_a(type));
-        return FALSE;
+        const GUID *subtype;
+        DWORD compression;
+        WORD depth;
+    }
+    format_table[] =
+    {
+        {0},
+        {&MEDIASUBTYPE_ARGB32, BI_RGB,                      32},
+        {&MEDIASUBTYPE_RGB32,  BI_RGB,                      32},
+        {&MEDIASUBTYPE_RGB24,  BI_RGB,                      24},
+        {&MEDIASUBTYPE_RGB555, BI_RGB,                      16},
+        {&MEDIASUBTYPE_RGB565, BI_BITFIELDS,                16},
+        {&MEDIASUBTYPE_AYUV,   mmioFOURCC('A','Y','U','V'), 32},
+        {&MEDIASUBTYPE_I420,   mmioFOURCC('I','4','2','0'), 12},
+        {&MEDIASUBTYPE_NV12,   mmioFOURCC('N','V','1','2'), 12},
+        {&MEDIASUBTYPE_UYVY,   mmioFOURCC('U','Y','V','Y'), 16},
+        {&MEDIASUBTYPE_YUY2,   mmioFOURCC('Y','U','Y','2'), 16},
+        {&MEDIASUBTYPE_YV12,   mmioFOURCC('Y','V','1','2'), 12},
+        {&MEDIASUBTYPE_YVYU,   mmioFOURCC('Y','V','Y','U'), 16},
+        {&MEDIASUBTYPE_CVID,   mmioFOURCC('C','V','I','D'), 24},
+    };
+
+    VIDEOINFO *video_format;
+    uint32_t frame_time;
+
+    if (format->u.video.format == WG_VIDEO_FORMAT_UNKNOWN)
+        return false;
+
+    if (!(video_format = CoTaskMemAlloc(sizeof(*video_format))))
+        return false;
+
+    assert(format->u.video.format < ARRAY_SIZE(format_table));
+
+    mt->majortype = MEDIATYPE_Video;
+    mt->subtype = *format_table[format->u.video.format].subtype;
+    mt->bTemporalCompression = TRUE;
+    mt->lSampleSize = 1;
+    mt->formattype = FORMAT_VideoInfo;
+    mt->cbFormat = sizeof(VIDEOINFOHEADER);
+    mt->pbFormat = (BYTE *)video_format;
+
+    memset(video_format, 0, sizeof(*video_format));
+
+    if ((frame_time = MulDiv(10000000, format->u.video.fps_d, format->u.video.fps_n)) != -1)
+        video_format->AvgTimePerFrame = frame_time;
+    video_format->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+    video_format->bmiHeader.biWidth = format->u.video.width;
+    video_format->bmiHeader.biHeight = format->u.video.height;
+    video_format->bmiHeader.biPlanes = 1;
+    video_format->bmiHeader.biBitCount = format_table[format->u.video.format].depth;
+    video_format->bmiHeader.biCompression = format_table[format->u.video.format].compression;
+    video_format->bmiHeader.biSizeImage = get_image_size(format);
+
+    if (format->u.video.format == WG_VIDEO_FORMAT_RGB16)
+    {
+        mt->cbFormat = offsetof(VIDEOINFO, u.dwBitMasks[3]);
+        video_format->u.dwBitMasks[iRED]   = 0xf800;
+        video_format->u.dwBitMasks[iGREEN] = 0x07e0;
+        video_format->u.dwBitMasks[iBLUE]  = 0x001f;
+    }
+
+    return true;
+}
+
+static bool amt_from_wg_format(AM_MEDIA_TYPE *mt, const struct wg_format *format)
+{
+    memset(mt, 0, sizeof(*mt));
+
+    switch (format->major_type)
+    {
+    case WG_MAJOR_TYPE_UNKNOWN:
+        return false;
+
+    case WG_MAJOR_TYPE_AUDIO:
+        return amt_from_wg_format_audio(mt, format);
+
+    case WG_MAJOR_TYPE_VIDEO:
+        return amt_from_wg_format_video(mt, format);
     }
+
+    assert(0);
+    return false;
+}
+
+static bool amt_from_gst_caps(const GstCaps *caps, AM_MEDIA_TYPE *mt)
+{
+    struct wg_format wg_format;
+
+    wg_format_from_caps(&wg_format, caps);
+    return amt_from_wg_format(mt, &wg_format);
 }
 
 static GstCaps *amt_to_gst_caps_video(const AM_MEDIA_TYPE *mt)
-- 
2.30.0




More information about the wine-devel mailing list