Llamadas

Importante

Esta página detalla el principio de las cuentas Jami. Para las cuentas SIP, se utiliza el protocolo SIP.

¡Hagamos una llamada en Jami!

El lado del demonio

Al crear una llamada entre dos pares, Jami utiliza principalmente protocolos conocidos como ICE, SIP o TLS. Sin embargo, para que se distribuya, el proceso de creación de una llamada es un poco diferente. En resumen, cuando alguien quiera contactar a uno de sus contactos, esto es lo que hará:

  1. Buscar la presencia de contactos en el DHT (para más detalles, Gestión de contactos).

  2. Una vez encontrado el contacto, se envía una solicitud de llamada, anunciando los candidatos conocidos (la dirección IP de cada interfaz de red + direcciones de retransmisión (TURN) + direcciones reflexivas (UPnP, públicas).

  3. Espere la respuesta del contacto (responderá a sus direcciones conocidas).

  4. Negociar el enchufe a través de ICE. De hecho, se negocian dos sesiones de ICE. Una (preferida) en TCP, otra en UDP (como fallback).

  5. Luego, el enchufe se cifre en TLS (si TCP) o DTLS (si UDP).

  6. Ahora, el contacto puede aceptar o rechazar la llamada. Sí acepta, se negocia un transporte ICE (UDP solo por ahora) para crear 4 nuevos sockets para los medios (2 para audio, 2 para vídeo).

  7. ¡La llamada está viva ahora!

Cambio de candidatos a ICE

Todo realmente comienza en jamiaccount.cpp (JamiAccount::startOutgoingCall). Una vez que ambos objetos ICE están listos y cuando se encuentra el contacto a través del DHT, se crea la solicitud de llamada para el contacto. Esta solicitud contiene toda la información necesaria para la sesión ICE remota definida por:

dht::IceCandidates(callvid,  blob)

donde:

  • “callvid” es un número aleatorio utilizado para identificar la llamada, y

  • «blob” contiene dos mensajes ICE concatenados (`Ice Transport::pack Ice Msg “ en “ ice_transport.cpp”) que contiene la contraseña de la sesión, la * ufrag* y candidatos ICE como:

0d04b935
7c33834e7cf944bf0e367b42
H6e6ca382 1 UDP 2130706431 2607:fad8:4:6:9eb6:d0ff:dead:c0de 14133 typ host
H42c1g477 1 UDP 2130706431 fe80::9eb6:d0ff:fee7:1412 14133 typ host
Hc0a8027e 1 UDP 2130706431 192.168.0.123 34567 typ host
Sc0a8027e 1 UDP 1694498815 X.X.X.X 32589 typ srflx
0d04b932
7c33834e7cf944bf0e367b47
H6e6ca682 1 TCP 2130706431 2607:fad8:4:6:9eb6:d0ff:dead:c0de 50693 typ host tcptype passive
H6e6ca682 1 TCP 2130706431 2607:fad8:4:6:9eb6:d0ff:dead:c0de 9 typ host tcptype active
H42c1b577 1 TCP 2130706431 fe80::9eb6:d0ff:fee7:1412 50693 typ host tcptype passive
H42c1b577 1 TCP 2130706431 fe80::9eb6:d0ff:fee7:1412 9 typ host tcptype active
Hc0a8007e 1 TCP 2130706431 192.168.0.123 42751 typ host tcptype passive
Hc0a8007e 1 TCP 2130706431 192.168.0.123 9 typ host tcptype active
Sc0a8007e 1 TCP 1694498815 X.X.X.X 42751 typ srflx tcptype passive

y se envía a través del DHT en un mensaje cifrado para que el dispositivo hash(callto:xxxxxx) donde xxxxxx es el ID del dispositivo. El par responderá exactamente en el mismo lugar (pero encriptado para el dispositivo remitente) a sus propios “dht:: IceCandidates”. Para más detalles ver “JamiAccount:: replyToIncomingIceMsg`.

La sesión ICE se crea en ambos lados cuando tienen todos los candidatos (es decir, para el remitente, cuando se recibe la respuesta del contacto).

Negociación del ICE

Las llamadas pendientes las administra Jamie Account:: handle Pending Call List ()', que primero espera a que finalice la negociación TCP (y si falla, espera a la UDP). El código para la negociación ICE lo administra principalmente [pjproject] (https://github.com/pjsip/pjproject) pero para Jami, la parte interesante se encuentra en ' ice_transport.cpp. Además, agregamos algunos parches/características importantes sobre *pjproject * que no se fusionaron en sentido ascendente por ahora (por ejemplo, ICE sobre TCP). Estos parches están presentes en “contrib/src/pjproject`.

Encripta el control de la toma

Una vez que se crea y administra el socket por parte de una instancia de Ice Transport, se envuelve en un SipTransport correspondiente a un TlsIceTransport. El código principal se encuentra en “JamiAccount:: handle Pending Call() y el ajuste se realiza en 'Sip Transport Broker:: getTlsIceTransport' Finalmente, nuestra sesión es administrada por Sesión Tls en “daemon/src/security/tls_session.cpp “ y usa la biblioteca GnuTLS.

Por lo tanto, el socket de control será un TLS (1.3 si usted y la versión GnuTLS de su par lo admiten) si se negocia un socket TCP. Si se negocia un socket UDP en su lugar (debido a restricciones de firewall/problemas en la negociación/etc.), el socket usará DTLS (aún administrado por las mismas partes).

El socket de control se usa para transmitir paquetes SIP, como invitaciones, mensajes personalizados (Jami envía la vCard de su perfil en este socket al inicio de la llamada o al girar la cámara) y mensajes de texto.

Artículos relacionados:

Sockets de medios

Los sockets para sonido vídeo son sockets SRTP donde la clave se negocia a través de la sesión TLS creada previamente.

Advertencia

TODO: Esta sección está incompleta.

Arquitectura

Advertencia

TODO: Esta sección está incompleta.

El sistema de transmisión de datos

Desde la versión 13.3.0 del demonio, la transmisión múltiple es totalmente compatible. Esta función permite a los usuarios compartir varios vídeos durante una llamada al mismo tiempo. En las siguientes partes, describiremos todos los cambios relacionados.

PJSIP

La primera parte es negociar suficientes flujos de medios. De hecho, cada flujo de medios usa 2 sockets UDP. Consideramos tres escenarios:

  1. Si es el anfitrión de una conferencia quien quiere agregar medios, no hay nada más que negociar, porque ya mezclamos los vídeos en una transmisión. Entonces, agregamos los nuevos medios directamente al mezclador de vídeo sin negociaciones.

  2. Si estamos en 1:1, por ahora, ya que no hay información de conferencia, multi-stream no es compatible.

  3. De lo contrario, se negocian dos nuevos sockets para nuevos medios.

Para hacer PJSIP capaz de generar más sockets por el ICE de la sesión, PJ_ICE_COMP_BITS se modificó a \(5\) (lo que corresponde a \(2^5\), entonces \(32\) streams).

Deprecar el interruptorInputo, solicitud de soporteMediaCambio

En el daemon, la antigua API switchInput ahora es DEPRECATED; igual para switchSecondaryInput:

<method name="switchInput" tp:name-for-bindings="switchInput">
    <tp:docstring>
        Switch input for the specified call
    </tp:docstring>
    <arg type="s" name="accountId" direction="in"/>
    <arg type="s" name="callId" direction="in"/>
    <arg type="s" name="input" direction="in"/>
    <arg type="b" direction="out" />
</method>

<method name="switchSecondaryInput" tp:name-for-bindings="switchSecondaryInput">
    <tp:added version="11.0.0"/>
    <tp:docstring>
        Switch secondary input for the specified conference
    </tp:docstring>
    <arg type="s" name="accountId" direction="in" />
    <arg type="s" name="conferenceId" direction="in"/>
    <arg type="s" name="input" direction="in"/>
    <arg type="b" direction="out" />
</method>

requestMediaChange sustituye a esto, tanto para las llamadas como para las conferencias:

<method name="requestMediaChange" tp:name-for-bindings="requestMediaChange">
    <tp:added version="11.0.0"/>
    <tp:docstring>
        <p>Request changes in the media of the specified call.</p>
    </tp:docstring>
    <arg type="s" name="accountId" direction="in" />
    <arg type="s" name="callId" direction="in">
        <tp:docstring>
        The ID of the call.
        </tp:docstring>
    </arg>
    <annotation name="org.qtproject.QtDBus.QtTypeName.In2" value="VectorMapStringString"/>
    <arg type="aa{ss}" name="mediaList" direction="in">
        <tp:docstring>
        A list of media attributes to apply.
        </tp:docstring>
    </arg>
    <arg type="b" name="requestMediaChangeSucceeded" direction="out"/>
</method>

Compatibilidad

Si se realiza una llamada con un par donde la versión del demonio es < 13.3.0, no se habilita la transmisión múltiple y se usa el comportamiento anterior( solo 1 vídeo).

Identificación de flujo

Debido a que ahora puede haber múltiples flujos, cada flujo de medios se identifica por su identificador, y el formato es «_»; por ejemplo: «audio_0», «video_2», etc.

Rotación

El XML se actualizó para agregar el flujo buscado:

<?xml version="1.0" encoding="utf-8" ?>
<media_control>
  <vc_primitive>
    <stream_id>{}</stream_id>
    <to_encoder>
      <device_orientation>0</device_orientation>
    </to_encoder>
  </vc_primitive>
</media_control>

Cuadro de teclas

El XML se actualizó para agregar el flujo buscado:

<?xml version="1.0" encoding="utf-8" ?>
<media_control>
  <vc_primitive>
    <stream_id>{}</stream_id>
    <to_encoder><picture_fast_update/></to_encoder>
  </vc_primitive>
</media_control>

Actividad de voz

Se actualizó XML para agregar la secuencia requerida:

<?xml version="1.0" encoding="utf-8" ?>
<media_control>
  <vc_primitive>
    <stream_id>{}</stream_id>
    <to_encoder>
      <voice_activity>true</voice_activity>
    </to_encoder>
  </vc_primitive>
</media_control>

Conferencia

Los cambios reflejados se documentan [aquí] (conferencia-protocolo).

Cliente

Incluso si el back-end admite hasta 32 medios al mismo tiempo, excepto para clientes personalizados, actualmente recomendamos solo permitir compartir una cámara y un vídeo al mismo tiempo. La cámara se controla a través del botón de la cámara y los demás medios a través del botón «Compartir».

En client-qt, la parte interesante está en “AvAdapter” (métodos como 'isCapturing', shareAllScreens”,` stopSharing”). En la lógica de la biblioteca, “addMedia” y “removeMedia” en “callModel” usan directamente “requestMediaChange”, y se pueden usar como referencia de diseño.