From 746a754244af9310119251e8436cdbd2a90a6caa Mon Sep 17 00:00:00 2001 From: "zihao.jiang" Date: Sun, 24 Apr 2016 01:49:10 +0800 Subject: [PATCH] rdpgfx: Implementation for server side channel server/shadow: support h264 codec with gfx channel --- channels/rdpgfx/CMakeLists.txt | 3 + channels/rdpgfx/client/CMakeLists.txt | 4 +- channels/rdpgfx/{client => }/rdpgfx_common.c | 3 +- channels/rdpgfx/{client => }/rdpgfx_common.h | 0 channels/rdpgfx/server/CMakeLists.txt | 34 + channels/rdpgfx/server/rdpgfx_main.c | 1239 ++++++++++++++++++++++++++ channels/rdpgfx/server/rdpgfx_main.h | 36 + include/freerdp/channels/rdpgfx.h | 9 + include/freerdp/codec/zgfx.h | 7 +- include/freerdp/server/rdpgfx.h | 96 ++ include/freerdp/server/shadow.h | 27 +- libfreerdp/codec/test/TestFreeRDPCodecZGfx.c | 207 +++++ libfreerdp/codec/zgfx.c | 102 ++- server/shadow/CMakeLists.txt | 2 + server/shadow/Mac/mac_shadow.c | 52 +- server/shadow/Win/win_dxgi.c | 5 +- server/shadow/Win/win_rdp.c | 3 +- server/shadow/Win/win_shadow.c | 10 +- server/shadow/X11/x11_shadow.c | 54 +- server/shadow/shadow_channels.c | 10 + server/shadow/shadow_channels.h | 1 + server/shadow/shadow_client.c | 625 +++++++++---- server/shadow/shadow_encoder.c | 98 +- server/shadow/shadow_encoder.h | 1 + server/shadow/shadow_rdpgfx.c | 87 ++ server/shadow/shadow_rdpgfx.h | 38 + server/shadow/shadow_subsystem.c | 5 - 27 files changed, 2423 insertions(+), 335 deletions(-) rename channels/rdpgfx/{client => }/rdpgfx_common.c (98%) rename channels/rdpgfx/{client => }/rdpgfx_common.h (100%) create mode 100644 channels/rdpgfx/server/CMakeLists.txt create mode 100644 channels/rdpgfx/server/rdpgfx_main.c create mode 100644 channels/rdpgfx/server/rdpgfx_main.h create mode 100644 include/freerdp/server/rdpgfx.h create mode 100644 server/shadow/shadow_rdpgfx.c create mode 100644 server/shadow/shadow_rdpgfx.h diff --git a/channels/rdpgfx/CMakeLists.txt b/channels/rdpgfx/CMakeLists.txt index acc50da..04820de 100644 --- a/channels/rdpgfx/CMakeLists.txt +++ b/channels/rdpgfx/CMakeLists.txt @@ -21,3 +21,6 @@ if(WITH_CLIENT_CHANNELS) add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME}) endif() +if(WITH_SERVER_CHANNELS) + add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME}) +endif() diff --git a/channels/rdpgfx/client/CMakeLists.txt b/channels/rdpgfx/client/CMakeLists.txt index 19c5d21..0de358a 100644 --- a/channels/rdpgfx/client/CMakeLists.txt +++ b/channels/rdpgfx/client/CMakeLists.txt @@ -22,8 +22,8 @@ set(${MODULE_PREFIX}_SRCS rdpgfx_main.h rdpgfx_codec.c rdpgfx_codec.h - rdpgfx_common.c - rdpgfx_common.h) + ../rdpgfx_common.c + ../rdpgfx_common.h) include_directories(..) diff --git a/channels/rdpgfx/client/rdpgfx_common.c b/channels/rdpgfx/rdpgfx_common.c similarity index 98% rename from channels/rdpgfx/client/rdpgfx_common.c rename to channels/rdpgfx/rdpgfx_common.c index a7518a5..849554d 100644 --- a/channels/rdpgfx/client/rdpgfx_common.c +++ b/channels/rdpgfx/rdpgfx_common.c @@ -54,7 +54,8 @@ const char* RDPGFX_CMDID_STRINGS[] = "RDPGFX_CMDID_CAPSADVERTISE", "RDPGFX_CMDID_CAPSCONFIRM", "RDPGFX_CMDID_UNUSED_0014", - "RDPGFX_CMDID_MAPSURFACETOWINDOW" + "RDPGFX_CMDID_MAPSURFACETOWINDOW", + "RDPGFX_CMDID_QOEFRAMEACKNOWLEDGE" }; const char* rdpgfx_get_cmd_id_string(UINT16 cmdId) diff --git a/channels/rdpgfx/client/rdpgfx_common.h b/channels/rdpgfx/rdpgfx_common.h similarity index 100% rename from channels/rdpgfx/client/rdpgfx_common.h rename to channels/rdpgfx/rdpgfx_common.h diff --git a/channels/rdpgfx/server/CMakeLists.txt b/channels/rdpgfx/server/CMakeLists.txt new file mode 100644 index 0000000..b4aadc2 --- /dev/null +++ b/channels/rdpgfx/server/CMakeLists.txt @@ -0,0 +1,34 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +define_channel_server("rdpgfx") + +set(${MODULE_PREFIX}_SRCS + rdpgfx_main.c + rdpgfx_main.h + ../rdpgfx_common.c + ../rdpgfx_common.h) + +include_directories(..) + +add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DVCPluginEntry") + + + +target_link_libraries(${MODULE_NAME} freerdp) + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server") diff --git a/channels/rdpgfx/server/rdpgfx_main.c b/channels/rdpgfx/server/rdpgfx_main.c new file mode 100644 index 0000000..ee31262 --- /dev/null +++ b/channels/rdpgfx/server/rdpgfx_main.c @@ -0,0 +1,1239 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Graphics Pipeline Extension + * + * Copyright 2016 Jiang Zihao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "rdpgfx_common.h" +#include "rdpgfx_main.h" + +#define TAG CHANNELS_TAG("rdpgfx.server") +#define RDPGFX_RESET_GRAPHICS_PDU_SIZE 340 + +/** + * Function description + * Create new stream for rdpgfx packet. The new stream length + * would be required data length + header. The header will be written + * to the stream before return, but the pduLength field might be + * changed in rdpgfx_server_packet_send. + * + * @return new stream + */ +static wStream* rdpgfx_server_packet_new(UINT16 cmdId, UINT32 dataLen) +{ + UINT error; + wStream* s; + RDPGFX_HEADER header; + + header.flags = 0; + header.cmdId = cmdId; + header.pduLength = RDPGFX_HEADER_SIZE + dataLen; + + s = Stream_New(NULL, header.pduLength); + + if(!s) + { + WLog_ERR(TAG, "Stream_New failed!"); + return NULL; + } + + /* Write header. Note that actual length will be filled + * after the entire packet has been constructed. */ + if ((error = rdpgfx_write_header(s, &header))) + { + WLog_ERR(TAG, "rdpgfx_write_header failed with error %lu!", error); + return NULL; + } + + return s; +} + +/** + * Function description + * Send the stream for rdpgfx packet. The packet would be compressed + * according to [MS-RDPEGFX]. + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT rdpgfx_server_packet_send(RdpgfxServerContext* context, wStream* s) +{ + UINT error; + UINT32 flags = 0; + ULONG written; + BYTE* pSrcData = Stream_Buffer(s); + UINT32 SrcSize = Stream_GetPosition(s); + wStream* fs; + + /* Fill actual length */ + Stream_SetPosition(s, sizeof(RDPGFX_HEADER) - sizeof(UINT32)); + Stream_Write_UINT32(s, SrcSize); /* pduLength (4 bytes) */ + + /* Allocate new stream with enough capacity. Additional overhead is + * descriptor (1 bytes) + segmentCount (2 bytes) + uncompressedSize (4 bytes) + * + segmentCount * size (4 bytes) */ + fs = Stream_New(NULL, SrcSize + 7 + + (SrcSize/ZGFX_SEGMENTED_MAXSIZE + 1) * 4); + + if (zgfx_compress_to_stream(context->priv->zgfx, fs, pSrcData, SrcSize, &flags) < 0) + { + WLog_ERR(TAG, "zgfx_compress_to_stream failed!"); + error = ERROR_INTERNAL_ERROR; + goto out; + } + + error = WTSVirtualChannelWrite(context->priv->rdpgfx_channel, (PCHAR) Stream_Buffer(fs), + Stream_GetPosition(fs), &written) + ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR; + +out: + Stream_Free(fs, TRUE); + Stream_Free(s, TRUE); + + return error; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT rdpgfx_send_caps_confirm_pdu(RdpgfxServerContext* context, RDPGFX_CAPS_CONFIRM_PDU* capsConfirm) +{ + RDPGFX_CAPSET* capsSet = capsConfirm->capsSet; + wStream* s = rdpgfx_server_packet_new( + RDPGFX_CMDID_CAPSCONFIRM, + sizeof(RDPGFX_CAPSET) + sizeof(capsSet->flags)); + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT32(s, capsSet->version); /* version (4 bytes) */ + Stream_Write_UINT32(s, sizeof(capsSet->flags)); /* capsDataLength (4 bytes) */ + Stream_Write_UINT32(s, capsSet->flags); /* capsData (4 bytes) */ + + return rdpgfx_server_packet_send(context, s); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT rdpgfx_send_reset_graphics_pdu(RdpgfxServerContext* context, RDPGFX_RESET_GRAPHICS_PDU* pdu) +{ + UINT32 index; + MONITOR_DEF* monitor; + wStream* s = rdpgfx_server_packet_new( + RDPGFX_CMDID_RESETGRAPHICS, + RDPGFX_RESET_GRAPHICS_PDU_SIZE - RDPGFX_HEADER_SIZE); + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT32(s, pdu->width); /* width (4 bytes) */ + Stream_Write_UINT32(s, pdu->height); /* height (4 bytes) */ + Stream_Write_UINT32(s, pdu->monitorCount); /* monitorCount (4 bytes) */ + + for (index = 0; index < pdu->monitorCount; index++) + { + monitor = &(pdu->monitorDefArray[index]); + Stream_Write_UINT32(s, monitor->left); /* left (4 bytes) */ + Stream_Write_UINT32(s, monitor->top); /* top (4 bytes) */ + Stream_Write_UINT32(s, monitor->right); /* right (4 bytes) */ + Stream_Write_UINT32(s, monitor->bottom); /* bottom (4 bytes) */ + Stream_Write_UINT32(s, monitor->flags); /* flags (4 bytes) */ + } + + /* pad (total size must be 340 bytes) */ + if (Stream_GetPosition(s) > RDPGFX_RESET_GRAPHICS_PDU_SIZE) + { + WLog_ERR(TAG, "Invalid RDPGFX_RESET_GRAPHICS_PDU data!"); + return CHANNEL_RC_NO_BUFFER; + } + Stream_SetPosition(s, RDPGFX_RESET_GRAPHICS_PDU_SIZE); + + return rdpgfx_server_packet_send(context, s); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT rdpgfx_send_evict_cache_entry_pdu(RdpgfxServerContext* context, RDPGFX_EVICT_CACHE_ENTRY_PDU* pdu) +{ + wStream* s = rdpgfx_server_packet_new( + RDPGFX_CMDID_EVICTCACHEENTRY, + sizeof(RDPGFX_EVICT_CACHE_ENTRY_PDU)); + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT16(s, pdu->cacheSlot); /* cacheSlot (2 bytes) */ + + return rdpgfx_server_packet_send(context, s); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT rdpgfx_send_cache_import_reply_pdu(RdpgfxServerContext* context, RDPGFX_CACHE_IMPORT_REPLY_PDU* pdu) +{ + UINT16 index; + wStream* s = rdpgfx_server_packet_new( + RDPGFX_CMDID_CACHEIMPORTREPLY, + sizeof(RDPGFX_CACHE_IMPORT_REPLY_PDU) + sizeof(UINT16) * pdu->importedEntriesCount); + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT16(s, pdu->importedEntriesCount); /* importedEntriesCount (2 bytes) */ + for (index = 0; index < pdu->importedEntriesCount; index++) + { + Stream_Write_UINT16(s, pdu->cacheSlots[index]); /* cacheSlot (2 bytes) */ + } + + return rdpgfx_server_packet_send(context, s); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT rdpgfx_send_create_surface_pdu(RdpgfxServerContext* context, RDPGFX_CREATE_SURFACE_PDU* pdu) +{ + wStream* s = rdpgfx_server_packet_new( + RDPGFX_CMDID_CREATESURFACE, + sizeof(RDPGFX_CREATE_SURFACE_PDU)); + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT16(s, pdu->surfaceId); /* surfaceId (2 bytes) */ + Stream_Write_UINT16(s, pdu->width); /* width (2 bytes) */ + Stream_Write_UINT16(s, pdu->height); /* height (2 bytes) */ + Stream_Write_UINT8(s, pdu->pixelFormat); /* RDPGFX_PIXELFORMAT (1 byte) */ + + return rdpgfx_server_packet_send(context, s); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +UINT rdpgfx_send_delete_surface_pdu(RdpgfxServerContext* context, RDPGFX_DELETE_SURFACE_PDU* pdu) +{ + wStream* s = rdpgfx_server_packet_new( + RDPGFX_CMDID_DELETESURFACE, + sizeof(RDPGFX_DELETE_SURFACE_PDU)); + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT16(s, pdu->surfaceId); /* surfaceId (2 bytes) */ + + return rdpgfx_server_packet_send(context, s); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT rdpgfx_send_start_frame_pdu(RdpgfxServerContext* context, RDPGFX_START_FRAME_PDU* pdu) +{ + wStream* s = rdpgfx_server_packet_new( + RDPGFX_CMDID_STARTFRAME, + sizeof(RDPGFX_START_FRAME_PDU)); + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT32(s, pdu->timestamp); /* timestamp (4 bytes) */ + Stream_Write_UINT32(s, pdu->frameId); /* frameId (4 bytes) */ + + return rdpgfx_server_packet_send(context, s); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT rdpgfx_send_end_frame_pdu(RdpgfxServerContext* context, RDPGFX_END_FRAME_PDU* pdu) +{ + wStream* s = rdpgfx_server_packet_new( + RDPGFX_CMDID_ENDFRAME, + sizeof(RDPGFX_END_FRAME_PDU)); + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT32(s, pdu->frameId); /* frameId (4 bytes) */ + + return rdpgfx_server_packet_send(context, s); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT rdpgfx_write_h264_metablock(RdpgfxServerContext* context, wStream* s, + RDPGFX_H264_METABLOCK* meta) +{ + UINT32 index; + RECTANGLE_16* regionRect; + RDPGFX_H264_QUANT_QUALITY* quantQualityVal; + UINT error = CHANNEL_RC_OK; + + Stream_Write_UINT32(s, meta->numRegionRects); /* numRegionRects (4 bytes) */ + + for (index = 0; index < meta->numRegionRects; index++) + { + regionRect = &(meta->regionRects[index]); + if ((error = rdpgfx_write_rect16(s, regionRect))) + { + WLog_ERR(TAG, "rdpgfx_write_rect16 failed with error %lu!", error); + return error; + } + } + + for (index = 0; index < meta->numRegionRects; index++) + { + quantQualityVal = &(meta->quantQualityVals[index]); + + Stream_Write_UINT8(s, quantQualityVal->qp + | (quantQualityVal->r << 6) + | (quantQualityVal->p << 7)); /* qpVal (1 byte) */ + Stream_Write_UINT8(s, quantQualityVal->qualityVal); /* qualityVal (1 byte) */ + } + + return error; +} + +/** + * Function description + * Write RFX_AVC420_BITMAP_STREAM structure to stream + * + * @return 0 on success, otherwise a Win32 error code + */ +static INLINE UINT rdpgfx_write_h264_avc420(RdpgfxServerContext* context, wStream* s, + RDPGFX_AVC420_BITMAP_STREAM* havc420) +{ + UINT error = CHANNEL_RC_OK; + if ((error = rdpgfx_write_h264_metablock(context, s, &(havc420->meta)))) + { + WLog_ERR(TAG, "rdpgfx_write_h264_metablock failed with error %lu!", error); + return error; + } + + Stream_Write(s, havc420->data, havc420->length); + return error; +} + +/** + * Function description + * Estimate RFX_AVC420_BITMAP_STREAM structure size in stream + * + * @return estimated size + */ +static INLINE UINT32 rdpgfx_estimate_h264_avc420(RDPGFX_AVC420_BITMAP_STREAM* havc420) +{ + return sizeof(UINT32) /* numRegionRects */ + + (sizeof(RECTANGLE_16) + 2) /* regionRects + quantQualityVals */ + * havc420->meta.numRegionRects + + havc420->length; +} +/** + * Function description + * Send RDPGFX_CMDID_WIRETOSURFACE_1 or RDPGFX_CMDID_WIRETOSURFACE_2 + * message according to codecId + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT rdpgfx_send_surface_command(RdpgfxServerContext* context, RDPGFX_SURFACE_COMMAND* cmd) +{ + wStream* s; + RDPGFX_AVC420_BITMAP_STREAM *havc420 = NULL; + RDPGFX_AVC444_BITMAP_STREAM *havc444 = NULL; + UINT32 h264Size = 0; + UINT32 bitmapDataStart = 0; + UINT32 bitmapDataLength = 0; + + /* Create new stream according to codec. */ + if (cmd->codecId == RDPGFX_CODECID_CAPROGRESSIVE || + cmd->codecId == RDPGFX_CODECID_CAPROGRESSIVE_V2) + { + s = rdpgfx_server_packet_new( + RDPGFX_CMDID_WIRETOSURFACE_2, + sizeof(RDPGFX_WIRE_TO_SURFACE_PDU_2) + cmd->length); + } + else if (cmd->codecId == RDPGFX_CODECID_AVC420) + { + havc420 = (RDPGFX_AVC420_BITMAP_STREAM *)cmd->extra; + h264Size = rdpgfx_estimate_h264_avc420(havc420); + + s = rdpgfx_server_packet_new( + RDPGFX_CMDID_WIRETOSURFACE_1, + sizeof(RDPGFX_WIRE_TO_SURFACE_PDU_1) + h264Size); + } + else if (cmd->codecId == RDPGFX_CODECID_AVC444) + { + havc444 = (RDPGFX_AVC444_BITMAP_STREAM *)cmd->extra; + h264Size = sizeof(UINT32); /* cbAvc420EncodedBitstream1 */ + + /* avc420EncodedBitstream1 */ + havc420 = &(havc444->bitstream[0]); + h264Size += rdpgfx_estimate_h264_avc420(havc420); + + /* avc420EncodedBitstream2 */ + if (havc444->LC == 0) + { + havc420 = &(havc444->bitstream[1]); + h264Size += rdpgfx_estimate_h264_avc420(havc420); + } + + s = rdpgfx_server_packet_new( + RDPGFX_CMDID_WIRETOSURFACE_1, + sizeof(RDPGFX_WIRE_TO_SURFACE_PDU_1) + h264Size); + } + else + { + s = rdpgfx_server_packet_new( + RDPGFX_CMDID_WIRETOSURFACE_1, + sizeof(RDPGFX_WIRE_TO_SURFACE_PDU_1) + sizeof(cmd->length)); + } + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + /* Reformat and send */ + if (cmd->codecId == RDPGFX_CODECID_CAPROGRESSIVE || + cmd->codecId == RDPGFX_CODECID_CAPROGRESSIVE_V2) + { + Stream_Write_UINT16(s, cmd->surfaceId); /* surfaceId (2 bytes) */ + Stream_Write_UINT16(s, cmd->codecId); /* codecId (2 bytes) */ + Stream_Write_UINT32(s, cmd->contextId); /* codecContextId (4 bytes) */ + Stream_Write_UINT8(s, cmd->format); /* pixelFormat (1 byte) */ + + Stream_Write_UINT32(s, cmd->length); /* bitmapDataLength (4 bytes) */ + Stream_Write(s, cmd->data, cmd->length); + + return rdpgfx_server_packet_send(context, s); + } + else + { + Stream_Write_UINT16(s, cmd->surfaceId); /* surfaceId (2 bytes) */ + Stream_Write_UINT16(s, cmd->codecId); /* codecId (2 bytes) */ + Stream_Write_UINT8(s, cmd->format); /* pixelFormat (1 byte) */ + + Stream_Write_UINT16(s, cmd->left); /* left (2 bytes) */ + Stream_Write_UINT16(s, cmd->top); /* top (2 bytes) */ + Stream_Write_UINT16(s, cmd->right); /* right (2 bytes) */ + Stream_Write_UINT16(s, cmd->bottom); /* bottom (2 bytes) */ + + Stream_Write_UINT32(s, cmd->length); /* bitmapDataLength (4 bytes) */ + bitmapDataStart = Stream_GetPosition(s); + + if (cmd->codecId == RDPGFX_CODECID_AVC420) + { + havc420 = (RDPGFX_AVC420_BITMAP_STREAM *)cmd->extra; + rdpgfx_write_h264_avc420(context, s, havc420); + } + 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)); + + /* avc420EncodedBitstream1 */ + rdpgfx_write_h264_avc420(context, s, havc420); + + /* avc420EncodedBitstream2 */ + if (havc444->LC == 0) + { + havc420 = &(havc444->bitstream[0]); + rdpgfx_write_h264_avc420(context, s, havc420); + } + } + else + { + Stream_Write(s, cmd->data, cmd->length); + } + + assert(Stream_GetPosition(s) <= Stream_Capacity(s)); + + /* Fill actual bitmap data length */ + bitmapDataLength = Stream_GetPosition(s) - bitmapDataStart; + Stream_SetPosition(s, bitmapDataStart - sizeof(UINT32)); + Stream_Write_UINT32(s, bitmapDataLength); /* bitmapDataLength (4 bytes) */ + Stream_Seek(s, bitmapDataLength); + + return rdpgfx_server_packet_send(context, s); + } +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT rdpgfx_send_delete_encoding_context_pdu(RdpgfxServerContext* context, RDPGFX_DELETE_ENCODING_CONTEXT_PDU* pdu) +{ + wStream* s = rdpgfx_server_packet_new( + RDPGFX_CMDID_DELETEENCODINGCONTEXT, + sizeof(RDPGFX_DELETE_ENCODING_CONTEXT_PDU)); + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT16(s, pdu->surfaceId); /* surfaceId (2 bytes) */ + Stream_Write_UINT32(s, pdu->codecContextId); /* codecContextId (4 bytes) */ + + return rdpgfx_server_packet_send(context, s); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +UINT rdpgfx_send_solid_fill_pdu(RdpgfxServerContext* context, RDPGFX_SOLID_FILL_PDU* pdu) +{ + UINT16 index; + RECTANGLE_16* fillRect; + UINT error; + wStream* s = rdpgfx_server_packet_new( + RDPGFX_CMDID_SOLIDFILL, + sizeof(RDPGFX_SOLID_FILL_PDU) + sizeof(RECTANGLE_16) * pdu->fillRectCount); + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT16(s, pdu->surfaceId); /* surfaceId (2 bytes) */ + if ((error = rdpgfx_write_color32(s, &(pdu->fillPixel)))) /* fillPixel (4 bytes) */ + { + WLog_ERR(TAG, "rdpgfx_write_color32 failed with error %lu!", error); + return error; + } + + Stream_Write_UINT16(s, pdu->fillRectCount); /* fillRectCount (2 bytes) */ + for (index = 0; index < pdu->fillRectCount; index++) + { + fillRect = &(pdu->fillRects[index]); + if ((error = rdpgfx_write_rect16(s, fillRect))) + { + WLog_ERR(TAG, "rdpgfx_write_rect16 failed with error %lu!", error); + return error; + } + } + + return rdpgfx_server_packet_send(context, s); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT rdpgfx_send_surface_to_surface_pdu(RdpgfxServerContext* context, RDPGFX_SURFACE_TO_SURFACE_PDU* pdu) +{ + UINT16 index; + UINT error; + RDPGFX_POINT16* destPt; + wStream* s = rdpgfx_server_packet_new( + RDPGFX_CMDID_SURFACETOSURFACE, + sizeof(RDPGFX_SURFACE_TO_SURFACE_PDU) + sizeof(RDPGFX_POINT16) * pdu->destPtsCount); + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT16(s, pdu->surfaceIdSrc); /* surfaceIdSrc (2 bytes) */ + Stream_Write_UINT16(s, pdu->surfaceIdDest); /* surfaceIdDest (2 bytes) */ + + if ((error = rdpgfx_write_rect16(s, &(pdu->rectSrc)))) /* rectSrc (8 bytes ) */ + { + WLog_ERR(TAG, "rdpgfx_write_rect16 failed with error %lu!", error); + return error; + } + + Stream_Write_UINT16(s, pdu->destPtsCount); /* destPtsCount (2 bytes) */ + for (index = 0; index < pdu->destPtsCount; index++) + { + destPt = &(pdu->destPts[index]); + if ((error = rdpgfx_write_point16(s, destPt))) + { + WLog_ERR(TAG, "rdpgfx_write_point16 failed with error %lu!", error); + return error; + } + } + + return rdpgfx_server_packet_send(context, s); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT rdpgfx_send_surface_to_cache_pdu(RdpgfxServerContext* context, RDPGFX_SURFACE_TO_CACHE_PDU* pdu) +{ + UINT error; + wStream* s = rdpgfx_server_packet_new( + RDPGFX_CMDID_SURFACETOCACHE, + sizeof(RDPGFX_SURFACE_TO_CACHE_PDU)); + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT16(s, pdu->surfaceId); /* surfaceId (2 bytes) */ + Stream_Write_UINT64(s, pdu->cacheKey); /* cacheKey (8 bytes) */ + Stream_Write_UINT16(s, pdu->cacheSlot); /* cacheSlot (2 bytes) */ + if ((error = rdpgfx_write_rect16(s, &(pdu->rectSrc)))) /* rectSrc (8 bytes ) */ + { + WLog_ERR(TAG, "rdpgfx_write_rect16 failed with error %lu!", error); + return error; + } + + return rdpgfx_server_packet_send(context, s); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT rdpgfx_send_cache_to_surface_pdu(RdpgfxServerContext* context, RDPGFX_CACHE_TO_SURFACE_PDU* pdu) +{ + UINT16 index; + UINT error; + RDPGFX_POINT16* destPt; + wStream* s = rdpgfx_server_packet_new( + RDPGFX_CMDID_CACHETOSURFACE, + sizeof(RDPGFX_CACHE_TO_SURFACE_PDU) + sizeof(RDPGFX_POINT16) * pdu->destPtsCount); + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT16(s, pdu->cacheSlot); /* cacheSlot (2 bytes) */ + Stream_Write_UINT16(s, pdu->surfaceId); /* surfaceId (2 bytes) */ + Stream_Write_UINT16(s, pdu->destPtsCount); /* destPtsCount (2 bytes) */ + + for (index = 0; index < pdu->destPtsCount; index++) + { + destPt = &(pdu->destPts[index]); + if ((error = rdpgfx_write_point16(s, destPt))) + { + WLog_ERR(TAG, "rdpgfx_write_point16 failed with error %lu", error); + return error; + } + } + + return rdpgfx_server_packet_send(context, s); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT rdpgfx_send_map_surface_to_output_pdu(RdpgfxServerContext* context, RDPGFX_MAP_SURFACE_TO_OUTPUT_PDU* pdu) +{ + wStream* s = rdpgfx_server_packet_new( + RDPGFX_CMDID_MAPSURFACETOOUTPUT, + sizeof(RDPGFX_MAP_SURFACE_TO_OUTPUT_PDU)); + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT16(s, pdu->surfaceId); /* surfaceId (2 bytes) */ + Stream_Write_UINT16(s, 0); /* reserved (2 bytes). Must be 0 */ + Stream_Write_UINT32(s, pdu->outputOriginX); /* outputOriginX (4 bytes) */ + Stream_Write_UINT32(s, pdu->outputOriginY); /* outputOriginY (4 bytes) */ + + return rdpgfx_server_packet_send(context, s); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT rdpgfx_send_map_surface_to_window_pdu(RdpgfxServerContext* context, RDPGFX_MAP_SURFACE_TO_WINDOW_PDU* pdu) +{ + wStream* s = rdpgfx_server_packet_new( + RDPGFX_CMDID_MAPSURFACETOWINDOW, + sizeof(RDPGFX_MAP_SURFACE_TO_WINDOW_PDU)); + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT16(s, pdu->surfaceId); /* surfaceId (2 bytes) */ + Stream_Write_UINT64(s, pdu->windowId); /* windowId (8 bytes) */ + Stream_Write_UINT32(s, pdu->mappedWidth); /* mappedWidth (4 bytes) */ + Stream_Write_UINT32(s, pdu->mappedHeight); /* mappedHeight (4 bytes) */ + + return rdpgfx_server_packet_send(context, s); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT rdpgfx_recv_frame_acknowledge_pdu(RdpgfxServerContext* context, wStream* s) +{ + RDPGFX_FRAME_ACKNOWLEDGE_PDU pdu; + UINT error = CHANNEL_RC_OK; + + Stream_Read_UINT32(s, pdu.queueDepth); /* queueDepth (4 bytes) */ + Stream_Read_UINT32(s, pdu.frameId); /* frameId (4 bytes) */ + Stream_Read_UINT32(s, pdu.totalFramesDecoded); /* totalFramesDecoded (4 bytes) */ + + if (context) + { + IFCALLRET(context->FrameAcknowledge, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->FrameAcknowledge failed with error %lu", error); + } + + return error; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT rdpgfx_recv_cache_import_offer_pdu(RdpgfxServerContext* context, wStream* s) +{ + UINT16 index; + RDPGFX_CACHE_IMPORT_OFFER_PDU pdu; + RDPGFX_CACHE_ENTRY_METADATA* cacheEntries; + UINT error = CHANNEL_RC_OK; + + Stream_Read_UINT16(s, pdu.cacheEntriesCount); /* cacheEntriesCount (2 bytes) */ + pdu.cacheEntries = (RDPGFX_CACHE_ENTRY_METADATA*) + calloc(pdu.cacheEntriesCount, sizeof(RDPGFX_CACHE_ENTRY_METADATA)); + + if (!pdu.cacheEntries) + { + WLog_ERR(TAG, "calloc failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + for (index = 0; index < pdu.cacheEntriesCount; index++) + { + cacheEntries = &(pdu.cacheEntries[index]); + Stream_Read_UINT64(s, cacheEntries->cacheKey); /* cacheKey (8 bytes) */ + Stream_Read_UINT32(s, cacheEntries->bitmapLength); /* bitmapLength (4 bytes) */ + } + + if (context) + { + IFCALLRET(context->CacheImportOffer, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->CacheImportOffer failed with error %lu", error); + } + + free(pdu.cacheEntries); + + return error; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT rdpgfx_recv_caps_advertise_pdu(RdpgfxServerContext* context, wStream* s) +{ + UINT16 index; + RDPGFX_CAPSET* capsSet; + RDPGFX_CAPSET capsSets[3]; + RDPGFX_CAPS_ADVERTISE_PDU pdu; + UINT error = CHANNEL_RC_OK; + + Stream_Read_UINT16(s, pdu.capsSetCount); /* capsSetCount (2 bytes) */ + pdu.capsSets = (RDPGFX_CAPSET*) capsSets; + + for (index = 0; index < pdu.capsSetCount; index++) + { + capsSet = &(pdu.capsSets[index]); + Stream_Read_UINT32(s, capsSet->version); /* version (4 bytes) */ + Stream_Seek(s, 4); /* capsDataLength (4 bytes) */ + Stream_Read_UINT32(s, capsSet->flags); /* capsData (4 bytes) */ + } + + if (context) + { + IFCALLRET(context->CapsAdvertise, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->CapsAdvertise failed with error %lu", error); + } + + return error; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT rdpgfx_recv_qoe_frame_acknowledge_pdu(RdpgfxServerContext* context, wStream* s) +{ + RDPGFX_QOE_FRAME_ACKNOWLEDGE_PDU pdu; + UINT error = CHANNEL_RC_OK; + + Stream_Read_UINT32(s, pdu.frameId); /* frameId (4 bytes) */ + Stream_Read_UINT32(s, pdu.timestamp); /* timestamp (4 bytes) */ + Stream_Read_UINT16(s, pdu.timeDiffSE); /* timeDiffSE (2 bytes) */ + Stream_Read_UINT16(s, pdu.timeDiffEDR); /* timeDiffEDR (2 bytes) */ + + if (context) + { + IFCALLRET(context->QoeFrameAcknowledge, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->QoeFrameAcknowledge failed with error %lu", error); + } + + return error; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT rdpgfx_server_receive_pdu(RdpgfxServerContext* context, wStream* s) +{ + int beg, end; + RDPGFX_HEADER header; + UINT error = CHANNEL_RC_OK; + + beg = Stream_GetPosition(s); + + if ((error = rdpgfx_read_header(s, &header))) + { + WLog_ERR(TAG, "rdpgfx_read_header failed with error %lu!", error); + return error; + } + + WLog_DBG(TAG, "cmdId: %s (0x%04X) flags: 0x%04X pduLength: %d", + rdpgfx_get_cmd_id_string(header.cmdId), header.cmdId, header.flags, header.pduLength); + + switch (header.cmdId) + { + case RDPGFX_CMDID_FRAMEACKNOWLEDGE: + if ((error = rdpgfx_recv_frame_acknowledge_pdu(context, s))) + WLog_ERR(TAG, "rdpgfx_recv_frame_acknowledge_pdu failed with error %lu!", error); + break; + + case RDPGFX_CMDID_CACHEIMPORTOFFER: + if ((error = rdpgfx_recv_cache_import_offer_pdu(context, s))) + WLog_ERR(TAG, "rdpgfx_recv_cache_import_offer_pdu failed with error %lu!", error); + break; + + case RDPGFX_CMDID_CAPSADVERTISE: + if ((error = rdpgfx_recv_caps_advertise_pdu(context, s))) + WLog_ERR(TAG, "rdpgfx_recv_caps_advertise_pdu failed with error %lu!", error); + break; + + case RDPGFX_CMDID_QOEFRAMEACKNOWLEDGE: + if ((error = rdpgfx_recv_qoe_frame_acknowledge_pdu(context, s))) + WLog_ERR(TAG, "rdpgfx_recv_qoe_frame_acknowledge_pdu failed with error %lu!", error); + break; + + default: + error = CHANNEL_RC_BAD_PROC; + break; + } + + if (error) + { + WLog_ERR(TAG, "Error while parsing GFX cmdId: %s (0x%04X)", + rdpgfx_get_cmd_id_string(header.cmdId), header.cmdId); + return error; + } + + end = Stream_GetPosition(s); + + if (end != (beg + header.pduLength)) + { + WLog_ERR(TAG, "Unexpected gfx pdu end: Actual: %d, Expected: %d", + end, (beg + header.pduLength)); + Stream_SetPosition(s, (beg + header.pduLength)); + } + + return error; + +} + +static void* rdpgfx_server_thread_func(void* arg) +{ + RdpgfxServerContext* context = (RdpgfxServerContext*) arg; + RdpgfxServerPrivate* priv = context->priv; + wStream* s; + DWORD status; + DWORD nCount; + void* buffer; + HANDLE events[8]; + HANDLE ChannelEvent; + DWORD BytesReturned = 0; + UINT error = CHANNEL_RC_OK; + BOOL ready = FALSE; + + buffer = NULL; + BytesReturned = 0; + ChannelEvent = NULL; + + /* Query for channel event handle */ + if (WTSVirtualChannelQuery(priv->rdpgfx_channel, WTSVirtualEventHandle, &buffer, &BytesReturned) == TRUE) + { + if (BytesReturned == sizeof(HANDLE)) + CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE)); + + WTSFreeMemory(buffer); + } + else + { + WLog_ERR(TAG, "WTSVirtualChannelQuery failed"); + error = ERROR_INTERNAL_ERROR; + goto out; + } + + nCount = 0; + events[nCount++] = priv->stopEvent; + events[nCount++] = ChannelEvent; + + /* Wait for the client to confirm that the dynamic channel is ready */ + while (1) + { + if ((status = WaitForMultipleObjects(nCount, events, FALSE, 100)) == WAIT_OBJECT_0) + goto out; + + if (status == WAIT_FAILED) + { + error = GetLastError(); + WLog_ERR(TAG, "WaitForMultipleObjects failed with error %lu", error); + goto out; + } + + if (WTSVirtualChannelQuery(priv->rdpgfx_channel, WTSVirtualChannelReady, &buffer, &BytesReturned) == FALSE) + { + WLog_ERR(TAG, "WTSVirtualChannelQuery failed"); + error = ERROR_INTERNAL_ERROR; + goto out; + } + + ready = *((BOOL*) buffer); + + WTSFreeMemory(buffer); + + if (ready) + break; + } + + /* Create shared stream */ + s = Stream_New(NULL, 4096); + if (!s) + { + WLog_ERR(TAG, "Stream_New failed!"); + error = CHANNEL_RC_NO_MEMORY; + goto out; + } + + /* Main virtual channel loop. RDPGFX do not need version negotiation */ + while (ready) + { + status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE); + + if (status == WAIT_FAILED) + { + error = GetLastError(); + WLog_ERR(TAG, "WaitForMultipleObjects failed with error %lu", error); + goto out; + } + + /* Stop Event */ + if (status == WAIT_OBJECT_0) + break; + + /* Channel Event */ + Stream_SetPosition(s, 0); + + if (!WTSVirtualChannelRead(priv->rdpgfx_channel, 0, NULL, 0, &BytesReturned)) + { + WLog_ERR(TAG, "WTSVirtualChannelRead failed!"); + error = ERROR_INTERNAL_ERROR; + break; + } + + if (BytesReturned < 1) + continue; + + if (!Stream_EnsureRemainingCapacity(s, BytesReturned)) + { + WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!"); + error = CHANNEL_RC_NO_MEMORY; + break; + } + + if (WTSVirtualChannelRead(priv->rdpgfx_channel, 0, (PCHAR) Stream_Buffer(s), + Stream_Capacity(s), &BytesReturned) == FALSE) + { + WLog_ERR(TAG, "WTSVirtualChannelRead failed!"); + error = ERROR_INTERNAL_ERROR; + break; + } + + Stream_SetLength(s, BytesReturned); + Stream_SetPosition(s, 0); + if ((error = rdpgfx_server_receive_pdu(context, s))) + { + WLog_ERR(TAG, "rdpgfx_server_receive_pdu failed with error %lu!", error); + break; + } + Stream_SetPosition(s, 0); + } + + Stream_Free(s, TRUE); +out: + WTSVirtualChannelClose(priv->rdpgfx_channel); + priv->rdpgfx_channel = NULL; + if (error && context->rdpcontext) + setChannelError(context->rdpcontext, error, "rdpgfx_server_thread_func reported an error"); + + ExitThread((DWORD)error); + return NULL; +} + +static BOOL rdpgfx_server_open(RdpgfxServerContext* context) +{ + RdpgfxServerPrivate* priv = (RdpgfxServerPrivate *) context->priv; + + if (!priv->thread) + { + PULONG pSessionId = NULL; + DWORD BytesReturned = 0; + + priv->SessionId = WTS_CURRENT_SESSION; + + if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, + WTSSessionId, (LPSTR*) &pSessionId, &BytesReturned)) + { + priv->SessionId = (DWORD) *pSessionId; + WTSFreeMemory(pSessionId); + } + + priv->rdpgfx_channel = WTSVirtualChannelOpenEx(priv->SessionId, + RDPGFX_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC); + + if (!priv->rdpgfx_channel) + { + WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed!"); + return FALSE; + } + + if (!(priv->zgfx = zgfx_context_new(TRUE))) + { + WLog_ERR(TAG, "Create zgfx context failed!"); + return FALSE; + } + + if (!(priv->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) + { + WLog_ERR(TAG, "CreateEvent failed!"); + return FALSE; + } + + if (!(priv->thread = CreateThread(NULL, 0, + (LPTHREAD_START_ROUTINE) rdpgfx_server_thread_func, (void*) context, 0, NULL))) + { + WLog_ERR(TAG, "CreateThread failed!"); + CloseHandle(priv->stopEvent); + priv->stopEvent = NULL; + return FALSE; + } + + return TRUE; + } + WLog_ERR(TAG, "thread already running!"); + return FALSE; +} + +static BOOL rdpgfx_server_close(RdpgfxServerContext* context) +{ + RdpgfxServerPrivate* priv = (RdpgfxServerPrivate *) context->priv; + + if (priv->thread) + { + SetEvent(priv->stopEvent); + if (WaitForSingleObject(priv->thread, INFINITE) == WAIT_FAILED) + { + WLog_ERR(TAG, "WaitForSingleObject failed with error %lu", GetLastError()); + return FALSE; + } + + CloseHandle(priv->thread); + CloseHandle(priv->stopEvent); + priv->thread = NULL; + priv->stopEvent = NULL; + } + + zgfx_context_free(priv->zgfx); + priv->zgfx = NULL; + + if (priv->rdpgfx_channel) + { + WTSVirtualChannelClose(priv->rdpgfx_channel); + priv->rdpgfx_channel = NULL; + } + + return TRUE; +} + +RdpgfxServerContext* rdpgfx_server_context_new(HANDLE vcm) +{ + RdpgfxServerContext* context; + RdpgfxServerPrivate *priv; + + context = (RdpgfxServerContext *)calloc(1, sizeof(RdpgfxServerContext)); + if (!context) + { + WLog_ERR(TAG, "calloc failed!"); + return NULL; + } + + context->vcm = vcm; + context->Open = rdpgfx_server_open; + context->Close = rdpgfx_server_close; + + context->ResetGraphics = rdpgfx_send_reset_graphics_pdu; + context->StartFrame = rdpgfx_send_start_frame_pdu; + context->EndFrame = rdpgfx_send_end_frame_pdu; + context->SurfaceCommand = rdpgfx_send_surface_command; + context->DeleteEncodingContext = rdpgfx_send_delete_encoding_context_pdu; + context->CreateSurface = rdpgfx_send_create_surface_pdu; + context->DeleteSurface = rdpgfx_send_delete_surface_pdu; + context->SolidFill = rdpgfx_send_solid_fill_pdu; + context->SurfaceToSurface = rdpgfx_send_surface_to_surface_pdu; + context->SurfaceToCache = rdpgfx_send_surface_to_cache_pdu; + context->CacheToSurface = rdpgfx_send_cache_to_surface_pdu; + context->CacheImportOffer = NULL; + context->CacheImportReply = rdpgfx_send_cache_import_reply_pdu; + context->EvictCacheEntry = rdpgfx_send_evict_cache_entry_pdu; + context->MapSurfaceToOutput = rdpgfx_send_map_surface_to_output_pdu; + context->MapSurfaceToWindow = rdpgfx_send_map_surface_to_window_pdu; + context->CapsAdvertise = NULL; + context->CapsConfirm = rdpgfx_send_caps_confirm_pdu; + context->FrameAcknowledge = NULL; + context->QoeFrameAcknowledge = NULL; + + context->priv = priv = (RdpgfxServerPrivate *)calloc(1, sizeof(RdpgfxServerPrivate)); + if (!priv) + { + WLog_ERR(TAG, "calloc failed!"); + goto out_free; + } + + return (RdpgfxServerContext*) context; + +out_free: + free(context); + return NULL; +} + +void rdpgfx_server_context_free(RdpgfxServerContext* context) +{ + rdpgfx_server_close(context); + + free(context->priv); + + free(context); +} diff --git a/channels/rdpgfx/server/rdpgfx_main.h b/channels/rdpgfx/server/rdpgfx_main.h new file mode 100644 index 0000000..caad8cc --- /dev/null +++ b/channels/rdpgfx/server/rdpgfx_main.h @@ -0,0 +1,36 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Graphics Pipeline Extension + * + * Copyright 2016 Jiang Zihao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_CHANNEL_RDPGFX_SERVER_MAIN_H +#define FREERDP_CHANNEL_RDPGFX_SERVER_MAIN_H + +#include +#include + +struct _rdpgfx_server_private +{ + ZGFX_CONTEXT* zgfx; + HANDLE thread; + HANDLE stopEvent; + void* rdpgfx_channel; + DWORD SessionId; + BOOL opened; +}; + +#endif /* FREERDP_CHANNEL_RDPGFX_SERVER_MAIN_H */ diff --git a/include/freerdp/channels/rdpgfx.h b/include/freerdp/channels/rdpgfx.h index e197149..fdc643b 100644 --- a/include/freerdp/channels/rdpgfx.h +++ b/include/freerdp/channels/rdpgfx.h @@ -73,6 +73,7 @@ typedef BYTE RDPGFX_PIXELFORMAT; #define RDPGFX_CMDID_CAPSCONFIRM 0x0013 #define RDPGFX_CMDID_UNUSED_0014 0x0014 #define RDPGFX_CMDID_MAPSURFACETOWINDOW 0x0015 +#define RDPGFX_CMDID_QOEFRAMEACKNOWLEDGE 0x0016 #define RDPGFX_HEADER_SIZE 8 @@ -372,6 +373,14 @@ struct _RDPGFX_AVC444_BITMAP_STREAM }; typedef struct _RDPGFX_AVC444_BITMAP_STREAM RDPGFX_AVC444_BITMAP_STREAM; +struct _RDPGFX_QOE_FRAME_ACKNOWLEDGE_PDU +{ + UINT32 frameId; + UINT32 timestamp; + UINT16 timeDiffSE; + UINT16 timeDiffEDR; +}; +typedef struct _RDPGFX_QOE_FRAME_ACKNOWLEDGE_PDU RDPGFX_QOE_FRAME_ACKNOWLEDGE_PDU; #endif /* FREERDP_CHANNEL_RDPGFX_H */ diff --git a/include/freerdp/codec/zgfx.h b/include/freerdp/codec/zgfx.h index 6fd3582..53a41f8 100644 --- a/include/freerdp/codec/zgfx.h +++ b/include/freerdp/codec/zgfx.h @@ -28,6 +28,10 @@ #define ZGFX_SEGMENTED_SINGLE 0xE0 #define ZGFX_SEGMENTED_MULTIPART 0xE1 +#define ZGFX_PACKET_COMPR_TYPE_RDP8 0x04 + +#define ZGFX_SEGMENTED_MAXSIZE 65535 + struct _ZGFX_CONTEXT { BOOL Compressor; @@ -53,8 +57,9 @@ typedef struct _ZGFX_CONTEXT ZGFX_CONTEXT; extern "C" { #endif -FREERDP_API int zgfx_compress(ZGFX_CONTEXT* zgfx, BYTE* pSrcData, UINT32 SrcSize, BYTE** ppDstData, UINT32* pDstSize, UINT32* pFlags); FREERDP_API int zgfx_decompress(ZGFX_CONTEXT* zgfx, BYTE* pSrcData, UINT32 SrcSize, BYTE** ppDstData, UINT32* pDstSize, UINT32 flags); +FREERDP_API int zgfx_compress(ZGFX_CONTEXT* zgfx, BYTE* pSrcData, UINT32 SrcSize, BYTE** ppDstData, UINT32* pDstSize, UINT32* pFlags); +FREERDP_API int zgfx_compress_to_stream(ZGFX_CONTEXT* zgfx, wStream* sDst, BYTE* pUncompressed, UINT32 uncompressedSize, UINT32* pFlags); FREERDP_API void zgfx_context_reset(ZGFX_CONTEXT* zgfx, BOOL flush); diff --git a/include/freerdp/server/rdpgfx.h b/include/freerdp/server/rdpgfx.h new file mode 100644 index 0000000..7f4d033 --- /dev/null +++ b/include/freerdp/server/rdpgfx.h @@ -0,0 +1,96 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Graphics Pipeline Extension + * + * Copyright 2016 Jiang Zihao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_CHANNEL_SERVER_RDPGFX_H +#define FREERDP_CHANNEL_SERVER_RDPGFX_H + +#include + +typedef struct _rdpgfx_server_context RdpgfxServerContext; +typedef struct _rdpgfx_server_private RdpgfxServerPrivate; + +typedef BOOL (*psRdpgfxServerOpen)(RdpgfxServerContext* context); +typedef BOOL (*psRdpgfxServerClose)(RdpgfxServerContext* context); + +typedef UINT (*pcRdpgfxResetGraphics)(RdpgfxServerContext* context, RDPGFX_RESET_GRAPHICS_PDU* resetGraphics); +typedef UINT (*pcRdpgfxStartFrame)(RdpgfxServerContext* context, RDPGFX_START_FRAME_PDU* startFrame); +typedef UINT (*pcRdpgfxEndFrame)(RdpgfxServerContext* context, RDPGFX_END_FRAME_PDU* endFrame); +typedef UINT (*pcRdpgfxSurfaceCommand)(RdpgfxServerContext* context, RDPGFX_SURFACE_COMMAND* cmd); +typedef UINT (*pcRdpgfxDeleteEncodingContext)(RdpgfxServerContext* context, RDPGFX_DELETE_ENCODING_CONTEXT_PDU* deleteEncodingContext); +typedef UINT (*pcRdpgfxCreateSurface)(RdpgfxServerContext* context, RDPGFX_CREATE_SURFACE_PDU* createSurface); +typedef UINT (*pcRdpgfxDeleteSurface)(RdpgfxServerContext* context, RDPGFX_DELETE_SURFACE_PDU* deleteSurface); +typedef UINT (*pcRdpgfxSolidFill)(RdpgfxServerContext* context, RDPGFX_SOLID_FILL_PDU* solidFill); +typedef UINT (*pcRdpgfxSurfaceToSurface)(RdpgfxServerContext* context, RDPGFX_SURFACE_TO_SURFACE_PDU* surfaceToSurface); +typedef UINT (*pcRdpgfxSurfaceToCache)(RdpgfxServerContext* context, RDPGFX_SURFACE_TO_CACHE_PDU* surfaceToCache); +typedef UINT (*pcRdpgfxCacheToSurface)(RdpgfxServerContext* context, RDPGFX_CACHE_TO_SURFACE_PDU* cacheToSurface); +typedef UINT (*pcRdpgfxCacheImportOffer)(RdpgfxServerContext* context, RDPGFX_CACHE_IMPORT_OFFER_PDU* cacheImportOffer); +typedef UINT (*pcRdpgfxCacheImportReply)(RdpgfxServerContext* context, RDPGFX_CACHE_IMPORT_REPLY_PDU* cacheImportReply); +typedef UINT (*pcRdpgfxEvictCacheEntry)(RdpgfxServerContext* context, RDPGFX_EVICT_CACHE_ENTRY_PDU* evictCacheEntry); +typedef UINT (*pcRdpgfxMapSurfaceToOutput)(RdpgfxServerContext* context, RDPGFX_MAP_SURFACE_TO_OUTPUT_PDU* surfaceToOutput); +typedef UINT (*pcRdpgfxMapSurfaceToWindow)(RdpgfxServerContext* context, RDPGFX_MAP_SURFACE_TO_WINDOW_PDU* surfaceToWindow); +typedef UINT (*pcRdpgfxCapsAdvertise)(RdpgfxServerContext* context, RDPGFX_CAPS_ADVERTISE_PDU* capsAdvertise); +typedef UINT (*pcRdpgfxCapsConfirm)(RdpgfxServerContext* context, RDPGFX_CAPS_CONFIRM_PDU* capsConfirm); +typedef UINT (*pcRdpgfxFrameAcknowledge)(RdpgfxServerContext* context, RDPGFX_FRAME_ACKNOWLEDGE_PDU* frameAcknowledge); +typedef UINT (*pcRdpgfxQoeFrameAcknowledge)(RdpgfxServerContext* context, RDPGFX_QOE_FRAME_ACKNOWLEDGE_PDU* qoeFrameAcknowledge); + +struct _rdpgfx_server_context +{ + HANDLE vcm; + void* custom; + + psRdpgfxServerOpen Open; + psRdpgfxServerClose Close; + + pcRdpgfxResetGraphics ResetGraphics; + pcRdpgfxStartFrame StartFrame; + pcRdpgfxEndFrame EndFrame; + pcRdpgfxSurfaceCommand SurfaceCommand; + pcRdpgfxDeleteEncodingContext DeleteEncodingContext; + pcRdpgfxCreateSurface CreateSurface; + pcRdpgfxDeleteSurface DeleteSurface; + pcRdpgfxSolidFill SolidFill; + pcRdpgfxSurfaceToSurface SurfaceToSurface; + pcRdpgfxSurfaceToCache SurfaceToCache; + pcRdpgfxCacheToSurface CacheToSurface; + pcRdpgfxCacheImportOffer CacheImportOffer; + pcRdpgfxCacheImportReply CacheImportReply; + pcRdpgfxEvictCacheEntry EvictCacheEntry; + pcRdpgfxMapSurfaceToOutput MapSurfaceToOutput; + pcRdpgfxMapSurfaceToWindow MapSurfaceToWindow; + pcRdpgfxCapsAdvertise CapsAdvertise; + pcRdpgfxCapsConfirm CapsConfirm; + pcRdpgfxFrameAcknowledge FrameAcknowledge; + pcRdpgfxQoeFrameAcknowledge QoeFrameAcknowledge; + + RdpgfxServerPrivate* priv; + rdpContext* rdpcontext; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +FREERDP_API RdpgfxServerContext* rdpgfx_server_context_new(HANDLE vcm); +FREERDP_API void rdpgfx_server_context_free(RdpgfxServerContext* context); + +#ifdef __cplusplus +} +#endif + +#endif /* FREERDP_CHANNEL_SERVER_RDPGFX_H */ diff --git a/include/freerdp/server/shadow.h b/include/freerdp/server/shadow.h index 60bb87d..e718ea0 100644 --- a/include/freerdp/server/shadow.h +++ b/include/freerdp/server/shadow.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -87,6 +88,7 @@ struct rdp_shadow_client BOOL inLobby; BOOL mayView; BOOL mayInteract; + BOOL suppressOutput; wMessageQueue* MsgQueue; CRITICAL_SECTION lock; REGION16 invalidRegion; @@ -102,6 +104,7 @@ struct rdp_shadow_client RemdeskServerContext* remdesk; RdpsndServerContext* rdpsnd; audin_server_context* audin; + RdpgfxServerContext* rdpgfx; }; struct rdp_shadow_server @@ -169,9 +172,12 @@ struct _RDP_SHADOW_ENTRY_POINTS int selectedMonitor; \ MONITOR_DEF monitors[16]; \ MONITOR_DEF virtualScreen; \ + \ + /* This event indicates that we have graphic change */ \ + /* such as screen update and resize. It should not be */ \ + /* used by subsystem implementation directly */ \ rdpShadowMultiClientEvent* updateEvent; \ - BOOL suppressOutput; \ - REGION16 invalidRegion; \ + \ wMessagePipe* MsgPipe; \ UINT32 pointerX; \ UINT32 pointerY; \ @@ -201,22 +207,7 @@ struct rdp_shadow_subsystem }; /* Definition of message between subsystem and clients */ -#define SHADOW_MSG_IN_REFRESH_OUTPUT_ID 1001 -#define SHADOW_MSG_IN_SUPPRESS_OUTPUT_ID 1002 - -struct _SHADOW_MSG_IN_REFRESH_OUTPUT -{ - UINT32 numRects; - RECTANGLE_16* rects; -}; -typedef struct _SHADOW_MSG_IN_REFRESH_OUTPUT SHADOW_MSG_IN_REFRESH_OUTPUT; - -struct _SHADOW_MSG_IN_SUPPRESS_OUTPUT -{ - BOOL allow; - RECTANGLE_16 rect; -}; -typedef struct _SHADOW_MSG_IN_SUPPRESS_OUTPUT SHADOW_MSG_IN_SUPPRESS_OUTPUT; +#define SHADOW_MSG_IN_REFRESH_REQUEST_ID 1001 typedef struct _SHADOW_MSG_OUT SHADOW_MSG_OUT; typedef void (*MSG_OUT_FREE_FN)(UINT32 id, SHADOW_MSG_OUT* msg); /* function to free SHADOW_MSG_OUT */ diff --git a/libfreerdp/codec/test/TestFreeRDPCodecZGfx.c b/libfreerdp/codec/test/TestFreeRDPCodecZGfx.c index 7284424..ec71287 100644 --- a/libfreerdp/codec/test/TestFreeRDPCodecZGfx.c +++ b/libfreerdp/codec/test/TestFreeRDPCodecZGfx.c @@ -1,10 +1,217 @@ #include #include +#include +#include #include +#include + +/* Sample from [MS-RDPEGFX] */ +static const BYTE TEST_FOX_DATA[] = + "The quick brown " + "fox jumps over t" + "he lazy dog"; + +static const BYTE TEST_FOX_DATA_SINGLE[] = + "\xE0\x04\x54\x68\x65\x20\x71\x75\x69\x63\x6B\x20\x62\x72\x6F\x77" + "\x6E\x20\x66\x6F\x78\x20\x6A\x75\x6D\x70\x73\x20\x6F\x76\x65\x72" + "\x20\x74\x68\x65\x20\x6C\x61\x7A\x79\x20\x64\x6F\x67"; + +static const BYTE TEST_FOX_DATA_MULTIPART[] = + "\xE1\x03\x00\x2B\x00\x00\x00\x11\x00\x00\x00\x04\x54\x68\x65\x20" + "\x71\x75\x69\x63\x6B\x20\x62\x72\x6F\x77\x6E\x20\x0E\x00\x00\x00" + "\x04\x66\x6F\x78\x20\x6A\x75\x6D\x70\x73\x20\x6F\x76\x65\x10\x00" + "\x00\x00\x24\x39\x08\x0E\x91\xF8\xD8\x61\x3D\x1E\x44\x06\x43\x79" + "\x9C\x02"; + +int test_ZGfxCompressFox() +{ + int status; + UINT32 Flags; + BYTE* pSrcData; + UINT32 SrcSize; + UINT32 DstSize; + BYTE* pDstData; + ZGFX_CONTEXT* zgfx; + UINT32 expectedSize; + + zgfx = zgfx_context_new(TRUE); + SrcSize = sizeof(TEST_FOX_DATA) - 1; + pSrcData = (BYTE*) TEST_FOX_DATA; + Flags = 0; + expectedSize = sizeof(TEST_FOX_DATA_SINGLE) - 1; + status = zgfx_compress(zgfx, pSrcData, SrcSize, &pDstData, &DstSize, &Flags); + printf("flags: 0x%04X size: %d\n", Flags, DstSize); + + if (DstSize != expectedSize) + { + printf("test_ZGfxCompressFox: output size mismatch: Actual: %d, Expected: %d\n", DstSize, expectedSize); + return -1; + } + + if (memcmp(pDstData, TEST_FOX_DATA_SINGLE, DstSize) != 0) + { + printf("test_ZGfxCompressFox: output mismatch\n"); + printf("Actual\n"); + BitDump(__FUNCTION__, WLOG_INFO, pDstData, DstSize * 8, 0); + printf("Expected\n"); + BitDump(__FUNCTION__, WLOG_INFO, TEST_FOX_DATA_SINGLE, DstSize * 8, 0); + return -1; + } + + zgfx_context_free(zgfx); + return 0; +} + +int test_ZGfxDecompressFoxSingle() +{ + int status; + UINT32 Flags; + BYTE* pSrcData; + UINT32 SrcSize; + UINT32 DstSize; + BYTE* pDstData; + ZGFX_CONTEXT* zgfx; + UINT32 expectedSize; + + zgfx = zgfx_context_new(TRUE); + SrcSize = sizeof(TEST_FOX_DATA_SINGLE) - 1; + pSrcData = (BYTE*) TEST_FOX_DATA_SINGLE; + Flags = 0; + expectedSize = sizeof(TEST_FOX_DATA) - 1; + status = zgfx_decompress(zgfx, pSrcData, SrcSize, &pDstData, &DstSize, Flags); + printf("flags: 0x%04X size: %d\n", Flags, DstSize); + + if (DstSize != expectedSize) + { + printf("test_ZGfxDecompressFoxSingle: output size mismatch: Actual: %d, Expected: %d\n", DstSize, expectedSize); + return -1; + } + + if (memcmp(pDstData, TEST_FOX_DATA, DstSize) != 0) + { + printf("test_ZGfxDecompressFoxSingle: output mismatch\n"); + printf("Actual\n"); + BitDump(__FUNCTION__, WLOG_INFO, pDstData, DstSize * 8, 0); + printf("Expected\n"); + BitDump(__FUNCTION__, WLOG_INFO, TEST_FOX_DATA, DstSize * 8, 0); + return -1; + } + + zgfx_context_free(zgfx); + return 0; +} + +int test_ZGfxDecompressFoxMultipart() +{ + int status; + UINT32 Flags; + BYTE* pSrcData; + UINT32 SrcSize; + UINT32 DstSize; + BYTE* pDstData; + ZGFX_CONTEXT* zgfx; + UINT32 expectedSize; + + zgfx = zgfx_context_new(TRUE); + SrcSize = sizeof(TEST_FOX_DATA_MULTIPART) - 1; + pSrcData = (BYTE*) TEST_FOX_DATA_MULTIPART; + Flags = 0; + expectedSize = sizeof(TEST_FOX_DATA) - 1; + status = zgfx_decompress(zgfx, pSrcData, SrcSize, &pDstData, &DstSize, Flags); + printf("flags: 0x%04X size: %d\n", Flags, DstSize); + + if (DstSize != expectedSize) + { + printf("test_ZGfxDecompressFoxSingle: output size mismatch: Actual: %d, Expected: %d\n", DstSize, expectedSize); + return -1; + } + + if (memcmp(pDstData, TEST_FOX_DATA, DstSize) != 0) + { + printf("test_ZGfxDecompressFoxSingle: output mismatch\n"); + printf("Actual\n"); + BitDump(__FUNCTION__, WLOG_INFO, pDstData, DstSize * 8, 0); + printf("Expected\n"); + BitDump(__FUNCTION__, WLOG_INFO, TEST_FOX_DATA, DstSize * 8, 0); + return -1; + } + + zgfx_context_free(zgfx); + return 0; +} + +int test_ZGfxCompressConsistent() +{ + int status; + UINT32 Flags; + BYTE* pSrcData; + UINT32 SrcSize; + UINT32 DstSize; + BYTE* pDstData; + UINT32 DstSize2; + BYTE* pDstData2; + ZGFX_CONTEXT* zgfx; + UINT32 expectedSize; + BYTE BigBuffer[65536]; + + memset(BigBuffer, 0xaa, sizeof(BigBuffer)); + memcpy(BigBuffer, TEST_FOX_DATA, sizeof(TEST_FOX_DATA) - 1); + zgfx = zgfx_context_new(TRUE); + + /* Compress */ + expectedSize = SrcSize = sizeof(BigBuffer); + pSrcData = (BYTE*) BigBuffer; + Flags = 0; + status = zgfx_compress(zgfx, pSrcData, SrcSize, &pDstData2, &DstSize2, &Flags); + printf("Compress: flags: 0x%04X size: %d\n", Flags, DstSize2); + + /* Decompress */ + status = zgfx_decompress(zgfx, pDstData2, DstSize2, &pDstData, &DstSize, Flags); + printf("Decompress: flags: 0x%04X size: %d\n", Flags, DstSize); + + if (DstSize != expectedSize) + { + printf("test_ZGfxDecompressFoxSingle: output size mismatch: Actual: %d, Expected: %d\n", DstSize, expectedSize); + return -1; + } + + if (memcmp(pDstData, BigBuffer, DstSize) != 0) + { + printf("test_ZGfxDecompressFoxSingle: output mismatch\n"); + printf("Actual\n"); + BitDump(__FUNCTION__, WLOG_INFO, pDstData, 64 * 8, 0); + printf("...\n"); + BitDump(__FUNCTION__, WLOG_INFO, pDstData + DstSize - 64, 64 * 8, 0); + printf("Expected\n"); + BitDump(__FUNCTION__, WLOG_INFO, BigBuffer, 64 * 8, 0); + printf("...\n"); + BitDump(__FUNCTION__, WLOG_INFO, BigBuffer + DstSize - 64, 64 * 8, 0); + printf("Middle Result\n"); + BitDump(__FUNCTION__, WLOG_INFO, pDstData2, 64 * 8, 0); + printf("...\n"); + BitDump(__FUNCTION__, WLOG_INFO, pDstData2 + DstSize2 - 64, 64 * 8, 0); + return -1; + } + + zgfx_context_free(zgfx); + return 0; +} int TestFreeRDPCodecZGfx(int argc, char* argv[]) { + if (test_ZGfxCompressFox() < 0) + return -1; + + if (test_ZGfxDecompressFoxSingle() < 0) + return -1; + + if (test_ZGfxDecompressFoxMultipart() < 0) + return -1; + + if (test_ZGfxCompressConsistent() < 0) + return -1; + return 0; } diff --git a/libfreerdp/codec/zgfx.c b/libfreerdp/codec/zgfx.c index 6049479..4d53734 100644 --- a/libfreerdp/codec/zgfx.c +++ b/libfreerdp/codec/zgfx.c @@ -25,8 +25,11 @@ #include #include +#include #include +#define TAG FREERDP_TAG("codec") + /** * RDP8 Compressor Limits: * @@ -188,7 +191,7 @@ void zgfx_history_buffer_ring_read(ZGFX_CONTEXT* zgfx, int offset, BYTE* dst, UI while ((bytesLeft -= bytes) > 0); } -int zgfx_decompress_segment(ZGFX_CONTEXT* zgfx, BYTE* pbSegment, UINT32 cbSegment) +static int zgfx_decompress_segment(ZGFX_CONTEXT* zgfx, BYTE* pbSegment, UINT32 cbSegment) { BYTE c; BYTE flags; @@ -383,11 +386,106 @@ int zgfx_decompress(ZGFX_CONTEXT* zgfx, BYTE* pSrcData, UINT32 SrcSize, BYTE** p return 1; } -int zgfx_compress(ZGFX_CONTEXT* zgfx, BYTE* pSrcData, UINT32 SrcSize, BYTE** ppDstData, UINT32* pDstSize, UINT32* pFlags) +static int zgfx_compress_segment(ZGFX_CONTEXT* zgfx, wStream* s, BYTE* pSrcData, UINT32 SrcSize, UINT32* pFlags) { + /* FIXME: Currently compression not implemented. Just copy the raw source */ + + if (!Stream_EnsureRemainingCapacity(s, SrcSize + 1)) + { + WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!"); + return -1; + } + (*pFlags) |= ZGFX_PACKET_COMPR_TYPE_RDP8; /* RDP 8.0 compression format */ + Stream_Write_UINT8(s, (*pFlags)); /* header (1 byte) */ + Stream_Write(s, pSrcData, SrcSize); + return 1; } +int zgfx_compress_to_stream(ZGFX_CONTEXT* zgfx, wStream* sDst, BYTE* pUncompressed, UINT32 uncompressedSize, UINT32* pFlags) +{ + int fragment; + UINT16 maxLength; + UINT32 totalLength; + UINT16* pSegmentCount = NULL; + BYTE* pSrcData; + int status = 0; + + maxLength = ZGFX_SEGMENTED_MAXSIZE; + + totalLength = uncompressedSize; + pSrcData = pUncompressed; + for (fragment = 0; (totalLength > 0) || (fragment == 0); fragment++) + { + UINT32 SrcSize; + UINT32* pDstSize; + UINT32 position; + + SrcSize = (totalLength > maxLength) ? maxLength : totalLength; + pDstSize = NULL; + totalLength -= SrcSize; + + if (fragment == 0) + { + /* First fragment */ + + /* descriptor (1 byte) */ + Stream_Write_UINT8(sDst, (totalLength == 0) ? + ZGFX_SEGMENTED_SINGLE : ZGFX_SEGMENTED_MULTIPART); + if (totalLength > 0) + { + pSegmentCount = (UINT16*)Stream_Pointer(sDst); /* segmentCount (2 bytes) */ + Stream_Seek(sDst, 2); + Stream_Write_UINT32(sDst, uncompressedSize); /* uncompressedSize (4 bytes) */ + } + } + else if (totalLength == 0) + { + /* Last fragment */ + if (pSegmentCount) + { + (*pSegmentCount) = fragment + 1; + } + } + + if (fragment > 0 || totalLength > 0) + { + /* Multipart */ + pDstSize = (UINT32*)Stream_Pointer(sDst); /* size (4 bytes) */ + Stream_Seek(sDst, 4); + } + + position = Stream_GetPosition(sDst); + if ((status = zgfx_compress_segment(zgfx, sDst, pSrcData, SrcSize, pFlags)) < 0) + { + return status; + } + + if (pDstSize) + { + (*pDstSize) = Stream_GetPosition(sDst) - position; + } + + pSrcData += SrcSize; + } + + return status; +} + +int zgfx_compress(ZGFX_CONTEXT* zgfx, BYTE* pSrcData, UINT32 SrcSize, BYTE** ppDstData, UINT32* pDstSize, UINT32* pFlags) +{ + int status; + wStream* s = Stream_New(NULL, SrcSize); + + status = zgfx_compress_to_stream(zgfx, s, pSrcData, SrcSize, pFlags); + (*ppDstData) = Stream_Buffer(s); + (*pDstSize) = Stream_GetPosition(s); + + Stream_Free(s, FALSE); + return status; +} + + void zgfx_context_reset(ZGFX_CONTEXT* zgfx, BOOL flush) { zgfx->HistoryIndex = 0; diff --git a/server/shadow/CMakeLists.txt b/server/shadow/CMakeLists.txt index 55597db..8e4e8e1 100644 --- a/server/shadow/CMakeLists.txt +++ b/server/shadow/CMakeLists.txt @@ -45,6 +45,8 @@ set(${MODULE_PREFIX}_SRCS shadow_rdpsnd.h shadow_audin.c shadow_audin.h + shadow_rdpgfx.c + shadow_rdpgfx.h shadow_subsystem.c shadow_subsystem.h shadow_mcevent.c diff --git a/server/shadow/Mac/mac_shadow.c b/server/shadow/Mac/mac_shadow.c index 10508c9..e281ffd 100644 --- a/server/shadow/Mac/mac_shadow.c +++ b/server/shadow/Mac/mac_shadow.c @@ -268,6 +268,7 @@ int mac_shadow_capture_get_dirty_region(macShadowSubsystem* subsystem) size_t numRects; const CGRect* rects; RECTANGLE_16 invalidRect; + rdpShadowSurface* surface = subsystem->server->surface; rects = CGDisplayStreamUpdateGetRects(subsystem->lastUpdate, kCGDisplayStreamUpdateDirtyRects, &numRects); @@ -290,7 +291,7 @@ int mac_shadow_capture_get_dirty_region(macShadowSubsystem* subsystem) invalidRect.bottom /= 2; } - region16_union_rect(&(subsystem->invalidRegion), &(subsystem->invalidRegion), &invalidRect); + region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect); } return 0; @@ -326,11 +327,11 @@ void (^mac_capture_stream_handler)(CGDisplayStreamFrameStatus, uint64_t, IOSurfa surfaceRect.right = surface->width; surfaceRect.bottom = surface->height; - region16_intersect_rect(&(subsystem->invalidRegion), &(subsystem->invalidRegion), &surfaceRect); + region16_intersect_rect(&(surface->invalidRegion), &(surface->invalidRegion), &surfaceRect); - if (!region16_is_empty(&(subsystem->invalidRegion))) + if (!region16_is_empty(&(surface->invalidRegion))) { - extents = region16_extents(&(subsystem->invalidRegion)); + extents = region16_extents(&(surface->invalidRegion)); x = extents->left; y = extents->top; @@ -375,7 +376,7 @@ void (^mac_capture_stream_handler)(CGDisplayStreamFrameStatus, uint64_t, IOSurfa ArrayList_Unlock(server->clients); - region16_clear(&(subsystem->invalidRegion)); + region16_clear(&(surface->invalidRegion)); } if (status != kCGDisplayStreamFrameStatusFrameComplete) @@ -446,46 +447,9 @@ int mac_shadow_subsystem_process_message(macShadowSubsystem* subsystem, wMessage { switch(message->id) { - case SHADOW_MSG_IN_REFRESH_OUTPUT_ID: - { - UINT32 index; - SHADOW_MSG_IN_REFRESH_OUTPUT* msg = (SHADOW_MSG_IN_REFRESH_OUTPUT*) message->wParam; - - if (msg->numRects) - { - for (index = 0; index < msg->numRects; index++) - { - region16_union_rect(&(subsystem->invalidRegion), - &(subsystem->invalidRegion), &msg->rects[index]); - } - } - else - { - RECTANGLE_16 refreshRect; - - refreshRect.left = 0; - refreshRect.top = 0; - refreshRect.right = subsystem->width; - refreshRect.bottom = subsystem->height; - - region16_union_rect(&(subsystem->invalidRegion), - &(subsystem->invalidRegion), &refreshRect); - } + case SHADOW_MSG_IN_REFRESH_REQUEST_ID: + shadow_subsystem_frame_update((rdpShadowSubsystem *)subsystem); break; - } - case SHADOW_MSG_IN_SUPPRESS_OUTPUT_ID: - { - SHADOW_MSG_IN_SUPPRESS_OUTPUT* msg = (SHADOW_MSG_IN_SUPPRESS_OUTPUT*) message->wParam; - - subsystem->suppressOutput = (msg->allow) ? FALSE : TRUE; - - if (msg->allow) - { - region16_union_rect(&(subsystem->invalidRegion), - &(subsystem->invalidRegion), &(msg->rect)); - } - break; - } default: WLog_ERR(TAG, "Unknown message id: %u", message->id); break; diff --git a/server/shadow/Win/win_dxgi.c b/server/shadow/Win/win_dxgi.c index bd2f5d6..50a1881 100644 --- a/server/shadow/Win/win_dxgi.c +++ b/server/shadow/Win/win_dxgi.c @@ -638,6 +638,7 @@ int win_shadow_dxgi_get_invalid_region(winShadowSubsystem* subsystem) RECT* pDirtyRectsBuffer; DXGI_OUTDUPL_MOVE_RECT* pMoveRect; DXGI_OUTDUPL_MOVE_RECT* pMoveRectBuffer; + rdpShadowSurface* surface = subsystem->server->surface; if (subsystem->dxgiFrameInfo.AccumulatedFrames == 0) return 0; @@ -704,7 +705,7 @@ int win_shadow_dxgi_get_invalid_region(winShadowSubsystem* subsystem) invalidRect.right = (UINT16) pDstRect->right; invalidRect.bottom = (UINT16) pDstRect->bottom; - region16_union_rect(&(subsystem->invalidRegion), &(subsystem->invalidRegion), &invalidRect); + region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect); } numDirtyRects = DirtyRectsBufferSize / sizeof(RECT); @@ -718,7 +719,7 @@ int win_shadow_dxgi_get_invalid_region(winShadowSubsystem* subsystem) invalidRect.right = (UINT16) pDirtyRect->right; invalidRect.bottom = (UINT16) pDirtyRect->bottom; - region16_union_rect(&(subsystem->invalidRegion), &(subsystem->invalidRegion), &invalidRect); + region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect); } return 1; diff --git a/server/shadow/Win/win_rdp.c b/server/shadow/Win/win_rdp.c index 231996e..0bfc9e6 100644 --- a/server/shadow/Win/win_rdp.c +++ b/server/shadow/Win/win_rdp.c @@ -63,6 +63,7 @@ BOOL shw_end_paint(rdpContext* context) rdpGdi* gdi = context->gdi; shwContext* shw = (shwContext*) context; winShadowSubsystem* subsystem = shw->subsystem; + rdpShadowSurface* surface = subsystem->server->surface; ninvalid = gdi->primary->hdc->hwnd->ninvalid; cinvalid = gdi->primary->hdc->hwnd->cinvalid; @@ -74,7 +75,7 @@ BOOL shw_end_paint(rdpContext* context) invalidRect.right = cinvalid[index].x + cinvalid[index].w; invalidRect.bottom = cinvalid[index].y + cinvalid[index].h; - region16_union_rect(&(subsystem->invalidRegion), &(subsystem->invalidRegion), &invalidRect); + region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect); } SetEvent(subsystem->RdpUpdateEnterEvent); diff --git a/server/shadow/Win/win_shadow.c b/server/shadow/Win/win_shadow.c index 0f23b9c..4cc86cc 100644 --- a/server/shadow/Win/win_shadow.c +++ b/server/shadow/Win/win_shadow.c @@ -219,7 +219,7 @@ int win_shadow_surface_copy(winShadowSubsystem* subsystem) if (ArrayList_Count(server->clients) < 1) { - region16_clear(&(subsystem->invalidRegion)); + region16_clear(&(surface->invalidRegion)); return 1; } @@ -228,12 +228,12 @@ int win_shadow_surface_copy(winShadowSubsystem* subsystem) surfaceRect.right = surface->x + surface->width; surfaceRect.bottom = surface->y + surface->height; - region16_intersect_rect(&(subsystem->invalidRegion), &(subsystem->invalidRegion), &surfaceRect); + region16_intersect_rect(&(surface->invalidRegion), &(surface->invalidRegion), &surfaceRect); - if (region16_is_empty(&(subsystem->invalidRegion))) + if (region16_is_empty(&(surface->invalidRegion))) return 1; - extents = region16_extents(&(subsystem->invalidRegion)); + extents = region16_extents(&(surface->invalidRegion)); CopyMemory(&invalidRect, extents, sizeof(RECTANGLE_16)); shadow_capture_align_clip_rect(&invalidRect, &surfaceRect); @@ -286,7 +286,7 @@ int win_shadow_surface_copy(winShadowSubsystem* subsystem) ArrayList_Unlock(server->clients); - region16_clear(&(subsystem->invalidRegion)); + region16_clear(&(surface->invalidRegion)); return 1; } diff --git a/server/shadow/X11/x11_shadow.c b/server/shadow/X11/x11_shadow.c index 8c5e159..0d0f624 100644 --- a/server/shadow/X11/x11_shadow.c +++ b/server/shadow/X11/x11_shadow.c @@ -763,9 +763,6 @@ int x11_shadow_screen_grab(x11ShadowSubsystem* subsystem) if (count < 1) return 1; - if ((count == 1) && subsystem->suppressOutput) - return 1; - surfaceRect.left = 0; surfaceRect.top = 0; surfaceRect.right = surface->width; @@ -814,12 +811,12 @@ int x11_shadow_screen_grab(x11ShadowSubsystem* subsystem) XUnlockDisplay(subsystem->display); - region16_union_rect(&(subsystem->invalidRegion), &(subsystem->invalidRegion), &invalidRect); - region16_intersect_rect(&(subsystem->invalidRegion), &(subsystem->invalidRegion), &surfaceRect); + region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect); + region16_intersect_rect(&(surface->invalidRegion), &(surface->invalidRegion), &surfaceRect); - if (!region16_is_empty(&(subsystem->invalidRegion))) + if (!region16_is_empty(&(surface->invalidRegion))) { - extents = region16_extents(&(subsystem->invalidRegion)); + extents = region16_extents(&(surface->invalidRegion)); x = extents->left; y = extents->top; @@ -849,7 +846,7 @@ int x11_shadow_screen_grab(x11ShadowSubsystem* subsystem) } } - region16_clear(&(subsystem->invalidRegion)); + region16_clear(&(surface->invalidRegion)); } if (!subsystem->use_xshm) @@ -868,46 +865,9 @@ int x11_shadow_subsystem_process_message(x11ShadowSubsystem* subsystem, wMessage { switch(message->id) { - case SHADOW_MSG_IN_REFRESH_OUTPUT_ID: - { - UINT32 index; - SHADOW_MSG_IN_REFRESH_OUTPUT* msg = (SHADOW_MSG_IN_REFRESH_OUTPUT*) message->wParam; - - if (msg->numRects) - { - for (index = 0; index < msg->numRects; index++) - { - region16_union_rect(&(subsystem->invalidRegion), - &(subsystem->invalidRegion), &msg->rects[index]); - } - } - else - { - RECTANGLE_16 refreshRect; - - refreshRect.left = 0; - refreshRect.top = 0; - refreshRect.right = subsystem->width; - refreshRect.bottom = subsystem->height; - - region16_union_rect(&(subsystem->invalidRegion), - &(subsystem->invalidRegion), &refreshRect); - } - break; - } - case SHADOW_MSG_IN_SUPPRESS_OUTPUT_ID: - { - SHADOW_MSG_IN_SUPPRESS_OUTPUT* msg = (SHADOW_MSG_IN_SUPPRESS_OUTPUT*) message->wParam; - - subsystem->suppressOutput = (msg->allow) ? FALSE : TRUE; - - if (msg->allow) - { - region16_union_rect(&(subsystem->invalidRegion), - &(subsystem->invalidRegion), &(msg->rect)); - } + case SHADOW_MSG_IN_REFRESH_REQUEST_ID: + shadow_subsystem_frame_update((rdpShadowSubsystem *)subsystem); break; - } default: WLog_ERR(TAG, "Unknown message id: %u", message->id); break; diff --git a/server/shadow/shadow_channels.c b/server/shadow/shadow_channels.c index bc90ab4..983b16f 100644 --- a/server/shadow/shadow_channels.c +++ b/server/shadow/shadow_channels.c @@ -43,11 +43,21 @@ UINT shadow_client_channels_post_connect(rdpShadowClient* client) shadow_client_audin_init(client); + if (client->context.settings->SupportGraphicsPipeline) + { + shadow_client_rdpgfx_init(client); + } + return CHANNEL_RC_OK; } void shadow_client_channels_free(rdpShadowClient* client) { + if (client->context.settings->SupportGraphicsPipeline) + { + shadow_client_rdpgfx_uninit(client); + } + shadow_client_audin_uninit(client); shadow_client_rdpsnd_uninit(client); diff --git a/server/shadow/shadow_channels.h b/server/shadow/shadow_channels.h index edba39c..e35b6f6 100644 --- a/server/shadow/shadow_channels.h +++ b/server/shadow/shadow_channels.h @@ -28,6 +28,7 @@ #include "shadow_remdesk.h" #include "shadow_rdpsnd.h" #include "shadow_audin.h" +#include "shadow_rdpgfx.h" #ifdef __cplusplus extern "C" { diff --git a/server/shadow/shadow_client.c b/server/shadow/shadow_client.c index 8d433e2..010289c 100644 --- a/server/shadow/shadow_client.c +++ b/server/shadow/shadow_client.c @@ -30,10 +30,62 @@ #include +#include "libfreerdp/core/server.h" #include "shadow.h" #define TAG CLIENT_TAG("shadow") +struct _SHADOW_GFX_STATUS +{ + BOOL gfxOpened; + BOOL gfxSurfaceCreated; +}; +typedef struct _SHADOW_GFX_STATUS SHADOW_GFX_STATUS; + +static void shadow_client_rdpgfx_new_surface(rdpShadowClient *client) +{ + RDPGFX_CREATE_SURFACE_PDU createSurface; + RDPGFX_MAP_SURFACE_TO_OUTPUT_PDU surfaceToOutput; + RdpgfxServerContext* context = client->rdpgfx; + rdpSettings* settings = ((rdpContext*) client)->settings; + + createSurface.width = settings->DesktopWidth; + createSurface.height = settings->DesktopHeight; + createSurface.pixelFormat = PIXEL_FORMAT_XRGB_8888; + createSurface.surfaceId = 0; + + surfaceToOutput.outputOriginX = 0; + surfaceToOutput.outputOriginY = 0; + surfaceToOutput.surfaceId = 0; + surfaceToOutput.reserved = 0; + + IFCALL(context->CreateSurface, context, &createSurface); + IFCALL(context->MapSurfaceToOutput, context, &surfaceToOutput); +} + +static void shadow_client_rdpgfx_release_surface(rdpShadowClient *client) +{ + RDPGFX_DELETE_SURFACE_PDU pdu; + RdpgfxServerContext* context = client->rdpgfx; + + pdu.surfaceId = 0; + IFCALL(context->DeleteSurface, context, &pdu); +} + +static void shadow_client_rdpgfx_reset_graphic(rdpShadowClient *client) +{ + RDPGFX_RESET_GRAPHICS_PDU pdu; + RdpgfxServerContext* context = client->rdpgfx; + rdpSettings* settings = ((rdpContext*) client)->settings; + + pdu.width = settings->DesktopWidth; + pdu.height = settings->DesktopHeight; + pdu.monitorCount = client->subsystem->numMonitors; + pdu.monitorDefArray = client->subsystem->monitors; + + IFCALL(context->ResetGraphics, context, &pdu); +} + static void shadow_client_free_queued_message(void *obj) { wMessage *message = (wMessage*)obj; @@ -44,7 +96,7 @@ static void shadow_client_free_queued_message(void *obj) } } -BOOL shadow_client_context_new(freerdp_peer* peer, rdpShadowClient* client) +static BOOL shadow_client_context_new(freerdp_peer* peer, rdpShadowClient* client) { rdpSettings* settings; rdpShadowServer* server; @@ -62,7 +114,8 @@ BOOL shadow_client_context_new(freerdp_peer* peer, rdpShadowClient* client) settings->BitmapCacheV3Enabled = TRUE; settings->FrameMarkerCommandEnabled = TRUE; settings->SurfaceFrameMarkerEnabled = TRUE; - settings->SupportGraphicsPipeline = FALSE; + settings->SupportGraphicsPipeline = TRUE; + settings->GfxH264 = FALSE; settings->DrawAllowSkipAlpha = TRUE; settings->DrawAllowColorSubsampling = TRUE; @@ -130,40 +183,36 @@ fail_cert_file: return FALSE; } -void shadow_client_context_free(freerdp_peer* peer, rdpShadowClient* client) +static void shadow_client_context_free(freerdp_peer* peer, rdpShadowClient* client) { rdpShadowServer* server = client->server; ArrayList_Remove(server->clients, (void*) client); - DeleteCriticalSection(&(client->lock)); - - region16_uninit(&(client->invalidRegion)); - - WTSCloseServer((HANDLE) client->vcm); - - /* Clear queued messages and free resource */ - MessageQueue_Clear(client->MsgQueue); - MessageQueue_Free(client->MsgQueue); - if (client->encoder) { shadow_encoder_free(client->encoder); client->encoder = NULL; } + + /* Clear queued messages and free resource */ + MessageQueue_Clear(client->MsgQueue); + MessageQueue_Free(client->MsgQueue); + + WTSCloseServer((HANDLE) client->vcm); + client->vcm = NULL; + + region16_uninit(&(client->invalidRegion)); + + DeleteCriticalSection(&(client->lock)); } void shadow_client_message_free(wMessage* message) { switch(message->id) { - case SHADOW_MSG_IN_REFRESH_OUTPUT_ID: - free(((SHADOW_MSG_IN_REFRESH_OUTPUT*)message->wParam)->rects); - free(message->wParam); - break; - - case SHADOW_MSG_IN_SUPPRESS_OUTPUT_ID: - free(message->wParam); + case SHADOW_MSG_IN_REFRESH_REQUEST_ID: + /* Refresh request do not have message to free */ break; default: @@ -173,42 +222,90 @@ void shadow_client_message_free(wMessage* message) } } -BOOL shadow_client_capabilities(freerdp_peer* peer) +static void shadow_client_mark_invalid(rdpShadowClient* client, int numRects, const RECTANGLE_16* rects) { - rdpShadowSubsystem* subsystem; - rdpShadowClient* client; + int index; + RECTANGLE_16 screenRegion; + rdpSettings* settings = ((rdpContext*) client)->settings; - client = (rdpShadowClient*) peer->context; - subsystem = client->server->subsystem; + EnterCriticalSection(&(client->lock)); - if (subsystem->ClientCapabilities) + /* Mark client invalid region. No rectangle means full screen */ + if (numRects > 0) { - return subsystem->ClientCapabilities(subsystem, client); + for (index = 0; index < numRects; index++) + { + region16_union_rect(&(client->invalidRegion), &(client->invalidRegion), &rects[index]); + } } - return TRUE; + else + { + screenRegion.left = 0; + screenRegion.top = 0; + screenRegion.right = settings->DesktopWidth; + screenRegion.bottom = settings->DesktopHeight; + + region16_union_rect(&(client->invalidRegion), + &(client->invalidRegion), &screenRegion); + } + + LeaveCriticalSection(&(client->lock)); } -static INLINE void shadow_client_calc_desktop_size(rdpShadowServer* server, int* pWidth, int* pHeight) +/** + * Function description + * Recalculate client desktop size + * + * @return TRUE if width/height changed. + */ +static BOOL shadow_client_recalc_desktop_size(rdpShadowClient* client) { - RECTANGLE_16 viewport = {0, 0, server->screen->width, server->screen->height}; + int width, height; + rdpShadowServer* server = client->server; + rdpSettings* settings = client->context.settings; + + RECTANGLE_16 viewport = {0, 0, server->surface->width, server->surface->height}; if (server->shareSubRect) { rectangles_intersection(&viewport, &(server->subRect), &viewport); } - (*pWidth) = viewport.right - viewport.left; - (*pHeight) = viewport.bottom - viewport.top; + width = viewport.right - viewport.left; + height = viewport.bottom - viewport.top; + + if (settings->DesktopWidth != (UINT32)width || settings->DesktopHeight != (UINT32)height) + { + settings->DesktopWidth = width; + settings->DesktopHeight = height; + return TRUE; + } + + return FALSE; +} + +static BOOL shadow_client_capabilities(freerdp_peer* peer) +{ + rdpShadowSubsystem* subsystem; + rdpShadowClient* client; + + client = (rdpShadowClient*) peer->context; + subsystem = client->server->subsystem; + + IFCALL(subsystem->ClientCapabilities, subsystem, client); + + /* Make sure we send correct width/height to client */ + (void)shadow_client_recalc_desktop_size(client); + + return TRUE; } -BOOL shadow_client_post_connect(freerdp_peer* peer) +static BOOL shadow_client_post_connect(freerdp_peer* peer) { int authStatus; - int width, height; rdpSettings* settings; rdpShadowClient* client; rdpShadowServer* server; - RECTANGLE_16 invalidRect; rdpShadowSubsystem* subsystem; client = (rdpShadowClient*) peer->context; @@ -216,30 +313,29 @@ BOOL shadow_client_post_connect(freerdp_peer* peer) server = client->server; subsystem = server->subsystem; - shadow_client_calc_desktop_size(server, &width, &height); - settings->DesktopWidth = width; - settings->DesktopHeight = height; - if (settings->ColorDepth == 24) settings->ColorDepth = 16; /* disable 24bpp */ if (settings->MultifragMaxRequestSize < 0x3F0000) settings->NSCodec = FALSE; /* NSCodec compressor does not support fragmentation yet */ - WLog_ERR(TAG, "Client from %s is activated (%dx%d@%d)", - peer->hostname, settings->DesktopWidth, settings->DesktopHeight, settings->ColorDepth); + WLog_INFO(TAG, "Client from %s is activated (%dx%d@%d)", + peer->hostname, settings->DesktopWidth, + settings->DesktopHeight, settings->ColorDepth); - peer->update->DesktopResize(peer->update->context); + /* Resize client if necessary */ + if (shadow_client_recalc_desktop_size(client)) + { + peer->update->DesktopResize(peer->update->context); + WLog_INFO(TAG, "Client from %s is resized (%dx%d@%d)", + peer->hostname, settings->DesktopWidth, + settings->DesktopHeight, settings->ColorDepth); + } if (shadow_client_channels_post_connect(client) != CHANNEL_RC_OK) return FALSE; - invalidRect.left = 0; - invalidRect.top = 0; - invalidRect.right = server->screen->width; - invalidRect.bottom = server->screen->height; - - region16_union_rect(&(client->invalidRegion), &(client->invalidRegion), &invalidRect); + shadow_client_mark_invalid(client, 0, NULL); authStatus = -1; @@ -271,7 +367,7 @@ BOOL shadow_client_post_connect(freerdp_peer* peer) /* Convert rects in sub rect coordinate to client/surface coordinate */ static INLINE void shadow_client_convert_rects(rdpShadowClient* client, - RECTANGLE_16* dst, RECTANGLE_16* src, UINT32 numRects) + RECTANGLE_16* dst, const RECTANGLE_16* src, UINT32 numRects) { if (client->server->shareSubRect) { @@ -289,71 +385,79 @@ static INLINE void shadow_client_convert_rects(rdpShadowClient* client, } else { - CopyMemory(dst, src, numRects * sizeof(RECTANGLE_16)); + if (src != dst) + { + CopyMemory(dst, src, numRects * sizeof(RECTANGLE_16)); + } } } -BOOL shadow_client_refresh_rect(rdpShadowClient* client, BYTE count, RECTANGLE_16* areas) +static BOOL shadow_client_refresh_request(rdpShadowClient* client) { wMessage message = { 0 }; - SHADOW_MSG_IN_REFRESH_OUTPUT* wParam; wMessagePipe* MsgPipe = client->subsystem->MsgPipe; - if (count && !areas) - return FALSE; + message.id = SHADOW_MSG_IN_REFRESH_REQUEST_ID; + message.wParam = NULL; + message.lParam = NULL; + message.context = (void*) client; + message.Free = NULL; - if (!(wParam = (SHADOW_MSG_IN_REFRESH_OUTPUT*) calloc(1, sizeof(SHADOW_MSG_IN_REFRESH_OUTPUT)))) - return FALSE; + return MessageQueue_Dispatch(MsgPipe->In, &message); +} - wParam->numRects = (UINT32) count; +static BOOL shadow_client_refresh_rect(rdpShadowClient* client, BYTE count, RECTANGLE_16* areas) +{ + RECTANGLE_16* rects; + + /* It is invalid if we have area count but no actual area */ + if (count && !areas) + return FALSE; - if (wParam->numRects) + if (count) { - wParam->rects = (RECTANGLE_16*) calloc(wParam->numRects, sizeof(RECTANGLE_16)); + rects = (RECTANGLE_16*) calloc(count, sizeof(RECTANGLE_16)); - if (!wParam->rects) + if (!rects) { - free (wParam); return FALSE; } - shadow_client_convert_rects(client, wParam->rects, areas, wParam->numRects); - } + shadow_client_convert_rects(client, rects, areas, count); - message.id = SHADOW_MSG_IN_REFRESH_OUTPUT_ID; - message.wParam = (void*) wParam; - message.lParam = NULL; - message.context = (void*) client; - message.Free = shadow_client_message_free; + shadow_client_mark_invalid(client, count, rects); - return MessageQueue_Dispatch(MsgPipe->In, &message); + free(rects); + } + else + { + shadow_client_mark_invalid(client, 0, NULL); + } + + return shadow_client_refresh_request(client); } -BOOL shadow_client_suppress_output(rdpShadowClient* client, BYTE allow, RECTANGLE_16* area) +static BOOL shadow_client_suppress_output(rdpShadowClient* client, BYTE allow, RECTANGLE_16* area) { - wMessage message = { 0 }; - SHADOW_MSG_IN_SUPPRESS_OUTPUT* wParam; - wMessagePipe* MsgPipe = client->subsystem->MsgPipe; - - wParam = (SHADOW_MSG_IN_SUPPRESS_OUTPUT*) calloc(1, sizeof(SHADOW_MSG_IN_SUPPRESS_OUTPUT)); - if (!wParam) - return FALSE; + RECTANGLE_16 region; - wParam->allow = (UINT32) allow; - - if (area) - shadow_client_convert_rects(client, &(wParam->rect), area, 1); - - message.id = SHADOW_MSG_IN_SUPPRESS_OUTPUT_ID; - message.wParam = (void*) wParam; - message.lParam = NULL; - message.context = (void*) client; - message.Free = shadow_client_message_free; - - return MessageQueue_Dispatch(MsgPipe->In, &message); + client->suppressOutput = allow ? FALSE : TRUE; + if (allow) + { + if (area) + { + shadow_client_convert_rects(client, ®ion, area, 1); + shadow_client_mark_invalid(client, 1, ®ion); + } + else + { + shadow_client_mark_invalid(client, 0, NULL); + } + } + return shadow_client_refresh_request(client); } -BOOL shadow_client_activate(freerdp_peer* peer) +static BOOL shadow_client_activate(freerdp_peer* peer) { rdpSettings* settings = peer->settings; rdpShadowClient* client = (rdpShadowClient*) peer->context; @@ -375,6 +479,7 @@ BOOL shadow_client_activate(freerdp_peer* peer) shadow_encoder_reset(client->encoder); + /* Update full screen in next update */ return shadow_client_refresh_rect(client, 0, NULL); } @@ -439,7 +544,7 @@ BOOL shadow_client_logon(freerdp_peer* peer, SEC_WINNT_AUTH_IDENTITY* identity, return TRUE; } -BOOL shadow_client_surface_frame_acknowledge(rdpShadowClient* client, UINT32 frameId) +static BOOL shadow_client_surface_frame_acknowledge(rdpShadowClient* client, UINT32 frameId) { /* * Record the last client acknowledged frame id to @@ -454,28 +559,91 @@ BOOL shadow_client_surface_frame_acknowledge(rdpShadowClient* client, UINT32 fra return TRUE; } -int shadow_client_send_surface_frame_marker(rdpShadowClient* client, UINT32 action, UINT32 id) +static UINT shadow_client_rdpgfx_frame_acknowledge(RdpgfxServerContext* context, RDPGFX_FRAME_ACKNOWLEDGE_PDU* frameAcknowledge) { - SURFACE_FRAME_MARKER surfaceFrameMarker; - rdpContext* context = (rdpContext*) client; - rdpUpdate* update = context->update; + shadow_client_surface_frame_acknowledge((rdpShadowClient *)context->custom, + frameAcknowledge->frameId); + return CHANNEL_RC_OK; +} - surfaceFrameMarker.frameAction = action; - surfaceFrameMarker.frameId = id; +static int shadow_client_send_surface_gfx(rdpShadowClient* client, + BYTE* pSrcData, int nSrcStep, int nXSrc, int nYSrc, int nWidth, int nHeight) +{ + rdpUpdate* update; + rdpContext* context; + rdpSettings* settings; + rdpShadowServer* server; + rdpShadowEncoder* encoder; + RDPGFX_SURFACE_COMMAND cmd; + RDPGFX_START_FRAME_PDU cmdstart; + RDPGFX_END_FRAME_PDU cmdend; + SYSTEMTIME sTime; + + context = (rdpContext*) client; + update = context->update; + settings = context->settings; + + server = client->server; + encoder = client->encoder; - IFCALL(update->SurfaceFrameMarker, context, &surfaceFrameMarker); + cmdstart.frameId = shadow_encoder_create_frame_id(encoder); + + GetSystemTime(&sTime); + cmdstart.timestamp = sTime.wHour << 22 | sTime.wMinute << 16 | + sTime.wSecond << 10 | sTime.wMilliseconds; + + cmdend.frameId = cmdstart.frameId; + + cmd.surfaceId = 0; + cmd.contextId = 0; + cmd.format = PIXEL_FORMAT_XRGB_8888; + cmd.left = nXSrc; + cmd.top = nYSrc; + cmd.right = cmd.left + nWidth; + cmd.bottom = cmd.top + nHeight; + cmd.width = nWidth; + cmd.height = nHeight; + + if (settings->GfxH264) + { + RDPGFX_AVC420_BITMAP_STREAM avc420; + RECTANGLE_16 regionRect; + RDPGFX_H264_QUANT_QUALITY quantQualityVal; + + shadow_encoder_prepare(encoder, FREERDP_CODEC_AVC420); + + avc420_compress(encoder->h264, pSrcData, PIXEL_FORMAT_RGB32, nSrcStep, + nWidth, nHeight, &avc420.data, &avc420.length); + + cmd.codecId = RDPGFX_CODECID_AVC420; + cmd.extra = (void *)&avc420; + 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; + avc420.meta.numRegionRects = 1; + avc420.meta.regionRects = ®ionRect; + avc420.meta.quantQualityVals = &quantQualityVal; + + IFCALL(client->rdpgfx->StartFrame, client->rdpgfx, &cmdstart); + IFCALL(client->rdpgfx->SurfaceCommand, client->rdpgfx, &cmd); + IFCALL(client->rdpgfx->EndFrame, client->rdpgfx, &cmdend); + } return 1; } -int shadow_client_send_surface_bits(rdpShadowClient* client, rdpShadowSurface* surface, int nXSrc, int nYSrc, int nWidth, int nHeight) +static int shadow_client_send_surface_bits(rdpShadowClient* client, + BYTE* pSrcData, int nSrcStep, int nXSrc, int nYSrc, int nWidth, int nHeight) { int i; BOOL first; BOOL last; wStream* s; - int nSrcStep; - BYTE* pSrcData; int numMessages; UINT32 frameId = 0; rdpUpdate* update; @@ -492,24 +660,6 @@ int shadow_client_send_surface_bits(rdpShadowClient* client, rdpShadowSurface* s server = client->server; encoder = client->encoder; - pSrcData = surface->data; - nSrcStep = surface->scanline; - - if (server->shareSubRect) - { - int subX, subY; - int subWidth, subHeight; - - subX = server->subRect.left; - subY = server->subRect.top; - subWidth = server->subRect.right - server->subRect.left; - subHeight = server->subRect.bottom - server->subRect.top; - - nXSrc -= subX; - nYSrc -= subY; - pSrcData = &pSrcData[(subY * nSrcStep) + (subX * 4)]; - } - if (encoder->frameAck) frameId = shadow_encoder_create_frame_id(encoder); @@ -613,14 +763,13 @@ int shadow_client_send_surface_bits(rdpShadowClient* client, rdpShadowSurface* s return 1; } -int shadow_client_send_bitmap_update(rdpShadowClient* client, rdpShadowSurface* surface, int nXSrc, int nYSrc, int nWidth, int nHeight) +static int shadow_client_send_bitmap_update(rdpShadowClient* client, + BYTE* pSrcData, int nSrcStep, int nXSrc, int nYSrc, int nWidth, int nHeight) { BYTE* data; BYTE* buffer; int yIdx, xIdx, k; int rows, cols; - int nSrcStep; - BYTE* pSrcData; UINT32 DstSize; UINT32 SrcFormat; BITMAP_DATA* bitmap; @@ -649,25 +798,8 @@ int shadow_client_send_bitmap_update(rdpShadowClient* client, rdpShadowSurface* else shadow_encoder_prepare(encoder, FREERDP_CODEC_PLANAR); - pSrcData = surface->data; - nSrcStep = surface->scanline; SrcFormat = PIXEL_FORMAT_RGB32; - if (server->shareSubRect) - { - int subX, subY; - int subWidth, subHeight; - - subX = server->subRect.left; - subY = server->subRect.top; - subWidth = server->subRect.right - server->subRect.left; - subHeight = server->subRect.bottom - server->subRect.top; - - nXSrc -= subX; - nYSrc -= subY; - pSrcData = &pSrcData[(subY * nSrcStep) + (subX * 4)]; - } - if ((nXSrc % 4) != 0) { nWidth += (nXSrc % 4); @@ -827,7 +959,7 @@ int shadow_client_send_bitmap_update(rdpShadowClient* client, rdpShadowSurface* return 1; } -int shadow_client_send_surface_update(rdpShadowClient* client) +static int shadow_client_send_surface_update(rdpShadowClient* client, SHADOW_GFX_STATUS* pStatus) { int status = -1; int nXSrc, nYSrc; @@ -840,6 +972,11 @@ int shadow_client_send_surface_update(rdpShadowClient* client) REGION16 invalidRegion; RECTANGLE_16 surfaceRect; const RECTANGLE_16* extents; + BYTE* pSrcData; + int nSrcStep; + int index; + int numRects = 0; + const RECTANGLE_16* rects; context = (rdpContext*) client; settings = context->settings; @@ -856,6 +993,12 @@ int shadow_client_send_surface_update(rdpShadowClient* client) LeaveCriticalSection(&(client->lock)); + rects = region16_rects(&(surface->invalidRegion), &numRects); + for (index = 0; index < numRects; index++) + { + region16_union_rect(&invalidRegion, &invalidRegion, &rects[index]); + } + surfaceRect.left = 0; surfaceRect.top = 0; surfaceRect.right = surface->width; @@ -870,27 +1013,63 @@ int shadow_client_send_surface_update(rdpShadowClient* client) if (region16_is_empty(&invalidRegion)) { + /* No image region need to be updated */ region16_uninit(&invalidRegion); return 1; } extents = region16_extents(&invalidRegion); - nXSrc = extents->left - 0; - nYSrc = extents->top - 0; + nXSrc = extents->left; + nYSrc = extents->top; nWidth = extents->right - extents->left; nHeight = extents->bottom - extents->top; + pSrcData = surface->data; + nSrcStep = surface->scanline; + + /* Move to new pSrcData / nXSrc / nYSrc according to sub rect */ + if (server->shareSubRect) + { + int subX, subY; + + subX = server->subRect.left; + subY = server->subRect.top; + + nXSrc -= subX; + nYSrc -= subY; + pSrcData = &pSrcData[(subY * nSrcStep) + (subX * 4)]; + } + //WLog_INFO(TAG, "shadow_client_send_surface_update: x: %d y: %d width: %d height: %d right: %d bottom: %d", // nXSrc, nYSrc, nWidth, nHeight, nXSrc + nWidth, nYSrc + nHeight); - if (settings->RemoteFxCodec || settings->NSCodec) + if (settings->SupportGraphicsPipeline && + settings->GfxH264 && + pStatus->gfxOpened) + { + /* GFX/h264 always full screen encoded */ + nWidth = settings->DesktopWidth; + nHeight = settings->DesktopHeight; + + /* Create primary surface if have not */ + if (!pStatus->gfxSurfaceCreated) + { + /* Only init surface when we have h264 supported */ + shadow_client_rdpgfx_new_surface(client); + shadow_client_rdpgfx_reset_graphic(client); + pStatus->gfxSurfaceCreated = TRUE; + } + + status = shadow_client_send_surface_gfx(client, pSrcData, nSrcStep, 0, 0, nWidth, nHeight); + } + else if (settings->RemoteFxCodec || settings->NSCodec) { - status = shadow_client_send_surface_bits(client, surface, nXSrc, nYSrc, nWidth, nHeight); + status = shadow_client_send_surface_bits(client, pSrcData, nSrcStep, nXSrc, nYSrc, nWidth, nHeight); } else { - status = shadow_client_send_bitmap_update(client, surface, nXSrc, nYSrc, nWidth, nHeight); + status = shadow_client_send_bitmap_update(client, pSrcData, nSrcStep, nXSrc, nYSrc, nWidth, nHeight); } region16_uninit(&invalidRegion); @@ -898,27 +1077,67 @@ int shadow_client_send_surface_update(rdpShadowClient* client) return status; } -int shadow_client_surface_update(rdpShadowClient* client, REGION16* region) +static void shadow_client_send_resize(rdpShadowClient* client, SHADOW_GFX_STATUS* pStatus) { - int index; - int numRects = 0; - const RECTANGLE_16* rects; + rdpContext* context; + rdpSettings* settings; + rdpShadowServer* server; + freerdp_peer* peer; - EnterCriticalSection(&(client->lock)); + server = client->server; + context = (rdpContext*) client; + peer = context->peer; + settings = context->settings; - rects = region16_rects(region, &numRects); + /** + * Unset client activated flag to avoid sending update message during + * resize. DesktopResize will reactive the client and + * shadow_client_activate would be invoked later. + */ + client->activated = FALSE; - for (index = 0; index < numRects; index++) + /* Close Gfx surfaces */ + if (pStatus->gfxSurfaceCreated) { - region16_union_rect(&(client->invalidRegion), &(client->invalidRegion), &rects[index]); + shadow_client_rdpgfx_release_surface(client); + pStatus->gfxSurfaceCreated = FALSE; } + /* Send Resize */ + peer->update->DesktopResize(peer->update->context); // update_send_desktop_resize + + /* Clear my invalidRegion. shadow_client_activate refreshes fullscreen */ + EnterCriticalSection(&(client->lock)); + region16_clear(&(client->invalidRegion)); LeaveCriticalSection(&(client->lock)); + WLog_INFO(TAG, "Client from %s is resized (%dx%d@%d)", + peer->hostname, settings->DesktopWidth, settings->DesktopHeight, settings->ColorDepth); +} + +static INLINE void shadow_client_no_surface_update(rdpShadowClient* client, SHADOW_GFX_STATUS* pStatus) +{ + rdpShadowServer* server; + rdpShadowSurface* surface; + + server = client->server; + surface = client->inLobby ? server->lobby : server->surface; + + shadow_client_surface_update(client, &(surface->invalidRegion)); +} + +int shadow_client_surface_update(rdpShadowClient* client, REGION16* region) +{ + int numRects = 0; + const RECTANGLE_16* rects; + + rects = region16_rects(region, &numRects); + shadow_client_mark_invalid(client, numRects, rects); + return 1; } -int shadow_client_subsystem_process_message(rdpShadowClient* client, wMessage* message) +static int shadow_client_subsystem_process_message(rdpShadowClient* client, wMessage* message) { rdpContext* context = (rdpContext*) client; rdpUpdate* update = context->update; @@ -1013,7 +1232,7 @@ int shadow_client_subsystem_process_message(rdpShadowClient* client, wMessage* m return 1; } -void* shadow_client_thread(rdpShadowClient* client) +static void* shadow_client_thread(rdpShadowClient* client) { DWORD status; DWORD nCount; @@ -1034,6 +1253,11 @@ void* shadow_client_thread(rdpShadowClient* client) rdpShadowEncoder* encoder; rdpShadowSubsystem* subsystem; wMessageQueue* MsgQueue = client->MsgQueue; + /* This should only be visited in client thread */ + SHADOW_GFX_STATUS gfxstatus; + + gfxstatus.gfxOpened = FALSE; + gfxstatus.gfxSurfaceCreated = FALSE; server = client->server; screen = server->screen; @@ -1080,50 +1304,35 @@ void* shadow_client_thread(rdpShadowClient* client) if (WaitForSingleObject(UpdateEvent, 0) == WAIT_OBJECT_0) { - if (client->activated) + /* The UpdateEvent means to start sending current frame. It is + * triggered from subsystem implementation and it should ensure + * that the screen and primary surface meta data (width, height, + * scanline, invalid region, etc) is not changed until it is reset + * (at shadow_multiclient_consume). As best practice, subsystem + * implementation should invoke shadow_subsystem_frame_update which + * triggers the event and then wait for completion */ + + if (client->activated && !client->suppressOutput) { - int index; - int numRects = 0; - const RECTANGLE_16* rects; - int width, height; + /* Send screen update or resize to this client */ /* Check resize */ - shadow_client_calc_desktop_size(server, &width, &height); - if (settings->DesktopWidth != (UINT32)width || settings->DesktopHeight != (UINT32)height) + if (shadow_client_recalc_desktop_size(client)) { /* Screen size changed, do resize */ - settings->DesktopWidth = width; - settings->DesktopHeight = height; - - /** - * Unset client activated flag to avoid sending update message during - * resize. DesktopResize will reactive the client and - * shadow_client_activate would be invoked later. - */ - client->activated = FALSE; - - /* Send Resize */ - peer->update->DesktopResize(peer->update->context); // update_send_desktop_resize - - /* Clear my invalidRegion. shadow_client_activate refreshes fullscreen */ - region16_clear(&(client->invalidRegion)); - - WLog_ERR(TAG, "Client from %s is resized (%dx%d@%d)", - peer->hostname, settings->DesktopWidth, settings->DesktopHeight, settings->ColorDepth); + shadow_client_send_resize(client, &gfxstatus); } else { /* Send frame */ - rects = region16_rects(&(subsystem->invalidRegion), &numRects); - - for (index = 0; index < numRects; index++) - { - region16_union_rect(&(client->invalidRegion), &(client->invalidRegion), &rects[index]); - } - - shadow_client_send_surface_update(client); + shadow_client_send_surface_update(client, &gfxstatus); } } + else + { + /* Our client don't receive graphic updates. Just save the invalid region */ + shadow_client_no_surface_update(client, &gfxstatus); + } /* * The return value of shadow_multiclient_consume is whether or not the subscriber really consumes the event. @@ -1139,6 +1348,38 @@ void* shadow_client_thread(rdpShadowClient* client) WLog_ERR(TAG, "Failed to check FreeRDP file descriptor"); break; } + + if (WTSVirtualChannelManagerIsChannelJoined(client->vcm, "drdynvc")) + { + /* Dynamic channel status may have been changed after processing */ + if (((WTSVirtualChannelManager*)(client->vcm))->drdynvc_state == DRDYNVC_STATE_NONE) + { + /* Call this routine to Initialize drdynvc channel */ + if (!WTSVirtualChannelManagerCheckFileDescriptor(client->vcm)) + { + WLog_ERR(TAG, "Failed to initialize drdynvc channel"); + break; + } + } + else if (((WTSVirtualChannelManager*)(client->vcm))->drdynvc_state == DRDYNVC_STATE_READY) + { + /* Init RDPGFX dynamic channel */ + if (settings->SupportGraphicsPipeline && client->rdpgfx && + !gfxstatus.gfxOpened) + { + if (!client->rdpgfx->Open(client->rdpgfx)) + { + WLog_ERR(TAG, "Failed to open GraphicsPipeline"); + settings->SupportGraphicsPipeline = FALSE; + } + + client->rdpgfx->FrameAcknowledge = shadow_client_rdpgfx_frame_acknowledge; + + gfxstatus.gfxOpened = TRUE; + WLog_INFO(TAG, "Gfx Pipeline Opened"); + } + } + } } if (WaitForSingleObject(ChannelEvent, 0) == WAIT_OBJECT_0) @@ -1220,6 +1461,14 @@ void* shadow_client_thread(rdpShadowClient* client) } /* Free channels early because we establish channels in post connect */ + if (gfxstatus.gfxOpened) + { + if (gfxstatus.gfxSurfaceCreated) + { + shadow_client_rdpgfx_release_surface(client); + } + (void)client->rdpgfx->Close(client->rdpgfx); + } shadow_client_channels_free(client); if (UpdateSubscriber) diff --git a/server/shadow/shadow_encoder.c b/server/shadow/shadow_encoder.c index 07bf880..be3194c 100644 --- a/server/shadow/shadow_encoder.c +++ b/server/shadow/shadow_encoder.c @@ -130,9 +130,6 @@ int shadow_encoder_uninit_grid(rdpShadowEncoder* encoder) int shadow_encoder_init_rfx(rdpShadowEncoder* encoder) { - rdpContext* context = (rdpContext*) encoder->client; - rdpSettings* settings = context->settings; - if (!encoder->rfx) encoder->rfx = rfx_context_new(TRUE); @@ -148,12 +145,6 @@ int shadow_encoder_init_rfx(rdpShadowEncoder* encoder) rfx_context_set_pixel_format(encoder->rfx, RDP_PIXEL_FORMAT_B8G8R8A8); - encoder->fps = 16; - encoder->maxFps = 32; - encoder->frameId = 0; - encoder->lastAckframeId = 0; - encoder->frameAck = settings->SurfaceFrameMarkerEnabled; - encoder->codecs |= FREERDP_CODEC_REMOTEFX; return 1; @@ -172,23 +163,24 @@ int shadow_encoder_init_nsc(rdpShadowEncoder* encoder) encoder->nsc = nsc_context_new(); if (!encoder->nsc) - return -1; - - nsc_context_set_pixel_format(encoder->nsc, RDP_PIXEL_FORMAT_B8G8R8A8); + goto fail; - encoder->fps = 16; - encoder->maxFps = 32; - encoder->frameId = 0; - encoder->lastAckframeId = 0; - encoder->frameAck = settings->SurfaceFrameMarkerEnabled; + if (!nsc_context_reset(encoder->nsc, encoder->width, encoder->height)) + goto fail; encoder->nsc->ColorLossLevel = settings->NSCodecColorLossLevel; encoder->nsc->ChromaSubsamplingLevel = settings->NSCodecAllowSubsampling ? 1 : 0; encoder->nsc->DynamicColorFidelity = settings->NSCodecAllowDynamicColorFidelity; + nsc_context_set_pixel_format(encoder->nsc, RDP_PIXEL_FORMAT_B8G8R8A8); + encoder->codecs |= FREERDP_CODEC_NSCODEC; return 1; + +fail: + nsc_context_free(encoder->nsc); + return -1; } int shadow_encoder_init_planar(rdpShadowEncoder* encoder) @@ -209,11 +201,18 @@ int shadow_encoder_init_planar(rdpShadowEncoder* encoder) } if (!encoder->planar) - return -1; + goto fail; + + if (!freerdp_bitmap_planar_context_reset(encoder->planar)) + goto fail; encoder->codecs |= FREERDP_CODEC_PLANAR; return 1; + +fail: + freerdp_bitmap_planar_context_free(encoder->planar); + return -1; } int shadow_encoder_init_interleaved(rdpShadowEncoder* encoder) @@ -222,11 +221,38 @@ int shadow_encoder_init_interleaved(rdpShadowEncoder* encoder) encoder->interleaved = bitmap_interleaved_context_new(TRUE); if (!encoder->interleaved) - return -1; + goto fail; + + if (!bitmap_interleaved_context_reset(encoder->interleaved)) + goto fail; encoder->codecs |= FREERDP_CODEC_INTERLEAVED; return 1; + +fail: + bitmap_interleaved_context_free(encoder->interleaved); + return -1; +} + +int shadow_encoder_init_h264(rdpShadowEncoder* encoder) +{ + if (!encoder->h264) + encoder->h264 = h264_context_new(TRUE); + + if (!encoder->h264) + goto fail; + + if (!h264_context_reset(encoder->h264, encoder->width, encoder->height)) + goto fail; + + encoder->codecs |= FREERDP_CODEC_AVC420; + + return 1; + +fail: + h264_context_free(encoder->h264); + return -1; } int shadow_encoder_init(rdpShadowEncoder* encoder) @@ -300,6 +326,19 @@ int shadow_encoder_uninit_interleaved(rdpShadowEncoder* encoder) return 1; } +int shadow_encoder_uninit_h264(rdpShadowEncoder* encoder) +{ + if (encoder->h264) + { + h264_context_free(encoder->h264); + encoder->h264= NULL; + } + + encoder->codecs &= ~FREERDP_CODEC_AVC420; + + return 1; +} + int shadow_encoder_uninit(rdpShadowEncoder* encoder) { shadow_encoder_uninit_grid(encoder); @@ -330,6 +369,11 @@ int shadow_encoder_uninit(rdpShadowEncoder* encoder) shadow_encoder_uninit_interleaved(encoder); } + if (encoder->codecs & FREERDP_CODEC_AVC420) + { + shadow_encoder_uninit_h264(encoder); + } + return 1; } @@ -337,6 +381,8 @@ int shadow_encoder_reset(rdpShadowEncoder* encoder) { int status; UINT32 codecs = encoder->codecs; + rdpContext* context = (rdpContext*) encoder->client; + rdpSettings* settings = context->settings; status = shadow_encoder_uninit(encoder); @@ -353,6 +399,12 @@ int shadow_encoder_reset(rdpShadowEncoder* encoder) if (status < 0) return -1; + encoder->fps = 16; + encoder->maxFps = 32; + encoder->frameId = 0; + encoder->lastAckframeId = 0; + encoder->frameAck = settings->SurfaceFrameMarkerEnabled; + return 1; } @@ -392,6 +444,14 @@ int shadow_encoder_prepare(rdpShadowEncoder* encoder, UINT32 codecs) return -1; } + if ((codecs & FREERDP_CODEC_AVC420) && !(encoder->codecs & FREERDP_CODEC_AVC420)) + { + status = shadow_encoder_init_h264(encoder); + + if (status < 0) + return -1; + } + return 1; } diff --git a/server/shadow/shadow_encoder.h b/server/shadow/shadow_encoder.h index fd7d46a..b2c01b9 100644 --- a/server/shadow/shadow_encoder.h +++ b/server/shadow/shadow_encoder.h @@ -49,6 +49,7 @@ struct rdp_shadow_encoder NSC_CONTEXT* nsc; BITMAP_PLANAR_CONTEXT* planar; BITMAP_INTERLEAVED_CONTEXT* interleaved; + H264_CONTEXT* h264; int fps; int maxFps; diff --git a/server/shadow/shadow_rdpgfx.c b/server/shadow/shadow_rdpgfx.c new file mode 100644 index 0000000..51459ec --- /dev/null +++ b/server/shadow/shadow_rdpgfx.c @@ -0,0 +1,87 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * + * Copyright 2016 Jiang Zihao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "shadow.h" + +#include "shadow_rdpgfx.h" + +#define TAG SERVER_TAG("shadow") + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT rdpgfx_caps_advertise(RdpgfxServerContext* context, RDPGFX_CAPS_ADVERTISE_PDU* capsAdvertise) +{ + UINT16 index; + RDPGFX_CAPS_CONFIRM_PDU pdu; + rdpSettings* settings = context->rdpcontext->settings; + UINT32 flags = 0; + + for (index = 0; index < capsAdvertise->capsSetCount; index++) + { + pdu.capsSet = &(capsAdvertise->capsSets[index]); + if (pdu.capsSet->version == RDPGFX_CAPVERSION_81) + { + if (settings) + { + flags = pdu.capsSet->flags; + settings->GfxThinClient = (flags & RDPGFX_CAPS_FLAG_THINCLIENT); + settings->GfxSmallCache = (flags & RDPGFX_CAPS_FLAG_SMALL_CACHE); + settings->GfxH264 = (flags & RDPGFX_CAPS_FLAG_AVC420_ENABLED); + } + + return context->CapsConfirm(context, &pdu); + } + } + + return CHANNEL_RC_UNSUPPORTED_VERSION; +} + +int shadow_client_rdpgfx_init(rdpShadowClient* client) +{ + RdpgfxServerContext* rdpgfx; + rdpgfx = client->rdpgfx = rdpgfx_server_context_new(client->vcm); + if (!rdpgfx) + { + return 0; + } + + rdpgfx->rdpcontext = &client->context; + + rdpgfx->custom = client; + + rdpgfx->CapsAdvertise = rdpgfx_caps_advertise; + + return 1; +} + +void shadow_client_rdpgfx_uninit(rdpShadowClient* client) +{ + if (client->rdpgfx) + { + rdpgfx_server_context_free(client->rdpgfx); + client->rdpgfx = NULL; + } +} diff --git a/server/shadow/shadow_rdpgfx.h b/server/shadow/shadow_rdpgfx.h new file mode 100644 index 0000000..0ac810d --- /dev/null +++ b/server/shadow/shadow_rdpgfx.h @@ -0,0 +1,38 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * + * Copyright 2016 Jiang Zihao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_SHADOW_SERVER_RDPGFX_H +#define FREERDP_SHADOW_SERVER_RDPGFX_H + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int shadow_client_rdpgfx_init(rdpShadowClient* client); +void shadow_client_rdpgfx_uninit(rdpShadowClient* client); + +#ifdef __cplusplus +} +#endif + +#endif /* FREERDP_SHADOW_SERVER_RDPGFX_H */ diff --git a/server/shadow/shadow_subsystem.c b/server/shadow/shadow_subsystem.c index 0cbb46f..7f3173a 100644 --- a/server/shadow/shadow_subsystem.c +++ b/server/shadow/shadow_subsystem.c @@ -86,8 +86,6 @@ int shadow_subsystem_init(rdpShadowSubsystem* subsystem, rdpShadowServer* server if (!(subsystem->updateEvent = shadow_multiclient_new())) goto fail; - region16_init(&(subsystem->invalidRegion)); - if ((status = subsystem->ep.Init(subsystem)) >= 0) return status; @@ -141,9 +139,6 @@ void shadow_subsystem_uninit(rdpShadowSubsystem* subsystem) shadow_multiclient_free(subsystem->updateEvent); subsystem->updateEvent = NULL; } - - if (subsystem->invalidRegion.data) - region16_uninit(&(subsystem->invalidRegion)); } int shadow_subsystem_start(rdpShadowSubsystem* subsystem) -- 2.7.4