diff --git a/channels/rdpgfx/server/rdpgfx_main.c b/channels/rdpgfx/server/rdpgfx_main.c index 29ccfdb51..632b9845c 100644 --- a/channels/rdpgfx/server/rdpgfx_main.c +++ b/channels/rdpgfx/server/rdpgfx_main.c @@ -626,9 +626,8 @@ static UINT rdpgfx_write_surface_command(wStream* s, else if (cmd->codecId == RDPGFX_CODECID_AVC444) { havc444 = (RDPGFX_AVC444_BITMAP_STREAM*)cmd->extra; - havc420 = &(havc444->bitstream[0]); - /* avc420EncodedBitstreamInfo (4 bytes) */ - Stream_Write_UINT32(s, havc420->length | (havc444->LC << 30UL)); + havc420 = &(havc444->bitstream[0]); /* avc420EncodedBitstreamInfo (4 bytes) */ + Stream_Write_UINT32(s, havc444->cbAvc420EncodedBitstream1 | (havc444->LC << 30UL)); /* avc420EncodedBitstream1 */ error = rdpgfx_write_h264_avc420(s, havc420); @@ -641,7 +640,7 @@ static UINT rdpgfx_write_surface_command(wStream* s, /* avc420EncodedBitstream2 */ if (havc444->LC == 0) { - havc420 = &(havc444->bitstream[0]); + havc420 = &(havc444->bitstream[1]); error = rdpgfx_write_h264_avc420(s, havc420); if (error != CHANNEL_RC_OK) diff --git a/server/shadow/shadow_client.c b/server/shadow/shadow_client.c index d4a194756..febf0de95 100644 --- a/server/shadow/shadow_client.c +++ b/server/shadow/shadow_client.c @@ -644,7 +644,7 @@ static UINT shadow_client_rdpgfx_caps_advertise(RdpgfxServerContext* context, settings->GfxH264 = FALSE; pdu.capsSet->flags |= RDPGFX_CAPS_FLAG_AVC_DISABLED; #else - settings->GfxH264 = !(flags & RDPGFX_CAPS_FLAG_AVC_DISABLED); + settings->GfxAVC444 = settings->GfxH264 = !(flags & RDPGFX_CAPS_FLAG_AVC_DISABLED); #endif } @@ -670,7 +670,7 @@ static UINT shadow_client_rdpgfx_caps_advertise(RdpgfxServerContext* context, settings->GfxH264 = FALSE; pdu.capsSet->flags |= RDPGFX_CAPS_FLAG_AVC_DISABLED; #else - settings->GfxH264 = !(flags & RDPGFX_CAPS_FLAG_AVC_DISABLED); + settings->GfxAVC444 = settings->GfxH264 = !(flags & RDPGFX_CAPS_FLAG_AVC_DISABLED); #endif } @@ -696,7 +696,7 @@ static UINT shadow_client_rdpgfx_caps_advertise(RdpgfxServerContext* context, settings->GfxH264 = FALSE; pdu.capsSet->flags |= RDPGFX_CAPS_FLAG_AVC_DISABLED; #else - settings->GfxH264 = !(flags & RDPGFX_CAPS_FLAG_AVC_DISABLED); + settings->GfxAVC444 = settings->GfxH264 = !(flags & RDPGFX_CAPS_FLAG_AVC_DISABLED); #endif } @@ -717,6 +717,7 @@ static UINT shadow_client_rdpgfx_caps_advertise(RdpgfxServerContext* context, if (settings) { flags = pdu.capsSet->flags; + settings->GfxAVC444 = FALSE; settings->GfxThinClient = (flags & RDPGFX_CAPS_FLAG_THINCLIENT); settings->GfxSmallCache = (flags & RDPGFX_CAPS_FLAG_SMALL_CACHE); #ifndef WITH_GFX_H264 @@ -755,6 +756,15 @@ static UINT shadow_client_rdpgfx_caps_advertise(RdpgfxServerContext* context, return CHANNEL_RC_UNSUPPORTED_VERSION; } +static INLINE UINT32 rdpgfx_estimate_h264_avc420( + RDPGFX_AVC420_BITMAP_STREAM* havc420) +{ + /* H264 metadata + H264 stream. See rdpgfx_write_h264_avc420 */ + return sizeof(UINT32) /* numRegionRects */ + + 10 /* regionRects + quantQualityVals */ + * havc420->meta.numRegionRects + + havc420->length; +} /** * Function description @@ -801,7 +811,50 @@ static BOOL shadow_client_send_surface_gfx(rdpShadowClient* client, cmd.data = NULL; cmd.extra = NULL; - if (settings->GfxH264) + if (settings->GfxAVC444) + { + RDPGFX_AVC444_BITMAP_STREAM avc444; + RECTANGLE_16 regionRect; + RDPGFX_H264_QUANT_QUALITY quantQualityVal; + + if (shadow_encoder_prepare(encoder, FREERDP_CODEC_AVC444) < 0) + { + WLog_ERR(TAG, "Failed to prepare encoder FREERDP_CODEC_AVC444"); + return FALSE; + } + + if (avc420_compress(encoder->h264, pSrcData, cmd.format, nSrcStep, + nWidth, nHeight, &avc444.bitstream[0].data, + &avc444.bitstream[0].length) < 0) + return FALSE; + + regionRect.left = cmd.left; + regionRect.top = cmd.top; + regionRect.right = cmd.right; + regionRect.bottom = cmd.bottom; + quantQualityVal.qp = encoder->h264->QP; + quantQualityVal.r = 0; + quantQualityVal.p = 0; + quantQualityVal.qualityVal = 100 - quantQualityVal.qp; + avc444.bitstream[0].meta.numRegionRects = 1; + avc444.bitstream[0].meta.regionRects = ®ionRect; + avc444.bitstream[0].meta.quantQualityVals = &quantQualityVal; + avc444.LC = 1; + avc444.cbAvc420EncodedBitstream1 = rdpgfx_estimate_h264_avc420(&avc444.bitstream[0]); + + cmd.codecId = RDPGFX_CODECID_AVC444; + cmd.extra = (void*)&avc444; + + IFCALLRET(client->rdpgfx->SurfaceFrameCommand, error, client->rdpgfx, &cmd, + &cmdstart, &cmdend); + + if (error) + { + WLog_ERR(TAG, "SurfaceFrameCommand failed with error %"PRIu32"", error); + return FALSE; + } + } + else if (settings->GfxH264) { RDPGFX_AVC420_BITMAP_STREAM avc420; RECTANGLE_16 regionRect; @@ -813,8 +866,10 @@ static BOOL shadow_client_send_surface_gfx(rdpShadowClient* client, return FALSE; } - avc420_compress(encoder->h264, pSrcData, cmd.format, nSrcStep, - nWidth, nHeight, &avc420.data, &avc420.length); + if (avc420_compress(encoder->h264, pSrcData, cmd.format, nSrcStep, + nWidth, nHeight, &avc420.data, &avc420.length) < 0) + return FALSE; + cmd.codecId = RDPGFX_CODECID_AVC420; cmd.extra = (void*)&avc420; regionRect.left = cmd.left; diff --git a/server/shadow/shadow_encoder.c b/server/shadow/shadow_encoder.c index b9d1cb55b..cbc191a85 100644 --- a/server/shadow/shadow_encoder.c +++ b/server/shadow/shadow_encoder.c @@ -238,7 +238,7 @@ static int shadow_encoder_init_h264(rdpShadowEncoder* encoder) encoder->h264->BitRate = encoder->server->h264BitRate; encoder->h264->FrameRate = encoder->server->h264FrameRate; encoder->h264->QP = encoder->server->h264QP; - encoder->codecs |= FREERDP_CODEC_AVC420; + encoder->codecs |= FREERDP_CODEC_AVC420 | FREERDP_CODEC_AVC444; return 1; fail: h264_context_free(encoder->h264); @@ -319,7 +319,7 @@ static int shadow_encoder_uninit_h264(rdpShadowEncoder* encoder) encoder->h264 = NULL; } - encoder->codecs &= ~FREERDP_CODEC_AVC420; + encoder->codecs &= ~(FREERDP_CODEC_AVC420 | FREERDP_CODEC_AVC444); return 1; } @@ -353,7 +353,7 @@ static int shadow_encoder_uninit(rdpShadowEncoder* encoder) shadow_encoder_uninit_interleaved(encoder); } - if (encoder->codecs & FREERDP_CODEC_AVC420) + if (encoder->codecs & (FREERDP_CODEC_AVC420 | FREERDP_CODEC_AVC444)) { shadow_encoder_uninit_h264(encoder); } @@ -430,8 +430,8 @@ int shadow_encoder_prepare(rdpShadowEncoder* encoder, UINT32 codecs) return -1; } - if ((codecs & FREERDP_CODEC_AVC420) - && !(encoder->codecs & FREERDP_CODEC_AVC420)) + if ((codecs & (FREERDP_CODEC_AVC420 | FREERDP_CODEC_AVC444)) + && !(encoder->codecs & (FREERDP_CODEC_AVC420 | FREERDP_CODEC_AVC444))) { status = shadow_encoder_init_h264(encoder);