Search code examples
gstreamerframebufferwayland

Specify a display for a sink in GStreamer


I'm trying to display a video stream on a specific screen. Right now I use the waylandsink that has display and fullscreen properties so I have:

gst-launch-1.0 videotestsrc ! waylandsink display=wayland-0 fullscreen=TRUE

It works fine. Then I check my display list using xrandr --listmonitors and I get:

Monitors: 2 
  +*XWAYLAND0 1920/508x1080/286+0+0  XWAYLAND0
  +XWAYLAND1 1920/508x1080/286+1920+0  XWAYLAND1

So I tried to replace wayland-0 by wayland-1 but the pipeline stops. I'm not sure if my display name is correct or how I should obtain it (as for now I took wayland-0 and simply incremented it). Or if it is possible to do that using waylandsink

Edit: I did a lot more research (but still not enough). First I became aware that waylandsink may not be what I'm looking for. Second is I didn't understand how rendering works in Linux (and it's still not quite clear).

But I found:

  • kmssink : was not able to make it work
  • dfbvideosink : was not installed
  • fbdevsink : Does not get 2D/3D hardware acceleration; Works fine but I have some problems (like not having another framebuffer for another display)
  • glimagesink : Did not find a way to specify a display to render

I'll keep searching...


Solution

  • I finally found! And it's the kmssink but I'll explain more why.

    First I talked about the waylandsink. The thing is I did not know was wayland is a protocol and it seems to only work for displaying content inside a Desktop Environment (DE). So I guess you could create windows for each display and then link your sink to those windows. But I was looking for a way to display without any DE so waylandsink is a not an option.

    For glimagesink from what I tried it's also in a DE so I did not explore more about it.

    Then there's the framebuffer using the fbdevsink. It works without DE, but suffers from limitations... There seems to be only one framebuffer situated in /dev/fb0 and what we draw in it is displayed on every screen regardless of the display resolution. So if we have 2 displays with different resolution we cannot do fullscreen without having some cropping on one of them. Plus we cannot display different video on each screen because the framebuffer is duplicated. Finally while I was testing it, I found out some time there were frames that were drawn at the same time into the framebuffer, so it was causing the video to have some weird visual. Maybe the issues I listed could have been fixed in some way but there are just too many so I discarded this option

    When I checked the documentation for the kmssink it says there are 2 main parameters I was interrested:

    • bus-id
    • connector-id

    By specifying the bus-id I though I could display on a specific screen. But all the displays use the same bus-id: 0000:00:02.0 so it's not the parameter to specify a display.

    Then there is connector-id. It's an integer and it can be used to specify the display. In my case it's 77 for HDMI-A-1 and 92 for HDMI-A-2.

    How do you get the connector-id? Well that's not simple... A command exists to get them and it's called modetest. The thing is it seems to be only included in some embedded devices. I found out that the command was included in the package libdrm but in my case installing it did not give me access to the command... I'm using GStreamer with Rust so by importing the drm package I was able to get a list of connector-id and a lot of data about displays.

    So in the end I can do:

    gst-launch-1.0 videotestsrc ! kmssink connector-id=77
    

    or:

    gst-launch-1.0 videotestsrc ! kmssink connector-id=92
    

    To display on the screen I want to. One last thing: kmssink need to be executed as root to work