影音數位典藏所需的自動化腳本(Bash Script)--case
影音數位典藏所需的自動化腳本(Bash Script)--case
數位典藏中,有些工作是經常性出現的重複任務,這種狀況很適合使用腳本來減輕工作所需。其中一個例子就是轉碼,將典藏母帶轉換為工作時所需的中間素材,或產生供大眾取用的取用版本。在這樣的狀況下,可以預期會有兩種工作需要執行,因此 case ... in ... esac 會是很適合這種狀況的判斷式。
case ... in ... esac 的完整表示方式為:
case variable in
value_n) command_n
;;
value_x) command_x
command_x
;;
;;
value_x) command_x
command_x
;;
*) command
;;
;;
esac
其中 variable 是主要用來進行判斷的變數,當變數數值與括號 ")" 前的數值相同時,則執行該數值所對應段落的指令。指令不限於一行,但每段的結尾需以雙分號 ";;" 為結尾。若數值不屬於上面任何一個指定的數值時,則執行 "*)" 這段的指令。
最簡單的 case 判斷式腳本可能長這樣:
#!/usr/bin/env bash
# Name:
# show3.sh
# Description:
# Show what you type
# History:
# 2020-07-02 by Mengchun
unset var
# 將 var 這個參數還原,以免之前執行過的腳本影響這個參數
function print_out() {
echo "Your choice is ${1:-Unknown Value.}"
exit 0
}
# 將重複輸出的行為寫成函式
read -p "Please input your choice: " var
# 使用 read 顯示提示問句,並讀取變數 var
case $var in
1) print_out "${var}." ;;
2) print_out "${var}.." ;;
3) print_out "${var}..." ;;
*) echo "Usage ${0}"$'\n'"Please answer with: {1|2|3}" ;;
esac
# 定義
試試執行這個名為 show3.sh 的腳本:
- 輸入值為 "1"bash-4.4$ ./show3.shPlease input your choice: 1Your choice is 1.
- 輸入值為 "2"bash-4.4$ ./show3.shPlease input your choice: 2Your choice is 2..
- 輸入值為 "3"bash-4.4$ ./show3.shPlease input your choice: 3Your choice is 3...
- 輸入值為 "4"bash-4.4$ ./show3.shPlease input your choice: 4Usage ./show3.shPlease answer with: {1|2|3}
使用 case 判斷式撰寫轉碼用腳本
在數位典藏時,將典藏母帶轉換為工作時所需的中間素材,或產生供大眾取用的取用版本,這兩項工作,利用 case 判斷式寫成腳本能怎麼寫:- 變數定義區
- 函式區
- 顯示操作說明
- 顯示錯誤訊息
- 判斷輸入與輸出目標
- 產生中間素材 (mezzanine)
- 產生取用素材 (access)
- 產生畫格與檔案的雜湊值 (MD5)
- 主指令區
- 讀取輸入參數
- 分析輸入與輸出目標
- 產生輸出檔案
內容說明
- 變數定義區script_name="$(basename "${0}")"# 定義腳本名稱變數# color codeRED='\033[1;31m'GREEN='\033[1;32m'YELLOW='\033[1;33m'BLUE='\033[1;34m'NC='\033[0m'# 定義色碼,使得顯示說明文字帶有顏色
# 更多顏色可搜尋: ANSI_escape_code Colors# unset variableunset input_fileunset output_fileunset mode
# 將廣域變數還原,確保不會有未預期的數值存在 - 指令確認# Command check# display a short help if no parameter in providedif (( "${#}" == 0)); then: cat << EOFHelp:${script_name} -hEOF: exit 1fi# 當無輸入任何參數時,提示如何顯示說明文件
- 函式區
- 顯示操作說明# display a help message and exitfunction prompt_help(){: cat <<EOFSyntax:: ${script_name} (-a | -m) -i INPUT_FILE -o OUTPUT_FILE: ${script_name} -hParameters:: -a generate an access file: -m generate a mezzanine file: -i define the INPUT_FILE: -o define the OUTPUT_FILE: -h display this helpEOF: exit 0}# 顯示操作語法,及各參數代表意義
- 顯示錯誤訊息# display an error message and exitfunction prompt_error(){: echo -e "${RED}Error: ${1:-unknown error.}${NC}": exit 1}# 顯示紅色錯誤訊息,結束後還原色碼以免汙染後續顯示文字
- 判斷目標是否為影音檔# verify input ###########function verify_input() {in_file="${1}"# 定義要在這個函式中所使用的區域變數if [[ "${#}" == 0 ]]; then: prompt_error "No input file passed."# 若無輸入目標,則顯示錯誤訊息elif [[ ! -f "${in_file}" ]]; then·······: prompt_error "'${in_file}' is not a file."# 若輸入目標非檔案,則顯示錯誤訊息elif ffprobe "${in_file}" 2>&1 | grep "Invalid data found" >/dev/null; then# 如輸入目標非影音檔,則顯示錯誤訊息: prompt_error "'$(basename ${in_file})' is not an AV file."fireturn 0# 當輸入目標不是上述狀況,結束函式並回報成功}
- 判斷是否有輸出目標# verify_outputfunction verify_output() {: in_file="${1}"# 定義要在這個函式中所使用的區域變數: if (( "${#}" == 0)); then: : prompt_error "No output file passed."# 若無輸入目標,則顯示錯誤訊息: elif echo '' > "${in_file}"; (( "${?}" != 0 )); then: : prompt_error "Cannot create a file '${in_file}'."
# echo '' > "${in_file}",產生這個名字的空檔案
# ${?} 為前一指令狀態的回應值,在此為產生檔案的狀態
# (( "${?}" !=0 )),表若產生檔案失敗,則顯示錯誤訊息: fi: return 0} - 產生中間素材 (mezzanine)# generate a mezzanine file from an archive masterfunction make_mezzanine() {: in_file="${1}": out_file="${2}"# 定義要在這個函式中所使用的區域變數,輸入與輸出檔: ffmpeg \: : -framerate 24 -f image2 \: : -i "${in_file}" \: : -c:v prores_ks -profile:v 3 \: : "${out_file}"# 使用 ffmpeg 製作 24 FPS Prores HQ 檔案
# -framerate:格率
# -f image2:強制拆解封包為單一影像檔案串
# -i :輸入檔案
# -c:v prores_ks:使用 ProRes 做為影像解編碼器
# -profile:v 3:選擇 ProRes 類型,"3"表 HQ: (( "${?}" !=0 )) && prompt_error "Script stopped at mezzanine."# 若 ffmpeg 指令失敗,則顯示錯誤訊息} - 產生取用素材 (access)# generate an access file from a mezzanine filefunction make_access() {: in_file="${1}": out_file="${2}": ffmpeg \: : -i "${in_file}" \: : -c:v libx264 -preset veryslow -crf 18 \: : -pix_fmt yuv420p -movflags +faststart \: : "${out_file}"# 使用 ffmpeg 製作 H.264 預覽檔
# -c:v libx264:使用 H.264 做為影像解編碼器
# -preset veryslow:編碼速度,越慢表品質越佳
# -crf 18:位元自動分配程度,"18"視覺無損壓縮,越小越佳
# -pix_fmt yuv420p:像素格式使用 YUV 4:2:0 色彩空間
# -movflags +faststart:若影像要供網頁瀏覽使用
# 此參數移動部份資訊至開頭,使得檔案不需全部載入即可播放(( "${?}" !=0 )) && prompt_error "Script stopped at access."} - 產生畫格與檔案的雜湊值 (MD5)# generate frame MD5 checksumsfunction make_frame_MD5() {: in_file="${1}": out_file="${in_file%.*}_${in_file##*.}_frame.md5": ffmpeg -i "${in_file}" -f framemd5 "${out_file}"}# 製作產生檔案逐格 MD5 資訊的檔案# generate file's MD5 checksumsfunction make_MD5(){: in_file="${1}": out_file="${in_file%.*}_${in_file##*.}.md5": md5 -r "${in_file}" > "${out_file}"}
# 記錄產出檔案的 MD5,md5 -r,表 MD5 值在前,檔名在後 - 主指令區
- 讀取輸入參數while getopts ":i:o:amh" opt; do: case "${opt}" in: : i) input_file="${OPTARG}" ;;: : o) output_file="${OPTARG}" ;;: : a) mode='access' ;;: : m) mode='mezzanine' ;;: : h) prompt_help ;;: : :) prompt_error "The parameter '-${OPTARG}' requires a value." ;;: : *) prompt_error "Option '-${OPTARG}' is not valid." ;;: esacdone
- getopts "Optstring" var_name:雙引號中間的字串代表會用到的參數指標,其中冒號(**:)代表該指標要帶有參數值。在此段落中,代表使用 -i 與 -o 時,要夾帶輸入與輸出目標。
- 參數為 -i 時,定義變數 $input_file 為後方字串
- 參數為 -o 時,定義變數 $output_file 為後方字串
- 參數為 -a 時,定義變數 $mode 為 "access"
- 參數為 -m 時,定義變數 $mode 為 "mezzanine"
- 參數為 -h 時,執行 prompt_help 函式
- 在某些環境中,第一個冒號前無指標,表示參數為空值("")。因此顯示錯誤訊息,提示需輸入參數數值。但一般狀況下,可能直接被當成其他值 (*)。
- 在當參數不屬於上述 ( i | o | amh ) 時,顯示這個參數無效。
- 分析輸入與輸出目標# verify input and output providedif [[ "${mode}" == 'mezzanine' ]]; then: number=$(echo "${input_file}" | grep -o -E '_[0-9]+.' | grep -o -E '[0-9]+'): verify_input "${input_file%_*}_${number%?}1.${input_file##*.}"else: verify_input "${input_file}"fi[[ ! "${output_file}" ]] && prompt_error "No output file passed."
- 輸入檔的部份,因為用來測試的典藏母檔為一系列連續數字所組成的影像檔。因此需要透過拆分檔名的方式,讓檔案可以通過 verify_input 這個函式。因此透過 $mode 這個變數確認工作狀態。
- grep -o -E '_[0-9]+.',表拆出字串中確實由"下底線(_)數字(0-9)點(.)"所組成的部份。再執行 grep -o -E '[0-9]+',確實拆出序列影像檔中的數字區段。
-o 表只輸出字串中確實符合部份
-E 表使用擴充正規表示式 (Extended Regular Express,見後附錄) - ${number%?}1 表取代數字串的最右一位數,更換為"1",因此例如數字 "012" 中的 "2" 會被 "1" 取代,成為 "011"。整串指令表示,使用檔名中編號為 "**1" 的檔案來進行是否為影音檔的判別。
- 若為取用檔時 ( mode = "access" ),由於輸入檔為單一影片檔,因此可以直接將檔名傳遞給 verify_input 這個函式。
- 輸出檔的確認,表"若不存在輸出檔,則顯示錯誤訊息"[[ ! "${output_file}" ]] && prompt_error "No output file passed."也可寫成[[ "${output_file}" ]] || prompt_error "No output file passed."利用 && 和 || 相反的特性來驅動後面指令。
- 產生輸出檔案# select mode and generate filescase "${mode}" in: mezzanine)·: : input_file_regex="${input_file%_*}_%0${#number}d.${input_file##*.}": : make_mezzanine "${input_file_regex}" "${output_file}"·: : ;;: access) make_access "${input_file}" "${output_file}" ;;: *) prompt_error "No mode (access or mezzanine) selected." ;;esacmake_frame_MD5 "${output_file}"make_MD5 "${output_file}"
- 當 $mode 為 mezzanine 時,先執行重建檔名的部份,再將檔名傳遞給 make_mezzanine 這個函式。 %0${#number}d 為 ffmpeg 所需,序列影像檔中數字命名方式,如 %03d 表三位數連續序列。
- 當 $mode 為 access 時,直接將檔名傳遞給 make_access 這個函式。
- 若非上註狀況,則顯示錯誤訊息。
- 最後兩行則將輸出檔送至 make_frame_MD5 與 make_MD5 函式處理,以產生相對應的畫格與檔案的 MD5 值。
實際執行這個腳本試試:
- 指令確認
bash-4.4$ ./transcode
Help:
transcode -h
- 顯示操作說明
bash-4.4$ ./transcode -h
Syntax:
transcode (-a | -m) -i INPUT_FILE -o OUTPUT_FILE
transcode -h
Parameters:
-a generate an access file
-m generate a mezzanine file
-i define the INPUT_FILE
-o define the OUTPUT_FILE
-h display this help
- 輸入目標非檔案
bash-4.4$ ./transcode -a -i no_file
Error: 'no_file' is not a file.
- 輸入目標非影音檔
bash-4.4$ ./transcode -a -i out_mp4.md5
Error: 'out_mp4.md5' is not an AV file.
- 判斷是否有輸出目標
bash-4.4$ ./transcode -a -i video.mp4
Error: No output file passed.
- 產生中間素材 (mezzanine)
bash-4.4$ ./transcode -m -i dpx/sample_001.dpx -o mezz.mov
確認是否產生影像檔與 MD5 檔
bash-4.4$ ls mezz*
mezz.mov mezz_mov.md5 mezz_mov_frame.md5
- 產生取用素材 (access)
bash-4.4$ ./transcode -a -i mezz.mov -o access.mp4
確認是否產生影像檔與 MD5 檔
bash-4.4$ ls access*
access.mp4 access_mp4.md5 access_mp4_frame.md5
- 產生輸出目標時發生錯誤
bash-4.4$ echo '' > asdf.mp4 ; chmod -w asdf.mp4
bash-4.4$ ./transcode -a -i video.mp4 -o asdf.mp4
(ffmpeg 輸出,省略)
File 'asdf.mp4' already exists. Overwrite? [y/N] y
asdf.mp4: Permission denied
Error: Script stopped at access.
這便是在產生中間素材與取用素材的腳本中使用 case 判斷式的方式。如果需要應付不同素材時,最可能需要修改的部份為 # verify input and output provided 這個段落。確保交給 ffmpeg 所執行的輸入與輸出格式符合規則及預期目標。若錯誤訊息為 Error: Script stopped at 則表示主要用於轉碼的 ffmpeg 指令有誤,建議回顧 ffmpeg 指令介紹,可參見下附錄。
附錄,說明,此文所使用的腳本與影片可由下列連結取得:
- ANSI 色碼:Wiki連結
- 擴充正規表達式:Wiki連結
- 未加註解原始腳本檔:show3.sh, transcode
- 測試用序列影像檔:dpx.zip, dpx.tar.gz
- 也可以利用 ffmpeg 產生測試用序列影像:
mkdir dpx ; ffmpeg \
-f lavfi -i testsrc2=duration=5:size=320x240:rate=24 \
dpx/sample_%03d.dpxtra
-
ffmpeg 將序列影像轉碼為影片:外部連結
-
ffmpeg 將影片轉碼為 H.264 格式:外部連結
-
ffprob 顯示影音檔 metadata:外部連結
-
ffplay 播放序列影像:外部連結
實際執行這個腳本試試:
- 指令確認bash-4.4$ ./transcodeHelp:transcode -h
- 顯示操作說明bash-4.4$ ./transcode -hSyntax:transcode (-a | -m) -i INPUT_FILE -o OUTPUT_FILEtranscode -hParameters:-a generate an access file-m generate a mezzanine file-i define the INPUT_FILE-o define the OUTPUT_FILE-h display this help
- 輸入目標非檔案bash-4.4$ ./transcode -a -i no_fileError: 'no_file' is not a file.
- 輸入目標非影音檔bash-4.4$ ./transcode -a -i out_mp4.md5Error: 'out_mp4.md5' is not an AV file.
- 判斷是否有輸出目標bash-4.4$ ./transcode -a -i video.mp4Error: No output file passed.
- 產生中間素材 (mezzanine)bash-4.4$ ./transcode -m -i dpx/sample_001.dpx -o mezz.mov確認是否產生影像檔與 MD5 檔bash-4.4$ ls mezz*mezz.mov mezz_mov.md5 mezz_mov_frame.md5
- 產生取用素材 (access)bash-4.4$ ./transcode -a -i mezz.mov -o access.mp4確認是否產生影像檔與 MD5 檔bash-4.4$ ls access*access.mp4 access_mp4.md5 access_mp4_frame.md5
- 產生輸出目標時發生錯誤bash-4.4$ echo '' > asdf.mp4 ; chmod -w asdf.mp4bash-4.4$ ./transcode -a -i video.mp4 -o asdf.mp4(ffmpeg 輸出,省略)File 'asdf.mp4' already exists. Overwrite? [y/N] yasdf.mp4: Permission deniedError: Script stopped at access.
這便是在產生中間素材與取用素材的腳本中使用 case 判斷式的方式。如果需要應付不同素材時,最可能需要修改的部份為 # verify input and output provided 這個段落。確保交給 ffmpeg 所執行的輸入與輸出格式符合規則及預期目標。若錯誤訊息為 Error: Script stopped at 則表示主要用於轉碼的 ffmpeg 指令有誤,建議回顧 ffmpeg 指令介紹,可參見下附錄。
附錄,說明,此文所使用的腳本與影片可由下列連結取得:
- ANSI 色碼:Wiki連結
- 擴充正規表達式:Wiki連結
- 未加註解原始腳本檔:show3.sh, transcode
- 測試用序列影像檔:dpx.zip, dpx.tar.gz
- 也可以利用 ffmpeg 產生測試用序列影像:mkdir dpx ; ffmpeg \
-f lavfi -i testsrc2=duration=5:size=320x240:rate=24 \
dpx/sample_%03d.dpxtra - ffmpeg 將序列影像轉碼為影片:外部連結
- ffmpeg 將影片轉碼為 H.264 格式:外部連結
- ffprob 顯示影音檔 metadata:外部連結
- ffplay 播放序列影像:外部連結
留言
張貼留言