1 /*
2 * Copyright (C) 2012 Intel Corporation. All Rights Reserved.
3 *
4 * This is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This software is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this software; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 * USA.
18 */
19
20 #ifdef LIBVNCSERVER_CONFIG_LIBVA
21
22 #include <X11/Xlib.h>
23 #include <va/va_x11.h>
24
25 enum _slice_types {
26 SLICE_TYPE_P = 0, /* Predicted */
27 SLICE_TYPE_B = 1, /* Bi-predicted */
28 SLICE_TYPE_I = 2, /* Intra coded */
29 };
30
31 #define SURFACE_NUM 7
32
33 VADisplay va_dpy = NULL;
34 VAConfigID va_config_id;
35 VASurfaceID va_surface_id[SURFACE_NUM];
36 VAContextID va_context_id = 0;
37
38 VABufferID va_pic_param_buf_id[SURFACE_NUM];
39 VABufferID va_mat_param_buf_id[SURFACE_NUM];
40 VABufferID va_sp_param_buf_id[SURFACE_NUM];
41 VABufferID va_d_param_buf_id[SURFACE_NUM];
42
43 static int cur_height = 0;
44 static int cur_width = 0;
45 static unsigned int num_frames = 0;
46 static int sid = 0;
47 static unsigned int frame_id = 0;
48 static int field_order_count = 0;
49 static VASurfaceID curr_surface = VA_INVALID_ID;
50
51 VAStatus gva_status;
52 VASurfaceStatus gsurface_status;
53 #define CHECK_SURF(X) \
54 gva_status = vaQuerySurfaceStatus(va_dpy, X, &gsurface_status); \
55 if (gsurface_status != 4) printf("ss: %d\n", gsurface_status);
56
57 #ifdef _DEBUG
58 #define DebugLog(A) rfbClientLog A
59 #else
60 #define DebugLog(A)
61 #endif
62
63 #define CHECK_VASTATUS(va_status,func) \
64 if (va_status != VA_STATUS_SUCCESS) { \
65 /*fprintf(stderr,"%s:%s (%d) failed,exit\n", __func__, func, __LINE__);*/ \
66 rfbClientErr("%s:%s:%d failed (0x%x),exit\n", __func__, func, __LINE__, va_status); \
67 exit(1); \
68 } else { \
69 /*fprintf(stderr,">> SUCCESS for: %s:%s (%d)\n", __func__, func, __LINE__);*/ \
70 DebugLog(("%s:%s:%d success\n", __func__, func, __LINE__)); \
71 }
72
73 /*
74 * Forward declarations
75 */
76 static void h264_decode_frame(int f_width, int f_height, char *framedata, int framesize, int slice_type);
77 static void SetVAPictureParameterBufferH264(VAPictureParameterBufferH264 *p, int width, int height);
78 static void SetVASliceParameterBufferH264(VASliceParameterBufferH264 *p);
79 static void SetVASliceParameterBufferH264_Intra(VASliceParameterBufferH264 *p, int first);
80
81 static void put_updated_rectangle(rfbClient *client, int x, int y, int width, int height, int f_width, int f_height, int first_for_frame);
82 static void nv12_to_rgba(const VAImage vaImage, rfbClient *client, int ch_x, int ch_y, int ch_w, int ch_h);
83
84
85 /* FIXME: get this value from the server instead of hardcoding 32bit pixels */
86 #define BPP (4 * 8)
87
string_of_FOURCC(uint32_t fourcc)88 static const char *string_of_FOURCC(uint32_t fourcc)
89 {
90 static int buf;
91 static char str[2][5];
92
93 buf ^= 1;
94 str[buf][0] = fourcc;
95 str[buf][1] = fourcc >> 8;
96 str[buf][2] = fourcc >> 16;
97 str[buf][3] = fourcc >> 24;
98 str[buf][4] = '\0';
99 return str[buf];
100 }
101
string_of_VAImageFormat(VAImageFormat * imgfmt)102 static inline const char *string_of_VAImageFormat(VAImageFormat *imgfmt)
103 {
104 return string_of_FOURCC(imgfmt->fourcc);
105 }
106
107
108 static rfbBool
HandleH264(rfbClient * client,int rx,int ry,int rw,int rh)109 HandleH264 (rfbClient* client, int rx, int ry, int rw, int rh)
110 {
111 rfbH264Header hdr;
112 char *framedata;
113
114 DebugLog(("Framebuffer update with H264 (x: %d, y: %d, w: %d, h: %d)\n", rx, ry, rw, rh));
115
116 /* First, read the frame size and allocate buffer to store the data */
117 if (!ReadFromRFBServer(client, (char *)&hdr, sz_rfbH264Header))
118 return FALSE;
119
120 hdr.slice_type = rfbClientSwap32IfLE(hdr.slice_type);
121 hdr.nBytes = rfbClientSwap32IfLE(hdr.nBytes);
122 hdr.width = rfbClientSwap32IfLE(hdr.width);
123 hdr.height = rfbClientSwap32IfLE(hdr.height);
124
125 framedata = (char*) malloc(hdr.nBytes);
126
127 /* Obtain frame data from the server */
128 DebugLog(("Reading %d bytes of frame data (type: %d)\n", hdr.nBytes, hdr.slice_type));
129 if (!ReadFromRFBServer(client, framedata, hdr.nBytes))
130 return FALSE;
131
132 /* First make sure we have a large enough raw buffer to hold the
133 * decompressed data. In practice, with a fixed BPP, fixed frame
134 * buffer size and the first update containing the entire frame
135 * buffer, this buffer allocation should only happen once, on the
136 * first update.
137 */
138 if ( client->raw_buffer_size < (( rw * rh ) * ( BPP / 8 ))) {
139 if ( client->raw_buffer != NULL ) {
140 free( client->raw_buffer );
141 }
142
143 client->raw_buffer_size = (( rw * rh ) * ( BPP / 8 ));
144 client->raw_buffer = (char*) malloc( client->raw_buffer_size );
145 rfbClientLog("Allocated raw buffer of %d bytes (%dx%dx%d BPP)\n", client->raw_buffer_size, rw, rh, BPP);
146 }
147
148 /* Decode frame if frame data was sent. Server only sends frame data for the first
149 * framebuffer update message for a particular frame buffer contents.
150 * If more than 1 rectangle is updated, the messages after the first one (with
151 * the H.264 frame) have nBytes == 0.
152 */
153 if (hdr.nBytes > 0) {
154 DebugLog((" decoding %d bytes of H.264 data\n", hdr.nBytes));
155 h264_decode_frame(hdr.width, hdr.height, framedata, hdr.nBytes, hdr.slice_type);
156 }
157
158 DebugLog((" updating rectangle (%d, %d)-(%d, %d)\n", rx, ry, rw, rh));
159 put_updated_rectangle(client, rx, ry, rw, rh, hdr.width, hdr.height, hdr.nBytes != 0);
160
161 free(framedata);
162
163 return TRUE;
164 }
165
h264_cleanup_decoder()166 static void h264_cleanup_decoder()
167 {
168 VAStatus va_status;
169
170 rfbClientLog("%s()\n", __FUNCTION__);
171
172 if (va_surface_id[0] != VA_INVALID_ID) {
173 va_status = vaDestroySurfaces(va_dpy, &va_surface_id[0], SURFACE_NUM);
174 CHECK_VASTATUS(va_status, "vaDestroySurfaces");
175 }
176
177 if (va_context_id) {
178 va_status = vaDestroyContext(va_dpy, va_context_id);
179 CHECK_VASTATUS(va_status, "vaDestroyContext");
180 va_context_id = 0;
181 }
182
183 num_frames = 0;
184 sid = 0;
185 frame_id = 0;
186 field_order_count = 0;
187 }
188
h264_init_decoder(int width,int height)189 static void h264_init_decoder(int width, int height)
190 {
191 VAStatus va_status;
192
193 if (va_context_id) {
194 rfbClientLog("%s: va_dpy already initialized\n", __FUNCTION__);
195 }
196
197 if (va_dpy != NULL) {
198 rfbClientLog("%s: Re-initializing H.264 decoder\n", __FUNCTION__);
199 }
200 else {
201 rfbClientLog("%s: initializing H.264 decoder\n", __FUNCTION__);
202
203 /* Attach VA display to local X display */
204 Display *win_display = (Display *)XOpenDisplay(":0.0");
205 if (win_display == NULL) {
206 rfbClientErr("Can't connect to local display\n");
207 exit(-1);
208 }
209
210 int major_ver, minor_ver;
211 va_dpy = vaGetDisplay(win_display);
212 va_status = vaInitialize(va_dpy, &major_ver, &minor_ver);
213 CHECK_VASTATUS(va_status, "vaInitialize");
214 rfbClientLog("%s: libva version %d.%d found\n", __FUNCTION__, major_ver, minor_ver);
215 }
216
217 /* Check for VLD entrypoint */
218 int num_entrypoints;
219 VAEntrypoint entrypoints[5];
220 int vld_entrypoint_found = 0;
221
222 /* Change VAProfileH264High if needed */
223 VAProfile profile = VAProfileH264High;
224 va_status = vaQueryConfigEntrypoints(va_dpy, profile, entrypoints, &num_entrypoints);
225 CHECK_VASTATUS(va_status, "vaQueryConfigEntrypoints");
226 int i;
227 for (i = 0; i < num_entrypoints; ++i) {
228 if (entrypoints[i] == VAEntrypointVLD) {
229 vld_entrypoint_found = 1;
230 break;
231 }
232 }
233
234 if (vld_entrypoint_found == 0) {
235 rfbClientErr("VLD entrypoint not found\n");
236 exit(1);
237 }
238
239 /* Create configuration for the decode pipeline */
240 VAConfigAttrib attrib;
241 attrib.type = VAConfigAttribRTFormat;
242 va_status = vaCreateConfig(va_dpy, profile, VAEntrypointVLD, &attrib, 1, &va_config_id);
243 CHECK_VASTATUS(va_status, "vaCreateConfig");
244
245 /* Create VA surfaces */
246 for (i = 0; i < SURFACE_NUM; ++i) {
247 va_surface_id[i] = VA_INVALID_ID;
248 va_pic_param_buf_id[i] = VA_INVALID_ID;
249 va_mat_param_buf_id[i] = VA_INVALID_ID;
250 va_sp_param_buf_id[i] = VA_INVALID_ID;
251 va_d_param_buf_id[i] = VA_INVALID_ID;
252 }
253 va_status = vaCreateSurfaces(va_dpy, width, height, VA_RT_FORMAT_YUV420, SURFACE_NUM, &va_surface_id[0]);
254 CHECK_VASTATUS(va_status, "vaCreateSurfaces");
255 for (i = 0; i < SURFACE_NUM; ++i) {
256 DebugLog(("%s: va_surface_id[%d] = %p\n", __FUNCTION__, i, va_surface_id[i]));
257 }
258
259 /* Create VA context */
260 va_status = vaCreateContext(va_dpy, va_config_id, width, height, 0/*VA_PROGRESSIVE*/, &va_surface_id[0], SURFACE_NUM, &va_context_id);
261 CHECK_VASTATUS(va_status, "vaCreateContext");
262 DebugLog(("%s: VA context created (id: %d)\n", __FUNCTION__, va_context_id));
263
264
265 /* Instantiate decode pipeline */
266 va_status = vaBeginPicture(va_dpy, va_context_id, va_surface_id[0]);
267 CHECK_VASTATUS(va_status, "vaBeginPicture");
268
269 rfbClientLog("%s: H.264 decoder initialized\n", __FUNCTION__);
270 }
271
h264_decode_frame(int f_width,int f_height,char * framedata,int framesize,int slice_type)272 static void h264_decode_frame(int f_width, int f_height, char *framedata, int framesize, int slice_type)
273 {
274 VAStatus va_status;
275
276 DebugLog(("%s: called for frame of %d bytes (%dx%d) slice_type=%d\n", __FUNCTION__, framesize, width, height, slice_type));
277
278 /* Initialize decode pipeline if necessary */
279 if ( (f_width > cur_width) || (f_height > cur_height) ) {
280 if (va_dpy != NULL)
281 h264_cleanup_decoder();
282 cur_width = f_width;
283 cur_height = f_height;
284
285 h264_init_decoder(f_width, f_height);
286 rfbClientLog("%s: decoder initialized\n", __FUNCTION__);
287 }
288
289 /* Decode frame */
290 static VAPictureH264 va_picture_h264, va_old_picture_h264;
291
292 /* The server should always send an I-frame when a new client connects
293 * or when the resolution of the framebuffer changes, but we check
294 * just in case.
295 */
296 if ( (slice_type != SLICE_TYPE_I) && (num_frames == 0) ) {
297 rfbClientLog("First frame is not an I frame !!! Skipping!!!\n");
298 return;
299 }
300
301 DebugLog(("%s: frame_id=%d va_surface_id[%d]=0x%x field_order_count=%d\n", __FUNCTION__, frame_id, sid, va_surface_id[sid], field_order_count));
302
303 va_picture_h264.picture_id = va_surface_id[sid];
304 va_picture_h264.frame_idx = frame_id;
305 va_picture_h264.flags = 0;
306 va_picture_h264.BottomFieldOrderCnt = field_order_count;
307 va_picture_h264.TopFieldOrderCnt = field_order_count;
308
309 /* Set up picture parameter buffer */
310 if (va_pic_param_buf_id[sid] == VA_INVALID_ID) {
311 va_status = vaCreateBuffer(va_dpy, va_context_id, VAPictureParameterBufferType, sizeof(VAPictureParameterBufferH264), 1, NULL, &va_pic_param_buf_id[sid]);
312 CHECK_VASTATUS(va_status, "vaCreateBuffer(PicParam)");
313 }
314 CHECK_SURF(va_surface_id[sid]);
315
316 VAPictureParameterBufferH264 *pic_param_buf = NULL;
317 va_status = vaMapBuffer(va_dpy, va_pic_param_buf_id[sid], (void **)&pic_param_buf);
318 CHECK_VASTATUS(va_status, "vaMapBuffer(PicParam)");
319
320 SetVAPictureParameterBufferH264(pic_param_buf, f_width, f_height);
321 memcpy(&pic_param_buf->CurrPic, &va_picture_h264, sizeof(VAPictureH264));
322
323 if (slice_type == SLICE_TYPE_P) {
324 memcpy(&pic_param_buf->ReferenceFrames[0], &va_old_picture_h264, sizeof(VAPictureH264));
325 pic_param_buf->ReferenceFrames[0].flags = 0;
326 }
327 else if (slice_type != SLICE_TYPE_I) {
328 rfbClientLog("Frame type %d not supported!!!\n");
329 return;
330 }
331 pic_param_buf->frame_num = frame_id;
332
333 va_status = vaUnmapBuffer(va_dpy, va_pic_param_buf_id[sid]);
334 CHECK_VASTATUS(va_status, "vaUnmapBuffer(PicParam)");
335
336 /* Set up IQ matrix buffer */
337 if (va_mat_param_buf_id[sid] == VA_INVALID_ID) {
338 va_status = vaCreateBuffer(va_dpy, va_context_id, VAIQMatrixBufferType, sizeof(VAIQMatrixBufferH264), 1, NULL, &va_mat_param_buf_id[sid]);
339 CHECK_VASTATUS(va_status, "vaCreateBuffer(IQMatrix)");
340 }
341 CHECK_SURF(va_surface_id[sid]);
342
343 VAIQMatrixBufferH264 *iq_matrix_buf = NULL;
344 va_status = vaMapBuffer(va_dpy, va_mat_param_buf_id[sid], (void **)&iq_matrix_buf);
345 CHECK_VASTATUS(va_status, "vaMapBuffer(IQMatrix)");
346
347 static const unsigned char m_MatrixBufferH264[]= {
348 /* ScalingList4x4[6][16] */
349 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,
350 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,
351 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,
352 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,
353 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,
354 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,
355 /* ScalingList8x8[2][64] */
356 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
357 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
358 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
359 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
360 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
361 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
362 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
363 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
364 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
365 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
366 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
367 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
368 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
369 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
370 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
371 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
372 };
373
374 memcpy(iq_matrix_buf, m_MatrixBufferH264, 224);
375 va_status = vaUnmapBuffer(va_dpy, va_mat_param_buf_id[sid]);
376 CHECK_VASTATUS(va_status, "vaUnmapBuffer(IQMatrix)");
377
378 VABufferID buffer_ids[2];
379 buffer_ids[0] = va_pic_param_buf_id[sid];
380 buffer_ids[1] = va_mat_param_buf_id[sid];
381
382 CHECK_SURF(va_surface_id[sid]);
383 va_status = vaRenderPicture(va_dpy, va_context_id, buffer_ids, 2);
384 CHECK_VASTATUS(va_status, "vaRenderPicture");
385
386 /* Set up slice parameter buffer */
387 if (va_sp_param_buf_id[sid] == VA_INVALID_ID) {
388 va_status = vaCreateBuffer(va_dpy, va_context_id, VASliceParameterBufferType, sizeof(VASliceParameterBufferH264), 1, NULL, &va_sp_param_buf_id[sid]);
389 CHECK_VASTATUS(va_status, "vaCreateBuffer(SliceParam)");
390 }
391 CHECK_SURF(va_surface_id[sid]);
392
393 VASliceParameterBufferH264 *slice_param_buf = NULL;
394 va_status = vaMapBuffer(va_dpy, va_sp_param_buf_id[sid], (void **)&slice_param_buf);
395 CHECK_VASTATUS(va_status, "vaMapBuffer(SliceParam)");
396
397 static int t2_first = 1;
398 if (slice_type == SLICE_TYPE_I) {
399 SetVASliceParameterBufferH264_Intra(slice_param_buf, t2_first);
400 t2_first = 0;
401 } else {
402 SetVASliceParameterBufferH264(slice_param_buf);
403 memcpy(&slice_param_buf->RefPicList0[0], &va_old_picture_h264, sizeof(VAPictureH264));
404 slice_param_buf->RefPicList0[0].flags = 0;
405 }
406 slice_param_buf->slice_data_bit_offset = 0;
407 slice_param_buf->slice_data_size = framesize;
408
409 va_status = vaUnmapBuffer(va_dpy, va_sp_param_buf_id[sid]);
410 CHECK_VASTATUS(va_status, "vaUnmapBuffer(SliceParam)");
411 CHECK_SURF(va_surface_id[sid]);
412
413 /* Set up slice data buffer and copy H.264 encoded data */
414 if (va_d_param_buf_id[sid] == VA_INVALID_ID) {
415 /* TODO use estimation matching framebuffer dimensions instead of this large value */
416 va_status = vaCreateBuffer(va_dpy, va_context_id, VASliceDataBufferType, 4177920, 1, NULL, &va_d_param_buf_id[sid]); /* 1080p size */
417 CHECK_VASTATUS(va_status, "vaCreateBuffer(SliceData)");
418 }
419
420 char *slice_data_buf;
421 va_status = vaMapBuffer(va_dpy, va_d_param_buf_id[sid], (void **)&slice_data_buf);
422 CHECK_VASTATUS(va_status, "vaMapBuffer(SliceData)");
423 memcpy(slice_data_buf, framedata, framesize);
424
425 CHECK_SURF(va_surface_id[sid]);
426 va_status = vaUnmapBuffer(va_dpy, va_d_param_buf_id[sid]);
427 CHECK_VASTATUS(va_status, "vaUnmapBuffer(SliceData)");
428
429 buffer_ids[0] = va_sp_param_buf_id[sid];
430 buffer_ids[1] = va_d_param_buf_id[sid];
431
432 CHECK_SURF(va_surface_id[sid]);
433 va_status = vaRenderPicture(va_dpy, va_context_id, buffer_ids, 2);
434 CHECK_VASTATUS(va_status, "vaRenderPicture");
435
436 va_status = vaEndPicture(va_dpy, va_context_id);
437 CHECK_VASTATUS(va_status, "vaEndPicture");
438
439 /* Prepare next one... */
440 int sid_new = (sid + 1) % SURFACE_NUM;
441 DebugLog(("%s: new Surface ID = %d\n", __FUNCTION__, sid_new));
442 va_status = vaBeginPicture(va_dpy, va_context_id, va_surface_id[sid_new]);
443 CHECK_VASTATUS(va_status, "vaBeginPicture");
444
445 /* Get decoded data */
446 va_status = vaSyncSurface(va_dpy, va_surface_id[sid]);
447 CHECK_VASTATUS(va_status, "vaSyncSurface");
448 CHECK_SURF(va_surface_id[sid]);
449
450 curr_surface = va_surface_id[sid];
451
452 sid = sid_new;
453
454 field_order_count += 2;
455 ++frame_id;
456 if (frame_id > 15) {
457 frame_id = 0;
458 }
459
460 ++num_frames;
461
462 memcpy(&va_old_picture_h264, &va_picture_h264, sizeof(VAPictureH264));
463 }
464
put_updated_rectangle(rfbClient * client,int x,int y,int width,int height,int f_width,int f_height,int first_for_frame)465 static void put_updated_rectangle(rfbClient *client, int x, int y, int width, int height, int f_width, int f_height, int first_for_frame)
466 {
467 if (curr_surface == VA_INVALID_ID) {
468 rfbClientErr("%s: called, but current surface is invalid\n", __FUNCTION__);
469 return;
470 }
471
472 VAStatus va_status;
473
474 if (client->outputWindow) {
475 /* use efficient vaPutSurface() method of putting the framebuffer on the screen */
476 if (first_for_frame) {
477 /* vaPutSurface() clears window contents outside the given destination rectangle => always update full screen. */
478 va_status = vaPutSurface(va_dpy, curr_surface, client->outputWindow, 0, 0, f_width, f_height, 0, 0, f_width, f_height, NULL, 0, VA_FRAME_PICTURE);
479 CHECK_VASTATUS(va_status, "vaPutSurface");
480 }
481 }
482 else if (client->frameBuffer) {
483 /* ... or copy the changed framebuffer region manually as a fallback */
484 VAImage decoded_image;
485 decoded_image.image_id = VA_INVALID_ID;
486 decoded_image.buf = VA_INVALID_ID;
487 va_status = vaDeriveImage(va_dpy, curr_surface, &decoded_image);
488 CHECK_VASTATUS(va_status, "vaDeriveImage");
489
490 if ((decoded_image.image_id == VA_INVALID_ID) || (decoded_image.buf == VA_INVALID_ID)) {
491 rfbClientErr("%s: vaDeriveImage() returned success but VA image is invalid (id: %d, buf: %d)\n", __FUNCTION__, decoded_image.image_id, decoded_image.buf);
492 }
493
494 nv12_to_rgba(decoded_image, client, x, y, width, height);
495
496 va_status = vaDestroyImage(va_dpy, decoded_image.image_id);
497 CHECK_VASTATUS(va_status, "vaDestroyImage");
498 }
499 }
500
SetVAPictureParameterBufferH264(VAPictureParameterBufferH264 * p,int width,int height)501 static void SetVAPictureParameterBufferH264(VAPictureParameterBufferH264 *p, int width, int height)
502 {
503 int i;
504 unsigned int width_in_mbs = (width + 15) / 16;
505 unsigned int height_in_mbs = (height + 15) / 16;
506
507 memset(p, 0, sizeof(VAPictureParameterBufferH264));
508 p->picture_width_in_mbs_minus1 = width_in_mbs - 1;
509 p->picture_height_in_mbs_minus1 = height_in_mbs - 1;
510 p->num_ref_frames = 1;
511 p->seq_fields.value = 145;
512 p->pic_fields.value = 0x501;
513 for (i = 0; i < 16; i++) {
514 p->ReferenceFrames[i].flags = VA_PICTURE_H264_INVALID;
515 p->ReferenceFrames[i].picture_id = 0xffffffff;
516 }
517 }
518
SetVASliceParameterBufferH264(VASliceParameterBufferH264 * p)519 static void SetVASliceParameterBufferH264(VASliceParameterBufferH264 *p)
520 {
521 int i;
522 memset(p, 0, sizeof(VASliceParameterBufferH264));
523 p->slice_data_size = 0;
524 p->slice_data_bit_offset = 64;
525 p->slice_alpha_c0_offset_div2 = 2;
526 p->slice_beta_offset_div2 = 2;
527 p->chroma_weight_l0_flag = 1;
528 p->chroma_weight_l0[0][0]=1;
529 p->chroma_offset_l0[0][0]=0;
530 p->chroma_weight_l0[0][1]=1;
531 p->chroma_offset_l0[0][1]=0;
532 p->luma_weight_l1_flag = 1;
533 p->chroma_weight_l1_flag = 1;
534 p->luma_weight_l0[0]=0x01;
535 for (i = 0; i < 32; i++) {
536 p->RefPicList0[i].flags = VA_PICTURE_H264_INVALID;
537 p->RefPicList1[i].flags = VA_PICTURE_H264_INVALID;
538 }
539 p->RefPicList1[0].picture_id = 0xffffffff;
540 }
541
SetVASliceParameterBufferH264_Intra(VASliceParameterBufferH264 * p,int first)542 static void SetVASliceParameterBufferH264_Intra(VASliceParameterBufferH264 *p, int first)
543 {
544 int i;
545 memset(p, 0, sizeof(VASliceParameterBufferH264));
546 p->slice_data_size = 0;
547 p->slice_data_bit_offset = 64;
548 p->slice_alpha_c0_offset_div2 = 2;
549 p->slice_beta_offset_div2 = 2;
550 p->slice_type = 2;
551 if (first) {
552 p->luma_weight_l0_flag = 1;
553 p->chroma_weight_l0_flag = 1;
554 p->luma_weight_l1_flag = 1;
555 p->chroma_weight_l1_flag = 1;
556 } else {
557 p->chroma_weight_l0_flag = 1;
558 p->chroma_weight_l0[0][0]=1;
559 p->chroma_offset_l0[0][0]=0;
560 p->chroma_weight_l0[0][1]=1;
561 p->chroma_offset_l0[0][1]=0;
562 p->luma_weight_l1_flag = 1;
563 p->chroma_weight_l1_flag = 1;
564 p->luma_weight_l0[0]=0x01;
565 }
566 for (i = 0; i < 32; i++) {
567 p->RefPicList0[i].flags = VA_PICTURE_H264_INVALID;
568 p->RefPicList1[i].flags = VA_PICTURE_H264_INVALID;
569 }
570 p->RefPicList1[0].picture_id = 0xffffffff;
571 p->RefPicList0[0].picture_id = 0xffffffff;
572 }
573
nv12_to_rgba(const VAImage vaImage,rfbClient * client,int ch_x,int ch_y,int ch_w,int ch_h)574 static void nv12_to_rgba(const VAImage vaImage, rfbClient *client, int ch_x, int ch_y, int ch_w, int ch_h)
575 {
576 DebugLog(("%s: converting region (%d, %d)-(%d, %d) from NV12->RGBA\n", __FUNCTION__, ch_x, ch_y, ch_w, ch_h));
577
578 VAStatus va_status;
579 uint8_t *nv12_buf;
580 va_status = vaMapBuffer(va_dpy, vaImage.buf, (void **)&nv12_buf);
581 CHECK_VASTATUS(va_status, "vaMapBuffer(DecodedData)");
582
583 /* adjust x, y, width, height of the affected area so
584 * x, y, width and height are always even.
585 */
586 if (ch_x % 2) { --ch_x; ++ch_w; }
587 if (ch_y % 2) { --ch_y; ++ch_h; }
588 if ((ch_x + ch_w) % 2) { ++ch_w; }
589 if ((ch_y + ch_h) % 2) { ++ch_h; }
590
591 /* point nv12_buf and dst to upper left corner of changed area */
592 uint8_t *nv12_y = &nv12_buf[vaImage.offsets[0] + vaImage.pitches[0] * ch_y + ch_x];
593 uint8_t *nv12_uv = &nv12_buf[vaImage.offsets[1] + vaImage.pitches[1] * (ch_y / 2) + ch_x];
594 uint32_t *dst = &((uint32_t*)client->frameBuffer)[client->width * ch_y + ch_x];
595
596 /* TODO: optimize R, G, B calculation. Possible ways to do this:
597 * - use lookup tables
598 * - convert from floating point to integer arithmetic
599 * - use MMX/SSE to vectorize calculations
600 * - use GPU (VA VPP, shader...)
601 */
602 int src_x, src_y;
603 for (src_y = 0; src_y < ch_h; src_y += 2) {
604 for (src_x = 0; src_x < ch_w; src_x += 2) {
605 uint8_t nv_u = nv12_uv[src_x];
606 uint8_t nv_v = nv12_uv[src_x + 1];
607 uint8_t nv_y[4] = { nv12_y[ src_x], nv12_y[ src_x + 1],
608 nv12_y[vaImage.pitches[0] + src_x], nv12_y[vaImage.pitches[0] + src_x + 1] };
609
610 int i;
611 for (i = 0; i < 4; ++i) {
612 double R = 1.164 * (nv_y[i] - 16) + 1.596 * (nv_v - 128);
613 double G = 1.164 * (nv_y[i] - 16) - 0.391 * (nv_u - 128) - 0.813 * (nv_v - 128);
614 double B = 1.164 * (nv_y[i] - 16) + 2.018 * (nv_u - 128);
615
616 /* clamp R, G, B values. For some Y, U, V combinations,
617 * the results of the above calculations fall outside of
618 * the range 0-255.
619 */
620 if (R < 0.0) R = 0.0;
621 if (G < 0.0) G = 0.0;
622 if (B < 0.0) B = 0.0;
623 if (R > 255.0) R = 255.0;
624 if (G > 255.0) G = 255.0;
625 if (B > 255.0) B = 255.0;
626
627 dst[client->width * (i / 2) + src_x + (i % 2)] = 0
628 | ((unsigned int)(R + 0.5) << client->format.redShift)
629 | ((unsigned int)(G + 0.5) << client->format.greenShift)
630 | ((unsigned int)(B + 0.5) << client->format.blueShift);
631 }
632 }
633
634 nv12_y += 2 * vaImage.pitches[0];
635 nv12_uv += vaImage.pitches[1];
636 dst += 2 * client->width;
637 }
638
639 CHECK_SURF(va_surface_id[sid]);
640 va_status = vaUnmapBuffer(va_dpy, vaImage.buf);
641 CHECK_VASTATUS(va_status, "vaUnmapBuffer(DecodedData)");
642 }
643
644 #endif /* LIBVNCSERVER_CONFIG_LIBVA */
645