设为首页 收藏本站
查看: 1826|回复: 0

[经验分享] arm-linux 裸机下 VNC 的实现

[复制链接]

尚未签到

发表于 2017-11-17 17:36:17 | 显示全部楼层 |阅读模式
  这里的 arm-linux 裸机指的是,只有基本 C 库和安装了 busybox 的嵌入式系统,没有 X11 或者 wayland 作为底层支援。
  这里的实现是基于 framebuffer 的,是将用于 LCD 显示的 /dev/fb* 设备中数据进行了拷贝(其实是 mmap 进行了数据共享,是直接取了 framebuffer 数据缓存),并通过 VNC 协议外发。本文只记录了一个没有键盘鼠标的,基于 framebuffer 的 VNC 桌面实验。本实验中,使用友善之臂设计的 s5pv21 开发板。
  今天目的只是实验是否可行,并没有详细 debug。这个明显是还有很大改进空间的。PS: 原来 VNC 的协议本身就是叫 rfb, remote framebuffer。
  下面是在 xtightvncviewer 中连接效果:
DSC0000.png

  实现路径:
  1. 为 arm 板编译安装了 libvncserver。编译的时候,进行了下面一些特殊操作:
  a. 有部分可以在 configure 时候关闭(比如 --without-gnutls)
  b. 对 SDL 和 GTK 的需求,我直接编辑了 configure 脚本,找到 HAVE_LIBSDL_FALSE= ,HAVE_LIBGTK_FALSE= 位置,去掉了外面的判断。即默认就是这两个状态。因为我不需要 GTK 和 SDL。
  c. 解压出来的工具无法正确判断系统类型,configure 时候指定了系统: build_os=gnu-linux ./configure --host=arm-linux --prefix=/tmp/install
  编译下面的文件,将二进制拷贝到开发板。
  该文件是从github获得的,作者实现的时候把键盘鼠标都删掉了,不过这个明显是可以添加的。网址:



/*
* $Id$
*
* This program 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, or (at your option) any
* later version.
*
* This program 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.
*
* This project is an adaptation of the original fbvncserver for the iPAQ
* and Zaurus.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>             /* For makedev() */
#include <fcntl.h>
#include <linux/fb.h>
#include <linux/input.h>
#include <assert.h>
#include <errno.h>
/* libvncserver */
#include "rfb/rfb.h"
//#include "rfb/keysym.h"
/*****************************************************************************/
//#define LOG_FPS
#define BITS_PER_SAMPLE     5
#define SAMPLES_PER_PIXEL   2
static char fb_device[256] = "/dev/fb0";
static struct fb_var_screeninfo scrinfo;
static int fbfd = -1;
static unsigned short int *fbmmap = MAP_FAILED;
static unsigned short int *vncbuf;
static unsigned short int *fbbuf;
static int vnc_port = 5900;
static rfbScreenInfoPtr server;
static size_t bytespp;

/* No idea, just copied from fbvncserver as part of the frame differerencing
* algorithm.  I will probably be later rewriting all of this. */
static struct varblock_t
{
int min_i;
int min_j;
int max_i;
int max_j;
int r_offset;
int g_offset;
int b_offset;
int rfb_xres;
int rfb_maxy;
} varblock;
/*****************************************************************************/

static void init_fb(void)
{
size_t pixels;
if ((fbfd = open(fb_device, O_RDONLY)) == -1)
{
fprintf(stderr, "cannot open fb device %s\n", fb_device);
exit(EXIT_FAILURE);
}
if (ioctl(fbfd, FBIOGET_VSCREENINFO, &scrinfo) != 0)
{
fprintf(stderr, "ioctl error\n");
exit(EXIT_FAILURE);
}
pixels = scrinfo.xres * scrinfo.yres;
bytespp = scrinfo.bits_per_pixel / 8;
fprintf(stderr, "xres=%d, yres=%d, xresv=%d, yresv=%d, xoffs=%d, yoffs=%d, bpp=%d\n",
(int)scrinfo.xres, (int)scrinfo.yres,
(int)scrinfo.xres_virtual, (int)scrinfo.yres_virtual,
(int)scrinfo.xoffset, (int)scrinfo.yoffset,
(int)scrinfo.bits_per_pixel);
fprintf(stderr, "offset:length red=%d:%d green=%d:%d blue=%d:%d \n",
(int)scrinfo.red.offset, (int)scrinfo.red.length,
(int)scrinfo.green.offset, (int)scrinfo.green.length,
(int)scrinfo.blue.offset, (int)scrinfo.blue.length
);
fbmmap = mmap(NULL, pixels * bytespp, PROT_READ, MAP_SHARED, fbfd, 0);
if (fbmmap == MAP_FAILED)
{
fprintf(stderr, "mmap failed\n");
exit(EXIT_FAILURE);
}
}
static void cleanup_fb(void)
{
if(fbfd != -1)
{
close(fbfd);
}
}
/*****************************************************************************/
static void init_fb_server(int argc, char **argv)
{
fprintf(stderr, "Initializing server...\n");
/* Allocate the VNC server buffer to be managed (not manipulated) by
* libvncserver. */
vncbuf = calloc(scrinfo.xres * scrinfo.yres, bytespp);
assert(vncbuf != NULL);
/* Allocate the comparison buffer for detecting drawing updates from frame
* to frame. */
fbbuf = calloc(scrinfo.xres * scrinfo.yres, bytespp);
assert(fbbuf != NULL);
/* TODO: This assumes scrinfo.bits_per_pixel is 16. */
server = rfbGetScreen(&argc, argv, scrinfo.xres, scrinfo.yres, BITS_PER_SAMPLE, SAMPLES_PER_PIXEL, bytespp);
assert(server != NULL);
server->desktopName = "framebuffer";
server->frameBuffer = (char *)vncbuf;
server->alwaysShared = TRUE;
server->httpDir = NULL;
server->port = vnc_port;
//    server->kbdAddEvent = keyevent;
//    server->ptrAddEvent = ptrevent;

rfbInitServer(server);
/* Mark as dirty since we haven't sent any updates at all yet. */
rfbMarkRectAsModified(server, 0, 0, scrinfo.xres, scrinfo.yres);
/* No idea. */
varblock.r_offset = scrinfo.red.offset + scrinfo.red.length - BITS_PER_SAMPLE;
varblock.g_offset = scrinfo.green.offset + scrinfo.green.length - BITS_PER_SAMPLE;
varblock.b_offset = scrinfo.blue.offset + scrinfo.blue.length - BITS_PER_SAMPLE;
varblock.rfb_xres = scrinfo.yres;
varblock.rfb_maxy = scrinfo.xres - 1;
}
// sec
#define LOG_TIME    5
int timeToLogFPS() {
static struct timeval now={0,0}, then={0,0};
double elapsed, dnow, dthen;
gettimeofday(&now,NULL);
dnow  = now.tv_sec  + (now.tv_usec /1000000.0);
dthen = then.tv_sec + (then.tv_usec/1000000.0);
elapsed = dnow - dthen;
if (elapsed > LOG_TIME)
memcpy((char *)&then, (char *)&now, sizeof(struct timeval));
return elapsed > LOG_TIME;
}
/*****************************************************************************/
//#define COLOR_MASK  0x1f001f
#define COLOR_MASK  (((1 << BITS_PER_SAMPLE) << 1) - 1)
#define PIXEL_FB_TO_RFB(p,r_offset,g_offset,b_offset) ((p>>r_offset)&COLOR_MASK) | (((p>>g_offset)&COLOR_MASK)<<BITS_PER_SAMPLE) | (((p>>b_offset)&COLOR_MASK)<<(2*BITS_PER_SAMPLE))
static void update_screen(void)
{
#ifdef LOG_FPS
static int frames = 0;
frames++;
if(timeToLogFPS())
{
double fps = frames / LOG_TIME;
fprintf(stderr, "  fps: %f\n", fps);
frames = 0;
}
#endif
varblock.min_i = varblock.min_j = 9999;
varblock.max_i = varblock.max_j = -1;
uint32_t *f = (uint32_t *)fbmmap;        /* -> framebuffer         */
uint32_t *c = (uint32_t *)fbbuf;         /* -> compare framebuffer */
uint32_t *r = (uint32_t *)vncbuf;        /* -> remote framebuffer  */
int xstep = 4/bytespp;
int y;
for (y = 0; y < (int)scrinfo.yres; y++)
{
/* Compare every 1/2/4 pixels at a time */
int x;
for (x = 0; x < (int)scrinfo.xres; x += xstep)
{
uint32_t pixel = *f;
if (pixel != *c)
{
*c = pixel;
#if 0
/* XXX: Undo the checkered pattern to test the efficiency
* gain using hextile encoding. */
if (pixel == 0x18e320e4 || pixel == 0x20e418e3)
pixel = 0x18e318e3;
#endif
*r = PIXEL_FB_TO_RFB(pixel,
varblock.r_offset, varblock.g_offset, varblock.b_offset);
if (x < varblock.min_i)
varblock.min_i = x;
else
{
if (x > varblock.max_i)
varblock.max_i = x;
if (y > varblock.max_j)
varblock.max_j = y;
else if (y < varblock.min_j)
varblock.min_j = y;
}
}
f++;
c++;
r++;
}
}
if (varblock.min_i < 9999)
{
if (varblock.max_i < 0)
varblock.max_i = varblock.min_i;
if (varblock.max_j < 0)
varblock.max_j = varblock.min_j;
fprintf(stderr, "Dirty page: %dx%d+%d+%d...\n",
(varblock.max_i+2) - varblock.min_i, (varblock.max_j+1) - varblock.min_j,
varblock.min_i, varblock.min_j);
rfbMarkRectAsModified(server, varblock.min_i, varblock.min_j,
varblock.max_i + 2, varblock.max_j + 1);
rfbProcessEvents(server, 10000);
}
}
/*****************************************************************************/
void print_usage(char **argv)
{
fprintf(stderr, "%s [-f device] [-p port] [-h]\n"
"-p port: VNC port, default is 5900\n"
"-f device: framebuffer device node, default is /dev/fb0\n"
"-h : print this help\n"
, *argv);
}
int main(int argc, char **argv)
{
if(argc > 1)
{
int i=1;
while(i < argc)
{
if(*argv == '-')
{
switch(*(argv + 1))
{
case 'h':
print_usage(argv);
exit(0);
break;
case 'f':
i++;
strcpy(fb_device, argv);
break;
case 'p':
i++;
vnc_port = atoi(argv);
break;
}
}
i++;
}
}
fprintf(stderr, "Initializing framebuffer device %s...\n", fb_device);
init_fb();
fprintf(stderr, "Initializing VNC server:\n");
fprintf(stderr, "    width:  %d\n", (int)scrinfo.xres);
fprintf(stderr, "    height: %d\n", (int)scrinfo.yres);
fprintf(stderr, "    bpp:    %d\n", (int)scrinfo.bits_per_pixel);
fprintf(stderr, "    port:   %d\n", (int)vnc_port);
init_fb_server(argc, argv);
/* Implement our own event loop to detect changes in the framebuffer. */
while (1)
{
while (server->clientHead == NULL)
rfbProcessEvents(server, 100000);
rfbProcessEvents(server, 100000);
update_screen();
}
fprintf(stderr, "Cleaning up...\n");
cleanup_fb();
}

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-407982-1-1.html 上篇帖子: Linux常见目录及命令介绍 下篇帖子: 50个最常用的Linux命令
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表