VoIP Contest: Round 1

Перейти к английской версии документа

Задача этапа

Построить базовую систему для тестирования голосовых звонков с двумя участниками.

Обзор VoIP звонков

  • Пользователь A хочет совершить голосовой звонок (caller)
  • Пользователь B принимает голосовой звонок от A (callee)
  • Есть также VoIP Relay - промежуточный сервер, который принимает данные от каждого из пользователей и передаёт их второму участнику. У него есть IP-адрес, порт и 2 “тега” на каждый звонок для того чтобы различать разные звонки между собой, а также, чтобы различать участников одного звонка, caller, callee.
  • Данные, которыми обмениваются A и B должны быть зашифрованы, чтобы всё работало корректно, ключ шифрования должен быть одинаковый с обеих сторон.
  • При этом в разных условиях (Edge, 3G, LTE, Wi-Fi) пакеты данных могут теряться, переставляться и т. д., и звук может искажаться.
  • Цель – автоматизировать процесс тестирования передачи звука в разных условиях.

[ Caller A ] <-> [ VoIP Relay ] <-> [ Callee B ]

Входные данные

  • Библиотека libtgvoip. В конкурсе будет использоваться исходный код по состоянию на коммит d4a0f719.
  • HTTP API для получения конфигурации и credentials для VoIP Relay (см. ниже).
  • Набор из 53 звуковых файлов в формате OPUS длиной 5-60 секунд (полученных путем записи аудиосообщений через официальные клиенты Telegram). Скачать: VoIP_Round1_Test_Sounds.zip, 6.49MB
  • Telegram API не требуется.

Задание

  • Научиться собирать libtgvoip под Linux.
  • Построить standalone приложение tgvoipcall с CLI-интерфейсом, подключающее библиотеку libtgvoip, которое будет совершать звонок с одной из сторон. Во время звонка звук должен быть получен не из звукового устройства микрофона (оно на сервере будет просто отсутствовать), а из одного из OPUS-файлов. Звук, полученный от собеседника, вместо воспроизведения звуковым устройством (которого также не будет), необходимо закодировать в OPUS-файл. Также необходимо вывести статистику звонка в формате JSON в STDOUT.
  • Построить standalone приложение tgvoiprate с CLI-интерфейсом, которое будет оценивать качество звонка, имея оригинальный звуковой файл и его искажённую копию, полученную принимающей стороной, вещественным числом от 1.0 до 5.0. Критерии для оценки качества выбираются на усмотрение участника в соответствии со здравым смыслом.
  • Свести к минимуму вмешательства в код libtgvoip
  • Свести к минимуму внешние зависимости обеих программ

Contest VoIP API

Для тестирования программы потребуется инициировать звонок и получать адрес и тег VoIP Relay, ключ шифрования и конфигурацию. Для этого существует simple API с одним GET-endpoint'ом.

Для работы с API потребуется {auth_token} вида 12345:c56d836270d512a6ac1a6ef78c4132ba14, уникальный для каждого участника конкурса, получаемый от @jobs_bot (был выслан с анонсом запуска конкурса или будет отправлен при вступлении в конкурс через бота).

Каждый новый звонок задается случайной строкой длиной от 1 до 128 байтов, передаваемой в GET-параметр call. Повторный вызов с одним и тем же значением call в течение до 300 секунд, приведет к одинаковому результату.

GET https://api.contest.com/voip{auth_token}/getConnection?call={call}

Ответ имеет следующий вид:

{  
   "ok":true,
   "result":{
      "id":1117254731,
      "date":1570802828,
      "p2p_allowed":false,
      "config":{
         "audio_frame_size":60,
         ...
         "audio_strong_fec_bitrate":7000
      },
      "encryption_key":"61362f271d0e13c59...c5eb9ba9ca9daa06c9f",
      "endpoints":[  
         {  
            "id":"2377378803836",
            "ip":"134.209.176.124",
            "port":"553",
            "peer_tags":{  
               "caller":"fedd4f39ea...89991f1d7b1",
               "callee":"fabd4f39ea...89991f1d7b1"
            }
         }
      ]
   }
}

Интерфейс tgvoipcall

Имеем 2 клиента A (caller), B (callee), которые будут издавать звуки sound_A.ogg и sound_B.ogg соответственно.

В примере ниже в файле config.json лежит конфигурация из поля config ответа API.

Программа tgvoipcall будет вызвана одновременно в двух копиях (для каждого из участников) со следующими параметрами, для A и B соответственно:

tgvoipcall reflector:port tag_caller_hex -k encryption_key_hex -i /path/to/sound_A.ogg -o /path/to/sound_output_B.ogg -c config.json -r caller
tgvoipcall reflector:port tag_callee_hex -k encryption_key_hex -i /path/to/sound_B.ogg -o /path/to/sound_output_A.ogg -c config.json -r callee

e.g.
$ tgvoipcall 134.209.176.124:553 fedd4f39e89991f1d7b1 -k 61362f271d0e13c59...c5eb9ba9ca9daa06c9f -i sound_B.ogg -o sound_output_A.ogg -c config.json -r caller
{"libtgvoip_version":"2.4.4","log_type":"call_stats","network":{"type":"wifi"},"p2p_type":"inet","packet_stats":{"in":1049,"lost_in":4,"lost_out":0,"out":1042},"pref_relay":"2243506735106","problems":[],"protocol_version":9,"relay_rtt":87,"rtt":5,"tcp_used":false,"udp_avail":false}  

tgvoipcall устанавливает соединение с собеседником и передаёт весь свой звуковой файл, после чего дождёт 3 секунды для завершения приёма данных от собеседника (длительность звуковых файлов выбирается примерно одинаковая). Только после этого завершает выполнение и выводит в STDOUT статистику вызова (см. VoIPController::GetDebugLog).

Звук, полученный от собеседника должен быть закодирован в OPUS-файл со следующими настройками: mono, bit_rate 64000, sample_rate 48000, TDesktop Source)

Если не удалось установить соединение в течение таймаута (5 секунд), приложение выводит ошибку в STDERR и выходит с ненулевым exit code'ом.

Интерфейс tgvoiprate

Запуск tgvoiprate не должен быть никак привязан к предыдущему запуску tgvoipcall. Звуковые файлы могут быть сгенерированы другим приложением.

$ tgvoiprate /path/to/sound_A.ogg /path/to/sound_output_A.ogg
4.6324

Если существуют внешние зависимости, которых никак не избежать, их можно перечислить в текстовом файле (см. ниже deb-packages.txt). Они будут установлены перед запуском командой sudo apt-get install ...

Примечания

  • OPUS файлы из набора образцов закодированы с разными настройками, необходимо проверить, что работает каждый из них
  • P2P должен быть выключен на стороне библиотеки
  • IPv6 не будет поддерживаться сервером во время тестирования
  • Для тестирования в разных условиях сети рекомендуется использовать netem.
  • Приложения будут тестироваться на серверах с Debian GNU/Linux 10.1 (buster), x86-64. Обязательно проверьте корректность работы обеих программ на чистой системе перед отправкой работы.
  • На выходе требуется ZIP-файл (максимальный размер - 200MB) со следующей структурой

    submission.zip
      -> tgvoipcall - запускаемый бинарный файл с вышеописанным интерфейсом
      -> tgvoiprate - запускаемый бинарный файл с вышеописанным интерфейсом
      -> src - директория с исходным кодом приложения
      -> deb-packages.txt - текстовый файл с названиями пакетов внешних зависимостей, разделенные переводом строки

Критерии оценки

  • Выполнение всех перечисленных условий, особенно внимание к примечаниям
  • Объём кода
  • Минимум внешних зависимостей
  • Для оценки tgvoiprate будет использован следующий алгоритм:
    • Будет выбрано несколько эталонных работ tgvoipcall
    • Эти работы будут использованы для генерации примеров искажённых файлов.
    • Для генерации искажённых файлов будет использоваться netem с ухудшением параметров сети (Rate control, Delays, Packet loss, duplication, re-ordering) до невозможности распознать голос.
    • В каждом случае будет изменять до двух параметров сети.
    • Полученный набор пар файлов предложим оценить нескольким людям целыми числами от 0 до 6
    • Вычисляем отклонение оценок программы от усредненных контрольных значений по формуле sum(max(|x_i - y_i| - 0.3, 0.0)^2).