ubuntu 21.10(arm64) + Raspberry Pi 4環境における動画ストリーミング配信(MPEG-DASH編)
February 24, 2022
Categories: ubuntu raspberrypi hardware
HLS編のついでとして、MPEG-DASHによる配信も試してみました。
今回の目標
- 自宅などの様子を外出先から見れるライブカメラをRaspberry Pi 4で作る
- DASHによるライブ配信を行う
- 動画はRaspberry Pi 4のハードウェアエンコーダを用いてH.264で圧縮する
MPEG-DASHとは?
HTTPを用いて動画をライブ配信するしくみ。やることはHLSとほとんど同じ。
ffmpegによるDASHファイル生成
DASHで配信を行う際は、H264でエンコード済みの動画をDASH用チャンクファイル(m4s)に分割し、連番を付与しつつmpdプレイリスト(manifest.mpd)を作る必要がある。 これを一発で解決するために、毎度おなじみffmpegを用いる。
今回はubuntu 21.10標準パッケージのffmpegをインストールする。
1
2
3
4
5
6
7
8
9
10
11
12
13
% sudo apt install ffmpeg
% ffmpeg -version
ffmpeg version 4.4-6ubuntu5 Copyright (c) 2000-2021 the FFmpeg developers
built with gcc 11 (Ubuntu 11.2.0-7ubuntu1)
configuration: --prefix=/usr --extra-version=6ubuntu5 --toolchain=hardened --libdir=/usr/lib/aarch64-linux-gnu --incdir=/usr/include/aarch64-linux-gnu --arch=arm64 --enable-gpl --disable-stripping --enable-gnutls --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libcodec2 --enable-libdav1d --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libjack --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librabbitmq --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libsrt --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzimg --enable-libzmq --enable-libzvbi --enable-lv2 --enable-omx --enable-openal --enable-opencl --enable-opengl --enable-sdl2 --enable-pocketsphinx --enable-librsvg --enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-chromaprint --enable-frei0r --enable-libx264 --enable-shared
libavutil 56. 70.100 / 56. 70.100
libavcodec 58.134.100 / 58.134.100
libavformat 58. 76.100 / 58. 76.100
libavdevice 58. 13.100 / 58. 13.100
libavfilter 7.110.100 / 7.110.100
libswscale 5. 9.100 / 5. 9.100
libswresample 3. 9.100 / 3. 9.100
libpostproc 55. 9.100 / 55. 9.100
ffmpeg -formats
でDASHがサポートされていることが確認できればOK。
1
2
3
% ffmpeg -formats 2>/dev/null | grep dash
DE dash DASH Muxer
DE webm_dash_manifest WebM DASH Manifest
早速実行してみる。 今回設定するオプションは下記の通り。
-f video4linux2
: 入力にv4l2を利用-input_format h264
: 入力フォーマットにH264を指定-video_size 1920x1080
: 入力動画の解像度を1920x1080に指定-framerate 30
: 入力動画のフレームレートを30fpsに指定-i /dev/video0
: 入力デバイスを/dev/video0に指定-f dash
: 出力フォーマットをDASHに指定-use_timeline 1
-use_template 1
-window_size 5
-adaptation_sets "id=0,streams=v"
-streaming 1
-seg_duration 1
-remove_at_exit 1
stream/manifest.mpd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
% mkdir -p stream
% rm -f stream/*.*
ffmpeg -f video4linux2 \
-input_format h264 \
-video_size 1920x1080 \
-framerate 30 \
-i /dev/video0 \
-f dash -c:v copy \
-use_timeline 1 \
-use_template 1 \
-window_size 5 \
-adaptation_sets "id=0,streams=v" \
-streaming 1 \
-seg_duration 1 \
-remove_at_exit 1 \
stream/manifest.mpd
カメラの接続・設定やアクセス権限の設定など、全てがうまくいっていれば、 下記のように数秒おきに動画ファイルが書き出され、プレイリストが更新されていく。
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
ffmpeg version 4.4-6ubuntu5 Copyright (c) 2000-2021 the FFmpeg developers
built with gcc 11 (Ubuntu 11.2.0-7ubuntu1)
configuration: --prefix=/usr --extra-version=6ubuntu5 --toolchain=hardened --libdir=/usr/lib/aarch64-linux-gnu --incdir=/usr/include/aarch64-linux-gnu --arch=arm64 --enable-gpl --disable-stripping --enable-gnutls --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libcodec2 --enable-libdav1d --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libjack --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librabbitmq --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libsrt --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzimg --enable-libzmq --enable-libzvbi --enable-lv2 --enable-omx --enable-openal --enable-opencl --enable-opengl --enable-sdl2 --enable-pocketsphinx --enable-librsvg --enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-chromaprint --enable-frei0r --enable-libx264 --enable-shared
libavutil 56. 70.100 / 56. 70.100
libavcodec 58.134.100 / 58.134.100
libavformat 58. 76.100 / 58. 76.100
libavdevice 58. 13.100 / 58. 13.100
libavfilter 7.110.100 / 7.110.100
libswscale 5. 9.100 / 5. 9.100
libswresample 3. 9.100 / 3. 9.100
libpostproc 55. 9.100 / 55. 9.100
Input #0, video4linux2,v4l2, from '/dev/video0':
Duration: N/A, start: 238172.345795, bitrate: N/A
Stream #0:0: Video: h264 (High), yuv420p(progressive), 1920x1080, 30 fps, 30 tbr, 1000k tbn, 2000k tbc
[dash @ 0xaaab099a7520] No bit rate set for stream 0
[dash @ 0xaaab099a7520] Opening 'stream/init-stream0.m4s' for writing
Output #0, dash, to 'stream/manifest.mpd':
Metadata:
encoder : Lavf58.76.100
Stream #0:0: Video: h264 (High), yuv420p(progressive), 1920x1080, q=2-31, 30 fps, 30 tbr, 1000k tbn, 1000k tbc
Stream mapping:
Stream #0:0 -> #0:0 (copy)
Press [q] to stop, [?] for help
[dash @ 0xaaab099a7520] Timestamps are unset in a packet for stream 0. This is deprecated and will stop working in the future. Fix your code to set the timestamps properly
[dash @ 0xaaab099a7520] Opening 'stream/chunk-stream0-00001.m4s.tmp' for writing
[dash @ 0xaaab099a7520] Non-monotonous DTS in output stream 0:0; previous: 0, current: 0; changing to 1. This may result in incorrect timestamps in the output file.
[dash @ 0xaaab099a7520] Opening 'stream/manifest.mpd.tmp' for writingd= 1.1x
[mp4 @ 0xaaab09a49770] Application provided duration: -22 / timestamp: 1965362 is out of range for mov/mp4 format
[mp4 @ 0xaaab09a49770] pts has no value
[dash @ 0xaaab099a7520] Opening 'stream/chunk-stream0-00002.m4s.tmp' for writing
[dash @ 0xaaab099a7520] Opening 'stream/manifest.mpd.tmp' for writingd=1.05x
[mp4 @ 0xaaab09a49770] Application provided duration: -22 / timestamp: 3964035 is out of range for mov/mp4 format
[mp4 @ 0xaaab09a49770] pts has no value
[dash @ 0xaaab099a7520] Opening 'stream/chunk-stream0-00003.m4s.tmp' for writing
[dash @ 0xaaab099a7520] Opening 'stream/manifest.mpd.tmp' for writingd=1.03x
[mp4 @ 0xaaab09a49770] Application provided duration: -21 / timestamp: 5962708 is out of range for mov/mp4 format
[mp4 @ 0xaaab09a49770] pts has no value
[dash @ 0xaaab099a7520] Opening 'stream/chunk-stream0-00004.m4s.tmp' for writing
[dash @ 0xaaab099a7520] Opening 'stream/manifest.mpd.tmp' for writingd=1.02x
frame= 228 fps= 31 q=-1.0 Lsize=N/A time=00:00:07.52 bitrate=N/A speed=1.02x
video:1378kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown
カメラの読み取りとDASHフォーマットでの出力がうまくできると、 streamディレクトリ以下に下記のようなファイルが出力される。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
% ls -l
total 4424
-rw------- 1 gloria trainers 345782 Feb 24 23:46 chunk-stream0-00026.m4s
-rw------- 1 gloria trainers 468022 Feb 24 23:46 chunk-stream0-00027.m4s
-rw------- 1 gloria trainers 406411 Feb 24 23:46 chunk-stream0-00028.m4s
-rw------- 1 gloria trainers 453560 Feb 24 23:46 chunk-stream0-00029.m4s
-rw------- 1 gloria trainers 374345 Feb 24 23:46 chunk-stream0-00030.m4s
-rw------- 1 gloria trainers 346165 Feb 24 23:46 chunk-stream0-00031.m4s
-rw------- 1 gloria trainers 383838 Feb 24 23:46 chunk-stream0-00032.m4s
-rw------- 1 gloria trainers 391931 Feb 24 23:46 chunk-stream0-00033.m4s
-rw------- 1 gloria trainers 371007 Feb 24 23:46 chunk-stream0-00034.m4s
-rw------- 1 gloria trainers 449495 Feb 24 23:46 chunk-stream0-00035.m4s
-rw------- 1 gloria trainers 500859 Feb 24 23:46 chunk-stream0-00036.m4s.tmp
-rw------- 1 gloria trainers 790 Feb 24 23:45 init-stream0.m4s
-rw------- 1 gloria trainers 1518 Feb 24 23:46 manifest.mpd
-rw------- 1 gloria trainers 588 Feb 24 23:46 media_0.m3u8
-rw------- 1 gloria trainers 117 Feb 24 23:45 out.m3u8
chunk-stream0-00000.m4sからの連番になっているが、-window_size 5
を設定しているため、
古いものから順に削除されている。
manifest.mpdの中身は下記のようになる。
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
<?xml version="1.0" encoding="utf-8"?>
<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="urn:mpeg:dash:schema:mpd:2011"
xmlns:xlink="http://www.w3.org/1999/xlink"
xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd"
profiles="urn:mpeg:dash:profile:isoff-live:2011"
type="dynamic"
minimumUpdatePeriod="PT1S"
suggestedPresentationDelay="PT1S"
availabilityStartTime="2022-02-24T14:47:53.221Z"
publishTime="2022-02-24T14:48:04.994Z"
timeShiftBufferDepth="PT9.9S"
maxSegmentDuration="PT1.0S"
minBufferTime="PT3.9S">
<ProgramInformation>
</ProgramInformation>
<ServiceDescription id="0">
</ServiceDescription>
<Period id="0" start="PT0.0S">
<AdaptationSet id="0" contentType="video" startWithSAP="1" segmentAlignment="true" bitstreamSwitching="true" frameRate="30/1" maxWidth="1920" maxHeight="1080" par="16:9">
<Representation id="0" mimeType="video/mp4" codecs="avc1.640028" bandwidth="1339936" width="1920" height="1080" sar="1:1">
<SegmentTemplate timescale="1000000" availabilityTimeComplete="false" initialization="init-stream$RepresentationID$.m4s" media="chunk-stream$RepresentationID$-$Number%05d$.m4s" startNumber="2">
<SegmentTimeline>
<S t="1965382" d="1998672" r="1" />
<S d="1998671" />
<S d="1998673" />
<S d="1998671" />
</SegmentTimeline>
</SegmentTemplate>
</Representation>
</AdaptationSet>
</Period>
</MPD>
次はこれらのファイルをブラウザから取得できるように、WebサーバとWebページを作成する。
動画閲覧用Webページの作成
Webブラウザ上でDASH動画を再生したい時は、dash.jsを用いる。
使い方はそれほど難しくなく、video
要素に対して読み取った動画ソースをattachするだけでよい。
video
要素にcontrols
属性を加えておくと、再生・停止や音量調整、一定の範囲内でのシークなどもできるようになる。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html>
<head>
<title>DASHのサンプル</title>
<meta charset="UTF-8">
<script src="http://cdn.dashjs.org/latest/dash.all.min.js"></script>
<script src="/static/video-dash.js"></script>
</head>
<body id="mainBox">
<h1>DASHのサンプル</h1>
<video id="sampleVideo" controls style="max-width: 95vw; max-height: 80vh;"></video>
<script type="text/javascript">
let url = '/stream/manifest.mpd';
let player = dashjs.MediaPlayer().create();
player.initialize(document.querySelector('#sampleVideo'), url, true);
</script>
</body>
</html>
ここまでのファイルが下記のように展開されている状態にし、このディレクトリをWebサーバで公開する。
1
2
3
4
5
6
7
8
9
10
11
12
13
% find .
.
./index.html
./stream
./stream/chunk-stream0-00007.m4s.tmp
./stream/manifest.mpd
./stream/chunk-stream0-00006.m4s
./stream/chunk-stream0-00005.m4s
./stream/chunk-stream0-00004.m4s
./stream/chunk-stream0-00003.m4s
./stream/chunk-stream0-00002.m4s
./stream/chunk-stream0-00001.m4s
./stream/init-stream0.m4s
Webサーバは単純に上記のHTMLファイルと動画ファイルを転送するだけなのでなんでもよい。
Python3が入っているのであれば、Python標準添付のhttp.server
モジュールを用いてWebサーバが起動できる。
1
% python3 -m http.server 8000 --bind 0.0.0.0 --directory .
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
% python3 -m http.server 8000 --bind 0.0.0.0 --directory .
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.39.1.1 - - [25/Feb/2022 00:04:34] "GET / HTTP/1.1" 200 -
10.39.1.1 - - [25/Feb/2022 00:04:34] "GET /favicon.ico HTTP/1.1" 404 -
10.39.1.1 - - [25/Feb/2022 00:04:34] code 404, message File not found
10.39.1.1 - - [25/Feb/2022 00:04:43] "GET /stream/manifest.mpd HTTP/1.1" 200 -
10.39.1.1 - - [25/Feb/2022 00:04:44] "GET /stream/init-stream0.m4s HTTP/1.1" 200 -
10.39.1.1 - - [25/Feb/2022 00:04:47] "GET /stream/manifest.mpd HTTP/1.1" 200 -
10.39.1.1 - - [25/Feb/2022 00:04:48] "GET /stream/chunk-stream0-00238.m4s HTTP/1.1" 200 -
10.39.1.1 - - [25/Feb/2022 00:04:48] "GET /stream/chunk-stream0-00239.m4s HTTP/1.1" 200 -
10.39.1.1 - - [25/Feb/2022 00:04:49] "GET /stream/manifest.mpd HTTP/1.1" 304 -
10.39.1.1 - - [25/Feb/2022 00:04:49] "GET /stream/chunk-stream0-00240.m4s HTTP/1.1" 200 -
10.39.1.1 - - [25/Feb/2022 00:04:50] "GET /stream/manifest.mpd HTTP/1.1" 200 -
10.39.1.1 - - [25/Feb/2022 00:04:50] "GET /stream/chunk-stream0-00241.m4s HTTP/1.1" 200 -
10.39.1.1 - - [25/Feb/2022 00:04:51] "GET /stream/manifest.mpd HTTP/1.1" 304 -
10.39.1.1 - - [25/Feb/2022 00:04:52] "GET /stream/manifest.mpd HTTP/1.1" 200 -
10.39.1.1 - - [25/Feb/2022 00:04:52] "GET /stream/chunk-stream0-00242.m4s HTTP/1.1" 200 -
ここまでの作業がうまくいっていれば、http://Raspberry Pi 4のIPアドレス:8000/
にブラウザからアクセスすると、動画が閲覧できる…はず。
DASHとHLSの同時配信
先ほどのDASH用コマンドに-hls_playlist
オプションと-hls_master_name
オプションを付けると、
HLS形式のプレイリストも同時に生成してくれるようになる。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
% mkdir -p stream
% rm -f stream/*.*
ffmpeg -f video4linux2 \
-input_format h264 \
-video_size 1920x1080 \
-framerate 30 \
-i /dev/video0 \
-f dash -c:v copy \
-use_timeline 1 \
-use_template 1 \
-window_size 5 \
-adaptation_sets "id=0,streams=v" \
-hls_playlist 1 \
-hls_master_name "out.m3u8" \
-streaming 1 \
-seg_duration 1 \
-remove_at_exit 1 \
stream/manifest.mpd
これで一番最初に参照されるout.m3u8
と、out.m3u8
経由で参照されるmedia_0.m3u8
が生成される。
後はout.m3u8
をhls.jsを用いて参照すれば、HLS単独版と同様に動画が再生できる。
1
2
3
4
#EXTM3U
#EXT-X-VERSION:7
#EXT-X-STREAM-INF:BANDWIDTH=2007405,RESOLUTION=1920x1080,CODECS="avc1.640028"
media_0.m3u8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#EXTM3U
#EXT-X-VERSION:6
#EXT-X-TARGETDURATION:2
#EXT-X-MEDIA-SEQUENCE:27
#EXT-X-MAP:URI="init-stream0.m4s"
#EXTINF:1.998673,
#EXT-X-PROGRAM-DATE-TIME:2022-02-25T00:13:20.932+0900
chunk-stream0-00027.m4s
#EXTINF:1.998673,
#EXT-X-PROGRAM-DATE-TIME:2022-02-25T00:13:22.931+0900
chunk-stream0-00028.m4s
#EXTINF:1.998673,
#EXT-X-PROGRAM-DATE-TIME:2022-02-25T00:13:24.930+0900
chunk-stream0-00029.m4s
#EXTINF:1.998673,
#EXT-X-PROGRAM-DATE-TIME:2022-02-25T00:13:26.928+0900
chunk-stream0-00030.m4s
#EXTINF:1.998673,
#EXT-X-PROGRAM-DATE-TIME:2022-02-25T00:13:28.927+0900
chunk-stream0-00031.m4s