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:个人比较喜欢中文注释,但是当时在红帽上开发,中文输入法总是出问题,用着不爽,所以用了一些简单的英文注释。