Создание полноценного видеохостинга своими руками (nginx+php5-fpm+ffmpeg+cumulusclips)

от автора

Добрый день, хабровчане!

Недавно в нашей компании возникла потребность создания своего видеоресурса, закрытого, но в тоже время немного публичного. И вот наконец, он закончен и я готов поделиться знаниями и применениями.

Задача была следующая:
Создать видеоресурс, способный проводить многопоточные односторонние трансляции с web камеры, а так же из любого файла (это например для защиты от прямого скачивания), видеошару с возможностью просмотреть видео в разных форматах и битрейтах.

В основу лег освободившийся сервер! Не очень мощный, но довольно таки подходящий.

Intel® Xeon® CPU L5520 @ 2.27GHz
количество ядер 16
оперативной памяти 16372 Мб

Немного забегу вперед, при декодировании видео процессорная нагрузка достигает 500% (примерно 6 ядер);

Начнем с самого начала, из ОС я выбрал Ubuntu Server 13.04 x64 ввиду того, что больше времени провожу с ней и собственно разбираюсь я в ней лучше чем в других семействах Linux.

В качестве WEB сервера я выбрал связку nginx+php5-fpm, потому что nginx довольно успешно справляется с нагрузками, а так же отдачей видео.

nginx по умолчанию ставится без потокового модуля, поэтому ставим из сорцов

Необходимые зависимости для сборки пакетов:

apt-get install build-essential checkinstall subversion unzip yamdi imagemagick php5-curl libssl-dev zlib1g-dev libpcre3-dev rpl php5-fpm git 

Скачиваем исходники:

cd /tmp wget http://nginx.org/download/nginx-1.5.2.zip unzip nginx-1.5.2.zip -d nginx/ rm -f nginx-1.5.2.zip cd nginx 

Скачиваем необходимые модули для стриминга:

mkdir modules git clone https://github.com/masterzen/nginx-upload-progress-module.git modules/nginx-upload-progress-module wget http://www.kernel-video-sharing.com/files/nginx_mod_h264_streaming-2.3.2.zip unzip nginx_mod_h264_streaming-2.3.2.zip -d modules/ rm -f nginx_mod_h264_streaming-2.3.2.zip git clone https://github.com/arut/nginx-rtmp-module.git modules/nginx-rtmp-module 

Для удобства создаем установочный скрипт:

touch nginx.sh nano nginx.sh 

с содержимым

./configure \ --conf-path=/etc/nginx/nginx.conf \ --error-log-path=/var/log/nginx/error.log \ --pid-path=/var/run/nginx.pid \ --lock-path=/var/lock/nginx.lock \ --http-log-path=/var/log/nginx/access.log \ --http-client-body-temp-path=/var/lib/nginx/body \ --http-proxy-temp-path=/var/lib/nginx/proxy \ --http-fastcgi-temp-path=/var/lib/nginx/fastcgi \ --with-debug \ --with-http_stub_status_module \ --with-http_secure_link_module \ --with-http_gzip_static_module \ --with-http_realip_module \ --with-http_mp4_module \ --with-http_flv_module \ --with-http_ssl_module \ --with-http_dav_module \ --with-md5=/usr/lib \ --add-module=modules/nginx-upload-progress-module \ --add-module=modules/nginx-rtmp-module \ --add-module=modules/nginx_mod_h264_streaming-2.3.2    make -j16 (16 - количество ядер. ускоряет сборку пакета. Можно узнать командой "grep -c processor /proc/cpuinfo")  checkinstall 

Возможно в процессе компиляции могут возникнуть ошибки. Поэтому делаем так:

В файле auto/cc/gcc комментируем строчку:

#CFLAGS="$CFLAGS -Werror" 

Запускаем:

sh nginx.sh 

После установки создаем необходимые симлинки и директории (если не создались):

ln -s /usr/local/nginx/sbin/nginx /usr/sbin/nginx mkdir -p /var/lib/nginx/body mkdir /var/lib/nginx/proxy mkdir /var/lib/nginx/fastcgi chown -R root /var/lib/nginx/ wget http://nginx-init-ubuntu.googlecode.com/files/nginx-init-ubuntu_v2.0.0-RC2.tar.bz2 tar -jxvf nginx-init-ubuntu_v2.0.0-RC2.tar.bz2 -C /etc/init.d/ chmod 715 /etc/init.d/nginx /usr/sbin/update-rc.d -f nginx defaults rm -f nginx-init-ubuntu_v2.0.0-RC2.tar.bz2 rpl 'DAEMON=/usr/local/sbin/nginx' 'DAEMON=/usr/local/nginx/sbin/nginx' /etc/init.d/nginx rpl 'NGINX_CONF_FILE="/usr/local/nginx/conf/nginx.conf"' 'NGINX_CONF_FILE="/etc/nginx/nginx.conf"' /etc/init.d/nginx 

На этом установка nginx и php5-fpm завершена.
К настройке вернемся позже.

Следующим на очереди стоит ffmpeg. Установка apt-get не желательна, проект уже депрекейтет и многое отказывается работать. В поисках адекватной и более свежей инструкции я провел почти 2 ночи. Не скрою, нашел доповольно таки хороший пак для установки.

Как ни странно проект называется www.ffmpeginstaller.com и даже при наличии всех инсталяторов в открытом доступе предлагает свои услуги за 50 баксов.

А делается все довольно просто.
Скачиваем пакет:

cd  /tmp wget http://mirror.ffmpeginstaller.com/old/scripts/ffmpeg7/ffmpeginstaller.7.4.tar.gz tar -xzf ffmpeginstaller.7.4.tar.gz ffmpeg cd ffmpeg 

После первой установки я понял… не хватает кодека. Отмотаем <<< Ставим кодек до установки:

apt-get install libvpx 

Открываем ffmpeg.sh и добавляем кодек в установку:

nano ffmpeg.sh ./configure --prefix=$INSTALL_DDIR --enable-shared --enable-nonfree \                 --enable-gpl --enable-pthreads  --enable-libopencore-amrnb  --enable-decoder=liba52 \                 --enable-libopencore-amrwb  --enable-libfaac  --enable-libmp3lame \                 --enable-libtheora --enable-libvorbis  --enable-libx264  --enable-libxvid <b>--enable-libvpx</b> \                 --extra-cflags=-I/usr/local/cpffmpeg/include/ --extra-ldflags=-L/usr/local/cpffmpeg/lib \                 --enable-version3 --extra-version=syslint 

На этом подготовка завершена. Можно устанавливать: sh install.sh

Установка займет минут 15-20 в зависимостти от возможностей железа. Можно пойти попить чаю (или кофе).

После установки выполняем:

hash x264 ffmpeg ffplay 

Все. Поздравляю, мы это сделали!

Теперь нам нужна CMS для управления этими инструментами

Вариантов было немного, а точнее всего 2 (а под мои нужды подходила только одна — cumulusclips).
Код исходников понятен, без лишней нервотрепки разобрался с составляющей. Вот только пришлось проект переписывать с mysql на mysqli. Весь код CMS структурирован и темлпейты лежат отдельно и гибко настраиваются. За основу выбрал шаблон псевдо Ютуба.

Пришлось полностью перекрутить проигрыватель, т.к. jwplayer был неспособен переключать видеопотоки. Немного полазив по github нашел незамысловатый плеер под простым названием jQplayer.
Данный плеер способен легко переключать потоки. Правда есть один минус. Проигрывание файла начинается с начала. И это не оказалось проблемой — видеофайлы легко режутся nginx из коробки.

Теперь нам нужно настроить web хостинг для нашего проекта.
Прилагаю небольшой скрипт для автоматизации данного процесса. В комплекте с CMS лежал .htaccess — а nginx его понимать отказывается, поэтому я его переписал под нужды данного web сервера.

#!/bin/bash echo -n "Введите имя создаваемого хоста: "  read host  echo -n "Введите имя пользователя nginx: "  read users  sap=/etc/nginx/sites-available/$host.conf  mkdir -p /var/hosting/ touch $sap  chmod 777 $sap  directives="upstream backend-${host} {server unix:/var/run/php5-${host}.sock;}  server {     listen 80;     server_name ${host} www.${host};     root /var/hosting/${host}/www;     access_log /var/log/nginx/${host}-access.log;     error_log /var/log/nginx/${host}-error.log;     index index.php;     rewrite_log on;     if ($host = '${host}' ) {         rewrite ^/(.*)$ http://www.${host}/$1 permanent;     }         location /im {         rewrite ^/im/(.*)$ /cc-core/controllers/thumbs.php?$1 last;     }     location /videos {         rewrite ^/videos/([0-9]+)/(.*)$ /cc-core/controllers/play.php?vid=$1 last;         rewrite ^/videos/page/([0-9]+)/$ /cc-core/controllers/videos.php?page=$1 last;         rewrite ^/videos/(most-recent|most-viewed|most-discussed|most-rated)/$ /cc-core/controllers/videos.php?load=$1 last;         rewrite ^/videos/(most-recent|most-viewed|most-discussed|most-rated)/page/([0-9]+)/$ /cc-core/controllers/videos.php?load=$1&page=$2 last;         rewrite ^/videos/([a-zA-Z0-9\-]+)/$ /cc-core/controllers/videos.php?category=$1 last;         rewrite ^/videos/([a-zA-Z-]+)/page/([0-9]+)/$ /cc-core/controllers/videos.php?category=$1&page=$2 last;         rewrite ^/videos/([0-9]+)/comments/$ /cc-core/controllers/comments.php?vid=$1 last;         rewrite ^/videos/([0-9]+)/comments/page/([0-9]+)/$ /cc-core/controllers/comments.php?vid=$1&page=$2 last;         rewrite ^/videos/$ /cc-core/controllers/videos.php last;     }         location /private {         rewrite ^/private/get/$ /cc-core/controllers/play.php?get_private=true last;         rewrite ^/private/videos/([a-zA-Z0-9]+)/$ /cc-core/controllers/play.php?private=$1 last;         rewrite ^/private/comments/([a-zA-Z0-9]+)/$ /cc-core/controllers/comments.php?private=$1 last;         rewrite ^/private/comments/([a-zA-Z0-9]+)/page/([a-z0-9]+)/$ /cc-core/controllers/comments.php?private=$1&page=$2 last;     }         location /members {                 rewrite ^/members/page/([0-9]+)/$ /cc-core/controllers/members.php?page=$1 last;         rewrite ^/members/([a-zA-Z0-9]+)/$ /cc-core/controllers/profile.php?username=$1 last;         rewrite ^/members/([a-zA-Z0-9]+)/videos/$ /cc-core/controllers/member_videos.php?username=$1 last;         rewrite ^/members/([a-zA-Z0-9]+)/videos/page/([0-9]+)/$ /cc-core/controllers/member_videos.php?username=$1&page=$2 last;         rewrite ^/members/$ /cc-core/controllers/members.php last;     }         location /search {         rewrite ^/search(/page/([0-9]+))?/$ /cc-core/controllers/search.php?page=$2 last;     }         location /login {         rewrite ^(.*)$ /cc-core/controllers/login.php last;     }         location /login/forgot {         rewrite ^(.*)$ /cc-core/controllers/login.php?action=forgot last;     }         location /logout {         rewrite ^(.*)$ /cc-core/system/logout.php last;     }         location /register {         rewrite ^(.*)$ /cc-core/controllers/register.php last;     }         location /activate {         rewrite ^(.*)$ /cc-core/controllers/activate.php last;     }         location /opt {         rewrite ^/opt-out/$ /cc-core/controllers/opt_out.php last;     }         location /contact {         rewrite ^(.*)$ /cc-core/controllers/contact.php last;     }         location /embed {         rewrite ^/embed/([0-9]+)/$ /cc-core/system/embed.php?vid=$1 last;     }         location /page {         rewrite ^(.*)$ /cc-core/system/page.php last;     }      location /translation {         rewrite ^(.*)$ /cc-core/system/translation.php last;     }         location /notify {         rewrite ^(.*)$ /cc-core/system/notify.php last;     }         location /language/get {         rewrite ^(.*)$ /cc-core/system/language.php?get last;     }         location /language {         rewrite ^/language/set/(.*)/$ /cc-core/system/language.php?set&language=$1 last;     }         location /feed {         rewrite ^/feed(/([a-zA-Z0-9]+))?/$ /cc-core/system/feed.php?username=$2 last;     }         location /video {         rewrite ^/video-sitemap(-([0-9]+))?\.xml$ /cc-core/system/video_sitemap.php?page=$2 last;     }         location /myaccount/upload/avatar {         rewrite ^(.*)$ /cc-core/system/avatar.ajax.php last;     }         location /myaccount/upload/validate {         rewrite ^(.*)$ /cc-core/system/upload.ajax.php last;     }         location /myaccount/grab/validate {         rewrite ^(.*)$ /cc-core/system/grab.ajax.php last;     }         location /actions/username {         rewrite ^(.*)$ /cc-core/system/username.ajax.php last;     }         location /actions/flag {         rewrite ^(.*)$ /cc-core/system/flag.ajax.php last;     }         location /actions/favorite {         rewrite ^(.*)$ /cc-core/system/favorite.ajax.php last;     }         location /actions/subscribe {         rewrite ^(.*)$ /cc-core/system/subscribe.ajax.php last;     }         location /actions/rate {         rewrite ^(.*)$ /cc-core/system/rate.ajax.php last;     }         location /actions/comment {         rewrite ^(.*)$ /cc-core/system/comment.ajax.php last;     }         location /actions/post {         rewrite ^(.*)$ /cc-core/system/post.ajax.php last;     }      location /actions/stream {         rewrite ^(.*)$ /cc-core/system/stream.ajax.php last;     }         location /actions {         rewrite ^/actions/mobile-(videos|search)/$ /cc-core/system/mobile_$1.ajax.php?mobile last;     }        location /myaccount {                 rewrite ^/myaccount/upload/complete/$ /cc-core/controllers/myaccount/upload_complete.php last;         rewrite ^/myaccount/upload/video/$ /cc-core/controllers/myaccount/upload_video.php last;         rewrite ^/myaccount/upload/$ /cc-core/controllers/myaccount/upload.php last;         rewrite ^/myaccount/myvideos(/page/([0-9]+))?/$ /cc-core/controllers/myaccount/myvideos.php?page=$2 last;         rewrite ^/myaccount/myvideos/([0-9]+)/$ /cc-core/controllers/myaccount/myvideos.php?vid=$1 last;         rewrite ^/myaccount/editvideo/([0-9]+)/$ /cc-core/controllers/myaccount/edit_video.php?vid=$1 last;         rewrite ^/myaccount/myfavorites(/page/([0-9]+))?/$ /cc-core/controllers/myaccount/myfavorites.php?page=$2 last;         rewrite ^/myaccount/myfavorites/([0-9]+)/$ /cc-core/controllers/myaccount/myfavorites.php?vid=$1 last;         rewrite ^/myaccount/privacy-settings/$ /cc-core/controllers/myaccount/privacy_settings.php last;         rewrite ^/myaccount/change-password/$ /cc-core/controllers/myaccount/change_password.php last;         rewrite ^/myaccount/subscriptions(/([0-9]+))?/$ /cc-core/controllers/myaccount/subscriptions.php?id=$2 last;         rewrite ^/myaccount/subscriptions(/page/([0-9]+))?/$ /cc-core/controllers/myaccount/subscriptions.php?page=$2 last;         rewrite ^/myaccount/subscribers(/page/([0-9]+))?/$ /cc-core/controllers/myaccount/subscribers.php?page=$2 last;         rewrite ^/myaccount/message/inbox(/page/([0-9]+))?/$ /cc-core/controllers/myaccount/message_inbox.php?page=$2 last;         rewrite ^/myaccount/message/inbox/([0-9]+)/$ /cc-core/controllers/myaccount/message_inbox.php?delete=$1 last;         rewrite ^/myaccount/message/read/([0-9]+)/$ /cc-core/controllers/myaccount/message_read.php?msg=$1 last;         rewrite ^/myaccount/message/send/([a-zA-Z0-9]+)/$ /cc-core/controllers/myaccount/message_send.php?username=$1 last;         rewrite ^/myaccount/message/reply/([0-9]+)/$ /cc-core/controllers/myaccount/message_send.php?msg=$1 last;         rewrite ^/myaccount/$ /cc-core/controllers/myaccount/myaccount.php last;     }         location /myaccount/profile {         rewrite ^(.*)$ /cc-core/controllers/myaccount/update_profile.php last;     }         location /myaccount/profile/reset {         rewrite ^(.*)$ /cc-core/controllers/myaccount/update_profile.php?action=reset last;     }     location /myaccount/message/send {         rewrite ^(.*)$ /cc-core/controllers/myaccount/message_send.php last;     }         location /m {         rewrite ^/m/v/([0-9]+)/$ /cc-core/controllers/mobile/play.php?mobile&vid=$1 last;         rewrite ^/m/v/$ /cc-core/controllers/mobile/videos.php?mobile last;         rewrite ^/m/s/$ /cc-core/controllers/mobile/search.php?mobile last;         rewrite ^/m/$ /cc-core/controllers/mobile/index.php?mobile last;     }         location /system {         rewrite ^/system-error/$ /cc-core/controllers/system_error.php last;     }     location /t {         rewrite ^/t/(.*)$ /cc-core/system/translation.php last;     }     location / {         if (!-e $request_filename){             #rewrite ^/(.*)$ /$request_uri/ permanent;                         rewrite ^/(.*)$ /cc-core/system/page.php last;         }     }     location ~ \.php$ {         include fastcgi_params;         fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;         fastcgi_pass backend-${host};     }     location ~* ^.+\.(jpg|jpeg|gif|css|png|js|ico|bmp)$ {         access_log off;         expires 10d;         break;     }     location ~ \.(flv|mp4|webm|ogg|ogv|mp3)$ {         mp4;         mp4_buffer_size     1m;         mp4_max_buffer_size 5m;             }     location ~ /\. {         deny all;     } } "  echo "$directives">$sap  sap_poll=/etc/php5/fpm/pool.d/$host.conf  touch $sap_poll  chmod 777 $sap_poll  directives_poll="[${host}] 	listen = /var/run/php5-${host}.sock 	listen.mode = 0666 	user = ${users} 	group = ${users} 	chdir = /var/hosting/${host} 	php_admin_value[upload_tmp_dir] = /var/hosting/${host}/tmp 	php_admin_value[soap.wsdl_cache_dir] = /var/hosting/${host}/tmp 	php_admin_value[date.timezone] = Asia/Yekaterinburg 	pm                                  = dynamic     pm.min_spare_servers                = 10     pm.max_spare_servers                = 20     pm.start_servers                    = 10     pm.max_children                     = 40"  echo "$directives_poll">$sap_poll  ln -s /etc/nginx/sites-available/$host.conf /etc/nginx/sites-enabled/$host.conf  mkdir -p /var/hosting/$host/www/  mkdir -p /var/hosting/$host/tmp/  dir=/var/hosting/$host/www  chown -R $users:$users "$dir";  find "$dir" -type d -exec chmod 0755 '{}' \;  find "$dir" -type f -exec chmod 0644 '{}' \;  /etc/init.d/nginx restart /etc/init.d/php5-fpm restart  

На этом все. Данная конфигурация способна кодировать видео в разные форматы, а так же стримить поток. Если данная статья заинтересует кого либо, я с удовольствием приведу живые примеры стриминга.

К сожалению полной начинки показать не могу, но вот что получилось у меня stream.etagi.com

Спасибо за внимание. Надеюсь обилие кода не помешает выдаче инвайта.

ссылка на оригинал статьи http://habrahabr.ru/post/188380/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *