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)
.