Last active
October 5, 2025 01:48
-
-
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/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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