通過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抓圖,效果不錯(見下圖),後面再做介紹。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章