Кодеки и согласование медиа

Коротко о главном

  • Кодеки звонка согласуются обменом SDP по модели offer/answer (RFC 3264): одна сторона предлагает список форматов (offer), другая отвечает выбранными из него (answer).

  • Платформа старается не транскодировать: при передаче предложения на другое плечо известные платформе форматы из входящего SDP остаются впереди в своем порядке, дополнительные кодеки добавляются после них (кроме --* и предложения esg внутрь платформы — см. opts.payloads_audio).

  • Состав и порядок дополнительных кодеков задает параметр роли payloads_audio_offer. Базовые кодеки PCMU/8000, PCMA/8000 и telephone-event/8000 предлагаются всегда, если их не исключить явно (кроме предложения esg внутрь платформы, плеча провайдера с opts.payloads_audio и audio_formats серверов записи).

  • У провайдера список кодеков opts.payloads_audio необязателен, а если задан — точный: на плече провайдера предлагаются только указанные кодеки в указанном порядке. Применяется только при обслуживании медиа через медиашлюз bgmg.

  • Ответ, отправляемый платформой, содержит множество кодеков или один кодек в зависимости от параметра allow_ac_set.

  • Видео в SIP-звонках платформа пробрасывает, как правило, без декодирования.

Термины

Плечо (leg)

Участок вызова между двумя узлами. Каждый микросервис сигнализации на пути вызова обслуживает два плеча — входящее и исходящее. Вызов внутреннего абонента на провайдера (абонент → sg → b2b → esg → провайдер) имеет 4 плеча сигнализации. Медийных плеч может быть столько, сколько медиашлюзов в цепочке обработки — от 0 до 3, в зависимости от задач и настроек.

Формат (fmt, payload type, PT)

Номер, под которым кодек указан в строке m= SDP. Номера 0–95 статические и закреплены за кодеками (0 — PCMU, 8 — PCMA, 18 — G729 и т.д.), 96–127 динамические и связываются с кодеком атрибутом a=rtpmap.

Терминейшен

Точка подключения потока плеча к медиашлюзу. У каждого медийного плеча свой терминейшен со своими local и remote SDP.

Транскодинг

Перекодирование аудио из одного кодека в другой медиашлюзом. В зависимости от сочетания кодеков может требовать дополнительных ресурсов: например, перепаковка между PCMU и PCMA табличная и почти ничего не стоит, а кодирование в G729 или opus заметно дороже.

Поддерживаемые кодеки

Имена кодеков в параметрах указываются в формате Name/Freq, регистр не важен.

Кодек PT по умолчанию Примечание

PCMU/8000

0

G.711 μ-law. Базовый

PCMA/8000

8

G.711 A-law. Базовый

GSM/8000

3

G722/8000

9

Широкополосный

CN/8000

13

Комфортный шум. Не используется как основной кодек

G729/8000

18

speex/8000, speex/16000, speex/32000

97, 98, 99

Динамический

telephone-event/8000

101

Сигналы DTMF по RFC 2833/4733. Базовый. Не используется как основной кодек

G726-16/8000, G726-24/8000, G726-32/8000, G726-40/8000

102–105

Динамический

iLBC/8000

106

Динамический

opus/48000/2

107

Динамический

Видео: H263/90000 (34), H263-1998/90000, H264/90000, VP8/90000, VP9/90000 (динамические).

Неизвестные платформе аудиоформаты отбрасываются, когда SDP обрабатывается для медиашлюза с транскодингом, неизвестные имена в параметрах игнорируются. Неизвестные видеоформаты не отбрасываются: видео пробрасывается, см. Видео.

Базовые кодеки

PCMU/8000, PCMA/8000 и telephone-event/8000 предлагаются всегда, даже если не указаны в payloads_audio_offer (кроме предложения esg внутрь платформы, плеча провайдера с opts.payloads_audio и audio_formats серверов записи). Так разные роли одного экземпляра платформы с разными настройками (например, ivr3 и b2b2) всегда имеют общий кодек и не отвечают друг другу финальным неуспешным ответом 415 Unsupported Media Type.

Исключить базовый кодек можно явно: -PCMU/8000 — не добавлять PCMU, -* — не добавлять ни один неуказанный базовый кодек.

Параметры

payloads_audio_offer, payloads_video_offer (роли)

Параметры ролей b2b, conf, esg, ivr, prompt, sg. Применяются, когда экземпляр роли формирует предложение (offer): создает новый исходящий вызов или передает предложение входящего вызова на другое плечо. sg применяет параметр только к SDP ответа на OPTIONS.

Элемент Значение

Name/Freq

Кодек предлагается; положение в SDP — по месту в списке

-Name/Freq

Кодек не добавляется (обычно базовый)

-*

Не добавляется ни один неуказанный базовый кодек

--Name/Freq

Кодек не добавляется и исключается из SDP входящего вызова, передаваемого на другое плечо

--*

Как -*; кроме того, при передаче входящего вызова на другое плечо в предложение попадают только кодеки, указанные в списке, в его порядке (telephone-event/8000 и CN/8000 — в конце), — остальные кодеки входящего SDP исключаются

Итоговый список: сначала указанные кодеки в порядке списка, затем неуказанные и не исключенные базовые. CN/8000 и telephone-event/8000 размещаются в конце. Если параметр не задан или пуст, предлагаются только базовые кодеки.

Для видео базовых кодеков нет: список задает состав и порядок.

opts.payloads_audio (провайдер)

Поле opts.payloads_audio провайдера.

  • Медиа не обслуживается (поле провайдера media выключено, bgmg не используется): кодеки к вызовам провайдера не применяются, SDP проходит через esg без изменений. Поле должно быть пустым.

  • Медиа обслуживается через bgmg, поле задано: на плече провайдера предлагаются только указанные кодеки в указанном порядке, медиашлюз при необходимости транскодирует; telephone-event/8000 добавляется в конец, если не исключен элементом -telephone-event/8000 или -*. Список действует только на плече провайдера. Вызов, re-INVITE или INVITE с Replaces от провайдера принимается, если в его SDP есть кодек из списка, иначе esg отвечает 488 Not Acceptable Here (re-INVITE отклоняется без разрыва вызова). Ответ провайдеру содержит кодеки списка из его предложения в порядке списка; при opts.allow_ac_set = false — один: тот, что уже использует другое плечо, иначе первый по списку. Если INVITE пришел без SDP, предложение внутрь строится по списку. Предложение внутрь платформы содержит кодеки провайдера из списка в порядке провайдера (telephone-event/8000 — если не исключен), поэтому транскодинг к устройствам выполняет медиашлюз b2b, а не bgmg. Кодеки списка должны поддерживаться медиашлюзами mg (параметр codecs), через которые обслуживаются вызовы b2b. Внутренние плечи списком не ограничиваются: если в предложении изнутри нет кодеков списка, транскодирует bgmg.

  • Медиа обслуживается через bgmg, поле не задано: esg передает SDP без изменений, медиашлюз пропускает медиа без транскодинга — это снижает нагрузку на bgmg. Если в INVITE, re-INVITE или INVITE с Replaces нет ни одного известного платформе основного кодека, esg отвечает 488 Not Acceptable Here (re-INVITE отклоняется без разрыва вызова). payloads_audio_offer экземпляра esg применяется только в двух случаях:

    • поле провайдера reinvite имеет значение 0: re-INVITE не пересылаются, на них отвечает сам esg, медиашлюз может транскодировать. С учетом payloads_audio_offer строятся INVITE к провайдеру и INVITE к новой цели при переводе на стороне провайдера (REFER), который обслуживает esg; предложение провайдера внутрь платформы передается с его кодеками в его порядке (неизвестные платформе форматы отбрасываются). Ответ esg на re-INVITE и INVITE с Replaces содержит только кодеки предложения; если общих нет — 488 Not Acceptable Here, re-INVITE отклоняется без разрыва вызова;

    • INVITE пришел без SDP: esg сам формирует предложение на другое плечо по payloads_audio_offer.

Порядок списка провайдера намеренно не подстраивается под порядок входящего SDP: список из нескольких кодеков может выражать предпочтение (например, G729/8000 для экономии полосы, PCMA/8000 — запасной). Поэтому возможен транскодинг: внутреннее устройство предложило PCMA, а провайдер выбрал первый в списке G729. Если транскодинг на медиашлюзе нежелателен, поставьте первым кодек, который используют внутренние устройства (например, ["PCMA/8000", "G729/8000"]), или укажите только его.

allow_ac_set

Параметр роли b2b (по умолчанию true) и поле провайдера opts.allow_ac_set (по умолчанию false, применяется при bgmg). Определяет, сколько кодеков платформа указывает в своем ответе (answer) на предложение:

  • true — подмножество известных платформе кодеков предложения; впереди — кодеки, выбранные противоположной (вызываемой) стороной в ее ответе, если они есть в предложении (ответ esg провайдеру со списком — в порядке списка);

  • false — один кодек: первый по порядку предложения, который есть и в ответе противоположной стороны; если таких нет — первый основной кодек предложения. telephone-event/8000 и CN/8000 из предложения добавляются к нему (ответ esg провайдеру со списком — см. opts.payloads_audio).

Подробнее — RFC 3264 и allow_ac_set.

Как строится SDP

Новый исходящий вызов

Роль формирует предложение сама, без входящего SDP: исходящий вызов из сценария IVR, вызов участника конференции, служебные вызовы. Список строится по payloads_audio_offer экземпляра роли.

Table 1. Примеры (исходящий вызов из IVR, строка m=audio)
payloads_audio_offer Форматы в SDP

не задан

0 8 101

["PCMA/8000", "PCMU/8000", "G722/8000", "CN/8000"]

8 0 9 13 101

["PCMA/8000", "PCMU/8000"]

8 0 101

["G722/8000", "PCMA/8000"]

9 8 0 101

["G722/8000", "PCMA/8000", "-PCMU/8000"]

9 8 101

["G722/8000", "PCMA/8000", "-*"]

9 8

["PCMA/8000", "telephone-event/8000", "G722/8000", "--*"]

8 9 101 — telephone-event/8000 в конце независимо от места в списке; в новом вызове --* действует как -*

["PCMA/8000", "-*"]

8 — только PCMA, без DTMF по RFC 2833 (см. DTMF без telephone-event)

Для вызовов на серверы записи SIPREC и потоковой записи состав кодеков можно задать отдельно — полем audio_formats сервера в настройках домена siprec_options, mixed_call_rec_options, streamed_call_rec_options. Если поле задано, предлагаются ровно указанные кодеки в порядке списка (telephone-event/8000 и CN/8000 — в конце, если указаны), payloads_audio_offer не применяется; если не задано — предложение строится по payloads_audio_offer экземпляра b2b.

Передача предложения на другое плечо

Роль получает INVITE с SDP и передает предложение на другое плечо (b2b; esg — см. opts.payloads_audio). Порядок:

  1. известные платформе форматы входящего SDP — в их порядке (кроме исключенных --);

  2. затем недостающие кодеки по payloads_audio_offer — в порядке итогового списка;

  3. CN/8000 и telephone-event/8000 — в конце.

Входящие форматы остаются впереди, потому что вызываемая сторона обычно выбирает первый подходящий формат. Если она выберет любой из форматов, предложенных вызывающей стороной, медиашлюзу не нужно транскодировать: потоки в этом формате передаются между плечами без изменений.

Table 2. Примеры (входящий SDP: m=audio …​ 8 0 101)
payloads_audio_offer экземпляра b2b Форматы в предложении на другое плечо

["G729/8000"]

8 0 18 101

["PCMU/8000", "PCMA/8000"]

8 0 101 — порядок входящих форматов не меняется

["PCMU/8000", "PCMA/8000", "telephone-event/8000", "--*"]

0 8 101 — с --* предложение строится в порядке списка

["PCMA/8000", "G722/8000", "-PCMU/8000"]

8 0 9 101 — - не исключает входящий PCMU

["G722/8000", "PCMA/8000", "--PCMU/8000"]

8 9 101

["PCMA/8000", "-*"]

8 0 101

["G722/8000", "--*"]

9 — медиашлюз транскодирует PCMA ↔ G722

Ответ

Ответ (answer) содержит только форматы из предложения, на которое он отвечает: статические — по номеру, динамические — по имени кодека. Количество — по allow_ac_set.

Table 3. Пример (плечо A предложило 8 0 101; b2b передал на плечо B предложение 8 0 18 101, добавив G729 по payloads_audio_offer)
Ответ плеча B Ответ b2b плечу A при allow_ac_set = true при allow_ac_set = false

8 101 (PCMA)

8 0 101

8 101

0 8 101

0 8 101

8 101

18 101 (G729, которого нет в предложении A)

0 8 101

8 101

Если кодек, выбранный плечом B, есть в предложении A, транскодинга нет. Иначе медиашлюз транскодирует.

Номера динамических форматов

В ответе динамические форматы указываются под номерами из предложения, на которое он отвечает (RFC 3264, раздел 6.1). В предложении, передаваемом на другое плечо, номера динамических форматов входящего SDP сохраняются; добавляемые динамические кодеки получают свободные номера, не занятые во входящем SDP. Если стороны объявили для одного кодека разные номера, медиашлюз сопоставляет форматы по имени кодека: каждая сторона получает поток с номерами из своего SDP.

Пример
Входящее предложение:
m=audio 30000 RTP/AVP 8 96 101
a=rtpmap:96 opus/48000/2
a=rtpmap:101 telephone-event/8000

Предложение на другое плечо (payloads_audio_offer = ["G722/8000", "speex/16000"]):
m=audio 40000 RTP/AVP 8 96 9 98 0 101
a=rtpmap:8 PCMA/8000
a=rtpmap:96 opus/48000/2
a=rtpmap:9 G722/8000
a=rtpmap:98 speex/16000
a=rtpmap:0 PCMU/8000
a=rtpmap:101 telephone-event/8000

Сценарии вызовов

Исходящий из IVR через провайдера

IVR → b2b → esg → провайдер.

  1. IVR формирует новое предложение по своему payloads_audio_offer.

  2. b2b передает его на плечо esg, добавляя кодеки по своему payloads_audio_offer.

  3. esg на плече провайдера: если у провайдера задан opts.payloads_audio (и используется bgmg) — только его кодеки в его порядке; иначе SDP передается без изменений, а при reinvite = 0 — дополняется по payloads_audio_offer esg (см. opts.payloads_audio).

  4. Провайдер отвечает; ответ на каждом плече содержит кодеки из предложения этого плеча, медиашлюз соединяет потоки, при необходимости транскодируя.

codecs_ivr_provider

Вызов между абонентами

Устройство → sg → b2b → sg → устройство (sg может быть одним и тем же экземпляром или разными).

sg при использовании bgmg не меняет кодеки, пропуская INVITE через себя, и работает без транскодинга. b2b передает предложение вызывающего устройства вызываемому, добавляя кодеки по payloads_audio_offer; входящие форматы остаются впереди. Если вызываемое устройство выбирает любой из форматов вызывающего, транскодинга нет.

codecs_device_device

С устройства на провайдера

Устройство → sg → b2b → esg → провайдер. Пример: устройство не поддерживает telephone-event/8000 (передает DTMF через SIP INFO), у провайдера opts.payloads_audio не задан.

b2b дополняет предложение базовым telephone-event/8000, esg передает SDP провайдеру без изменений (при reinvite = 0 — дополняет по payloads_audio_offer esg). Ответ b2b устройству содержит только кодеки его предложения, без telephone-event/8000. Сигналы DTMF провайдера по RFC 2833 b2b передает устройству через SIP INFO и обратно (см. DTMF без telephone-event).

codecs_device_provider

Входящий от провайдера

Провайдер → esg → b2b → …​

  • У провайдера задан opts.payloads_audio (bgmg): esg принимает вызов, только если в SDP провайдера есть кодек из списка (иначе — 488 Not Acceptable Here), и отвечает кодеками из пересечения: множеством или одним по opts.allow_ac_set. Предложение дальше в платформу содержит кодеки провайдера из списка в порядке провайдера, b2b дополняет его по своему payloads_audio_offer.

  • Не задан: esg передает предложение провайдера в платформу без изменений (при reinvite = 0 — без неизвестных платформе форматов); b2b дополняет его по своему payloads_audio_offer. Ответ провайдеру содержит кодеки из его предложения.

Разные роли одного экземпляра платформы

Роли одного экземпляра платформы могут быть настроены по-разному, например ivr3 с ["G722/8000"] и b2b2 с ["G729/8000"]. Благодаря базовым кодекам у них всегда есть общий кодек, и вызов не завершается финальным неуспешным ответом 415. Исключая базовые кодеки (-PCMU/8000, -*), не лишним будет убедиться, что у всех ролей экземпляра остается общий кодек.

sg и esg при использовании bgmg

  • sg не меняет кодеки: INVITE проходит через sg со своим SDP, транскодинга на sg нет.

  • esg действует по opts.payloads_audio провайдера: если список задан, он применяется к SDP плеча провайдера, внутрь передаются кодеки провайдера из списка, при необходимости выполняется транскодинг; если не задан — предложение провайдера передается внутрь с его кодеками, а payloads_audio_offer экземпляра esg применяется к предложениям провайдеру при reinvite = 0 и к INVITE без SDP.

DTMF без telephone-event

Сигналы DTMF передаются в RTP по RFC 2833 (формат telephone-event/8000) или сообщениями SIP INFO (application/dtmf-relay). b2b перепаковывает их между плечами:

  • SIP INFO с одного плеча передается на другое как RFC 2833, если в SDP другого плеча есть telephone-event/8000, иначе — как SIP INFO;

  • событие RFC 2833 с одного плеча передается на другое как SIP INFO, если в SDP другого плеча нет telephone-event/8000.

Поэтому исключение telephone-event/8000 (-telephone-event/8000, -*) не лишает вызов DTMF, если устройство или провайдер принимает SIP INFO.

RFC 3264 и allow_ac_set

RFC 3264 допускает обмен множествами кодеков и в предложении, и в ответе:

  • предложение перечисляет форматы в порядке предпочтения (раздел 5.1); предложившая сторона должна быть готова принимать любой из них;

  • ответ должен содержать хотя бы один формат из предложения; рекомендуется отвечать подмножеством форматов предложения, в том же относительном порядке, с теми же номерами динамических форматов (раздел 6.1);

  • в ответ можно включить и форматы, которых не было в предложении, но в текущем сеансе они не используются, пока не появятся в новом предложении;

  • в течение сеанса стороны могут переключаться между согласованными форматами без нового обмена SDP.

На практике большинство устройств и провайдеров отвечают одним кодеком из предложения. Встречаются реализации, которые некорректно обрабатывают ответ множеством кодеков: например, устройство воспроизводит тишину, когда поток приходит в согласованном, но не первом кодеке ответа. Для сохранения работоспособности связки платформы с такими устройствами и провайдерами платформа поддерживает ответ одним кодеком: allow_ac_set = false (у провайдера это значение по умолчанию). Параметр задается для экземпляра роли b2b и для провайдера; для отдельной учетной записи абонента он не задается.

Медиашлюз: tubes

Медиашлюз получает для терминейшена каждого медийного плеча local и remote SDP. local приходит как шаблон: адреса, порты и специфические атрибуты подставляет сам медиашлюз.

Между терминейшенами противоположных плеч строятся tubes — пути из каждого формата одного плеча в форматы другого, в обе стороны. Пакет RTP находит вход по номеру формата (PT) и проходит через tube в противоположный поток: без перекодирования, если формат совпадает (номер формата — из SDP принимающей стороны), или с перекодированием. Tubes строятся так, чтобы транскодирования было как можно меньше: совпадающие форматы соединяются напрямую.

Поэтому смена кодека стороной в пределах согласованного множества, допустимая по RFC 3264, обслуживается без нового обмена SDP: пакеты другого формата просто идут через другой tube.

codecs_tubes

Видео

Видео в SIP-звонках платформа не записывает, поэтому, как правило, пробрасывает его между плечами без декодирования: форматы видео, в том числе неизвестные платформе, передаются как есть. Транскодинг видео выключен по умолчанию (параметр use_video_transcoding). payloads_video_offer задает состав и порядок видеоформатов в формируемых предложениях; базовых видеокодеков нет.

В настоящее время для видеосвязи в основном используются сервисы видео- и веб-конференций, в том числе сервис meet на борту платформы (ВКС), поэтому акцент развития смещен туда.

Частые вопросы

Почему в INVITE кодеки идут в другом порядке, чем в payloads_audio_offer?

В предложении, передаваемом на другое плечо, впереди остаются форматы входящего SDP в их порядке; список настройки задает порядок добавляемых кодеков. Для нового вызова (IVR, конференция) порядок совпадает с настройкой, CN/8000 и telephone-event/8000 — в конце.

Как предлагать только PCMA?

["PCMA/8000", "-*"]. При передаче предложения на другое плечо входящие форматы сохраняются; чтобы оставить только PCMA и там, используйте ["PCMA/8000", "--*"].

Как убрать telephone-event?

-telephone-event/8000 в payloads_audio_offer или в opts.payloads_audio провайдера. DTMF при этом передается SIP INFO, см. DTMF без telephone-event.

Почему вызов отклонен финальным неуспешным ответом 415 или 488?

У сторон нет общего кодека: например, исключены базовые кодеки, а в предложении нет ни одного кодека из списка. esg отвечает 488 Not Acceptable Here, если в SDP провайдера нет кодека из opts.payloads_audio или в предложении нет ни одного известного платформе кодека.

Где посмотреть SDP вызова?

В trn-логах и на диаграмме последовательности звонка (/api/monitor/v1/trace).