Streaming video with Raspberry Pi (cont.)

上一次我們知道如何使用 raspistill 以及 raspivid 來實現影像串流。現在我要來嘗試如何在 Raspberry Pi 上實現 HLS stream。

在正式開始之前,先來看一下 HLS (HTTP Live Streaming) 吧,HLS 是蘋果公司推出的一款基於 HTTP 協定的影音串流協定,其檔案格式在 Video 端是以 H.264 編碼,在 Audio 方面則是以 MP3, HE-AAC 或 AC-3 格式編碼。也因為如此,所以我們必須把從 Camear 取得的影音資料打包成 HLS 格式,所幸 FFmpeg 已經提供了這部分的功能。

Build Library

FFmpeg 是一個自由軟體,可以執行 Audio, Video 多種格式的錄影、轉檔及串流功能,其中分成幾個組件 (ex. ffmpeg, ffplayer, ffserver),這些組件也會使用像 libavcodec (audio, video codec), libavformat (audio, video converter) 等函式庫,FFmpeg 同時也可以使用外部函式庫來加強對於音視頻編碼的支援廣度。我們待會也會一併把 x264, faac, lame 編入 ffmpeg 中。

Build x264 (H264 encoder)

1
2
3
4
5
git clone git://git.videolan.org/x264
cd x264
./configure --disable-asm --enable-shared
make
make install

Build faac (AAC encoder)

1
2
3
4
5
6
wget http://downloads/sourceforge.net/project/faac/faac-src/faac-1.28/faac-1.28.tar.gz
tar xzvf faac-1.28.tar.gz
cd faac-1.28
./configure
make
make install

Build lame (MP3 encoder)

1
2
3
4
5
6
wget http://downloads.sourceforge.net/project/lame/lame/3.99/lame-3.99.tar.gz
tar xzvf lame-3.99.tar.gz
cd lame-3.99
./configure
make
make install

Build FFmpeg

1
2
3
4
5
git clone git://source.ffmpeg.org/ffmpeg.git
cd ffmpeg
./configure --enable-shared --enable-gpl --prefix=/usr/local --enable-nofree --enablelibmp3lame --enable-libfaac --enable-libx264 --enable-version3 --disable-mmx
make
make install

由於我們用到了 x264,因此在編譯 FFMpeg 時必須選用 GPL license。另外一提,在編譯 FFmpeg 其實是很花時間的工作,讓我們喝杯咖啡休息一下吧…

alt coffee

喝完咖啡,又睡了個午覺後,所有的 Library 終於都已經就位了 (/usr/local),這時候趕緊來試一下 ffmpeg 吧…咦?為什麼會發生錯誤?

1
ffmpeg: error while loading shared libraries: libavdevice.so.56: cannot open shared object file: No such file or directory

原來是 ffmpeg 會用到的函式庫 (ex. libavcodec) 所在目錄不在 library search path 裡,因此才會無法執行,這時候可以透過下述二種方式擇一進行:

Solution 1:

1
2
export LD_LIBRARY_PATH=/usr/local/lib
ffmpeg

Solution 2:

1
LD_LIBRARY_PATH=/usr/local/lib ffmpeg

重新再執行一次,耶!成功了!

1
2
3
4
ffmpeg version N-72416-gc7bd6a5 Copyright (c) 2000-2015 the FFmpeg developers
built with gcc 4.6 (Debian 4.6.3-14+rpi1)
configuration: --enable-shared --enable-gpl --prefix=/usr/local --enable-nonfree --enable-libmp3lame --enable-libfaac --enable-libx264 --enable-version3 --disable-mmx
...

Setting up the streaming server

為了能讓外部連線進來,我們先使用 python 啟用一個 http service。

1
python -m SimpleHTTPServer &

接著我們使用 raspvid 來產生我們需要的 video stream data。

1
2
3
rm -rf live*
mkfifo live.h264
raspivid -hf -vf -w 640 -h 480 -fps 25 -g 25 -b 128000 -t -o - > live.h264

我們使用 mkfifo 透過 named pipe 方式讓 FFmpeg 讀取由 raspivid 輸出的 streaming data。

1
2
3
4
5
6
7
8
9
10
11
12
13
LD_LIBRARY_PATH=/usr/local/lib ffmpeg -y \\
-i live.h264 \\
-c:v copy -b:v 128k \\
-c:a libfaac -b:a 128k \\
-map 0:0 \\
-f segment \\
-segment_time 8 \\
-segment_format mpegts \\
-segment_list "live.m3u8" \\
-segment_list_size 720 \\
-segment_list_flags live \\
-segment_list_type m3u8 \\
"live%08d.ts" < /dev/null

由於我手上的 Raspberry Pi 沒有接麥克風,所以我拿掉了 audio 部分。
好了,現在執行 VLC 打開網址 http://{the_ip}:8000/live.m3u8

alt screenshot

在完成 Video streaming server 端的設定後,我們要使用 Android 上的 VideoView 控件來實現 HLS 影像播放。
目前 Google 正在力推 Android Studio 作為 Android 開發的主要編輯器,我們就下載這套編輯器來使用吧。

截至目前最新版本是 v.1.2.1.1,功能已經相當完整,距 2013 年 Google I/O 剛發表 v0.1 版本時,其整合的功能可說是天壤之別。

alt Android Studio welcome page

在成功安裝設定完成後,我們新建一個專案 “RPiPlayer”。

alt create project step1

選擇 SDK 版本,我個人習慣是選擇 Android 4.1 (Jelly Bean)。

alt create project step2

我們選擇一個空白的 Activity。

alt create project step3

保持預設的 Activity name。

alt create project step4

好了,讓我們寫一些代碼吧。
由於需要賦予 APP 有存取網路的權限,我們打開 AndroidManifest.xml 加入:

1
2
3
...
<uses-permission android:name="android.permission.INTERNET" />
...

然後再開啟 activity_main.xml,修改成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".MainActivity">


<TextView
android:text="URL"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/url"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />


<EditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/edtUrl"
android:layout_alignParentTop="true"
android:layout_toRightOf="@+id/url"
android:layout_toLeftOf="@+id/btnGo" />


<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Go"
android:id="@+id/btnGo"
android:layout_alignParentRight="true" />


<VideoView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/videoView"
android:layout_below="@+id/edtUrl"
android:layout_marginTop="20dip"/>


</RelativeLayout>

完成後,從 Android Studio 上可以看到 APP 的 Layout

alt APP screenshot

最後我們來編輯 Activity 程式碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Override
protected void onCreate(Bundle savedInstanceState) {
...
Button btnGo = (Button) findViewById(R.id.btnGo);
btnGo.setOnClickListener(onBtnGoClicked);
}

private View.OnClickListener onBtnGoClicked = new View.OnClickListener(){

@Override
public void onClick(View v) {
EditText edtUrl = (EditText) findViewById(R.id.edtUrl);
VideoView videoView = (VideoView) findViewById(R.id.videoView);

String url = edtUrl.getText().toString();
videoView.setVideoURI(Uri.parse(url));
videoView.setMediaController(new MediaController(MainActivity.this));
videoView.start();
}
};

然後,開啟模擬器或是接上你的手機來試試,在 APP 上的 EditText 內輸入 HLS 的網址,按下 GO 按鈕。
alt APP screenshot

耶!看到影像了!


經過這個系列的文章,我們學會了如何使用 Raspberry Pi 以及 Raspberry Pi Camera module 來獲取圖片、錄影,以及輸出成 RTSP、HLS 等串流影像。
我們也製作了一個簡單的 Android APP 來觀看 HLS 串流影像。