MP4 to DASH (bash script)

2019-07-15 05:33发布

问题:

I have a web site in which users can upload video files. I want to stream all of them using DASH to obtain an adaptive bitrate streaming. So I wrote a bash script (to run by cron) that converts all mp4 files to DASH, but it doesn't work properly: what is wrong?

For example, using the following script, I obtained: https://www.informatica-libera.net/dash_faq/stream.mpd

It validates, but it doesn't play. I tested it on: http://dash-mse-test.appspot.com/dash-player.html?url=https%3A%2F%2Fwww.informatica-libera.net%2Fdash_faq%2Fstream.mpd&autoplay=on&adapt=auto&flavor=

Thank you for any help. The code:

#!/bin/bash

# THIS SCRIPT CONVERTS EVERY MP4 (IN THE CURRENT FOLDER AND SUBFOLDER)
# TO A MULTI-BITRATE VIDEO IN MP4-DASH
# For each file "videoname.mp4" it creates a folder "dash_videoname" 
# containing a dash manifest file "stream.mpd" and subfolders containing 
# video segments.

# mp4dash documentation and download: https://www.bento4.com/developers/dash/

MYDIR=$(dirname $(readlink -f ${BASH_SOURCE[0]}))
SAVEDIR=$(pwd)

# Check programs
if [ -z "$(which ffmpeg)" ]; then
    echo "Error: ffmpeg is not installed"
    exit 1
fi

if [ -z "$(which mp4dash)" ]; then
    echo "Error: mp4dash is not installed"
    exit 1
fi

cd "$MYDIR"

TARGET_FILES=$(find ./ -type f -name "*.mp4")
for f in $TARGET_FILES
do
  f=$(basename "$f") # fullname of the file
  f="${f%.*}" # name without extension

  if [ ! -d "dash_${f}" ]; then
    echo "Converting \"$f\" to multi-bitrate video in MPEG-DASH"

    ffmpeg -y -i "${f}.mp4" -c:a libfdk_aac -ac 2 -ab 128k -c:v libx264 -x264opts 'keyint=24:min-keyint=24:no-scenecut' -b:v 1500k -vf "scale=-2:720" -f mp4 -pass 1 -y /dev/null
    ffmpeg -y -i "${f}.mp4" -c:a libfdk_aac -ac 2 -ab 128k -c:v libx264 -x264opts 'keyint=24:min-keyint=24:no-scenecut' -b:v 1500k -vf "scale=-2:720" -f mp4 -pass 2 "${f}_1500.mp4"

    ffmpeg -y -i "${f}.mp4" -c:a libfdk_aac -ac 2 -ab 128k -c:v libx264 -x264opts 'keyint=24:min-keyint=24:no-scenecut' -b:v 800k -vf "scale=-2:540" -f mp4 -pass 1 -y /dev/null
    ffmpeg -y -i "${f}.mp4" -c:a libfdk_aac -ac 2 -ab 128k -c:v libx264 -x264opts 'keyint=24:min-keyint=24:no-scenecut' -b:v 800k -vf "scale=-2:540" -f mp4 -pass 2 "${f}_800.mp4"

    ffmpeg -y -i "${f}.mp4" -c:a libfdk_aac -ac 2 -ab 128k -c:v libx264 -x264opts 'keyint=24:min-keyint=24:no-scenecut' -b:v 400k -vf "scale=-2:360" -f mp4 -pass 1 -y /dev/null
    ffmpeg -y -i "${f}.mp4" -c:a libfdk_aac -ac 2 -ab 128k -c:v libx264 -x264opts 'keyint=24:min-keyint=24:no-scenecut' -b:v 400k -vf "scale=-2:360" -f mp4 -pass 2 "${f}_400.mp4"

    ffmpeg -y -i "${f}.mp4" -c:a libfdk_aac -ac 2 -ab 128k -c:v libx264 -x264opts 'keyint=24:min-keyint=24:no-scenecut' -b:v 200k -vf "scale=-2:180" -f mp4 -pass 1 -y /dev/null
    ffmpeg -y -i "${f}.mp4" -c:a libfdk_aac -ac 2 -ab 128k -c:v libx264 -x264opts 'keyint=24:min-keyint=24:no-scenecut' -b:v 200k -vf "scale=-2:180" -f mp4 -pass 2 "${f}_200.mp4"

    rm -f ffmpeg*log*

    mp4fragment "${f}_1500.mp4" "${f}_1500_fragmented.mp4"
    mp4fragment "${f}_800.mp4" "${f}_800_fragmented.mp4"
    mp4fragment "${f}_400.mp4" "${f}_400_fragmented.mp4"
    mp4fragment "${f}_200.mp4" "${f}_200_fragmented.mp4"

    rm -f "${f}_1500.mp4" "${f}_800.mp4" "${f}_400.mp4" "${f}_200.mp4"

    mp4dash -v -o "dash_${f}" "${f}_1500_fragmented.mp4" "${f}_800_fragmented.mp4" "${f}_400_fragmented.mp4" "${f}_200_fragmented.mp4"

    rm -f "${f}_1500_fragmented.mp4" "${f}_800_fragmented.mp4" "${f}_400_fragmented.mp4" "${f}_200_fragmented.mp4"

    fi

done

cd "$SAVEDIR"

回答1:

I solved so (however it's not fully compatible with every modern browser, for example on my Linux version of Firefox the audio doesn't play):

#!/bin/bash

# THIS SCRIPT CONVERTS EVERY MP4 (IN THE CURRENT FOLDER AND SUBFOLDER) TO A MULTI-BITRATE VIDEO IN MP4-DASH
# For each file "videoname.mp4" it creates a folder "dash_videoname" containing a dash manifest file "stream.mpd" and subfolders containing video segments.

# Validation tool:
# http://dashif.org/conformance.html

# Documentation:
# https://tdngan.wordpress.com/2016/11/17/how-to-encode-multi-bitrate-videos-in-mpeg-dash-for-mse-based-media-players/

# Remember to add the following mime-types (uncommented) to .htaccess:
# AddType video/mp4 m4s
# AddType application/dash+xml mpd

# DASH-264 JavaScript Reference Client
# https://github.com/Dash-Industry-Forum/dash.js
# https://github.com/Dash-Industry-Forum/dash.js/wiki

MYDIR=$(dirname $(readlink -f ${BASH_SOURCE[0]}))
SAVEDIR=$(pwd)

# Check programs
if [ -z "$(which ffmpeg)" ]; then
    echo "Error: ffmpeg is not installed"
    exit 1
fi

if [ -z "$(which MP4Box)" ]; then
    echo "Error: MP4Box is not installed"
    exit 1
fi

cd "$MYDIR"

TARGET_FILES=$(find ./ -type f -name "*.mp4")
for f in $TARGET_FILES
do
  f=$(basename "$f") # fullname of the file
  f="${f%.*}" # name without extension

  if [ ! -d "dash_${f}" ]; then
    echo "Converting \"$f\" to multi-bitrate video in MPEG-DASH"

    ffmpeg -y -i "${f}.mp4" -c:a libfdk_aac -ac 2 -ab 128k -vn "${f}_audio.m4a"

    ffmpeg -y -i "${f}.mp4" -an -c:v libx264 -x264opts 'keyint=24:min-keyint=24:no-scenecut' -b:v 1500k -vf "scale=-2:720" -f mp4 -pass 1 -y /dev/null
    ffmpeg -y -i "${f}.mp4" -an -c:v libx264 -x264opts 'keyint=24:min-keyint=24:no-scenecut' -b:v 1500k -vf "scale=-2:720" -f mp4 -pass 2 "${f}_1500.mp4"

    ffmpeg -y -i "${f}.mp4" -an -c:v libx264 -x264opts 'keyint=24:min-keyint=24:no-scenecut' -b:v 800k -vf "scale=-2:540" -f mp4 -pass 1 -y /dev/null
    ffmpeg -y -i "${f}.mp4" -an -c:v libx264 -x264opts 'keyint=24:min-keyint=24:no-scenecut' -b:v 800k -vf "scale=-2:540" -f mp4 -pass 2 "${f}_800.mp4"

    ffmpeg -y -i "${f}.mp4" -an -c:v libx264 -x264opts 'keyint=24:min-keyint=24:no-scenecut' -b:v 400k -vf "scale=-2:360" -f mp4 -pass 1 -y /dev/null
    ffmpeg -y -i "${f}.mp4" -an -c:v libx264 -x264opts 'keyint=24:min-keyint=24:no-scenecut' -b:v 400k -vf "scale=-2:360" -f mp4 -pass 2 "${f}_400.mp4"

    ffmpeg -y -i "${f}.mp4" -an -c:v libx264 -x264opts 'keyint=24:min-keyint=24:no-scenecut' -b:v 200k -vf "scale=-2:180" -f mp4 -pass 1 -y /dev/null
    ffmpeg -y -i "${f}.mp4" -an -c:v libx264 -x264opts 'keyint=24:min-keyint=24:no-scenecut' -b:v 200k -vf "scale=-2:180" -f mp4 -pass 2 "${f}_200.mp4"

    rm -f ffmpeg*log*

    MP4Box -dash 2000 -rap -frag-rap -profile onDemand "${f}_1500.mp4" "${f}_800.mp4" "${f}_400.mp4" "${f}_200.mp4" "${f}_audio.m4a" -out "${f}_MP4.mpd"


    rm  "${f}_1500.mp4" "${f}_800.mp4" "${f}_400.mp4" "${f}_200.mp4" "${f}_audio.m4a"

    fi

done

cd "$SAVEDIR"

I also tried VP9 instead of h.264, but also in this case there isn't compatibility with all browser (in my Linux distro, it plays correctly only on Firefox, while it doesn't play at all on Chrome):

#!/bin/bash
MYDIR=$(dirname $(readlink -f ${BASH_SOURCE[0]}))
SAVEDIR=$(pwd)

# Controlla che i programmi richiesti siano installati
if [ -z "$(which ffmpeg)" ]; then
    echo "Errore: ffmpeg non e' installato"
    exit 1
fi

cd "$MYDIR"

TARGET_FILES=$(find ./ -type f -name "*.mp4")
for f in $TARGET_FILES
do
  f=$(basename "$f") # memorizza il nome completo del file
  f="${f%.*}" # toglie l'estensione
  if [ ! -f "${f}.mpd" ]; then
    echo "Converto il file \"$f\" in Adaptive WebM using DASH"
    echo "Riferimenti: http://wiki.webmproject.org/adaptive-streaming/instructions-to-playback-adaptive-webm-using-dash"

    # http://wiki.webmproject.org/adaptive-streaming/instructions-to-playback-adaptive-webm-using-dash

    VP9_DASH_PARAMS="-tile-columns 4 -frame-parallel 1"

    ffmpeg -i "${f}.mp4" -c:v libvpx-vp9 -vf scale=-1:90 -b:v 250k -keyint_min 150 -g 150 ${VP9_DASH_PARAMS} -an -f webm -dash 1 -pass 1 -y /dev/null
    ffmpeg -i "${f}.mp4" -c:v libvpx-vp9 -vf scale=-1:90 -b:v 250k -keyint_min 150 -g 150 ${VP9_DASH_PARAMS} -an -f webm -dash 1 -pass 2 "${f}_160px_250k.webm"

    ffmpeg -i "${f}.mp4" -c:v libvpx-vp9 -vf scale=-1:180 -b:v 500k -keyint_min 150 -g 150 ${VP9_DASH_PARAMS} -an -f webm -dash 1 -pass 1 -y /dev/null
    ffmpeg -i "${f}.mp4" -c:v libvpx-vp9 -vf scale=-1:180 -b:v 500k -keyint_min 150 -g 150 ${VP9_DASH_PARAMS} -an -f webm -dash 1 -pass 2 "${f}_320px_500k.webm"

    ffmpeg -i "${f}.mp4" -c:v libvpx-vp9 -vf scale=-1:360 -b:v 750k -keyint_min 150 -g 150 ${VP9_DASH_PARAMS} -an -f webm -dash 1 -pass 1 -y /dev/null
    ffmpeg -i "${f}.mp4" -c:v libvpx-vp9 -vf scale=-1:360 -b:v 750k -keyint_min 150 -g 150 ${VP9_DASH_PARAMS} -an -f webm -dash 1 -pass 2 "${f}_640px_750k.webm"

    ffmpeg -i "${f}.mp4" -c:v libvpx-vp9 -vf scale=-1:360 -b:v 1000k -keyint_min 150 -g 150 ${VP9_DASH_PARAMS} -an -f webm -dash 1 -pass 1 -y /dev/null
    ffmpeg -i "${f}.mp4" -c:v libvpx-vp9 -vf scale=-1:360 -b:v 1000k -keyint_min 150 -g 150 ${VP9_DASH_PARAMS} -an -f webm -dash 1 -pass 2 "${f}_640px_1000k.webm"

    ffmpeg -i "${f}.mp4" -c:v libvpx-vp9 -vf scale=-1:720 -b:v 1500k -keyint_min 150 -g 150 ${VP9_DASH_PARAMS} -an -f webm -dash 1 -pass 1 -y /dev/null
    ffmpeg -i "${f}.mp4" -c:v libvpx-vp9 -vf scale=-1:720 -b:v 1500k -keyint_min 150 -g 150 ${VP9_DASH_PARAMS} -an -f webm -dash 1 -pass 2 "${f}_1280px_1500k.webm"

    ffmpeg -i "${f}.mp4" -c:a libvorbis -b:a 128k -vn -f webm -dash 1 "${f}_audio_128k.webm"


    rm -f ffmpeg*.log

    ffmpeg \
    -f webm_dash_manifest -i "${f}_160px_250k.webm" \
    -f webm_dash_manifest -i "${f}_320px_500k.webm" \
    -f webm_dash_manifest -i "${f}_640px_750k.webm" \
    -f webm_dash_manifest -i "${f}_640px_1000k.webm" \
    -f webm_dash_manifest -i "${f}_1280px_1500k.webm" \
    -f webm_dash_manifest -i "${f}_audio_128k.webm" \
    -c copy -map 0 -map 1 -map 2 -map 3 -map 4 -map 5 \
    -f webm_dash_manifest \
    -adaptation_sets "id=0,streams=0,1,2,3,4 id=1,streams=5" \
    "${f}.mpd"

    fi

done

cd "$SAVEDIR"

I didn't find a way to serve audio/video content to all browsers. I've done my tests so:

<!DOCTYPE html>
<html>
<head>
<script src="http://cdn.dashjs.org/latest/dash.all.min.js"></script>
<style>
    video {
       width: 640px;
       height: 360px;
    }
</style>
</head>
<body>
<div>
       <video data-dashjs-player autoplay src="test.mpd" controls></video>
   </div>
</body>
</html>