在Linux上开发HDMI端口

7
如何让应用程序独占地驱动HDMI输出,而不允许操作系统自动为显示输出进行配置?
例如,将标准的DVI/VGA作为主显示器,但使用设备文件将Mplayer视频输出发送到HDMI。
这是一个通过谷歌很难回答的问题。几乎每一个结果都与使音频在HDMI上工作有关。
下面的评论提到了使用单独的Xorg服务器。虽然这是一个有用的想法,但它没有回答我问过的一个问题,以及我暗示的一个问题:
1)如果它比其他显示器先加载,或者如果它是唯一的显示器(仅使用SSH进行登录),如何防止Linux希望将控制台放在那个显示器上? 2)如果没有X呢?我想直接将图形驱动到适配器。我能否使用标准功能从代码中执行此操作,而无需直接与驱动程序交互(可能已过时,但使用SVGALib或其他非X图形层)?
我查看了SVGALib(它很老)和SDL。后者在X内外都可以使用,甚至可以访问OpenGL。我通过某个论坛链接找到了1.3版本,但网站和FTP似乎只有1.2。总体而言,SDL是一个很好的解决方案,但它具有以下两个特定的缺点:
1)一般的创建设备调用接受设备索引,但完全忽略它:
(src/video/bwindow/SDL_bvideo.cc)
BE_CreateDevice(int devindex)

司机特定的调用似乎有相同的缺陷。例如,DirectFB(我认为在控制台下提供图形):
(src/video/directfb/SDL_DirectFB_video.c)
DirectFB_CreateDevice(int devindex)

这两个函数的主体似乎都没有现有的位置来设置设备索引...毫无疑问,这是因为它们所构建的标准接口缺乏支持。

2) 在任何被选中的适配器上,SDL似乎会自动将所有显示器连接起来。例如“testsprite2.c”(随库附带),可以接受“--display”参数,该参数在“common.c”(所有示例的共同功能)中进行处理。您可以看到,“--display”参数所做的就是计算该屏幕在一个大的组合画布中的X/Y坐标:

if (SDL_strcasecmp(argv[index], "--display") == 0) {
    ++index;
    if (!argv[index]) {
        return -1;
    }
    state->display = SDL_atoi(argv[index]);
    if (SDL_WINDOWPOS_ISUNDEFINED(state->window_x)) {
        state->window_x = SDL_WINDOWPOS_UNDEFINED_DISPLAY(state->display);
        state->window_y = SDL_WINDOWPOS_UNDEFINED_DISPLAY(state->display);
    }
    if (SDL_WINDOWPOS_ISCENTERED(state->window_x)) {
        state->window_x = SDL_WINDOWPOS_CENTERED_DISPLAY(state->display);
        state->window_y = SDL_WINDOWPOS_CENTERED_DISPLAY(state->display);
    }
    return 2;
}

所以,如果两个显示器在同一个适配器上,就没有办法将其中一个显示器与另一个隔离开来。SDL无法实现。

除非有类似于SDL的可比较解决方案,或者在适当的位置(这可能不是情况,并且因此很可能是未实现的原因)设置特定设备(devindex)变得微不足道,否则独占并完全专用于屏幕的最佳选项似乎是编写自己的窗口管理器,在分配给第二个设备的单独Xorg实例下运行。


HDMI端口由硬件设备(例如:显卡)控制。为了使其正常工作,您需要与硬件进行通信,这意味着它是特定于供应商的。直接在操作系统上与复杂的硬件进行交互是一项困难的任务,无论是在内核空间还是用户空间,特别是如果您的设备同时被操作系统(用于DVI / VGA)和您的程序(用于HDMI)使用。 - netcoder
完全不是。我不介意与驱动程序或甚至更普遍的操作系统进行交流。我只是在寻找一种方法,使它不会立即被声明为控制台显示设备,以便我可以从代码中独占地访问它。 - Dustin Oprea
1
Python 如何适用于这项任务超出了我的理解范围。 - Ulterior
这里同时标记了Python和C,因为我想吸引开发人员的注意,而不仅仅是管理员,因为这是我对这个问题感兴趣的地方。 - Dustin Oprea
1
配置并启动一个额外的Xorg服务器以在HDMI端口上显示... - Basile Starynkevitch
SDL的最新版本可以通过Mercurial在http://hg.libsdl.org/SDL获取。 - Dustin Oprea
1个回答

5
你可以直接写入帧缓冲设备/dev/fb(假设您的控制台默认使用它)。为了防止控制台显示在其中,请禁用所有虚拟终端(您只能远程登录)。如果您有多个适配器,则应该获得多个帧缓冲设备(这需要确认)。
这里有一个在帧缓冲上绘制矩形的C示例: 通过Linux FrameBuffer将像素绘制到屏幕上
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <sys/ioctl.h>

int main()
{
    int fbfd = 0;
    struct fb_var_screeninfo vinfo;
    struct fb_fix_screeninfo finfo;
    long int screensize = 0;
    char *fbp = 0;
    int x = 0, y = 0;
    long int location = 0;

    // Open the file for reading and writing
    fbfd = open("/dev/fb0", O_RDWR);
    if (fbfd == -1) {
        perror("Error: cannot open framebuffer device");
        exit(1);
    }
    printf("The framebuffer device was opened successfully.\n");

    // Get fixed screen information
    if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo) == -1) {
        perror("Error reading fixed information");
        exit(2);
    }

    // Get variable screen information
    if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo) == -1) {
        perror("Error reading variable information");
        exit(3);
    }

    printf("%dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);

    // Figure out the size of the screen in bytes
    screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;

    // Map the device to memory
    fbp = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0);
    if ((int)fbp == -1) {
        perror("Error: failed to map framebuffer device to memory");
        exit(4);
    }
    printf("The framebuffer device was mapped to memory successfully.\n");

    x = 100; y = 100;       // Where we are going to put the pixel

    // Figure out where in memory to put the pixel
    for (y = 100; y < 300; y++)
        for (x = 100; x < 300; x++) {

            location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) +
                       (y+vinfo.yoffset) * finfo.line_length;

            if (vinfo.bits_per_pixel == 32) {
                *(fbp + location) = 100;        // Some blue
                *(fbp + location + 1) = 15+(x-100)/2;     // A little green
                *(fbp + location + 2) = 200-(y-100)/5;    // A lot of red
                *(fbp + location + 3) = 0;      // No transparency
        //location += 4;
            } else  { //assume 16bpp
                int b = 10;
                int g = (x-100)/6;     // A little green
                int r = 31-(y-100)/16;    // A lot of red
                unsigned short int t = r<<11 | g << 5 | b;
                *((unsigned short int*)(fbp + location)) = t;
            }

        }
    munmap(fbp, screensize);
    close(fbfd);
    return 0;
}

只要您拥有构建工具以及系统的头文件,它就应该可以编译。为了刺激一下,可以从SSH中运行它,并观看它在您未登录的物理屏幕上绘制。
需要注意的是,除了X11之外,还有许多针对帧缓冲区的工具,但它们不会直接访问帧缓冲区。相反,它们通过一个名为DirectFB的附加抽象层进行操作。DirectFB将允许相同的应用程序在X11内外运行... 包括MPlayer、GStreamer、任何集成SDL(调用DirectFB)的应用程序,以及一个轻量级、流行的虚假X11容器XDirectFB(我认为它可以运行X11应用程序,但不像典型的窗口管理器那样过于繁琐)。

据我了解,帧缓冲区是内核中内置的最佳可用选项,当没有硬件加速时,然后有DirectFB,它将使用帧缓冲、硬件加速、OpenGL等,具体取决于您的需求和可用情况。请参见上面的最后一段。 - Dustin Oprea

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接