[QUARTZ] Add FFMpeg video wrapper filter

Christian Costa titan.costa at wanadoo.fr
Thu Jun 9 18:19:06 CDT 2005


Hi,

This wrapper only support rle, msvideo1 and mjpeg for now.
To enable support for it, download lastest FFMpeg package, built it and 
install it.
Also copy avcodec.h, rational.h and common.h into a ffmpeg directory 
created in a
standard directory (/usr/include or /usr/local/include).
Once done, run configure to detect the library and the headers.
The library is static so there is no runtime dependency.

Changelog:
Added FFMpeg video wrapper filter.

Christian Costa   titan.costa at wanadoo.fr

-------------- next part --------------
Index: configure.ac
===================================================================
RCS file: /home/wine/wine/configure.ac,v
retrieving revision 1.362
diff -u -r1.362 configure.ac
--- configure.ac	9 Jun 2005 10:21:31 -0000	1.362
+++ configure.ac	9 Jun 2005 21:54:16 -0000
@@ -541,6 +541,14 @@
 AC_SUBST(FREETYPELIBS)
 AC_SUBST(FREETYPEINCL)
 
+dnl **** Check for avcodec lib ****
+AVCODECLIBS=""
+    AC_CHECK_HEADERS(ffmpeg/avcodec.h,
+      [AC_CHECK_LIB(avcodec,avcodec_open,
+          [AC_DEFINE(HAVE_LIBAVCODEC, 1, [Define if you have the avcodec library (-lavcodec)])
+           AVCODECLIBS="-lavcodec -lz -lm"],,-lz -lm)])
+AC_SUBST(AVCODECLIBS)
+
 dnl Only build the fonts dir if we have both freetype and fontforge
 if test "$FONTFORGE" != "false" -a -n "$FREETYPELIBS"
 then
Index: dlls/quartz/Makefile.in
===================================================================
RCS file: /home/wine/wine/dlls/quartz/Makefile.in,v
retrieving revision 1.45
diff -u -r1.45 Makefile.in
--- dlls/quartz/Makefile.in	9 May 2005 14:42:32 -0000	1.45
+++ dlls/quartz/Makefile.in	9 Jun 2005 21:54:21 -0000
@@ -5,7 +5,7 @@
 MODULE    = quartz.dll
 IMPORTLIB = libquartz.$(IMPLIBEXT)
 IMPORTS   = dsound ddraw msacm32 msvfw32 ole32 oleaut32 user32 advapi32 kernel32
-EXTRALIBS = -lstrmiids -luuid $(LIBUNICODE)
+EXTRALIBS = -lstrmiids -luuid $(LIBUNICODE) @AVCODECLIBS@
 
 C_SRCS = \
 	acmwrapper.c \
@@ -18,6 +18,7 @@
 	enummoniker.c \
 	enumpins.c \
 	enumregfilters.c \
+	ffmpeg_video_wrapper.c \
 	filesource.c \
 	filtergraph.c \
 	filtermapper.c \
Index: dlls/quartz/quartz_private.h
===================================================================
RCS file: /home/wine/wine/dlls/quartz/quartz_private.h,v
retrieving revision 1.24
diff -u -r1.24 quartz_private.h
--- dlls/quartz/quartz_private.h	6 May 2005 14:34:02 -0000	1.24
+++ dlls/quartz/quartz_private.h	9 Jun 2005 21:54:23 -0000
@@ -53,6 +53,9 @@
 HRESULT QUARTZ_CreateSystemClock(IUnknown * pUnkOuter, LPVOID * ppv);
 HRESULT ACMWrapper_create(IUnknown * pUnkOuter, LPVOID * ppv);
 HRESULT WAVEParser_create(IUnknown * pUnkOuter, LPVOID * ppv);
+#ifdef HAVE_LIBAVCODEC
+HRESULT FFMpegVideoWrapper_create(IUnknown * pUnkOuter, LPVOID * ppv);
+#endif
 
 HRESULT EnumMonikerImpl_Create(IMoniker ** ppMoniker, ULONG nMonikerCount, IEnumMoniker ** ppEnum);
 
@@ -81,5 +84,9 @@
 void DeleteMediaType(AM_MEDIA_TYPE * pmt);
 BOOL CompareMediaTypes(const AM_MEDIA_TYPE * pmt1, const AM_MEDIA_TYPE * pmt2, BOOL bWildcards);
 void dump_AM_MEDIA_TYPE(const AM_MEDIA_TYPE * pmt);
+
+#ifdef HAVE_LIBAVCODEC
+extern CLSID CLSID_FFMpegVideoWrapper;
+#endif
 
 #endif /* __QUARTZ_PRIVATE_INCLUDED__ */
Index: dlls/quartz/regsvr.c
===================================================================
RCS file: /home/wine/wine/dlls/quartz/regsvr.c,v
retrieving revision 1.21
diff -u -r1.21 regsvr.c
--- dlls/quartz/regsvr.c	17 May 2005 14:41:37 -0000	1.21
+++ dlls/quartz/regsvr.c	9 Jun 2005 21:54:36 -0000
@@ -18,6 +18,8 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+#include "config.h"
+
 #define NONAMELESSUNION
 #define NONAMELESSSTRUCT
 #define COBJMACROS
@@ -840,6 +842,10 @@
     return res;
 }
 
+#ifdef HAVE_LIBAVCODEC
+CLSID CLSID_FFMpegVideoWrapper = { 0x30355649, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x72 } };
+#endif
+
 /***********************************************************************
  *		coclass list
  */
@@ -928,6 +934,14 @@
 	"quartz.dll",
 	"Both"
     },
+#ifdef HAVE_LIBAVCODEC
+    {   &CLSID_FFMpegVideoWrapper,
+	"FFMpeg Video Wrapper",
+	NULL,
+	"quartz.dll",
+	"Both"
+    },
+#endif
     { NULL }			/* list terminator */
 };
 
@@ -1118,6 +1132,26 @@
 	    { 0xFFFFFFFF },
 	}
     },
+    /* Wine specific filters */
+#ifdef HAVE_LIBAVCODEC
+    {   &CLSID_FFMpegVideoWrapper,
+	&CLSID_LegacyAmFilterCategory,
+	{'F','F','M','p','e','g',' ','V','i','d','e','o',' ','W','r','a','p','p','e','r',0},
+	0x5FFFFF, /* Just below AVIDec */
+	{   {   0,
+		{   { &MEDIATYPE_Video, &GUID_NULL },
+		    { NULL }
+		},
+	    },
+	    {   REG_PINFLAG_B_OUTPUT,
+		{   { &MEDIATYPE_Video, &GUID_NULL },
+		    { NULL }
+		},
+	    },
+	    { 0xFFFFFFFF },
+	}
+    },
+#endif
     { NULL }		/* list terminator */
 };
 
Index: dlls/quartz/main.c
===================================================================
RCS file: /home/wine/wine/dlls/quartz/main.c,v
retrieving revision 1.47
diff -u -r1.47 main.c
--- dlls/quartz/main.c	6 Jun 2005 19:50:36 -0000	1.47
+++ dlls/quartz/main.c	9 Jun 2005 21:54:37 -0000
@@ -72,7 +72,10 @@
     { &CLSID_AVIDec, AVIDec_create },
     { &CLSID_SystemClock, &QUARTZ_CreateSystemClock },
     { &CLSID_ACMWrapper, &ACMWrapper_create },
-    { &CLSID_WAVEParser, &WAVEParser_create }
+    { &CLSID_WAVEParser, &WAVEParser_create },
+#ifdef HAVE_LIBAVCODEC
+    { &CLSID_FFMpegVideoWrapper, &FFMpegVideoWrapper_create },
+#endif
 };
 
 static HRESULT WINAPI
--- /dev/null	1970-01-01 01:00:00.000000000 +0100
+++ dlls/quartz/ffmpeg_video_wrapper.c	2005-06-09 22:55:14.000000000 +0100
@@ -0,0 +1,372 @@
+/*
+ * FFMpeg Video Decompressor (libavcodec video wrapper)
+ *
+ * Copyright 2004-2005 Christian Costa
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "config.h"
+
+#include "quartz_private.h"
+#include "control_private.h"
+#include "pin.h"
+
+#include "uuids.h"
+#include "aviriff.h"
+#include "mmreg.h"
+#include "vfwmsgs.h"
+#include "amvideo.h"
+#include "windef.h"
+#include "winbase.h"
+#include "dshow.h"
+#include "strmif.h"
+#include "vfwmsgs.h"
+#include "evcode.h"
+#include "vfw.h"
+/* #include "fourcc.h" */
+#include <ffmpeg/avcodec.h>
+
+#include <assert.h>
+
+#include "wine/unicode.h"
+#include "wine/debug.h"
+
+#include "transform.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(quartz);
+
+#ifdef HAVE_LIBAVCODEC
+
+#define MAKE_FOURCC(a,b,c,d) ((a<<0)|(b<<8)|(c<<16)|(d<<24))
+
+struct toto {
+    DWORD fourcc;
+    int codec_id;
+};
+
+struct toto list[] = {
+    { MAKE_FOURCC('R','L','E',' '), CODEC_ID_MSRLE },
+    { 1, CODEC_ID_MSRLE },
+    { MAKE_FOURCC('m','s','v','c'), CODEC_ID_MSVIDEO1 },
+    { MAKE_FOURCC('C','R','A','M'), CODEC_ID_MSVIDEO1 },
+    { MAKE_FOURCC('i','v','3','2'), CODEC_ID_INDEO3 },
+    { MAKE_FOURCC('I','V','3','2'), CODEC_ID_INDEO3 },
+    { MAKE_FOURCC('c','v','i','d'), CODEC_ID_CINEPAK },
+    { MAKE_FOURCC('D','I','V','3'), CODEC_ID_MSMPEG4V3 },
+    { MAKE_FOURCC('M','J','P','G'), CODEC_ID_MJPEG },
+    { MAKE_FOURCC('m','j','p','g'), CODEC_ID_MJPEG }
+};
+
+typedef struct FFMpegVideoWrapperImpl
+{
+    TransformFilterImpl tf;
+    BITMAPINFOHEADER* pBihIn;
+    BITMAPINFOHEADER* pBihOut;
+    AVCodec* codec;
+    AVCodecContext* ctx;
+    AVFrame* tmp_pic;
+    AVPaletteControl palctrl;
+    int init;
+} FFMpegVideoWrapperImpl;
+
+static HRESULT FFMpegVideoWrapper_ProcessSampleData(TransformFilterImpl* pTransformFilter, LPBYTE data, DWORD size)
+{
+    FFMpegVideoWrapperImpl* This = (FFMpegVideoWrapperImpl*)pTransformFilter;
+    VIDEOINFOHEADER* format;
+    AM_MEDIA_TYPE amt;
+    HRESULT hr;
+    IMediaSample* pSample = NULL;
+    DWORD cbDstStream;
+    LPBYTE pbDstStream;
+    int nOut, got_pic;
+    int i;
+    AVPicture dst_pic;
+    int ret;
+    BYTE* tmp;
+    int fmt;
+
+    hr = IPin_ConnectionMediaType(This->tf.ppPins[0], &amt);
+    if (FAILED(hr))
+    {
+	ERR("Unable to retrieve media type\n");
+	goto error;
+    }
+    format = (VIDEOINFOHEADER*)amt.pbFormat;
+
+    /* Update input size to match sample size */
+    This->pBihIn->biSizeImage = size;
+
+    hr = OutputPin_GetDeliveryBuffer((OutputPin*)This->tf.ppPins[1], &pSample, NULL, NULL, 0);
+    if (FAILED(hr))
+    {
+	ERR("Unable to get delivery buffer (%lx)\n", hr);
+	goto error;
+    }
+    
+    hr = IMediaSample_SetActualDataLength(pSample, 0);
+    assert(hr == S_OK);
+
+    hr = IMediaSample_GetPointer(pSample, &pbDstStream);
+    if (FAILED(hr))
+    {
+	ERR("Unable to get pointer to buffer (%lx)\n", hr);
+	goto error;
+    }
+    cbDstStream = IMediaSample_GetSize(pSample);
+    if (cbDstStream < This->pBihOut->biSizeImage)
+    {
+        ERR("Sample size is too small %ld < %ld\n", cbDstStream, This->pBihOut->biSizeImage);
+	hr = E_FAIL;
+	goto error;
+    }
+
+    This->ctx->width = This->pBihIn->biWidth;
+    This->ctx->height = This->pBihIn->biHeight;
+    This->ctx->bits_per_sample = This->pBihIn->biBitCount;
+
+    TRACE("Decode %d %d\n", This->tmp_pic->linesize[0], This->pBihOut->biBitCount);
+
+    nOut = avcodec_decode_video( This->ctx, This->tmp_pic, &got_pic, (void*)data, size );
+
+    TRACE("End decode %d\n", This->tmp_pic->linesize[0]);
+
+    if (nOut < 0)
+    {
+	ERR("Decoding error\n");
+	hr = E_FAIL;
+	goto error;
+    }
+    if (!got_pic)
+    {
+	ERR("No pic!!!\n");
+	hr = E_FAIL;
+	goto error;
+    }
+
+    TRACE("used %d,%ld size %d x %d\n", nOut, size, This->ctx->width, This->ctx->height);
+
+    switch(This->pBihOut->biBitCount)
+    {
+        case 32: fmt = PIX_FMT_RGBA32; break;
+        case 24: fmt = PIX_FMT_BGR24; break;
+        case 16: fmt = PIX_FMT_RGB565; break;
+        case 8:  fmt = PIX_FMT_PAL8; break;
+        default: FIXME("Bad depth\n"); fmt = PIX_FMT_RGBA32; break;
+    }
+
+    if (This->pBihOut->biBitCount != 8)
+    {
+        tmp = CoTaskMemAlloc(This->ctx->width*3*This->ctx->height);
+        dst_pic.data[0] = tmp;
+        dst_pic.linesize[0] = This->ctx->width*3;
+        ret = img_convert(&dst_pic, fmt, (AVPicture*)This->tmp_pic, This->ctx->pix_fmt, This->ctx->width, This->ctx->height);
+        if (ret != 0)
+        {
+	    ERR("Cannot convert img!\n");
+            CoTaskMemFree(tmp);
+	    hr = E_FAIL;
+	    goto error;
+        }
+
+        for(i = 0; i < This->ctx->height; i++)
+    	    memcpy(pbDstStream + (This->ctx->height-i-1) * This->ctx->width*This->pBihOut->biBitCount/8,
+                   dst_pic.data[0] + i * dst_pic.linesize[0], This->ctx->width*This->pBihOut->biBitCount/8);
+
+        CoTaskMemFree(tmp);
+    }
+    else
+    {
+        for(i = 0; i < This->ctx->height; i++)
+    	    memcpy(pbDstStream + (This->ctx->height-i-1) * This->ctx->width,
+                   This->tmp_pic->data[0] + i * This->tmp_pic->linesize[0], This->ctx->width);
+    }
+
+    hr = OutputPin_SendSample((OutputPin*)This->tf.ppPins[1], pSample);
+    if (hr != S_OK && hr != VFW_E_NOT_CONNECTED)
+    {
+        ERR("Error sending sample (%lx)\n", hr);
+	goto error;
+    }
+
+error:
+    if (pSample)
+        IMediaSample_Release(pSample);
+
+    return hr;
+}
+
+static HRESULT FFMpegVideoWrapper_ConnectInput(TransformFilterImpl* pTransformFilter, const AM_MEDIA_TYPE * pmt)
+{
+    FFMpegVideoWrapperImpl* This = (FFMpegVideoWrapperImpl*)pTransformFilter;
+    int i;
+
+    TRACE("(%p)->(%p)\n", This, pmt);
+    
+    if ((IsEqualIID(&pmt->majortype, &MEDIATYPE_Video)) &&
+        (!memcmp(((char*)&pmt->subtype)+4, ((char*)&MEDIATYPE_Video)+4, sizeof(GUID)-4)) && /* Check root (GUID w/o FOURCC) */
+        (IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo)))
+    {
+        VIDEOINFOHEADER* format = (VIDEOINFOHEADER*)pmt->pbFormat;
+        AM_MEDIA_TYPE* outpmt = &((OutputPin*)This->tf.ppPins[1])->pin.mtCurrent;
+        const CLSID* outsubtype;
+        DWORD bih_size;
+        DWORD output_depth = format->bmiHeader.biBitCount;
+        int codec_id = CODEC_ID_NONE;
+
+        switch(format->bmiHeader.biBitCount)
+        {
+            case 32: outsubtype = &MEDIASUBTYPE_RGB32; break;
+            case 24: outsubtype = &MEDIASUBTYPE_RGB24; break;
+            case 16: outsubtype = &MEDIASUBTYPE_RGB565; break;
+            case 8:  outsubtype = &MEDIASUBTYPE_RGB8; break;
+            default:
+                TRACE("Non standard input depth %d, forced ouptut depth to 32\n", format->bmiHeader.biBitCount);
+                outsubtype = &MEDIASUBTYPE_RGB32;
+                output_depth = 32;
+                break;
+        }
+
+        /* Copy bitmap header from media type to 1 for input and 1 for output */
+        if (This->pBihIn)
+        {
+            CoTaskMemFree(This->pBihIn);
+            CoTaskMemFree(This->pBihOut);
+        }
+
+        bih_size = format->bmiHeader.biSize + format->bmiHeader.biClrUsed * 4;
+        This->pBihIn = (BITMAPINFOHEADER*)CoTaskMemAlloc(bih_size);
+        if (!This->pBihIn)
+            return E_OUTOFMEMORY;
+        This->pBihOut = (BITMAPINFOHEADER*)CoTaskMemAlloc(bih_size);
+        if (!This->pBihOut)
+        {
+            CoTaskMemFree(This->pBihIn);
+            This->pBihIn = NULL;
+            return E_OUTOFMEMORY;
+        }
+        memcpy(This->pBihIn, &format->bmiHeader, bih_size);
+        memcpy(This->pBihOut, &format->bmiHeader, bih_size);
+
+        /* Update output format as non compressed bitmap */
+        This->pBihOut->biCompression = 0;
+        This->pBihOut->biBitCount = output_depth;
+        This->pBihOut->biSizeImage = This->pBihOut->biWidth * This->pBihOut->biHeight * This->pBihOut->biBitCount / 8;
+
+        /* Update buffer size of media samples in output */
+        ((OutputPin*)This->tf.ppPins[1])->allocProps.cbBuffer = This->pBihOut->biSizeImage;
+
+        TRACE("FMT %.04s %ld %.04s\n", (LPCSTR)&pmt->subtype.Data1, pmt->subtype.Data1, (LPCSTR)&list[0].fourcc);
+
+        for(i = 0; i < sizeof(list)/sizeof(list[0]); i++)
+            if (pmt->subtype.Data1 == list[i].fourcc)
+            {
+                codec_id = list[i].codec_id;
+                break;
+            }
+        if (codec_id == CODEC_ID_NONE)
+        {
+            TRACE("Codec not supported yet\n");
+	    goto skip;
+        }
+
+        This->codec = avcodec_find_decoder(codec_id);
+        if (!This->codec)
+        {
+            TRACE("Couldn't find codec\n");
+            goto skip;
+        }
+
+        This->ctx = avcodec_alloc_context();
+        This->tmp_pic = avcodec_alloc_frame();
+
+        if (format->bmiHeader.biClrUsed)
+        {
+            This->ctx->palctrl = &This->palctrl;
+            CopyMemory(This->ctx->palctrl->palette, ((LPBYTE)This->pBihIn) + format->bmiHeader.biSize, format->bmiHeader.biClrUsed * 4);
+        }
+
+        if (avcodec_open( This->ctx, This->codec) < 0)
+        {
+            ERR("Couldn't open codec\n");
+            goto skip;
+        }
+
+        /* Update output media type */
+        CopyMediaType(outpmt, pmt);
+        outpmt->subtype = *outsubtype;
+        memcpy(&(((VIDEOINFOHEADER*)outpmt->pbFormat)->bmiHeader), This->pBihOut, This->pBihOut->biSize);
+
+        This->init = 1;
+        TRACE("Connection accepted\n");
+        return S_OK;
+
+skip:
+        TRACE("Unable to find a suitable FFMpeg video decompressor\n");
+    }
+    
+    TRACE("Connection refused\n");
+    return S_FALSE;
+}
+
+static HRESULT FFMpegVideoWrapper_Cleanup(TransformFilterImpl* pTransformFilter)
+{
+    FFMpegVideoWrapperImpl* This = (FFMpegVideoWrapperImpl*)pTransformFilter;
+
+    TRACE("(%p)->()\n", This);
+    
+    return S_OK;
+}
+
+TransformFuncsTable FFMpegVideoWrapper_FuncsTable = {
+    NULL,
+    FFMpegVideoWrapper_ProcessSampleData,
+    NULL,
+    FFMpegVideoWrapper_ConnectInput,
+    FFMpegVideoWrapper_Cleanup
+};
+
+HRESULT FFMpegVideoWrapper_create(IUnknown * pUnkOuter, LPVOID * ppv)
+{
+    HRESULT hr;
+    FFMpegVideoWrapperImpl * This;
+
+    TRACE("(%p, %p)\n", pUnkOuter, ppv);
+
+    *ppv = NULL;
+
+    if (pUnkOuter)
+        return CLASS_E_NOAGGREGATION;
+
+    /* Note: This memory is managed by the transform filter once created */
+    This = CoTaskMemAlloc(sizeof(FFMpegVideoWrapperImpl));
+
+    This->pBihIn = NULL;
+    This->pBihOut = NULL;
+
+    hr = TransformFilter_Create(&(This->tf), &CLSID_FFMpegVideoWrapper, &FFMpegVideoWrapper_FuncsTable);
+
+    if (FAILED(hr))
+        return hr;
+
+    avcodec_init();
+    avcodec_register_all();
+    
+    *ppv = (LPVOID)This;
+
+    return hr;
+}
+
+#endif /* HAVE_LIBAVCODEC */


More information about the wine-patches mailing list