diff --git a/include/freerdp/server/shadow.h b/include/freerdp/server/shadow.h index 9ce5e0063..21c8aadf1 100644 --- a/include/freerdp/server/shadow.h +++ b/include/freerdp/server/shadow.h @@ -94,6 +94,7 @@ struct rdp_shadow_client HANDLE thread; BOOL activated; + BOOL first_frame; BOOL inLobby; BOOL mayView; BOOL mayInteract; diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index 04e72dc2b..96cf0d396 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -862,6 +862,7 @@ typedef struct _RDPDR_PARALLEL RDPDR_PARALLEL; #define FreeRDP_GfxSendQoeAck (3846) #define FreeRDP_GfxAVC444v2 (3847) #define FreeRDP_GfxCapsFilter (3848) +#define FreeRDP_GfxPlanar (3849) #define FreeRDP_BitmapCacheV3CodecId (3904) #define FreeRDP_DrawNineGridEnabled (3968) #define FreeRDP_DrawNineGridCacheSize (3969) @@ -1475,7 +1476,8 @@ struct rdp_settings ALIGN64 BOOL GfxSendQoeAck; /* 3846 */ ALIGN64 BOOL GfxAVC444v2; /* 3847 */ ALIGN64 UINT32 GfxCapsFilter; /* 3848 */ - UINT64 padding3904[3904 - 3849]; /* 3849 */ + ALIGN64 BOOL GfxPlanar; /* 3849 */ + UINT64 padding3904[3904 - 3850]; /* 3850 */ /** * Caches diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c index d0ea54458..d925228b7 100644 --- a/libfreerdp/codec/planar.c +++ b/libfreerdp/codec/planar.c @@ -943,7 +943,7 @@ BOOL planar_decompress(BITMAP_PLANAR_CONTEXT* planar, const BYTE* pSrcData, UINT static INLINE BOOL freerdp_split_color_planes(const BYTE* data, UINT32 format, UINT32 width, UINT32 height, UINT32 scanline, BYTE* planes[4]) { - INT32 i, j, k; + INT64 i, j, k; if ((width > INT32_MAX) || (height > INT32_MAX) || (scanline > INT32_MAX)) return FALSE; @@ -952,11 +952,11 @@ static INLINE BOOL freerdp_split_color_planes(const BYTE* data, UINT32 format, U if (scanline == 0) scanline = width * GetBytesPerPixel(format); - for (i = (INT32)height - 1; i >= 0; i--) + for (i = (INT64)height - 1; i >= 0; i--) { - const BYTE* pixel = &data[(INT32)scanline * i]; + const BYTE* pixel = &data[scanline * (UINT32)i]; - for (j = 0; j < (INT32)width; j++) + for (j = 0; j < (INT64)width; j++) { const UINT32 color = ReadColor(pixel, format); pixel += GetBytesPerPixel(format); diff --git a/libfreerdp/common/settings_getters.c b/libfreerdp/common/settings_getters.c index d5bbd16df..935cad5c8 100644 --- a/libfreerdp/common/settings_getters.c +++ b/libfreerdp/common/settings_getters.c @@ -234,6 +234,9 @@ BOOL freerdp_settings_get_bool(const rdpSettings* settings, size_t id) case FreeRDP_GfxH264: return settings->GfxH264; + case FreeRDP_GfxPlanar: + return settings->GfxPlanar; + case FreeRDP_GfxProgressive: return settings->GfxProgressive; @@ -820,6 +823,10 @@ BOOL freerdp_settings_set_bool(rdpSettings* settings, size_t id, BOOL val) settings->GfxH264 = val; break; + case FreeRDP_GfxPlanar: + settings->GfxPlanar = val; + break; + case FreeRDP_GfxProgressive: settings->GfxProgressive = val; break; diff --git a/libfreerdp/common/settings_str.c b/libfreerdp/common/settings_str.c index 41c2eb066..801b07353 100644 --- a/libfreerdp/common/settings_str.c +++ b/libfreerdp/common/settings_str.c @@ -83,6 +83,7 @@ static const struct settings_str_entry settings_map[] = { { FreeRDP_GfxAVC444, 0, "FreeRDP_GfxAVC444" }, { FreeRDP_GfxAVC444v2, 0, "FreeRDP_GfxAVC444v2" }, { FreeRDP_GfxH264, 0, "FreeRDP_GfxH264" }, + { FreeRDP_GfxPlanar, 0, "FreeRDP_GfxPlanar" }, { FreeRDP_GfxProgressive, 0, "FreeRDP_GfxProgressive" }, { FreeRDP_GfxProgressiveV2, 0, "FreeRDP_GfxProgressiveV2" }, { FreeRDP_GfxSendQoeAck, 0, "FreeRDP_GfxSendQoeAck" }, diff --git a/libfreerdp/core/settings.c b/libfreerdp/core/settings.c index 8275c8bc1..c2235a5dd 100644 --- a/libfreerdp/core/settings.c +++ b/libfreerdp/core/settings.c @@ -558,6 +558,7 @@ rdpSettings* freerdp_settings_new(DWORD flags) !freerdp_settings_set_bool(settings, FreeRDP_GfxSmallCache, FALSE) || !freerdp_settings_set_bool(settings, FreeRDP_GfxProgressive, FALSE) || !freerdp_settings_set_bool(settings, FreeRDP_GfxProgressiveV2, FALSE) || + !freerdp_settings_set_bool(settings, FreeRDP_GfxPlanar, TRUE) || !freerdp_settings_set_bool(settings, FreeRDP_GfxH264, FALSE) || !freerdp_settings_set_bool(settings, FreeRDP_GfxAVC444, FALSE) || !freerdp_settings_set_bool(settings, FreeRDP_GfxSendQoeAck, FALSE)) diff --git a/libfreerdp/core/test/settings_property_lists.h b/libfreerdp/core/test/settings_property_lists.h index e8f2c4a88..72e7abbfd 100644 --- a/libfreerdp/core/test/settings_property_lists.h +++ b/libfreerdp/core/test/settings_property_lists.h @@ -72,6 +72,7 @@ static const size_t bool_list_indices[] = { FreeRDP_GfxAVC444, FreeRDP_GfxAVC444v2, FreeRDP_GfxH264, + FreeRDP_GfxPlanar, FreeRDP_GfxProgressive, FreeRDP_GfxProgressiveV2, FreeRDP_GfxSendQoeAck, diff --git a/server/shadow/shadow.c b/server/shadow/shadow.c index d773c2560..eed59d4dc 100644 --- a/server/shadow/shadow.c +++ b/server/shadow/shadow.c @@ -81,6 +81,10 @@ int main(int argc, char** argv) "NTLM SAM file for NLA authentication" }, { "gfx-progressive", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "Allow GFX progressive codec" }, + { "gfx-rfx", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, + "Allow GFX RFX codec" }, + { "gfx-planar", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, + "Allow GFX planar codec" }, { "gfx-avc420", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "Allow GFX AVC420 codec" }, { "gfx-avc444", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, @@ -114,6 +118,7 @@ int main(int argc, char** argv) /* By default allow all GFX modes. * This can be changed with command line flags [+|-]gfx-CODEC */ + freerdp_settings_set_bool(settings, FreeRDP_RemoteFxCodec, TRUE); freerdp_settings_set_bool(settings, FreeRDP_GfxH264, TRUE); freerdp_settings_set_bool(settings, FreeRDP_GfxAVC444, TRUE); freerdp_settings_set_bool(settings, FreeRDP_GfxAVC444v2, TRUE); diff --git a/server/shadow/shadow_client.c b/server/shadow/shadow_client.c index e3aca6ffc..9ca956e43 100644 --- a/server/shadow/shadow_client.c +++ b/server/shadow/shadow_client.c @@ -138,6 +138,7 @@ static INLINE BOOL shadow_client_rdpgfx_reset_graphic(rdpShadowClient* client) return FALSE; } + client->first_frame = TRUE; return TRUE; } @@ -797,6 +798,8 @@ static BOOL shadow_client_caps_test_version(RdpgfxServerContext* context, rdpSha if (currentCaps->version == capsVersion) { UINT32 flags; + BOOL planar = FALSE; + BOOL rfx = FALSE; BOOL avc444v2 = FALSE; BOOL avc444 = FALSE; BOOL avc420 = FALSE; @@ -825,6 +828,12 @@ static BOOL shadow_client_caps_test_version(RdpgfxServerContext* context, rdpSha progressive = freerdp_settings_get_bool(srvSettings, FreeRDP_GfxProgressiveV2); freerdp_settings_set_bool(clientSettings, FreeRDP_GfxProgressiveV2, progressive); + rfx = freerdp_settings_get_bool(srvSettings, FreeRDP_RemoteFxCodec); + freerdp_settings_set_bool(clientSettings, FreeRDP_RemoteFxCodec, rfx); + + planar = freerdp_settings_get_bool(srvSettings, FreeRDP_GfxPlanar); + freerdp_settings_set_bool(clientSettings, FreeRDP_GfxPlanar, planar); + if (!avc444v2 && !avc444 && !avc420) pdu.capsSet->flags |= RDPGFX_CAPS_FLAG_AVC_DISABLED; @@ -994,10 +1003,11 @@ static INLINE UINT32 rdpgfx_estimate_h264_avc420(RDPGFX_AVC420_BITMAP_STREAM* ha * * @return TRUE on success */ -static BOOL shadow_client_send_surface_gfx(const rdpShadowClient* client, const BYTE* pSrcData, +static BOOL shadow_client_send_surface_gfx(rdpShadowClient* client, const BYTE* pSrcData, UINT32 nSrcStep, UINT32 SrcFormat, UINT16 nXSrc, UINT16 nYSrc, UINT16 nWidth, UINT16 nHeight) { + UINT32 id; UINT error = CHANNEL_RC_OK; const rdpContext* context = (const rdpContext*)client; const rdpSettings* settings; @@ -1016,6 +1026,12 @@ static BOOL shadow_client_send_surface_gfx(const rdpShadowClient* client, const if (!settings || !encoder) return FALSE; + if (client->first_frame) + { + rfx_context_reset(encoder->rfx, nWidth, nHeight); + client->first_frame = FALSE; + } + cmdstart.frameId = shadow_encoder_create_frame_id(encoder); GetSystemTime(&sTime); cmdstart.timestamp = (UINT32)(sTime.wHour << 22U | sTime.wMinute << 16U | sTime.wSecond << 10U | @@ -1035,6 +1051,7 @@ static BOOL shadow_client_send_surface_gfx(const rdpShadowClient* client, const cmd.data = NULL; cmd.extra = NULL; + id = freerdp_settings_get_uint32(settings, FreeRDP_RemoteFxCodecId); if (settings->GfxAVC444 || settings->GfxAVC444v2) { INT32 rc; @@ -1130,6 +1147,57 @@ static BOOL shadow_client_send_surface_gfx(const rdpShadowClient* client, const return FALSE; } } + else if (freerdp_settings_get_bool(settings, FreeRDP_RemoteFxCodec) && (id != 0)) + { + BOOL rc; + wStream* s; + RECTANGLE_16 rect; + + if (shadow_encoder_prepare(encoder, FREERDP_CODEC_REMOTEFX) < 0) + { + WLog_ERR(TAG, "Failed to prepare encoder FREERDP_CODEC_REMOTEFX"); + return FALSE; + } + + s = Stream_New(NULL, 1024); + WINPR_ASSERT(s); + + WINPR_ASSERT(cmd.left <= UINT16_MAX); + WINPR_ASSERT(cmd.top <= UINT16_MAX); + WINPR_ASSERT(cmd.right <= UINT16_MAX); + WINPR_ASSERT(cmd.bottom <= UINT16_MAX); + rect.left = (UINT16)cmd.left; + rect.top = (UINT16)cmd.top; + rect.right = (UINT16)cmd.right; + rect.bottom = (UINT16)cmd.bottom; + + rc = rfx_compose_message(encoder->rfx, s, &rect, 1, pSrcData, nWidth, nHeight, nSrcStep); + + if (!rc) + { + WLog_ERR(TAG, "rfx_compose_message failed"); + Stream_Free(s, TRUE); + return FALSE; + } + + /* rc > 0 means new data */ + if (rc > 0) + { + cmd.codecId = RDPGFX_CODECID_CAVIDEO; + cmd.data = Stream_Buffer(s); + cmd.length = Stream_GetPosition(s); + + IFCALLRET(client->rdpgfx->SurfaceFrameCommand, error, client->rdpgfx, &cmd, &cmdstart, + &cmdend); + } + + Stream_Free(s, TRUE); + if (error) + { + WLog_ERR(TAG, "SurfaceFrameCommand failed with error %" PRIu32 "", error); + return FALSE; + } + } else if (freerdp_settings_get_bool(settings, FreeRDP_GfxProgressive)) { INT32 rc; @@ -1176,6 +1244,47 @@ static BOOL shadow_client_send_surface_gfx(const rdpShadowClient* client, const return FALSE; } } + else if (freerdp_settings_get_bool(settings, FreeRDP_GfxPlanar)) + { + BOOL rc; + const UINT32 w = cmd.right - cmd.left; + const UINT32 h = cmd.bottom - cmd.top; + const size_t step = w * GetBytesPerPixel(SrcFormat); + const size_t size = step * h; + BYTE* dst; + + if (shadow_encoder_prepare(encoder, FREERDP_CODEC_PLANAR) < 0) + { + WLog_ERR(TAG, "Failed to prepare encoder FREERDP_CODEC_PLANAR"); + return FALSE; + } + + rc = freerdp_bitmap_planar_context_reset(encoder->planar, w, h); + WINPR_ASSERT(rc); + + dst = malloc(size); + WINPR_ASSERT(dst); + rc = freerdp_image_copy(dst, SrcFormat, step, 0, 0, w, h, pSrcData, SrcFormat, nSrcStep, + cmd.left, cmd.top, NULL, FREERDP_FLIP_VERTICAL); + WINPR_ASSERT(rc); + + cmd.data = freerdp_bitmap_compress_planar(encoder->planar, dst, SrcFormat, w, h, step, NULL, + &cmd.length); + WINPR_ASSERT(cmd.data || (cmd.length == 0)); + + free(dst); + + cmd.codecId = RDPGFX_CODECID_PLANAR; + + IFCALLRET(client->rdpgfx->SurfaceFrameCommand, error, client->rdpgfx, &cmd, &cmdstart, + &cmdend); + free(cmd.data); + if (error) + { + WLog_ERR(TAG, "SurfaceFrameCommand failed with error %" PRIu32 "", error); + return FALSE; + } + } else { BOOL rc; @@ -1183,10 +1292,10 @@ static BOOL shadow_client_send_surface_gfx(const rdpShadowClient* client, const const UINT32 h = cmd.bottom - cmd.top; const UINT32 length = w * 4 * h; BYTE* data = malloc(length); - if (!data) - return FALSE; - rc = freerdp_image_copy(data, PIXEL_FORMAT_BGRA32, w * 4, 0, 0, w, h, pSrcData, SrcFormat, + WINPR_ASSERT(data); + + rc = freerdp_image_copy(data, PIXEL_FORMAT_BGRA32, 0, 0, 0, w, h, pSrcData, SrcFormat, nSrcStep, cmd.left, cmd.top, NULL, 0); WINPR_ASSERT(rc); @@ -1501,6 +1610,7 @@ static BOOL shadow_client_send_bitmap_update(rdpShadowClient* client, BYTE* pSrc UINT32 dstSize; buffer = encoder->grid[k]; data = &pSrcData[(bitmap->destTop * nSrcStep) + (bitmap->destLeft * 4)]; + buffer = freerdp_bitmap_compress_planar(encoder->planar, data, SrcFormat, bitmap->width, bitmap->height, nSrcStep, buffer, &dstSize); diff --git a/server/shadow/shadow_server.c b/server/shadow/shadow_server.c index c08af4944..135577815 100644 --- a/server/shadow/shadow_server.c +++ b/server/shadow/shadow_server.c @@ -371,6 +371,17 @@ int shadow_server_parse_command_line(rdpShadowServer* server, int argc, char** a arg->Value ? TRUE : FALSE)) return COMMAND_LINE_ERROR; } + CommandLineSwitchCase(arg, "gfx-rfx") + { + if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteFxCodec, + arg->Value ? TRUE : FALSE)) + return COMMAND_LINE_ERROR; + } + CommandLineSwitchCase(arg, "gfx-planar") + { + if (!freerdp_settings_set_bool(settings, FreeRDP_GfxPlanar, arg->Value ? TRUE : FALSE)) + return COMMAND_LINE_ERROR; + } CommandLineSwitchCase(arg, "gfx-avc420") { if (!freerdp_settings_set_bool(settings, FreeRDP_GfxH264, arg->Value ? TRUE : FALSE))