#!/usr/bin/env bash
#
set -euo pipefail
IFS=$'\n\t'
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

: ${VIDEO_BITRATE:="1M"} # Only for encoding modes targeting bitrate
: ${AUDIO_BITRATE:="192k"}
: ${SPEED:="1"}

############## Setup #########################

function die {
    local status_code="$1"
    shift
    (>&2 echo "${@}")
    exit "$status_code"
}

function log {
    (>&2 echo "${@}")
}

############## Program #########################

function main {
    local cmd
    cmd=$1
    shift
    if [ "$cmd" = "copy" ]; then
        copy "${@}"
    elif [ "$cmd" = "convert" ]; then
        convert "${@}"
    elif [ "$cmd" = "stream" ]; then
        stream "${@}"
    elif [ "$cmd" = "webcam" ]; then
        webcam "${@}"
    elif [ "$cmd" = "encode_webcam" ]; then
        encode_webcam "${@}"
    else
        (>&2 echo "Unknown command: $cmd")
        exit 1
    fi
}

function copy {
    local file_to_cast
    file_to_cast="$3"

    local USERNAME PASSWORD
    USERNAME="$1"
    PASSWORD="$2"

    set -x
    </dev/null exec ffmpeg \
        -re \
        -stream_loop -1 \
        -i "$file_to_cast" \
        -c copy \
        -strict experimental \
        -f rtsp \
        -rtsp_transport tcp \
        "rtsp://$USERNAME:$PASSWORD@172.16.16.251:8554/fetch"
}

# benchmark vulkan decode:
# ffmpeg -init_hw_device "vulkan=vk:0" -hwaccel vulkan -hwaccel_output_format vulkan -i INPUT -f null - -benchmark
function convert {
    local args=()
    local acceleration_type="$1" # "software" or "hardware"
    local codec="$2" # "h264" or "av1"
    local file_to_cast="$3"
    local file_to_save="$4"



    # Verify parameters


    if [ "$acceleration_type" == "software" ]; then
        true
    elif [ "$acceleration_type" == "hardware" ]; then
        true
    else
        die 1 "Unknown acceleration type: $acceleration_type"
    fi

    if [ "$codec" == "h264" ]; then
        true
    elif [ "$codec" == "av1" ]; then
        true
    else
        die 1 "Unknown codec: $codec"
    fi



    # Build command



    if [ "$acceleration_type" == "software" ]; then
        true
    elif [ "$acceleration_type" == "hardware" ]; then
        if [ "$codec" == "h264" ]; then
            args+=(-init_hw_device vulkan)
        elif [ "$codec" == "av1" ]; then
            args+=(-vaapi_device /dev/dri/renderD128)
        fi
    fi

    args+=(-i "$file_to_cast")

    if [ "$SPEED" != "1" ]; then
        local audio_speed video_speed
        video_speed=$(bc -l <<< "1/${SPEED}")
        audio_speed=$(bc -l <<< "${SPEED}/1")
        args+=(-filter:v "setpts=${video_speed}*PTS")
        args+=(-filter:a "atempo=${audio_speed}")
    fi

    if [ "$codec" == "h264" ]; then
        if [ "$acceleration_type" == "software" ]; then
            args+=(-c:v h264)
            args+=(-profile:v high)
            args+=(-vf format=yuv420p)
            args+=(-b:v "$VIDEO_BITRATE")
        elif [ "$acceleration_type" == "hardware" ]; then
            args+=(-vf 'format=nv12|vaapi,hwupload')
            args+=(-c:v h264_vulkan)
            args+=(-profile:v high)
            args+=(-vf format=yuv420p)
            args+=(-b:v "$VIDEO_BITRATE")
        fi
    elif [ "$codec" == "av1" ]; then
        if [ "$acceleration_type" == "software" ]; then
            args+=(-c:v libsvtav1)
            args+=(-preset 4) # [0-13] default 10, lower = higher quality / slower encode
            # args+=(-crf 20) # [0-63] default 35, lower = higher quality / larger file
            # Parameters: https://gitlab.com/AOMediaCodec/SVT-AV1/-/blob/master/Docs/Parameters.md
            # fast-decode [0-2] default 0 (off), higher = faster decode
            # tune [0-2] default 1, Specifies whether to use PSNR or VQ as the tuning metric [0 = VQ, 1 = PSNR, 2 = SSIM]
            # film-grain-denoise, setting to 0 uses the original frames instead of denoising the film grain
            # rc 1 = vbr 2 = cbr
            # tbr = average bitrate
            args+=(-svtav1-params "fast-decode=1:film-grain-denoise=0:tbr=${VIDEO_BITRATE}:rc=1:passes=2")
        elif [ "$acceleration_type" == "hardware" ]; then
            # -c:v av1_amf -quality quality
            args+=(-vf 'format=nv12|vaapi,hwupload')
            args+=(-c:v av1_vaapi)
            args+=(-b:v "$VIDEO_BITRATE")
        fi
    fi



    # -bf 0 :: Disable b-frames because webrtc doesn't support h264 streams with b-frames.
    args+=(-bf 0)
    args+=(-strict -2)
    args+=(-c:a opus)
    args+=(-ac 2)
    args+=(-b:a "$AUDIO_BITRATE")
    args+=(-ar 48000)
    args+=("$file_to_save")
    set -x
    </dev/null exec ffmpeg "${args[@]}"
}

function stream {
    local args=()
    local acceleration_type="$1" # "software" or "hardware"
    local codec="$2" # "h264" or "av1"

    local USERNAME="$3"
    local PASSWORD="$4"
    local file_to_cast="$5"


    args+=(-re -stream_loop -1)

    args+=(-f rtsp)
    args+=(-rtsp_transport tcp)
    args+=("rtsp://$USERNAME:$PASSWORD@172.16.16.251:8554/fetch")
}

function webcam {
    # Uses on-webcam h264 encoding.

    local USERNAME PASSWORD
    USERNAME="$1"
    PASSWORD="$2"

    set -x

    </dev/null exec ffmpeg \
        -re \
        -input_format h264 \
        -video_size 1920x1080 \
        -i /dev/video0 \
        -c:v copy \
        -an \
        -f rtsp \
        -rtsp_transport tcp \
        "rtsp://$USERNAME:$PASSWORD@172.16.16.251:8554/fetch"
}

function encode_webcam {
    # Uses hardware accelerated gpu-based encoding.

    local USERNAME PASSWORD
    USERNAME="$1"
    PASSWORD="$2"

    set -x

    </dev/null exec ffmpeg \
        -re \
        -vaapi_device /dev/dri/renderD128 \
        -i /dev/video0 \
        -vf 'format=nv12,hwupload' \
        -c:v h264_vaapi \
        -an \
        -f rtsp \
        -rtsp_transport tcp \
        "rtsp://$USERNAME:$PASSWORD@172.16.16.251:8554/fetch"
}

main "${@}"
