做虚拟屏幕,为了把服务器上几个app映射到一个画面上,再把这个画面通过wifi显示到远程物理屏幕,特此研究了xvfb ffmpeg wmctrl 的配置。
xvfb 是 x 服务虚拟缓存,简单的说,就是把本该输出到物理屏幕的图像,输出到缓存里。这个缓存就是内存,并且自动映射到文件系统(一个文件)。具体参数:http://www.xfree86.org/4.0/Xvfb.1.html 。
做一个简单的测试,在ubuntu服务器上,新建两个虚拟屏幕:
sudo apt-get install xvfb firefox x11-apps imagemagick
这里 firefox x11-apps和 imagemagick只是用来方便测试。
Xvfb :1 -screen 0 600x840x16 -screen 1 1024x768x16 -fbdir /home/fu/ar/xvfb &
Display=:1.0 import -window root 0.png
(如图,因为暂时没有程序投影到这个桌面,所以是空桌面,600x800的全黑图);
Display=:1.1 import -window root 1.png
(同理,得到一个1024x768的全黑图)
现在制定虚拟屏幕运行firefox,因为只有x服务,并没有windows manager,所以理论上,一个x程序(所谓x程序,就是有界面的程序)应该非常原始的出现在指定屏幕的左上角。
Display=:1.0 firefox http://www.google.com &
Display=:1.1 xcalc &
看来没有窗口管理器是不行的,之后还要涉及到窗口的focus什么的,还是得装一个窗口管理器。这里选择openbox,因为强大的可配置性,以及极少的空间占用。
sudo apt-get install openbox
Xvfb :1 -screen 0 600x840x24 -screen 1 1024x768x24 -fbdir /home/fu/ar/xvfb &
sleep 3;
DISPLAY=:1.0 openbox &
DISPLAY=:1.1 openbox &
#DISPLAY=:1.0 fluxbox &
#DISPLAY=:1.1 fluxbox &
这里屏幕用24位彩色,是为了RGB都是3字节,和通用的图形软件,opencv,matlab什么的都对应起来。
这里屏幕用24位彩色,是为了RGB都是3字节,和通用的图形软件,opencv,matlab什么的都对应起来。
import 无法截openbox的屏,只有窗口框,里面是黑的。截fluxbox的屏幕没有任何问题。替代方法是用scrot来截屏,或者其他什么软件,就是不要用 import。
sudo apt-get install scrot
用ps kill 掉之前的程序,重新运行一遍:
DISPLAY=:1.0 xcalc &
DISPLAY=:1.1 xclock -digital -update 1 &
DISPLAY=:1.0 scrot 0.png
DISPLAY=:1.1 scrot 1.png
这下子可以顺利看到xvfb+openbox的实际情况了。可以看到,openbox默认是居中对齐。
也可以通过opencv,在程序里捕捉和显示各个桌面:
//testXwd.cpp
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "iostream"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <cstring>
#include <vector>
using namespace std;
using namespace cv;
IplImage *XImage2IplImageAdapter(XImage *ximage){
IplImage *iplImage;
assert(ximage->format == ZPixmap);
assert(ximage->depth == 24);
iplImage = cvCreateImageHeader(
cvSize(ximage->width, ximage->height),
IPL_DEPTH_8U,
ximage->bits_per_pixel/8);
iplImage->widthStep = ximage->bytes_per_line;
if(ximage->data != NULL)
iplImage->imageData = ximage->data;
return iplImage;
}
int main(int argc, char* argv[]){
int Width = 0;
int Height = 0;
int Bpp = 0;
std::vector<unsigned char> Pixels;
namedWindow("MyVideo",CV_WINDOW_AUTOSIZE);
Mat frame;
Display* display = XOpenDisplay(argv[1]);
Window root = DefaultRootWindow(display);
while(1){
XWindowAttributes attributes = {0};
XGetWindowAttributes(display, root, &attributes);
XImage* img = XGetImage(display, root, 0, 0 , attributes.width, attributes.height, AllPlanes, ZPixmap);
IplImage *cvImageSample = XImage2IplImageAdapter(img);
frame = Mat(cvImageSample);
imshow("MyVideo", frame);
if(waitKey(50) == 27) {
cout << "esc key is pressed by user" << endl;
break;
}
XDestroyImage(img);
}
XCloseDisplay(display);
return 0;
}
也可以通过opencv,在程序里捕捉和显示各个桌面:
//testXwd.cpp
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "iostream"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <cstring>
#include <vector>
using namespace std;
using namespace cv;
IplImage *XImage2IplImageAdapter(XImage *ximage){
IplImage *iplImage;
assert(ximage->format == ZPixmap);
assert(ximage->depth == 24);
iplImage = cvCreateImageHeader(
cvSize(ximage->width, ximage->height),
IPL_DEPTH_8U,
ximage->bits_per_pixel/8);
iplImage->widthStep = ximage->bytes_per_line;
if(ximage->data != NULL)
iplImage->imageData = ximage->data;
return iplImage;
}
int main(int argc, char* argv[]){
int Width = 0;
int Height = 0;
int Bpp = 0;
std::vector<unsigned char> Pixels;
namedWindow("MyVideo",CV_WINDOW_AUTOSIZE);
Mat frame;
Display* display = XOpenDisplay(argv[1]);
Window root = DefaultRootWindow(display);
while(1){
XWindowAttributes attributes = {0};
XGetWindowAttributes(display, root, &attributes);
XImage* img = XGetImage(display, root, 0, 0 , attributes.width, attributes.height, AllPlanes, ZPixmap);
IplImage *cvImageSample = XImage2IplImageAdapter(img);
frame = Mat(cvImageSample);
imshow("MyVideo", frame);
if(waitKey(50) == 27) {
cout << "esc key is pressed by user" << endl;
break;
}
XDestroyImage(img);
}
XCloseDisplay(display);
return 0;
}
//makefile
testXwd: testXwd.cpp
g++ -g testXwd.cpp -I/usr/local/include/ -L/usr/local/lib -lopencv_highgui -lopencv_core -lopencv_imgproc -fopenmp -o testXwd
//example
testXwd :1.1
testXwd: testXwd.cpp
g++ -g testXwd.cpp -I/usr/local/include/ -L/usr/local/lib -lopencv_highgui -lopencv_core -lopencv_imgproc -fopenmp -o testXwd
//example
testXwd :1.1
为了能够实时的观看某个程序,可以用ffmpeg x11grab来捕捉其所在的屏幕。因为我把每个程序放在单独的屏幕,所以捕捉屏幕等同于捕捉程序窗口。
ffmpeg -y -r 15 -f x11grab -s 1024x768 -i :1.1 out.avi
ffmpeg 会自动选择编码,打开录制的out.avi,窗口大小是对的,电子表也在走。
再测试通过网络发送到opencv:
ffmpeg -r 15 -f x11grab -s 1024x768 -i :0.0 -vcodec mpeg4 -qscale 12 -f mpegts udp://127.0.0.1:6666?pkt_size=188?buffer_size=65535
ffmpeg -r 20 -f x11grab -s 1024x768 -i :0.0 -vcodec mpeg4 -maxrate 900k -bufsize 900k -qscale 10 -f mpegts udp://127.0.0.1:6666
//testRtp.cpp
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "iostream"
using namespace std;
using namespace cv;
int main(int argc, char* argv[]){
VideoCapture cap("udp://127.0.0.1:6666");
if ( !cap.isOpened() ) {
cout << "Cannot open the video file" << endl;
return -1;
}
double fps = cap.get(CV_CAP_PROP_FPS);
cout << "Frame per seconds : " << fps << endl;
namedWindow("MyVideo",CV_WINDOW_AUTOSIZE);
Mat frame;
while(1){
bool bSuccess = cap.read(frame);
if (!bSuccess){
cout << "Cannot read the frame from video file" << endl;
break;
}
imshow("MyVideo", frame);
if(waitKey(30) == 27) {
cout << "esc key is pressed by user" << endl;
break;
}
}
return 0;
}
//makefile
testRtp: testRtp.cpp
g++ -g testRtp.cpp -I/usr/local/include/ -L/usr/local/lib -lopencv_highgui -lopencv_core -lopencv_imgproc -fopenmp -o testRtp
尽管只消耗500kbps的带宽,效果却非常不理想。






Aucun commentaire:
Enregistrer un commentaire