xvfb ffmpeg wmctrl 配置

做虚拟屏幕,为了把服务器上几个app映射到一个画面上,再把这个画面通过wifi显示到远程物理屏幕,特此研究了xvfb ffmpeg wmctrl 的配置。

1. Xvfb
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什么的都对应起来。

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;
}
//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

2. Ffmpeg 

为了能够实时的观看某个程序,可以用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