diff --git a/channels/rdpgfx/CMakeLists.txt b/channels/rdpgfx/CMakeLists.txt index acc50da70..04820de9d 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 19c5d21b0..0de358a2e 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_codec.c b/channels/rdpgfx/client/rdpgfx_codec.c index bf28ddd4d..323cda5a5 100644 --- a/channels/rdpgfx/client/rdpgfx_codec.c +++ b/channels/rdpgfx/client/rdpgfx_codec.c @@ -57,7 +57,7 @@ static UINT rdpgfx_read_h264_metablock(RDPGFX_PLUGIN* gfx, wStream* s, Stream_Read_UINT32(s, meta->numRegionRects); /* numRegionRects (4 bytes) */ - if (Stream_GetRemainingLength(s) < (meta->numRegionRects * sizeof(RECTANGLE_16))) + if (Stream_GetRemainingLength(s) < (meta->numRegionRects * 8)) { WLog_ERR(TAG, "not enough data!"); goto error_out; diff --git a/channels/rdpgfx/client/rdpgfx_main.c b/channels/rdpgfx/client/rdpgfx_main.c index 50403eade..e2aa58607 100644 --- a/channels/rdpgfx/client/rdpgfx_main.c +++ b/channels/rdpgfx/client/rdpgfx_main.c @@ -457,7 +457,7 @@ static UINT rdpgfx_recv_start_frame_pdu(RDPGFX_CHANNEL_CALLBACK* callback, wStre RdpgfxClientContext* context = (RdpgfxClientContext*) gfx->iface.pInterface; UINT error = CHANNEL_RC_OK; - if (Stream_GetRemainingLength(s) < 8) + if (Stream_GetRemainingLength(s) < RDPGFX_START_FRAME_PDU_SIZE) { WLog_ERR(TAG, "not enough data!"); return ERROR_INVALID_DATA; @@ -494,7 +494,7 @@ static UINT rdpgfx_recv_end_frame_pdu(RDPGFX_CHANNEL_CALLBACK* callback, wStream RdpgfxClientContext* context = (RdpgfxClientContext*) gfx->iface.pInterface; UINT error = CHANNEL_RC_OK; - if (Stream_GetRemainingLength(s) < 4) + if (Stream_GetRemainingLength(s) < RDPGFX_END_FRAME_PDU_SIZE) { WLog_ERR(TAG, "not enough data!"); return ERROR_INVALID_DATA; @@ -550,7 +550,7 @@ static UINT rdpgfx_recv_wire_to_surface_1_pdu(RDPGFX_CHANNEL_CALLBACK* callback, RDPGFX_PLUGIN* gfx = (RDPGFX_PLUGIN*) callback->plugin; UINT error; - if (Stream_GetRemainingLength(s) < 17) + if (Stream_GetRemainingLength(s) < RDPGFX_WIRE_TO_SURFACE_PDU_1_SIZE) { WLog_ERR(TAG, "not enough data!"); return ERROR_INVALID_DATA; @@ -615,7 +615,7 @@ static UINT rdpgfx_recv_wire_to_surface_2_pdu(RDPGFX_CHANNEL_CALLBACK* callback, RdpgfxClientContext* context = (RdpgfxClientContext*) gfx->iface.pInterface; UINT error = CHANNEL_RC_OK; - if (Stream_GetRemainingLength(s) < 13) + if (Stream_GetRemainingLength(s) < RDPGFX_WIRE_TO_SURFACE_PDU_2_SIZE) { WLog_ERR(TAG, "not enough data!"); return ERROR_INVALID_DATA; 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 a7518a5f0..849554dd3 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 000000000..1b1f48b20 --- /dev/null +++ b/channels/rdpgfx/server/CMakeLists.txt @@ -0,0 +1,34 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP cmake build script +# +# 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. + +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 000000000..906374b27 --- /dev/null +++ b/channels/rdpgfx/server/rdpgfx_main.c @@ -0,0 +1,1643 @@ +/** + * 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 + * Calculate packet size from data length. + * It would be data length + header. + * + * @param dataLen estimated data length without header + * + * @return new stream + */ +static INLINE UINT32 rdpgfx_pdu_length(UINT32 dataLen) +{ + return RDPGFX_HEADER_SIZE + dataLen; +} + +static INLINE UINT rdpgfx_server_packet_init_header(wStream* s, + UINT16 cmdId, UINT32 pduLength) +{ + RDPGFX_HEADER header; + header.flags = 0; + header.cmdId = cmdId; + header.pduLength = pduLength; + /* Write header. Note that actual length might be changed + * after the entire packet has been constructed. */ + return rdpgfx_write_header(s, &header); +} + +/** + * Function description + * Complete the rdpgfx packet header. + * + * @param s stream + * @param start saved start pos of the packet in the stream + */ +static INLINE void rdpgfx_server_packet_complete_header(wStream* s, size_t start) +{ + size_t current = Stream_GetPosition(s); + /* Fill actual length */ + Stream_SetPosition(s, start + RDPGFX_HEADER_SIZE - sizeof(UINT32)); + Stream_Write_UINT32(s, current - start); /* pduLength (4 bytes) */ + Stream_SetPosition(s, current); +} + +/** + * Function description + * Send the stream for rdpgfx server 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; + /* 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 (!fs) + { + WLog_ERR(TAG, "Stream_New failed!"); + error = CHANNEL_RC_NO_MEMORY; + goto out; + } + + 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; + } + + if (!WTSVirtualChannelWrite(context->priv->rdpgfx_channel, + (PCHAR) Stream_Buffer(fs), + Stream_GetPosition(fs), &written)) + { + WLog_ERR(TAG, "WTSVirtualChannelWrite failed!"); + error = ERROR_INTERNAL_ERROR; + goto out; + } + + if (written < Stream_GetPosition(fs)) + { + WLog_WARN(TAG, "Unexpected bytes written: %lu/%lu", + written, Stream_GetPosition(fs)); + } + + error = CHANNEL_RC_OK; +out: + Stream_Free(fs, TRUE); + Stream_Free(s, TRUE); + return error; +} + +/** + * Function description + * Create new stream for single 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_single_packet_send. + * + * @param cmdId + * @param dataLen estimated data length without header + * + * @return new stream + */ +static wStream* rdpgfx_server_single_packet_new(UINT16 cmdId, UINT32 dataLen) +{ + UINT error; + wStream* s; + UINT32 pduLength = rdpgfx_pdu_length(dataLen); + s = Stream_New(NULL, pduLength); + + if (!s) + { + WLog_ERR(TAG, "Stream_New failed!"); + goto error; + } + + if ((error = rdpgfx_server_packet_init_header(s, cmdId, pduLength))) + { + WLog_ERR(TAG, "Failed to init header with error %lu!", error); + goto error; + } + + return s; +error: + Stream_Free(s, TRUE); + return NULL; +} + +/** + * Function description + * Send the stream for single rdpgfx packet. + * The header will be filled with actual length. + * The packet would be compressed according to [MS-RDPEGFX]. + * + * @return 0 on success, otherwise a Win32 error code + */ +static INLINE UINT rdpgfx_server_single_packet_send( + RdpgfxServerContext* context, wStream* s) +{ + /* Fill actual length */ + rdpgfx_server_packet_complete_header(s, 0); + return rdpgfx_server_packet_send(context, s); +} + +/** + * 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_single_packet_new( + RDPGFX_CMDID_CAPSCONFIRM, RDPGFX_CAPSET_SIZE); + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_single_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT32(s, capsSet->version); /* version (4 bytes) */ + Stream_Write_UINT32(s, 4); /* capsDataLength (4 bytes) */ + Stream_Write_UINT32(s, capsSet->flags); /* capsData (4 bytes) */ + return rdpgfx_server_single_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; + + /* Check monitorCount. This ensures total size within 340 bytes) */ + if (pdu->monitorCount >= 16) + { + WLog_ERR(TAG, "Monitor count MUST be less than or equal to 16: %lu", + pdu->monitorCount); + return ERROR_INVALID_DATA; + } + + s = rdpgfx_server_single_packet_new( + RDPGFX_CMDID_RESETGRAPHICS, + RDPGFX_RESET_GRAPHICS_PDU_SIZE - RDPGFX_HEADER_SIZE); + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_single_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) */ + Stream_SetPosition(s, RDPGFX_RESET_GRAPHICS_PDU_SIZE); + return rdpgfx_server_single_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_single_packet_new(RDPGFX_CMDID_EVICTCACHEENTRY, 2); + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_single_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT16(s, pdu->cacheSlot); /* cacheSlot (2 bytes) */ + return rdpgfx_server_single_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_single_packet_new( + RDPGFX_CMDID_CACHEIMPORTREPLY, + 2 + 2 * pdu->importedEntriesCount); + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_single_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + /* importedEntriesCount (2 bytes) */ + Stream_Write_UINT16(s, pdu->importedEntriesCount); + + for (index = 0; index < pdu->importedEntriesCount; index++) + { + Stream_Write_UINT16(s, pdu->cacheSlots[index]); /* cacheSlot (2 bytes) */ + } + + return rdpgfx_server_single_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_single_packet_new(RDPGFX_CMDID_CREATESURFACE, 7); + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_single_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_single_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_single_packet_new(RDPGFX_CMDID_DELETESURFACE, 2); + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_single_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT16(s, pdu->surfaceId); /* surfaceId (2 bytes) */ + return rdpgfx_server_single_packet_send(context, s); +} + +static INLINE void rdpgfx_write_start_frame_pdu(wStream* s, + RDPGFX_START_FRAME_PDU* pdu) +{ + Stream_Write_UINT32(s, pdu->timestamp); /* timestamp (4 bytes) */ + Stream_Write_UINT32(s, pdu->frameId); /* frameId (4 bytes) */ +} + +static INLINE void rdpgfx_write_end_frame_pdu(wStream* s, + RDPGFX_END_FRAME_PDU* pdu) +{ + Stream_Write_UINT32(s, pdu->frameId); /* frameId (4 bytes) */ +} + +/** + * 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_single_packet_new( + RDPGFX_CMDID_STARTFRAME, + RDPGFX_START_FRAME_PDU_SIZE); + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_single_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + rdpgfx_write_start_frame_pdu(s, pdu); + return rdpgfx_server_single_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_single_packet_new( + RDPGFX_CMDID_ENDFRAME, + RDPGFX_END_FRAME_PDU_SIZE); + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_single_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + rdpgfx_write_end_frame_pdu(s, pdu); + return rdpgfx_server_single_packet_send(context, s); +} + +/** + * 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) +{ + /* H264 metadata + H264 stream. See rdpgfx_write_h264_avc420 */ + return sizeof(UINT32) /* numRegionRects */ + + 10 /* regionRects + quantQualityVals */ + * havc420->meta.numRegionRects + + havc420->length; +} + +/** + * Function description + * Estimate surface command packet size in stream without header + * + * @return estimated size + */ +static INLINE UINT32 rdpgfx_estimate_surface_command(RDPGFX_SURFACE_COMMAND* cmd) +{ + RDPGFX_AVC420_BITMAP_STREAM* havc420 = NULL; + RDPGFX_AVC444_BITMAP_STREAM* havc444 = NULL; + UINT32 h264Size = 0; + + /* Estimate stream size according to codec. */ + switch (cmd->codecId) + { + case RDPGFX_CODECID_CAPROGRESSIVE: + case RDPGFX_CODECID_CAPROGRESSIVE_V2: + return RDPGFX_WIRE_TO_SURFACE_PDU_2_SIZE + cmd->length; + case RDPGFX_CODECID_AVC420: + havc420 = (RDPGFX_AVC420_BITMAP_STREAM*)cmd->extra; + h264Size = rdpgfx_estimate_h264_avc420(havc420); + return RDPGFX_WIRE_TO_SURFACE_PDU_1_SIZE + h264Size; + case 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); + } + + return RDPGFX_WIRE_TO_SURFACE_PDU_1_SIZE + h264Size; + default: + return RDPGFX_WIRE_TO_SURFACE_PDU_1_SIZE + cmd->length; + } +} + +/** + * Function description + * Resolve RDPGFX_CMDID_WIRETOSURFACE_1 or RDPGFX_CMDID_WIRETOSURFACE_2 + * according to codecId + * + * @return 0 on success, otherwise a Win32 error code + */ +static INLINE UINT16 rdpgfx_surface_command_cmdid(RDPGFX_SURFACE_COMMAND* cmd) +{ + if (cmd->codecId == RDPGFX_CODECID_CAPROGRESSIVE || + cmd->codecId == RDPGFX_CODECID_CAPROGRESSIVE_V2) + { + return RDPGFX_CMDID_WIRETOSURFACE_2; + } + + return RDPGFX_CMDID_WIRETOSURFACE_1; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT rdpgfx_write_h264_metablock(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) */ + /* qualityVal (1 byte) */ + Stream_Write_UINT8(s, quantQualityVal->qualityVal); + } + + 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(wStream* s, + RDPGFX_AVC420_BITMAP_STREAM* havc420) +{ + UINT error = CHANNEL_RC_OK; + + if ((error = rdpgfx_write_h264_metablock(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 + * Write RDPGFX_CMDID_WIRETOSURFACE_1 or RDPGFX_CMDID_WIRETOSURFACE_2 + * to the stream according to RDPGFX_SURFACE_COMMAND message + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT rdpgfx_write_surface_command(wStream* s, RDPGFX_SURFACE_COMMAND* cmd) +{ + UINT error = CHANNEL_RC_OK; + RDPGFX_AVC420_BITMAP_STREAM* havc420 = NULL; + RDPGFX_AVC444_BITMAP_STREAM* havc444 = NULL; + UINT32 bitmapDataStart = 0; + UINT32 bitmapDataLength = 0; + + if (cmd->codecId == RDPGFX_CODECID_CAPROGRESSIVE || + cmd->codecId == RDPGFX_CODECID_CAPROGRESSIVE_V2) + { + /* Write RDPGFX_CMDID_WIRETOSURFACE_2 format for CAPROGRESSIVE */ + 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); + } + else + { + /* Write RDPGFX_CMDID_WIRETOSURFACE_1 format for others */ + 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; + error = rdpgfx_write_h264_avc420(s, havc420); + + if (error != CHANNEL_RC_OK) + { + WLog_ERR(TAG, "rdpgfx_write_h264_avc420 failed!"); + return error; + } + } + 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 */ + error = rdpgfx_write_h264_avc420(s, havc420); + + if (error != CHANNEL_RC_OK) + { + WLog_ERR(TAG, "rdpgfx_write_h264_avc420 failed!"); + return error; + } + + /* avc420EncodedBitstream2 */ + if (havc444->LC == 0) + { + havc420 = &(havc444->bitstream[0]); + error = rdpgfx_write_h264_avc420(s, havc420); + + if (error != CHANNEL_RC_OK) + { + WLog_ERR(TAG, "rdpgfx_write_h264_avc420 failed!"); + return error; + } + } + } + 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 error; +} + +/** + * 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) +{ + UINT error = CHANNEL_RC_OK; + wStream* s; + s = rdpgfx_server_single_packet_new( + rdpgfx_surface_command_cmdid(cmd), + rdpgfx_estimate_surface_command(cmd)); + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_single_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + error = rdpgfx_write_surface_command(s, cmd); + + if (error != CHANNEL_RC_OK) + { + WLog_ERR(TAG, "rdpgfx_write_surface_command failed!"); + goto error; + } + + return rdpgfx_server_single_packet_send(context, s); +error: + Stream_Free(s, TRUE); + return error; +} + +/** + * Function description + * Send RDPGFX_CMDID_WIRETOSURFACE_1 or RDPGFX_CMDID_WIRETOSURFACE_2 + * message according to codecId. + * Prepend/append start/end frame message in same packet if exists. + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT rdpgfx_send_surface_frame_command(RdpgfxServerContext* context, + RDPGFX_SURFACE_COMMAND* cmd, RDPGFX_START_FRAME_PDU* startFrame, + RDPGFX_END_FRAME_PDU* endFrame) + +{ + UINT error = CHANNEL_RC_OK; + wStream* s; + UINT32 position = 0; + UINT32 size = rdpgfx_pdu_length(rdpgfx_estimate_surface_command(cmd)); + + if (startFrame) + { + size += rdpgfx_pdu_length(RDPGFX_START_FRAME_PDU_SIZE); + } + + if (endFrame) + { + size += rdpgfx_pdu_length(RDPGFX_END_FRAME_PDU_SIZE); + } + + s = Stream_New(NULL, size); + + if (!s) + { + WLog_ERR(TAG, "Stream_New failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + /* Write start frame if exists */ + if (startFrame) + { + position = Stream_GetPosition(s); + error = rdpgfx_server_packet_init_header(s, + RDPGFX_CMDID_STARTFRAME, 0); + + if (error != CHANNEL_RC_OK) + { + WLog_ERR(TAG, "Failed to init header with error %lu!", error); + goto error; + } + + rdpgfx_write_start_frame_pdu(s, startFrame); + rdpgfx_server_packet_complete_header(s, position); + } + + /* Write RDPGFX_CMDID_WIRETOSURFACE_1 or RDPGFX_CMDID_WIRETOSURFACE_2 */ + position = Stream_GetPosition(s); + error = rdpgfx_server_packet_init_header(s, + rdpgfx_surface_command_cmdid(cmd), + 0); // Actual length will be filled later + + if (error != CHANNEL_RC_OK) + { + WLog_ERR(TAG, "Failed to init header with error %lu!", error); + goto error; + } + + error = rdpgfx_write_surface_command(s, cmd); + + if (error != CHANNEL_RC_OK) + { + WLog_ERR(TAG, "rdpgfx_write_surface_command failed!"); + goto error; + } + + rdpgfx_server_packet_complete_header(s, position); + + /* Write end frame if exists */ + if (endFrame) + { + position = Stream_GetPosition(s); + error = rdpgfx_server_packet_init_header(s, + RDPGFX_CMDID_ENDFRAME, 0); + + if (error != CHANNEL_RC_OK) + { + WLog_ERR(TAG, "Failed to init header with error %lu!", error); + goto error; + } + + rdpgfx_write_end_frame_pdu(s, endFrame); + rdpgfx_server_packet_complete_header(s, position); + } + + return rdpgfx_server_packet_send(context, s); +error: + Stream_Free(s, TRUE); + return error; +} + +/** + * 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_single_packet_new( + RDPGFX_CMDID_DELETEENCODINGCONTEXT, 6); + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_single_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_single_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) +{ + UINT error = CHANNEL_RC_OK; + UINT16 index; + RECTANGLE_16* fillRect; + wStream* s = rdpgfx_server_single_packet_new( + RDPGFX_CMDID_SOLIDFILL, + 8 + 8 * pdu->fillRectCount); + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_single_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT16(s, pdu->surfaceId); /* surfaceId (2 bytes) */ + + /* fillPixel (4 bytes) */ + if ((error = rdpgfx_write_color32(s, &(pdu->fillPixel)))) + { + WLog_ERR(TAG, "rdpgfx_write_color32 failed with error %lu!", error); + goto 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); + goto error; + } + } + + return rdpgfx_server_single_packet_send(context, s); +error: + Stream_Free(s, TRUE); + return error; +} + +/** + * 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) +{ + UINT error = CHANNEL_RC_OK; + UINT16 index; + RDPGFX_POINT16* destPt; + wStream* s = rdpgfx_server_single_packet_new( + RDPGFX_CMDID_SURFACETOSURFACE, + 14 + 4 * pdu->destPtsCount); + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_single_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) */ + + /* rectSrc (8 bytes ) */ + if ((error = rdpgfx_write_rect16(s, &(pdu->rectSrc)))) + { + WLog_ERR(TAG, "rdpgfx_write_rect16 failed with error %lu!", error); + goto 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); + goto error; + } + } + + return rdpgfx_server_single_packet_send(context, s); +error: + Stream_Free(s, TRUE); + return error; +} + +/** + * 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 = CHANNEL_RC_OK; + wStream* s = rdpgfx_server_single_packet_new( + RDPGFX_CMDID_SURFACETOCACHE, 20); + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_single_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) */ + + /* rectSrc (8 bytes ) */ + if ((error = rdpgfx_write_rect16(s, &(pdu->rectSrc)))) + { + WLog_ERR(TAG, "rdpgfx_write_rect16 failed with error %lu!", error); + goto error; + } + + return rdpgfx_server_single_packet_send(context, s); +error: + Stream_Free(s, TRUE); + return error; +} + +/** + * 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) +{ + UINT error = CHANNEL_RC_OK; + UINT16 index; + RDPGFX_POINT16* destPt; + wStream* s = rdpgfx_server_single_packet_new( + RDPGFX_CMDID_CACHETOSURFACE, + 6 + 4 * pdu->destPtsCount); + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_single_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); + goto error; + } + } + + return rdpgfx_server_single_packet_send(context, s); +error: + Stream_Free(s, TRUE); + return error; +} + +/** + * 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_single_packet_new( + RDPGFX_CMDID_MAPSURFACETOOUTPUT, 12); + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_single_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_single_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_single_packet_new( + RDPGFX_CMDID_MAPSURFACETOWINDOW, 18); + + if (!s) + { + WLog_ERR(TAG, "rdpgfx_server_single_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_single_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; + + if (Stream_GetRemainingLength(s) < 12) + { + WLog_ERR(TAG, "not enough data!"); + return ERROR_INVALID_DATA; + } + + Stream_Read_UINT32(s, pdu.queueDepth); /* queueDepth (4 bytes) */ + Stream_Read_UINT32(s, pdu.frameId); /* frameId (4 bytes) */ + /* totalFramesDecoded (4 bytes) */ + Stream_Read_UINT32(s, pdu.totalFramesDecoded); + + 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; + + if (Stream_GetRemainingLength(s) < 2) + { + WLog_ERR(TAG, "not enough data!"); + return ERROR_INVALID_DATA; + } + + /* cacheEntriesCount (2 bytes) */ + Stream_Read_UINT16(s, pdu.cacheEntriesCount); + + if (pdu.cacheEntriesCount <= 0) + { + /* According to the latest spec, capsSetCount <= 3 */ + WLog_ERR(TAG, "Invalid cacheEntriesCount: %u", pdu.cacheEntriesCount); + return ERROR_INVALID_DATA; + } + + if (Stream_GetRemainingLength(s) < (pdu.cacheEntriesCount * 12)) + { + WLog_ERR(TAG, "not enough data!"); + return ERROR_INVALID_DATA; + } + + 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) */ + /* bitmapLength (4 bytes) */ + Stream_Read_UINT32(s, cacheEntries->bitmapLength); + } + + 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; + UINT32 capsDataLength; + + if (Stream_GetRemainingLength(s) < 2) + { + WLog_ERR(TAG, "not enough data!"); + return ERROR_INVALID_DATA; + } + + Stream_Read_UINT16(s, pdu.capsSetCount); /* capsSetCount (2 bytes) */ + + if (pdu.capsSetCount > 3) + { + /* According to the latest spec, capsSetCount <= 3 */ + WLog_ERR(TAG, "capsSetCount is greater than 3: %u", pdu.capsSetCount); + return ERROR_INVALID_DATA; + } + + if (Stream_GetRemainingLength(s) < (pdu.capsSetCount * 12)) + { + WLog_ERR(TAG, "not enough data!"); + return ERROR_INVALID_DATA; + } + + 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_Read_UINT32(s, capsDataLength); /* capsDataLength (4 bytes) */ + + if (capsDataLength != 4) + { + WLog_ERR(TAG, "capsDataLength does not equal to 4: %lu", + capsDataLength); + return ERROR_INVALID_DATA; + } + + 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; + + if (Stream_GetRemainingLength(s) < 12) + { + WLog_ERR(TAG, "not enough data!"); + return ERROR_INVALID_DATA; + } + + 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; + DWORD status; + DWORD nCount; + void* buffer; + HANDLE events[8]; + DWORD BytesReturned = 0; + UINT error = CHANNEL_RC_OK; + buffer = NULL; + BytesReturned = 0; + nCount = 0; + events[nCount++] = priv->stopEvent; + events[nCount++] = priv->channelEvent; + + /* Main virtual channel loop. RDPGFX do not need version negotiation */ + while (TRUE) + { + status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE); + + if (status == WAIT_FAILED) + { + error = GetLastError(); + WLog_ERR(TAG, "WaitForMultipleObjects failed with error %lu", error); + break; + } + + /* Stop Event */ + if (status == WAIT_OBJECT_0) + break; + + if ((error = rdpgfx_server_handle_messages(context))) + { + WLog_ERR(TAG, "rdpgfx_server_handle_messages failed with error %lu", + error); + break; + } + } + + 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; + void* buffer = NULL; + + if (!priv->isOpened) + { + PULONG pSessionId = NULL; + DWORD BytesReturned = 0; + priv->SessionId = WTS_CURRENT_SESSION; + + if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, + WTSSessionId, (LPSTR*) &pSessionId, + &BytesReturned) == FALSE) + { + WLog_ERR(TAG, "WTSQuerySessionInformationA failed!"); + return FALSE; + } + + 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; + } + + /* Query for channel event handle */ + if (!WTSVirtualChannelQuery(priv->rdpgfx_channel, WTSVirtualEventHandle, + &buffer, &BytesReturned) + || (BytesReturned != sizeof(HANDLE))) + { + WLog_ERR(TAG, "WTSVirtualChannelQuery failed " + "or invalid returned size(%d)", + BytesReturned); + + if (buffer) + WTSFreeMemory(buffer); + + goto out_close; + } + + CopyMemory(&priv->channelEvent, buffer, sizeof(HANDLE)); + WTSFreeMemory(buffer); + + if (!(priv->zgfx = zgfx_context_new(TRUE))) + { + WLog_ERR(TAG, "Create zgfx context failed!"); + goto out_close; + } + + if (priv->ownThread) + { + if (!(priv->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) + { + WLog_ERR(TAG, "CreateEvent failed!"); + goto out_zgfx; + } + + if (!(priv->thread = CreateThread(NULL, 0, + (LPTHREAD_START_ROUTINE) + rdpgfx_server_thread_func, + (void*) context, 0, NULL))) + { + WLog_ERR(TAG, "CreateThread failed!"); + goto out_stopEvent; + } + } + + priv->isOpened = TRUE; + priv->isReady = FALSE; + return TRUE; + } + + WLog_ERR(TAG, "RDPGFX channel is already opened!"); + return FALSE; +out_stopEvent: + CloseHandle(priv->stopEvent); + priv->stopEvent = NULL; +out_zgfx: + zgfx_context_free(priv->zgfx); + priv->zgfx = NULL; +out_close: + WTSVirtualChannelClose(priv->rdpgfx_channel); + priv->rdpgfx_channel = NULL; + priv->channelEvent = NULL; + return FALSE; +} + +static BOOL rdpgfx_server_close(RdpgfxServerContext* context) +{ + RdpgfxServerPrivate* priv = (RdpgfxServerPrivate*) context->priv; + + if (priv->ownThread && 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; + } + + priv->channelEvent = NULL; + priv->isOpened = FALSE; + priv->isReady = FALSE; + 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->SurfaceFrameCommand = rdpgfx_send_surface_frame_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; + } + + /* Create shared input stream */ + priv->input_stream = Stream_New(NULL, 4); + + if (!priv->input_stream) + { + WLog_ERR(TAG, "Stream_New failed!"); + goto out_free_priv; + } + + priv->isOpened = FALSE; + priv->isReady = FALSE; + priv->ownThread = TRUE; + return (RdpgfxServerContext*) context; +out_free_priv: + free(context->priv); +out_free: + free(context); + return NULL; +} + +void rdpgfx_server_context_free(RdpgfxServerContext* context) +{ + rdpgfx_server_close(context); + + if (context->priv) + Stream_Free(context->priv->input_stream, TRUE); + + free(context->priv); + free(context); +} + +FREERDP_API HANDLE rdpgfx_server_get_event_handle(RdpgfxServerContext* context) +{ + return context->priv->channelEvent; +} + +/* + * Handle rpdgfx messages - server side + * + * @param Server side context + * + * @return 0 on success + * ERROR_NO_DATA if no data could be read this time + * otherwise a Win32 error code + */ +UINT rdpgfx_server_handle_messages(RdpgfxServerContext* context) +{ + DWORD BytesReturned; + void* buffer; + UINT ret = CHANNEL_RC_OK; + RdpgfxServerPrivate* priv = context->priv; + wStream* s = priv->input_stream; + + /* Check whether the dynamic channel is ready */ + if (!priv->isReady) + { + if (WTSVirtualChannelQuery(priv->rdpgfx_channel, + WTSVirtualChannelReady, + &buffer, &BytesReturned) == FALSE) + { + if (GetLastError() == ERROR_NO_DATA) + return ERROR_NO_DATA; + + WLog_ERR(TAG, "WTSVirtualChannelQuery failed"); + return ERROR_INTERNAL_ERROR; + } + + priv->isReady = *((BOOL*) buffer); + WTSFreeMemory(buffer); + } + + /* Consume channel event only after the gfx dynamic channel is ready */ + if (priv->isReady) + { + Stream_SetPosition(s, 0); + + if (!WTSVirtualChannelRead(priv->rdpgfx_channel, + 0, NULL, 0, &BytesReturned)) + { + if (GetLastError() == ERROR_NO_DATA) + return ERROR_NO_DATA; + + WLog_ERR(TAG, "WTSVirtualChannelRead failed!"); + return ERROR_INTERNAL_ERROR; + } + + if (BytesReturned < 1) + return CHANNEL_RC_OK; + + if (!Stream_EnsureRemainingCapacity(s, BytesReturned)) + { + WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + if (WTSVirtualChannelRead(priv->rdpgfx_channel, 0, + (PCHAR) Stream_Buffer(s), + Stream_Capacity(s), &BytesReturned) == FALSE) + { + WLog_ERR(TAG, "WTSVirtualChannelRead failed!"); + return ERROR_INTERNAL_ERROR; + } + + Stream_SetLength(s, BytesReturned); + Stream_SetPosition(s, 0); + + while (((size_t) Stream_GetPosition(s)) < Stream_Length(s)) + { + if ((ret = rdpgfx_server_receive_pdu(context, s))) + { + WLog_ERR(TAG, "rdpgfx_server_receive_pdu " + "failed with error %lu!", ret); + return ret; + } + } + } + + return ret; +} diff --git a/channels/rdpgfx/server/rdpgfx_main.h b/channels/rdpgfx/server/rdpgfx_main.h new file mode 100644 index 000000000..be29b7638 --- /dev/null +++ b/channels/rdpgfx/server/rdpgfx_main.h @@ -0,0 +1,40 @@ +/** + * 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; + BOOL ownThread; + HANDLE thread; + HANDLE stopEvent; + HANDLE channelEvent; + void* rdpgfx_channel; + DWORD SessionId; + wStream* input_stream; + BOOL isOpened; + BOOL isReady; +}; + +#endif /* FREERDP_CHANNEL_RDPGFX_SERVER_MAIN_H */ diff --git a/channels/server/channels.c b/channels/server/channels.c index 84ccf6e15..9f99ebbda 100644 --- a/channels/server/channels.c +++ b/channels/server/channels.c @@ -49,6 +49,7 @@ #include #include #include +#include void freerdp_channels_dummy() { @@ -79,6 +80,8 @@ void freerdp_channels_dummy() encomsp_server_context_new(NULL); encomsp_server_context_free(NULL); + rdpgfx_server_context_new(NULL); + rdpgfx_server_context_free(NULL); } /** diff --git a/include/freerdp/channels/rdpgfx.h b/include/freerdp/channels/rdpgfx.h index e19714916..972a08208 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 @@ -142,6 +143,8 @@ typedef struct _RDPGFX_CAPSET_VERSION10 RDPGFX_CAPSET_VERSION10; #define RDPGFX_CODECID_ALPHA 0x000C #define RDPGFX_CODECID_AVC444 0x000E +#define RDPGFX_WIRE_TO_SURFACE_PDU_1_SIZE 17 + struct _RDPGFX_WIRE_TO_SURFACE_PDU_1 { UINT16 surfaceId; @@ -156,6 +159,8 @@ typedef struct _RDPGFX_WIRE_TO_SURFACE_PDU_1 RDPGFX_WIRE_TO_SURFACE_PDU_1; #define RDPGFX_CODECID_CAPROGRESSIVE 0x0009 #define RDPGFX_CODECID_CAPROGRESSIVE_V2 0x000D +#define RDPGFX_WIRE_TO_SURFACE_PDU_2_SIZE 13 + struct _RDPGFX_WIRE_TO_SURFACE_PDU_2 { UINT16 surfaceId; @@ -250,6 +255,8 @@ struct _RDPGFX_DELETE_SURFACE_PDU }; typedef struct _RDPGFX_DELETE_SURFACE_PDU RDPGFX_DELETE_SURFACE_PDU; +#define RDPGFX_START_FRAME_PDU_SIZE 8 + struct _RDPGFX_START_FRAME_PDU { UINT32 timestamp; @@ -257,6 +264,8 @@ struct _RDPGFX_START_FRAME_PDU }; typedef struct _RDPGFX_START_FRAME_PDU RDPGFX_START_FRAME_PDU; +#define RDPGFX_END_FRAME_PDU_SIZE 4 + struct _RDPGFX_END_FRAME_PDU { UINT32 frameId; @@ -372,6 +381,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/channels/wtsvc.h b/include/freerdp/channels/wtsvc.h index 66ee9eb24..ac3bd8410 100644 --- a/include/freerdp/channels/wtsvc.h +++ b/include/freerdp/channels/wtsvc.h @@ -43,6 +43,13 @@ extern "C" { #endif +enum +{ + DRDYNVC_STATE_NONE = 0, + DRDYNVC_STATE_INITIALIZED = 1, + DRDYNVC_STATE_READY = 2 +}; + /** * WTSVirtualChannelManager functions are FreeRDP extensions to the API. */ @@ -51,6 +58,7 @@ FREERDP_API void WTSVirtualChannelManagerGetFileDescriptor(HANDLE hServer, void* FREERDP_API BOOL WTSVirtualChannelManagerCheckFileDescriptor(HANDLE hServer); FREERDP_API HANDLE WTSVirtualChannelManagerGetEventHandle(HANDLE hServer); FREERDP_API BOOL WTSVirtualChannelManagerIsChannelJoined(HANDLE hServer, const char* name); +FREERDP_API BYTE WTSVirtualChannelManagerGetDrdynvcState(HANDLE hServer); /** * Extended FreeRDP WTS functions for channel handling diff --git a/include/freerdp/codec/zgfx.h b/include/freerdp/codec/zgfx.h index 6fd35825a..b5c65319c 100644 --- a/include/freerdp/codec/zgfx.h +++ b/include/freerdp/codec/zgfx.h @@ -28,12 +28,16 @@ #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; - BYTE* pbInputCurrent; - BYTE* pbInputEnd; + const BYTE* pbInputCurrent; + const BYTE* pbInputEnd; UINT32 bits; UINT32 cBitsRemaining; @@ -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_decompress(ZGFX_CONTEXT* zgfx, const BYTE* pSrcData, UINT32 SrcSize, BYTE** ppDstData, UINT32* pDstSize, UINT32 flags); +FREERDP_API int zgfx_compress(ZGFX_CONTEXT* zgfx, const BYTE* pSrcData, UINT32 SrcSize, BYTE** ppDstData, UINT32* pDstSize, UINT32* pFlags); +FREERDP_API int zgfx_compress_to_stream(ZGFX_CONTEXT* zgfx, wStream* sDst, const 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 000000000..fad778935 --- /dev/null +++ b/include/freerdp/server/rdpgfx.h @@ -0,0 +1,100 @@ +/** + * 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(*psRdpgfxResetGraphics)(RdpgfxServerContext* context, RDPGFX_RESET_GRAPHICS_PDU* resetGraphics); +typedef UINT(*psRdpgfxStartFrame)(RdpgfxServerContext* context, RDPGFX_START_FRAME_PDU* startFrame); +typedef UINT(*psRdpgfxEndFrame)(RdpgfxServerContext* context, RDPGFX_END_FRAME_PDU* endFrame); +typedef UINT(*psRdpgfxSurfaceCommand)(RdpgfxServerContext* context, RDPGFX_SURFACE_COMMAND* cmd); +typedef UINT(*psRdpgfxSurfaceFrameCommand)(RdpgfxServerContext* context, RDPGFX_SURFACE_COMMAND* cmd, RDPGFX_START_FRAME_PDU* startFrame, RDPGFX_END_FRAME_PDU* endFrame); +typedef UINT(*psRdpgfxDeleteEncodingContext)(RdpgfxServerContext* context, RDPGFX_DELETE_ENCODING_CONTEXT_PDU* deleteEncodingContext); +typedef UINT(*psRdpgfxCreateSurface)(RdpgfxServerContext* context, RDPGFX_CREATE_SURFACE_PDU* createSurface); +typedef UINT(*psRdpgfxDeleteSurface)(RdpgfxServerContext* context, RDPGFX_DELETE_SURFACE_PDU* deleteSurface); +typedef UINT(*psRdpgfxSolidFill)(RdpgfxServerContext* context, RDPGFX_SOLID_FILL_PDU* solidFill); +typedef UINT(*psRdpgfxSurfaceToSurface)(RdpgfxServerContext* context, RDPGFX_SURFACE_TO_SURFACE_PDU* surfaceToSurface); +typedef UINT(*psRdpgfxSurfaceToCache)(RdpgfxServerContext* context, RDPGFX_SURFACE_TO_CACHE_PDU* surfaceToCache); +typedef UINT(*psRdpgfxCacheToSurface)(RdpgfxServerContext* context, RDPGFX_CACHE_TO_SURFACE_PDU* cacheToSurface); +typedef UINT(*psRdpgfxCacheImportOffer)(RdpgfxServerContext* context, RDPGFX_CACHE_IMPORT_OFFER_PDU* cacheImportOffer); +typedef UINT(*psRdpgfxCacheImportReply)(RdpgfxServerContext* context, RDPGFX_CACHE_IMPORT_REPLY_PDU* cacheImportReply); +typedef UINT(*psRdpgfxEvictCacheEntry)(RdpgfxServerContext* context, RDPGFX_EVICT_CACHE_ENTRY_PDU* evictCacheEntry); +typedef UINT(*psRdpgfxMapSurfaceToOutput)(RdpgfxServerContext* context, RDPGFX_MAP_SURFACE_TO_OUTPUT_PDU* surfaceToOutput); +typedef UINT(*psRdpgfxMapSurfaceToWindow)(RdpgfxServerContext* context, RDPGFX_MAP_SURFACE_TO_WINDOW_PDU* surfaceToWindow); +typedef UINT(*psRdpgfxCapsAdvertise)(RdpgfxServerContext* context, RDPGFX_CAPS_ADVERTISE_PDU* capsAdvertise); +typedef UINT(*psRdpgfxCapsConfirm)(RdpgfxServerContext* context, RDPGFX_CAPS_CONFIRM_PDU* capsConfirm); +typedef UINT(*psRdpgfxFrameAcknowledge)(RdpgfxServerContext* context, RDPGFX_FRAME_ACKNOWLEDGE_PDU* frameAcknowledge); +typedef UINT(*psRdpgfxQoeFrameAcknowledge)(RdpgfxServerContext* context, RDPGFX_QOE_FRAME_ACKNOWLEDGE_PDU* qoeFrameAcknowledge); + +struct _rdpgfx_server_context +{ + HANDLE vcm; + void* custom; + + psRdpgfxServerOpen Open; + psRdpgfxServerClose Close; + + psRdpgfxResetGraphics ResetGraphics; + psRdpgfxStartFrame StartFrame; + psRdpgfxEndFrame EndFrame; + psRdpgfxSurfaceCommand SurfaceCommand; + psRdpgfxSurfaceFrameCommand SurfaceFrameCommand; + psRdpgfxDeleteEncodingContext DeleteEncodingContext; + psRdpgfxCreateSurface CreateSurface; + psRdpgfxDeleteSurface DeleteSurface; + psRdpgfxSolidFill SolidFill; + psRdpgfxSurfaceToSurface SurfaceToSurface; + psRdpgfxSurfaceToCache SurfaceToCache; + psRdpgfxCacheToSurface CacheToSurface; + psRdpgfxCacheImportOffer CacheImportOffer; + psRdpgfxCacheImportReply CacheImportReply; + psRdpgfxEvictCacheEntry EvictCacheEntry; + psRdpgfxMapSurfaceToOutput MapSurfaceToOutput; + psRdpgfxMapSurfaceToWindow MapSurfaceToWindow; + psRdpgfxCapsAdvertise CapsAdvertise; + psRdpgfxCapsConfirm CapsConfirm; + psRdpgfxFrameAcknowledge FrameAcknowledge; + psRdpgfxQoeFrameAcknowledge 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); +FREERDP_API HANDLE rdpgfx_server_get_event_handle(RdpgfxServerContext* context); +FREERDP_API UINT rdpgfx_server_handle_messages(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 60bb87d79..20c611537 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 @@ -124,6 +127,14 @@ struct rdp_shadow_server BOOL authentication; int selectedMonitor; RECTANGLE_16 subRect; + + /* Codec settings */ + RLGR_MODE rfxMode; + H264_RATECONTROL_MODE h264RateControlMode; + UINT32 h264BitRate; + FLOAT h264FrameRate; + UINT32 h264QP; + char* ipcSocket; char* ConfigPath; char* CertificateFile; @@ -169,9 +180,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 +215,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 7284424e5..ec71287c6 100644 --- a/libfreerdp/codec/test/TestFreeRDPCodecZGfx.c +++ b/libfreerdp/codec/test/TestFreeRDPCodecZGfx.c @@ -1,10 +1,217 @@ #include #include +#include +#include #include +#include -int TestFreeRDPCodecZGfx(int argc, char* argv[]) +/* 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 604947988..e5c6c3cfe 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: * @@ -105,7 +108,7 @@ static const ZGFX_TOKEN ZGFX_TOKEN_TABLE[] = _zgfx->bits = _zgfx->BitsCurrent >> _zgfx->cBitsCurrent; \ _zgfx->BitsCurrent &= ((1 << _zgfx->cBitsCurrent) - 1); -void zgfx_history_buffer_ring_write(ZGFX_CONTEXT* zgfx, BYTE* src, UINT32 count) +void zgfx_history_buffer_ring_write(ZGFX_CONTEXT* zgfx, const BYTE* src, UINT32 count) { UINT32 front; UINT32 residue; @@ -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, const BYTE* pbSegment, UINT32 cbSegment) { BYTE c; BYTE flags; @@ -322,7 +325,7 @@ int zgfx_decompress_segment(ZGFX_CONTEXT* zgfx, BYTE* pbSegment, UINT32 cbSegmen return 1; } -int zgfx_decompress(ZGFX_CONTEXT* zgfx, BYTE* pSrcData, UINT32 SrcSize, BYTE** ppDstData, UINT32* pDstSize, UINT32 flags) +int zgfx_decompress(ZGFX_CONTEXT* zgfx, const BYTE* pSrcData, UINT32 SrcSize, BYTE** ppDstData, UINT32* pDstSize, UINT32 flags) { int status; BYTE descriptor; @@ -383,11 +386,120 @@ 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, const 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, const BYTE* pUncompressed, UINT32 uncompressedSize, UINT32* pFlags) +{ + int fragment; + UINT16 maxLength; + UINT32 totalLength; + size_t posSegmentCount = 0; + const BYTE* pSrcData; + int status = 0; + + maxLength = ZGFX_SEGMENTED_MAXSIZE; + + totalLength = uncompressedSize; + pSrcData = pUncompressed; + for (fragment = 0; (totalLength > 0) || (fragment == 0); fragment++) + { + UINT32 SrcSize; + size_t posDstSize; + size_t posDataStart; + UINT32 DstSize; + + SrcSize = (totalLength > maxLength) ? maxLength : totalLength; + posDstSize = 0; + totalLength -= SrcSize; + + /* Ensure we have enough space for headers */ + if (!Stream_EnsureRemainingCapacity(sDst, 12)) + { + WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!"); + return -1; + } + + if (fragment == 0) + { + /* First fragment */ + + /* descriptor (1 byte) */ + Stream_Write_UINT8(sDst, (totalLength == 0) ? + ZGFX_SEGMENTED_SINGLE : ZGFX_SEGMENTED_MULTIPART); + if (totalLength > 0) + { + posSegmentCount = Stream_GetPosition(sDst); /* segmentCount (2 bytes) */ + Stream_Seek(sDst, 2); + Stream_Write_UINT32(sDst, uncompressedSize); /* uncompressedSize (4 bytes) */ + } + } + + if (fragment > 0 || totalLength > 0) + { + /* Multipart */ + posDstSize = Stream_GetPosition(sDst); /* size (4 bytes) */ + Stream_Seek(sDst, 4); + } + + posDataStart = Stream_GetPosition(sDst); + if ((status = zgfx_compress_segment(zgfx, sDst, pSrcData, SrcSize, pFlags)) < 0) + { + return status; + } + + if (posDstSize) + { + /* Fill segment data size */ + DstSize = Stream_GetPosition(sDst) - posDataStart; + Stream_SetPosition(sDst, posDstSize); + Stream_Write_UINT32(sDst, DstSize); + Stream_SetPosition(sDst, posDataStart + DstSize); + } + + pSrcData += SrcSize; + } + + Stream_SealLength(sDst); + + /* fill back segmentCount */ + if (posSegmentCount) + { + Stream_SetPosition(sDst, posSegmentCount); + Stream_Write_UINT16(sDst, fragment); + Stream_SetPosition(sDst, Stream_Length(sDst)); + } + + return status; +} + +int zgfx_compress(ZGFX_CONTEXT* zgfx, const 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/libfreerdp/core/server.c b/libfreerdp/core/server.c index 625585e94..fb1a740fd 100644 --- a/libfreerdp/core/server.c +++ b/libfreerdp/core/server.c @@ -557,6 +557,13 @@ BOOL WTSVirtualChannelManagerIsChannelJoined(HANDLE hServer, const char* name) return wts_get_joined_channel_by_name(vcm->rdp->mcs, name) == NULL ? FALSE : TRUE; } +BYTE WTSVirtualChannelManagerGetDrdynvcState(HANDLE hServer) +{ + WTSVirtualChannelManager* vcm = (WTSVirtualChannelManager*) hServer; + + return vcm->drdynvc_state; +} + UINT16 WTSChannelGetId(freerdp_peer* client, const char* channel_name) { rdpMcsChannel* channel; @@ -1205,6 +1212,7 @@ BOOL WINAPI FreeRDP_WTSVirtualChannelWrite(HANDLE hChannelHandle, PCHAR Buffer, BYTE* buffer; UINT32 length; UINT32 written; + UINT32 totalWritten = 0; rdpPeerChannel* channel = (rdpPeerChannel*) hChannelHandle; BOOL ret = TRUE; @@ -1222,6 +1230,8 @@ BOOL WINAPI FreeRDP_WTSVirtualChannelWrite(HANDLE hChannelHandle, PCHAR Buffer, } CopyMemory(buffer, Buffer, length); + totalWritten = Length; + ret = wts_queue_send_item(channel, buffer, length); } else if (!channel->vcm->drdynvc_channel || (channel->vcm->drdynvc_state != DRDYNVC_STATE_READY)) @@ -1270,13 +1280,14 @@ BOOL WINAPI FreeRDP_WTSVirtualChannelWrite(HANDLE hChannelHandle, PCHAR Buffer, Length -= written; Buffer += written; + totalWritten += written; ret = wts_queue_send_item(channel->vcm->drdynvc_channel, buffer, length); } } if (pBytesWritten) - *pBytesWritten = Length; + *pBytesWritten = totalWritten; return ret; } diff --git a/libfreerdp/core/server.h b/libfreerdp/core/server.h index aa165557e..35a9883c6 100644 --- a/libfreerdp/core/server.h +++ b/libfreerdp/core/server.h @@ -47,13 +47,6 @@ enum RDP_PEER_CHANNEL_TYPE_DVC = 1 }; -enum -{ - DRDYNVC_STATE_NONE = 0, - DRDYNVC_STATE_INITIALIZED = 1, - DRDYNVC_STATE_READY = 2 -}; - enum { DVC_OPEN_STATE_NONE = 0, diff --git a/server/shadow/CMakeLists.txt b/server/shadow/CMakeLists.txt index 55597dbf8..8e4e8e119 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 10508c9fe..0f6c23a71 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; @@ -316,9 +317,6 @@ void (^mac_capture_stream_handler)(CGDisplayStreamFrameStatus, uint64_t, IOSurfa if (count < 1) return; - if ((count == 1) && subsystem->suppressOutput) - return; - mac_shadow_capture_get_dirty_region(subsystem); surfaceRect.left = 0; @@ -326,11 +324,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; @@ -359,7 +357,9 @@ void (^mac_capture_stream_handler)(CGDisplayStreamFrameStatus, uint64_t, IOSurfa count = ArrayList_Count(server->clients); + EnterCriticalSection(&(surface->lock)); shadow_subsystem_frame_update((rdpShadowSubsystem *)subsystem); + LeaveCriticalSection(&(surface->lock)); if (count == 1) { @@ -375,7 +375,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) @@ -444,48 +444,15 @@ int mac_shadow_screen_grab(macShadowSubsystem* subsystem) int mac_shadow_subsystem_process_message(macShadowSubsystem* subsystem, wMessage* message) { + rdpShadowServer* server = subsystem->server; + rdpShadowSurface* surface = server->surface; 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: + EnterCriticalSection(&(surface->lock)); + shadow_subsystem_frame_update((rdpShadowSubsystem *)subsystem); + LeaveCriticalSection(&(surface->lock)); 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 bd2f5d62b..50a1881d9 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 231996ed7..0bfc9e680 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 0f23b9c05..4cc86cc3a 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 8c5e159ec..0d0f624d4 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); - } + 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/shadow_channels.c b/server/shadow/shadow_channels.c index bc90ab494..983b16f1d 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 edba39cb7..e35b6f6ee 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 8d433e216..e978d5bbd 100644 --- a/server/shadow/shadow_client.c +++ b/server/shadow/shadow_client.c @@ -34,7 +34,88 @@ #define TAG CLIENT_TAG("shadow") -static void shadow_client_free_queued_message(void *obj) +struct _SHADOW_GFX_STATUS +{ + BOOL gfxOpened; + BOOL gfxSurfaceCreated; +}; +typedef struct _SHADOW_GFX_STATUS SHADOW_GFX_STATUS; + +static INLINE BOOL shadow_client_rdpgfx_new_surface(rdpShadowClient *client) +{ + UINT error = CHANNEL_RC_OK; + 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; + + IFCALLRET(context->CreateSurface, error, context, &createSurface); + if (error) + { + WLog_ERR(TAG, "CreateSurface failed with error %lu", error); + return FALSE; + } + + IFCALLRET(context->MapSurfaceToOutput, error, context, &surfaceToOutput); + if (error) + { + WLog_ERR(TAG, "MapSurfaceToOutput failed with error %lu", error); + return FALSE; + } + + return TRUE; +} + +static INLINE BOOL shadow_client_rdpgfx_release_surface(rdpShadowClient *client) +{ + UINT error = CHANNEL_RC_OK; + RDPGFX_DELETE_SURFACE_PDU pdu; + RdpgfxServerContext* context = client->rdpgfx; + + pdu.surfaceId = 0; + IFCALLRET(context->DeleteSurface, error, context, &pdu); + if (error) + { + WLog_ERR(TAG, "DeleteSurface failed with error %lu", error); + return FALSE; + } + + return TRUE; +} + +static INLINE BOOL shadow_client_rdpgfx_reset_graphic(rdpShadowClient *client) +{ + UINT error = CHANNEL_RC_OK; + 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; + + IFCALLRET(context->ResetGraphics, error, context, &pdu); + if (error) + { + WLog_ERR(TAG, "ResetGraphics failed with error %lu", error); + return FALSE; + } + + return TRUE; +} + +static INLINE void shadow_client_free_queued_message(void *obj) { wMessage *message = (wMessage*)obj; if (message->Free) @@ -44,7 +125,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 +143,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 +212,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 +251,94 @@ void shadow_client_message_free(wMessage* message) } } -BOOL shadow_client_capabilities(freerdp_peer* peer) +static INLINE void shadow_client_mark_invalid(rdpShadowClient* client, int numRects, const RECTANGLE_16* rects) +{ + int index; + RECTANGLE_16 screenRegion; + rdpSettings* settings = ((rdpContext*) client)->settings; + + EnterCriticalSection(&(client->lock)); + + /* Mark client invalid region. No rectangle means full screen */ + if (numRects > 0) + { + for (index = 0; index < numRects; index++) + { + region16_union_rect(&(client->invalidRegion), &(client->invalidRegion), &rects[index]); + } + } + 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)); +} + +/** + * Function description + * Recalculate client desktop size and update to rdpSettings + * + * @return TRUE if width/height changed. + */ +static INLINE BOOL shadow_client_recalc_desktop_size(rdpShadowClient* client) +{ + 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); + } + + 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; + BOOL ret = TRUE; client = (rdpShadowClient*) peer->context; subsystem = client->server->subsystem; - if (subsystem->ClientCapabilities) - { - return subsystem->ClientCapabilities(subsystem, client); - } - return TRUE; + IFCALLRET(subsystem->ClientCapabilities, ret, subsystem, client); + if (!ret) + WLog_WARN(TAG, "subsystem->ClientCapabilities failed"); + + /* Recalculate desktop size regardless whether previous call fail + * or not. Make sure we send correct width/height to client */ + (void)shadow_client_recalc_desktop_size(client); + + return ret; } -static INLINE void shadow_client_calc_desktop_size(rdpShadowServer* server, int* pWidth, int* pHeight) -{ - RECTANGLE_16 viewport = {0, 0, server->screen->width, server->screen->height}; - - if (server->shareSubRect) - { - rectangles_intersection(&viewport, &(server->subRect), &viewport); - } - - (*pWidth) = viewport.right - viewport.left; - (*pHeight) = viewport.bottom - viewport.top; -} - -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 +346,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; @@ -270,8 +399,8 @@ 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) +static INLINE void shadow_client_convert_rects(rdpShadowClient* client, + RECTANGLE_16* dst, const RECTANGLE_16* src, UINT32 numRects) { if (client->server->shareSubRect) { @@ -289,71 +418,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; + message.id = SHADOW_MSG_IN_REFRESH_REQUEST_ID; + message.wParam = NULL; + message.lParam = NULL; + message.context = (void*) client; + message.Free = NULL; + + return MessageQueue_Dispatch(MsgPipe->In, &message); +} + +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 = (SHADOW_MSG_IN_REFRESH_OUTPUT*) calloc(1, sizeof(SHADOW_MSG_IN_REFRESH_OUTPUT)))) - return FALSE; - - wParam->numRects = (UINT32) count; - - 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); + + shadow_client_mark_invalid(client, count, rects); + + free(rects); + } + else + { + shadow_client_mark_invalid(client, 0, NULL); } - 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; - - return MessageQueue_Dispatch(MsgPipe->In, &message); + 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; + RECTANGLE_16 region; - wParam = (SHADOW_MSG_IN_SUPPRESS_OUTPUT*) calloc(1, sizeof(SHADOW_MSG_IN_SUPPRESS_OUTPUT)); - if (!wParam) - return FALSE; - - 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; @@ -373,8 +510,13 @@ BOOL shadow_client_activate(freerdp_peer* peer) client->activated = TRUE; client->inLobby = client->mayView ? FALSE : TRUE; - shadow_encoder_reset(client->encoder); + if (shadow_encoder_reset(client->encoder) < 0) + { + WLog_ERR(TAG, "Failed to reset encoder"); + return FALSE; + } + /* Update full screen in next update */ return shadow_client_refresh_rect(client, 0, NULL); } @@ -439,43 +581,135 @@ 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 INLINE void shadow_client_common_frame_acknowledge(rdpShadowClient* client, UINT32 frameId) { /* - * Record the last client acknowledged frame id to + * Record the last client acknowledged frame id to * calculate how much frames are in progress. - * Some rdp clients (win7 mstsc) skips frame ACK if it is - * inactive, we should not expect ACK for each frame. + * Some rdp clients (win7 mstsc) skips frame ACK if it is + * inactive, we should not expect ACK for each frame. * So it is OK to calculate inflight frame count according to * a latest acknowledged frame id. */ client->encoder->lastAckframeId = frameId; +} + +static BOOL shadow_client_surface_frame_acknowledge(rdpShadowClient* client, UINT32 frameId) +{ + shadow_client_common_frame_acknowledge(client, frameId); + return TRUE; +} + +static UINT shadow_client_rdpgfx_frame_acknowledge(RdpgfxServerContext* context, RDPGFX_FRAME_ACKNOWLEDGE_PDU* frameAcknowledge) +{ + shadow_client_common_frame_acknowledge((rdpShadowClient *)context->custom, + frameAcknowledge->frameId); + return CHANNEL_RC_OK; +} +static UINT shadow_client_rdpgfx_qoe_frame_acknowledge(RdpgfxServerContext* context, RDPGFX_QOE_FRAME_ACKNOWLEDGE_PDU* qoeFrameAcknowledge) +{ + shadow_client_common_frame_acknowledge((rdpShadowClient *)context->custom, + qoeFrameAcknowledge->frameId); + return CHANNEL_RC_OK; +} + +/** + * Function description + * + * @return TRUE on success + */ +static BOOL shadow_client_send_surface_gfx(rdpShadowClient* client, + BYTE* pSrcData, int nSrcStep, int nXSrc, int nYSrc, int nWidth, int nHeight) +{ + UINT error = CHANNEL_RC_OK; + 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; + + 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; + + if (shadow_encoder_prepare(encoder, FREERDP_CODEC_AVC420) < 0) + { + WLog_ERR(TAG, "Failed to prepare encoder FREERDP_CODEC_AVC420"); + return FALSE; + } + + 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; + + IFCALLRET(client->rdpgfx->SurfaceFrameCommand, error, client->rdpgfx, &cmd, &cmdstart, &cmdend); + if (error) + { + WLog_ERR(TAG, "SurfaceFrameCommand failed with error %lu", error); + return FALSE; + } + } return TRUE; } -int shadow_client_send_surface_frame_marker(rdpShadowClient* client, UINT32 action, UINT32 id) -{ - SURFACE_FRAME_MARKER surfaceFrameMarker; - rdpContext* context = (rdpContext*) client; - rdpUpdate* update = context->update; - - surfaceFrameMarker.frameAction = action; - surfaceFrameMarker.frameId = id; - - IFCALL(update->SurfaceFrameMarker, context, &surfaceFrameMarker); - - return 1; -} - -int shadow_client_send_surface_bits(rdpShadowClient* client, rdpShadowSurface* surface, int nXSrc, int nYSrc, int nWidth, int nHeight) +/** + * Function description + * + * @return TRUE on success + */ +static BOOL shadow_client_send_surface_bits(rdpShadowClient* client, + BYTE* pSrcData, int nSrcStep, int nXSrc, int nYSrc, int nWidth, int nHeight) { + BOOL ret = TRUE; int i; BOOL first; BOOL last; wStream* s; - int nSrcStep; - BYTE* pSrcData; int numMessages; UINT32 frameId = 0; rdpUpdate* update; @@ -492,24 +726,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); @@ -519,7 +735,11 @@ int shadow_client_send_surface_bits(rdpShadowClient* client, rdpShadowSurface* s RFX_MESSAGE* messages; RFX_RECT *messageRects = NULL; - shadow_encoder_prepare(encoder, FREERDP_CODEC_REMOTEFX); + if (shadow_encoder_prepare(encoder, FREERDP_CODEC_REMOTEFX) < 0) + { + WLog_ERR(TAG, "Failed to prepare encoder FREERDP_CODEC_REMOTEFX"); + return FALSE; + } s = encoder->bs; @@ -532,7 +752,8 @@ int shadow_client_send_surface_bits(rdpShadowClient* client, rdpShadowSurface* s settings->DesktopWidth, settings->DesktopHeight, nSrcStep, &numMessages, settings->MultifragMaxRequestSize))) { - return 0; + WLog_ERR(TAG, "rfx_encode_messages failed"); + return FALSE; } cmd.codecID = settings->RemoteFxCodecId; @@ -559,6 +780,8 @@ int shadow_client_send_surface_bits(rdpShadowClient* client, rdpShadowSurface* s { rfx_message_free(encoder->rfx, &messages[i++]); } + WLog_ERR(TAG, "rfx_write_message failed"); + ret = FALSE; break; } rfx_message_free(encoder->rfx, &messages[i]); @@ -570,9 +793,15 @@ int shadow_client_send_surface_bits(rdpShadowClient* client, rdpShadowSurface* s last = ((i + 1) == numMessages) ? TRUE : FALSE; if (!encoder->frameAck) - IFCALL(update->SurfaceBits, update->context, &cmd); + IFCALLRET(update->SurfaceBits, ret, update->context, &cmd); else - IFCALL(update->SurfaceFrameBits, update->context, &cmd, first, last, frameId); + IFCALLRET(update->SurfaceFrameBits, ret, update->context, &cmd, first, last, frameId); + + if (!ret) + { + WLog_ERR(TAG, "Send surface bits(RemoteFxCodec) failed"); + break; + } } free(messageRects); @@ -580,7 +809,11 @@ int shadow_client_send_surface_bits(rdpShadowClient* client, rdpShadowSurface* s } else if (settings->NSCodec) { - shadow_encoder_prepare(encoder, FREERDP_CODEC_NSCODEC); + if (shadow_encoder_prepare(encoder, FREERDP_CODEC_NSCODEC) < 0) + { + WLog_ERR(TAG, "Failed to prepare encoder FREERDP_CODEC_NSCODEC"); + return FALSE; + } s = encoder->bs; Stream_SetPosition(s, 0); @@ -605,22 +838,32 @@ int shadow_client_send_surface_bits(rdpShadowClient* client, rdpShadowSurface* s last = TRUE; if (!encoder->frameAck) - IFCALL(update->SurfaceBits, update->context, &cmd); + IFCALLRET(update->SurfaceBits, ret, update->context, &cmd); else - IFCALL(update->SurfaceFrameBits, update->context, &cmd, first, last, frameId); + IFCALLRET(update->SurfaceFrameBits, ret, update->context, &cmd, first, last, frameId); + + if (!ret) + { + WLog_ERR(TAG, "Send surface bits(NSCodec) failed"); + } } - return 1; + return ret; } -int shadow_client_send_bitmap_update(rdpShadowClient* client, rdpShadowSurface* surface, int nXSrc, int nYSrc, int nWidth, int nHeight) +/** + * Function description + * + * @return TRUE on success + */ +static BOOL shadow_client_send_bitmap_update(rdpShadowClient* client, + BYTE* pSrcData, int nSrcStep, int nXSrc, int nYSrc, int nWidth, int nHeight) { + BOOL ret = TRUE; BYTE* data; BYTE* buffer; int yIdx, xIdx, k; int rows, cols; - int nSrcStep; - BYTE* pSrcData; UINT32 DstSize; UINT32 SrcFormat; BITMAP_DATA* bitmap; @@ -645,28 +888,23 @@ int shadow_client_send_bitmap_update(rdpShadowClient* client, rdpShadowSurface* maxUpdateSize = settings->MultifragMaxRequestSize; if (settings->ColorDepth < 32) - shadow_encoder_prepare(encoder, FREERDP_CODEC_INTERLEAVED); - 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 (shadow_encoder_prepare(encoder, FREERDP_CODEC_INTERLEAVED) < 0) + { + WLog_ERR(TAG, "Failed to prepare encoder FREERDP_CODEC_INTERLEAVED"); + return FALSE; + } } + else + { + if (shadow_encoder_prepare(encoder, FREERDP_CODEC_PLANAR) < 0) + { + WLog_ERR(TAG, "Failed to prepare encoder FREERDP_CODEC_PLANAR"); + return FALSE; + } + } + + SrcFormat = PIXEL_FORMAT_RGB32; if ((nXSrc % 4) != 0) { @@ -688,7 +926,7 @@ int shadow_client_send_bitmap_update(rdpShadowClient* client, rdpShadowSurface* bitmapUpdate.count = bitmapUpdate.number = rows * cols; if (!(bitmapData = (BITMAP_DATA*) malloc(sizeof(BITMAP_DATA) * bitmapUpdate.number))) - return -1; + return FALSE; bitmapUpdate.rectangles = bitmapData; if ((nWidth % 4) != 0) @@ -783,8 +1021,9 @@ int shadow_client_send_bitmap_update(rdpShadowClient* client, rdpShadowSurface* if (!fragBitmapData) { - free(bitmapData); - return -1; + WLog_ERR(TAG, "Failed to allocate memory for fragBitmapData"); + ret = FALSE; + goto out; } bitmapUpdate.rectangles = fragBitmapData; @@ -807,9 +1046,15 @@ int shadow_client_send_bitmap_update(rdpShadowClient* client, rdpShadowSurface* CopyMemory(&fragBitmapData[j++], &bitmapData[i++], sizeof(BITMAP_DATA)); updateSize = newUpdateSize; } - + bitmapUpdate.count = bitmapUpdate.number = j; - IFCALL(update->BitmapUpdate, context, &bitmapUpdate); + IFCALLRET(update->BitmapUpdate, ret, context, &bitmapUpdate); + if (!ret) + { + WLog_ERR(TAG, "BitmapUpdate failed"); + break; + } + updateSize = 1024; j = 0; } @@ -819,17 +1064,27 @@ int shadow_client_send_bitmap_update(rdpShadowClient* client, rdpShadowSurface* } else { - IFCALL(update->BitmapUpdate, context, &bitmapUpdate); + IFCALLRET(update->BitmapUpdate, ret, context, &bitmapUpdate); + if (!ret) + { + WLog_ERR(TAG, "BitmapUpdate failed"); + } } +out: free(bitmapData); - return 1; + return ret; } -int shadow_client_send_surface_update(rdpShadowClient* client) +/** + * Function description + * + * @return TRUE on success (or nothing need to be updated) + */ +static BOOL shadow_client_send_surface_update(rdpShadowClient* client, SHADOW_GFX_STATUS* pStatus) { - int status = -1; + BOOL ret = TRUE; int nXSrc, nYSrc; int nWidth, nHeight; rdpContext* context; @@ -840,6 +1095,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 +1116,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,55 +1136,162 @@ int shadow_client_send_surface_update(rdpShadowClient* client) if (region16_is_empty(&invalidRegion)) { - region16_uninit(&invalidRegion); - return 1; + /* No image region need to be updated. Success */ + goto out; } 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) { - status = shadow_client_send_surface_bits(client, surface, nXSrc, nYSrc, nWidth, nHeight); + /* 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 */ + if (!(ret = shadow_client_rdpgfx_reset_graphic(client))) + goto out; + + if (!(ret = shadow_client_rdpgfx_new_surface(client))) + goto out; + + pStatus->gfxSurfaceCreated = TRUE; + } + + ret = shadow_client_send_surface_gfx(client, pSrcData, nSrcStep, 0, 0, nWidth, nHeight); + } + else if (settings->RemoteFxCodec || settings->NSCodec) + { + ret = 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); + ret = shadow_client_send_bitmap_update(client, pSrcData, nSrcStep, nXSrc, nYSrc, nWidth, nHeight); } +out: region16_uninit(&invalidRegion); - return status; + return ret; } -int shadow_client_surface_update(rdpShadowClient* client, REGION16* region) +/** + * Function description + * Notify client for resize. The new desktop width/height + * should have already been updated in rdpSettings. + * + * @return TRUE on success + */ +static BOOL shadow_client_send_resize(rdpShadowClient* client, SHADOW_GFX_STATUS* pStatus) +{ + rdpContext* context; + rdpSettings* settings; + rdpShadowServer* server; + freerdp_peer* peer; + + server = client->server; + context = (rdpContext*) client; + peer = context->peer; + settings = context->settings; + + /** + * 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; + + /* Close Gfx surfaces */ + if (pStatus->gfxSurfaceCreated) + { + if (!shadow_client_rdpgfx_release_surface(client)) + return FALSE; + + pStatus->gfxSurfaceCreated = FALSE; + } + + /* Send Resize */ + if (!peer->update->DesktopResize(peer->update->context)) + { + WLog_ERR(TAG, "DesktopResize failed"); + return FALSE; + } + + /* 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); + + return TRUE; +} + +/** + * Function description + * Mark invalid region for client + * + * @return TRUE on success + */ +BOOL shadow_client_surface_update(rdpShadowClient* client, REGION16* region) { - int index; int numRects = 0; const RECTANGLE_16* rects; - EnterCriticalSection(&(client->lock)); - rects = region16_rects(region, &numRects); + shadow_client_mark_invalid(client, numRects, rects); - for (index = 0; index < numRects; index++) - { - region16_union_rect(&(client->invalidRegion), &(client->invalidRegion), &rects[index]); - } - - LeaveCriticalSection(&(client->lock)); - - return 1; + return TRUE; } -int shadow_client_subsystem_process_message(rdpShadowClient* client, wMessage* message) +/** + * Function description + * Only union invalid region from server surface + * + * @return TRUE on success + */ +static INLINE BOOL 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; + + return shadow_client_surface_update(client, &(surface->invalidRegion)); +} + +static int shadow_client_subsystem_process_message(rdpShadowClient* client, wMessage* message) { rdpContext* context = (rdpContext*) client; rdpUpdate* update = context->update; @@ -1013,7 +1386,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 +1407,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,54 +1458,51 @@ 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); + if (!shadow_client_send_resize(client, &gfxstatus)) + { + WLog_ERR(TAG, "Failed to send resize message"); + break; + } } - else + else { /* Send frame */ - rects = region16_rects(&(subsystem->invalidRegion), &numRects); - - for (index = 0; index < numRects; index++) + if (!shadow_client_send_surface_update(client, &gfxstatus)) { - region16_union_rect(&(client->invalidRegion), &(client->invalidRegion), &rects[index]); + WLog_ERR(TAG, "Failed to send surface update"); + break; } - - shadow_client_send_surface_update(client); + } + } + else + { + /* Our client don't receive graphic updates. Just save the invalid region */ + if (!shadow_client_no_surface_update(client, &gfxstatus)) + { + WLog_ERR(TAG, "Failed to handle surface update"); + break; } } - /* - * The return value of shadow_multiclient_consume is whether or not the subscriber really consumes the event. - * It's not cared currently. + /* + * The return value of shadow_multiclient_consume is whether or not + * the subscriber really consumes the event. It's not cared currently. */ (void)shadow_multiclient_consume(UpdateSubscriber); } @@ -1139,6 +1514,39 @@ 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 (WTSVirtualChannelManagerGetDrdynvcState(client->vcm) == 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 (WTSVirtualChannelManagerGetDrdynvcState(client->vcm) == DRDYNVC_STATE_READY) + { + /* Init RDPGFX dynamic channel */ + if (settings->SupportGraphicsPipeline && client->rdpgfx && + !gfxstatus.gfxOpened) + { + if (!client->rdpgfx->Open(client->rdpgfx)) + { + WLog_WARN(TAG, "Failed to open GraphicsPipeline"); + settings->SupportGraphicsPipeline = FALSE; + } + + client->rdpgfx->FrameAcknowledge = shadow_client_rdpgfx_frame_acknowledge; + client->rdpgfx->QoeFrameAcknowledge = shadow_client_rdpgfx_qoe_frame_acknowledge; + + gfxstatus.gfxOpened = TRUE; + WLog_INFO(TAG, "Gfx Pipeline Opened"); + } + } + } } if (WaitForSingleObject(ChannelEvent, 0) == WAIT_OBJECT_0) @@ -1220,6 +1628,15 @@ void* shadow_client_thread(rdpShadowClient* client) } /* Free channels early because we establish channels in post connect */ + if (gfxstatus.gfxOpened) + { + if (gfxstatus.gfxSurfaceCreated) + { + if (!shadow_client_rdpgfx_release_surface(client)) + WLog_WARN(TAG, "GFX release surface failure!"); + } + (void)client->rdpgfx->Close(client->rdpgfx); + } shadow_client_channels_free(client); if (UpdateSubscriber) @@ -1235,7 +1652,7 @@ void* shadow_client_thread(rdpShadowClient* client) out: peer->Disconnect(peer); - + freerdp_peer_context_free(peer); freerdp_peer_free(peer); ExitThread(0); diff --git a/server/shadow/shadow_client.h b/server/shadow/shadow_client.h index a78e5411a..789dde5cc 100644 --- a/server/shadow/shadow_client.h +++ b/server/shadow/shadow_client.h @@ -25,7 +25,7 @@ extern "C" { #endif -int shadow_client_surface_update(rdpShadowClient* client, REGION16* region); +BOOL shadow_client_surface_update(rdpShadowClient* client, REGION16* region); BOOL shadow_client_accepted(freerdp_listener* instance, freerdp_peer* client); #ifdef __cplusplus diff --git a/server/shadow/shadow_encoder.c b/server/shadow/shadow_encoder.c index 07bf880b2..44fb3ac16 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); @@ -142,18 +139,10 @@ int shadow_encoder_init_rfx(rdpShadowEncoder* encoder) if (!rfx_context_reset(encoder->rfx, encoder->width, encoder->height)) goto fail; - encoder->rfx->mode = RLGR3; - encoder->rfx->width = encoder->width; - encoder->rfx->height = encoder->height; + encoder->rfx->mode = encoder->server->rfxMode; 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 +161,24 @@ int shadow_encoder_init_nsc(rdpShadowEncoder* encoder) encoder->nsc = nsc_context_new(); if (!encoder->nsc) - return -1; + goto fail; - nsc_context_set_pixel_format(encoder->nsc, RDP_PIXEL_FORMAT_B8G8R8A8); - - 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 +199,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 +219,43 @@ 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->h264->RateControlMode = encoder->server->h264RateControlMode; + encoder->h264->BitRate = encoder->server->h264BitRate; + encoder->h264->FrameRate = encoder->server->h264FrameRate; + encoder->h264->QP = encoder->server->h264QP; + + encoder->codecs |= FREERDP_CODEC_AVC420; + + return 1; + +fail: + h264_context_free(encoder->h264); + return -1; } int shadow_encoder_init(rdpShadowEncoder* encoder) @@ -300,6 +329,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 +372,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 +384,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 +402,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 +447,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 fd7d46a11..b2c01b908 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 000000000..a8ad1153d --- /dev/null +++ b/server/shadow/shadow_rdpgfx.c @@ -0,0 +1,98 @@ +/** + * 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_10) + { + if (settings) + { + flags = pdu.capsSet->flags; + settings->GfxSmallCache = (flags & RDPGFX_CAPS_FLAG_SMALL_CACHE); + settings->GfxH264 = !(flags & RDPGFX_CAPS_FLAG_AVC_DISABLED); + } + + return context->CapsConfirm(context, &pdu); + } + else 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 000000000..0ac810d74 --- /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_server.c b/server/shadow/shadow_server.c index ad5ebbe78..cbbff4efd 100644 --- a/server/shadow/shadow_server.c +++ b/server/shadow/shadow_server.c @@ -769,6 +769,12 @@ rdpShadowServer* shadow_server_new() server->mayView = TRUE; server->mayInteract = TRUE; + server->rfxMode = RLGR3; + server->h264RateControlMode = H264_RATECONTROL_VBR; + server->h264BitRate = 1000000; + server->h264FrameRate = 30; + server->h264QP = 0; + server->authentication = FALSE; server->settings = freerdp_settings_new(FREERDP_SETTINGS_SERVER_MODE); diff --git a/server/shadow/shadow_subsystem.c b/server/shadow/shadow_subsystem.c index 0cbb46f5e..7f3173af6 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)