Skip to content

Instantly share code, notes, and snippets.

@espeon
Last active October 5, 2025 01:48
Show Gist options
  • Select an option

  • Save espeon/d2e438cea3343a71ddf8068779f13468 to your computer and use it in GitHub Desktop.

Select an option

Save espeon/d2e438cea3343a71ddf8068779f13468 to your computer and use it in GitHub Desktop.
Generate a CMAF streamable HLS/DASH manifest from any audio file. Will need FFmpeg and Bento4 in your PATH. You can get binaries here: https://ffmpeg.org https://www.bento4.com/
#!/bin/bash
# Generate a CMAF streamable HLS/DASH manifest from any audio file.
# You will need FFmpeg and Bento4 in your PATH.
# Get binaries here: https://ffmpeg.org https://www.bento4.com/
# array of kbps to transcode to opus to mp4
# use below as a 'magic number' to use FLAC instead of Opus
magic_number=1000
bitrates=(16 32 96 160 320 1000)
# Main playlist name
main_playlist="playlist.m3u8"
# Get the bitrate and store it in a variable
bitrate=$(ffprobe -v error -show_entries format=bit_rate -of default=noprint_wrappers=1:nokey=1 "$1")
# Convert the bitrate to kilobits per second (kbps) if needed
track_bitrate=$((bitrate / 1000 + 120))
# select only the bitrates under the passed-in track's bitrate
filtered_bitrates=()
for b in "${bitrates[@]}"; do
if (( b <= track_bitrate )); then
filtered_bitrates+=("$b")
fi
done
echo filtered_bitrates: "${filtered_bitrates[@]}"
for b in "${filtered_bitrates[@]}"; do
echo "Transcoding $1 to $b kbps"
base_name="$1"
# run in background
{
if [[ b -eq magic_number ]]; then
ffmpeg -i "$base_name" -c:a flac -compression_level 10 -vn "${base_name}.${b}k.mp4"
else
ffmpeg -i "$base_name" -c:a libopus -b:a "${b}k" -vbr on -vn "${base_name}.${b}k.mp4"
fi &&
mp4fragment --fragment-duration 2048 "${base_name}.${b}k.mp4" "${base_name}.${b}k.frag.mp4"
rm "${base_name}.${b}k.mp4"
} &
done
echo "Waiting for everything to finish before CMAFing"
wait
output_dir="output"
# Initialize the command as an array
mp4dash_command=("mp4dash" "--hls" "--no-split" "--use-segment-list" "--force" "--rename-media" "--hls-master-playlist-name=${main_playlist}" "--output-dir" "$output_dir")
# Loop through all .frag.mp4 files in the current directory
for frag_file in "$1"*.frag.mp4; do
# Check if the file exists to avoid issues with globbing
if [[ -f "$frag_file" ]]; then
# Extract the bitrate or relevant identifier from the filename
if [[ "$frag_file" =~ .([0-9]+k)\.frag\.mp4$ ]]; then
bitrate="${BASH_REMATCH[1]}"
# Construct the HLS group name
label="audio_${bitrate}"
# Append to the command (without inner quotes)
mp4dash_command+=("[+label=${label},+language=en,+hls_default=NO,+hls_autoselect=NO,+hls_group=audio]${frag_file}")
echo "Appending: [+label=${label},+language=en,+hls_default=NO,+hls_autoselect=NO,+hls_group=audio]${frag_file}" # Debug output
else
echo "File $frag_file does not match expected format." # Debug output
fi
else
echo "Warning: $frag_file does not exist." # In case of missing files
fi
done
# Print the final command for debugging
echo "Running: ${mp4dash_command[@]}"
# Execute the command
"${mp4dash_command[@]}"
input_file="${output_dir}/${main_playlist}"
if [[ "$OSTYPE" == "darwin"* ]]; then
# macOS
sed -i '' '/^#EXT-X-MEDIA:TYPE=AUDIO/d' "$input_file"
sed -i '' 's/Opus/opus/g' "$input_file"
else
# Linux (and other UNIX-like systems)
sed -i '/^#EXT-X-MEDIA:TYPE=AUDIO/d' "$input_file"
sed -i 's/Opus/opus/g' "$input_file"
fi
for file in ${output_dir}/*-Opus-*.m3u8; do
mv "$file" "${file/Opus/opus}"
done
# Clean up fragmented mp4s
#rm *.frag.mp4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment