[MSVFW32] Sync with Wine Staging 4.18. CORE-16441
[reactos.git] / dll / win32 / msvfw32 / msvideo_main.c
index f76b42a..09ed031 100644 (file)
  *      - no thread safety
  */
 
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "winnls.h"
+#include "wingdi.h"
+#include "wine/winternl.h"
+#include "winuser.h"
+#include "commdlg.h"
+#include "vfw.h"
 #include "msvideo_private.h"
-
-#include <winreg.h>
-#include <commdlg.h>
-
-#include "resource.h"
+#include "wine/debug.h"
+#include "wine/heap.h"
+#include "wine/list.h"
 
 /* Drivers32 settings */
 #define HKLM_DRIVERS32 "Software\\Microsoft\\Windows NT\\CurrentVersion\\Drivers32"
 
 WINE_DEFAULT_DEBUG_CHANNEL(msvideo);
 
+/* This one is a macro in order to work for both ASCII and Unicode */
+#define fourcc_to_string(str, fcc) do { \
+       (str)[0] = LOBYTE(LOWORD(fcc)); \
+       (str)[1] = HIBYTE(LOWORD(fcc)); \
+       (str)[2] = LOBYTE(HIWORD(fcc)); \
+       (str)[3] = HIBYTE(HIWORD(fcc)); \
+       } while(0)
+
 static inline const char *wine_dbgstr_fcc( DWORD fcc )
 {
-    return wine_dbg_sprintf("%c%c%c%c", 
-                            LOBYTE(LOWORD(fcc)), HIBYTE(LOWORD(fcc)),
-                            LOBYTE(HIWORD(fcc)), HIBYTE(HIWORD(fcc)));
+    char fcc_str[5];
+    fourcc_to_string(fcc_str, fcc);
+    fcc_str[4] = '\0';
+    /* Last byte may be ' ' in some cases like "DIB " */
+    if (isalnum(fcc_str[0]) && isalnum(fcc_str[1]) && isalnum(fcc_str[2])
+    && (isalnum(fcc_str[3]) || isspace(fcc_str[3])))
+        return wine_dbg_sprintf("%s", fcc_str);
+    return wine_dbg_sprintf("0x%08x", fcc);
+}
+
+static const char *wine_dbgstr_icerr( int ret )
+{
+    const char *str;
+    if (ret <= ICERR_CUSTOM)
+        return wine_dbg_sprintf("ICERR_CUSTOM (%d)", ret);
+#define XX(x) case (x): str = #x; break
+    switch (ret)
+    {
+        XX(ICERR_OK);
+        XX(ICERR_DONTDRAW);
+        XX(ICERR_NEWPALETTE);
+        XX(ICERR_GOTOKEYFRAME);
+        XX(ICERR_STOPDRAWING);
+        XX(ICERR_UNSUPPORTED);
+        XX(ICERR_BADFORMAT);
+        XX(ICERR_MEMORY);
+        XX(ICERR_INTERNAL);
+        XX(ICERR_BADFLAGS);
+        XX(ICERR_BADPARAM);
+        XX(ICERR_BADSIZE);
+        XX(ICERR_BADHANDLE);
+        XX(ICERR_CANTUPDATE);
+        XX(ICERR_ABORT);
+        XX(ICERR_ERROR);
+        XX(ICERR_BADBITDEPTH);
+        XX(ICERR_BADIMAGESIZE);
+        default: str = wine_dbg_sprintf("UNKNOWN (%d)", ret);
+    }
+#undef XX
+    return str;
 }
 
 static WINE_HIC*        MSVIDEO_FirstHic /* = NULL */;
 
-typedef struct _reg_driver reg_driver;
-struct _reg_driver
+struct reg_driver
 {
     DWORD       fccType;
     DWORD       fccHandler;
     DRIVERPROC  proc;
-    LPWSTR      name;
-    reg_driver* next;
+    struct list entry;
 };
 
-static reg_driver* reg_driver_list = NULL;
-
-/* This one is a macro in order to work for both ASCII and Unicode */
-#define fourcc_to_string(str, fcc) do { \
-       (str)[0] = LOBYTE(LOWORD(fcc)); \
-       (str)[1] = HIBYTE(LOWORD(fcc)); \
-       (str)[2] = LOBYTE(HIWORD(fcc)); \
-       (str)[3] = HIBYTE(HIWORD(fcc)); \
-       } while(0)
+static struct list reg_driver_list = LIST_INIT(reg_driver_list);
 
 HMODULE MSVFW32_hModule;
 
@@ -165,7 +211,7 @@ static LRESULT MSVIDEO_SendMessage(WINE_HIC* whic, UINT msg, DWORD_PTR lParam1,
         ret = SendDriverMessage(whic->hdrv, msg, lParam1, lParam2);
     }
 
-    TRACE("    -> 0x%08lx\n", ret);
+    TRACE("    -> %s\n", wine_dbgstr_icerr(ret));
     return ret;
 }
 
@@ -175,54 +221,16 @@ static int compare_fourcc(DWORD fcc1, DWORD fcc2)
   char fcc_str2[4];
   fourcc_to_string(fcc_str1, fcc1);
   fourcc_to_string(fcc_str2, fcc2);
-  return strncasecmp(fcc_str1, fcc_str2, 4);
+  return _strnicmp(fcc_str1, fcc_str2, 4);
 }
 
-typedef BOOL (*enum_handler_t)(const char*, unsigned int, void*);
-
-static BOOL enum_drivers(DWORD fccType, enum_handler_t handler, void* param)
+static DWORD get_size_image(LONG width, LONG height, WORD depth)
 {
-    CHAR buf[2048], fccTypeStr[5], *s;
-    DWORD i, cnt = 0, lRet;
-    BOOL result = FALSE;
-    HKEY hKey;
-
-    fourcc_to_string(fccTypeStr, fccType);
-    fccTypeStr[4] = '.';
-
-    /* first, go through the registry entries */
-    lRet = RegOpenKeyExA(HKEY_LOCAL_MACHINE, HKLM_DRIVERS32, 0, KEY_QUERY_VALUE, &hKey);
-    if (lRet == ERROR_SUCCESS) 
-    {
-        DWORD name, data, type;
-        i = 0;
-        for (;;)
-       {
-           name = 10;
-           data = sizeof buf - name;
-           lRet = RegEnumValueA(hKey, i++, buf, &name, 0, &type, (LPBYTE)(buf+name), &data);
-           if (lRet == ERROR_NO_MORE_ITEMS) break;
-           if (lRet != ERROR_SUCCESS) continue;
-           if (name != 9 || strncasecmp(buf, fccTypeStr, 5)) continue;
-           buf[name] = '=';
-           if ((result = handler(buf, cnt++, param))) break;
-       }
-       RegCloseKey( hKey );
-    }
-    if (result) return result;
-
-    /* if that didn't work, go through the values in system.ini */
-    if (GetPrivateProfileSectionA("drivers32", buf, sizeof(buf), "system.ini")) 
-    {
-       for (s = buf; *s; s += strlen(s) + 1)
-       {
-            TRACE("got %s\n", s);
-           if (strncasecmp(s, fccTypeStr, 5) || s[9] != '=') continue;
-           if ((result = handler(s, cnt++, param))) break;
-       }
-    }
-
-    return result;
+    DWORD ret = width * depth;
+    ret = (ret + 7) / 8;    /* divide by byte size, rounding up */
+    ret = (ret + 3) & ~3;   /* align to 4 bytes */
+    ret *= abs(height);
+    return ret;
 }
 
 /******************************************************************
@@ -249,27 +257,6 @@ DWORD WINAPI VideoForWindowsVersion(void)
     return 0x040003B6; /* 4.950 */
 }
 
-static BOOL ICInfo_enum_handler(const char *drv, unsigned int nr, void *param)
-{
-    ICINFO *lpicinfo = param;
-    DWORD fccHandler = mmioStringToFOURCCA(drv + 5, 0);
-
-    /* exact match of fccHandler or nth driver found */
-    if ((lpicinfo->fccHandler != nr) && (lpicinfo->fccHandler != fccHandler))
-       return FALSE;
-
-    lpicinfo->fccHandler = fccHandler;
-    lpicinfo->dwFlags = 0;
-    lpicinfo->dwVersion = 0;
-    lpicinfo->dwVersionICM = ICVERSION;
-    lpicinfo->szName[0] = 0;
-    lpicinfo->szDescription[0] = 0;
-    MultiByteToWideChar(CP_ACP, 0, drv + 10, -1, lpicinfo->szDriver, 
-                       sizeof(lpicinfo->szDriver)/sizeof(WCHAR));
-
-    return TRUE;
-}
-
 /***********************************************************************
  *             ICInfo                          [MSVFW32.@]
  * Get information about an installable compressor. Return TRUE if there
@@ -280,99 +267,170 @@ static BOOL ICInfo_enum_handler(const char *drv, unsigned int nr, void *param)
  *   fccHandler  [I] real fcc for handler or <n>th compressor
  *   lpicinfo    [O] information about compressor
  */
-BOOL VFWAPI ICInfo( DWORD fccType, DWORD fccHandler, ICINFO *lpicinfo)
+BOOL VFWAPI ICInfo(DWORD type, DWORD handler, ICINFO *info)
 {
-    TRACE("(%s,%s/%08x,%p)\n",
-          wine_dbgstr_fcc(fccType), wine_dbgstr_fcc(fccHandler), fccHandler, lpicinfo);
+    char name_buf[10], buf[2048];
+    DWORD ret_type, ret_handler;
+    struct reg_driver *driver;
+    DWORD i, count = 0;
+    LONG res;
+    HKEY key;
+
+    TRACE("type %s, handler %s, info %p.\n",
+            wine_dbgstr_fcc(type), wine_dbgstr_fcc(handler), info);
+
+    memset(info, 0, sizeof(*info));
+    info->dwSize = sizeof(*info);
+    info->dwVersionICM = ICVERSION;
 
-    lpicinfo->fccType = fccType;
-    lpicinfo->fccHandler = fccHandler;
-    return enum_drivers(fccType, ICInfo_enum_handler, lpicinfo);
+    if (!RegOpenKeyExA(HKEY_LOCAL_MACHINE, HKLM_DRIVERS32, 0, KEY_QUERY_VALUE, &key))
+    {
+        i = 0;
+        for (;;)
+        {
+            DWORD name_len = ARRAY_SIZE(name_buf), driver_len = ARRAY_SIZE(info->szDriver);
+
+            res = RegEnumValueA(key, i++, name_buf, &name_len, 0, 0, (BYTE *)buf, &driver_len);
+            if (res == ERROR_NO_MORE_ITEMS) break;
+
+            if (name_len != 9 || name_buf[4] != '.') continue;
+            ret_type = mmioStringToFOURCCA(name_buf, 0);
+            ret_handler = mmioStringToFOURCCA(name_buf + 5, 0);
+            if (type && compare_fourcc(type, ret_type)) continue;
+            if (compare_fourcc(handler, ret_handler) && handler != count++) continue;
+
+            info->fccType = ret_type;
+            info->fccHandler = ret_handler;
+            MultiByteToWideChar(CP_ACP, 0, buf, -1, info->szDriver, ARRAY_SIZE(info->szDriver));
+            TRACE("Returning codec %s, driver %s.\n", debugstr_a(name_buf), debugstr_a(buf));
+            return TRUE;
+        }
+        RegCloseKey(key);
+    }
+
+    if (GetPrivateProfileSectionA("drivers32", buf, sizeof(buf), "system.ini"))
+    {
+        char *s;
+        for (s = buf; *s; s += strlen(s) + 1)
+        {
+            if (s[4] != '.' || s[9] != '=') continue;
+            ret_type = mmioStringToFOURCCA(s, 0);
+            ret_handler = mmioStringToFOURCCA(s + 5, 0);
+            if (type && compare_fourcc(type, ret_type)) continue;
+            if (compare_fourcc(handler, ret_handler) && handler != count++) continue;
+
+            info->fccType = ret_type;
+            info->fccHandler = ret_handler;
+            MultiByteToWideChar(CP_ACP, 0, s + 10, -1, info->szDriver, ARRAY_SIZE(info->szDriver));
+            TRACE("Returning codec %s, driver %s.\n", debugstr_an(s, 8), debugstr_a(s + 10));
+            return TRUE;
+        }
+    }
+
+    LIST_FOR_EACH_ENTRY(driver, &reg_driver_list, struct reg_driver, entry)
+    {
+        if (type && compare_fourcc(type, driver->fccType)) continue;
+        if (compare_fourcc(handler, driver->fccHandler) && handler != count++) continue;
+        if (driver->proc(0, NULL, ICM_GETINFO, (DWORD_PTR)info, sizeof(*info)) == sizeof(*info))
+            return TRUE;
+    }
+
+    info->fccType = type;
+    info->fccHandler = handler;
+    WARN("No driver found for codec %s.%s.\n", wine_dbgstr_fcc(type), wine_dbgstr_fcc(handler));
+    return FALSE;
 }
 
 static DWORD IC_HandleRef = 1;
 
 /***********************************************************************
- *             ICInstall                       [MSVFW32.@]
+ *              ICInstall                       [MSVFW32.@]
  */
-BOOL VFWAPI ICInstall(DWORD fccType, DWORD fccHandler, LPARAM lParam, LPSTR szDesc, UINT wFlags) 
+BOOL VFWAPI ICInstall(DWORD type, DWORD handler, LPARAM lparam, char *desc, UINT flags)
 {
-    reg_driver* driver;
-    unsigned len;
+    struct reg_driver *driver;
 
-    TRACE("(%s,%s,%p,%p,0x%08x)\n", wine_dbgstr_fcc(fccType), wine_dbgstr_fcc(fccHandler), (void*)lParam, szDesc, wFlags);
+    TRACE("type %s, handler %s, lparam %#lx, desc %s, flags %#x.\n",
+            wine_dbgstr_fcc(type), wine_dbgstr_fcc(handler), lparam, debugstr_a(desc), flags);
 
-    /* Check if a driver is already registered */
-    for (driver = reg_driver_list; driver; driver = driver->next)
+    LIST_FOR_EACH_ENTRY(driver, &reg_driver_list, struct reg_driver, entry)
     {
-        if (!compare_fourcc(fccType, driver->fccType) &&
-            !compare_fourcc(fccHandler, driver->fccHandler))
-            break;
+        if (!compare_fourcc(type, driver->fccType)
+                && !compare_fourcc(handler, driver->fccHandler))
+        {
+            return FALSE;
+        }
     }
-    if (driver) return FALSE;
-
-    /* Register the driver */
-    driver = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(reg_driver));
-    if (!driver) goto oom;
-    driver->fccType = fccType;
-    driver->fccHandler = fccHandler;
 
-    switch(wFlags)
+    switch (flags)
     {
     case ICINSTALL_FUNCTION:
-        driver->proc = (DRIVERPROC)lParam;
-       driver->name = NULL;
-        break;
+        if (!(driver = heap_alloc_zero(sizeof(*driver))))
+            return FALSE;
+        driver->fccType = type;
+        driver->fccHandler = handler;
+        driver->proc = (DRIVERPROC)lparam;
+        list_add_tail(&reg_driver_list, &driver->entry);
+        return TRUE;
     case ICINSTALL_DRIVER:
-       driver->proc = NULL;
-        len = MultiByteToWideChar(CP_ACP, 0, (char*)lParam, -1, NULL, 0);
-        driver->name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
-        if (!driver->name) goto oom;
-        MultiByteToWideChar(CP_ACP, 0, (char*)lParam, -1, driver->name, len);
-       break;
+    {
+        const char *driver = (const char *)lparam;
+        char value[10];
+        HKEY key;
+        LONG res;
+
+        if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, HKLM_DRIVERS32, 0, KEY_SET_VALUE, &key))
+            return FALSE;
+        fourcc_to_string(value, type);
+        value[4] = '.';
+        fourcc_to_string(value + 5, handler);
+        value[9] = 0;
+        res = RegSetValueExA(key, value, 0, REG_SZ, (const BYTE *)driver, strlen(driver) + 1);
+        RegCloseKey(key);
+        return !res;
+    }
     default:
-       ERR("Invalid flags!\n");
-       HeapFree(GetProcessHeap(), 0, driver);
-       return FALSE;
-   }
-
-   /* Insert our driver in the list*/
-   driver->next = reg_driver_list;
-   reg_driver_list = driver;
-    
-   return TRUE;
-oom:
-   HeapFree(GetProcessHeap(), 0, driver);
-   return FALSE;
+        FIXME("Unhandled flags %#x.\n", flags);
+        return FALSE;
+    }
 }
 
 /***********************************************************************
- *             ICRemove                        [MSVFW32.@]
+ *              ICRemove                        [MSVFW32.@]
  */
-BOOL VFWAPI ICRemove(DWORD fccType, DWORD fccHandler, UINT wFlags) 
+BOOL VFWAPI ICRemove(DWORD type, DWORD handler, UINT flags)
 {
-    reg_driver** pdriver;
-    reg_driver*  drv;
+    struct reg_driver *driver;
+    char value[10];
+    HKEY key;
+    LONG res;
 
-    TRACE("(%s,%s,0x%08x)\n", wine_dbgstr_fcc(fccType), wine_dbgstr_fcc(fccHandler), wFlags);
+    TRACE("type %s, handler %s, flags %#x.\n",
+            wine_dbgstr_fcc(type), wine_dbgstr_fcc(handler), flags);
 
-    /* Check if a driver is already registered */
-    for (pdriver = &reg_driver_list; *pdriver; pdriver = &(*pdriver)->next)
+    LIST_FOR_EACH_ENTRY(driver, &reg_driver_list, struct reg_driver, entry)
     {
-        if (!compare_fourcc(fccType, (*pdriver)->fccType) &&
-            !compare_fourcc(fccHandler, (*pdriver)->fccHandler))
-            break;
+        if (!compare_fourcc(type, driver->fccType)
+                && !compare_fourcc(handler, driver->fccHandler))
+        {
+            list_remove(&driver->entry);
+            heap_free(driver);
+            return TRUE;
+        }
     }
-    if (!*pdriver)
-        return FALSE;
 
-    /* Remove the driver from the list */
-    drv = *pdriver;
-    *pdriver = (*pdriver)->next;
-    HeapFree(GetProcessHeap(), 0, drv->name);
-    HeapFree(GetProcessHeap(), 0, drv);
-    
-    return TRUE;  
+    if (!RegOpenKeyExA(HKEY_LOCAL_MACHINE, HKLM_DRIVERS32, 0, KEY_SET_VALUE, &key))
+    {
+        fourcc_to_string(value, type);
+        value[4] = '.';
+        fourcc_to_string(value + 5, handler);
+        value[9] = 0;
+        res = RegDeleteValueA(key, value);
+        RegCloseKey(key);
+        return !res;
+    }
+
+    return FALSE;
 }
 
 
@@ -384,42 +442,58 @@ HIC VFWAPI ICOpen(DWORD fccType, DWORD fccHandler, UINT wMode)
 {
     WCHAR              codecname[10];
     ICOPEN             icopen;
-    HDRVR              hdrv;
     WINE_HIC*           whic;
     static const WCHAR  drv32W[] = {'d','r','i','v','e','r','s','3','2','\0'};
-    reg_driver*         driver;
+    struct reg_driver *driver;
+    HDRVR hdrv = NULL;
 
     TRACE("(%s,%s,0x%08x)\n", wine_dbgstr_fcc(fccType), wine_dbgstr_fcc(fccHandler), wMode);
 
-    /* Check if there is a registered driver that matches */
-    driver = reg_driver_list;
-    while(driver)
-        if (!compare_fourcc(fccType, driver->fccType) &&
-            !compare_fourcc(fccHandler, driver->fccHandler)) {
-           fccType = driver->fccType;
-           fccHandler = driver->fccHandler;
-           break;
-        } else
-            driver = driver->next;
-
-    if (driver && driver->proc)
-       /* The driver has been registered at runtime with its driverproc */
-        return ICOpenFunction(fccType, fccHandler, wMode, driver->proc);
-  
+    if (!fccHandler) /* No specific handler, return the first valid for wMode */
+    {
+        HIC local;
+        ICINFO info;
+        DWORD loop = 0;
+        info.dwSize = sizeof(info);
+        while(ICInfo(fccType, loop++, &info))
+        {
+            /* Ensure fccHandler is not 0x0 because we will recurse on ICOpen */
+            if(!info.fccHandler)
+                continue;
+            local = ICOpen(fccType, info.fccHandler, wMode);
+            if (local != 0)
+            {
+                TRACE("Returning %s as default handler for %s\n",
+                      wine_dbgstr_fcc(info.fccHandler), wine_dbgstr_fcc(fccType));
+                return local;
+            }
+        }
+    }
+
+    LIST_FOR_EACH_ENTRY(driver, &reg_driver_list, struct reg_driver, entry)
+    {
+        if (!compare_fourcc(fccType, driver->fccType)
+                && !compare_fourcc(fccHandler, driver->fccHandler))
+        {
+            return ICOpenFunction(driver->fccType, driver->fccHandler, wMode, driver->proc);
+        }
+    }
+
     /* Well, lParam2 is in fact a LPVIDEO_OPEN_PARMS, but it has the
      * same layout as ICOPEN
      */
-    icopen.dwSize              = sizeof(ICOPEN);
-    icopen.fccType             = fccType;
-    icopen.fccHandler          = fccHandler;
-    icopen.dwVersion            = 0x00001000; /* FIXME */
-    icopen.dwFlags             = wMode;
-    icopen.dwError              = 0;
-    icopen.pV1Reserved          = NULL;
-    icopen.pV2Reserved          = NULL;
-    icopen.dnDevNode            = 0; /* FIXME */
-       
-    if (!driver) {
+    icopen.dwSize      = sizeof(ICOPEN);
+    icopen.fccType     = fccType;
+    icopen.fccHandler  = fccHandler;
+    icopen.dwVersion   = 0x00001000; /* FIXME */
+    icopen.dwFlags     = wMode;
+    icopen.dwError     = 0;
+    icopen.pV1Reserved = NULL;
+    icopen.pV2Reserved = NULL;
+    icopen.dnDevNode   = 0; /* FIXME */
+
+    if (!hdrv)
+    {
         /* normalize to lower case as in 'vidc' */
         ((char*)&fccType)[0] = tolower(((char*)&fccType)[0]);
         ((char*)&fccType)[1] = tolower(((char*)&fccType)[1]);
@@ -427,23 +501,17 @@ HIC VFWAPI ICOpen(DWORD fccType, DWORD fccHandler, UINT wMode)
         ((char*)&fccType)[3] = tolower(((char*)&fccType)[3]);
         icopen.fccType = fccType;
         /* Seek the driver in the registry */
-       fourcc_to_string(codecname, fccType);
+        fourcc_to_string(codecname, fccType);
         codecname[4] = '.';
-       fourcc_to_string(codecname + 5, fccHandler);
+        fourcc_to_string(codecname + 5, fccHandler);
         codecname[9] = '\0';
 
         hdrv = OpenDriver(codecname, drv32W, (LPARAM)&icopen);
-        if (!hdrv) 
+        if (!hdrv)
             return 0;
-    } else {
-        /* The driver has been registered at runtime with its name */
-        hdrv = OpenDriver(driver->name, NULL, (LPARAM)&icopen);
-        if (!hdrv) 
-            return 0; 
     }
 
-    whic = HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_HIC));
-    if (!whic)
+    if (!(whic = heap_alloc(sizeof(*whic))))
     {
         CloseDriver(hdrv, 0, 0);
         return FALSE;
@@ -472,18 +540,18 @@ HIC VFWAPI ICOpenFunction(DWORD fccType, DWORD fccHandler, UINT wMode, DRIVERPRO
     TRACE("(%s,%s,%d,%p)\n",
           wine_dbgstr_fcc(fccType), wine_dbgstr_fcc(fccHandler), wMode, lpfnHandler);
 
-    icopen.dwSize              = sizeof(ICOPEN);
-    icopen.fccType             = fccType;
-    icopen.fccHandler          = fccHandler;
-    icopen.dwVersion            = ICVERSION;
-    icopen.dwFlags             = wMode;
-    icopen.dwError              = 0;
-    icopen.pV1Reserved          = NULL;
-    icopen.pV2Reserved          = NULL;
-    icopen.dnDevNode            = 0; /* FIXME */
+    icopen.dwSize      = sizeof(ICOPEN);
+    icopen.fccType     = fccType;
+    icopen.fccHandler  = fccHandler;
+    icopen.dwVersion   = ICVERSION;
+    icopen.dwFlags     = wMode;
+    icopen.dwError     = 0;
+    icopen.pV1Reserved = NULL;
+    icopen.pV2Reserved = NULL;
+    icopen.dnDevNode   = 0; /* FIXME */
 
-    whic = HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_HIC));
-    if (!whic) return 0;
+    if (!(whic = heap_alloc(sizeof(*whic))))
+        return NULL;
 
     whic->driverproc   = lpfnHandler;
     while (MSVIDEO_GetHicPtr((HIC)(ULONG_PTR)IC_HandleRef) != NULL) IC_HandleRef++;
@@ -498,7 +566,7 @@ HIC VFWAPI ICOpenFunction(DWORD fccType, DWORD fccHandler, UINT wMode, DRIVERPRO
     {
         WARN("DRV_LOAD failed for hic %p\n", whic->hic);
         MSVIDEO_FirstHic = whic->next;
-        HeapFree(GetProcessHeap(), 0, whic);
+        heap_free(whic);
         return 0;
     }
     /* return value is not checked */
@@ -512,7 +580,7 @@ HIC VFWAPI ICOpenFunction(DWORD fccType, DWORD fccHandler, UINT wMode, DRIVERPRO
     {
         WARN("DRV_OPEN failed for hic %p\n", whic->hic);
         MSVIDEO_FirstHic = whic->next;
-        HeapFree(GetProcessHeap(), 0, whic);
+        heap_free(whic);
         return 0;
     }
 
@@ -556,150 +624,170 @@ LRESULT VFWAPI ICGetInfo(HIC hic, ICINFO *picinfo, DWORD cb)
         lstrcpyW(picinfo->szDriver, ii.szDriver);
     }
 
-    TRACE("    -> 0x%08lx\n", ret);
     return ret;
 }
 
-typedef struct {
-    DWORD fccType;
-    DWORD fccHandler;
-    LPBITMAPINFOHEADER lpbiIn;
-    LPBITMAPINFOHEADER lpbiOut;
-    WORD wMode;
-    DWORD querymsg;
-    HIC hic;
-} driver_info_t;
-
-static HIC try_driver(driver_info_t *info)
-{
-    HIC   hic;
-
-    if ((hic = ICOpen(info->fccType, info->fccHandler, info->wMode))) 
-    {
-       if (!ICSendMessage(hic, info->querymsg, (DWORD_PTR)info->lpbiIn, (DWORD_PTR)info->lpbiOut))
-           return hic;
-       ICClose(hic);
-    }
-    return 0;
-}
-
-static BOOL ICLocate_enum_handler(const char *drv, unsigned int nr, void *param)
-{
-    driver_info_t *info = param;
-    info->fccHandler = mmioStringToFOURCCA(drv + 5, 0);
-    info->hic = try_driver(info);
-    return info->hic != 0;
-}
-
 /***********************************************************************
- *             ICLocate                        [MSVFW32.@]
+ *              ICLocate                        [MSVFW32.@]
  */
-HIC VFWAPI ICLocate(DWORD fccType, DWORD fccHandler, LPBITMAPINFOHEADER lpbiIn,
-                    LPBITMAPINFOHEADER lpbiOut, WORD wMode)
+HIC VFWAPI ICLocate(DWORD type, DWORD handler, BITMAPINFOHEADER *in,
+        BITMAPINFOHEADER *out, WORD mode)
 {
-    driver_info_t info;
-
-    TRACE("(%s,%s,%p,%p,0x%04x)\n", 
-          wine_dbgstr_fcc(fccType), wine_dbgstr_fcc(fccHandler), lpbiIn, lpbiOut, wMode);
+    ICINFO info = {sizeof(info)};
+    UINT msg;
+    HIC hic;
+    DWORD i;
 
-    info.fccType = fccType;
-    info.fccHandler = fccHandler;
-    info.lpbiIn = lpbiIn;
-    info.lpbiOut = lpbiOut;
-    info.wMode = wMode;
+    TRACE("type %s, handler %s, in %p, out %p, mode %u.\n",
+            wine_dbgstr_fcc(type), wine_dbgstr_fcc(handler), in, out, mode);
 
-    switch (wMode) 
+    switch (mode)
     {
     case ICMODE_FASTCOMPRESS:
     case ICMODE_COMPRESS:
-        info.querymsg = ICM_COMPRESS_QUERY;
+        msg = ICM_COMPRESS_QUERY;
         break;
     case ICMODE_FASTDECOMPRESS:
     case ICMODE_DECOMPRESS:
-        info.querymsg = ICM_DECOMPRESS_QUERY;
+        msg = ICM_DECOMPRESS_QUERY;
         break;
     case ICMODE_DRAW:
-        info.querymsg = ICM_DRAW_QUERY;
+        msg = ICM_DRAW_QUERY;
         break;
     default:
-        WARN("Unknown mode (%d)\n", wMode);
+        FIXME("Unhandled mode %#x.\n", mode);
         return 0;
     }
 
-    /* Easy case: handler/type match, we just fire a query and return */
-    info.hic = try_driver(&info);
-    /* If it didn't work, try each driver in turn. 32 bit codecs only. */
-    /* FIXME: Move this to an init routine? */
-    if (!info.hic) enum_drivers(fccType, ICLocate_enum_handler, &info);
+    if ((hic = ICOpen(type, handler, mode)))
+    {
+        if (!ICSendMessage(hic, msg, (DWORD_PTR)in, (DWORD_PTR)out))
+        {
+            TRACE("Found codec %s.%s.\n", wine_dbgstr_fcc(type),
+                    wine_dbgstr_fcc(handler));
+            return hic;
+        }
+        ICClose(hic);
+    }
 
-    if (info.hic) 
+    for (i = 0; ICInfo(type, i, &info); ++i)
     {
-        TRACE("=> %p\n", info.hic);
-       return info.hic;
+        if ((hic = ICOpen(info.fccType, info.fccHandler, mode)))
+        {
+            if (!ICSendMessage(hic, msg, (DWORD_PTR)in, (DWORD_PTR)out))
+            {
+                TRACE("Found codec %s.%s.\n", wine_dbgstr_fcc(info.fccType),
+                        wine_dbgstr_fcc(info.fccHandler));
+                return hic;
+            }
+            ICClose(hic);
+        }
     }
 
-    if (fccType == streamtypeVIDEO) 
-        return ICLocate(ICTYPE_VIDEO, fccHandler, lpbiIn, lpbiOut, wMode);
-    
-    WARN("(%s,%s,%p,%p,0x%04x) not found!\n",
-         wine_dbgstr_fcc(fccType), wine_dbgstr_fcc(fccHandler), lpbiIn, lpbiOut, wMode);
+    if (type == streamtypeVIDEO)
+        return ICLocate(ICTYPE_VIDEO, handler, in, out, mode);
+
+    WARN("Could not find a driver for codec %s.%s.\n",
+            wine_dbgstr_fcc(type), wine_dbgstr_fcc(handler));
+
     return 0;
 }
 
 /***********************************************************************
  *             ICGetDisplayFormat                      [MSVFW32.@]
  */
-HIC VFWAPI ICGetDisplayFormat(
-       HIC hic,LPBITMAPINFOHEADER lpbiIn,LPBITMAPINFOHEADER lpbiOut,
-       INT depth,INT dx,INT dy)
+HIC VFWAPI ICGetDisplayFormat(HIC hic, BITMAPINFOHEADER *in, BITMAPINFOHEADER *out,
+                              int depth, int width, int height)
 {
-       HIC     tmphic = hic;
+    HIC tmphic = hic;
 
-       TRACE("(%p,%p,%p,%d,%d,%d)!\n",hic,lpbiIn,lpbiOut,depth,dx,dy);
+    TRACE("(%p, %p, %p, %d, %d, %d)\n", hic, in, out, depth, width, height);
 
-       if (!tmphic) {
-               tmphic=ICLocate(ICTYPE_VIDEO,0,lpbiIn,NULL,ICMODE_DECOMPRESS);
-               if (!tmphic)
-                       return tmphic;
-       }
-       if ((dy == lpbiIn->biHeight) && (dx == lpbiIn->biWidth))
-               dy = dx = 0; /* no resize needed */
+    if (!tmphic)
+    {
+        tmphic = ICLocate(ICTYPE_VIDEO, 0, in, NULL, ICMODE_DECOMPRESS);
+        if (!tmphic)
+            return NULL;
+    }
 
-       /* Can we decompress it ? */
-       if (ICDecompressQuery(tmphic,lpbiIn,NULL) != 0)
-               goto errout; /* no, sorry */
+    if (ICDecompressQuery(tmphic, in, NULL))
+        goto err;
 
-       ICSendMessage(tmphic, ICM_DECOMPRESS_GET_FORMAT, (DWORD_PTR)lpbiIn, (DWORD_PTR)lpbiOut);
+    if (width <= 0 || height <= 0)
+    {
+        width = in->biWidth;
+        height = in->biHeight;
+    }
 
-       if (lpbiOut->biCompression != 0) {
-           FIXME("Ooch, how come decompressor outputs compressed data (%d)??\n",
-                        lpbiOut->biCompression);
-       }
-       if (lpbiOut->biSize < sizeof(*lpbiOut)) {
-           FIXME("Ooch, size of output BIH is too small (%d)\n",
-                        lpbiOut->biSize);
-          lpbiOut->biSize = sizeof(*lpbiOut);
-       }
-       if (!depth) {
-               HDC     hdc;
-
-               hdc = GetDC(0);
-               depth = GetDeviceCaps(hdc,BITSPIXEL)*GetDeviceCaps(hdc,PLANES);
-               ReleaseDC(0,hdc);
-               if (depth==15)  depth = 16;
-               if (depth<8)    depth =  8;
-       }
-       if (lpbiIn->biBitCount == 8)
-               depth = 8;
+    if (!depth)
+        depth = 32;
+
+    *out = *in;
+    out->biSize = sizeof(*out);
+    out->biWidth = width;
+    out->biHeight = height;
+    out->biCompression = BI_RGB;
+    out->biSizeImage = get_size_image(width, height, depth);
+
+    /* first try the given depth */
+    out->biBitCount = depth;
+    out->biSizeImage = get_size_image(width, height, out->biBitCount);
+    if (!ICDecompressQuery(tmphic, in, out))
+    {
+        if (depth == 8)
+            ICDecompressGetPalette(tmphic, in, out);
+        return tmphic;
+    }
+
+    /* then try 16, both with BI_RGB and BI_BITFIELDS */
+    if (depth <= 16)
+    {
+        out->biBitCount = 16;
+        out->biSizeImage = get_size_image(width, height, out->biBitCount);
+        if (!ICDecompressQuery(tmphic, in, out))
+            return tmphic;
+
+        out->biCompression = BI_BITFIELDS;
+        if (!ICDecompressQuery(tmphic, in, out))
+            return tmphic;
+        out->biCompression = BI_RGB;
+    }
+
+    /* then try 24 */
+    if (depth <= 24)
+    {
+        out->biBitCount = 24;
+        out->biSizeImage = get_size_image(width, height, out->biBitCount);
+        if (!ICDecompressQuery(tmphic, in, out))
+            return tmphic;
+    }
+
+    /* then try 32 */
+    if (depth <= 32)
+    {
+        out->biBitCount = 32;
+        out->biSizeImage = get_size_image(width, height, out->biBitCount);
+        if (!ICDecompressQuery(tmphic, in, out))
+            return tmphic;
+    }
+
+    /* as a last resort, try 32 bpp with the original width and height */
+    out->biWidth = in->biWidth;
+    out->biHeight = in->biHeight;
+    out->biBitCount = 32;
+    out->biSizeImage = get_size_image(out->biWidth, out->biHeight, out->biBitCount);
+    if (!ICDecompressQuery(tmphic, in, out))
+        return tmphic;
+
+    /* finally, ask the compressor for its default output format */
+    if (!ICSendMessage(tmphic, ICM_DECOMPRESS_GET_FORMAT, (DWORD_PTR)in, (DWORD_PTR)out))
+        return tmphic;
 
-       TRACE("=> %p\n", tmphic);
-       return tmphic;
-errout:
-       if (hic!=tmphic)
-               ICClose(tmphic);
+err:
+    if (hic != tmphic)
+        ICClose(tmphic);
 
-       TRACE("=> 0\n");
-       return 0;
+    return NULL;
 }
 
 /***********************************************************************
@@ -753,8 +841,6 @@ DWORD VFWAPIV  ICDecompress(HIC hic,DWORD dwFlags,LPBITMAPINFOHEADER lpbiFormat,
        icd.ckid        = 0;
        ret = ICSendMessage(hic,ICM_DECOMPRESS,(DWORD_PTR)&icd,sizeof(ICDECOMPRESS));
 
-       TRACE("-> %d\n",ret);
-
        return ret;
 }
 
@@ -812,7 +898,7 @@ static BOOL enum_compressors(HWND list, COMPVARS *pcv, BOOL enum_all)
 
             idx = SendMessageW(list, CB_ADDSTRING, 0, (LPARAM)icinfo.szDescription);
 
-            ic = HeapAlloc(GetProcessHeap(), 0, sizeof(struct codec_info));
+            ic = heap_alloc(sizeof(*ic));
             ic->icinfo = icinfo;
             ic->hic = hic;
             SendMessageW(list, CB_SETITEMDATA, idx, (LPARAM)ic);
@@ -865,7 +951,7 @@ static INT_PTR CALLBACK icm_choose_compressor_dlgproc(HWND hdlg, UINT msg, WPARA
         LoadStringW(MSVFW32_hModule, IDS_FULLFRAMES, buf, 128);
         SendDlgItemMessageW(hdlg, IDC_COMP_LIST, CB_ADDSTRING, 0, (LPARAM)buf);
 
-        ic = HeapAlloc(GetProcessHeap(), 0, sizeof(struct codec_info));
+        ic = heap_alloc(sizeof(*ic));
         ic->icinfo.fccType = streamtypeVIDEO;
         ic->icinfo.fccHandler = comptypeDIB;
         ic->hic = 0;
@@ -986,7 +1072,7 @@ static INT_PTR CALLBACK icm_choose_compressor_dlgproc(HWND hdlg, UINT msg, WPARA
                 if (!ic || (LONG_PTR)ic == CB_ERR) break;
 
                 if (ic->hic) ICClose(ic->hic);
-                HeapFree(GetProcessHeap(), 0, ic);
+                heap_free(ic);
             }
 
             EndDialog(hdlg, LOWORD(wparam) == IDOK);
@@ -1058,23 +1144,25 @@ BOOL VFWAPI ICCompressorChoose(HWND hwnd, UINT uiFlags, LPVOID pvIn,
  */
 void VFWAPI ICCompressorFree(PCOMPVARS pc)
 {
-  TRACE("(%p)\n",pc);
+    TRACE("(%p)\n", pc);
 
-  if (pc != NULL && pc->cbSize == sizeof(COMPVARS)) {
-    if (pc->hic != NULL) {
-      ICClose(pc->hic);
-      pc->hic = NULL;
+    if (pc && pc->cbSize == sizeof(COMPVARS))
+    {
+        if (pc->hic)
+        {
+            ICClose(pc->hic);
+            pc->hic = NULL;
+        }
+        heap_free(pc->lpbiIn);
+        pc->lpbiIn = NULL;
+        heap_free(pc->lpBitsOut);
+        pc->lpBitsOut = NULL;
+        heap_free(pc->lpBitsPrev);
+        pc->lpBitsPrev = NULL;
+        heap_free(pc->lpState);
+        pc->lpState = NULL;
+        pc->dwFlags = 0;
     }
-    HeapFree(GetProcessHeap(), 0, pc->lpbiIn);
-    pc->lpbiIn = NULL;
-    HeapFree(GetProcessHeap(), 0, pc->lpBitsOut);
-    pc->lpBitsOut = NULL;
-    HeapFree(GetProcessHeap(), 0, pc->lpBitsPrev);
-    pc->lpBitsPrev = NULL;
-    HeapFree(GetProcessHeap(), 0, pc->lpState);
-    pc->lpState = NULL;
-    pc->dwFlags = 0;
-  }
 }
 
 /***********************************************************************
@@ -1184,7 +1272,7 @@ LRESULT WINAPI ICClose(HIC hic)
         }
     }
 
-    HeapFree(GetProcessHeap(), 0, whic);
+    heap_free(whic);
     return 0;
 }
 
@@ -1269,8 +1357,7 @@ HANDLE VFWAPI ICImageDecompress(
                cbHdr = ICDecompressGetFormatSize(hic,lpbiIn);
                if ( cbHdr < sizeof(BITMAPINFOHEADER) )
                        goto err;
-               pHdr = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,cbHdr+sizeof(RGBQUAD)*256);
-               if ( pHdr == NULL )
+               if (!(pHdr = heap_alloc_zero(cbHdr + sizeof(RGBQUAD) * 256)))
                        goto err;
                if ( ICDecompressGetFormat( hic, lpbiIn, pHdr ) != ICERR_OK )
                        goto err;
@@ -1294,7 +1381,7 @@ HANDLE VFWAPI ICImageDecompress(
 
        biSizeImage = lpbiOut->bmiHeader.biSizeImage;
        if ( biSizeImage == 0 )
-               biSizeImage = ((((lpbiOut->bmiHeader.biWidth * lpbiOut->bmiHeader.biBitCount + 7) >> 3) + 3) & (~3)) * abs(lpbiOut->bmiHeader.biHeight);
+               biSizeImage = get_size_image(lpbiOut->bmiHeader.biWidth, lpbiOut->bmiHeader.biHeight, lpbiOut->bmiHeader.biBitCount);
 
        TRACE( "call ICDecompressBegin\n" );
 
@@ -1325,7 +1412,7 @@ err:
                ICDecompressEnd( hic );
        if ( bReleaseIC )
                ICClose(hic);
-        HeapFree(GetProcessHeap(),0,pHdr);
+        heap_free(pHdr);
        if ( pMem != NULL )
                GlobalUnlock( hMem );
        if ( !bSucceeded && hMem != NULL )
@@ -1358,7 +1445,7 @@ LPVOID VFWAPI ICSeqCompressFrame(PCOMPVARS pc, UINT uiFlags, LPVOID lpBits, BOOL
         if (pc->lKey && pc->lKeyCount == (pc->lKey - 1))
         /* No key frames if pc->lKey == 0 */
            pc->lKeyCount = 0;
-       icComp->dwFlags = 0;
+        icComp->dwFlags = 0;
     }
 
     icComp->lpInput = lpBits;
@@ -1367,30 +1454,46 @@ LPVOID VFWAPI ICSeqCompressFrame(PCOMPVARS pc, UINT uiFlags, LPVOID lpBits, BOOL
     icComp->lpPrev = pc->lpBitsPrev;
     ret = ICSendMessage(pc->hic, ICM_COMPRESS, (DWORD_PTR)icComp, sizeof(*icComp));
 
-    if (icComp->dwFlags & AVIIF_KEYFRAME)
+    if (ret == ICERR_OK)
     {
-       pc->lKeyCount = 1;
-       *pfKey = TRUE;
-       TRACE("Key frame\n");
+        LPVOID oldprev, oldout;
+
+        if (icComp->dwFlags & AVIIF_KEYFRAME)
+        {
+            pc->lKeyCount = 1;
+            *pfKey = TRUE;
+            TRACE("Key frame\n");
+        }
+        else
+            *pfKey = FALSE;
+
+        *plSize = icComp->lpbiOutput->biSizeImage;
+
+        /* We shift Prev and Out, so we don't have to allocate and release memory */
+        oldprev = pc->lpBitsPrev;
+        oldout = pc->lpBitsOut;
+        pc->lpBitsPrev = oldout;
+        pc->lpBitsOut = oldprev;
+
+        TRACE("returning: %p, compressed frame size %u\n", icComp->lpOutput, *plSize);
+        return icComp->lpOutput;
     }
-    else
-       *pfKey = FALSE;
+    return NULL;
+}
 
-    *plSize = icComp->lpbiOutput->biSizeImage;
-    TRACE(" -- 0x%08x\n", ret);
-    if (ret == ICERR_OK)
+static void clear_compvars(PCOMPVARS pc)
+{
+    heap_free(pc->lpbiIn);
+    heap_free(pc->lpBitsPrev);
+    heap_free(pc->lpBitsOut);
+    heap_free(pc->lpState);
+    pc->lpbiIn = pc->lpBitsPrev = pc->lpBitsOut = pc->lpState = NULL;
+    if (pc->dwFlags & 0x80000000)
     {
-       LPVOID oldprev, oldout;
-/* We shift Prev and Out, so we don't have to allocate and release memory */
-       oldprev = pc->lpBitsPrev;
-       oldout = pc->lpBitsOut;
-       pc->lpBitsPrev = oldout;
-       pc->lpBitsOut = oldprev;
-
-       TRACE("returning: %p\n", icComp->lpOutput);
-       return icComp->lpOutput;
+        heap_free(pc->lpbiOut);
+        pc->lpbiOut = NULL;
+        pc->dwFlags &= ~0x80000000;
     }
-    return NULL;
 }
 
 /***********************************************************************
@@ -1398,15 +1501,31 @@ LPVOID VFWAPI ICSeqCompressFrame(PCOMPVARS pc, UINT uiFlags, LPVOID lpBits, BOOL
  */
 void VFWAPI ICSeqCompressFrameEnd(PCOMPVARS pc)
 {
-    DWORD ret;
     TRACE("(%p)\n", pc);
-    ret = ICSendMessage(pc->hic, ICM_COMPRESS_END, 0, 0);
-    TRACE(" -- %x\n", ret);
-    HeapFree(GetProcessHeap(), 0, pc->lpbiIn);
-    HeapFree(GetProcessHeap(), 0, pc->lpBitsPrev);
-    HeapFree(GetProcessHeap(), 0, pc->lpBitsOut);
-    HeapFree(GetProcessHeap(), 0, pc->lpState);
-    pc->lpbiIn = pc->lpBitsPrev = pc->lpBitsOut = pc->lpState = NULL;
+    ICSendMessage(pc->hic, ICM_COMPRESS_END, 0, 0);
+    clear_compvars(pc);
+}
+
+static BITMAPINFO *copy_bitmapinfo(const BITMAPINFO *src)
+{
+    int num_colors;
+    unsigned int size;
+    BITMAPINFO *dst;
+
+    if (src->bmiHeader.biClrUsed)
+        num_colors = min(src->bmiHeader.biClrUsed, 256);
+    else
+        num_colors = src->bmiHeader.biBitCount > 8 ? 0 : 1 << src->bmiHeader.biBitCount;
+
+    size = FIELD_OFFSET(BITMAPINFO, bmiColors[num_colors]);
+    if (src->bmiHeader.biCompression == BI_BITFIELDS)
+        size += 3 * sizeof(DWORD);
+
+    if (!(dst = heap_alloc(size)))
+        return NULL;
+
+    memcpy(dst, src, size);
+    return dst;
 }
 
 /***********************************************************************
@@ -1418,68 +1537,99 @@ BOOL VFWAPI ICSeqCompressFrameStart(PCOMPVARS pc, LPBITMAPINFO lpbiIn)
      * it doesn't appear to be used though
      */
     DWORD ret;
-    pc->lpbiIn = HeapAlloc(GetProcessHeap(), 0, sizeof(BITMAPINFO));
-    if (!pc->lpbiIn)
+    ICCOMPRESS* icComp;
+
+    if (!(pc->lpbiIn = copy_bitmapinfo(lpbiIn)))
         return FALSE;
 
-    *pc->lpbiIn = *lpbiIn;
-    pc->lpBitsPrev = HeapAlloc(GetProcessHeap(), 0, pc->lpbiIn->bmiHeader.biSizeImage);
-    if (!pc->lpBitsPrev)
-    {
-        HeapFree(GetProcessHeap(), 0, pc->lpbiIn);
-       return FALSE;
-    }
+    if (!(pc->lpState = heap_alloc(sizeof(ICCOMPRESS) + sizeof(*icComp->lpckid) + sizeof(*icComp->lpdwFlags))))
+        goto error;
 
-    pc->lpState = HeapAlloc(GetProcessHeap(), 0, sizeof(ICCOMPRESS));
-    if (!pc->lpState)
-    {
-       HeapFree(GetProcessHeap(), 0, pc->lpbiIn);
-       HeapFree(GetProcessHeap(), 0, pc->lpBitsPrev);
-       return FALSE;
-    }
     pc->cbState = sizeof(ICCOMPRESS);
 
-    pc->lpBitsOut = HeapAlloc(GetProcessHeap(), 0, pc->lpbiOut->bmiHeader.biSizeImage);
-    if (!pc->lpBitsOut)
+    if (!pc->lpbiOut)
     {
-       HeapFree(GetProcessHeap(), 0, pc->lpbiIn);
-       HeapFree(GetProcessHeap(), 0, pc->lpBitsPrev);
-       HeapFree(GetProcessHeap(), 0, pc->lpState);
-       return FALSE;
+        /* Ask compressor for needed header size */
+        int size = ICSendMessage(pc->hic, ICM_COMPRESS_GET_FORMAT,
+                                 (DWORD_PTR)pc->lpbiIn, 0);
+        if (size <= 0)
+            goto error;
+
+        if (!(pc->lpbiOut = heap_alloc_zero(size)))
+            goto error;
+        /* Flag to show that we allocated lpbiOut for proper cleanup */
+        pc->dwFlags |= 0x80000000;
+
+        ret = ICSendMessage(pc->hic, ICM_COMPRESS_GET_FORMAT,
+                            (DWORD_PTR)pc->lpbiIn, (DWORD_PTR)pc->lpbiOut);
+        if (ret != ICERR_OK)
+        {
+            ERR("Could not get output format from compressor\n");
+            goto error;
+        }
+        if (!pc->lpbiOut->bmiHeader.biSizeImage)
+        {
+            /* If we can't know the output frame size for sure at least allocate
+             * the same size of the input frame and also at least 8Kb to be sure
+             * that poor compressors will have enough memory to work if the input
+             * frame is too small.
+             */
+            pc->lpbiOut->bmiHeader.biSizeImage = max(8192, pc->lpbiIn->bmiHeader.biSizeImage);
+            ERR("Bad codec! Invalid output frame size, guessing from input\n");
+        }
     }
+
+    TRACE("Input: %ux%u, fcc %s, bpp %u, size %u\n",
+          pc->lpbiIn->bmiHeader.biWidth, pc->lpbiIn->bmiHeader.biHeight,
+          wine_dbgstr_fcc(pc->lpbiIn->bmiHeader.biCompression),
+          pc->lpbiIn->bmiHeader.biBitCount,
+          pc->lpbiIn->bmiHeader.biSizeImage);
+    TRACE("Output: %ux%u, fcc %s, bpp %u, size %u\n",
+          pc->lpbiOut->bmiHeader.biWidth, pc->lpbiOut->bmiHeader.biHeight,
+          wine_dbgstr_fcc(pc->lpbiOut->bmiHeader.biCompression),
+          pc->lpbiOut->bmiHeader.biBitCount,
+          pc->lpbiOut->bmiHeader.biSizeImage);
+
+    /* Buffer for compressed frame data */
+    if (!(pc->lpBitsOut = heap_alloc(pc->lpbiOut->bmiHeader.biSizeImage)))
+        goto error;
+
+    /* Buffer for previous compressed frame data */
+    if (!(pc->lpBitsPrev = heap_alloc(pc->lpbiOut->bmiHeader.biSizeImage)))
+        goto error;
+
     TRACE("Compvars:\n"
-          "\tpc:\n"
           "\tsize: %i\n"
-          "\tflags: %i\n"
+          "\tflags: 0x%x\n"
           "\thic: %p\n"
-          "\ttype: %x\n"
-          "\thandler: %x\n"
+          "\ttype: %s\n"
+          "\thandler: %s\n"
           "\tin/out: %p/%p\n"
-          "key/data/quality: %i/%i/%i\n",
-            pc->cbSize, pc->dwFlags, pc->hic, pc->fccType, pc->fccHandler,
-            pc->lpbiIn, pc->lpbiOut, pc->lKey, pc->lDataRate, pc->lQ);
+          "\tkey/data/quality: %i/%i/%i\n",
+    pc->cbSize, pc->dwFlags, pc->hic, wine_dbgstr_fcc(pc->fccType),
+    wine_dbgstr_fcc(pc->fccHandler), pc->lpbiIn, pc->lpbiOut, pc->lKey,
+    pc->lDataRate, pc->lQ);
 
     ret = ICSendMessage(pc->hic, ICM_COMPRESS_BEGIN, (DWORD_PTR)pc->lpbiIn, (DWORD_PTR)pc->lpbiOut);
-    TRACE(" -- %x\n", ret);
     if (ret == ICERR_OK)
     {
-       ICCOMPRESS* icComp = pc->lpState;
-       /* Initialise some variables */
-       pc->lFrame = 0; pc->lKeyCount = 0;
-
-       icComp->lpbiOutput = &pc->lpbiOut->bmiHeader;
-       icComp->lpbiInput = &pc->lpbiIn->bmiHeader;
-       icComp->lpckid = NULL;
-       icComp->dwFrameSize = 0;
-       icComp->dwQuality = pc->lQ;
-       icComp->lpbiPrev = &pc->lpbiIn->bmiHeader;
-       return TRUE;
+        icComp = pc->lpState;
+        /* Initialise some variables */
+        pc->lFrame = 0; pc->lKeyCount = 0;
+
+        icComp->lpbiOutput = &pc->lpbiOut->bmiHeader;
+        icComp->lpbiInput = &pc->lpbiIn->bmiHeader;
+        icComp->lpckid = (DWORD *)(icComp + 1);
+        *icComp->lpckid = 0;
+        icComp->lpdwFlags = (DWORD *)((char *)(icComp + 1) + sizeof(*icComp->lpckid));
+        *icComp->lpdwFlags = 0;
+        icComp->dwFrameSize = 0;
+        icComp->dwQuality = pc->lQ;
+        icComp->lpbiPrev = &pc->lpbiIn->bmiHeader;
+        return TRUE;
     }
-    HeapFree(GetProcessHeap(), 0, pc->lpbiIn);
-    HeapFree(GetProcessHeap(), 0, pc->lpBitsPrev);
-    HeapFree(GetProcessHeap(), 0, pc->lpState);
-    HeapFree(GetProcessHeap(), 0, pc->lpBitsOut);
-    pc->lpBitsPrev = pc->lpbiIn = pc->lpState = pc->lpBitsOut = NULL;
+error:
+    clear_compvars(pc);
     return FALSE;
 }