Jibri as SIP VIDEO Gateway, Incoming call configuration questions


I can’t determine exactly how media communication is configured between pjsua and jibri and ffmpeg.

What i have:

  1. Jitsi Meet works
  2. Jibri on different node up and running.
  3. With http api a call to JIbri for start service and Jibri run pjsua (build from jibri-2.10-dev1 ) with own config in /etc/jitsi/jibri/pjsua.config , and join to room after call with sip video client to pjsua
  4. From tcpdump i can see that media flow have in both side and both direction with videobridge (p2p disable)

But no audio\video from Jibri participant and no audi\video on sip client, and JIbri participant have muted audio and camera icons

From browser.0.txt i see - “”[modules/RTC/RTCUtils.js]" “Failed to get access to local media. NotFoundError: Requested device not f…”"

From pjsua.log i see:
“v4l2_dev.c …Video4Linux2 has 0 devices”
“sdl_dev.c !SDL_Init() error: No available video device”
“alsa_dev.c …open_playback: Open playback device ‘surround71:CARD=Loopback,DEV=0’”
" alsa_dev.c …open_capture: Open capture device ‘surround41:CARD=Loopback,DEV=0’"
“pjsua_media.c …pjsua_vid_channel_update() failed for call_id 0 media 1: Invalid video device (PJMEDIA_EVID_INVDEV)”

From log.0.txt:
“AbstractPageObject.visit#32: Visiting url https://meettest.example.com/t10#config.iAmRecorder=true&config.iAmSipGateway=true&config.ignoreStartMuted=true&config.analytics.disabled=true&config.enableEmailIn

from /tmp/chromedriver.log i can see running options for chrome:
“args”: [ “–use-fake-ui-for-media-stream”, “–start-maximized”, “–kiosk”, “–enabled”, “–autoplay-policy=no-user-gesture-required”, “–alsa-input-device=plughw:1,1” ]

So, my questions is:

  1. How in pjsua.config properly determine --capture-dev, --playback-dev and --vcapture-dev ?
  2. –alsa-input-device=plughw:1,1 - it is correct for chrome run or need to change ?
  3. where is and how is ffmpeg involved (i want to try h264 and ulaw from sip video client)?
  4. What triggering devices.videoInput=“PJSUA” when Jibri join to room?

i had messing around a week with it … please help

after “options snd-aloop enable=1,1 index=0,1” to /etc/modprode.d/alsa-loopback.conf
have no muted audio icons for jibri (after this Looback_1 is appear in /proc/asound/), but still no audio

THis suggests you didn’t create the v4l2loopback device with the right name. IIRC you need to have 1 called “Chrome” and the other one “PJSUA”.

first time I hear about it
Is this in the JIbri documentation somewhere?

Unfortunately not, the SIP Jibri part is not very well documented…

I find that:


  • Starting a [SipGatewayServiceParams] involves the following steps:

    1. Start selenium and join the web call on display :0
    1. Start the SIP client to join the SIP call on display :1
  • There are already ffmpeg daemons running which are capturing from

  • each of the displays and writing to video devices which selenium

  • and pjsua will use


So, i need:

  1. Intsall v4l2loopback-utils and configure 2 Video loopback devices named PJSUA and Chrome
  2. ffmpeg daemons which i must configured myself to capture from display:0 to --vcapture-dev (Chrome video loopback ) of pjsua and capture from display:1 to devices.videoInput=“PJSUA” (PJSUA video loopback)
  3. properly set for --capture-dev, --playback-dev, where capture = plughw: … Loopback.DEV =1 and playback = Loopback_1,DEV =0 , (chrome use plughw:1,1 for input, and it is hardcoded?)
  4. any else?

Who run Xorg on display :1 ? I need do it myself?

Yep, all of that sounds about right! I think you can change the audio device config now in the jibri config file.

Thanks a lot! i got a working configuration. Lot of configurations finded from docker-jitsi-meet/jibri at dev · netaskd/docker-jitsi-meet · GitHub

@slider.xtm great job on getting it to work. Would you consider writing a tutorial/guide here? There’s been quite a bit of interest on this functionality expressed in the community; I’m certain your guide will help some people.

Ansible polaybooks set is used to deploy the jibri virtual machine, but i can`t publish them.

I will briefly describe the main points included in these playbooks (i use google translate, excuse me):

  1. seems to be, load for one sip video session with video\audio h264\ulaw <-> vp8\opus codecs need take 8 CPU and 3-4 Gb RAM
  2. Ubuntu 20.04.4 LTS kernel 5.4.0-107-generic
  3. Install packages [‘mc’, ‘bind9’, ‘traceroute’, ‘apt-transport-https’, ‘ca-certificates-java’, ‘debconf-utils’, ‘ncdu’, ‘open-vm-tools’, ’ git’, ‘lua-cjson’, ‘lua-luaossl’]
  4. else [‘openjdk-8-jre’, ‘openjdk-8-jdk’, ‘maven’]
  5. and more [‘linux-image-extra-virtual’, ‘ffmpeg’, ‘libv4l-0’, ‘libgl1-mesa-dri’, ‘unclutter’, ‘psmisc’ ]
  6. and more [‘default-jre-headless’, ‘curl’, ‘alsa-utils’, ‘icewm’, ‘xdotool’, ‘xserver-xorg-video-dummy’]
  7. change /usr/share/alsa/alsa.conf removing surrounds and sysdefault - this was done, because without this, ALSA gave out more than 32 devices, but by default it does not automatically pick up more than 32, I removed what was superfluous. As a result, I get 24 devices
  8. load the snd-aloop module with the addition of options: options snd-aloop enable=1,1 index=0,1
  9. Install chrome from deb [arch=amd64 signed-by=/usr/share/keyrings/google-chrome-keyring.gpg] http://dl.google.com/linux /chrome/deb/ stable main
  10. Here - /etc/opt/chrome/policies/managed/managed_policies.json should be: {CommandLineFlagSecurityWarningsEnabled": false }
  11. Download the desired version of chromedriver: CHROME_DRIVER_VERSION=curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE && wget -N http://chromedriver.storage.googleapis.com/$CHROME_DRIVER_VERSION/chromedriver_linux64.zip
    unpack to /usr/local/bin/
  12. replace the link /usr/bin/google-chrome with the script , it is taken from here, but something went wrong with running ffmpeg and I moved this part to systemd unit

shopt -s huponexit
#set -o pipefail
#set -o errexit

_term() {
  local SIGNAL=$1
  if [ ! -z "${CHILD}" ]; then
   kill -${SIGNAL} "${CHILD}"
  exit 111

trap "_term SIGABRT" SIGABRT
trap "_term SIGTERM" SIGTERM
trap "_term SIGKILL" SIGKILL
trap "_term SIGQUIT" SIGQUIT
trap "_term SIGSTOP" SIGSTOP
trap "_term STOP" STOP

# start ffmpeg cross capturing X displays to video devices
/usr/bin/ffmpeg -re -f x11grab -r 60 -s 1920x1080 -i :0 -f x11grab -r 60 -s 1920x1080 -i :1 \
  -map 0 -vcodec rawvideo -pix_fmt yuv420p -threads 0 -f v4l2 /dev/video1 \
  -map 1 -vcodec rawvideo -pix_fmt yuv420p -threads 0 -f v4l2 /dev/video0 2> /tmp/ffmpeg-jibri.log &

wait ${CHILD} || exit 1

exit 0

put as /usr/local/bin/ffmpeg-jibri.sh to run ffmpeg in systemd

  1. install [‘v4l2loopback-utils’]
  2. Load v4l2loopback module with options: options v4l2loopback devices=2 video_nr=0, 1 exclusive_caps=1,1 card_label=“PJSUA,Chrome” (neither chrome nor pjsua will see these capture devices until these devices broadcast something in the appropriate format, which is ensured by running ffmpeg BEFORE chrome and pjsua start)
  3. Add jitsi repository - deb [signed-by=/usr/share/keyrings/jitsi-keyring.gpg] https://download.jitsi.org stable/
  4. Install packages [‘jibri’, ‘jitsi-upload-integrations’, ‘jq’]
  5. Add jibri to groups adm,audio,video,plugdev,users
  6. Copy your jibri config
my ansible template for jibri config
jibri {
  // A unique identifier for this Jibri
  // TODO: eventually this will be required with no default
  id = ""
  // Whether or not Jibri should return to idle state after handling
  // (successfully or unsuccessfully) a request.  A value of 'true'
  // here means that a Jibri will NOT return back to the IDLE state
  // and will need to be restarted in order to be used again.
  single-use-mode = false
  api {
    http {
      external-api-port = 2222
      internal-api-port = 3333
    xmpp {
      // See example_xmpp_envs.conf for an example of what is expected here
      environments = [
                name = "test environment"
                xmpp-server-hosts = ["{{ jms_domain }}"]
                xmpp-domain = "{{ jms_domain }}"

                control-muc {
                    domain = "internal.auth.{{ jms_domain }}"
                    room-name = "JibriBrewery"
                    nickname = "{{ inventory_hostname_short }}"

                control-login {
                    domain = "auth.{{ jms_domain }}"
                    username = "jibri-sip"
                    password = "{{ jibri_sip_password }}"

                call-login {
                    domain = "jibri-sip.{{ jms_domain }}"
                    username = "jibri-sip"
                    password = "{{ jibri_sip_password }}"

                // An (optional) MUC configuration where we'll
                // join to announce SIP gateway services
                sip-control-muc {
                    domain = "conference.{{ jms_domain }}"
                    room-name = "TheSipBrewery"
                    nickname = "{{ inventory_hostname_short }}"

                strip-from-room-domain = "conference."
                usage-timeout = 0
                trust-all-xmpp-certs = true
  recording {
    # recordings-directory = "/srv/recordings"
    # TODO: make this an optional param and remove the default
    # finalize-script = "/srv/recordings/finalize_recording.sh"
  streaming {
    // A list of regex patterns for allowed RTMP URLs.  The RTMP URL used
    // when starting a stream must match at least one of the patterns in
    // this list.
    rtmp-allow-list = [
      // By default, all services are allowed

  ffmpeg {
    resolution = "1920x1080"
    // The audio source that will be used to capture audio on Linux
    audio-source = "alsa"
    // The audio device that will be used to capture audio on Linux
    audio-device = "plug:bsnoop"

  chrome {
    // The flags which will be passed to chromium when launching
    flags = [
      // "--alsa-input-device=plug:asnoop"
  stats {
    enable-stats-d = true
  webhook {
    // A list of subscribers interested in receiving webhook events
    subscribers = []
  jwt-info {
    // The path to a .pem file which will be used to sign JWT tokens used in webhook
    // requests.  If not set, no JWT will be added to webhook requests.
    //# signing-key-path = "/path/to/key.pem"

    // The kid to use as part of the JWT
    # kid = "key-id"

    // The issuer of the JWT
    # issuer = "issuer"

    // The audience of the JWT
    # audience = "audience"

    // The TTL of each generated JWT.  Can't be less than 10 minutes.
    # ttl = 1 hour
  call-status-checks {
    // If all clients have their audio and video muted and if Jibri does not
    // detect any data stream (audio or video) comming in, it will stop
    // recording after NO_MEDIA_TIMEOUT expires.
    no-media-timeout = 3 minutes

    // If all clients have their audio and video muted, Jibri consideres this
    // as an empty call and stops the recording after ALL_MUTED_TIMEOUT expires.
    all-muted-timeout = 10 minutes

    // When detecting if a call is empty, Jibri takes into consideration for how
    // long the call has been empty already. If it has been empty for more than
    // DEFAULT_CALL_EMPTY_TIMEOUT, it will consider it empty and stop the recording.
    default-call-empty-timeout = 30 seconds

    // If ICE hasn't completed, or stays in a state other than "connected" for this amount of time, Jibri will stop.
    ice-connection-timeout = 30 seconds

  1. Modify /opt/jitsi/jibri/pjsua.sh to get away from invalid --id format when pjsua starts
my pjsua template

/usr/bin/sudo -E /usr/local/bin/pjsua --config-file /etc/jitsi/jibri/pjsua.config --id="sip:{{ inventory_hostname_short }}@{{ inventory_hostname }}" --auto-answer-timer=60  --auto-answer=200 > /dev/null

#exec /usr/local/bin/pjsua --config-file /etc/jitsi/jibri/pjsua.config "$@"

  1. Copy your pjsua.config (can get from here but need to change for playback-dev and capture-dev)
  2. add a restart of the jibri service to /opt/jitsi/jibri/finalize_sip.sh because in some undefined cases, strange errors occur after the call is completed and a new call is not accepted
  3. In case of custom cert signed your local CA, add your CA to chrome - certutil -d sql:/home/jibri/.pki/nssdb -A -t TC -n “Custom CA” -i /usr/local/share/ca-certificates/custom_ca.pem.crt (before that, you need to run chrome at least once to create this certificate database)
  4. I add systemd units to run under the jibri user:
  • pjsua-xorg: /usr/bin/Xorg -nocursor -noreset +extension RANDR +extension RENDER -logfile /var/log/jitsi/jibri/pjsua xorg.log -config /etc/jitsi/jibri/xorg-video-dummy-pjsua.conf :1 with Environment=DISPLAY=:1
  • ffmpeg-jibri: /usr/local/bin/ffmpeg-jibri.sh
  • pjsua-icewm: /usr/bin/icewm-session --notray --display=:1 with Environment=DISPLAY=:1
  1. Install packages before compiling pjsua: [‘build-essential’, ‘libv4l-dev’, ‘libsdl2-dev’, ‘libavcodec-dev’, ‘libavdevice-dev’, ‘libavfilter-dev’, ‘libavformat-dev’, ’ libavresample-dev’, ‘libavutil-dev’, ‘libswresample-dev’, ‘libswscale-dev’, ‘libasound2-dev’, ‘libopus-dev’, ‘libvpx-dev’, ‘libsdl-dev’]
  2. Compiling GitHub - jitsi/pjproject: PJSIP fork used in Jibri https://github.com/jitsi/jibri version jibri-2.10-dev1
  3. Copy pjsip-apps/bin/pjsua-x86_64-unknown-linux-gnu as /usr/local/bin/pjsua
  4. Copy pjsip-apps/bin/pjsystest-x86_64-unknown-linux-gnu as /usr/local/bin/pjsystest
  5. I launch pjsua via sudo because there was an error Unable to increase thread priority, root access needed when running under the jibri user, (although this may have been due incorrectly specified the device id in pjsua the config.), in any case, need to run pjsystest same way as pjsua to find out the correct playback-id and capture-id, on this screenshot is --capture-dev=15 and --playback-dev=22:
Alsa device list screenshot


  1. about Jibri it`s all

To initiate a jibri session on an incoming call, I added an asterisk setup to the JMS with the following call instructions:

Asterisk Dial plan
exten => test1,1,Dial(SIP/test1,30)
exten => test2,1,Dial(SIP/test2,30)
exten => test3,1,Dial(SIP/test3,30)
exten => test4,1,Dial(SIP/test4,30)

exten => jibri-sip1,1,Dial(SIP/jibri-sip1@jibri-sip1.example.com:5060,30)

exten => h,1,Hangup()
exten => s,1,Hangup()

exten => _.,1,Verbose("Run Call Jibri jibri-sip1 for ROOM = ${EXTEN}")
exten => _.,n,AGI(call_jibri_api.sh,${EXTEN},jibri-sip1.example.com)
exten => _.,n,Wait(5)
exten => _.,n,Dial(SIP/jibri-sip1@jibri-sip1.example.com:5060,30)

Thus , it is enough to have an sip account on this asterisk and call the desired conference room.
Script start Jibri session for called room via http api:

ansible template script for start Jibri call session


if [ "$JITSI_ROOM" = "" ]; then 

if [ "$JIBRI_NODE" = "" ]; then 

SESSION_ID=`shuf -i 1000000-9999999 -n 1`

curl -s --header 'Content-Type: application/json' --request 'POST' --data '{"sessionId": "'"$SESSION_ID"'","callParams": { "callUrlInfo": {"baseUrl": "https://{{ jms_domain }}", "callName": "'"$JITSI_ROOM"'" } }, "sinkType": "gateway", "sipClientParams": { "sipAddress": "'"sip:$JIBRI_NODE_NAME@$JIBRI_NODE"'", "autoAnswer": true } }' http://$JIBRI_NODE:2222/jibri/api/v1.0/startService

it`s works.


@slider.xtm Thanks for sharing!

Hello, i was following your steps to install and test the Jibri.
1 I enabled the JWT of jitsi-meet, deploy a service for searchpeople request in add contacts dialog.[because i can not search room list if i didn’t enable JWT]
2 Install the jibri service, the recording works normal.
3 when i add people from the searchpeople service, i can get the list and choose one to invite, it is failed.
4 i checked the console log and get below errors:

Logger.js:154 2022-07-07T08:05:26.421Z [modules/videosipgw/JitsiVideoSIPGWSession.js] Failed to start video SIP GW session, error:

5 i checked logs of jicofo below:

Jicofo 2022-07-07 16:05:26.474 WARNING: [62] JibriIqHandler.handleRequest#66: Jibri IQ not accepted by any conference:

6 i have no idea about that, maybe there is something wrong in config of jicofo? could you share your config to me please ?

Thank your so much for your work.

@saghul @slider.xtm

Did you deploy Jibris configured to act as SIP video gws?

@saghul Hi, i think i fixed the problem last time, for now i can invite a sip account into the conference, but i am confused the right process. i will write down the steps below, could you check and modify it for me please ?

1, send the invite to someone which is in searchpeople service.
2, jicofo send the IQ to jibri and jibri start the sipgate way process.
3, jibri start a chrome process to login into the conference
4, jibri start to exec the pjsua.sh and call he target sip account.
5, ffmpeg record the video and audio of chrome and pjsua process and forward to each other ?? not sure it.

Is there more detail document about this ?

Thank you very much.

Yes, that’s how it happens! We presented the architecture at OpenSIPS Summit last year, here is the video: Conference, Day 5 - YouTube and slides: https://www.opensips.org/events/Summit-2021Distributed/assets/presentations/2021-oana-ianc-raluca-toth-saul-ibarra-corretge-sip-jibri-production.pdf

Many thanks for your reply.

Could you give more points about how to setup different DISPLAY :0 and :1, and how to run the Chrome and Pjsua into different display? is there any config file to setup it ?

i checked @slider.xtm tips, but there are many steps i didn’t understand.

For now, i did these steps:

1 Installed alsa drivers, chrome drivers , google chrome. pjsua and those packages needed.
2 Added and loaded 2 alas audio devices and 2 video devices labeled with chrome and pjsua. [but don’t know how to determe right capture and playback id]
3 as @slider.xtm said, we need to use the google-chrome.replace to replace the orig one and i did it.
4 setup the policies json file for chrome.
5 in defalut, the jibri installs a jibri-xorg service and jibri-icewm in systemd with DISPLAY :0 , i also created new Xorg-pjsua and icewm-pjsua with DISPLAY :1
6 compile the pjsua from the jitsi repo

1 it seems that pjsua doesn’t support --video by default, should i compile it with some parameter?
2 Did i miss something else?

Hope to get your help.

Thank you very much.


@oianc Got some tips handy?

i think the key point is how to setup the right DISPLAY , Video and Audio devices, and make ffmpeg to forward video and audio correctly.

Thanks for your work and help.

Have a nice day.

step 28.

No, it`s already configured with video support in forked repo (jitsi/pjproject), If I am not mistaken.

Dev ID reslult:
Here is my Audio Device List, which one is right ? and how to find the right video-capture id ?
Found 32 devices
0: ALSA [default:CARD=Loopback] (1/1)
1: ALSA [sysdefault:CARD=Loopback] (1/1)
2: ALSA [front:CARD=Loopback,DEV=0] (1/1)
3: ALSA [surround21:CARD=Loopback,DEV=0] (1/1)
4: ALSA [surround40:CARD=Loopback,DEV=0] (1/1)
5: ALSA [surround41:CARD=Loopback,DEV=0] (1/1)
6: ALSA [surround50:CARD=Loopback,DEV=0] (1/1)
7: ALSA [surround51:CARD=Loopback,DEV=0] (1/1)
8: ALSA [surround71:CARD=Loopback,DEV=0] (1/1)
9: ALSA [dmix:CARD=Loopback,DEV=0] (0/1)
10: ALSA [dmix:CARD=Loopback,DEV=1] (0/1)
11: ALSA [dsnoop:CARD=Loopback,DEV=0] (1/0)
12: ALSA [dsnoop:CARD=Loopback,DEV=1] (1/0)
13: ALSA [hw:CARD=Loopback,DEV=0] (1/1)
14: ALSA [hw:CARD=Loopback,DEV=1] (1/1)
15: ALSA [plughw:CARD=Loopback,DEV=0] (1/1)
16: ALSA [plughw:CARD=Loopback,DEV=1] (1/1)
17: ALSA [default:CARD=Loopback_1] (1/1)
18: ALSA [sysdefault:CARD=Loopback_1] (1/1)
19: ALSA [front:CARD=Loopback_1,DEV=0] (1/1)
20: ALSA [surround21:CARD=Loopback_1,DEV=0] (1/1)
21: ALSA [surround40:CARD=Loop

Thanks for your help.