Fix H264 in multi monitor case
authorDavid Fort <contact@hardening-consulting.com>
Mon, 1 May 2017 20:39:52 +0000 (22:39 +0200)
committerDavid Fort <contact@hardening-consulting.com>
Tue, 2 May 2017 16:39:33 +0000 (18:39 +0200)
The H264 context is surface specific, so in multi-monitor (with multiple surfaces)
the decoding was failing. This patch fixes that by introducing a surface specific
h264 context.

client/X11/xf_gfx.c
include/freerdp/gdi/gfx.h
libfreerdp/gdi/gfx.c

index 4f89dfb..a6a85b5 100644 (file)
@@ -210,21 +210,21 @@ UINT32 x11_pad_scanline(UINT32 scanline, UINT32 inPad)
 static UINT xf_CreateSurface(RdpgfxClientContext* context,
                              const RDPGFX_CREATE_SURFACE_PDU* createSurface)
 {
+       UINT ret = CHANNEL_RC_NO_MEMORY;
        size_t size;
        xfGfxSurface* surface;
        rdpGdi* gdi = (rdpGdi*)context->custom;
        xfContext* xfc = (xfContext*) gdi->context;
-       surface = (xfGfxSurface*) calloc(1, sizeof(xfGfxSurface));
 
+       surface = (xfGfxSurface *) calloc(1, sizeof(xfGfxSurface));
        if (!surface)
                return CHANNEL_RC_NO_MEMORY;
 
        surface->gdi.codecs = gdi->context->codecs;
-
        if (!surface->gdi.codecs)
        {
-               free(surface);
-               return CHANNEL_RC_NO_MEMORY;
+               WLog_ERR(TAG, "%s: global GDI codecs aren't set", __FUNCTION__);
+               goto out_free;
        }
 
        surface->gdi.surfaceId = createSurface->surfaceId;
@@ -242,22 +242,21 @@ static UINT xf_CreateSurface(RdpgfxClientContext* context,
                        break;
 
                default:
-                       free(surface);
-                       return ERROR_INTERNAL_ERROR;
+                       WLog_ERR(TAG, "%s: unknown pixelFormat 0x%"PRIx32"", __FUNCTION__, createSurface->pixelFormat);
+                       ret = ERROR_INTERNAL_ERROR;
+                       goto out_free;
        }
 
-       surface->gdi.scanline = surface->gdi.width * GetBytesPerPixel(
-                                   surface->gdi.format);
+       surface->gdi.scanline = surface->gdi.width * GetBytesPerPixel(surface->gdi.format);
        surface->gdi.scanline = x11_pad_scanline(surface->gdi.scanline, xfc->scanline_pad);
        size = surface->gdi.scanline * surface->gdi.height;
-       surface->gdi.data = (BYTE*) _aligned_malloc(size, 16);
 
+       surface->gdi.data = (BYTE*)_aligned_malloc(size, 16);
        if (!surface->gdi.data)
        {
-               free(surface);
-               return CHANNEL_RC_NO_MEMORY;
+               WLog_ERR(TAG, "%s: unable to allocate GDI data", __FUNCTION__);
+               goto out_free;
        }
-
        ZeroMemory(surface->gdi.data, size);
 
        if (AreColorFormatsEqualNoAlpha(gdi->dstFormat, surface->gdi.format))
@@ -273,26 +272,45 @@ static UINT xf_CreateSurface(RdpgfxClientContext* context,
                surface->stageScanline = width * bytes;
                surface->stageScanline = x11_pad_scanline(surface->stageScanline, xfc->scanline_pad);
                size = surface->stageScanline * surface->gdi.height;
-               surface->stage = (BYTE*) _aligned_malloc(size, 16);
 
+               surface->stage = (BYTE*) _aligned_malloc(size, 16);
                if (!surface->stage)
                {
-                       _aligned_free(surface->gdi.data);
-                       free(surface);
-                       return CHANNEL_RC_NO_MEMORY;
+                       WLog_ERR(TAG, "%s: unable to allocate stage buffer", __FUNCTION__);
+                       goto out_free_gdidata;
                }
-
                ZeroMemory(surface->stage, size);
+
                surface->image = XCreateImage(xfc->display, xfc->visual, xfc->depth,
                                              ZPixmap, 0, (char*) surface->stage,
                                              surface->gdi.width, surface->gdi.height,
                                              xfc->scanline_pad, surface->stageScanline);
        }
 
+       if (!surface->image)
+       {
+               WLog_ERR(TAG, "%s: an error occurred when creating the XImage", __FUNCTION__);
+               goto error_surface_image;
+       }
+
        surface->gdi.outputMapped = FALSE;
        region16_init(&surface->gdi.invalidRegion);
-       context->SetSurfaceData(context, surface->gdi.surfaceId, (void*) surface);
+       if (context->SetSurfaceData(context, surface->gdi.surfaceId, (void*) surface) < 0)
+       {
+               WLog_ERR(TAG, "%s: an error occurred during SetSurfaceData", __FUNCTION__);
+               goto error_set_surface_data;
+       }
        return CHANNEL_RC_OK;
+
+error_set_surface_data:
+       XFree(surface->image);
+error_surface_image:
+       _aligned_free(surface->stage);
+out_free_gdidata:
+       _aligned_free(surface->gdi.data);
+out_free:
+       free(surface);
+       return ret;
 }
 
 /**
index a03490e..24de3e0 100644 (file)
@@ -27,6 +27,7 @@ struct gdi_gfx_surface
 {
        UINT16 surfaceId;
        rdpCodecs* codecs;
+       H264_CONTEXT *h264;
        UINT32 width;
        UINT32 height;
        BYTE* data;
index be30280..16a7290 100644 (file)
@@ -402,14 +402,22 @@ static UINT gdi_SurfaceCommand_AVC420(rdpGdi* gdi,
                return ERROR_NOT_FOUND;
        }
 
-       bs = (RDPGFX_AVC420_BITMAP_STREAM*) cmd->extra;
+       if (!surface->h264)
+       {
+               surface->h264 = h264_context_new(FALSE);
+               if (!surface->h264)
+               {
+                       WLog_ERR(TAG, "%s: unable to create h264 context", __FUNCTION__);
+                       return ERROR_NOT_ENOUGH_MEMORY;
+               }
+       }
 
+       bs = (RDPGFX_AVC420_BITMAP_STREAM*) cmd->extra;
        if (!bs)
                return ERROR_INTERNAL_ERROR;
 
        meta = &(bs->meta);
-       rc = avc420_decompress(surface->codecs->h264, bs->data, bs->length,
-                              surface->data, surface->format,
+       rc = avc420_decompress(surface->h264, bs->data, bs->length, surface->data, surface->format,
                               surface->scanline, surface->width,
                               surface->height, meta->regionRects,
                               meta->numRegionRects);
@@ -462,6 +470,16 @@ static UINT gdi_SurfaceCommand_AVC444(rdpGdi* gdi, RdpgfxClientContext* context,
                return ERROR_NOT_FOUND;
        }
 
+       if (!surface->h264)
+       {
+               surface->h264 = h264_context_new(FALSE);
+               if (!surface->h264)
+               {
+                       WLog_ERR(TAG, "%s: unable to create h264 context", __FUNCTION__);
+                       return ERROR_NOT_ENOUGH_MEMORY;
+               }
+       }
+
        bs = (RDPGFX_AVC444_BITMAP_STREAM*) cmd->extra;
 
        if (!bs)
@@ -471,7 +489,7 @@ static UINT gdi_SurfaceCommand_AVC444(rdpGdi* gdi, RdpgfxClientContext* context,
        avc2 = &bs->bitstream[1];
        meta1 = &avc1->meta;
        meta2 = &avc2->meta;
-       rc = avc444_decompress(surface->codecs->h264, bs->LC,
+       rc = avc444_decompress(surface->h264, bs->LC,
                               meta1->regionRects, meta1->numRegionRects,
                               avc1->data, avc1->length,
                               meta2->regionRects, meta2->numRegionRects,
@@ -488,16 +506,12 @@ static UINT gdi_SurfaceCommand_AVC444(rdpGdi* gdi, RdpgfxClientContext* context,
 
        for (i = 0; i < meta1->numRegionRects; i++)
        {
-               region16_union_rect(&(surface->invalidRegion),
-                                   &(surface->invalidRegion),
-                                   &(meta1->regionRects[i]));
+               region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &(meta1->regionRects[i]));
        }
 
        for (i = 0; i < meta2->numRegionRects; i++)
        {
-               region16_union_rect(&(surface->invalidRegion),
-                                   &(surface->invalidRegion),
-                                   &(meta2->regionRects[i]));
+               region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &(meta2->regionRects[i]));
        }
 
        if (!gdi->inGfxFrame)
@@ -758,11 +772,11 @@ static UINT gdi_DeleteSurface(RdpgfxClientContext* context,
 {
        rdpCodecs* codecs = NULL;
        gdiGfxSurface* surface = NULL;
-       surface = (gdiGfxSurface*) context->GetSurfaceData(context,
-                 deleteSurface->surfaceId);
+       surface = (gdiGfxSurface*) context->GetSurfaceData(context, deleteSurface->surfaceId);
 
        if (surface)
        {
+               h264_context_free(surface->h264);
                region16_uninit(&surface->invalidRegion);
                codecs = surface->codecs;
                _aligned_free(surface->data);
@@ -772,8 +786,7 @@ static UINT gdi_DeleteSurface(RdpgfxClientContext* context,
        context->SetSurfaceData(context, deleteSurface->surfaceId, NULL);
 
        if (codecs && codecs->progressive)
-               progressive_delete_surface_context(codecs->progressive,
-                                                  deleteSurface->surfaceId);
+               progressive_delete_surface_context(codecs->progressive, deleteSurface->surfaceId);
 
        return CHANNEL_RC_OK;
 }