Category: Tech articles

  • Вещание онлайн-видео с помощью nginx

    \"776px-Nginx-battleship.svg\"Что такое онлайн-видео?

    Под термином онлайн-видео я понимаю длительное вещание какого-то живого видеосигнала (к примеру, из телестудии). Традиционные средства отдачи видео (flv- и mp4-стриминг) в данном случае не работают, просто потому что файла, содержащего весь видеопоток, не существует.

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

    RTMP-протокол

    RTMP — Real Time Messaging Protocol (вики) — протокол, который обычно используется для раздачи живого видео- и аудиоконтента клиентам. Он удобен тем, что в AS3 есть его поддержка из коробки, требует не слишком много ресурсов на клиенте и поддерживает множество плюшек (например, трансляцию с переменными битрейтами и переключение клиентов на более высокое качество при наличии свободного канала).

    NB: Помимо RTMP существует набор других протоколов вещания медиапотоков (RTSP, Apple HTTP Live Streaming и др.), но в этой статье они не рассматриваются.

    Протокол RTMP — детище компании Adobe. Единственным официальным сервером, которым можно вещать RTMP-поток, является Adobe Flash Media Server. Его цена и производительность, к сожалению, оставляют желать лучшего, поэтому различными разработчиками были предприняты попытки создать более или менее совместимые альтернативные реализации протокола. К сожалению, у RTMP-протокола есть известные проблемы с лицензированием, к примеру, его официальная спецификация, если следовать ей строго, не позволяет написать работающий RTMP-сервер. Тем не менее, существует несколько реализаций:

    • Red5 — один из первых RTMP-серверов, написан на Java
    • Wowza — проприетарный RTMP-сервер, написан на Java. Помимо RTMP, поддерживает также RTSP, Apple HLS, и Smoothstreaming (Silverlight).
    • erlyvideo — свободный RTMP-сервер с отдельно распространяющимися проприетарными модулями, написан на Erlang. Проект пишется русскими программистами и активно развивается.

    Java тормозит

    В тестах, которые я проводил, Red5 и Wowza показали неприлично низкую производительность. При мегабитном видеопотоке Wowza при 1000 зрителей онлайн съедала уже около 300% процессорного времени (Intel Xeon E5607). Если требуется вещать хотя бы для 20000 человек онлайн, то нужно закупать 20 серверов? Это слишком дорого.

    Что же делать?

    Использовать неблокирующие операции. Тормоза в Wowza и Red5 следуют из-за далеко не самой оптимальной схемы обработки событий. Для реализации действительно быстрого стримингового сервера, нужно использовать эффективный метод обработки событий (для linux это epoll). Именно такую схему работы и реализовал Роман Арутюнян (блог, github-профиль, rarutyunyan) в своём RTMP-сервере, реализованном как модуль к nginx.

    nginx-rtmp-module

    Достоинства:

    • Нереально шустрый. На том же физическом сервере, который я использовал для тестов Wowza, nginx выдержал 2500 мегабитных соединений на ядро, т.е. 10000 на весь сервер целиком.
    • Удобные конфиги :). После километровых XML-конфигов Wowza эти выглядят спасением.
    • Отзывчивость автора к фичреквестам.

    Недостатки:

    • Проект молодой, наличествуют баги, которые, впрочем, оперативно исправляются.
    • Небольшая функциональность. Сервер вещает только в RTMP, перекодировка появилась буквально несколько дней назад, имеет статус экспериментальной и фактически является обёрткой над ffmpeg. UNIX-way во всей своей красе.
    • Нет многопоточности. Модуль может работать только в том случае, если nginx запускается с одним воркером. Для утилизации всех процессорных ядер нужно запускать несколько воркеров на разных IP-адресах и/или портах.

    Многопоточность можно реализовать с помощью примерно такого костыля (Ubuntu):

        for ip in $(cat /etc/network/interfaces | grep address | awk \'{print $2}\') ; do
            touch /etc/nginx/nginx.$ip.conf
            cp /etc/nginx/nginx.conf.skel /etc/nginx/nginx.$ip.conf
            sed -i \"s/%IPADDR%/${ip}/g\" /etc/nginx/nginx.$ip.conf
            /usr/sbin/nginx -c /etc/nginx/nginx.$ip.conf
        done

    При этом у вас должен быть написан скелет конфига с указанием %IPADDR% вместо IP-адреса. Например, такой:

    worker_processes  1;
    
    error_log   logs/error.%IPADDR%.log debug;
    
    pid /var/run/nginx.%IPADDR%.pid;
    
    worker_rlimit_nofile 65536;
    
    events {
        worker_connections  16384;
    }
    
    rtmp {
            server {
                    listen %IPADDR%:1935;
                    chunk_size 4000;
                    application live {
                            live on;
                            pull live stream %masterIP%;
                    }
            }
    }
    
    http {
            server {
                    listen %IPADDR%:8080;
                    location /stat {
                            rtmp_stat all;
                            rtmp_stat_stylesheet stat.xsl;
                    }
                    location /stat.xsl {
                            root /srv/nginx/html;
                    }
            }
    }

    Впрочем, автор обещает реализовать нормальную многопоточность в будущих релизах.

    С помощью nginx-rtmp-module мне удалось полностью утилизировать 10-гигабитный канал. При этом основной проблемой стала вовсе не прожорливость самого nginx, а необходимость тюнинга сетевой карты и параметров ядра, чтобы software interrupts не съедали 100% CPU.

    Ссылки по теме:

    RTMP на Википедии
    Wowza — официальный сайт
    Erlyvideo — официальный сайт
    Nginx-rtmp-module — проект на GitHub