跳到主要內容區塊

ntuccepaper2019

技術論壇

HTML5 Video API與Apache mod h264 streaming的簡易串流技巧
  • 卷期:v0033
  • 出版日期:2015-06-20

作者:楊德倫 / 臺灣大學計算機及資訊網路中心教學組幹事


過去使用影音串流技術時,需要建置串流相關協定的伺服器,來提供服務,然而目前大多主流網頁改以HTML5技術來加以開發,其Video元素的影片來源格式尚未支援串流協定,因此我們必須使用一些技巧,讓HTML5Video元素能達到串流的功能。

 

前言

大多主流的入口網站及官方頁面,已經採用HTML5來開發自家網頁,尤以YouTube為最,透過自家開發的影音串流機制,來達到最佳的播放效果,同時也解決了過去使用Flash Player來當作播放平台的不便。筆者將透過HTML5 Video API與H264 Streaming Module的相關機制,將影片時間進行切割,並針對切割的間距逐一播放,以達到串流效果的相關技巧。

 


圖一 採用HTML5的YouTube播放器

 

安裝測試環境

為了讓讀者方便測試與重複操作,我們必須進行作業系統(Ubuntu)、網頁伺服器(Apache)、H264串流模組(mod_h264_streaming.so)、影音轉檔平台(FFmpeg)等機制的安裝,相關知識的取得,便成為閱讀本電子報的先決條件。另外,安裝過程中,需要對Linux的基礎指令有一定的了解與熟悉,請自行至網路上查詢相關概念,並加以學習。若讀者已有熟悉的安裝流程、環境與方法,可自行測試與實作。

 

安裝Ubuntu

請下載「伺服器版本」;若讀者希望能夠擁有長期支援(Long-term support, LTS),請下載LTS版本。

 


圖二 Ubuntu 官方下載頁面

 

前置作業

當ubuntu安裝完成後,我們將進行一些前置作業。請依先後順序來輸入指令「sudo apt-get update」、「sudo apt-get upgrade」「sudo apt-get dist-upgrade」,來進行系統內套件的更新與下載,若有詢問,請按下「y」鍵來繼續安裝。待一切更新完成後,請輸入「sudo apt-get install gcc build-essential libtool python-dev m4 autoconf pkg-config」來安裝後續需要用到的套件。

 

設定對外服務用的 IP

請輸入「sudo vi /etc/network/interfaces」來編輯網路卡相關設定,並加入下圖所示資訊:

 


圖三 設定網路卡對外服務用的 IP後,儲存離開

 


圖四 輸入「sudo ifup eth1」來啟動第二張網卡,並輸入「ifconfig」來確定是否如圖中所示。若有,則代表開啟成功

 

安裝Apache Web Server

一、安裝apache portable runtime
下載指令:sudo wget http://mirror.reverse.net/pub/apache/apr/apr-1.5.1.tar.gz
說明:
解壓縮後,進入該資料夾,先編輯configure檔案,約略第30145行,註解掉「$RM “$cfgfile”」(在面前加上 # 字號)後儲存,再進行以下安裝指令:
$ sudo ./configure
--prefix=/usr/local/apr
--enable-shared
--enable-static
$ sudo make
$ sudo make install

 


圖五 若安裝有問題,請先修改configure檔

 

二、安裝apr-iconv
下載指令:sudo wget http://mirror.reverse.net/pub/apache/apr/apr-iconv-1.2.1.tar.gz
說明:解壓縮後,進入該資料夾,請輸入以下安裝指令:
$ sudo ./configure
--prefix=/usr/local/apr-iconv
--with-apr=/usr/local/apr
--enable-shared
--enable-static
$ sudo make
$ sudo make install

 

三、安裝apr-util
下載指令:sudo wget http://mirror.reverse.net/pub/apache/apr/apr-util-1.5.4.tar.gz
說明:解壓縮後,進入該資料夾,請輸入以下安裝指令:
$ sudo ./configure
--prefix=/usr/local/apr-util
--with-apr=/usr/local/apr
--with-apr-iconv=apr-iconv
$ sudo make
$ sudo make install

 

四、安裝pcre (Perl-compatible regular expression library)
下載指令:sudo wget http://jaist.dl.sourceforge.net/project/pcre/pcre/8.36/pcre-8.36.tar.gz
說明:解壓縮後,進入該資料夾,請輸入以下安裝指令:
$ sudo ./configure
--prefix=/usr/local/pcre
--enable-utf8
--enable-unicode-properties
$ sudo make
$ sudo make install

 

五、安裝apache web server
下載指令:sudo wget http://apache.mirrors.tds.net/httpd/httpd-2.4.12.tar.gz
說明:解壓縮後,進入該資料夾,請輸入以下安裝指令:
$ sudo ./configure
--prefix=/usr/local/apache2
--with-apr=/usr/local/apr
--with-apr-util=/usr/local/apr-util
--with-pcre=/usr/local/pcre
--enable-so
$ sudo make
$ sudo make install

 

六、設定apache帳號 for Web server
說明:請輸入以下指令
$ sudo groupadd apache
$ sudo useradd -s /sbin/nologin-g apacheapache

 

七、設定 ServerName 及 web server 所使用的帳號
至 /usr/local/apache2/conf/httpd.conf 中,找到下面的部分,並將User及Group皆改成「apache」;再來搜尋ServerName,會看到註解後的文字,可於下方空白處(或直接拿掉註解),輸入ServerName 192.168.56.100(請自行輸入先前所設定對外開啟服務用的IP),而後再輸入「sudo /usr/local/apache2/bin/apachectl restart」來重新啟動Apache Web Server。若開啟成功,在瀏覽器網址列上輸入IP,便會出現「It works!」。

 


圖六 將User與Group從daemon改成apache

 


圖七 由於沒有domain name,因此輸入對外開啟服務用的IP

 

八、Apache 的預設網頁根目錄
預設網頁根目錄路徑為 /usr/local/apache2/htdocs,之後我們必須要將撰寫的程式和相關檔案,放至該路徑底下。

 

安裝 H264 串流模組

下載指令:sudo wget http://h264.code-shop.com/download/apache_mod_h264_streaming-2.2.7.tar.gz
說明:解壓縮後,進入該資料夾,請輸入以下安裝指令
$ sudo ./configure
--prefix=/usr/local/apache2/modules/
--with-apxs=/usr/local/apache2/bin/apxs

 


圖八 可指/usr/local/apache2/modules查看是否有h264的模組

 

再到 httpd.conf 當中,尋找 LoadModule,在當中或相關設定後面,加上「LoadModule h264_streaming_module /usr/local/apache2/modules/mod_h264_streaming.so」

 


圖九 讓apache讀取H264模組

 

另外再到 <IfModule mime_module> 裡面,加入以下資料:
AddType video/mp4 .mp4 .m4v .f4v .f4p
AddHandler h264-streaming.extensions .mp4

 


圖十 加入H264相關參數與資訊

 

安裝 FFmpeg

以下安裝過程與參數,是筆者個人為了轉換「影」與「音」所自訂的設定;若是對FFmpeg不甚了解,可以參考筆者設定:
一、先更新所有套件
$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo apt-get dist-upgrade

 

二、安裝 git、subversion套件
$ sudo apt-get install git subversion

 

三、安裝 build-essential nasm pkg-config套件
$ sudo apt-get install build-essential nasm pkg-config

 

四、安裝 yasm
說明:稍候安裝 x264(mp4影像編碼格式)需要 yasm 組譯器
$ sudo apt-get remove yasm
$ sudo wget http://www.tortall.net/projects/yasm/releases/yasm-1.2.0.tar.gz
$ sudo tar -zxvf yasm-1.2.0.tar.gz
$ cd yasm-1.2.0
$ sudo ./configure --prefix=/usr/local
$ sudo make
$ sudo make install
$ yasm --version(這指令是作 yasm 版本查詢)

 

五、下載 ffmpeg
$ sudo git clone git://source.ffmpeg.org/ffmpeg.git ffmpeg

 

六、安裝 AAC, MP3, Theora, Vorbis, AMR, GSM 套件
說明:AAC, MP3, Theora, Vorbis, AMR, GSM 是影音轉檔會用到的編碼格式
$ sudo apt-get install
libfaac-dev
libmp3lame-dev
libtheora-dev
libopencore-amrnb-dev
libopencore-amrwb-dev
libgsm1-dev
zlib1g-dev
libgpac-dev

 

七、安裝 libx264
$ sudo git clone git://git.videolan.org/x264.git
$ cd x264
$ sudo./configure --prefix=/usr/local --enable-shared –enable-static
$ sudo make
$ sudo make install

 

八、安裝 libxvid
$ sudo wget http://downloads.xvid.org/downloads/xvidcore-1.3.2.tar.gz
$ sudo tar -zxvf xvidcore-1.3.2.tar.gz
$ cd xvidcore-1.3.2/build/generic
$ sudo ./configure --prefix=/usr/local
$ sudo make
$ sudo make install

 

九、安裝 libvpx
$ sudo wget https://webm.googlecode.com/files/libvpx-v1.3.0.tar.bz2
$ sudo tar -jxvf libvpx-v1.3.0.tar.bz2
$ cd libvpx-v1.3.0
$ sudo ./configure --prefix=/usr/local --enable-vp8 --enable-vp9 --enable-shared
$ sudo make
$ sudo make install

 

十、安裝 libvorbis
$ sudo wget http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.4.tar.gz
$ sudo tar -zxvf libvorbis-1.3.4.tar.gz
$ cd libvorbis-1.3.4
$ sudo ./configure --prefix=/usr/local --enable-shared –enable-static
$ sudo make
$ sudo make install

 

十一、安裝 ffmpeg 與相關參數
$ cd /usr/local/src/ffmpeg
$ sudo ./configure
--prefix=/usr/local
--enable-gpl
--enable-version3
--enable-nonfree
--enable-libopencore-amrnb
--enable-libopencore-amrwb
--enable-libfaac
--enable-libgsm
--enable-libmp3lame
--enable-libtheora
--enable-libx264
--enable-libxvid
--enable-libvpx
--enable-libvorbis
--enable-pthreads
--enable-shared
--enable-static
$ sudo make
$ sudo make install
$ sudo ldconfig –v(請務必輸入該指令)

 

將影片轉成h264 streamiong模組可用的mp4格式

首先,我們要準備一部 mp4 格式的影片;若無,則需要透過以下實驗指令來進行影片轉檔(若讀者對ffmpeg已有一定了解,請以自身經驗實作),將其轉成 mp4 格式,以方便串流模組使用:
$ ffmpeg –i  [來源影片名稱.副檔名]
-s 640x480 (寬x高)
-vcodec libx264 (影片編碼格式)
-quality good (品質:good;另有best可選擇)
-cpu-used 5 (0~5,值愈低,cpu使用愈多,可產生愈佳品質)
-threads 4 (欲使用cpu核心數)
result.mp4(轉換影音所產出的檔案)

 

確認影片長度

我們可以透過Linux指令,來確認影片的長度,稍候可以讓程式方便計算時間間距:「ffmpeg –i 影片名稱.副檔名 2>&1 | grep Duration | cut –d ‘ ‘ –f 4 | sed s/,//」。在本測試影片的時間長度為 00:00:48.90(時:分:秒),約略為48秒。

 


圖十一 找出影片的時間總長(持續時間)

 

確認Apache mod h264 streaming是否有作用

Apache h264 模組的特色之一,即是在影片連結的後方,加入「?start=開始時間&end=結束時間」,讓播放器知道影片播放的始終,例如在網址列輸入「http://(你對外服務的IP)/result.mp4?start=5&end=10」,時間間距5秒,若播出影片總長為5秒,即為正確結果。

 


圖十二 測試影片是否僅播放自訂的時間間距

 

自訂播放器格式
 

準備好前面所要安裝、建置的所有機制,我們將自訂播放器格式,分別有HTML5 的Video元素,以及jQuery UI的slider功能,還有幾個「播放、暫停、停止」的按鈕。筆者將在隨後分享程式碼給大家,希望大家多多測試與利用。

 


圖十三 影片播放器格式

 

原始碼分享

 

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>HTML5 Video API 與 Apache mod h264 streaming 的簡易串流技巧</title>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script> <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/smoothness/jquery-ui.css">
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>

<style>
<!--
/* 播放器 - 外觀初始設定 */

video{
    width100%;
    max-width640px;
}

/* 播放器 - 時間軸 */
div#slider{
    margin0 auto;
    padding0;
    displayinline-block;
    width100%;
    max-width502px;
}

-->
</style>

<script>
$(document).ready(function(){
   
    //隨機給影片編號
    var sn = 1;
   
    //放置切割時間點用的陣列
    var arr_divide_time = new Array();
   
    //放置間距時間用的陣列
    var arr_interval_time = new Array();
   
    //影片元素
    var video = $("video");
   
    //時間軸元素
    var slider = $("#slider");
   
    //影片來源
    var path_mp4 = "http://192.168.56.100/result.mp4";
   
    //影片時間總長
    var duration = 48.9;
   
    //切割成幾份
    var divide = 5;
   
    //將切割時間分成若干個有顏色的時間軸區塊
    for(var b = 0; b < divide; b++)
    {
        slider.append("<div class='slider_color_" + sn + "'></div>");
    }
   
    //將每個時間軸區塊設定等份(百分比)
    var percentage = 100 / divide;
   
    //設定時間軸等份的 CSS 屬性/值,讓他們顏色相間
    var slider_color = $(".slider_color_" + sn);
    slider_color.filter(":even").css({
        "display":"inline-block",
        "background-color": "yellow",
        "width": percentage + "%",
        "height":"100%",
        "float":"left"
        });
    slider_color.filter(":odd").css({
        "display":"inline-block",
        "background-color""orange",
        "width": percentage + ""%,
        "height":"100%",
        "float":"left"
        });
   
   
    //每一等份是多少
    var seg = duration / divide ;
   
    //初始化資料存放
    arr_divide_time[sn] = new Array();
    arr_interval_time[sn] = new Array();
   
    //分割用字串
    var str = "";
   
    //分割字串後的陣列
    var split;
   
    //儲存每一個切割影片的時間間距 e.g. 0-9, 10-19, 20-29 ... etc
    for(var t = 1; t <= divide; t++)
    {
        arr_divide_time[sn][t] = seg * t;
        if( t == 1 )
        {
            arr_interval_time[sn][t] = "0-" + arr_divide_time[sn][t];
        }
        else
        {
            arr_interval_time[sn][t] = arr_divide_time[sn][t-1] + "-" + arr_divide_time[sn][t];
        }
    }
   
    //讓陣列變數產生初始值,以便計算時間距間
    arr_divide_time[sn][0] = 0;
   
    //放置初始 source 元素
    str = arr_interval_time[sn][1];
    split = str.split("-");
    video.prepend('<source src="' + path_mp4 + '?start=' + split[0] + '&end=' + split[1] + '"  type="video/mp4">');
   
    //計算 slider 與 video progress 時間用
    var g = 1;
   
    //讓 slider 可以控制 video 的時間軸
    slider.slider({
        range: "min",
        min: 0,
        max: parseInt( duration, 10 ),
        value: 0,
        slide: function(event_slide, ui_slide){
            //當滑動 slider 時,檢查當前需要播放的區段代號
         for(var gg = 1; gg <= divide; gg++)
            {
                if( parseFloat(arr_divide_time[sn][gg]) > parseFloat( ui_slide.value ) &&parseFloat( ui_slide.value ) >= parseFloat(arr_divide_time[sn][gg-1]) )
                {
                    g = gg;
                }
            }
        },
        start: function(event_start, ui_start){
            video[0].pause();
        },
        stop: function(event_stop, ui_stop){
            str = arr_interval_time[sn][g];
         split = str.split("-");
            video.children("source").eq(0).attr("src", path_mp4 + '?start=' + split[0] + '&end=' + split[1]);
            video[0].load();
            video[0].play();
           
            //目前時間 - 目前距間的開始時間 = 該段影片的當前播放時間
            video[0].currentTime = parseInt( parseFloat( ui_stop.value ) - parseFloat( split[0] ) );
        }
    });

    //播放器播放時,時間進度會跟 slider 同步移動
    video[0].addEventListener("timeupdate"function(){
        slider.slider('value', parseFloat(video[0].currentTime) + parseFloat(arr_divide_time[sn][g-1]));
    });
   
    //若是當現影片區間播放完了,自動跳下一個區間
    video[0].addEventListener("ended"function(){
        g++;
        video[0].pause();
        str = arr_interval_time[sn][g];
        split = str.split("-");
        video.children("source").eq(0).attr("src", path_mp4 + '?start=' + split[0] + '&end=' + split[1]);
        video[0].load();
        video[0].play();
    });
   
    //按下播放鈕
    $("#btn_start").on("click"function(){
        video[0].play();
    });
   
    //按下暫停鈕
    $("#btn_pause").on("click"function(){
        video[0].pause();
    });
   
    //按下停止鈕
    $("#btn_stop").on("click"function(){
        str = arr_interval_time[sn][1];
        split = str.split("-");
        video.children("source").eq(0).attr("src", path_mp4 + '?start=' + split[0] + '&end=' + split[1]);
        g = 1;
        slider.slider('value', 0);
        video[0].load();
    });
});
</script>
</head>
<body>

<video preload="auto">
    <!-- 都不支援,可能是瀏覽器版本老舊,要請使用者更新 -->
    <p>Your user agent does not support the HTML5 Video element.</p>
</video>
<br />
<div
 id="slider"></div>
<div>
    <input 
type="button" id="btn_start" value="播放" />
    <input
 type="button" id="btn_pause" value="暫停" />
    <input
 type="button" id="btn_stop" value="停止" />
</div>

</body>
</html>

 

後記

HTML5 Video元素的影音來源下載,是以HTTP Code 206(Partial Content)作為基礎,簡而言之,即是使用類似續傳軟體的手法,來達到檔案下載的目的。我們利用這項特點,將影片時間進行區隔,讓Video元素在抓取檔案時,僅能在特定區間內(例如第5秒至第14秒)進行下載,而不會浪費多餘的頻寬,對未來不確定是否會觀看的影片片段進行緩衝與抓取,如此一來便能達到串流的目的,並能有效地節省網路資源。

 

參考資料

[1] Ubuntu Download
http://www.ubuntu-tw.org/modules/tinyd0/
[2] Apache Web Server
http://httpd.apache.org/
[3] H264 Streaming Module for Apache
http://h264.code-shop.com/trac/wiki/Mod-H264-Streaming-Apache-Version2
[4] FFmpeg
https://www.ffmpeg.org/
[5] jQuery user interface - Slider
https://jqueryui.com/slider/
[6] HTML Audio/Video DOM Reference(查詢API)
http://www.w3schools.com/tags/ref_av_dom.asp