#!/bin/bash

set -e

# test encoders and decoders

SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
TEST_NAME="$1"
[ ! -z $TEST_NAME ] || TEST_NAME=${BASH_SOURCE[0]##*/}
ENCDECLIST="$SCRIPT_DIR/${TEST_NAME}"_list.txt
TEST_DIR="$AUTOPKGTEST_TMP/test"

encode_file() {
    test=$1
    dir=$2
    mux=${test%%;*}
    tenc=${test##*;}
    type=${tenc%%=*}
    enc=${tenc##*=}
    codec=${enc%%:*}
    encoder=${enc##*:}
    file_name="$dir/$encoder.$mux"
    opts=
    # some codecs require extra options
    if [ "$codec" == "avui" ]; then
        opts="-s 720x486"
    elif [ "$codec" == "dnxhd" ]; then
        opts="-s 1280x720 -b:v 90M -pix_fmt yuv422p"
    elif [ "$codec" == "dvvideo" ]; then
        opts="-s 720x480 -pix_fmt yuv411p"
    elif [ "$codec" == "h261" ]; then
        opts="-s 176x144"
    elif [ "$codec" == "h263" ]; then
        opts="-s 128x96"
    elif [ "$codec" == "hevc" ]; then
        opts="-s 64x64"
    elif [ "$codec" == "roq" ]; then
        opts="-s 128x128"
    elif [ "$codec" == "xface" ]; then
        opts="-s 48x48"
    elif [ "$codec" == "g723_1" ]; then
        opts="-ar 8000 -b:a 6300"
    elif [ "$codec" == "gsm" ]; then
        opts="-ar 8000 -b:a 13000"
    elif [ "$codec" == "gsm_ms" ]; then
        opts="-ar 8000 -b:a 13000"
    elif [ "$codec" == "roq_dpcm" ]; then
        opts="-ar 22050"
    elif [ "$codec" == "s302m" ]; then
        opts="-ac 2"
    elif [ "$codec" == "vorbis" ]; then
        opts="-ac 2"
    fi
    src=
    if [ "$type" = "v" ]; then
        src=testsrc=s=32x32:d=0.1
    elif [ "$type" = "a" ]; then
        src=sine=d=0.1
    else
        # unknown codec type
        return 10
    fi
    echo -e "trying muxer '$mux' with '$type' encoder '$encoder' for codec '$codec'\n"
    ret=0
    CMD="ffmpeg -f lavfi -i $src -strict -2 $opts -c:$type $encoder -f $mux $file_name -y -hide_banner -nostdin"
    echo $CMD
    $CMD 2>&1 || ret=$?
    return $ret
}

probe_file() {
    file_name=$1
    test=$2
    mux=${test%%;*}
    tenc=${test##*;}
    enc=${tenc##*=}
    codec=${enc%%:*}

    # determine the codec/format
    ret=0
    info=$(ffprobe -hide_banner -strict -2 "$file_name" 2>&1) || ret=$?
    if [ "$ret" != 0 ]; then
        echo "probing failed: $ret"
        return "$ret"
    fi
    # file formats can be a comma-separated list, e.g. matroska,webm
    file_formats=$(echo "$info" | grep "Input #" | sed "s/.*Input #[0-9]*, \([^ ]*\), from.*/\1/")
    file_formats=${file_formats//,/ }
    # there can be multiple streams, thus multiple codecs
    file_codecs=$(echo "$info" | grep "Stream #" | sed "s/.*Stream #.*:.*:.*: \([^ ,]*\).*/\1/")
    same_format=
    same_codec=

    # test if the file has the correct codec/format
    for file_format in $file_formats; do
        if [ "$mux" == "$file_format" ]; then
            same_format="$file_format"
        fi
    done
    if [ ! "$same_format" ]; then
        # wrong format
        echo "file has the wrong format: $file_formats"
        return 10
    fi
    for file_codec in $file_codecs; do
        if [ "$codec" == "$file_codec" ]; then
            same_codec="$file_codec"
        fi
    done
    if [ ! "$same_codec" ]; then
        # wrong codec
        echo "file has the wrong codec: $file_codecs"
        return 11
    fi
    return 0
}

decode_file() {
    file_name=$1
    ret=0
    # The option '-t 1' is necessary, because the comfortnoise decoder never stops producing noise.
    CMD="ffmpeg -strict -2 -i $file_name -t 1 -c:v rawvideo -c:a pcm_s32le -f nut /dev/null -y -hide_banner -nostdin"
    echo $CMD
    $CMD 2>&1 || ret=$?
    return $ret
}

streamcopy_file() {
    file_name=$1
    test=$2
    mux=${test%%;*}
    file_name_copy="$file_name.copy.$mux"
    ret=0
    # The option '-t 1' is necessary, because the comfortnoise decoder never stops producing noise.
    CMD="ffmpeg -strict -2 -i $file_name -t 1 -c copy -f $mux $file_name_copy -y -hide_banner -nostdin"
    echo $CMD
    $CMD 2>&1 || ret=$?
    return $ret
}

list_formats() {
    match="$1"
    old_IFS=$IFS
    # Set the field seperator to a newline
    IFS="
"
    # Get the raw list
    lines=$(ffmpeg -hide_banner -formats | grep "^ $match")
    for line in $lines; do
        item=$(echo "$line" | sed "s/^ [D ][E ] \([^ ]*\) .*/\1/")
        item=${item%%,*}
        echo "$item"
    done
    IFS=$old_IFS
}

list_codecs() {
    what=$1
    match="$2"
    type="$3"
    old_IFS=$IFS
    # Set the field seperator to a newline
    IFS="
"
    # Get the raw list
    lines=$(ffmpeg -hide_banner -codecs | grep "^ $match$type")
    for line in $lines; do
        items=$(echo "$line" | grep "($what:")
        codec=$(echo "$line" | sed "s/^ [^ ]* \([^ ]*\) .*/\1/")
        if [ -z "$items" ]; then
            echo "${type,,}=$codec"
        else
            # de/encoder(s) name(s) differ from codec name
            items=$(echo "$items" | sed "s/.*($what: \([^)]*\) ).*/\1/")
            IFS=" "
            for item in $items; do
                echo "${type,,}=$codec:$item"
            done
            IFS="
"
        fi
    done
    IFS=$old_IFS
}

list_muxers() {
    muxs=$(list_formats ".E [^=]")
    echo $muxs
}

list_demuxers() {
    dems=$(list_formats "D. [^=]")
    echo $dems
}

list_video_encoders() {
    vencs=$(list_codecs encoders ".E" "V")
    echo $vencs
}

list_video_decoders() {
    vdecs=$(list_codecs decoders "D." "V")
    echo $vdecs
}

list_audio_encoders() {
    aencs=$(list_codecs encoders ".E" "A")
    echo $aencs
}

list_audio_decoders() {
    adecs=$(list_codecs decoders "D." "A")
    echo $adecs
}

read_tests() {
    filename=$1
    old_IFS=$IFS
    # Set the field seperator to a newline
    IFS="
"
    # Get the raw list
    lines=$(cat "$filename")
    for line in $lines; do
        muxer=${line%%;*}
        tencs=${line##*;}
        if [ "$tencs" ]; then
            IFS=" "
            for tenc in $tencs; do
                echo "$muxer;$tenc"
            done
            IFS="
"
        fi
    done
    IFS=$old_IFS
}

echo "TEST: $TEST_NAME"
echo -e "AUTOPKGTEST_TMP directory: $AUTOPKGTEST_TMP\n\n"

mkdir -p "$TEST_DIR"

ffmpeg -version 2>&1

echo -e "\n"

# determine the supported muxers and encoders
echo "muxers:"
muxs=$(list_muxers)
echo "$muxs"
echo -e "\n"

vencs=$(list_video_encoders)
aencs=$(list_audio_encoders)
echo "encoders:"
encs="$vencs $aencs"
echo "$encs"
echo -e "\n"

for mux in $muxs; do
    mux_tests=
    for tenc in $encs; do
        mux_tests="$mux_tests $mux;$tenc "
    done
    possible_tests="$possible_tests$mux_tests"
done

if [ -f $ENCDECLIST ]; then
    tests=$(read_tests $ENCDECLIST)
else
    # create the list of working tests if it doesn't exist
    update="true"
    tests=$possible_tests
fi

num_tests=$(echo $tests | wc -w)
echo -e "$num_tests tests\n"

crashes=
failures=
skipped=

num_test=0
num_success=0

for test in $tests; do
    num_test=$((num_test + 1))
    echo "Test $num_test:"
    streamcopy=${test##*|}
    if [ "$update" ]; then
        streamcopy=1
    fi
    test=${test%%|*}
    # skip the test, if the muxer/encoder is not available
    available=$(echo "$possible_tests" | grep " $test ") || true
    if [ ! "$available" ]; then
        skipped="${skipped}${test}\n"
        echo -e "SKIPPED: $test\n\n"
        continue
    fi
    mux=${test%%;*}
    tenc=${test##*;}
    ret=0
    # try encoding a file
    # this also sets the file_name variable
    encode_file $test $TEST_DIR || ret=$?
    echo -e "\n"
    if [ "$ret" != "0" ]; then
        errmsg="$test; encoding return code: $ret"
        if [ "$ret" -gt 128 ]; then
            crashes="${crashes}${errmsg}\n"
        else
            failures="${failures}${errmsg}\n"
        fi
        echo -e "\nFAILED: $errmsg\n\n"
        continue
    fi
    ret=0
    # test if the file has the correct format/codec
    err=$(probe_file "$file_name" $test) || ret=$?
    if [ "$ret" != "0" ]; then
        errmsg="$test; $err"
        if [ "$ret" -gt 128 ]; then
            crashes="${crashes}${errmsg}\n"
        else
            failures="${failures}${errmsg}\n"
        fi
        echo -e "\nFAILED: $errmsg\n\n"
        continue
    fi
    ret=0
    # test decoding the file
    decode_file "$file_name" || ret=$?
    if [ "$ret" != "0" ]; then
        errmsg="$test; decoding return code: $ret"
        if [ "$ret" -gt 128 ]; then
            crashes="${crashes}${errmsg}\n"
        else
            failures="${failures}${errmsg}\n"
        fi
        echo -e "\nFAILED: $errmsg\n\n"
        continue
    fi
    streamcopy_orig="$streamcopy"
    if [ "$streamcopy" == "1" ]; then
        ret=0
        # test streamcopying the file
        # this also sets the file_name_copy variable
        echo -e "\n"
        streamcopy_file "$file_name" "$test" || ret=$?
        if [ "$ret" != "0" ]; then
            errmsg="$test; streamcopy return code: $ret"
            if [ "$ret" -gt 128 ]; then
                crashes="${crashes}${errmsg}\n"
            else
                failures="${failures}${errmsg}\n"
            fi
            echo -e "\nFAILED: $errmsg\n\n"
            streamcopy=0
        fi
    fi
    if [ "$streamcopy" == "1" ]; then
        ret=0
        # test if the file has the correct format/codec
        err=$(probe_file "$file_name_copy" $test) || ret=$?
        if [ "$ret" != "0" ]; then
            errmsg="$test; streamcopy: $err"
            if [ "$ret" -gt 128 ]; then
                crashes="${crashes}${errmsg}\n"
            else
                failures="${failures}${errmsg}\n"
            fi
            echo -e "\nFAILED: $errmsg\n\n"
            streamcopy=0
        fi
    fi
    works=$([ "$streamcopy" = "1" ] && echo works || echo fails)
    if [ "$streamcopy_orig" = "$streamcopy" ]; then
        echo -e "\nSUCCESS: correctly created file with format '$mux' and codec '$tenc'; streamcopying $works\n\n"
        num_success=$((num_success + 1))
    fi
    if [ "$update" ]; then
        if [ "x$last_mux" != "x$mux" ]; then
            if [ "$last_mux" ]; then
                # newline
                echo "" >> $ENCDECLIST
            fi
            echo -n "$mux;" >> $ENCDECLIST
        fi
        last_mux=$mux
        echo -n " $tenc|$streamcopy" >> $ENCDECLIST
    fi
done


echo "done!"


ret=0

num_crashes=0
num_failures=0
num_skipped=0

if [ "$crashes" ]; then
    num_crashes=$(echo -e "$crashes" | wc -l)
    num_crashes=$((num_crashes - 1))
fi
if [ "$failures" ]; then
    num_failures=$(echo -e "$failures" | wc -l)
    num_failures=$((num_failures - 1))
fi
if [ "$skipped" ]; then
    num_skipped=$(echo -e "$skipped" | wc -l)
    num_skipped=$((num_skipped - 1))
fi

if [ "$num_crashes" != "0" ]; then
    ret=1
    echo -e "\n\ncrashes: $num_crashes\n"
    echo -e "$crashes"
fi

if [ "$num_failures" != "0" ]; then
    ret=1
    echo -e "\n\nfailures: $num_failures\n"
    echo -e "$failures"
fi

if [ "$num_skipped" != "0" ]; then
    echo -e "\n\nskipped: $num_skipped\n"
    echo -e "$skipped"
fi

if [ "$num_success" != "0" ]; then
    echo -e "\n\nsuccess: $num_success\n"
else
    ret=1
    echo -e "\n\nno single success: something is definitely wrong\n"
fi

exit $ret
