/* * Copyright (C) 2012 Intel Corporation. All Rights Reserved. * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this software; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. */ #ifdef LIBVNCSERVER_CONFIG_LIBVA #include <X11/Xlib.h> #include <va/va_x11.h> enum _slice_types { SLICE_TYPE_P = 0, /* Predicted */ SLICE_TYPE_B = 1, /* Bi-predicted */ SLICE_TYPE_I = 2, /* Intra coded */ }; #define SURFACE_NUM 7 VADisplay va_dpy = NULL; VAConfigID va_config_id; VASurfaceID va_surface_id[SURFACE_NUM]; VAContextID va_context_id = 0; VABufferID va_pic_param_buf_id[SURFACE_NUM]; VABufferID va_mat_param_buf_id[SURFACE_NUM]; VABufferID va_sp_param_buf_id[SURFACE_NUM]; VABufferID va_d_param_buf_id[SURFACE_NUM]; static int cur_height = 0; static int cur_width = 0; static unsigned int num_frames = 0; static int sid = 0; static unsigned int frame_id = 0; static int field_order_count = 0; static VASurfaceID curr_surface = VA_INVALID_ID; VAStatus gva_status; VASurfaceStatus gsurface_status; #define CHECK_SURF(X) \ gva_status = vaQuerySurfaceStatus(va_dpy, X, &gsurface_status); \ if (gsurface_status != 4) printf("ss: %d\n", gsurface_status); #ifdef _DEBUG #define DebugLog(A) rfbClientLog A #else #define DebugLog(A) #endif #define CHECK_VASTATUS(va_status,func) \ if (va_status != VA_STATUS_SUCCESS) { \ /*fprintf(stderr,"%s:%s (%d) failed,exit\n", __func__, func, __LINE__);*/ \ rfbClientErr("%s:%s:%d failed (0x%x),exit\n", __func__, func, __LINE__, va_status); \ exit(1); \ } else { \ /*fprintf(stderr,">> SUCCESS for: %s:%s (%d)\n", __func__, func, __LINE__);*/ \ DebugLog(("%s:%s:%d success\n", __func__, func, __LINE__)); \ } /* * Forward declarations */ static void h264_decode_frame(int f_width, int f_height, char *framedata, int framesize, int slice_type); static void SetVAPictureParameterBufferH264(VAPictureParameterBufferH264 *p, int width, int height); static void SetVASliceParameterBufferH264(VASliceParameterBufferH264 *p); static void SetVASliceParameterBufferH264_Intra(VASliceParameterBufferH264 *p, int first); 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); static void nv12_to_rgba(const VAImage vaImage, rfbClient *client, int ch_x, int ch_y, int ch_w, int ch_h); /* FIXME: get this value from the server instead of hardcoding 32bit pixels */ #define BPP (4 * 8) static const char *string_of_FOURCC(uint32_t fourcc) { static int buf; static char str[2][5]; buf ^= 1; str[buf][0] = fourcc; str[buf][1] = fourcc >> 8; str[buf][2] = fourcc >> 16; str[buf][3] = fourcc >> 24; str[buf][4] = '\0'; return str[buf]; } static inline const char *string_of_VAImageFormat(VAImageFormat *imgfmt) { return string_of_FOURCC(imgfmt->fourcc); } static rfbBool HandleH264 (rfbClient* client, int rx, int ry, int rw, int rh) { rfbH264Header hdr; char *framedata; DebugLog(("Framebuffer update with H264 (x: %d, y: %d, w: %d, h: %d)\n", rx, ry, rw, rh)); /* First, read the frame size and allocate buffer to store the data */ if (!ReadFromRFBServer(client, (char *)&hdr, sz_rfbH264Header)) return FALSE; hdr.slice_type = rfbClientSwap32IfLE(hdr.slice_type); hdr.nBytes = rfbClientSwap32IfLE(hdr.nBytes); hdr.width = rfbClientSwap32IfLE(hdr.width); hdr.height = rfbClientSwap32IfLE(hdr.height); framedata = (char*) malloc(hdr.nBytes); /* Obtain frame data from the server */ DebugLog(("Reading %d bytes of frame data (type: %d)\n", hdr.nBytes, hdr.slice_type)); if (!ReadFromRFBServer(client, framedata, hdr.nBytes)) return FALSE; /* First make sure we have a large enough raw buffer to hold the * decompressed data. In practice, with a fixed BPP, fixed frame * buffer size and the first update containing the entire frame * buffer, this buffer allocation should only happen once, on the * first update. */ if ( client->raw_buffer_size < (( rw * rh ) * ( BPP / 8 ))) { if ( client->raw_buffer != NULL ) { free( client->raw_buffer ); } client->raw_buffer_size = (( rw * rh ) * ( BPP / 8 )); client->raw_buffer = (char*) malloc( client->raw_buffer_size ); rfbClientLog("Allocated raw buffer of %d bytes (%dx%dx%d BPP)\n", client->raw_buffer_size, rw, rh, BPP); } /* Decode frame if frame data was sent. Server only sends frame data for the first * framebuffer update message for a particular frame buffer contents. * If more than 1 rectangle is updated, the messages after the first one (with * the H.264 frame) have nBytes == 0. */ if (hdr.nBytes > 0) { DebugLog((" decoding %d bytes of H.264 data\n", hdr.nBytes)); h264_decode_frame(hdr.width, hdr.height, framedata, hdr.nBytes, hdr.slice_type); } DebugLog((" updating rectangle (%d, %d)-(%d, %d)\n", rx, ry, rw, rh)); put_updated_rectangle(client, rx, ry, rw, rh, hdr.width, hdr.height, hdr.nBytes != 0); free(framedata); return TRUE; } static void h264_cleanup_decoder() { VAStatus va_status; rfbClientLog("%s()\n", __FUNCTION__); if (va_surface_id[0] != VA_INVALID_ID) { va_status = vaDestroySurfaces(va_dpy, &va_surface_id[0], SURFACE_NUM); CHECK_VASTATUS(va_status, "vaDestroySurfaces"); } if (va_context_id) { va_status = vaDestroyContext(va_dpy, va_context_id); CHECK_VASTATUS(va_status, "vaDestroyContext"); va_context_id = 0; } num_frames = 0; sid = 0; frame_id = 0; field_order_count = 0; } static void h264_init_decoder(int width, int height) { VAStatus va_status; if (va_context_id) { rfbClientLog("%s: va_dpy already initialized\n", __FUNCTION__); } if (va_dpy != NULL) { rfbClientLog("%s: Re-initializing H.264 decoder\n", __FUNCTION__); } else { rfbClientLog("%s: initializing H.264 decoder\n", __FUNCTION__); /* Attach VA display to local X display */ Display *win_display = (Display *)XOpenDisplay(":0.0"); if (win_display == NULL) { rfbClientErr("Can't connect to local display\n"); exit(-1); } int major_ver, minor_ver; va_dpy = vaGetDisplay(win_display); va_status = vaInitialize(va_dpy, &major_ver, &minor_ver); CHECK_VASTATUS(va_status, "vaInitialize"); rfbClientLog("%s: libva version %d.%d found\n", __FUNCTION__, major_ver, minor_ver); } /* Check for VLD entrypoint */ int num_entrypoints; VAEntrypoint entrypoints[5]; int vld_entrypoint_found = 0; /* Change VAProfileH264High if needed */ VAProfile profile = VAProfileH264High; va_status = vaQueryConfigEntrypoints(va_dpy, profile, entrypoints, &num_entrypoints); CHECK_VASTATUS(va_status, "vaQueryConfigEntrypoints"); int i; for (i = 0; i < num_entrypoints; ++i) { if (entrypoints[i] == VAEntrypointVLD) { vld_entrypoint_found = 1; break; } } if (vld_entrypoint_found == 0) { rfbClientErr("VLD entrypoint not found\n"); exit(1); } /* Create configuration for the decode pipeline */ VAConfigAttrib attrib; attrib.type = VAConfigAttribRTFormat; va_status = vaCreateConfig(va_dpy, profile, VAEntrypointVLD, &attrib, 1, &va_config_id); CHECK_VASTATUS(va_status, "vaCreateConfig"); /* Create VA surfaces */ for (i = 0; i < SURFACE_NUM; ++i) { va_surface_id[i] = VA_INVALID_ID; va_pic_param_buf_id[i] = VA_INVALID_ID; va_mat_param_buf_id[i] = VA_INVALID_ID; va_sp_param_buf_id[i] = VA_INVALID_ID; va_d_param_buf_id[i] = VA_INVALID_ID; } va_status = vaCreateSurfaces(va_dpy, width, height, VA_RT_FORMAT_YUV420, SURFACE_NUM, &va_surface_id[0]); CHECK_VASTATUS(va_status, "vaCreateSurfaces"); for (i = 0; i < SURFACE_NUM; ++i) { DebugLog(("%s: va_surface_id[%d] = %p\n", __FUNCTION__, i, va_surface_id[i])); } /* Create VA context */ va_status = vaCreateContext(va_dpy, va_config_id, width, height, 0/*VA_PROGRESSIVE*/, &va_surface_id[0], SURFACE_NUM, &va_context_id); CHECK_VASTATUS(va_status, "vaCreateContext"); DebugLog(("%s: VA context created (id: %d)\n", __FUNCTION__, va_context_id)); /* Instantiate decode pipeline */ va_status = vaBeginPicture(va_dpy, va_context_id, va_surface_id[0]); CHECK_VASTATUS(va_status, "vaBeginPicture"); rfbClientLog("%s: H.264 decoder initialized\n", __FUNCTION__); } static void h264_decode_frame(int f_width, int f_height, char *framedata, int framesize, int slice_type) { VAStatus va_status; DebugLog(("%s: called for frame of %d bytes (%dx%d) slice_type=%d\n", __FUNCTION__, framesize, width, height, slice_type)); /* Initialize decode pipeline if necessary */ if ( (f_width > cur_width) || (f_height > cur_height) ) { if (va_dpy != NULL) h264_cleanup_decoder(); cur_width = f_width; cur_height = f_height; h264_init_decoder(f_width, f_height); rfbClientLog("%s: decoder initialized\n", __FUNCTION__); } /* Decode frame */ static VAPictureH264 va_picture_h264, va_old_picture_h264; /* The server should always send an I-frame when a new client connects * or when the resolution of the framebuffer changes, but we check * just in case. */ if ( (slice_type != SLICE_TYPE_I) && (num_frames == 0) ) { rfbClientLog("First frame is not an I frame !!! Skipping!!!\n"); return; } 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)); va_picture_h264.picture_id = va_surface_id[sid]; va_picture_h264.frame_idx = frame_id; va_picture_h264.flags = 0; va_picture_h264.BottomFieldOrderCnt = field_order_count; va_picture_h264.TopFieldOrderCnt = field_order_count; /* Set up picture parameter buffer */ if (va_pic_param_buf_id[sid] == VA_INVALID_ID) { va_status = vaCreateBuffer(va_dpy, va_context_id, VAPictureParameterBufferType, sizeof(VAPictureParameterBufferH264), 1, NULL, &va_pic_param_buf_id[sid]); CHECK_VASTATUS(va_status, "vaCreateBuffer(PicParam)"); } CHECK_SURF(va_surface_id[sid]); VAPictureParameterBufferH264 *pic_param_buf = NULL; va_status = vaMapBuffer(va_dpy, va_pic_param_buf_id[sid], (void **)&pic_param_buf); CHECK_VASTATUS(va_status, "vaMapBuffer(PicParam)"); SetVAPictureParameterBufferH264(pic_param_buf, f_width, f_height); memcpy(&pic_param_buf->CurrPic, &va_picture_h264, sizeof(VAPictureH264)); if (slice_type == SLICE_TYPE_P) { memcpy(&pic_param_buf->ReferenceFrames[0], &va_old_picture_h264, sizeof(VAPictureH264)); pic_param_buf->ReferenceFrames[0].flags = 0; } else if (slice_type != SLICE_TYPE_I) { rfbClientLog("Frame type %d not supported!!!\n"); return; } pic_param_buf->frame_num = frame_id; va_status = vaUnmapBuffer(va_dpy, va_pic_param_buf_id[sid]); CHECK_VASTATUS(va_status, "vaUnmapBuffer(PicParam)"); /* Set up IQ matrix buffer */ if (va_mat_param_buf_id[sid] == VA_INVALID_ID) { va_status = vaCreateBuffer(va_dpy, va_context_id, VAIQMatrixBufferType, sizeof(VAIQMatrixBufferH264), 1, NULL, &va_mat_param_buf_id[sid]); CHECK_VASTATUS(va_status, "vaCreateBuffer(IQMatrix)"); } CHECK_SURF(va_surface_id[sid]); VAIQMatrixBufferH264 *iq_matrix_buf = NULL; va_status = vaMapBuffer(va_dpy, va_mat_param_buf_id[sid], (void **)&iq_matrix_buf); CHECK_VASTATUS(va_status, "vaMapBuffer(IQMatrix)"); static const unsigned char m_MatrixBufferH264[]= { /* ScalingList4x4[6][16] */ 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, /* ScalingList8x8[2][64] */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; memcpy(iq_matrix_buf, m_MatrixBufferH264, 224); va_status = vaUnmapBuffer(va_dpy, va_mat_param_buf_id[sid]); CHECK_VASTATUS(va_status, "vaUnmapBuffer(IQMatrix)"); VABufferID buffer_ids[2]; buffer_ids[0] = va_pic_param_buf_id[sid]; buffer_ids[1] = va_mat_param_buf_id[sid]; CHECK_SURF(va_surface_id[sid]); va_status = vaRenderPicture(va_dpy, va_context_id, buffer_ids, 2); CHECK_VASTATUS(va_status, "vaRenderPicture"); /* Set up slice parameter buffer */ if (va_sp_param_buf_id[sid] == VA_INVALID_ID) { va_status = vaCreateBuffer(va_dpy, va_context_id, VASliceParameterBufferType, sizeof(VASliceParameterBufferH264), 1, NULL, &va_sp_param_buf_id[sid]); CHECK_VASTATUS(va_status, "vaCreateBuffer(SliceParam)"); } CHECK_SURF(va_surface_id[sid]); VASliceParameterBufferH264 *slice_param_buf = NULL; va_status = vaMapBuffer(va_dpy, va_sp_param_buf_id[sid], (void **)&slice_param_buf); CHECK_VASTATUS(va_status, "vaMapBuffer(SliceParam)"); static int t2_first = 1; if (slice_type == SLICE_TYPE_I) { SetVASliceParameterBufferH264_Intra(slice_param_buf, t2_first); t2_first = 0; } else { SetVASliceParameterBufferH264(slice_param_buf); memcpy(&slice_param_buf->RefPicList0[0], &va_old_picture_h264, sizeof(VAPictureH264)); slice_param_buf->RefPicList0[0].flags = 0; } slice_param_buf->slice_data_bit_offset = 0; slice_param_buf->slice_data_size = framesize; va_status = vaUnmapBuffer(va_dpy, va_sp_param_buf_id[sid]); CHECK_VASTATUS(va_status, "vaUnmapBuffer(SliceParam)"); CHECK_SURF(va_surface_id[sid]); /* Set up slice data buffer and copy H.264 encoded data */ if (va_d_param_buf_id[sid] == VA_INVALID_ID) { /* TODO use estimation matching framebuffer dimensions instead of this large value */ va_status = vaCreateBuffer(va_dpy, va_context_id, VASliceDataBufferType, 4177920, 1, NULL, &va_d_param_buf_id[sid]); /* 1080p size */ CHECK_VASTATUS(va_status, "vaCreateBuffer(SliceData)"); } char *slice_data_buf; va_status = vaMapBuffer(va_dpy, va_d_param_buf_id[sid], (void **)&slice_data_buf); CHECK_VASTATUS(va_status, "vaMapBuffer(SliceData)"); memcpy(slice_data_buf, framedata, framesize); CHECK_SURF(va_surface_id[sid]); va_status = vaUnmapBuffer(va_dpy, va_d_param_buf_id[sid]); CHECK_VASTATUS(va_status, "vaUnmapBuffer(SliceData)"); buffer_ids[0] = va_sp_param_buf_id[sid]; buffer_ids[1] = va_d_param_buf_id[sid]; CHECK_SURF(va_surface_id[sid]); va_status = vaRenderPicture(va_dpy, va_context_id, buffer_ids, 2); CHECK_VASTATUS(va_status, "vaRenderPicture"); va_status = vaEndPicture(va_dpy, va_context_id); CHECK_VASTATUS(va_status, "vaEndPicture"); /* Prepare next one... */ int sid_new = (sid + 1) % SURFACE_NUM; DebugLog(("%s: new Surface ID = %d\n", __FUNCTION__, sid_new)); va_status = vaBeginPicture(va_dpy, va_context_id, va_surface_id[sid_new]); CHECK_VASTATUS(va_status, "vaBeginPicture"); /* Get decoded data */ va_status = vaSyncSurface(va_dpy, va_surface_id[sid]); CHECK_VASTATUS(va_status, "vaSyncSurface"); CHECK_SURF(va_surface_id[sid]); curr_surface = va_surface_id[sid]; sid = sid_new; field_order_count += 2; ++frame_id; if (frame_id > 15) { frame_id = 0; } ++num_frames; memcpy(&va_old_picture_h264, &va_picture_h264, sizeof(VAPictureH264)); } 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) { if (curr_surface == VA_INVALID_ID) { rfbClientErr("%s: called, but current surface is invalid\n", __FUNCTION__); return; } VAStatus va_status; if (client->outputWindow) { /* use efficient vaPutSurface() method of putting the framebuffer on the screen */ if (first_for_frame) { /* vaPutSurface() clears window contents outside the given destination rectangle => always update full screen. */ 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); CHECK_VASTATUS(va_status, "vaPutSurface"); } } else if (client->frameBuffer) { /* ... or copy the changed framebuffer region manually as a fallback */ VAImage decoded_image; decoded_image.image_id = VA_INVALID_ID; decoded_image.buf = VA_INVALID_ID; va_status = vaDeriveImage(va_dpy, curr_surface, &decoded_image); CHECK_VASTATUS(va_status, "vaDeriveImage"); if ((decoded_image.image_id == VA_INVALID_ID) || (decoded_image.buf == VA_INVALID_ID)) { rfbClientErr("%s: vaDeriveImage() returned success but VA image is invalid (id: %d, buf: %d)\n", __FUNCTION__, decoded_image.image_id, decoded_image.buf); } nv12_to_rgba(decoded_image, client, x, y, width, height); va_status = vaDestroyImage(va_dpy, decoded_image.image_id); CHECK_VASTATUS(va_status, "vaDestroyImage"); } } static void SetVAPictureParameterBufferH264(VAPictureParameterBufferH264 *p, int width, int height) { int i; unsigned int width_in_mbs = (width + 15) / 16; unsigned int height_in_mbs = (height + 15) / 16; memset(p, 0, sizeof(VAPictureParameterBufferH264)); p->picture_width_in_mbs_minus1 = width_in_mbs - 1; p->picture_height_in_mbs_minus1 = height_in_mbs - 1; p->num_ref_frames = 1; p->seq_fields.value = 145; p->pic_fields.value = 0x501; for (i = 0; i < 16; i++) { p->ReferenceFrames[i].flags = VA_PICTURE_H264_INVALID; p->ReferenceFrames[i].picture_id = 0xffffffff; } } static void SetVASliceParameterBufferH264(VASliceParameterBufferH264 *p) { int i; memset(p, 0, sizeof(VASliceParameterBufferH264)); p->slice_data_size = 0; p->slice_data_bit_offset = 64; p->slice_alpha_c0_offset_div2 = 2; p->slice_beta_offset_div2 = 2; p->chroma_weight_l0_flag = 1; p->chroma_weight_l0[0][0]=1; p->chroma_offset_l0[0][0]=0; p->chroma_weight_l0[0][1]=1; p->chroma_offset_l0[0][1]=0; p->luma_weight_l1_flag = 1; p->chroma_weight_l1_flag = 1; p->luma_weight_l0[0]=0x01; for (i = 0; i < 32; i++) { p->RefPicList0[i].flags = VA_PICTURE_H264_INVALID; p->RefPicList1[i].flags = VA_PICTURE_H264_INVALID; } p->RefPicList1[0].picture_id = 0xffffffff; } static void SetVASliceParameterBufferH264_Intra(VASliceParameterBufferH264 *p, int first) { int i; memset(p, 0, sizeof(VASliceParameterBufferH264)); p->slice_data_size = 0; p->slice_data_bit_offset = 64; p->slice_alpha_c0_offset_div2 = 2; p->slice_beta_offset_div2 = 2; p->slice_type = 2; if (first) { p->luma_weight_l0_flag = 1; p->chroma_weight_l0_flag = 1; p->luma_weight_l1_flag = 1; p->chroma_weight_l1_flag = 1; } else { p->chroma_weight_l0_flag = 1; p->chroma_weight_l0[0][0]=1; p->chroma_offset_l0[0][0]=0; p->chroma_weight_l0[0][1]=1; p->chroma_offset_l0[0][1]=0; p->luma_weight_l1_flag = 1; p->chroma_weight_l1_flag = 1; p->luma_weight_l0[0]=0x01; } for (i = 0; i < 32; i++) { p->RefPicList0[i].flags = VA_PICTURE_H264_INVALID; p->RefPicList1[i].flags = VA_PICTURE_H264_INVALID; } p->RefPicList1[0].picture_id = 0xffffffff; p->RefPicList0[0].picture_id = 0xffffffff; } static void nv12_to_rgba(const VAImage vaImage, rfbClient *client, int ch_x, int ch_y, int ch_w, int ch_h) { DebugLog(("%s: converting region (%d, %d)-(%d, %d) from NV12->RGBA\n", __FUNCTION__, ch_x, ch_y, ch_w, ch_h)); VAStatus va_status; uint8_t *nv12_buf; va_status = vaMapBuffer(va_dpy, vaImage.buf, (void **)&nv12_buf); CHECK_VASTATUS(va_status, "vaMapBuffer(DecodedData)"); /* adjust x, y, width, height of the affected area so * x, y, width and height are always even. */ if (ch_x % 2) { --ch_x; ++ch_w; } if (ch_y % 2) { --ch_y; ++ch_h; } if ((ch_x + ch_w) % 2) { ++ch_w; } if ((ch_y + ch_h) % 2) { ++ch_h; } /* point nv12_buf and dst to upper left corner of changed area */ uint8_t *nv12_y = &nv12_buf[vaImage.offsets[0] + vaImage.pitches[0] * ch_y + ch_x]; uint8_t *nv12_uv = &nv12_buf[vaImage.offsets[1] + vaImage.pitches[1] * (ch_y / 2) + ch_x]; uint32_t *dst = &((uint32_t*)client->frameBuffer)[client->width * ch_y + ch_x]; /* TODO: optimize R, G, B calculation. Possible ways to do this: * - use lookup tables * - convert from floating point to integer arithmetic * - use MMX/SSE to vectorize calculations * - use GPU (VA VPP, shader...) */ int src_x, src_y; for (src_y = 0; src_y < ch_h; src_y += 2) { for (src_x = 0; src_x < ch_w; src_x += 2) { uint8_t nv_u = nv12_uv[src_x]; uint8_t nv_v = nv12_uv[src_x + 1]; uint8_t nv_y[4] = { nv12_y[ src_x], nv12_y[ src_x + 1], nv12_y[vaImage.pitches[0] + src_x], nv12_y[vaImage.pitches[0] + src_x + 1] }; int i; for (i = 0; i < 4; ++i) { double R = 1.164 * (nv_y[i] - 16) + 1.596 * (nv_v - 128); double G = 1.164 * (nv_y[i] - 16) - 0.391 * (nv_u - 128) - 0.813 * (nv_v - 128); double B = 1.164 * (nv_y[i] - 16) + 2.018 * (nv_u - 128); /* clamp R, G, B values. For some Y, U, V combinations, * the results of the above calculations fall outside of * the range 0-255. */ if (R < 0.0) R = 0.0; if (G < 0.0) G = 0.0; if (B < 0.0) B = 0.0; if (R > 255.0) R = 255.0; if (G > 255.0) G = 255.0; if (B > 255.0) B = 255.0; dst[client->width * (i / 2) + src_x + (i % 2)] = 0 | ((unsigned int)(R + 0.5) << client->format.redShift) | ((unsigned int)(G + 0.5) << client->format.greenShift) | ((unsigned int)(B + 0.5) << client->format.blueShift); } } nv12_y += 2 * vaImage.pitches[0]; nv12_uv += vaImage.pitches[1]; dst += 2 * client->width; } CHECK_SURF(va_surface_id[sid]); va_status = vaUnmapBuffer(va_dpy, vaImage.buf); CHECK_VASTATUS(va_status, "vaUnmapBuffer(DecodedData)"); } #endif /* LIBVNCSERVER_CONFIG_LIBVA */