EDIT: Before I published this, I used to use obs.ninja / vdo.ninja instead.. At the time I didn't know it was an open source project! VDO.ninja does most of all the same things I mentioned here with no modification, and in my experience it does give better video quality and more control. However, I was never able to get it to automatically highlight the active speaker, so for now, I'm going to keep using Jitsi Meet.

Back when I started streaming, I didn't have a webcam on my workstation setup, so I was looking for alternative options to set up a facecam.

OBS can get video input from webcams, but it can also has all kinds of other input types. One of them is called "Browser", and it opens up a web view, like a browser window, inside OBS. You can configure it to load any web page, and OBS will display the rendered HTML + any audio output from that page.

So Jitsi Meet is the first thing I tried. It's an open/self-hostable alternative to Google Hangouts. Jitsi had worked pretty well in my experience, however it hadn't worked with OBS when I first tried it. It would prompt the browser embedded in OBS to enter a username before it would join the call. I think that at the time, either I didn't realize I could use OBS to interact with the embedded browser (Right-click the browser source, choose "Interact", enter a name in the input field and click OK) or else it didn't work when I tried.

However, recently, just out of curiosity, I tried again with a newer version of Jitsi Meet, and it worked! The OBS embedded browser was able to get into the Jitsi call, no prompt, no problem, the only issue was, it kinda looked like crap:

But not to worry! Even though I could not inject JavaScript into the page to clean it up, there was in fact a Custom CSS setting on the OBS Browser Input. I started messing around with this.

First, I would open up the Jitsi Meet web application in Firefox, and then use the Firefox browser debugger to inspect each one of these elements and figure out what attributes it had, like id, class, etc. I could use these attributes to write a style sheet which would hopefully clean up the view.

Before long, I had come up with the following custom style sheet:



#subject-container,
#notifications-container, 
#new-toolbox,
.watermark,
body .filmstrip  .filmstrip__videos.remote-videos .videocontainer:not(.display-video),
.videocontainer .remotevideomenu,
.videocontainer .videocontainer__toptoolbar,
.videocontainer .videocontainer__toolbar {
  display: none;
}
body .filmstrip  .filmstrip__videos.remote-videos .videocontainer.display-video {
    left: 3px !important;
    top: 3px !important;
    width: 98% !important;
    height: 98% !important;
    border-left: 3px solid cyan;
    border-right: 3px solid orange;
    border-top: 3px solid #55ff00;
    border-bottom: 3px solid blue;
    border-radius: 13px !important;
}
body .filmstrip  .filmstrip__videos.remote-videos .videocontainer.display-video .videocontainer__background,
body .filmstrip  .filmstrip__videos.remote-videos .videocontainer.display-video video {
    border-radius: 11px;
}
body .filmstrip  .filmstrip__videos.remote-videos .videocontainer.display-video.active-speaker {
    z-index: 1000;
}

document, body, html, div {
    overflow: hidden;
}

body, .cLnhlo, #largeVideoContainer {
  background-color: transparent !important;
}

First of all, I had to hide all of the notifications, watermark, header and footer, etc, so we can actually see the video.

Second, I had to override the dynamically specified positions and widths of the video tiles so that one video would take up the whole screen. Finally, I set it so that whoever jitsi thinks is the "active speaker" in the call will have thier video flying on top of all the others.

Finally, I had to set overflow: hidden on the page as a whole to avoid getting scroll bars covering part of the video tile.

It looked great! I even tested it with multiple computers in the call, and the active-speaker feature looked fairly nice.

However, I wasn't done yet.

Getting the audio to work properly was an excercise in frustration, especially because of the unique constraints imposed by my borderline masochistic frugality: I would rather figure out how to do without a webcam than consume more "next products".

So for me, I was using my phone as a webcam. At first I tried using the phone to record audio for the jitsi meeting. I liked this solution because it didn't require me me to interact with the OBS embedded browser at all; if the the Jitsi meeting was the only source of my voice in OBS, the same audio input could safely be used for both the stream audio and the conversation inside the jitsi meeting.

However, I ran into a few issues with this. First of all, the Jitsi Meet android app I have does not support muting the call. It does not respect the system volume settings (at least on LineageOS) and it does not have any volume settings of its own.

Second of all, my nice quality USB mic doesn't work with my phone, I have to keep the phone plugged into the charger while streaming. So I briefly tried plugging a set of earbuds into the phone. This worked ok, but it required me to switch off mics between streaming and other uses, plus the earbud mic was not nearly as nice as the Antlion Audio USB clip on mic that I normally use (no, they are not paying me to say that , I just like it 😊. It feels a lot less planned-obsolescence-y than any other solution I've found).

The next evolution involved more manual work to set up the stream and somewhat broke the active-speaker thing, but I think I might stick with it, because it allowed me to use my nice mic for both the stream and the Jitsi call.

The plan was this:

All I had to do to achieve this was:

  • mute the mic on the phone
  • open the jitsi meeting on the streaming PC in audio-only mode

The only problem now being the active-speaker feature is slightly broken: The streamer's video can't be the active speaker because they are muted. I might have to add some additional CSS to ensure the streamer's video appears on top of any guests' video tiles by default. I'm not sure if there is an automatic way to do this, there's no CSS class that gets applied to the participant element of the meeting host or anything convenient like that. I can however manually create a CSS selector that selects a specific participant:

#participant_abc06b92 {
  z-index: 500;
}

The only problem is I would have to manually update this selector every time I set up the stream. But whatever, it's darn close, close enough for me to start using it regularly 😃

Comments