1.大二暑假在东软做了个项目“一起上课吧”,是一个linux下的电子教室系统。在局域网内使用,要做两个程序,一个教师端,一个学生端。主要功能是桌面共享、文件收发、即时通讯、监视学生机桌面、控制学生机的功能。用途主要用于教师授课、学生管理等,用c语言做的,使用gtk+来做的界面。
2.我主要负责屏幕共享这块,主要思想就是不断的截取屏幕图像,广播到其他机器上。
3.为了达到更好的效果,我使用了差位算法的思想,把整个屏幕分成若干个区域,每次只是把发生图像发生变化的区域发送出去。具体的怎么划分,划分多少块最好,是没有标准的,并不是说划分的越细就越好,因为划分,以及判断图像是否变化也需要消耗时间,这个时间和网络传输时间要找到一个最高效的点,我们当时的思路是,电子教室主要是用来讲课,演示ppt的,所以就模拟这个场景进行不断测试,最后得到一个效率最高的屏幕划分方法,竖着分8块横着分10块。
教师端:
主要是利用gtk库中对应的api首先获取屏幕截图,然后分块,每次把发生变化的发送出去(我只是通过计算图形二进制数据的大小来判断是否改变,基本没有问题。如果想更精确判断图形是否变化,则涉及到图形学里的分布取像素点的比较算法,这里不赘述)。
项目中相关具体代码如下:
while(1) //just send the blocks changed
{
gdk_threads_enter();
for(x=0;x<V_BLOCKS;x++)
{
start_x=block_height*x;// figure the block's x point
for(y=0;y<H_BLOCKS;y++)
{
start_y=block_wigth*y;// figure the block's y point
//get pixbuf of the scrren block
pixbuf= gdk_pixbuf_get_from_drawable (NULL, window,NULL,start_y,
start_x, 0, 0,block_wigth,block_height);
pixbuf_simple=gdk_pixbuf_scale_simple(pixbuf,100,100,GDK_INTERP_BILINEAR);
//GET the char data of picture
gdk_pixbuf_save_to_buffer(pixbuf_simple, &pic_buf,&pic_buf_size, "jpeg", NULL,
"quality","60", NULL);
if(old_size[x][y]!=pic_buf_size &&pic_buf_size<=BLOCK) //changed
{
old_size[x][y]=pic_buf_size;//save the char data's size
//set the data to the grid
send_grid.x=x;
send_grid.y=y;
send_grid.total_size=pic_buf_size;
memcpy(send_grid.data,pic_buf,pic_buf_size);
//send the grid
sendto(socket_fd, &send_grid, sizeof_grid, 0, (struct sockaddr *)&addr_teacher, len);
}
//free the memory
free(pic_buf);
g_object_unref (pixbuf_simple);
g_object_unref (pixbuf); //free pixbuf
}
}
gdk_threads_leave();
}
学生端:
就是不断的去接受数据,接收到数据后转成对应的图片格式填入到相应的屏幕位置。
我的项目中具体代码如下:
void * receive_pixbuf(void * arg)
{
int sizeof_grid=sizeof(grid); //grid's size
grid receive_grid={0}; //temp grid to save the data rececived
int x,y,start_x,start_y;
gint a, b;
int receive_num,socket_fd,len,maxfdp;
GdkPixbuf* pixbuf=NULL;
GdkPixbufLoader* pixloader;
struct sockaddr_in addr_client;
fd_set fds;
struct timeval timeout={5,0}; //select等待1秒,1秒轮询
socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
maxfdp=socket_fd+1;
if(socket_fd ==1)
{
printf("socket error");
}
len = sizeof(addr_client);
bzero(&addr_client, len);
addr_client.sin_family = AF_INET;
addr_client.sin_port = htons(PORT); //set port
addr_client.sin_addr.s_addr = htonl(INADDR_ANY); //set ipaddr
if(bind(socket_fd, (struct sockaddr *)&addr_client, len) < 0)
{
printf("bind error:");
}
while(1)
{
gdk_threads_enter();
FD_ZERO(&fds); //每次循环都要清空集合,否则不能检测描述符变化
FD_SET(socket_fd,&fds); //添加描述符
switch(select(maxfdp,&fds,NULL,NULL,&timeout)) //select使用
{
case 0:
break; //再次轮询
default:
if(FD_ISSET(socket_fd,&fds)) //测试sock是否可读,即是否网络上有数据
{
//receive the data and save to receive_grid
recvfrom(socket_fd, &receive_grid, sizeof_grid, 0,
(struct sockaddr *)&addr_client, (socklen_t *)&len);
pixloader=gdk_pixbuf_loader_new_with_type("jpeg",NULL);//init a pixloader
//get coordinate x and y
x=receive_grid.x;
start_x=BLOCK_HEIGHT*x;
y=receive_grid.y;
start_y=BLOCK_WIGTH*y;
//get pixbuf from pixloader
gdk_pixbuf_loader_write(pixloader,receive_grid.data, receive_grid.total_size, NULL);
pixbuf=gdk_pixbuf_loader_get_pixbuf(pixloader);
//show the pixture in imagebox
gtk_image_set_from_pixbuf(GTK_IMAGE(ourgif[x][y]),pixbuf);
//free the memory
g_object_unref(pixbuf);
gdk_pixbuf_loader_close (pixloader,NULL);
}// end if break;
}// end switch
gdk_threads_leave();
}
}
每块大小是64×64。测试阶段将变化块直接输出到图形文件(居然没删),win7下打开效果如下:
桌面的空白区域你懂得 Terminal 上的一小块变化区域
注:
@代码没有多少价值,主要是差位传输图像的思想比较有用。@所以这里没有贴出全部代码,一些变量的定义,函数的解释,和头文件中的一些宏定义也没列出。
@如果有兴趣,或则有需要可以联系我共同探讨。
PS:个人比较喜欢中文注释,但是当时在红帽上开发,中文输入法总是出问题,用着不爽,所以用了一些简单的英文注释。