Nginx

      在〈Nginx〉中尚無留言

Nginx (讀作 engine x)是一個免費的開源軟體, 一個非同步框架的 web server. 不過它的功用不僅為 web server, 更多的用途是作為反向代理、Http Cache、負載平衡器. 近幾年十分火熱, 使用率有超越 Apache 的趨勢.

這一二年來, 本人發現 Apache在同時約60人的連線中, 有時就會爛爛的, 甚至crash. 激起了本人把Apache換掉的念頭. 底下是Nginx的優勢, 也是換掉Apache的原因

另一個會換掉Apache的原因, 是因為Nginx可以作直播系統伺服器

面向效能

Nginx 在處理 IO 並發與靜態文檔方面效能比 Apache 強很多, 這跟 web server 的底層設計有關. 因此在需要應對大流量的狀況下,許多公司會選擇使用 Nginx 作為 server 而不是 Apache。

記憶體消耗低

一樣拿 Apache 來比較,Nginx 佔用了相對低的資源與內存,另外值得注意的是 Nginx 在高併發狀況下可以保持低資源低消耗,卻仍能保有高效能。

反向代理&負載平衡

這點Apache 也做得到, 但效能差. Nginx 可以消耗更少的資源處理更多的連線請求。

將Apache轉換成Nginx, 幾乎是無痛的, 把/etc/apache2/ports.conf 及 /etc/apache2/siet-available/000-default.conf裏的80 port空出來. 然後照下面的方式操作, 就可以把網站全部改過來. 用來運作wordpress順的不得了

nginx安裝及設定

安裝nginx

在ubuntu20.04安裝nginx很簡單, 只要下達如下指令即可

sudo apt-get install nginx libnginx-mod-rtmp

此時在瀏覽器網址輸入 http://localhost即可看到如下畫面

nginx1

啟動停止

sudo systemctl stop nginx
sudo syatemctl start nginx 
sudo systemctl restart nginx 
sudo systemctl reload nginx

開啟port

1935 port為 rtmp直播接收port, 必需打開

設定RTMP直播伺服器

sudo vim /etc/nginx/nginx.conf

events {
        use epoll;
        worker_connections 768;
        # multi_accept on;
}
http{
    #手動加入此段, 防止資料太大無法上傳
    client_max_body_size 1000m;
    proxy_connect_timeout   10;
    proxy_send_timeout      10;
    proxy_read_timeout      20;
}
rtmp {
    server {
        listen 1935;
        application live {
            live on;
            hls on;
            wait_key on;
            hls_path /data/video/live;
            hls_fragment 1000ms;
            hls_playlist_length 5000ms;
            hls_continuous on;
            hls_cleanup on;
            hls_nested on;
            allow publish all;
            allow play all;
            record all ;
            record_path /data/video/record;
            record_max_size 500m;
            record_suffix -%Y-%m-%d-%H.%M.%S.ts;
            record_unique on;
            #exec_record_done bash /data/server/auto/ts2mp4.sh $path $basename;
        }
    }
}

上述的設定會啟動 rtmp 直播系統,直播內容放在 /data/video/live 中,而且會自動記錄備份到 /data/video/record 目錄。不過請注意, live 及 record 的 owner 都必需是 www-data,請使用如下指令更改

sudo chown www-data:www-data -R /data/video/live
sudo chown www-data:www-data -R /data/video/record

直播的內容是由 /data/video/live/channel_xx 目錄下的 index.m3u8 所提供。上述為了把直播的延遲降到最低,所以設定了

hls_fragment 1000ms; 
hls_playlist_length 5000ms;

這表示每一個檔只儲存 1 秒的內容,list 裏保留了 5 秒的影像。/data/video/live 只是暫時儲存直播的內容,所以建議把此目錄改成 ramdisk。更改成 ramdisk 的方法很簡單,只要在 /etc/fstab 裏增如下設定,然後重新開機即可

tmpfs           /data/video/live        tmpfs   size=1G 0 0

php安裝及設定

安裝 php

Nginx 亦可支援php, php-FPM(FastCGI Process Manager), 請安裝如下套件

sudo apt-get install nginx-extras php-fpm php-mysqlnd php-mysqli php-gd

設定

sudo vim /etc/php/8.1/fpm/pool.d/www.conf, 在www.conf更改如下設定

listen = 127.0.0.1:9000

sudo vim /etc/php/8.1/fpm/php.ini, 修改如下四個。

尤其是 memory_limit,如果使用預設的 128M,會讓 wordpress 無法正確顯示網頁。因為選單很多的話,128M 是不夠的。本站就佔用到快 300M 的記憶体容量。

max_execution_time = 300
memory_limit = 512M
post_max_size = 1000M 
upload_max_filesize = 1000M

設定虛擬伺服器

先執行如下指令建立 web 目錄

sudo mkdir /data/video/web
sudo chown www-data:www-data /data/video/web

然後使用 sudo vim /etc/nginx/sites-available/default,將 server 區塊改成如下

server{
        listen 80;
        listen [::]:80;
        server_name ~.*;
        root /data/server/web;
        add_header X-Frame-Options SAMEORIGIN;#禁止網頁被內崁
        index index.php index.html;
        location ~* \.(ico|css|js|gif|jpe?g|png|ogg|ogv|svg|svgz|eot|otf|woff)(\?.+)?$ {
                expires max;
                log_not_found off;
        }
        location / {
                try_files $uri $uri/ /index.php?$args;
        }
        location ~* \.php$ {
                fastcgi_pass 127.0.0.1:9000;
                fastcgi_index index.php;
                fastcgi_split_path_info ^(.+\.php)(.*)$;
                include fastcgi_params;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        }
        #底下是靜態伺服器, rtmp 要使用的
        location /live {
            alias /data/video/live;
        }
        location /record {
            alias /data/video/record;
        }
}

請注意上面紅色的部份,”?$args” 是為了防止 wordpress 5.0版開始在新增文章時,會出現 Cannot read properties of undefined (reading ‘show_ui’) 這種莫名奇妙的錯誤。

server_name 與浮動 ip

server_name 後面可以加多個網域或ip,如下

server_name localost mahaljsp.asuscomm.com 192.168.1.5 220.18.92.63;

但如是要使用浮動ip進行連線呢?? 先說明為什麼非要用 ip連線 : 因為本人使用 asus的 ip分享器進行轉址,但華碩動不動就掛掉,所以緊急時就只能用 ip 連線。

而浮動ip隨時都會變,所以將浮動 ip 寫在 server_name後面,其實沒啥意義。所以必需想個方法,讓所有的 ip 都轉到我們的虛擬伺服器上,此方法如下

server_name ~.*;

重新啟動

最後重新啟動 php-pfm 及 nginx

sudo service php8.1-fpm restart
sudo service nginx restart

測試

因為網頁根目錄設定在 /data/video/web, 所以在此目錄下新增 info.php

<?php
    phpinfo();
?>

記得更改檔案擁有者

sudo chown www-data:www-data /data/server/web/info.php

然後於瀏覽器輸入 http://localhost/info.php即可看到 php 的資訊

rtmp 儲存成 mp4

rtmp 伺服器設定 record all 時,會將影片儲存在 record_path 路徑中,副檔名為 .ts 檔。ts 檔其實也是 mp4 的格式,只不過它是切片檔,可惜的是 html5 的 video.js 在瀏覽器中只能播放 mp4 檔,對於 .ts 的支援一直是個問題,所以如果想在瀏覽器中作歷史回放,就必需把 ts 檔轉成 mp4 檔。

將 ts 檔轉成 mp4 檔首推 ffmpeg 套件,請執行如下指令安裝 ffmpeg 套件

sudo apt-get install ffmpeg

執行單個指令

nginx 有個 exec_record_done 選項,當 nginx 儲存完畢 ts 檔,可用此指定接下來要執行的指令,比如如下藍色代碼所示

rtmp {
    server {
        listen 1935;
        application live {
            ....................
            record_unique on;
            exec_record_done ffmpeg -hwaccel cuda -y -i $path -vsync 2 -c:v h264_nvenc /data/video/mp4/$basename.mp4;
        }
    }

$path 及 $ basename 是系統變數,請照打不能更改。藍色代碼指定當 ts 檔儲存完畢,就觸發 ffmpeg 硬体編碼將 /data/video/record/xxxx.ts 轉成 /data/video/mp4/xxxx.mp4 。

執行多個指令

如果儲存 ts 後要執行的指令不僅轉檔,還要將 ts 刪除的話,就必需把 nginx.conf 改成如下

rtmp {
    server {
        listen 1935;
        application live {
            .......
            exec_record_done bash /data/server/auto/ts2mp4.sh $path $basename;
        }
    }
}

藍色代碼指定儲存影片後執行 /data/server/auto/ts2mp4.sh 檔,而且要傳入 $path 及 $basename 二個變數。請在 /data/server/auto 新增 ts2mp4.sh,內容如下

#!/bin/bash
filePath=$1
baseName=$2
/usr/bin/ffmpeg -i $filePath -c copy /data/video/record/$baseName.mp4
/usr/bin/ffmpeg -hwaccel cuda -i /data/video/record/$baseName.mp4 -c:v h264_nvenc /data/video/mp4/$baseName.mp4
rm -rf $filePath
rm -rf /data/video/record/$baseName.mp4

ts2mp4.sh 儲存後,還要將此腳本改成可執行檔

sudo chmod 755 ts2mp4.sh

硬体編碼

ffmpeg -hwaccel cuda 使用硬体編碼,所以伺服器必需安裝 nVidia 顯卡及驅動程式,但好像不需要安裝 Cuda。但 Cuda 是 Python 必用的套件,所以還是建議安裝一下,請參照 https://mahaljsp.ddns.net/ubuntu_setting/ 最後面的說明。

上述第一個 ffmpeg 使用 -c copy 將 ts  copy 成 mp4 檔,這個步驟會重寫影片的 sps/pps,這是因為使用手機編碼時,對於 sps 及 pps  不好控制,而網路上也沒這方面的資訊,不知道要怎麼寫,所以借由 ffmpeg 重寫 sps/pps,最後才開始轉檔,這樣就可以避開 duplicate frame 的錯誤。使用 RTX-3080Ti 顯卡可達到 20x 的轉檔速度。

請注意一下,如果將 ts 副檔名直接改成 mp4 是沒有用的。

權限

管理者若想要刪除 mp4 目錄內的資料,除了將管理者加入 www-data 群組外,還要把 mp4 目錄設為 775

sudo usermod -a -G www-data 帳號
such chmod 775 /data/videa/mp4

安裝Tomcat

移除apt所安裝的tomcat9

Tomcat 10的版本比Tomcat 9更快更穩定,所以先前若有使用 apt-get 安裝過Tomcat 9的人,請依如下步驟移除。

sudo apt-get purge tomcat9
sudo apt autoremove
sudo rm -rf /etc/tomcat9

下載及安裝

請到 https://tomcat.apache.org/download-10.cgi 下載Core版 .tar.gz檔,然後依如下步驟安裝。

sudo apt-get install openjdk-16-jdk
sudo useradd -m -d /opt/tomcat -U -s /bin/false tomcat
wget https://downloads.apache.org/tomcat/tomcat-10/v10.0.7/bin/apache-tomcat-10.0.7.tar.gz
sudo mkdir /opt/tomcat
sudo tar xzvf apache-tomcat-10.0.7.tar.gz -C /opt/tomcat --strip-components=1
sudo chown -R tomcat:tomcat /opt/tomcat
sudo chmod -R u+x /opt/tomcat/bin

設定啟動服務

sudo vim /etc/systemd/system/tomcat.service,並寫入如下指令

[Unit]
Description=Tomcat
After=network.target

[Service]
Type=forking

User=tomcat
Group=tomcat

Environment="JAVA_HOME=/usr/lib/jvm/java-1.16.0-openjdk-amd64"
Environment="JAVA_OPTS=-Djava.security.egd=file:///dev/urandom"
Environment="CATALINA_BASE=/opt/tomcat"
Environment="CATALINA_HOME=/opt/tomcat"
Environment="CATALINA_PID=/opt/tomcat/temp/tomcat.pid"
Environment="CATALINA_OPTS=-Xms512M -Xmx1024M -server -XX:+UseParallelGC"

ExecStart=/opt/tomcat/bin/startup.sh
ExecStop=/opt/tomcat/bin/shutdown.sh

[Install]
WantedBy=multi-user.target

啟用服數及啟動Tomcat

sudo systemctl daemon-reload
sudo systemctl enable tomcat.service
sudo systemctl start tomcat.service
sudo systemctl status tomcat.service

更改網頁根目錄

sudo vim /opt/tomcat/conf/server.xml,新增如下藍色部份

<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log" suffix=".txt" pattern="%h %l %u %t &quot;%r&quot; %s %b" />
<Context path="" docBase="/data/server/tomcat" debug="0" reloadable="true"/>
</Host>

Nginx設定

開啟 /etc/nginx/sites-available/default,新增如下藍色的部份即可,將所有 *.jsp的檔案全都轉向給 Tomcat http://localhost:8080

location ~* \.php$ {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_split_path_info ^(.+\.php)(.*)$;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
#location ~ .*.jsp$ {
location ~* \.jsp$ {
        index index.jsp;
        proxy_pass http://localhost:8080;
}

開機無法自動啟動

請注意, nginx使用atp安裝的話, 開機後是會自動啟動的. 如果沒有自動啟動, 是因為啟動失敗. 

啟動失敗的原因, 除了設定錯誤外, 另一個最常看到的現像就是在vmware裏, 網頁根目錄採用外掛的方式外掛到windows. 因為外掛到windows的目錄, 需啟動vmtools, 再用mount –bind外掛. 

nginx, php7.4-fpm的 rc3.d順序是S01, 如果把 mount –bind也改為S01, 這樣也是沒辨法的. 因為rc3.d的啟動順序採用多行程, 外掛要一段很長的時間.

解決的方式為在原生的ext4先建立空目錄, 如 /data/video, /date/video/web, /data/video/record, /data/video/web/live. 此時讓 nginx可以順利啟動.

後續 mount –bind慢慢掛上去後, nginx所需的內容也就會自動變更.

手機直播Streamlab

在手機上進入GooglePlay, 安裝Streamlab, 然後在Setting/Streaming platform, 輸入網址 rtmp://ip/live, 第二欄位輸入video1

nginx2

手機開始直播後, 就會在 ubuntu伺服器的 /usr/local/nginx/html/hls/video1裏存放影片資料

手機直播CamON Live

此款app的廣告非常煩人, 不建議使用此軟体

手機直播Larix Broadcaster

此款app目前沒有廣告, 也支援推播到rtmp Server,建議使用此軟体

直播網頁撰寫

請在 /usr/local/nginx/html之下, 撰寫如下videolive.html

<html>
    <title>live</title>
    <body>
        <video id="video1" class="video-js vjs-default-skin" controls width="400" height="225"
            data-setup='{ "autoplay": true, "controls": true, "poster": "", "preload": "auto", "enterfullscreen": true }'>
            <source src="http://192.168.1.2/data/video1/index.m3u8" type="application/x-mpegURL">
        </video>
        <!--These three scripts aren't necessary for you to use videojs-contrib-hls -->
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
        <script src="node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
        <script src="js/ie10-viewport-bug-workaround.js"></script>

        <!-- videojs-contrib-hls setup --!>
        <!--Make sure to include the video.js CSS. This could go in the <head>, too. -->
        <link href="https://unpkg.com/video.js/dist/video-js.css" rel="stylesheet">
        <!--Include video.js and videojs-contrib-hls in your page -->
        <script src="https://unpkg.com/video.js/dist/video.js"></script>
        <script src="https://unpkg.com/videojs-flash/dist/videojs-flash.js"></script>
        <script src="https://unpkg.com/videojs-contrib-hls/dist/videojs-contrib-hls.js"></script>
        <script>
        </script>
    </body>
</html>

然後於瀏覽器輸入 http://192.168.1.2/videolive.html即可看到影像

video.js 7

上面網頁, 在需要直播的情形下, 需要多加入 videojs-contrib-hls.js, 但hls的專案已被棄用了, 改而使用videojs-http-streaming(VHS), 並且內建在video.js 7 的版本. 請參照https://github.com/videojs/videojs-contrib-hls 裏的說明

Notice: this project will be deprecated and is succeeded by videojs-http-streaming. VHS supports HLS and DASH and is built into video.js 7, see the video.js 7 blog post

下載video.js 7

video.js 7.7.5下載點
https://github.com/videojs/video.js/releases/tag/v7.7.5

video.js 7.0.0下載點
https://github.com/videojs/video.js/releases/tag/v7.0.0

下載 zip檔, 然後將解開的 .js檔及其子目錄 copy 到網頁伺服器根目錄下, 然後撰寫如下html檔

<html>
 <head>
 <!--
 <meta http-equiv="refresh" content="60" />
 -->
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 <link href="./v7/video-js.css" rel="stylesheet">
 <script src="./v7/video.js"></script>

 <style type="text/css">
 #video1{
 position:absolute;
 top:0px;
 left:0px;
<!-- 
 width:335px;
 height:100px;
 overflow-y:hidden;
 
--> 
 z-index:0;
 }
 #showbox{
 font-size: 16px;
 color:#FFFFFF; 
 font-weight:bold; 
 position:absolute;
 top:20px;
 left:20px;
 z-index:1;
 }
 #refresh{
 position:fixed;
 right:2px;
 bottom:2px;
 height:30px;
 z-index:2;
 overflow:hidden;
 background: rgba(255, 255, 255, 0.3)
 }
 .vjs-default-skin { color: #fdfdfd; } 
 .vjs-default-skin .vjs-play-progress, 
 .vjs-default-skin .vjs-volume-level { background-color: #1880b8 } 
 .vjs-default-skin .vjs-control-bar { font-size: 100% } 
 </style>
<script language="JavaScript">
 function ShowTime(){
 var NowDate = new Date();
 var year = NowDate.getFullYear();
 var month = NowDate.getMonth();
 var day = NowDate.getDate();
 var h = NowDate.getHours();
 var m = NowDate.getMinutes();
 var s = NowDate.getSeconds();
 if (month<10)month = "0"+month;
 if (day<10)day = "0"+day;
 if (h<10)h = "0"+h;
 if (m<10)m = "0"+m;
 if (s<10)s = "0"+s;
 document.getElementById('showbox').innerHTML = year+"/"+month+"/"+day+" "+h+":"+m+":"+s+
 "&nbsp;&nbsp;&nbsp;&nbsp;&nbspCar:8888-WG";
 setTimeout('ShowTime()',1000);
 }
 </script>


 <title>live</title>
 </head>
 <body>
 <!--vjs-default-skin-->
 <div id="showbox"> </div>
 <video id="video1" class="video-js vjs-default-skin vjs-big-play-centered" controls
 crossorigin="use-credentials"
 data-setup='{ "autoplay": true, "controls": true , "poster": "", "preload": "auto" }'>

 <p class="vjs-no-vjs">Device don't connect</p> 
 </video>
 <a href="#" onclick="javascript:window.location.reload()">
 <button id="refresh">Refresh</button>
 </a>
 <script>
 videojs('video1', {
 controls:false,
 muted: true,
 fluid: true,
 aspectRatio: "16:9",
 requestFullScreen: true,
 notSupportedMessage: "no connection",
 sources: [{src:"http://ip/live/video1/index.m3u8",type:"application/x-mpegURL"}]
 });
 ShowTime();
 </script>
 </body>
</html>


撰寫mp4網頁

先將mp4影片copy到 html/data之下, 再於html編輯videomp4.html

<html>
    <head>
        <link href="https://vjs.zencdn.net/7.6.6/video-js.css" rel="stylesheet" />
        <!-- If you'd like to support IE8 (for Video.js versions prior to v7) -->
        <script src="https://vjs.zencdn.net/ie8/1.1.2/videojs-ie8.min.js"></script>
    </head>
    <body>
        <video id="my-video" class="video-js" controls preload="auto" width="640" height="480" poster="MY_VIDEO_POSTER.jpg" 
           data-setup='{"autoplay": true, "controls": true, "poster": "", "preload": "auto"}'>
            <source src="./data/video1/a.mp4" type="video/mp4" />
            <source src="MY_VIDEO.webm" type="video/webm" />
            <p class="vjs-no-js">please enable JavaScript</p>
        </video>
        <script>
           var videoPlayer = videojs( "my-video" );
           videoPlayer.play();
        </script>
        <script src="https://vjs.zencdn.net/7.6.6/video.js"></script>
    </body>
</html>

然後在瀏覽器網址輸入 http://192.168.1.2/videomp4.html即可觀看影片

儲存路徑

直播影像可以儲存在Server裏, 在rtmp/server加入如下即可

record all;
record_path /record;
record_suffix -%Y-%m-%d-%T.flv
record_unique on;

日期格式請參考如下網站說明
https://pubs.opengroup.org/onlinepubs/009695399/functions/strftime.html

Issue

1. 網路斷線後, 客戶端無法自動連線 — 解決方案 : 使用 html 每1分鐘自動 refresh, 但會閃頻
2. 影片timestamp — survey
3. 檔案備份問題 — 解決方案 : server 加入 record 設定. 所以需教育人員如何使用 ftp
4. The media could not be loaded, either because the server or network failed or because the format is not supported.   修改成  “網路斷線, 請稍後”

srs

SRS/3.0,OuXuli, 是一個串流媒体集群, 支援RTMP/HLS/WebRTC/SRT/GB28181. 高效、穩定、簡單易用. 可以用來取代rtmp.

rtmp 使用 TCP協定, 而 SRS使用UDP. UDP只負責傳送, 而不進行驗証. 這正好符合推播的特性, 有傳出去就好, 不用管他傳出去後有沒有被正確的接收.

下載

wget http://archive.ubuntu.com/ubuntu/pool/universe/libm/libmail-srs-perl/srs_0.31-6_all.deb

sudo dpkg -i ./srs_0.31-6_all.deb

git clone https://gitee.com/winlinvip/srs.oschina.git srs &&
cd srs/trunk && git remote set-url origin https://github.com/ossrs/srs.git && git pull
./configure && make

修改錯誤

sudo apt-get install python3-pip

sudo vim auto/depends.sh

將414行的 python改成 python3

參考

https://codertw.com/%E4%BA%BA%E5%B7%A5%E6%99%BA%E6%85%A7/131701/

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *