由于项目需要,研究了一下xfreerdp的图像更新流程。freerdp中有各种各样的函数指针,还有很多宏定义,具体的函数调用,通过阅读源码很难知道!这时候就需要祭出大杀器:systemtap了。
最最基础我知道每次图像更新调用了xf_client.c的函数:xf_hw_begin_paint,xf_hw_end_paint。
通过侦测libfreerdp/core/rdp.c和xf_client.c的两个函数,获得如下信息:
可以看到程序中有两个线程在读取socket中的数据:31762和6454。
0 gokuapp(6454): << freerdp_check_event_handles
7 gokuapp(6454): << freerdp_check_fds
12 gokuapp(6454): << rdp_check_fds
//rdp_check_fds中会调用transport_check_fds(transport.c),在transport.c中负责数据的接收。
17 gokuapp(6454): << freerdp_shall_disconnect
22 gokuapp(6454): >> freerdp_shall_disconnect
//transport接收完数据后,回调rdp_recv_callback,这个函数负责数据的解析。
70 gokuapp(6454): << rdp_recv_callback
75 gokuapp(6454): << rdp_recv_pdu
//解析到一个fastpath pdu!
79 gokuapp(6454): << rdp_recv_fastpath_pdu
//我们推测具体画的动作在这两个函数之间:<<<<<<<
//begin和end之间又做了些什么呢?
85 gokuapp(6454): << xf_hw_begin_paint
88 gokuapp(6454): >> xf_hw_begin_paint
107 gokuapp(6454): << xf_hw_end_paint
132 gokuapp(6454): >> xf_hw_end_paint
//我们推测具体画的动作在这两个函数之间:>>>>>>
134 gokuapp(6454): >> rdp_recv_fastpath_pdu
135 gokuapp(6454): >> rdp_recv_pdu
137 gokuapp(6454): >> rdp_recv_callback
141 gokuapp(6454): << freerdp_shall_disconnect
144 gokuapp(6454): >> freerdp_shall_disconnect
150 gokuapp(6454): >> rdp_check_fds
152 gokuapp(6454): >> freerdp_check_fds
156 gokuapp(6454): << checkChannelErrorEvent
159 gokuapp(6454): >> checkChannelErrorEvent
161 gokuapp(6454): >> freerdp_check_event_handles
0 gokuapp(31762): << freerdp_check_event_handles
6 gokuapp(31762): << freerdp_check_fds
12 gokuapp(31762): << rdp_check_fds
17 gokuapp(31762): << freerdp_shall_disconnect
23 gokuapp(31762): >> freerdp_shall_disconnect
40 gokuapp(31762): >> rdp_check_fds
41 gokuapp(31762): >> freerdp_check_fds
46 gokuapp(31762): << checkChannelErrorEvent
49 gokuapp(31762): >> checkChannelErrorEvent
50 gokuapp(31762): >> freerdp_check_event_handles
0 gokuapp(31762): << freerdp_shall_disconnect
3 gokuapp(31762): >> freerdp_shall_disconnect
0 gokuapp(31762): << freerdp_focus_required
2 gokuapp(31762): >> freerdp_focus_required
0 gokuapp(31762): << freerdp_get_event_handles
4 gokuapp(31762): << getChannelErrorEventHandle
6 gokuapp(31762): >> getChannelErrorEventHandle
8 gokuapp(31762): >> freerdp_get_event_handles
//上面留下了一个疑问,begin和end之间又做了些什么呢:
int fastpath_recv_updates(rdpFastPath* fastpath, wStream* s)
{
rdpUpdate* update = fastpath->rdp->update;
IFCALL(update->BeginPaint, update->context);
while (Stream_GetRemainingLength(s) >= 3)
{
if (fastpath_recv_update_data(fastpath, s) < 0)
{
WLog_ERR(TAG, "fastpath_recv_update_data() fail");
return -1;
}
}
IFCALL(update->EndPaint, update->context);
return 0;
}
这个函数的调用信息如下:
0 gokuapp(6454): <<fastpath_recv_updates
6 gokuapp(6454): <<fastpath_recv_update_data
//首先,读头!
9 gokuapp(6454): <<fastpath_read_update_header
12 gokuapp(6454): >>fastpath_read_update_header
//然后,根据头的信息,解压数据!
16 gokuapp(6454): <<bulk_decompress
20 gokuapp(6454): <<bulk_compression_max_size
23 gokuapp(6454): <<bulk_compression_level
25 gokuapp(6454): >>bulk_compression_level
27 gokuapp(6454): >>bulk_compression_max_size
56 gokuapp(6454): <<metrics_write_bytes
58 gokuapp(6454): >>metrics_write_bytes
59 gokuapp(6454): >>bulk_decompress
//解析解压后的数据!
63 gokuapp(6454): <<fastpath_recv_update
static int fastpath_recv_update(rdpFastPath* fastpath, BYTE updateCode, UINT32 size, wStream* s)
//根据不同的updateCode解析数据,后面的函数的updatecode是FASTPATH_UPDATETYPE_ORDERS!
79 gokuapp(6454): <<fastpath_recv_orders
82 gokuapp(6454): <<update_recv_order
86 gokuapp(6454): <<update_recv_altsec_order
91 gokuapp(6454): <<update_read_frame_marker_order
93 gokuapp(6454): >>update_read_frame_marker_order
94 gokuapp(6454): >>update_recv_altsec_order
96 gokuapp(6454): >>update_recv_order
98 gokuapp(6454): <<update_recv_order
101 gokuapp(6454): <<update_recv_primary_order
105 gokuapp(6454): <<update_read_field_flags
107 gokuapp(6454): >>update_read_field_flags
110 gokuapp(6454): <<update_read_memblt_order
114 gokuapp(6454): <<update_read_coord
116 gokuapp(6454): >>update_read_coord
118 gokuapp(6454): <<update_read_coord
120 gokuapp(6454): >>update_read_coord
121 gokuapp(6454): >>update_read_memblt_order
127 gokuapp(6454): >>update_recv_primary_order
128 gokuapp(6454): >>update_recv_order
130 gokuapp(6454): <<update_recv_order
133 gokuapp(6454): <<update_recv_primary_order
136 gokuapp(6454): <<update_read_field_flags
137 gokuapp(6454): >>update_read_field_flags
139 gokuapp(6454): <<update_read_memblt_order
142 gokuapp(6454): <<update_read_coord
144 gokuapp(6454): >>update_read_coord
145 gokuapp(6454): >>update_read_memblt_order
147 gokuapp(6454): >>update_recv_primary_order
148 gokuapp(6454): >>update_recv_order
150 gokuapp(6454): <<update_recv_order
153 gokuapp(6454): <<update_recv_primary_order
156 gokuapp(6454): <<update_read_field_flags
158 gokuapp(6454): >>update_read_field_flags
160 gokuapp(6454): <<update_read_memblt_order
162 gokuapp(6454): <<update_read_coord
164 gokuapp(6454): >>update_read_coord
165 gokuapp(6454): >>update_read_memblt_order
166 gokuapp(6454): >>update_recv_primary_order
168 gokuapp(6454): >>update_recv_order
170 gokuapp(6454): <<update_recv_order
172 gokuapp(6454): <<update_recv_primary_order
175 gokuapp(6454): <<update_read_field_flags
177 gokuapp(6454): >>update_read_field_flags
179 gokuapp(6454): <<update_read_memblt_order
182 gokuapp(6454): <<update_read_coord
183 gokuapp(6454): >>update_read_coord
184 gokuapp(6454): >>update_read_memblt_order
186 gokuapp(6454): >>update_recv_primary_order
187 gokuapp(6454): >>update_recv_order
189 gokuapp(6454): <<update_recv_order
192 gokuapp(6454): <<update_recv_primary_order
194 gokuapp(6454): <<update_read_field_flags
196 gokuapp(6454): >>update_read_field_flags
198 gokuapp(6454): <<update_read_memblt_order
201 gokuapp(6454): <<update_read_coord
203 gokuapp(6454): >>update_read_coord
205 gokuapp(6454): <<update_read_coord
206 gokuapp(6454): >>update_read_coord
207 gokuapp(6454): >>update_read_memblt_order
209 gokuapp(6454): >>update_recv_primary_order
210 gokuapp(6454): >>update_recv_order
212 gokuapp(6454): <<update_recv_order
215 gokuapp(6454): <<update_recv_primary_order
217 gokuapp(6454): <<update_read_field_flags
219 gokuapp(6454): >>update_read_field_flags
221 gokuapp(6454): <<update_read_memblt_order
224 gokuapp(6454): <<update_read_coord
226 gokuapp(6454): >>update_read_coord
227 gokuapp(6454): >>update_read_memblt_order
228 gokuapp(6454): >>update_recv_primary_order
229 gokuapp(6454): >>update_recv_order
231 gokuapp(6454): <<update_recv_order
234 gokuapp(6454): <<update_recv_primary_order
237 gokuapp(6454): <<update_read_field_flags
238 gokuapp(6454): >>update_read_field_flags
241 gokuapp(6454): <<update_read_memblt_order
243 gokuapp(6454): <<update_read_coord
245 gokuapp(6454): >>update_read_coord
246 gokuapp(6454): >>update_read_memblt_order
247 gokuapp(6454): >>update_recv_primary_order
248 gokuapp(6454): >>update_recv_order
250 gokuapp(6454): <<update_recv_order
253 gokuapp(6454): <<update_recv_primary_order
256 gokuapp(6454): <<update_read_field_flags
258 gokuapp(6454): >>update_read_field_flags
260 gokuapp(6454): <<update_read_memblt_order
263 gokuapp(6454): <<update_read_coord
264 gokuapp(6454): >>update_read_coord
265 gokuapp(6454): >>update_read_memblt_order
267 gokuapp(6454): >>update_recv_primary_order
268 gokuapp(6454): >>update_recv_order
270 gokuapp(6454): <<update_recv_order
272 gokuapp(6454): <<update_recv_primary_order
275 gokuapp(6454): <<update_read_field_flags
277 gokuapp(6454): >>update_read_field_flags
279 gokuapp(6454): <<update_read_memblt_order
282 gokuapp(6454): <<update_read_coord
283 gokuapp(6454): >>update_read_coord
286 gokuapp(6454): <<update_read_coord
287 gokuapp(6454): >>update_read_coord
288 gokuapp(6454): >>update_read_memblt_order
290 gokuapp(6454): >>update_recv_primary_order
291 gokuapp(6454): >>update_recv_order
293 gokuapp(6454): <<update_recv_order
295 gokuapp(6454): <<update_recv_primary_order
298 gokuapp(6454): <<update_read_field_flags
300 gokuapp(6454): >>update_read_field_flags
302 gokuapp(6454): <<update_read_memblt_order
305 gokuapp(6454): <<update_read_coord
306 gokuapp(6454): >>update_read_coord
308 gokuapp(6454): >>update_read_memblt_order
309 gokuapp(6454): >>update_recv_primary_order
310 gokuapp(6454): >>update_recv_order
312 gokuapp(6454): <<update_recv_order
315 gokuapp(6454): <<update_recv_primary_order
317 gokuapp(6454): <<update_read_field_flags
319 gokuapp(6454): >>update_read_field_flags
321 gokuapp(6454): <<update_read_memblt_order
324 gokuapp(6454): <<update_read_coord
326 gokuapp(6454): >>update_read_coord
327 gokuapp(6454): >>update_read_memblt_order
328 gokuapp(6454): >>update_recv_primary_order
329 gokuapp(6454): >>update_recv_order
331 gokuapp(6454): <<update_recv_order
334 gokuapp(6454): <<update_recv_primary_order
337 gokuapp(6454): <<update_read_field_flags
338 gokuapp(6454): >>update_read_field_flags
340 gokuapp(6454): <<update_read_memblt_order
343 gokuapp(6454): <<update_read_coord
345 gokuapp(6454): >>update_read_coord
346 gokuapp(6454): >>update_read_memblt_order
347 gokuapp(6454): >>update_recv_primary_order
348 gokuapp(6454): >>update_recv_order
350 gokuapp(6454): <<update_recv_order
353 gokuapp(6454): <<update_recv_altsec_order
356 gokuapp(6454): <<update_read_frame_marker_order
358 gokuapp(6454): >>update_read_frame_marker_order
359 gokuapp(6454): >>update_recv_altsec_order
360 gokuapp(6454): >>update_recv_order
361 gokuapp(6454): >>fastpath_recv_orders
363 gokuapp(6454): >>fastpath_recv_update
364 gokuapp(6454): >>fastpath_recv_update_data
385 gokuapp(6454): >>fastpath_recv_updates
上面的内容是典型的rdp gdi协议!
下面我们直接看rdp gdi协议文档内容:
The Remote Desktop Protocol: Graphics Devices Interfaces (GDI) Acceleration Extension is an extension to the Remote Desktop Protocol: Basic Connectivity and Graphics Remoting (as specified in [MS-DPBCGR]). The aim of the Remote Desktop Protocol: GDI Acceleration Extension is to reduce the bandwidth associated with graphics remoting by encoding the drawing operations that produce an image instead of encoding the actual image.
可以看到gdi协议的目的是减少网络带宽的占用,如何减少,用传输drawing operations替代传输编码的图像。所以,gdi协议的大部分内容是传输drawing operations!
一些文档中用到的术语!列出了我认为比较重要的几个:
ARGB: A color space wherein each color is represented as a quad (A, R, G, B), where A represents the alpha (transparency) component, R represents the red component, G represents the green component, and B represents the blue component. The ARGB value is typically stored as a 32-bit integer, wherein the alpha channel is stored in the highest 8 bits and the blue value is storedin the lowest 8 bits.
brush: An 8-by-8-pixel bitmap that is repeated horizontally and vertically to fill an area.
color plane: A two-dimensional surface containing a collection of values that represent a single component of the ARGB or AYCoCg color space.
color space: Any method of representing colors for printing or electronic display.
Packed Encoding Rules (PER): A set of encoding rules for ASN.1 notation, specified in [ITUX691]. These rules enable the identification, extraction, and decoding of data structures.
protocol data unit (PDU): Information that is delivered as a unit among peer entities of a network and that may contain control information, address information, or data. For more information on remote procedure call (RPC)-specific PDUs, see [C706] section 12.
Reverse Polish Notation (RPN): A mathematical notation wherein each operator follows all of its operands. Also known as postfix notation.
screen tearing: A phenomenon in video rendering where a newly rendered frame partially overlaps with a previously rendered frame, creating a torn look as graphical objects do not line up.
具体GDI协议是如何降低带宽的使用,同时又能保证终端的体验效果呢?文档有如下解释:
For example, instead of sending the bitmap image of a filled rectangle from server to client, an orderto render a rectangle at coordinate (X, Y) with a given width, height, and fill color is sent to the client. The client then executes the drawing order to produce the intended graphics result.
In addition to defining how to encode common drawing operations, the Remote Desktop Protocol: GDI Acceleration Extension also facilitates the use of caches to store drawing primitives such as bitmaps, color tables, and characters. The effective use of caching techniques helps to reduce wire traffic by ensuring that items used in multiple drawing operations are sent only once from server to client (retransmission of these items for use in conjunction with future drawing operations is not required after the item has been cached on the client).
GDI协议是发送的编码的drawing order,同时还会发送bitmaps, color tables, characters的cache。所以GDI协议的cache机制很关键!
GDI协议的几个关键部分:
Caches
The Remote Desktop Protocol: GDI Acceleration Extension defines a number of caches that may be
leveraged by clients and servers:
Bitmap Cache: Stores bitmap images.
Color Table Cache: Stores color palettes.
Glyph Cache: Stores character images.
Fragment Cache: Stores collections of glyphs.
Brush Cache: Stores 8-by-8-pixel bitmaps used to fill regions.
Offscreen Bitmap Cache: Stores writable bitmaps.
GDI+ Caches: Used to cache GDI+ 1.1 primitives:
Graphics Cache
Brush Cache
Pen Cache
Image Attributes Cache
Image Cache
NineGrid Bitmap Cache: Stores NineGrid-compliant bitmaps. For more information about nine-grid
bitmaps, see [NINEGRID].Drawing Orders
Drawing orders are used to perform the following operations:
Transport bitmap data
Encode graphics rendering primitives
Manipulate data caches
Manage rendering surfaces
Support application remoting ([MS-RDPERP] section 1.3)
Support desktop composition ([MS-RDPEDC] section 1.3)
There are three classes of drawing orders:
Primary
Secondary
Alternate secondaryBulk Data Compression