這兩個月一直在搗騰如何利用輸出桌面到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抓圖,效果不錯(見下圖),後面再做介紹。