
В этой статье речь пойдёт о том, как разработать и запустить собственный аудиосервис на площадке Bitcord Open Network. Аудиосервис будет построен как связь между приложением Bitcord и удалённого стримингового HLS сервиса на базе ОС Linux (Ubuntu).
Напомню, Bitcord — это по сути, небольшая открытая социальная платформа, выполненная в виде приложения для смартфонов и работающая на базе клиент-серверной архитектуры. Здесь можно общаться с друзьями и коллегами, создавать каналы, а теперь стало возможным запускать собственные музыкальные сервисы.
Приложение Bitcord занимает очень мало места. Размер его установочного пакета составляет чуть более 3 мегабайт (3.2 Мб. актуальный релиз). Оно не перегружено сложной графикой и не использует тяжёлые библиотеки или SDK. Это позволяет использовать его на малобюджетных устройствах, что сулит проекту большую популярность среди пользователей. Подробнее об этой платформе читайте в других моих статьях.
Суть проблемы
Итак, для того чтобы реализовать доступ пользователей к вашему стриминговому сервису внутри приложения Bitcord нужно решить две задачи:
-
Запустить собственно сам стриминговый сервис в формате HLS
-
Интегрировать этот сервис в приложение Bitcord используя его API
Почему именно HLS формат? Ответ прост, — это один из самых популярных протоколов для передачи потокового видео и аудио через интернет. Он стал общепринятым стандартом и поддерживается практически везде: на смартфонах, компьютерах, смарт-ТВ и в браузерах. По сути, HLS перед отправкой большого медиа файла, режет его на множество маленьких файлов, посылая один за одним на устройство пользователя.
Я не просто так выделил выше слово «аудио». Причина в том, что HLS протокол работает только с видео данными в формате MP4 но не MP3. Секрет отправки аудио состоит лишь в том, чтобы аудио файл перед отправкой упаковать в контейнер MP4, заблокировав отправку видео кадров.
Конвертацию проще всего выполнить с помощью старого доброго FFmpeg — мощной консольной утилиты, предназначенной для обработки мультимедийных файлов. Команда преобразования может выглядеть так:
-i sourceMp3 -vn -c:a aac -b:a 192k outputMp4 2>&1
Параметры включают:
-
-i sourceMp3— путь к входному (исходному) файлу -
-vn— отключает видеопоток (то, что доктор прописал) -
-c:a aac— выбор аудиокодека (к примеру AAC) -
-b:a 192k— установка битрейта аудио (чем выше тем выше качество) -
outputMp4— путь к выходному (итоговому) файлу -
2>&1— команда перенаправления потоков ( stderr + stdout)
Выполнив эту команду мы упакуем обычный MP3 аудио файл в контейнер MP4.
Настраиваем Web-сервер
Итак, файл данных успешно упакован в подходящий для нарезки формат. Теперь настало время подготовить его для стмиринга в интернет. Для этого воспользуемся самым подходящим инструментом для шаринга данных. В системах Linux наиболее востребована программа nginx — это очень гибкий и лёгкий высокопроизводительный веб-сервер. Программа может раздавать статические данные прямо в интернет (аналог IIS в Windows).
К сожалению сам сервер не имеет встроенных функций для нарезки медиа данных на фрагменты. Для этого придётся воспользоваться сторонними компонентами от других разработчиков. В моём случае, самым подходящим вариантом выступила библиотека от компании Kaltura: nginx-vod-module.
Это мощный модуль работающий как MP4-репакер. Особенностью этого модуля является то, что вместо нарезки аудио на множество мелких кусочков и хранения их на диске, модуль выполняет эту операцию на лету в оперативной памяти в момент, когда пользователь запрашивает данные VOD (video on demand).
Чтобы этот модуль заработал, нужно скомпилировать его в среде Linux с веб-сервером. Для начала нужно установить необходимые зависимости:
sudo apt install -y build-essential git libpcre3 libpcre3-dev zlib1g zlib1g-dev libssl-dev libxml2 libxml2-dev libxslt1-dev ffmpeg libavcodec-dev libavformat-dev libavutil-dev
Далее переходим в папку с исходным кодом NGINX и запускаем конфигуратор, указав путь к скачанному модулю через флаг --add-module
cd nginx-(ваша версия )./configure \ --prefix=/etc/nginx \ --sbin-path=/usr/sbin/nginx \ --conf-path=/etc/nginx/nginx.conf \ --error-log-path=/var/log/nginx/error.log \ --http-log-path=/var/log/nginx/access.log \ --pid-path=/var/run/nginx.pid \ --lock-path=/var/run/nginx.lock \ --with-http_ssl_module \ --with-file-aio \ --with-threads \ --add-module=../nginx-vod-module# Запускаем сборку (флаг -j4 ускоряет процесс)make -j4# Собственно сама установка NGINX в системуsudo make install
Теперь, когда модуль добавлен, настраиваем сам nginx.
Открываем файл конфигурации (/etc/nginx/nginx.conf) и добавляем в него основные настройки для VOD-модуля:
http { # Включаем глобальные настройки для VOD-модуля vod_mode local; vod_metadata_cache 1024m 512m; vod_response_cache 128m 64m; vod_last_modified_types *; # Путь к библиотекам FFmpeg vod_fallback_upstream_retry 3; server { listen 80; server_name localhost; # Папка, где лежат ваши медиафайлы в формате MP4 location /audio/ { alias /var/www/media/; # Включаем маппинг в HLS на лету vod hls; # Настройки сегментов vod_fragment_duration 4000; # Длина кусочка в миллисекундах (4 секунды) vod_align_fragments_on_key off; # Разрешаем CORS-запросы, чтобы плееры из приложения могли читать поток add_header Access-Control-Allow-Origin '*'; add_header Access-Control-Allow-Headers '*'; } }}
Теперь проверьте как работает ваш новоиспечённый сервер. Запустите плеер который умеет работать с HLS стримом, к примеру VLC, и введите URL адрес вашего сервера:
http://ваш_сервер/audio/track.mp4/master.m3u8

Подключаем стриминг в Bitcord
Если вы выполнили описанную выше задачу и услышали собственную трансляцию на своём компьютере, примите мои поздравления! Но на этом работа ещё не закончена, ведь вашу музыку можете слушать только вы, зная адрес вашего сервера. Если вы хотите поделиться вашей музыкальной коллекцией со всем миром, тогда читайте дальше.
Платформа Bitcord недавно разработала специальное API (открытый программный интерфейс) для желающих интегрировать свой стриминговый сервис в этот мессенджер. В итоге, запустив в Bitcord собственный бот (аккаунт пользователя с фунцией внешних запросов) ваши подписчики и другие пользователи платформы смогут слушать вашу музыку онлайн.
Бот настраивается очень просто. Вы запускаете мессенджер Bitcord и переходите в меню настроект профиля. В профиле, в разделе «Account Type», устанавливаете параметр «Enable server bot» в правое положение. У вас откроются дополнительные пункты где нужно будет ввести адрес вашего новоиспечённого стримингового сервера и сгенерировать специальный защитный Bearer токен, который защитит ваш сервер от нежелательных гостей.
Если вы захотите монетизировать вашу музыку в криптовалюте, вы можете указать дополнительный параметр «Cost of service». Это сделает ваш бот коммерческим и доступ к нему станет платным. На начальном этапе, я не рекомендую заполнять этот параметр. Это можно сделать, когда ваш сервис станет популярным в мессенджере.
Как это будет работать?
Бот, это опычный аккаунт пользователя который присоединён к удаленному серверу и может отвечать на запросы пользователей в чате. Что именно он будет отвечать зависит от того, как этот удаленный сервис построен. Это может быть сервис погоды, конвертор валют, в общем всё что угодно что может обрабатывать запросы пользователей и выводить их в формате поддерживаемом самим мессенджером.
Для реализации взаимодействия пользователей бота со стриминговым сервисом, Bitcord предлагает специальный интерфейс в виде кнопочного управления. На практике это выглядит так:
-
пользователь переходит в меню и выбирает команду поиска
-
в панели поиска вводит имя бота и удерживает палец на найденном имени
-
бот добавляется в список контактов
-
пользователь открывает чат с этим ботом и отправляет сообщение
/start -
бот выводит на экран главное меню сервиса в виде набора кнопок
-
пользователь выбирает нужный трек и музыка звучит на его устройстве
Мессенджер Bitcord оснащён функцией удержания активности устройства через Foreground Service, поэтому вам не нужно бескопоиться если телефон «уснёт» пока играет трек. После остановки воспроизведения, сервис отключается освобождая ресурсы устройства.
PHP бэкенд
Чтобы ваш стриминговый сервер принимал запросы пользователей Bitcord и формировал соответствующий интерфейс, мы напишем небольшой PHP скрипт. В верхней его части будет выполняться проверка на соответствие Beaer токена вашему боту в Bitcord, что обезопасит ваш сервер от нежелательного проникновения:
<?phpsession_start();header('Content-Type: application/json; charset=utf-8');// AUTHENTICATION BEGIN$allowedToken = 'secret_bearer_token';$providedToken = '';if (isset($_SERVER['HTTP_AUTHORIZATION'])) { if (preg_match('/Bearer\s+(.+)/i', $_SERVER['HTTP_AUTHORIZATION'], $m)) { $providedToken = trim($m[1]); }}if (empty($providedToken) || $providedToken !== $allowedToken) { echo json_encode(['success' => false, 'error' => 'error token'], JSON_UNESCAPED_UNICODE); exit;}// AUTHENTICATION END// Получаем и декодируем входящие данные от Bitcord-клиента$rawInput = file_get_contents('php://input');$userData = json_decode($rawInput, true); // Переменные из запроса (клиент передаст их при клике)$callbackData = $userData['data'] ?? null;// Главное меню (Показываем при первом входе)$mainKeyboard = [ 'text' => 'Главное меню', 'inline_keyboard' => [ [ ['text' => 'Топ лист', 'callback_data' => 'cmd_top'], ['text' => 'Новинки', 'callback_data' => 'cmd_new'] ], [ ['text' => 'Жанры', 'callback_data' => 'cmd_genre'], ['text' => 'Плейлисты', 'callback_data' => 'cmd_playlists'] ], [ ['text' => '🎵 Случайный трек (Snow Leopard)', 'url' => 'https://ваш_сервер.com/media/track_1/master.m3u8'] ] ]];// Меню "Топ лист" (Показываем при клике на cmd_top)$topKeyboard = [ 'text' => 'Топ лист', 'inline_keyboard' => [ [ ['text' => '⏮ Назад', 'callback_data' => 'cmd_main_menu'] ], [ ['text' => 'Snow Leopard', 'url' => 'https://ваш_сервер.com/audio/track_1/master.m3u8'], ['text' => 'Темною нічкою', 'url' => 'https://ваш_сервер.com/audio/track_2/master.m3u8'] ] ]];// Меню "Новинки" (Показываем при клике на cmd_new)$newKeyboard = [ 'text' => 'Новинки музыки', 'inline_keyboard' => [ [ ['text' => '⏮ Назад', 'callback_data' => 'cmd_main_menu'] ], [ ['text' => 'Snow Leopard', 'url' => 'https://ваш_сервер.com/audio/track_1/master.m3u8'] ], [ ['text' => 'Темною нічкою', 'url' => 'https://ваш_сервер.com/audio/track_2/master.m3u8'] ], [ ['text' => 'Rhythm of the Night', 'url' => 'https://ваш_сервер.com/audio/track_3/master.m3u8'] ], [ ['text' => 'Another Love - Tiësto', 'url' => 'https://ваш_сервер.com/audio/track_4/master.m3u8'] ], [ ['text' => '⏭ Далее', 'callback_data' => 'cmd_next'] ] ]];// ЛОГИКА ОПРЕДЕЛЕНИЯ ДЕЙСТВИЯif ($callbackData === 'cmd_top') { // && !empty($messageId) // Пользователь нажал "Топ лист" $response = [ 'success' => true, 'type' => 6, 'data' => json_encode($topKeyboard, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ];} elseif ($callbackData === 'cmd_new') { // Пользователь нажал "Новинки" $response = [ 'success' => true, 'type' => 6, 'data' => json_encode($newKeyboard, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ];} elseif ($callbackData === 'cmd_main_menu') { // Пользователь нажал "Назад" $response = [ 'success' => true, 'type' => 6, 'data' => json_encode($mainKeyboard, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ];} elseif ($callbackData === '/start') { // Первое действие / Обычный вход $response = [ 'success' => true, 'type' => 6, 'data' => json_encode($mainKeyboard, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ];} else { exit;}echo json_encode($response, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);?>
Как видно из кода всё довольно просто. Мы получаем запрос от пользователя в виде $callbackData === ‘ваша_команда’ и заполняем соответствующим образом ответ в формате JSON.
Пример использования с базой данных Sqlite
Фрагмент ниже показывает как можно выбирать данные из базы Sqlite.
// Главное меню$mainKeyboard = [ 'text' => 'Главное меню музыкального каталога', 'inline_keyboard' => [ [ ['text' => '🔥 Топ лист', 'callback_data' => 'cmd_top'], ['text' => '✨ Новинки', 'callback_data' => 'cmd_new'] ], [ ['text' => '🎸 Жанры', 'callback_data' => 'cmd_genre'], ['text' => '📂 Плейлисты', 'callback_data' => 'cmd_playlists'] ], [ ['text' => '🎵 Случайный трек', 'callback_data' => 'cmd_random'] ] ]];// Логика обработки callback-запросовif ($callbackData === '/start' || $callbackData === 'cmd_main_menu') { $responseKeyboard = $mainKeyboard;} elseif ($callbackData === 'cmd_top') { // Топ лист (треки с высоким рейтингом, например, rating >= 4 или ORDER BY rating DESC) $stmt = $pdo->query("SELECT t.track_name, t.track_uuid, a.artist_name FROM tracks t LEFT JOIN artists a ON t.artist_id = a.id ORDER BY t.rating DESC LIMIT 10"); $tracks = $stmt->fetchAll(PDO::FETCH_ASSOC); $inlineKeyboard = [[['text' => '⏮ Назад', 'callback_data' => 'cmd_main_menu']]]; foreach ($tracks as $track) { $title = $track['artist_name'] ? "{$track['artist_name']} - {$track['track_name']}" : $track['track_name']; $inlineKeyboard[] = [[ 'text' => $title, 'url' => getTrackUrl($track['track_uuid']) ]]; } $topKeyboard = [ 'text' => '🔥 Топ лист лучших треков', 'inline_keyboard' => $inlineKeyboard ]; $responseKeyboard = $topKeyboard;}
Далее отправляем готовый ответ клиенту:
// Формируем и отправляем JSON-ответ клиенту$response = [ 'success' => true, 'type' => 6, 'data' => json_encode($responseKeyboard, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)];echo json_encode($response, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
Пояснение: ‘type’ => 6 в примерах выше, указывает клиенту Bitcord сгенерировать кнопку. Другие типы генераций в данной статье не рассматриваются.
В заключении
Функционал вашего сервиса будет зависеть от сложности вашего проекта. Для реализации полноценного музыкального сервиса видимо придётся спроектировать базу данных, к примеру Sqlite где будет прописана информация об артистах, треках, плейлистах и прочего.
Далее можно разнообразить интерфейс сервиса передавая пользователю дополнительную информацию в виде текста, картинок и прочего. Здесь всё зависит от вашей фантазии и вашего мастерства.
Мне остаётся пожелать всем творческих успехов и дальнейшего роста.
Пример работы музыкального сервиса доступен в мессенджере Bitcord.
Пользователь/Бот — «Музыка и Песни» логин @music
Всем мира и добра!
ссылка на оригинал статьи https://habr.com/ru/articles/1095866/