summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--client_examples/gtkvncviewer.c16
-rw-r--r--configure.ac14
-rw-r--r--libvncclient/Makefile.am2
-rw-r--r--libvncclient/h264.c644
-rw-r--r--libvncclient/rfbproto.c24
-rw-r--r--libvncclient/vncviewer.c7
-rw-r--r--rfb/rfbclient.h1
-rw-r--r--rfb/rfbproto.h18
8 files changed, 725 insertions, 1 deletions
diff --git a/client_examples/gtkvncviewer.c b/client_examples/gtkvncviewer.c
index 57085a5..261b2da 100644
--- a/client_examples/gtkvncviewer.c
+++ b/client_examples/gtkvncviewer.c
@@ -23,6 +23,10 @@
#include <gdk/gdkkeysyms.h>
#include <rfb/rfbclient.h>
+#ifdef LIBVNCSERVER_CONFIG_LIBVA
+#include <gdk/gdkx.h>
+#endif
+
static rfbClient *cl;
static gchar *server_cut_text = NULL;
static gboolean framebuffer_allocated = FALSE;
@@ -57,6 +61,14 @@ static gboolean expose_event (GtkWidget *widget,
cl->format.greenMax = (1 << image->visual->green_prec) - 1;
cl->format.blueMax = (1 << image->visual->blue_prec) - 1;
+#ifdef LIBVNCSERVER_CONFIG_LIBVA
+ /* Allow libvncclient to use a more efficient way
+ * of putting the framebuffer on the screen when
+ * using the H.264 format.
+ */
+ cl->outputWindow = GDK_WINDOW_XID(widget->window);
+#endif
+
SetFormatAndEncodings (cl);
framebuffer_allocated = TRUE;
@@ -67,12 +79,14 @@ static gboolean expose_event (GtkWidget *widget,
gdk_cursor_unref( cur );
}
+#ifndef LIBVNCSERVER_CONFIG_LIBVA
gdk_draw_image (GDK_DRAWABLE (widget->window),
widget->style->fg_gc[gtk_widget_get_state(widget)],
image,
event->area.x, event->area.y,
event->area.x, event->area.y,
event->area.width, event->area.height);
+#endif
return FALSE;
}
@@ -462,10 +476,12 @@ static void update (rfbClient *cl, int x, int y, int w, int h) {
dialog_connecting = NULL;
}
+#ifndef LIBVNCSERVER_CONFIG_LIBVA
GtkWidget *drawing_area = rfbClientGetClientData (cl, gtk_init);
if (drawing_area != NULL)
gtk_widget_queue_draw_area (drawing_area, x, y, w, h);
+#endif
}
static void kbd_leds (rfbClient *cl, int value, int pad) {
diff --git a/configure.ac b/configure.ac
index 3c8e3b7..c8b0d61 100644
--- a/configure.ac
+++ b/configure.ac
@@ -151,6 +151,20 @@ HAVE_X11="false"
AC_PATH_XTRA
AH_TEMPLATE(HAVE_X11, [X11 build environment present])
+# See if we want libva support
+# TODO: check if library actually exists
+AH_TEMPLATE(CONFIG_LIBVA, [Build libva support])
+AC_ARG_WITH(libva,
+[ --with-libva build libva support],,)
+if test "x$with_libva" != "xno"; then
+ AC_CHECK_LIB(va, vaInitialize,
+ VA_LIBS="-lva -lva-x11"
+ [AC_DEFINE(CONFIG_LIBVA) CONFIG_LIBVA="true"], ,)
+fi
+AC_SUBST(VA_LIBS)
+AM_CONDITIONAL(CONFIG_LIBVA, test ! -z "$VA_LIBS")
+
+
# See if we are to build x11vnc:
AH_TEMPLATE(HAVE_SYSTEM_LIBVNCSERVER, [Use the system libvncserver build environment for x11vnc.])
AC_ARG_WITH(system-libvncserver,
diff --git a/libvncclient/Makefile.am b/libvncclient/Makefile.am
index bf05a39..53b349e 100644
--- a/libvncclient/Makefile.am
+++ b/libvncclient/Makefile.am
@@ -14,7 +14,7 @@ endif
libvncclient_la_SOURCES=cursor.c listen.c rfbproto.c sockets.c vncviewer.c ../common/minilzo.c $(TLSSRCS)
-libvncclient_la_LIBADD=$(TLSLIBS)
+libvncclient_la_LIBADD=$(TLSLIBS) $(VA_LIBS)
noinst_HEADERS=../common/lzodefs.h ../common/lzoconf.h ../common/minilzo.h tls.h
diff --git a/libvncclient/h264.c b/libvncclient/h264.c
new file mode 100644
index 0000000..1d94454
--- /dev/null
+++ b/libvncclient/h264.c
@@ -0,0 +1,644 @@
+/*
+ * 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 */
diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c
index 41afc6c..958780c 100644
--- a/libvncclient/rfbproto.c
+++ b/libvncclient/rfbproto.c
@@ -158,6 +158,10 @@ static void FillRectangle(rfbClient* client, int x, int y, int w, int h, uint32_
static void CopyRectangle(rfbClient* client, uint8_t* buffer, int x, int y, int w, int h) {
int j;
+ if (client->frameBuffer == NULL) {
+ return;
+ }
+
#define COPY_RECT(BPP) \
{ \
int rs = w * BPP / 8, rs2 = client->width * BPP / 8; \
@@ -260,6 +264,9 @@ static rfbBool HandleZRLE24Up(rfbClient* client, int rx, int ry, int rw, int rh)
static rfbBool HandleZRLE24Down(rfbClient* client, int rx, int ry, int rw, int rh);
static rfbBool HandleZRLE32(rfbClient* client, int rx, int ry, int rw, int rh);
#endif
+#ifdef LIBVNCSERVER_CONFIG_LIBVA
+static rfbBool HandleH264 (rfbClient* client, int rx, int ry, int rw, int rh);
+#endif
/*
* Server Capability Functions
@@ -1344,6 +1351,10 @@ SetFormatAndEncodings(rfbClient* client)
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingCoRRE);
} else if (strncasecmp(encStr,"rre",encStrLen) == 0) {
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingRRE);
+#ifdef LIBVNCSERVER_CONFIG_LIBVA
+ } else if (strncasecmp(encStr,"h264",encStrLen) == 0) {
+ encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingH264);
+#endif
} else {
rfbClientLog("Unknown encoding '%.*s'\n",encStrLen,encStr);
}
@@ -1412,6 +1423,10 @@ SetFormatAndEncodings(rfbClient* client)
encs[se->nEncodings++] = rfbClientSwap32IfLE(client->appData.qualityLevel +
rfbEncodingQualityLevel0);
}
+#ifdef LIBVNCSERVER_CONFIG_LIBVA
+ encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingH264);
+ rfbClientLog("h264 encoding added\n");
+#endif
}
@@ -2128,6 +2143,14 @@ HandleRFBServerMessage(rfbClient* client)
}
#endif
+#ifdef LIBVNCSERVER_CONFIG_LIBVA
+ case rfbEncodingH264:
+ {
+ if (!HandleH264(client, rect.r.x, rect.r.y, rect.r.w, rect.r.h))
+ return FALSE;
+ break;
+ }
+#endif
default:
{
@@ -2361,6 +2384,7 @@ HandleRFBServerMessage(rfbClient* client)
#define UNCOMP -8
#include "zrle.c"
#undef BPP
+#include "h264.c"
/*
diff --git a/libvncclient/vncviewer.c b/libvncclient/vncviewer.c
index 4153c97..8b09d87 100644
--- a/libvncclient/vncviewer.c
+++ b/libvncclient/vncviewer.c
@@ -91,7 +91,11 @@ static rfbBool MallocFrameBuffer(rfbClient* client) {
static void initAppData(AppData* data) {
data->shareDesktop=TRUE;
data->viewOnly=FALSE;
+#ifdef LIBVNCSERVER_CONFIG_LIBVA
+ data->encodingsString="h264 tight zrle ultra copyrect hextile zlib corre rre raw";
+#else
data->encodingsString="tight zrle ultra copyrect hextile zlib corre rre raw";
+#endif
data->useBGR233=FALSE;
data->nColours=0;
data->forceOwnCmap=FALSE;
@@ -129,6 +133,9 @@ rfbClient* rfbGetClient(int bitsPerSample,int samplesPerPixel,
/* default: use complete frame buffer */
client->updateRect.x = -1;
+ client->frameBuffer = NULL;
+ client->outputWindow = 0;
+
client->format.bitsPerPixel = bytesPerPixel*8;
client->format.depth = bitsPerSample*samplesPerPixel;
client->appData.requestedDepth=client->format.depth;
diff --git a/rfb/rfbclient.h b/rfb/rfbclient.h
index 0ecc5e4..07e5063 100644
--- a/rfb/rfbclient.h
+++ b/rfb/rfbclient.h
@@ -175,6 +175,7 @@ typedef void (*GotCopyRectProc)(struct _rfbClient* client, int src_x, int src_y,
typedef struct _rfbClient {
uint8_t* frameBuffer;
+ unsigned long outputWindow; /* Output Window ID. When set, client application enables libvncclient to perform direct rendering in its window */
int width, height;
int endianTest;
diff --git a/rfb/rfbproto.h b/rfb/rfbproto.h
index acb2e7b..43f41e8 100644
--- a/rfb/rfbproto.h
+++ b/rfb/rfbproto.h
@@ -514,6 +514,9 @@ typedef struct {
#define rfbEncodingSupportedEncodings 0xFFFE0002
#define rfbEncodingServerIdentity 0xFFFE0003
+#ifdef LIBVNCSERVER_CONFIG_LIBVA
+#define rfbEncodingH264 0x48323634
+#endif
/*****************************************************************************
*
@@ -869,6 +872,21 @@ typedef struct {
#endif
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * h264 - h264 encoding. We have an rfbH264Header structure
+ * giving the number of bytes following. Finally the data follows is
+ * h264 encoded frame.
+ */
+
+typedef struct {
+ uint32_t nBytes;
+ uint32_t slice_type;
+ uint32_t width;
+ uint32_t height;
+} rfbH264Header;
+
+#define sz_rfbH264Header 16
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* XCursor encoding. This is a special encoding used to transmit X-style
* cursor shapes from server to clients. Note that for this encoding,
* coordinates in rfbFramebufferUpdateRectHeader structure hold hotspot