Zum Inhalt springen

MPEG-DASH and HLS Adaptive Bitrate Streaming with ffmpeg

Last updated on 5. Mai 2020

Online learning, product explanations, image videos, online fairs, in times of Corona we need video more than ever. But video has the problem, that the files are really big and hard to stream over the internet. Additionally the bad thing in the past was, that a little smartphone screen is getting the same big video as a huge desktop monitor. Most of the time the user experience needs a good sound all the time, but not always the utter most best video quality. MPEG-DASH and HLS are providing a solution by sending different little chunks of video and audio according to the stability and capability of the internet connection. The video is not getting stuck, the sound is heard clearly all the time and only the quality of the video varies. But in the end the user is getting a seamless video experience.

Many people use YouTube for this, but if you read the community rules, you will realise, that the rights you have to give for this service to Google/Alphabet are tremendous. And do you really want your website visitor to end up on YouTube watching videos, instead of checking out the content of your website? Yes, you can also embed YouTube, but you still give them a lot of rights and insights plus your site is not rated so high for SEO since you have "second hand content", YouTube content on it and not something original.

It is not so difficult to stream directly from your own website, to keep your visitors on your site and your creative rights on the video by yourself.

-> My code was developed starting from this base

ffmpeg inputfile.mov \
    -c:v libx264 -s 640x360 -r25 -crf 21 -maxrate 1M -bufsize 2M -preset veryslow -keyint_min 100 -g 100 -sc_threshold 0  \
    -c:a aac -b:a 128k -ac 1 \
    -f hls -hls_time 4 -hls_playlist_type vod \
    stream.m3u8
command description
-c:v libx264 codec for H264
–s 640×360 size of the video
-r 25 frames (rate) per second fps 25
-crf 21 quality of the video Constant Rate Factor 17-18 visually lossless, 23 default, 0 lossless, 51 highest
-maxrate 1M maximum bitrate nevertheless of the quality factor crf
-bufsize 2M the size of blog among the maxrate in average is achieved
-preset veryslow the time the codec takes to analyse the video and to get the best compression; goes from "ultrafast" to "veryslow", default is "medium", for website video it can be "veryslow", since we only have to wait once for ffmpeg to finish decoding"
-keyint_min 100 keyframes minimum every 100 frames, if the video is 25fps this gives you minimum every 4 seconds a keyframe, apple recommends 2 seconds
-g 100 maximum amout of GOP size, maximum every 100 frames there will be a keyframe, together with -keyint_min this gives a keyframe every 100 frames
-sc_threshold 0 ffmpeg has scene detection, if a scene ends the software will set automatically a keyframe, this is distrubing here, we want equal blocks of exactly the same size; also important for buffering, 0 (stands for false) blocks this behavior
-c:a aac audio codec aac
-b:a 128k bitrate for the audio; apple recommends 32-160 kb/s
-ac 1 audio channels, here we stay with mono = 1
-f hls the output format is HLS, HTTP-Live-Streaming (for Apple devices)
-hls_time 4 the size of the segments of audio and video, shorter: faster adaption to the internet line; longer: less files to handle, less disc space; the longer the video the bigger the hls_time; Set the target segment length in seconds. Default value is 2. Segment will be cut on the next key frame after this time has passed.
-hls_playlist_type event Emit #EXT-X-PLAYLIST-TYPE:EVENT in the m3u8 header. Forces hls_list_size to 0; the playlist can only be appended to. This command forces the player not to delete old segments -> the life stream video can be searched -> a live stream can be paused; "vod" = Emit #EXT-X-PLAYLIST-TYPE:VOD in the m3u8 header. Forces hls_list_size to 0; the playlist must not change. Video on Demand "vod" MUST BE WRITTEN IN small LETTERS!

-> link to a ffmpeg H.264 manual

Different streams for diverent qualities

Apple recommendations

-> description and reference from Apple

-> Apple detailed description of HLS

  • keyframes every 2 seconds
  • profile for the codec -profile:v high

Apple size recommendations

16:9 aspect ratio H.264/AVC kb/s Frame rate
416 x 234 145 ≤ 30 fps
640 x 360 365 ≤ 30 fps
768 x 432 730 ≤ 30 fps
768 x 432 1100 ≤ 30 fps
960 x 540 2000 same as source
1280 x 720 3000 same as source
1280 x 720 4500 same as source
1920 x 1080 6000 same as source
1920 x 1080 7800 same as source

Bitrate 2000k should be default, default = first in line

Tip: For frames per second just use 25fps, 30fps and 60fps is for the US norms, derived from the old US TV-standards.

Extended code for different streams

#!/bin/bash
VIDEO_IN=/Users/thomas/Desktop/Test/test20.mov
VIDEO_OUT=master
HLS_TIME=4
FPS=25
GOP_SIZE=100
CRF_P=21
PRESET_P=veryslow
V_SIZE_1=960x540
V_SIZE_2=416x234
V_SIZE_3=640x360
V_SIZE_4=768x432
V_SIZE_5=1280x720
V_SIZE_6=1920x1080

ffmpeg -i $VIDEO_IN \
    -preset $PRESET_P -keyint_min $GOP_SIZE -g $GOP_SIZE -sc_threshold 0 -r $FPS -c:v libx264 -pix_fmt yuv420p -crf $CRF_P \
    -map v:0 -s:0 $V_SIZE_1 -maxrate:0 2M -bufsize:0 4M \
    -map v:0 -s:1 $V_SIZE_2 -maxrate:1 145k -bufsize:1 290k \
    -map v:0 -s:2 $V_SIZE_3 -maxrate:2 365k -bufsize:2 730k \
    -map v:0 -s:3 $V_SIZE_4 -maxrate:3 730k -bufsize:3 1460k \
    -map v:0 -s:4 $V_SIZE_4 -maxrate:4 1.1M -bufsize:4 2.2M \
    -map v:0 -s:5 $V_SIZE_5 -maxrate:5 3M -bufsize:5 6M \
    -map v:0 -s:6 $V_SIZE_5 -maxrate:6 4.5M -bufsize:6 9M \
    -map v:0 -s:7 $V_SIZE_6 -maxrate:7 6M -bufsize:7 12M \
    -map v:0 -s:8 $V_SIZE_6 -maxrate:8 7.8M -bufsize:8 15.6M \
    -map a:0 -map a:0 -map a:0 -map a:0 -map a:0 -map a:0 -map a:0 -map a:0 -map a:0 -c:a aac -b:a 128k -ac 1 -ar 44100\
    -f hls -hls_time $HLS_TIME -hls_playlist_type vod \
    -master_pl_name $VIDEO_OUT.m3u8 \
    -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2 v:3,a:3 v:4,a:4 v:5,a:5 v:6,a:6 v:7,a:7 v:8,a:8" stream_%v.m3u8
command description
VIDEO_IN constant for the complete path to the input video
VIDEO_OUT name of the master-file name; no path possible -> path will be the folder in which the bash-script is located; the master-file has the reference to the different stream-files, which have the reference to the video-audio files (audio and video are not seperated like in MPEG-DASH)
V_SIZE_x the different sizes of the output, sizes here as recommended by Apple
-pix_fmt yuv420p if encoding from .mov ffmpeg will produce a "yuv422p10le(tv, bt709/bt709/unknown, progressive)" pixel format that is not readable by browsers; it is a 10bit pixelformat "10le"
-map a:0 for each video we need a audio declaration (looks like a loop is going on in the background); 9 video streams 9 times the same audio in my case
-ar 44100 sample rate for audio; a CD uses 44100 this is enough; most modern cameras use 48000Hz
-hls_playlist_type vod changed to "vod", since there is no input stream, just streaming from a website
-var_stream_map "v:0,a:0 … v:8,a:8" comma seperated pairs of video,audio streams which are mapped together, in case different video quality gets different audio quality
stream_%v.m3u8 the %-sign will be replaced by the stream numbers

-> good link to ffmpeg hls

#!/bin/bash
VIDEO_IN=/Users/thomas/Desktop/Test/test20.mov
VIDEO_OUT=master
HLS_TIME=4
FPS=25
GOP_SIZE=100
CRF_P=21
PRESET_P=veryfast
V_SIZE_1=960x540
V_SIZE_2=416x234
V_SIZE_3=640x360
V_SIZE_4=768x432
V_SIZE_5=1280x720
V_SIZE_6=1920x1080

#HLS
ffmpeg -i $VIDEO_IN \
    -preset $PRESET_P -keyint_min $GOP_SIZE -g $GOP_SIZE -sc_threshold 0 -r $FPS -c:v libx264 -pix_fmt yuv420p -crf $CRF_P \
    -map v:0 -s:0 $V_SIZE_1 -maxrate:0 2M -bufsize:0 4M \
    -map v:0 -s:1 $V_SIZE_2 -maxrate:1 145k -bufsize:1 290k \
    -map v:0 -s:2 $V_SIZE_3 -maxrate:2 365k -bufsize:2 730k \
    -map v:0 -s:3 $V_SIZE_4 -maxrate:3 730k -bufsize:3 1460k \
    -map v:0 -s:4 $V_SIZE_4 -maxrate:4 1.1M -bufsize:4 2.2M \
    -map v:0 -s:5 $V_SIZE_5 -maxrate:5 3M -bufsize:5 6M \
    -map v:0 -s:6 $V_SIZE_5 -maxrate:6 4.5M -bufsize:6 9M \
    -map v:0 -s:7 $V_SIZE_6 -maxrate:7 6M -bufsize:7 12M \
    -map v:0 -s:8 $V_SIZE_6 -maxrate:8 7.8M -bufsize:8 15.6M \
    -map a:0 -map a:0 -map a:0 -map a:0 -map a:0 -map a:0 -map a:0 -map a:0 -map a:0 -c:a aac -b:a 128k -ac 1 -ar 44100\
    -f hls -hls_time $HLS_TIME -hls_playlist_type vod -hls_flags independent_segments \
    -master_pl_name $VIDEO_OUT.m3u8 \
    -hls_segment_filename stream_%v/s%06d.ts \
    -strftime_mkdir 1 \
    -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2 v:3,a:3 v:4,a:4 v:5,a:5 v:6,a:6 v:7,a:7 v:8,a:8" stream_%v.m3u8

Issues

Using the Apple mediastreamvalidator to check the code

-> Link to different Apple Tools

-> Apple Live Streaming Tools in the developer section

The mediastreamvalidator is part of a bundle and can be installed on a Mac using the normal programme install automatic for .dmg files.

Then go to Terminal navigate into the folder where the master.m3u8 is located

mediastreamvalidator master.m3u8

The answer was as follows:

MUST fix issues

Error: Measured peak bitrate compared to master playlist declared value exceeds error tolerance
--> Detail:  Measured: 3167.80 kb/s, Master playlist: 5090.80 kb/s, Error: 37.77%
--> Source:  master.m3u8
--> Compare: stream_6.m3u8

--> Detail:  Measured: 5919.37 kb/s, Master playlist: 6740.80 kb/s, Error: 12.19%
--> Source:  master.m3u8
--> Compare: stream_7.m3u8

--> Detail:  Measured: 5919.37 kb/s, Master playlist: 8720.80 kb/s, Error: 32.12%
--> Source:  master.m3u8
--> Compare: stream_8.m3u8

--> Detail:  Measured: 1052.42 kb/s, Master playlist: 943.80 kb/s, Error: 11.51%
--> Source:  master.m3u8
--> Compare: stream_3.m3u8

--> Detail:  Measured: 1507.76 kb/s, Master playlist: 1350.80 kb/s, Error: 11.62%
--> Source:  master.m3u8
--> Compare: stream_4.m3u8

Explanation

-> Explanation for max-bitrates from the Apple spezification

  • 1.26. For VOD content, the average segment bit rate MUST be within 10% of the AVERAGE-BANDWIDTH attribute. (See Declared versus measured values of bandwidths.)
  • 1.27. For VOD content, the measured peak bit rate MUST be within 10% of the BANDWIDTH attribute.
  • 1.28. For live/linear content, the average segment bit rate over a long (~1 hour) period of time MUST be less than 110% of the AVERAGE-BANDWIDTH attribute.
  • 1.29. For live/linear content, the measured peak bit rate MUST be less than 125% of the BANDWIDTH attribute.

My idea of giving the encoder the freedom to go down to lower bitrates in easy to code areas by giving the encoder only a "-crf" qualtiy factor for encoding and then only set a top limit with the "-maxrate" statement doesn't work. In the first three error messages my real bitrate is lower then the declared bitrate by 37.7%, 32.12% and 12%. Stream 3 and Stream 4 are a little bit higher than the declared bitrate.

Conclusion

Instead of using a encoding quality factor "-crf" I have to use a set bitrate with "-b:v:X" for every stream and ommit the "-crf" statement. To fine-tune the we can change the -bufsize:v:X between 150% of the set bitrate to 200% of the set bitrate. I took 175%. The bufsize is the area in witch the bitrate is calculated and adjusted by the encoder. The -maxrate:v:X can be set 7% (10%) above the set bitrate of -b:v:X.

#!/bin/bash
VIDEO_IN=/Users/thomas/Desktop/Test/test20.mov
VIDEO_OUT=master
HLS_TIME=4
FPS=25
GOP_SIZE=100
PRESET_P=veryfast
V_SIZE_1=960x540
V_SIZE_2=416x234
V_SIZE_3=640x360
V_SIZE_4=768x432
V_SIZE_5=1280x720
V_SIZE_6=1920x1080

# HLS
ffmpeg -i $VIDEO_IN \
    -preset $PRESET_P -keyint_min $GOP_SIZE -g $GOP_SIZE -sc_threshold 0 -r $FPS -c:v libx264 -pix_fmt yuv420p \
    -map v:0 -s:0 $V_SIZE_1 -b:v:0 2M -maxrate:0 2.14M -bufsize:0 3.5M \
    -map v:0 -s:1 $V_SIZE_2 -b:v:1 145k -maxrate:1 155k -bufsize:1 220k \
    -map v:0 -s:2 $V_SIZE_3 -b:v:2 365k -maxrate:2 390k -bufsize:2 640k \
    -map v:0 -s:3 $V_SIZE_4 -b:v:3 730k -maxrate:3 781k -bufsize:3 1278k \
    -map v:0 -s:4 $V_SIZE_4 -b:v:4 1.1M -maxrate:4 1.17M -bufsize:4 2M \
    -map v:0 -s:5 $V_SIZE_5 -b:v:5 3M -maxrate:5 3.21M -bufsize:5 5.5M \
    -map v:0 -s:6 $V_SIZE_5 -b:v:6 4.5M -maxrate:6 4.8M -bufsize:6 8M \
    -map v:0 -s:7 $V_SIZE_6 -b:v:7 6M -maxrate:7 6.42M -bufsize:7 11M \
    -map v:0 -s:8 $V_SIZE_6 -b:v:8 7.8M -maxrate:8 8.3M -bufsize:8 14M \
    -map a:0 -map a:0 -map a:0 -map a:0 -map a:0 -map a:0 -map a:0 -map a:0 -map a:0 -c:a aac -b:a 128k -ac 1 -ar 44100 \
    -f hls -hls_time $HLS_TIME -hls_playlist_type vod -hls_flags independent_segments \
    -master_pl_name $VIDEO_OUT.m3u8 \
    -hls_segment_filename stream_%v/s%06d.ts \
    -strftime_mkdir 1 \
    -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2 v:3,a:3 v:4,a:4 v:5,a:5 v:6,a:6 v:7,a:7 v:8,a:8" stream_%v.m3u8

For stream_1 I still got error messages so I changed to "-bufsize:1 220k", that is 152%.

Adding MPEG-DASH streaming

MPEG-DASH is the new standard for Adaptive Bitrate Streaming. It is not supported directly by the browsers, but works with the help of the dash.js JavaScript-Player. This is the solution for all but Apple devices.

More about dash.js: cdn.dashjs.org

DASH

ffmpeg -i $VIDEO_IN \
    -preset $PRESET_P -keyint_min $GOP_SIZE -g $GOP_SIZE -sc_threshold 0 \
    -r $FPS -c:v libx264 -pix_fmt yuv420p -c:a aac -b:a 128k -ac 1 -ar 44100 \
    -map v:0 -s:0 $V_SIZE_1 -b:v:0 2M -maxrate:0 2.14M -bufsize:0 3.5M \
    -map v:0 -s:1 $V_SIZE_2 -b:v:1 145k -maxrate:1 155k -bufsize:1 220k \
    -map v:0 -s:2 $V_SIZE_3 -b:v:2 365k -maxrate:2 390k -bufsize:2 640k \
    -map v:0 -s:3 $V_SIZE_4 -b:v:3 730k -maxrate:3 781k -bufsize:3 1278k \
    -map v:0 -s:4 $V_SIZE_4 -b:v:4 1.1M -maxrate:4 1.17M -bufsize:4 2M \
    -map v:0 -s:5 $V_SIZE_5 -b:v:5 3M -maxrate:5 3.21M -bufsize:5 5.5M \
    -map v:0 -s:6 $V_SIZE_5 -b:v:6 4.5M -maxrate:6 4.8M -bufsize:6 8M \
    -map v:0 -s:7 $V_SIZE_6 -b:v:7 6M -maxrate:7 6.42M -bufsize:7 11M \
    -map v:0 -s:8 $V_SIZE_6 -b:v:8 7.8M -maxrate:8 8.3M -bufsize:8 14M \
    -map 0:a \
    -init_seg_name init\$RepresentationID\$.\$ext\$ -media_seg_name chunk\$RepresentationID\$-\$Number%05d\$.\$ext\$ \
    -use_template 1 -use_timeline 1  \
    -seg_duration 4 -adaptation_sets "id=0,streams=v id=1,streams=a" \
    -f dash Dash/dash.mpd

ffmpeg commands for DASH

command description
VIDEO_IN constant for the complete path to the input video
-preset veryslow the time the codec takes to analyse the video and to get the best compression; goes from "ultrafast" to "veryslow", default is "medium", for website video it can be "veryslow", since this is only the time we once have to wait for ffmpeg to finish decoding"
-keyint_min 100 keyframes minimum every 100 frames, if the video is 25fps this gives you minimum every 4 seconds a keyframe, apple recommends 2 seconds
-g 100 maximum amout of GOP size, maximum every 100 frames there will be a keyframe, together with -keyint_min this gives a keyframe every 100 frames
-sc_threshold 0 ffmpeg has scene detection, if a scene ends the software will set automatically a keyframe, this is distrubing here, we want equal blocks of exactly the same size; also important for buffering, 0 (stands for false) blocks this behavior
-r 25 frames (rate) per second fps 25
-c:v libx264 codec for H264
-pix_fmt yuv420p if encoding from .mov ffmpeg will produce a "yuv422p10le(tv, bt709/bt709/unknown, progressive)" pixel format that is not readable by browsers; it is a 10bit pixelformat "10le"
-c:a aac audio codec aac
-b:a 128k bitrate for the audio; apple recommends 32-160 kb/s
-ac 1 audio channels, here we stay with mono = 1
-ar 44100 sample rate for audio; a CD uses 44100 Hz this is enough; most modern cameras use 48000 Hz
-map v:0 meaning: take the first video stream (index 0) and do something with it …
-s:0 the size of the first (index 0) output stream e.g. 1920×1080
-b:v:1 the bitrate of the second (index 1) output video stream, ist exepts k=kilo bytes/second and m=meg mytes/second
-maxrate 1M maximum bitrate during a range of video chunk (expressed through bufsize) in witch the maxrate is calculated
-bufsize 2M the size of blog among the maxrate in average is achieved
-map 0:a we only want one audio file to output for all video sizes, so here only one -map-command is listed for the output, if we would want more audio streams we would have to have two -map-commands …, we take from all inputs "0" the audio "a"; ( "-map 0" would take all inputs to do something with them)
-init_seg_name init\$RepresentationID\$.\$ext\$ This gives the init-file a name. The default name is "init-stream" since this are many files I shortend the name to "init". If used with a dash-script the dollar sign must be escaped for the ffmpeg variables "$var$" "\".
-init_seg_name init\$RepresentationID\$.\$ext\$ This gives the init-file a name. The default name is "init-stream" since this are many files I shortend the name to "init". If used with a dash-script in the ffmpeg variables e.g. "$var$" the dollar sign must be escaped "\".
-media_seg_name chunk\$RepresentationID\$-\$Number%05d\$.\$ext\$ This gives the video and audio files a name. The default name is "chunk-stream". There will be a lot of chunks, so I shortend the name to "chunk" to save some download time. Fot the bash-script we need to escape the dollar signs.
-use_template 1 In order not to list every single chunk of video or audio in the dash manifest it is better to use a template, that shows how to access the right chunk of video, instead of a long, long list. The template works with variables and patterns, instead of listing every chunk of video and audio, to choose from.
-use_timeline 1 Enable (1) or disable (0) the use of a SegmentTimline inside a SegmentTemplate. The SegmentTimeline provides more possibilities then the duration attribute. It also enables to specify exact segment durations.
-seg_duration The length of an individual segment. Only between segments the player can switch bandwidth. Default 8 seconds, I took 4.
-adaptation_sets "id=0,streams=v id=1,streams=a" Adaption set is a term from the Dash specification. Adaption Sets contain a media stream or many of them. In many cases you have one video adaption set and for each language a audio adaption set. In our case we have one adaption set (index 0) with different video streams and one adaption set (index 1) with one audio stream.
-f dash format is dash
Dash/dash.mpd all output goes into the Dash folder, the manifest is dash.mpd
Recommendations of the ffmpeg documantation

-profie:v:1 baseline -provile:v0 main -b_strategy 0 -bf 1

Apple is suggesting "-profile:v high" for the H264 codec to be used on HLS, but in most wikis it is recommended to leave the decission about the codec profile to the ffmpeg encoder libx264. The same applies for the B-frames and how to use them. Default is a maximum of 16 B-frames and the "-b_strategy" of "1 / Fast". "0" would be very fast and is in the ffmpeg documentation used for the Dash commands. But also here it is better to leave the decission to the encoder. More information is on Google-Sites.

Putting everything together as a bash-script

HLS plus MPEG-DASH plus fallback file

Copy the script in a file.sh, put it into your project folder. Navigate in the bash or terminal into the folder and call the script:

bash file.sh

The script expects a "HLS" and a "Dash" folder inside your project folder. The path to your video is set with the "VIDEO_IN" configuration constant. If you want a good quality change "PRESET_P" to "veryslow"!

#!/bin/bash
#HLS, Dash and fallback code from zazu.berlin 2020, Version 20200424

VIDEO_IN=/Users/thomas/Desktop/apache-official/TestB/test20.mov
VIDEO_OUT=master
HLS_TIME=4
FPS=25
GOP_SIZE=100
PRESET_P=veryslow
V_SIZE_1=960x540
V_SIZE_2=416x234
V_SIZE_3=640x360
V_SIZE_4=768x432
V_SIZE_5=1280x720
V_SIZE_6=1920x1080

# HLS
ffmpeg -i $VIDEO_IN -y \
    -preset $PRESET_P -keyint_min $GOP_SIZE -g $GOP_SIZE -sc_threshold 0 -r $FPS -c:v libx264 -pix_fmt yuv420p \
    -map v:0 -s:0 $V_SIZE_1 -b:v:0 2M -maxrate:0 2.14M -bufsize:0 3.5M \
    -map v:0 -s:1 $V_SIZE_2 -b:v:1 145k -maxrate:1 155k -bufsize:1 220k \
    -map v:0 -s:2 $V_SIZE_3 -b:v:2 365k -maxrate:2 390k -bufsize:2 640k \
    -map v:0 -s:3 $V_SIZE_4 -b:v:3 730k -maxrate:3 781k -bufsize:3 1278k \
    -map v:0 -s:4 $V_SIZE_4 -b:v:4 1.1M -maxrate:4 1.17M -bufsize:4 2M \
    -map v:0 -s:5 $V_SIZE_5 -b:v:5 3M -maxrate:5 3.21M -bufsize:5 5.5M \
    -map v:0 -s:6 $V_SIZE_5 -b:v:6 4.5M -maxrate:6 4.8M -bufsize:6 8M \
    -map v:0 -s:7 $V_SIZE_6 -b:v:7 6M -maxrate:7 6.42M -bufsize:7 11M \
    -map v:0 -s:8 $V_SIZE_6 -b:v:8 7.8M -maxrate:8 8.3M -bufsize:8 14M \
    -map a:0 -map a:0 -map a:0 -map a:0 -map a:0 -map a:0 -map a:0 -map a:0 -map a:0 -c:a aac -b:a 128k -ac 1 -ar 44100\
    -f hls -hls_time $HLS_TIME -hls_playlist_type vod -hls_flags independent_segments \
    -master_pl_name $VIDEO_OUT.m3u8 \
    -hls_segment_filename HLS/stream_%v/s%06d.ts \
    -strftime_mkdir 1 \
    -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2 v:3,a:3 v:4,a:4 v:5,a:5 v:6,a:6 v:7,a:7 v:8,a:8" HLS/stream_%v.m3u8

# DASH
ffmpeg -i $VIDEO_IN -y \
    -preset $PRESET_P -keyint_min $GOP_SIZE -g $GOP_SIZE -sc_threshold 0 -r $FPS -c:v libx264 -pix_fmt yuv420p -c:a aac -b:a 128k -ac 1 -ar 44100 \
    -map v:0 -s:0 $V_SIZE_1 -b:v:0 2M -maxrate:0 2.14M -bufsize:0 3.5M \
    -map v:0 -s:1 $V_SIZE_2 -b:v:1 145k -maxrate:1 155k -bufsize:1 220k \
    -map v:0 -s:2 $V_SIZE_3 -b:v:2 365k -maxrate:2 390k -bufsize:2 640k \
    -map v:0 -s:3 $V_SIZE_4 -b:v:3 730k -maxrate:3 781k -bufsize:3 1278k \
    -map v:0 -s:4 $V_SIZE_4 -b:v:4 1.1M -maxrate:4 1.17M -bufsize:4 2M \
    -map v:0 -s:5 $V_SIZE_5 -b:v:5 3M -maxrate:5 3.21M -bufsize:5 5.5M \
    -map v:0 -s:6 $V_SIZE_5 -b:v:6 4.5M -maxrate:6 4.8M -bufsize:6 8M \
    -map v:0 -s:7 $V_SIZE_6 -b:v:7 6M -maxrate:7 6.42M -bufsize:7 11M \
    -map v:0 -s:8 $V_SIZE_6 -b:v:8 7.8M -maxrate:8 8.3M -bufsize:8 14M \
    -map 0:a \
    -init_seg_name init\$RepresentationID\$.\$ext\$ -media_seg_name chunk\$RepresentationID\$-\$Number%05d\$.\$ext\$ \
    -use_template 1 -use_timeline 1  \
    -seg_duration 4 -adaptation_sets "id=0,streams=v id=1,streams=a" \
    -f dash Dash/dash.mpd

# Fallback video file
ffmpeg -i $VIDEO_IN -y -c:v libx264 -pix_fmt yuv420p -r $FPS -s $V_SIZE_1 -b:v 1.8M -c:a aac -b:a 128k -ac 1 -ar 44100 fallback-video-$V_SIZE_1.mp4

`

The HTML code for the streaming site

The HTML5 video element will choose the file to play in the order they are noted. Since MPEG-DASH is controlled by JavaScript it will be tried first, then HLS and if both do not work there is a regular video file in a medium size and bitrate.

The type declaration is according to the norm as follows:

  • application/vnd.apple.mpegurl
  • video/mp4
  • application/dash+xml

Make shure, that the server knows the types or add them to the .htaccess.

code on the video html5 site

<video id="videoPlayer" autoplay="autoplay" controls="controls" width="960" height="540">
  <source src="myProjectFolder/HLS/master.m3u8" type="application/vnd.apple.mpegurl" />
  <source src="myProjectFolder/fallback-video-960x540.mp4" type="video/mp4" />
</video>

<!-- dash-player - no browser is natively playing DASH -->
<script src="/JavaScript/dash.all.min.js"></script>
<script>

    /* initialize the dash.js player in the #videoPlayer element */
    (function () {
        let url = "myProjectFolder/Dash/dash.mpd";
        player = dashjs.MediaPlayer().create();
        player.initialize(document.querySelector("#videoPlayer"), url, false);
    })();

</script>

Note: Some browsers took over the behavior of Apple devices to auto-play videos only if they are silenced.

The dash.js player has a lot of APIs and hooks. More information you will find on: cdn.dashjs.org

Here is an example with chapter navigation from a json-file

Open tasks

  • The HLS and the DASH video conversion blocks are just the same, reuse the first encoded files from HLS also in Dash and run it all in one ffmpeg command.
  • Add a HTML document with rich snippets and twitter cards and embed it as an iframe.

Final thoughts

Enrich your company and don't make YouTube even richer. Stream from your own website and fight for data democracy!

Author: Thomas Hezel, zazu.berlin 2020

Abbonieren Sie diesen Blog

Ein Kommentar

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

*

code