From d72c2429bbeb76ab96c755a125aa611fc7036e70 Mon Sep 17 00:00:00 2001 From: Levon Gevorgyan Date: Fri, 13 Dec 2024 22:56:17 -0600 Subject: [PATCH] init macos scripts --- scripts/analyze.sh | 2 +- scripts/benchmark.sh | 2 +- scripts/build.sh | 96 +++++++++++++++++++--------------------- scripts/estimate_fg.sh | 2 +- scripts/full.sh | 2 +- scripts/install_deps.sh | 74 +++++++++++++++++++------------ scripts/recc_encode.sh | 57 ++++++++++++++++-------- scripts/refresh_build.sh | 2 +- 8 files changed, 133 insertions(+), 104 deletions(-) diff --git a/scripts/analyze.sh b/scripts/analyze.sh index 9866c7e..c6dfedd 100755 --- a/scripts/analyze.sh +++ b/scripts/analyze.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash BASE_DIR=$(pwd) RESULTS="$BASE_DIR/benchmark/results.csv" diff --git a/scripts/benchmark.sh b/scripts/benchmark.sh index 207e868..793f1f0 100755 --- a/scripts/benchmark.sh +++ b/scripts/benchmark.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash has_encoder() { CHECK_ENC="$1" diff --git a/scripts/build.sh b/scripts/build.sh index a2bf395..a38e956 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash usage() { echo "./scripts/build.sh [-h] [-A] [-s] [-o] [-r] [-O n]" @@ -124,23 +124,23 @@ VPX_DIR="$BASE_DIR/vpx" # save options use echo "$@" > "$BASE_DIR/.last_opts" +# prefix to install +PREFIX='/usr/local' + export ARCH=$(uname -m) -export COMP_FLAGS="" +export COMP_FLAGS="-I${PREFIX}/include" if [[ "$ARCH" == "x86_64" ]] then - COMP_FLAGS="-march=native" -elif [[ "$ARCH" == "aarch64" ]] + COMP_FLAGS+=" -march=native" +elif [[ "$ARCH" == "aarch64" || "$ARCH" == "arm64" ]] then - COMP_FLAGS="-mcpu=native" + COMP_FLAGS+=" -mcpu=native" fi -echo "COMP_FLAGS: $COMP_FLAGS" +echo "COMP_FLAGS: [$COMP_FLAGS]" # for ccache export PATH="/usr/lib/ccache:$PATH" -# prefix to install -PREFIX='/usr/local' - # options for ffmpeg configure FFMPEG_CONFIGURE_OPT="" @@ -153,6 +153,11 @@ git clone --depth "$GIT_DEPTH" https://code.videolan.org/videolan/dav1d.git "$DA git clone --depth "$GIT_DEPTH" https://github.com/xiph/opus.git "$OPUS_DIR" git clone --depth "$GIT_DEPTH" https://github.com/FFmpeg/FFmpeg "$FFMPEG_DIR" +# check for required local directories +sudo mkdir ${PREFIX}/bin \ + ${PREFIX}/lib \ + ${PREFIX}/include + # rockchip ffmpeg libs # IS_ROCKCHIP=$(uname -r | grep "rockchip" > /dev/null && echo "yes" || echo "no") if [[ "$BUILD_ROCKCHIP" == "true" ]] @@ -175,7 +180,7 @@ then cmake .. -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_RPEFIX="$PREFIX" \ -DBUILD_SHARED_LIBS=ON \ - -DBUILD_TEST=OFF \ + -DBUILD_TEST=OFF -DCMAKE_INSTALL_RPATH="${PREFIX}/lib" \ -DCMAKE_C_FLAGS="-O${OPT_LVL} $COMP_FLAGS" \ -DCMAKE_CXX_FLAGS="-O${OPT_LVL} $COMP_FLAGS" || exit make -j"$(nproc)" || exit @@ -187,8 +192,9 @@ then rm -rf rga_build.user mkdir rga_build.user meson setup . rga_build.user --buildtype release -Db_lto=true \ - --default-library=shared -Dlibdrm=false -Dlibrga_demo=false --prefix "$PREFIX" \ - --optimization="$OPT_LVL" -Dc_args="$COMP_FLAGS" -Dcpp_args="-fpermissive $COMP_FLAGS" || exit + --default-library=shared -Dlibdrm=false -Dlibrga_demo=false \ + --prefix "$PREFIX" --optimization="$OPT_LVL" \ + -Dc_args="$COMP_FLAGS" -Dcpp_args="-fpermissive $COMP_FLAGS" || exit ninja -vC rga_build.user || exit sudo ninja -vC rga_build.user install || exit fi @@ -208,19 +214,13 @@ then rm -rf ffmpeg_build.user && mkdir ffmpeg_build.user || exit source "$HOME/.cargo/env" # for good measure cargo clean - RUSTFLAGS="-C target-cpu=native" cargo build --release - sudo cp target/release/dovi_tool /usr/local/bin/ || exit + RUSTFLAGS="-C target-cpu=native" ccache cargo build --release + sudo cp target/release/dovi_tool ${PREFIX}/bin/ || exit # build libdovi cd dolby_vision || exit - RUSTFLAGS="-C target-cpu=native" cargo cinstall --release \ - --prefix="$DOVI_DIR/ffmpeg_build.user" \ - --libdir="$DOVI_DIR/ffmpeg_build.user"/lib \ - --includedir="$DOVI_DIR/ffmpeg_build.user"/include - cd "$DOVI_DIR"/ffmpeg_build.user || exit - sudo rm /usr/local/include/libdovi /usr/local/lib/libdovi.* - sudo cp ./lib/* /usr/local/lib/ -r || exit - sudo cp ./include/* /usr/local/include/ -r + RUSTFLAGS="-C target-cpu=native" ccache cargo cbuild --release + sudo cargo cinstall --release # build hdr10plus_tool cd "$HDR10_DIR/" || exit @@ -228,19 +228,13 @@ then rm -rf ffmpeg_build.user && mkdir ffmpeg_build.user || exit source "$HOME/.cargo/env" # for good measure cargo clean - RUSTFLAGS="-C target-cpu=native" cargo build --release - sudo cp target/release/hdr10plus_tool /usr/local/bin/ || exit + RUSTFLAGS="-C target-cpu=native" ccache cargo build --release + sudo cp target/release/hdr10plus_tool ${PREFIX}/bin/ || exit # build libhdr10plus cd hdr10plus || exit - RUSTFLAGS="-C target-cpu=native" cargo cinstall --release \ - --prefix="$HDR10_DIR/ffmpeg_build.user" \ - --libdir="$HDR10_DIR/ffmpeg_build.user"/lib \ - --includedir="$HDR10_DIR/ffmpeg_build.user"/include - cd "$HDR10_DIR"/ffmpeg_build.user || exit - sudo rm /usr/local/include/libhdr10plus-rs /usr/local/lib/libhdr10plus-rs.* - sudo cp ./lib/* /usr/local/lib/ -r || exit - sudo cp ./include/* /usr/local/include/ -r + RUSTFLAGS="-C target-cpu=native" ccache cargo cbuild --release + sudo cargo cinstall --release # build svt-avt-psy cd "$SVT_PSY_DIR/" || exit @@ -254,9 +248,10 @@ then -DENABLE_AVX512=ON -DBUILD_TESTING=OFF \ -DCOVERAGE=OFF -DLIBDOVI_FOUND=1 \ -DLIBHDR10PLUS_RS_FOUND=1 \ + -DCMAKE_INSTALL_RPATH="${PREFIX}/lib" \ -DCMAKE_C_FLAGS="-O${OPT_LVL} $COMP_FLAGS" \ -DCMAKE_CXX_FLAGS="-O${OPT_LVL} $COMP_FLAGS" || exit - make -j"$(nproc)" || exit + ccache make -j"$(nproc)" || exit sudo make install else # build svt-av1 @@ -269,7 +264,7 @@ else cmake .. -DCMAKE_BUILD_TYPE=Release -DSVT_AV1_LTO=ON \ -DCMAKE_INSTALL_RPEFIX="$PREFIX" \ -DENABLE_AVX512=ON -DBUILD_TESTING=OFF \ - -DCOVERAGE=OFF \ + -DCOVERAGE=OFF -DCMAKE_INSTALL_RPATH="${PREFIX}/lib" \ -DCMAKE_C_FLAGS="-O${OPT_LVL} $COMP_FLAGS" \ -DCMAKE_CXX_FLAGS="-O${OPT_LVL} $COMP_FLAGS" || exit make -j"$(nproc)" || exit @@ -285,14 +280,7 @@ if [[ "$BUILD_ALL_AV1" == "true" ]]; then rm -rf ffmpeg_build.user && mkdir ffmpeg_build.user || exit source "$HOME/.cargo/env" # for good measure cargo clean - RUSTFLAGS="-C target-cpu=native" cargo cinstall --release \ - --prefix="$(pwd)"/ffmpeg_build.user \ - --libdir="$(pwd)"/ffmpeg_build.user/lib \ - --includedir="$(pwd)"/ffmpeg_build.user/include || exit - cd ffmpeg_build.user || exit - sudo rm /usr/local/include/rav1e /usr/local/lib/librav1e.* - sudo cp ./lib/* /usr/local/lib/ -r || exit - sudo cp ./include/* /usr/local/include/ -r || exit + RUSTFLAGS="-C target-cpu=native" sudo ccache cargo cinstall --release # build aom cd "$AOM_DIR/" || exit @@ -334,7 +322,7 @@ rm -rf build.user mkdir build.user meson setup . build.user --buildtype release -Db_lto=true --prefix "$PREFIX" \ --optimization="$OPT_LVL" -Dc_args="$COMP_FLAGS" -Dcpp_args="$COMP_FLAGS" || exit -ninja -vC build.user || exit +ccache ninja -vC build.user || exit sudo ninja -vC build.user install || exit # build opus @@ -343,7 +331,7 @@ update_git ./autogen.sh || exit export CFLAGS="-O${OPT_LVL} -flto $COMP_FLAGS" make clean -./configure --prefix="$PREFIX" || exit +ccache ./configure --prefix="$PREFIX" || exit make -j"$(nproc)" || exit sudo make install || exit unset CFLAGS @@ -422,17 +410,23 @@ if [[ "$BUILD_OTHERS" == "true" ]]; then sudo make install || exit fi -# ldconfig for shared libs -sudo mkdir /etc/ld.so.conf.d/ -echo -e "/usr/local/lib\n/usr/local/lib/$(gcc -dumpmachine)" | sudo tee /etc/ld.so.conf.d/ffmpeg.conf || exit 1 -sudo ldconfig +if command -v ldconfig ; then + # ldconfig for shared libs + sudo mkdir /etc/ld.so.conf.d/ + echo -e "${PREFIX}/lib\n${PREFIX}/lib/$(gcc -dumpmachine)" | sudo tee /etc/ld.so.conf.d/ffmpeg.conf || exit 1 + sudo ldconfig +fi + +# for MacOs / Darwin +test "$(uname)" == "Darwin" && \ + FFMPEG_CONFIGURE_OPT+="--enable-rpath " # build ffmpeg cd "$FFMPEG_DIR/" || exit update_git -export PKG_CONFIG_PATH="/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH" +export PKG_CONFIG_PATH="${PREFIX}/lib/pkgconfig:$PKG_CONFIG_PATH" make clean -./configure --enable-libsvtav1 --prefix="$PREFIX" \ +ccache ./configure --enable-libsvtav1 --prefix="$PREFIX" \ --enable-libdav1d --enable-libopus \ $FFMPEG_CONFIGURE_OPT \ --arch="$ARCH" --cpu=native \ @@ -443,7 +437,7 @@ make clean --disable-podpages --disable-txtpages || exit make -j"$(nproc)" || exit sudo make install || exit -sudo cp ff*_g /usr/local/bin/ +sudo cp ff*_g ${PREFIX}/bin/ # validate encoders hash -r diff --git a/scripts/estimate_fg.sh b/scripts/estimate_fg.sh index ceef910..970161f 100755 --- a/scripts/estimate_fg.sh +++ b/scripts/estimate_fg.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash usage() { echo "$(basename "$0") -i input_file [options]" diff --git a/scripts/full.sh b/scripts/full.sh index 4b4d989..9a0742f 100755 --- a/scripts/full.sh +++ b/scripts/full.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash bash ./scripts/install_deps.sh || exit 1 bash ./scripts/build.sh || exit 1 diff --git a/scripts/install_deps.sh b/scripts/install_deps.sh index 48dbda6..d5dc4fb 100755 --- a/scripts/install_deps.sh +++ b/scripts/install_deps.sh @@ -1,8 +1,9 @@ -#!/bin/bash +#!/usr/bin/env bash -COMMON_DEP_NAMES="autoconf automake cmake libtool pkg-config bc texinfo \ - wget nasm yasm time python3 meson doxygen xxd jq lshw gnuplot curl \ - clang valgrind ccache gawk" +COMMON_DEP_NAMES="autoconf automake cmake libtool texinfo \ + wget nasm yasm python3 meson doxygen jq gnuplot ccache gawk" + +COMMON_DEP_NAMES_LINUX="time clang valgrind curl bc lshw xxd pkg-config" APT_DEP_NAMES="build-essential git-core libass-dev libfreetype6-dev \ libsdl2-dev libva-dev libvdpau-dev libvorbis-dev libxcb1-dev mold \ @@ -11,34 +12,49 @@ APT_DEP_NAMES="build-essential git-core libass-dev libfreetype6-dev \ PACMAN_DEP_NAMES="base-devel ninja python-pip" -USING_NALA=$(type nala > /dev/null; echo $?) -USING_APT=$(type apt > /dev/null; echo $?) -USING_PACMAN=$(type pacman > /dev/null; echo $?) +BREW_DEP_NAMES="pkgconf mkvtoolnix" -if [[ "$USING_NALA" == "0" ]]; then - # if nala fails, try apt - USING_APT="1" - echo "Installing with nala" - sudo nala update - sudo nala install -y $COMMON_DEP_NAMES $APT_DEP_NAMES || USING_APT="0" -fi -if [[ "$USING_APT" == "0" ]]; then - echo "Installing with apt" - sudo apt-get update - sudo apt-get install -y $COMMON_DEP_NAMES $APT_DEP_NAMES || exit 1 -fi -if [[ "$USING_PACMAN" == "0" ]]; then - echo "Installing with pacman" - sudo pacman -S $COMMON_DEP_NAMES $PACMAN_DEP_NAMES --noconfirm || exit 1 -fi +install_deps() { + if command -v nala ; then + echo "Installing with nala" + sudo nala update + sudo nala install -y $COMMON_DEP_NAMES \ + $COMMON_DEP_NAMES_LINUX \ + $APT_DEP_NAMES && return 0 + fi + if command -v apt ; then + echo "Installing with apt" + sudo apt-get update + sudo apt-get install -y $COMMON_DEP_NAMES \ + $COMMON_DEP_NAMES_LINUX \ + $APT_DEP_NAMES || exit 1 + return 0 + fi + if command -v pacman ; then + echo "Installing with pacman" + sudo pacman -S $COMMON_DEP_NAMES \ + $COMMON_DEP_NAMES_LINUX \ + $PACMAN_DEP_NAMES --noconfirm || exit 1 + return 0 + fi + if command -v brew ; then + echo "Installing with brew" + brew install $COMMON_DEP_NAMES \ + $BREW_DEP_NAMES || exit 1 + return 0 + fi + return 1 +} + +install_deps curl https://sh.rustup.rs -sSf | sh -s -- -y source "$HOME/.cargo/env" cargo install cargo-c || exit 1 -sudo rm /etc/pip.conf -grep -q '\[global\]' /etc/pip.conf 2> /dev/null || printf '%b' '[global]\n' | sudo tee -a /etc/pip.conf > /dev/null -sudo sed -i '/^\[global\]/a\break-system-packages=true' /etc/pip.conf -pip install --upgrade pip -python3 -m pip install --upgrade virtualenv || exit 1 - \ No newline at end of file +if test -f /etc/pip.conf ; then + sudo rm /etc/pip.conf + grep -q '\[global\]' /etc/pip.conf 2> /dev/null || printf '%b' '[global]\n' | sudo tee -a /etc/pip.conf > /dev/null + sudo sed -i '/^\[global\]/a\break-system-packages=true' /etc/pip.conf +fi +python3 -m pip install --upgrade virtualenv --break-system-packages || exit 1 diff --git a/scripts/recc_encode.sh b/scripts/recc_encode.sh index 361b687..ec30445 100755 --- a/scripts/recc_encode.sh +++ b/scripts/recc_encode.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # this is simply my recommended encoding method. # do not take this as a holy grail. @@ -26,10 +26,10 @@ get_duration() { } get_crop() { - DURATION="$(get_duration "$INPUT")" - TOTAL_SECONDS="$(echo "$DURATION" | awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }')" + local DURATION="$(get_duration "$INPUT")" + local TOTAL_SECONDS="$(echo "$DURATION" | awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }')" # get cropdetect value for first 1/5 of input - TIME_ENC="$(echo "$TOTAL_SECONDS / 5" | bc)" + local TIME_ENC="$(echo "$TOTAL_SECONDS / 5" | bc)" ffmpeg -hide_banner -ss 0 -discard 'nokey' -i "$INPUT" -t "$TIME_ENC" \ -map '0:v:0' -filter:v:0 'cropdetect=limit=64:round=16:skip=2:reset_count=0' \ -codec:v 'wrapped_avframe' -f 'null' '/dev/null' -y 2>&1 | grep -o crop=.* \ @@ -37,10 +37,10 @@ get_crop() { } unmap_streams(){ - INPUT="$1" - UNMAP_FILTER="bin_data|jpeg|png" - UNMAP_STREAMS=$(ffprobe "$INPUT" 2>&1 | grep "Stream" | grep -Ei "$UNMAP_FILTER" | cut -d':' -f2 | cut -d'[' -f1 | tr '\n' ' ') - UNMAP_CMD="" + local INPUT="$1" + local UNMAP_FILTER="bin_data|jpeg|png" + local UNMAP_STREAMS=$(ffprobe "$INPUT" 2>&1 | grep "Stream" | grep -Ei "$UNMAP_FILTER" | cut -d':' -f2 | cut -d'[' -f1 | tr '\n' ' ') + local UNMAP_CMD="" for UNMAP_STREAM in $UNMAP_STREAMS; do UNMAP_CMD+="-map -0:$UNMAP_STREAM " done @@ -48,7 +48,7 @@ unmap_streams(){ } get_bitrate_audio() { - BITRATE_CMD="" + local BITRATE_CMD="" NUM_AUDIO_STREAMS=$(ffprobe -v error -select_streams a -show_entries stream=index -of csv=p=0 "$INPUT" | wc -l) for ((i = 0; i < NUM_AUDIO_STREAMS; i++)); do NUM_CHANNELS=$(ffprobe -v error -select_streams "a:$i" -show_entries stream=channels -of default=noprint_wrappers=1:nokey=1 "$INPUT") @@ -59,7 +59,7 @@ get_bitrate_audio() { } convert_subs() { - SUB_CONVERT_CMD="" + local SUB_CONVERT_CMD="" NUM_SUBTITLE_STREAMS=$(ffprobe -v error -select_streams s -show_entries stream=index -of csv=p=0 "$INPUT" | wc -l) for ((i = 0; i < NUM_SUBTITLE_STREAMS; i++)); do SUBTITLE_FORMAT=$(ffprobe -v error -select_streams "s:$i" -show_entries stream=codec_name -of default=noprint_wrappers=1:nokey=1 "$INPUT") @@ -68,9 +68,30 @@ convert_subs() { echo "$SUB_CONVERT_CMD" } +ffmpeg_version() { + ffmpeg -version 2>&1 | head -n 1 | grep version | cut -d' ' -f1-3 +} + +video_enc_version() { + SvtAv1EncApp --version | head -n 1 +} + +audio_enc_version() { + local AUDIO_ENC_VERSION + if command -v ldd > /dev/null ; then + AUDIO_ENC_VERSION="$(ldd "$(which ffmpeg)" | grep -i libopus | cut -d' ' -f3 | xargs readlink)" + elif command -v otool > /dev/null ; then + AUDIO_ENC_VERSION="$(otool -L $(which ffmpeg) | grep libopus | tr -d ')' | awk -F' ' '{print $NF}')" + fi + local AUDIO_ENC_GIT="$(cd "$BUILDER_DIR/opus" && git rev-parse --short HEAD)" + test "$AUDIO_ENC_GIT" != '' && AUDIO_ENC_VERSION+="-g${AUDIO_ENC_GIT}" + echo "$AUDIO_ENC_VERSION" + +} + encode() { ENCODE_FILE="/tmp/$(basename "$OUTPUT")_encode.sh" - echo -e '#!/bin/bash\n' > "$ENCODE_FILE" + echo -e '#!/usr/bin/env bash\n' > "$ENCODE_FILE" echo "export OUTPUT=\"$OUTPUT\"" >> "$ENCODE_FILE" SVT_PARAMS="${GRAIN}sharpness=3:tune=3:enable-overlays=1:scd=1:fast-decode=1:enable-variance-boost=1:enable-qm=1:qm-min=0:qm-max=15" @@ -102,14 +123,13 @@ encode() { FFMPEG_PARAMS="-y -c:s copy \$CONVERT_SUBS -c:V \$VIDEO_ENCODER \$VIDEO_PARAMS" echo "export FFMPEG_PARAMS=\"$FFMPEG_PARAMS\"" >> "$ENCODE_FILE" - FFMPEG_VERSION="ffmpeg_version=$(ffmpeg -version 2>&1 | grep version | cut -d' ' -f1-3)" + FFMPEG_VERSION="ffmpeg_version=$(ffmpeg_version)" echo "export FFMPEG_VERSION=\"$FFMPEG_VERSION\"" >> "$ENCODE_FILE" - VIDEO_ENC_VERSION="video_encoder=$(SvtAv1EncApp --version | head -n 1)" + VIDEO_ENC_VERSION="video_encoder=$(video_enc_version)" echo "export VIDEO_ENC_VERSION=\"$VIDEO_ENC_VERSION\"" >> "$ENCODE_FILE" - AUDIO_ENC_VERSION="audio_encoder=$(ldd "$(which ffmpeg)" | grep -i libopus | cut -d' ' -f3 | xargs readlink)" - AUDIO_ENC_VERSION+="-g$(cd "$BUILDER_DIR/opus" && git rev-parse --short HEAD)" + AUDIO_ENC_VERSION="audio_encoder=$(audio_enc_version)" echo "export AUDIO_ENC_VERSION=\"$AUDIO_ENC_VERSION\"" >> "$ENCODE_FILE" ADD_METADATA="\"encoding_params=\$VIDEO_PARAMS \$SVT_PARAMS\"" @@ -182,10 +202,9 @@ while getopts "$OPTS" flag; do ;; v) SCRIPT_VER="$(cd "$BUILDER_DIR" && git rev-parse --short HEAD)" - FFMPEG_VER="$(ffmpeg -version 2>&1 | head -n 1 | grep version | cut -d' ' -f1-3)" - SVTAV1_VER="$(SvtAv1EncApp --version | head -n 1)" - LIBOPUS_VER="$(ldd "$(which ffmpeg)" | grep -i libopus | cut -d' ' -f3 | xargs readlink)" - LIBOPUS_VER+="-g$(cd "$BUILDER_DIR/opus" && git rev-parse --short HEAD)" + FFMPEG_VER="$(ffmpeg_version)" + SVTAV1_VER="$(video_enc_version)" + LIBOPUS_VER="$(audio_enc_version)" echo "encode version: $SCRIPT_VER" echo "ffmpeg version: $FFMPEG_VER" echo "svtav1 version: $SVTAV1_VER" diff --git a/scripts/refresh_build.sh b/scripts/refresh_build.sh index 94a554c..03d98c2 100755 --- a/scripts/refresh_build.sh +++ b/scripts/refresh_build.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # global path variables SCRIPT_PATH="$(readlink -f "${BASH_SOURCE[0]}")"