無料でVODのCMAF(HLS + MPEG-DASH)の作成方法


##はじめに

こんにちは streampack チームのメディです。
https://cloudpack.jp/service/option/streampack.html


##Copyrights 
**Sintel**
© copyright Blender Foundation | www.sintel.org

#EDIT 26/7/2019
There is a simpler way to create CMAF with FFMPEG. 
FFMPEGを使用してCMAFを作成する簡単な方法があります。
Please check ・ 次のリンクをチェックしてください
https://qiita.com/mehdi/items/d5c3bb173ebbac5b0b53

##What is CMAF ・ CMAFとは
CMAF means ***Common media application format***
In order to simplify let's say that CMAF has support for both MPEG-DASH and HLS. You just need to create your video segments once. Both of the manifests (**MPD** & **m3u8**) will use the same video segments. 

CMAFは「***Common media application format***」を意味します。
単純化するため、CMAFがMPEG-DASHとHLSの両方をサポートしているとしましょう。 あなたは一度あなたのビデオセグメントを作成する必要があります。 両方のマニフェスト（**MPD** ＆ **m3u8**）は同じビデオセグメントを使用します。

##Objective・目的
Learning how to create a CMAF compliant ***on demand*** video stream.
オンデマンドのCMAFのビデオストリームの簡単な例を学ぶこと。


## Tools ・ツール
### FFMPEG
FFMPEG will encode the video file.
FFMPEGはビデオファイルをエンコードします。
I am using a static build of FFMPEG v4.1.
FFMPEG「v4.1」の静的ビルドを使用しています。
####FFMPEG static builds ・ FFMPEG静的ビルド
- OSX: https://evermeet.cx/ffmpeg/
- Linux: https://johnvansickle.com/ffmpeg/

### Bento4
Bento will create CMAF fragmented **MP4** files with the associated manifests for HLS and MPEG-Dash.
Bentoは、HLSおよびMPEG-Dashの関連するマニフェストを含むCMAF断片化された **MP4** ファイルを作成します。
https://github.com/axiomatic-systems/Bento4


##CMAF creation on OSX ・  OSXでのCMAFの作成方法

```shell 
brew install p7zip
wget https://evermeet.cx/ffmpeg/ffmpeg-4.1.7z
7za x  ffmpeg-4.1.7z
```

```shell 
rm ffmpeg-4.1.7z
```

I was not able to use the Bento4 builds for my system, so I had to compile it.
Bento4ビルドを私のシステムに使用することができなかったため、コンパイルする必要がありました。

If you want to try the Bento4 builds for your system, please check [this page.](https://www.bento4.com/downloads/)
あなたのシステム用のBento4ビルドを試したい場合は、[このページをチェックしてください。](https://www.bento4.com/downloads/)




```shell 
cd
mkdir cmaf 
cd cmaf
git init 
git clone https://github.com/axiomatic-systems/Bento4.git
```

In the Bento4 folder please open the following file with Xcode :
Bento4フォルダにXcodeで次のファイルを開きます：
`Build/Targets/universal-apple-macosx/Bento4.xcodeproj`
Then click:  ***Product > Build***
次に、***Product***> ***Build***をクリックします。
<img width="1397" alt="Screen Shot 2018-12-17 at 15.42.21.png" src="https://qiita-image-store.s3.amazonaws.com/0/229807/9ad1bcac-d7be-4f6b-7b9d-9364c4c837fb.png">

In this example, we use Xcode on OSX, for other systems & IDE, please refer to [this page](https://github.com/axiomatic-systems/Bento4#building)
この例では、OSX上でXcodeを使用しています。他のシステムとIDEについては、[このページ](https://github.com/axiomatic-systems/Bento4#building)を参照してください。

By default, on OSX, your builds will be located in:
デフォルトでは、OSXではビルドは次の場所にあります。
`~/Library/Developer/Xcode/DerivedData/`

For example in my case, the full path of the build directory is:
たとえば私の場合、ビルドディレクトリのフルパスは次のようになります。

```bash 
/Users/mehdi/Library/Developer/Xcode/DerivedData/Bento4-csvbfwwtinqvwpdlscgeezmabnlx/Build/Products/Debug
```

###Shell script  ・ シェルスクリプト
This shell script will use FFMPEG & Bento4 to generate a CMAF stream from a **mp4** video.
このシェルスクリプトは、FFMPEGとBento4を使用して **mp4** ビデオからCMAFストリームを生成します。

```bash 
#! /bin/sh

# Current working directory
cwd="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
file="${cwd}/sintel-1024-surround.mp4"
timestamp=`date +%s`
ffmpeg="${cwd}/ffmpeg" #v 4.1.
vb_array=(500 1000 2000) # video bitrates in kbps
x264encoder="libx264"  # software encoder : libx264 , OSX hardware acceleration : h264_videotoolbox
#You REALLY should use hardware acceleration :-)  https://trac.ffmpeg.org/wiki/HWAccelIntro

#TODO CHANGE FILE PATH
mp4dash="/Users/mehdi/cmaf/Bento4/Source/Python/utils/mp4-dash.py" # v1.5.1-627
#TODO CHANGE FILE PATH
bento4Builds="/Users/mehdi/Library/Developer/Xcode/DerivedData/Bento4-csvbfwwtinqvwpdlscgeezmabnlx/Build/Products/Debug" # Compiled with Xcode

echo "Creating a working directory :  $timestamp"
mkdir -p "${timestamp}"

echo "Creating an output directory  : output/${timestamp}"
mkdir -p "output/${timestamp}"

echo "Audio extraction & encoding"
ffmpeg -i "${file}" \
-f mp4 -map 0:1 -movflags frag_keyframe+empty_moov+default_base_moof+faststart  -vn -strict experimental -c:a aac -b:a 128k -f mp4 -frag_duration 4000000 "${timestamp}"/audio_only.fmp4


echo "Video extraction & encoding"
for bitrate in "${vb_array[@]}"; do
     ffmpeg -i "${file}" \
     -f mp4 -map 0:0 -movflags frag_keyframe+empty_moov+default_base_moof+faststart -bf 2 -g 90 -sc_threshold 0 -an -strict experimental -profile:v baseline -frag_duration 4000000  -c:v "${x264encoder}" -b:v "${bitrate}k" "${timestamp}"/video_only_"${bitrate}"k.fmp4
done

echo "Creating CMAF VOD streams"
${mp4dash}  -f -o ${cwd}/output/${timestamp} -v --hls --profiles=onDemand --use-segment-timeline ${cwd}/${timestamp}/*.fmp4  --exec-dir=${bento4Builds}

echo "Your files have been saved in ${cwd}/output/${timestamp}"

```

#### Created files ・ 作成されたファイル
<img width="200" alt="Screen Shot 2018-12-18 at 18.34.55.png" src="https://qiita-image-store.s3.amazonaws.com/0/229807/8f7c7f82-c937-d2a3-6b58-7110927c7da1.png">

In **video/avc1** we have 3 folders, because we chose 3 different video bitrates (500, 1000 & 2000 kbps).
**video/avc1** では、3つの異なるビデオビットレート（500,1000,2000 kbps）を選択しているので、3つのフォルダがあります。

### Javascript player : Plyr
Plyr is an open sources javascript media player.
Plyr はオープンソースのjavascriptメディアプレーヤーです。

If you are interested in Plyr, I wrote [a more detailed article.](https://qiita.com/mehdi/items/4e31f0c6dee202efeb65)
Plyr に興味がある場合は、[より詳細な記事にあります。](https://qiita.com/mehdi/items/4e31f0c6dee202efeb65)

####Note about old web browsers ・ 古いウェブブラウザについて
To support old browsers, I use the polyfill version of **plyr**.
古いブラウザをサポートするために、**plyr** の polyfill 版を使用します。

```html 
<!-- Plyr -->
  <script src="//cdn.plyr.io/3.4.7/plyr.polyfilled.js"></script>
```
I also hide the big central button ***onPlay*** to avoid issues on some browsers.
いくつかのWebブラウザでの不具合を避けるために、***onPlay*** イベントが発生したときに大きな再生ボタンを非表示にします。

```js

// To support old browsers, you may want to hide the big central play button. 
  document.getElementsByClassName('plyr__control--overlaid')[0].style.visibility = 'hidden';
```

#### Implementation  ・ 実装
 
```html
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>Plyr CMAF</title>
  <!-- Plyr style sheet -->
  <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/plyr/3.4.7/plyr.css" />
</head>

<body>
  <!-- HLS support -->
  <script src="//cdn.jsdelivr.net/hls.js/latest/hls.js"></script>
  <!-- MPEG Dash support -->
  <script src="//cdnjs.cloudflare.com/ajax/libs/dashjs/2.9.2/dash.all.debug.js"></script>
  <!-- Plyr -->
  <script src="//cdn.plyr.io/3.4.7/plyr.polyfilled.js"></script>


  <video preload="none" id="player" controls crossorigin>
    <!-- **** Don't forget to change the source paths ! **** -->
    <source src="output/1545184765/master.m3u8" type="application/x-mpegURL">
    <source src="output/1545184765/stream.mpd" type="application/dash+xml">
  </video>

</body>
<script type="text/javascript">
  var isDebug = true;
  var video = document.querySelector('#player');
  var sources = video.getElementsByTagName('source');

  // Detecting browser capabilities
  var canUseDash = false;
  var obj = document.createElement('video');
  if (obj.canPlayType('application/dash+xml')) {
    canUseDash = true;
  }

  if (canUseDash) {
    // MPEG-DASH
    var mpd = sources[1].src
    var dash = dashjs.MediaPlayer().create();
    dash.initialize(video, mpd, false);

  } else {
    // HLS
    var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);

    // Safari supports HLS natively, we don't want to use HLS.js
    if (!isSafari && Hls.isSupported()) {
      var hlsSrc = sources[0].src;
      var hls = new Hls();
      hls.loadSource(hlsSrc);
      hls.attachMedia(video);
    }
  }

  var player = new Plyr(video, {
    resetOnEnd: true,
    debug: isDebug
  });

  // To avoid  problems on old browsers, you may want to hide the big central play button when onPlay if fired
  player.on('play', function(e) {
    document.getElementsByClassName('plyr__control--overlaid')[0].style.visibility = 'hidden';
  });


  player.on('pause', function(e) {
    document.getElementsByClassName('plyr__control--overlaid')[0].style.visibility = '';
  });


</script>

</html>

```
#### Compatibility ・ウェブブラウザの互換性
**Windows specific browsers**

| Windows Version     |       Browser |    Compatibility   |
|:-----------------:|:------------------:|:------------------:|
| Windows 10     | EDGE (15,16,17,18)|        YES |
| Windows 10  |  Internet Explorer 11 |    YES |
| Windows 8.1            |         Internet Explorer 11 |        YES   |
| Windows 7 |  Internet Explorer 11 |       **NO   :-(**  |


**Mac OS specific browsers**

| OSX Version     |       Browser |    Compatibility   |
|:-----------------:|:------------------:|:------------------:|
|Mojave| Safari 12|YES|
|High Sierra|  Safari 11.1|YES|
|Sierra |Safari 10.1|YES| 
|ElCapitan| Safari 9.1|YES|

**Multi platform desktop browsers**

|       Browser |    Compatibility   |
|:-----------------:|:------------------:|
|Firefox 45|YES|
|Firefox 64|YES|
|Chrome 40|YES|
|Chrome 71|YES|

**Android**

| OS Version     |       Browser |    Compatibility   |
|:-----------------:|:------------------:|:------------------:|
|Android 4.4 |Chrome: |YES|
|Android 4.4 |Firefox: |YES
|Android 8 |Chrome: |YES|
|Android 8 |Firefox: |YES

**iOS**

| OS Version     |       Browser |    Compatibility   |
|:-----------------:|:------------------:|:------------------:|
|iOS 12|Safari 12|YES|
|iOS 12 |Chrome: |YES|
|iOS11|Safari 12|YES|
|iOS 11 |Chrome |YES|
|iOS 10|Safari 10|YES|
|iOS 10 |Chrome |YES|
|iOS 9|Safari 9|**NO :-(**|
|iOS 9|Chrome|**NO :-(**|




##Sources
#### Tools
http://bento4.com/documentation
https://www.ffmpeg.org/ffmpeg-all.html

#### A more complete CMAF creation script 
https://gist.github.com/GnaphronG/ad239570c47141d89a9282913607d83f

#### Player capabilities detection
https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canPlayType

#### Plyr javascript media player
https://github.com/sampotts/plyr

#### CMAF with AWS MediaConvert
https://qiita.com/tomopyonsama/items/e4d58af20928cab41cb5

#### About CMAF
https://blogs.akamai.com/2016/06/cmaf-what-it-is-and-why-it-may-change-your-ott-future.html
https://blogs.akamai.com/jp/2018/04/cmaf-cmaf-low-lalatency-live.html
https://developer.apple.com/documentation/http_live_streaming/about_the_common_media_application_format_with_http_live_streaming
https://bitmovin.com/what-is-cmaf-threat-opportunity/