Системный администратор
0,0
рейтинг
15 июня 2012 в 12:56

Разработка → Вещание онлайн-видео с помощью nginx

Что такое онлайн-видео?


Под термином онлайн-видео я понимаю длительное вещание какого-то живого видеосигнала (к примеру, из телестудии). Традиционные средства отдачи видео (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
Сергей Пузырёв @Aecktann
карма
29,1
рейтинг 0,0
Системный администратор
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (61)

  • 0
    Круто, ждем активного развития.
  • 0
    Очень интересный проект. Возможно, в будущем будем использовать для своего сервиса.
  • +2
    отличная замана адобовскому серверу, nginx как всегда радует
  • +37
    Сергей спасибо за статью. Спрашивайте кому интересно — отвечу на вопросы. Я автор прокета. В будущем планирую написать более развернутую статью по модулю.
    • 0
      Очень бы хотелось поддержку стриминга файлов по rtmp. И подскажите возможно ли как то пушить поток в сервер с энкодера?
      • +2
        Поддержки файлов пока нет. Однако сам nginx умеет отдавать flv по http с возможностью рандомного позиционирования. Т.е. это ровно то же самое.

        Пушить поток можно конечно. Берете и пушите :)
        • 0
          Ну вот как раз progressive download совсем не то же самое. RTMP гораздо прогрессивнее технически для вещания видео…
          • 0
            rtmp не нужен. автор erlyvideo отказался от него в новом erlyvideo.

            levgem.livejournal.com/379560.html
            • 0
              Планы вещания в HLS есть. Даже есть готовый прототип, вещающий аудио. Но на десктопе RTMP пока что рулит.
              • 0
                отлично! тогда немного вопросов можно?

                1)HDS не планируете добавить?
                2)можно ли имея на входе mpeg-ts конвертировать в hls/hds без декодирования?
                • 0
                  1) сначала с HLS разберусь, потом посмотрим
                  2) ваш вопрос теоретический или применительно к сабжу? HLS это и есть MPEG-TS, только разрезанный. Есть программа segmenter, которая может нарезать ваш MPEG-TS и создать плейлист m3u8. Все это и есть HLS. Это все конечно не относится к live-потоку.
                  • 0
                    вот в том и беда, что мне надо live сегментировать. afaik, это можно делать с помощью gstreamer, который у меня кодирует видео и пакует в mpeg-ts.

                    то что HLS — это MPEG-TS порезанный кусками по 10с я в курсе.
      • +1
        Конечно, это базовая функциональность.
        Самый простой конфиг:

        rtmp {
                server {
                        listen 1935;
                        application live {
                                live on;
                        }
                }
        }
        
        


        Все, что вы будете паблишить в сервер с энкодера в приложение live будет отдаваться клиентам.
        Чтобы разрешить паблишить только с определённых IP, нужно использовать директиву allow publish, например:

        
        rtmp {
                server {
                        listen 1935;
                        application live {
                                live on;
                                allow publish 127.0.0.1
                        }
                }
        }
        
        • 0
          В данном функционале была бы очень интересная плюшка возможность принимать праймэри и бэкап стрим.
    • 0
      Сейчас работает только в 1 воркере потому, что буфер хранится в локальной памяти? Если в shared память запихнуть кольцевой буфер и из него клиентам отдавать контент — то должно неплохо работать с любым количеством воркеров. В нгинксе есть все нужные обёртки для работы с shared memory, используются в ngx_http_file_cache.c.
      • +4
        Я знаю. Но появится проблема синхронизации. И это еще большой вопрос — что лучше — синхронизация или сериализация (=ретрансляция). Я думаю, второе гораздо лучше. Надо, конечно, сделать внутренюю ретрансляцию прозрачной для пользователя.

        Но, еще раз повторю, вы еще попытайтесь упереться в проц. Если у вас pentium4 с 10G сетевухой, то именно так и произойдет. На ядре современного Xeon и 2G сетевухой такой проблемы просто нет.
        • +1
          На сколько я вижу из поста, автор смог выжать 2.5 гигабита с одного ядра, так что смысл есть.

          А с синхронизацией в кольцевом буфере особо проблем нету. Если сделать буфер на, к примеру, 1 минуту, кто клиент, который отстал на 1 минуту от паблишера, принудительно отключается. Даже локи не нужны, надо только «оборот» кольца хранить и делать небольшую зону от хвоста, куда нельзя залезать (чтобы паблишер не записал туда новый код, пока клиенту отдаются данные). Ваш код пристально не смотрел, но должна быть похожая схема с ограничением длины буферов, чтобы память не выжирать.
          • 0
            Прочтите внимательно — он смог выжать 2,5 (т.к. сетевуха на 10), а у обычного сервера на выход только 2, так что от ядра еще останется, а от канала ничего.
            • +1
              Я внимательно читал. Если есть техническая возможность использовать 10G — то надо её использовать. Нынче 10G не такая уж редкость, встречается всё чаще и чаще.
              • 0
                О! Дайте пару линков, интересно цену посмотреть.
                • 0
                  www.fdcservers.net, там есть опция на 10Gpbs Unmeter
                  2 ДЦ в Румынии тоже дают 10Gbps
                  • 0
                    некропостеры в топике? :)
                    +350$ за 10гигабит. В чудеса не верю — небось настучат по башке на второй же день как за гигабит вылезешь? Реальные опыт есть?
                    • 0
                      про 10Gbps конкретно не скажу, брал раньше 2 сервера на 1Gbps — на пиках до 70-85MBps удавалось выжать.
                    • 0
                      в Румынском ДЦ честный 10Gbps + BGP будет стоить где-то 5k$ — но там и ДЦ намного лучше + множество ISP.
                      • 0
                        ну вот это уже ближе к реальности, хотя тоже демпинг. ;)
                        Ну а уж 350$ — смех!
    • 0
      дико нубский вопрос «на пальцах»: например с ноутбучной вебки можно передать видео поток на сервер для его, потока, дальнейшей раздачи силами nginx + rtmp_module?
      • 0
        да, конечно.
        для этого вы можете либо
        — использовать ffmpeg с правильными ключиками для захвата с камеры и передачи по rtmp на сервер
        — либо использовать флешку — например старую версию jwplayer с возможностью захвата видео (в новой версии такой возможности нет). эта версия лежит у меня в примерах. можете написать свой flash-апплет

        еще, вероятно, vlc способен на такие вещания.
        • 0
          Извиняюсь за нубовский сленг. Мне так проще «въехать» в тему.
          Рассмотрю две ситуации:
          1. Локально, т.е. на одном linux хосте — работает: одним браузером я запускаю jwplayer, идущий вместе с модулем nginx-rtmp, другим смотрю.
          2. На Win7 хосте запускаю jwplayer, на линукс хосте пытаюсь смотреть. jwplayer крутит кольцо таймаута.

          Порты открыты (домашняя сеть, открыл порты).
          Что я делаю не так?
          • 0
            Отвечу сам себе:

            в файлике `test/www/record.html` прописать адрес куда поток будет отдаваться.

            Спасибо за ответы, а самое главное — спасибо за разработку!
          • 0
            надо смотреть в настройки.
            вероятно, у вас где-то прописан localhost
            • 0
              Прошу извинить мне еще один вопрос: видео есть, звука нет. В настройках nginx, в секции

              rtmp -> server -> application appname -> record all;

              Аппаратура, с которой снимается видео и звук рабочая. Возможно передать видеопоток со звуком? Если да, куда копать?

              Документация по модулю существует?
              • 0
                record all не имеет отношения к вещанию, только к записи.

                а звука у вас нет скорее всего потому, что jwplayer кривой :)
                у меня тоже проблема со звуком с ним.

                а по поводу документации, ее пока нет.
                есть большой README, где примеры и комментарии к ним.
                дока будет как только у меня дойдут руки.
      • 0
        Да, например с помощью FMLE.
  • –5
    Сколько держит онлайна? В наш век, когда 8 ядер это минимум для сервера, один поток — это не вариант. Даже странно, что автор об этом не позаботился.
    • 0
      На четырёхъядерном сервере в 4 потока с разных IP-адресов в синтетическом тесте я смог вытащить с сервера около 9к подключений по мегабиту на каждое. Дальше упёрся в сеть.
    • +6
      1) Задействовать все ядра можно — надо настроить ретрансляцию потоков. Сейчас с этим придется повозиться, но в будущем я сделаю так, чтобы это было проще.
      2) Очень вероятно (зависит конечно от вашего железа), даже на одном ядре вы упретесь в сеть, а не в проц.
      3) Остальные ядра можно использовать для перекодирования (автоматический запуск ffmpeg, об этом упоминалось в статье)
      • +1
        Спасибо.
  • 0
    супер
  • +3
    Не вводите читателей в заблуждение. Erlyvideo это проект с открытым исходным кодом лицензия GPL v3, код на github-е. На коммерческой основе автор предоставляет услуги поддержки и кастомизации, так же есть некоторые проприетарные модули.
    • 0
      Спасибо, исправил.
  • +1
    Жалко, что вы вниманием обошли erlyvideo. По моему проект замечательный.
    • +3
      В планах у меня провести синтетические нагрузочные тесты и сравнить производительность всех популярных решений.
  • +1
    Извиняюсь за офттоп, но скажите, где вы берете 10G каналы в рунете?
    • +1
      А в чём проблема то? Даже в локальные пиринги типа Home-ix можно получить 10G канал, что уж говорить про MSK-IX.
      • 0
        Ну вот, например, ситуация: я нахожусь в Ростовской области.
        Наш клиент – небольшой молодежный телеканал, ориентированный в первую очередь на регионы Юга России.
        CloudFront и видео-инстансы на Amazon AWS приемлемы и пока устраивают, но хочется поближе и побыстрее.

        Пиринговые сети Москвы, мне не очень помогут в реализации быстрой доставки контента. Ngenix – на данном этапе дорого. Из известных мне поставщиков, только Транстелеком, но это еще дороже.
        Да, я очень поверхностно в контексте телекомов, я разработчик )
        • 0
          Если в таком контексте — то вам нужен CDN свой. Ставьте по машинке у каждого крупного провайдера из интересующих регионов, каждая из них будет хватать поток с вашего телеканала и вещать локально. Если провайдер достаточно крупный — то он может предложить вам и 10G до своего ядра. Ну и 1 машинку в Москве, на неё будут ходить из тех провайдеров, которые не согласились у себя поставить ваши региональные машины.
  • –4
    Автор (обзора), вы конечно, как и многие авторы в охоте за халявной кармой пошли по стратегии обругать все и выделить одно, то на что направлен обзор. Если бы вы потрудились запустить Erlyvideo, вы могли бы с удивлением для себя обнаружить подобную утилизацию ресурсов, а кроме того возможность организации серьезной бизнес логики и управления потоками. (а также умеет кучу всего включая RTSP/HLS/HDS стриминг, VoD, Shoutcast имеет модули поддержки SIP/MMS/MMSH, etc ) Автор(проекта) безусловно заслуживает похвал настолько насколько автор(обзора) порицания.
    • +2
      Ткните, пожалуйста, пальцем, где я обругал erlyvideo.
  • +1
    На erlyvideo получается использовать два включения по гигабиту или около 2000 подключенных клиентов забирающих мегабитный поток.

    Причем это не синтетический тест, а реальная картина.
    • +1
      сервер при этом использовался с процессором Intel® Core(TM) i7-2600K CPU @ 3.40GHz. В пике получилось около 70% нагрузки, то есть теоретически можно еще гигабит осилить.
  • +2
    Жду этого вещателя на спортбоксе!
  • НЛО прилетело и опубликовало эту надпись здесь
  • 0
    Интересно, а в чем мотивация Романа в написании такого модуля для nginx'a?
    Побочный продукт при реализации своего проекта?
  • +1
    Использовали wowza и нагрузка на процессор не доставила таких неудобств, как стоимость канала.
    10 Gb/s у нас стоят как самолет.

    Вообще, онлайн-вещание — это весело. Нагрузки большие, неизведанных проблем масса.
    Как раз недавно закончили проект по доставке видео-контента на игровые площадки.
    Поставили
    а) 5 серверов с гигабитными каналом, SATA–III дисками, камнем с 9000 по cpubenchmark
    б) сервер с E3-1275 для декодирования видео
    в) региональный сервер рестрима.
    г) веб сервер (nginx + php как cgi, apache бы копыта отбросил в первую минуту стрима) с флеш–клиентом, чатом и счетчиком посещений.
    Итого — 8 серверов для 5Гб/с.

    Дальше roundrobin в DNS. С веб сервера клиенты nginx'ом забирают плеер, в то время как один из серверов с гигабитом рестримит картинку всем.
    — Специфичное железо оказалось несовместимым с 2.6.32 ядром, перекатились на 3.2.0 из бэкпортов.
    — На третьем ядре стал грузиться drm. Пришлось выкинуть его в блэклист.
    — Под нагрузкой отвалился r8169, пришлось ставить r8168.
    — Прогнали iperf, замерили каждый сервер с учетом утилизации порта гигабит, но не догадались, что серваки с гигабитными портами воткнули в свич с гигабитным аплинком. Это, конечно, был провал. Очень быстро сообразили, переткнули.
    — Затем под нагрузкой переполнилась таблица для хранения записей соединений и начал лететь nf_conntrack: table full, dropping packet, пофиксили.
    — Дали стрим на весь канал и на web сервере ON–AIR оказалась около 4.000 и прогнулся web сервер, установивший лимит соединений. То есть, коннектится клиент, он поднимает соединения и не может закрыть их 60 секунд. Тупо netstat –na | wc –l = 65.000 и приехали. Больше не будет никогда, sysctl не поможет. Пересобрали ядро, понизив порог TIME_WAIT и сайт взлетел.

    Ну и с десяток мелких тонкостей еще было, sysctl перековыряли как следует.
    Над тюнингом вовзы и самим плеером также ребята трудились. Сейчас вполне все бегает и в месяц раздает под 100ТБ траффика.
    • –1
      Вспоминается анекдот
      —Надо сделать операцию на позвоночнике моему другу (ему 16 лет). К врачам идти —дорого. Книжку по хирургии мы уже купили. У кого есть опыт — поделитесь!

      Вы бы хоть известный фак/доклад по тюнингу от Сысоева прочли. Он кстати называется «FreeBSD для обслуживания 100-200 тысяч соединений», так сразу чтоб забыли свои стереотипы про 65к. ;)
      • 0
        >Вы бы хоть известный фак/доклад по тюнингу от Сысоева прочли. Он кстати называется «FreeBSD для обслуживания 100-200 тысяч соединений», так сразу чтоб забыли свои стереотипы про 65к. ;)

        и как оно относится к тому, что у автора предыдущего комментария linux?

        100-200k — это вчерашний день.

        C500k in Action at Urban Airship:
        urbanairship.com/blog/2010/08/24/c500k-in-action-at-urban-airship/

        тюнинг:
        urbanairship.com/blog/2010/09/29/linux-kernel-tuning-for-c500k/
      • –2
        Мы занимаемся в основном практикой телекома и зарабатыванием денег. На теорию, к сожалению, времени остается очень мало.
        Лимит количества открытых TCP соединений — 65536.
        Почему?
        Отвечаю: Порты. В Линуксе 16 бит всего на все порты, включая входящие соединения.
        Обычно это решается созданием алиасов на интерфейсах и того же roundrobin (по ряду причин такой вариант не подходил). Наше решение довольно красво.
        • 0
          Ссылку ж наверху дали.
          Цитата оттуда —
          «It’s a common misconception that you can only accept 64,000 connections per IP address and the only way around it is to add more IPs. This is absolutely false.

          The misconception begins with the premise that there are only so many ephemeral ports per IP. The truth is that the limit is based on the IP pair, or said another way, the client and server IPs together. A single client IP can connect to a server IP 64,000 times and so can another client IP.

          Were this myth true it would be a significant and easy-to-exploit DDoS vector.»
    • –1
      > веб сервер (nginx + php как cgi, apache бы копыта отбросил в первую минуту стрима) с флеш–клиентом, чатом и счетчиком посещений.

      Не откинул бы, если правильно настроен. Счетчик посещений вообще просто инкремент, можно держать в том же Redis. Так же выкинуть стандартные сессии и вынести их опять же в Redis и все будет работать и не «дохнуть».

      Фронтендом при этом конечно же должен выступать nginx, но если сильно нужен mod_php и апач (бывает такое), то при правильной настройке все работает.
    • 0
      Признаю свою ошибку
      Хвалебные отзывы выше вовзе можно выкинуть.
      Нагрузка увеличилась незначительно(на 10-20%) и wowza действительно свалилась по процессору. Чем дальше, тем хуже — никакого железа не хватает.
      Курю nginx модуль.
  • 0
    Не хватает упоминания про StreamBuilder:

    inventos.ru/produkty/streambuilder/

    Фактически, это стример и транскодер в одном флаконе — причем не только в формате RTMP, но и более популярного HLS.

    Есть бесплатная версия с наложением логотипа — для некоммерческого использования норм.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.