通过framebuffer获取桌面

这两个月一直在捣腾如何利用输出桌面到SDI卡,远程输出到显示器上显示。弄了一个多月framebuffer,虽然最后没有采用这个方案,但在折腾的过程中学习到很多,未免遗忘,正好记下。


开启framebuffer。

参考这篇文章中我成功的开启了framebuffer(我的ubuntu是10.04版,12.04只成功设置过一次,原因待查)。

设置系统的分辨率和framebuffer的分辨率一致,不然会导致花屏或程序崩溃。


安装SDL。

利用SDL我们用来输出桌面查看效果。安装SDL库只需安装下面几个包:

sudo apt-get install libsdl-image1.2-dev
sudo apt-get install libsdl-mixer1.2-dev
sudo apt-get install libsdl-ttf2.0-dev
sudo apt-get install libsdl-gfx1.2-dev

安装完后,我们就可以在程序中调用SDL库了,编译中加上-lSDL。


获取framebuffer数据并同步到SDL上显示。

打开framebuffer设备并映射到系统内存:

        unsigned char* fb_mem;

        int fbfd = open("/dev/fb0", O_RDWR);<span style="white-space:pre">	</span>//打开framebuffer
        if (fbfd < 0)
                printf("open /dev/fb0 failed.\n");

        fb_mem = mmap(NULL, WIDTH * HEIGHT * 4, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0); //映射到内存, WIDTH HEIGHT BBP 为宽高

映射framebuffer到内存,我们直接通过显示这个地址的数据,即可得到桌面画面。

初始化SDL,建立宽高和桌面以上的SDL_Surface

        SDL_Surface* screen;
        SDL_Event event;

        int keypress = 0;

        if (SDL_Init(SDL_INIT_VIDEO) < 0)
        {
                printf("SDL_Init failed.\n");
                return -1;
        }

        if (!(screen = SDL_SetVideoMode(WIDTH, HEIGHT, 32, SDL_HWSURFACE)))
        {
                SDL_Quit();
                printf("SDL_Quit failed.\n");
                return -1;
        }
最后通过不断获得framebuffer数据刷新SDL,即可实现同步桌面。

void DrawScreen(SDL_Surface* screen, unsigned  char * fb)
{
        memcpy(screen->pixels, fb, WIDTH * HEIGHT * BPP);
        SDL_Flip(screen);
}

源码:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <linux/kd.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <string.h>
#include <SDL/SDL.h>

#define WIDTH 1280
#define HEIGHT 720
#define DEPTH 32
#define BPP 4

void DrawScreen(SDL_Surface* screen, unsigned  char * fb)
{
	memcpy(screen->pixels, fb, WIDTH * HEIGHT * BPP);
	SDL_Flip(screen);
}

int main()
{
	unsigned char* fb_mem;

	int fbfd = open("/dev/fb0", O_RDWR);
	if (fbfd < 0)
		printf("open /dev/fb0 failed.\n");
	
	fb_mem = mmap(NULL, WIDTH * HEIGHT * 4, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0);

	SDL_Surface* screen;
	SDL_Event event;
	
	int keypress = 0;
	
	if (SDL_Init(SDL_INIT_VIDEO) < 0)
	{
		printf("SDL_Init failed.\n");
		return -1;
	}
	
	if (!(screen = SDL_SetVideoMode(WIDTH, HEIGHT, 32, SDL_HWSURFACE)))
	{
		SDL_Quit();
		printf("SDL_Quit failed.\n");
		return -1;
	}

	while(!keypress)
	{
		DrawScreen(screen, fb_mem);
		while(SDL_PollEvent(&event))
		{
			switch(event.type)
			{
				case SDL_QUIT:
					keypress = 1;
					break;
				case SDL_KEYDOWN:
					keypress = 1;
					break;
			}
		}
	}
	return 0;
}

在10.04上,同步桌面非常流畅,但是视频不能同步过去,视频部分变成透明,可能由于视频播放并不是通过framebuffer显示的原因。

在12.04上,视频可以同步过去,但是画面却没有那么流畅了,会出现撕裂的情况(google tearing)。同样是memcpy,在10.04上为1-2ms,而在12.04上却要50+ ms,这可能就是造成撕裂的主要原因,因为memcpy的时候过慢,framebuffer已经刷新了好几次了。

这可能是由于不同版本的linux实现framebuffer的机制不一样造成的吧。

ps.由于项目必须用12.04,通过framebuffer不能达到要求,故弃之,我采用xlib抓图,效果不错(见下图),后面再做介绍。


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值