Hallo Freunde,
wieder mal möchte ich mir die Arbeit am Computer vereinfachen. Es kommt mitunter vor, das ich über DVB-C Film aufzeichne und die sind selbstverständlich vollgemüllt mit Werbung. Da ich das schon recht lange mache, habe ich viel viel Erfahrung mit ffmpeg. Viele Parameter und Optionen kommen da wie aus der Pistole geschossen, dennoch ist man es irgentwann lite, diese Schritte immer und immer wieder zu durchlaufen. Also muss man die Sache größtenteils automatisieren. Und das habe ich mittels eines Scriptes getan.
Folgendes Script erledigt für euch.
01) Fragt nach den Encodersettings für die Postproduktion (Videoschnitt) und der Ablage. (libx264, prores, prores_ks, dpx, ffv1, ffvyuv, huffyuv oder zlib(lossless codec library))
02) Listet euch die verfügbaren TransportStreams auf. (mpegts (.ts))
03) Sucht nach den idealen Video und Audiostream.
04) Erkennt unnütze Ränder und rechnet diese gleich zur Postproduktion raus. ( Suchposition kann auch manuel bestimmt werden)
05) Begrenzt Bilder pro Sekunde auf 25.
06) Rechnet anamorphe Pixel (64:45) in quadratische (1:1) um.
07) Konvertiert den Transportstream in ein Postproduktions konformes Intra-Frame Format.
08) Startet ShotCut und das erstellte File, um den brauchbaren Anteil des Films zu markieren.
09) Zerschneidet den Film in seine einzelnen Teile und entfernt die Werbung.
10) Fügt die Teile wieder zusammen.
11) Konvertiert und komprimiert den Film mit h264 zur Ablage.
Vorteile:
-Der Film wird im ersten Schritt fast lossless oder gar lossless mit libx264, prores, huffyuv, u.s.w. vorbereitet.
-Präzises Schneiden (1000/25), da nur I-Frames benutzt werden. (nur Referenzbilder, keine Differenzialbilder B oder P)
-Beim zerschneiden und zusammenfügen wird nicht erneut Encodiert, also recht schnell und kein weiterer Qualitätsverlust.
-für Besitzer von neueren nVidia Grafikkarten habe ich h264_nvenc mit berücksichtigt. Damit encodiert der Computer extrem schnell und sauber.
Nachteile:
-Ein in Postproduktions-Verfahren kodierter Film ist auf Grund des ausschließlichen Einsatz von I-Frames extrem groß. Das geht los bei 20Mbit/s bis 285Mbit/s je nach Encoder und Encodersettings.
Systemvorraussetzung:
-Linux
-Shell/Bash
-ShotCut Videoeditor
-ffmpeg
-ffmpeg compiliert mit --enable-nonfree und --enable-nvenc, wenn ihr eure Grafikkarte zur Beschleunigung benutzen wollt
Sonstiges:
Es ist noch in der Entwicklung, funktioniert aber schon. Es ist trotzdem ratsam, wenn ihr den Code versteht, damit ihr wisst, was da passiert!!!
Noch ein gut gemeinter Rat. DPX (Digital Picture Exchange) ist ein toller Encoder, da er verlustfrei ist, aber er ist auch verdammt noch mal, wahnsinnig Speicherhungrig.
Der Encoder wird in der professionellen Filmproduktion eingesetzt und eigentlich ist es überflüssig ihn hier einzusetzen, na ja was soll, isch sage... isch tu's trotzdääääääääääääm.
Den daraus entstandenen Stream könnt ihr nicht mit normalen Playern öffnen, da die meisten den dpx nicht kennen. Nicht einmal VLC!! Ihr könnt dafür ffplay benutzen...
Aber behauptet hinterher nicht, ich hätte euch nicht gewarnt...
Wichtig:
Ich habe festgestellt, dass im Moment nicht mehr als 9 Film-Schnipsel mit ShotCut erstellt werden dürfen. Grund: Mir ist noch nicht klar wie "ffmpeg -concat" die genierierte Datei mit den Files einliest. Obwohl ich die Dateien bewusst so benenne, das sie vom Computer richtig chronologisch eingelesen werden, wirft ffmpeg bei mehr als 9 Schnipseln alles durcheinander.
Wenn also jemand weiß, warum das so ist, bitte ich um Hilfe...
ffmpeg wurde mit folgenden Parametern kompiliert:
ffmpeg version 3.1.2 Copyright (c) 2000-2016 the FFmpeg developers
built with gcc 5.4.0 (Ubuntu 5.4.0-6ubuntu1~16.04.2) 20160609
configuration: --enable-gpl --enable-version3 --enable-nonfree --enable-ffplay --enable-avresample --enable-frei0r --enable-ladspa --enable-libass --enable-libbluray --enable-libbs2b --enable-libcdio --enable-libdc1394 --enable-libfaac --enable-libfdk-aac --enable-libmp3lame --enable-libopencv --enable-libtheora --enable-libtwolame --enable-libv4l2 --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libx264 --enable-libx265 --enable-libxcb --enable-libxvid --enable-openal --enable-opencl --enable-opengl --enable-sdl --enable-cuda --enable-cuvid --enable-nvenc --enable-vdpau
libavutil 55. 28.100 / 55. 28.100
libavcodec 57. 48.101 / 57. 48.101
libavformat 57. 41.100 / 57. 41.100
libavdevice 57. 0.101 / 57. 0.101
libavfilter 6. 47.100 / 6. 47.100
libavresample 3. 0. 0 / 3. 0. 0
libswscale 4. 1.100 / 4. 1.100
libswresample 2. 1.100 / 2. 1.100
libpostproc 54. 0.100 / 54. 0.100
Was ist noch geplant:
- Vorschau für die Crop-Settings per ffplay
- Manuelle Auswahl des Audio und Videostreams
- Subtitle berücksichtigen falls vorhanden
- Rückgabewerte auswerten und differenziert handeln
Versionsinfo:
20160801-03 - release
20160801-04 - fix - some bugs in function SELECT_FILM() - [ use 1 if ${FILM_CHOICE} are empty ]
20160802-01 - add - set position to scan crop by manual
20160802-02 - fix - two-pass encoding if chosing libx264
20160802-03 - fix - some paths
20160802-04 - add - increase speed, if only one movie_part is present. (no need to concat movie)
20160802-06 - fix - CROP_AT is now valid between 0 - 59 minutes
20160803-01 - add - !! outsource some parts of configuration. !!
20160804-01 - add - autodetect ! crop values are invalid ! and force scan at other position.
20160804-02 - add - function DETECT_CROP() is now checking the userinput.
20160804-03 - fix - return invalid input in DETECT_CROP() if no userinput.
20160804-04 - add - add dpx video-encoder for postproduction.
20160804-05 - fix - no concat needed, if only one movie_part is present. (just copy)
20160805-01 - add - check projectfile was saved and is'nt empty.
----------------------------------------------------------------------------------
20160805-02 - fix - check projectfile is always 'not found'
20160805-02 - change - input wide is now upscale to 1024px
20160805-02 - fix - many little bugs ...
20160805-02 - add - create var FILTER_SCALE for global configuration - stable release (tested with ffmpeg 3.1.2, nVidia Driver Version 361.31, nVidia CUDA 7.5 and nVidia Video Encoder SDK 6.01)
----------------------------------------------------------------------------------
20160826-01 - add - lossless encoder ffv1, ffvyuv, huffyuv and zlib(lossless codec library)
----------------------------------------------------------------------------------
20160828-01 - add - separate gop-settings for encoder to global configuration [ ${GOP} ]
20160828-01 - add - separate video-bit-settings for encoder to global configuration [ ${VIDEO_BITRATE} ] - stable release (tested with ffmpeg 3.1.2, nVidia Driver Version 367.44, nVidia CUDA 7.5 and nVidia Video Encoder SDK 7.01)
----------------------------------------------------------------------------------
20160829-01 - add - Configuration-Assist will call setup, if no config-file can't be found in userspace, or it is'nt executable.
20160829-02 - fix - some bugs in Configuration-Assist.
----------------------------------------------------------------------------------
20160913-01 - change - set yadif filter from 0:-1:0 to 1:-1:0. this will double up the framerate to 50fps. at the end of ffmpeg commandline the option -r 25 will reduce the framerate back to 25fps.
(this is an workoverhead but the picture quality will be increase)
----------------------------------------------------------------------------------
20170524-01 - fix - repair/fix regexp ${TRIM} in funktion TRIM_FILM() from "entry.*producer${MOVIE_PARTS}" to "entry.*producer${MOVIE_PARTS}\""
#!/bin/bash
DEBUG="0";
CLEAR="clear";
VERSION="20160913-01";
HOME_DIR="$(dirname $0)";
SCRIPT_NAME="$(basename $0)";
CONFIG_FILE="${HOME}/.config/auto_encode.cfg";
POSTPRODUCTION_VIDEO_ENCODER="prores_ks -profile:v 3 -quant_mat 3";
POSTPRODUCTION_AUDIO_ENCODER="copy";
POSTPRODUCTION_CONTAINER_FORMAT=".mov";
POSTPRODUCTION_TEMP_NAME="postproduction_movie";
FILTER_SCALE="scale=w=(iw*1.4222222222222222):h=ih";
TRIM_OUTPUT_NAME="output_t";
VIDEO_BITRATE="-b:v 6.5M -maxrate 8.5M -minrate 4M -bufsize 6.5M";
GOP="-g 150 -refs 6 -bf 3";
DESTINATION_VIDEO_ENCODER="h264_nvenc -preset 1 -profile:v 2 -level 0 -2pass 1 ${VIDEO_BITRATE} ${GOP} -qmin 0 -qmax 25 -pix_fmt yuv420p";
DESTINATION_AUDIO_ENCODER="ac3 -b:a 384k";
DESTINATION_CONTAINER_FORMAT=".mkv";
FFMPEG_LOGLEVEL="-loglevel 8 -stats";
CROP_AT="07";
MOVIE_PARTS="0";
CHECK_ENV() {
${CLEAR};
echo "Scriptversion: ${SCRIPT_NAME} ${VERSION}";
if [ ${UID} -eq 0 ]; then
echo "Das Script darf nicht als Rootuser ausgeführt werden.";
PRESS_ENTER;
exit 1;
fi
if [ ! -x ${CONFIG_FILE} ]; then
echo "Konfigurationsdatei entweder nicht gefunden oder nicht ausführbar.";
CREATE_CONFIG;
source ${CONFIG_FILE};
else
source ${CONFIG_FILE};
fi
for DIRECTORY in "${SOURCE_VIDEO_DIR}" "${POSTPRODUCTION_DIR}" "${DESTINATION_DIR}";
do
if [ ! -d "${DIRECTORY}" ]; then
echo "Verzeichnis ${DIRECTORY} wurde nicht gefunden.";
PRESS_ENTER;
exit 1;
fi
done
for EXECUTABLE in "${SHOTCUT_EXECUTABLE}" "${FFMPEG_EXECUTABLE}" "${FFPLAY_EXECUTABLE}";
do
if [ ! -x "${EXECUTABLE}" ]; then
echo "Programm "${EXECUTABLE}" wurde nicht gefunden, oder ist nicht ausführbar.";
PRESS_ENTER;
exit 1;
fi
done
if [ $(ls ${POSTPRODUCTION_DIR}${SCRIPT_NAME} > /dev/null 2>&1; echo ${?}) -eq 0 ]; then
echo "Das Script darf sich nicht im Postproduktions Verzeichnis befinden.";
PRESS_ENTER;
exit 1;
fi
rm -R ${POSTPRODUCTION_DIR}{.*,*} > /dev/null 2>&1;
}
CREATE_CONFIG() {
INPUT_LOOP="1";
while [ "${INPUT_LOOP}" -eq 1 ];
do
echo "Das Programm kann nur mit einer Konfigurationsdatei im Heimverzeichnis funktionieren.";
read -p "Möchten Sie den Einrichtungsassistenten benutzen, um eine Konfiguration anzulegen? j/n [j]: " CREATE_CONFIG_FILE;
if [ -z "${CREATE_CONFIG_FILE}" ]; then
CREATE_CONFIG_FILE="j";
fi
case ${CREATE_CONFIG_FILE} in
j) ${CLEAR};
echo "Der Einrichtungsassistent führt Sie nun durch einige Fragen bezüglich benötigter Pfade.";
PRESS_ENTER;
unset INPUT_LOOP;
CONFIG_ASSIST;
;;
n) exit 0;
;;
*) echo "Antworten Sie nur mit j oder n.";
;;
esac
done
}
CONFIG_ASSIST() {
INPUT_LOOP="1";
echo -e "#!/bin/bash\n# FILE MUST BE EXECUTABLE !!!" > ${CONFIG_FILE};
echo "!! WICHTIG!! Alle Pfade müssen absolut angegeben werden.";
while [ ${INPUT_LOOP} = 1 ];
do
read -p "Bitte geben Sie den Pfad zum Verzeichnis der aufgenommenen DVB-C Videostreams an: " SET_PATH;
CHECK_CONFIG_PATH;
echo "SOURCE_VIDEO_DIR=\"${SET_PATH}\";" >> ${CONFIG_FILE};
done
INPUT_LOOP="1";
while [ ${INPUT_LOOP} = 1 ];
do
read -p "Bitte geben Sie den Pfad zum Verzeichnis für die Postproduktion an: " SET_PATH;
CHECK_CONFIG_PATH;
echo "POSTPRODUCTION_DIR=\"${SET_PATH}\";" >> ${CONFIG_FILE};
done
INPUT_LOOP="1";
while [ ${INPUT_LOOP} = 1 ];
do
read -p "Bitte geben Sie den Pfad zum Verzeichnis der Videoablage an: " SET_PATH;
CHECK_CONFIG_PATH;
echo "DESTINATION_DIR=\"${SET_PATH}\";" >> ${CONFIG_FILE};
done
INPUT_LOOP="1";
while [ ${INPUT_LOOP} = 1 ];
do
read -p "Bitte geben Sie den Pfad zu ShotCut an: " SET_PATH;
CHECK_CONFIG_PATH;
if [ -x "${SET_PATH}shotcut" ]; then
SET_PATH=$(echo ${SET_PATH}shotcut);
echo "SHOTCUT_EXECUTABLE=\"${SET_PATH}\";" >> ${CONFIG_FILE};
else
echo "Die ShotCut Executable wurde nicht gefunden.";
fi
done
INPUT_LOOP="1";
while [ ${INPUT_LOOP} = 1 ];
do
read -p "Bitte geben Sie den Pfad zu ffmpeg an: " SET_PATH;
CHECK_CONFIG_PATH;
if [ -x "${SET_PATH}ffmpeg" ]; then
SET_PATH=$(echo ${SET_PATH}ffmpeg);
echo "FFMPEG_EXECUTABLE=\"${SET_PATH}\";" >> ${CONFIG_FILE};
else
echo "Die ffmpeg Executable wurde nicht gefunden.";
fi
done
INPUT_LOOP="1";
while [ ${INPUT_LOOP} = 1 ];
do
read -p "Bitte geben Sie den Pfad zu ffplay an: " SET_PATH;
CHECK_CONFIG_PATH;
if [ -x "${SET_PATH}ffplay" ]; then
SET_PATH=$(echo ${SET_PATH}ffplay);
echo "FFPLAY_EXECUTABLE=\"${SET_PATH}\";" >> ${CONFIG_FILE};
else
echo "Die ffplay Executable wurde nicht gefunden.";
fi
done
chmod u+x ${CONFIG_FILE};
echo "Konfiguration vollständig. Nun kann es losgehen..."
}
CHECK_CONFIG_PATH() {
if [ -z "${SET_PATH}" ]; then
echo "Keine Eingabe..."; unset SET_PATH; continue;
fi
if [ ! -d "${SET_PATH}" ]; then
echo "Der Pfad "${SET_PATH}" existiert nicht."; unset SET_PATH; continue;
fi
if [ "$(echo ${SET_PATH} | grep '^/.*' > /dev/null 2>&1; echo ${?})" -gt 0 ]; then
echo "${SET_PATH} ist keine absolute Pfadangabe"; unset SET_PATH; continue;
fi
if [ "$(echo ${SET_PATH} | grep '.*/$' > /dev/null 2>&1; echo ${?})" -gt 0 ]; then
SET_PATH=$(echo "${SET_PATH}/");
fi
INPUT_LOOP="0";
}
ENCODER_CHOICE() {
echo -e "!! Achtung: Die Ausführung einiger Funktionen beansprucht je nach Encoder-Einstellungen und nach Film/Datei-größe sehr viel Zeit und Speicherplatz !!\n";
read -p "Möchten Sie fortfahren j/n [n]: " CONTINUE;
if [ "${CONTINUE}" != "j" ]; then
exit 0;
fi
unset CONTINUE;
ENCODER_SETTINGS="0";
while [ ${ENCODER_SETTINGS} -eq 0 ];
do
echo -e "\n## Einstellungen für die Postproduktion (Videoschnitt) ##";
echo "---------------------------------------------------------";
echo "Videoencoder: ${POSTPRODUCTION_VIDEO_ENCODER}";
echo "Audioencoder: ${POSTPRODUCTION_AUDIO_ENCODER}";
echo -e "Conainer-Format: ${POSTPRODUCTION_CONTAINER_FORMAT}\n";
echo "## Einstellungen für die Ablage des Films ##";
echo "--------------------------------------------";
echo "Videoencoder: ${DESTINATION_VIDEO_ENCODER}";
echo "Audioencoder: ${DESTINATION_AUDIO_ENCODER}";
echo -e "Conainer-Format: ${DESTINATION_CONTAINER_FORMAT}\n";
read -p "Möchten Sie die Encoder-Einstellungen ändern? j/n [n]: " ENCODER;
if [ "${ENCODER}" = "j" ]; then
${CLEAR};
echo "## Einstellungen für die Postproduktion (Videoschnitt) ##";
echo "---------------------------------------------------------";
POST_VIDEO_ENCODER;
POST_AUDIO_ENCODER;
echo -e "\n## Einstellungen für die Ablage des Films ##";
echo "--------------------------------------------";
DEST_VIDEO_ENCODER;
DEST_AUDIO_ENCODER;
else
ENCODER_SETTINGS="1";
fi
done
}
POST_VIDEO_ENCODER() {
INPUT_LOOP="1";
while [ ${INPUT_LOOP} -eq 1 ];
do
INPUT_LOOP="0";
echo -e "\n[1] (Intermediate) libx264 -preset slow -qp 0 -g 1 -bf 0 -pix_fmt yuv422p";
echo "[2] (Intermediate) prores -pix_fmt yuv422p10le";
echo "[3] (Intermediate) prores_ks -profile:v 3 -quant_mat 3 -pix_fmt yuv422p10le";
echo "[4] (Lossless) dpx -pix_fmt rgb24";
echo "[5] (Lossless) ffv1 -pix_fmt yuv422p";
echo "[6] (Lossless) ffvhuff -pix_fmt yuv422p";
echo "[7] (Lossless) huffyuv -pix_fmt yuv422p";
echo "[8] (Lossless) zlib -pix_fmt bgr24";
echo -e "\n!!! Warnung !!! dpx (Digital Picture Exchange) produziert unglaublich große Dateien mit durchschnittlich 250Mbit/s@720x576\n";
read -p "Auswahl Video-Encoder: " VIDEO_ENCODER_FOR_POST;
case "${VIDEO_ENCODER_FOR_POST}" in
1) POSTPRODUCTION_VIDEO_ENCODER="libx264 -preset slow -qp 0 -g 1 -bf 0 -pix_fmt yuv422p";
;;
2) POSTPRODUCTION_VIDEO_ENCODER="prores -pix_fmt yuv422p10le";
;;
3) POSTPRODUCTION_VIDEO_ENCODER="prores_ks -profile:v 3 -quant_mat 3 -pix_fmt yuv422p10le";
;;
4) POSTPRODUCTION_VIDEO_ENCODER="dpx -pix_fmt rgb24";
;;
5) POSTPRODUCTION_VIDEO_ENCODER="ffv1 -pix_fmt yuv422p";
;;
6) POSTPRODUCTION_VIDEO_ENCODER="ffvhuff -pix_fmt yuv422p";
;;
7) POSTPRODUCTION_VIDEO_ENCODER="huffyuv -pix_fmt yuv422p";
;;
8) POSTPRODUCTION_VIDEO_ENCODER="zlib -pix_fmt bgr24";
;;
*) echo "ungültige Auswahl";
PRESS_ENTER;
INPUT_LOOP="1";
;;
esac
done
unset INPUT_LOOP;
}
POST_AUDIO_ENCODER() {
INPUT_LOOP="1";
while [ ${INPUT_LOOP} -eq 1 ];
do
INPUT_LOOP="0";
echo -e "\n[1] copy";
echo "[2] pcm_s16le";
read -p "Auswahl Audio-Encoder: " AUDIO_ENCODER_FOR_POST;
case "${AUDIO_ENCODER_FOR_POST}" in
1) POSTPRODUCTION_AUDIO_ENCODER="copy";
;;
2) POSTPRODUCTION_AUDIO_ENCODER="pcm_s16le";
;;
*) echo "ungültige Auswahl";
PRESS_ENTER;
INPUT_LOOP="1";
;;
esac
done
unset INPUT_LOOP;
}
DEST_VIDEO_ENCODER() {
INPUT_LOOP="1";
while [ ${INPUT_LOOP} -eq 1 ];
do
INPUT_LOOP="0";
echo -e "\n[1] libx264 -preset slow -profile:v high ${VIDEO_BITRATE} ${GOP} -pix_fmt yuv420p -pass 2";
echo "[2] h264_nvenc -preset 1 -profile:v 2 -level 0 -2pass 1 ${VIDEO_BITRATE} ${GOP} -qmin 0 -qmax 25 -pix_fmt yuv420p";
read -p "Auswahl Video-Encoder: " VIDEO_ENCODER_FOR_DEST;
case "${VIDEO_ENCODER_FOR_DEST}" in
1) DESTINATION_VIDEO_ENCODER="libx264 -preset slow -profile:v high ${VIDEO_BITRATE} ${GOP} -pix_fmt yuv420p";
;;
2) DESTINATION_VIDEO_ENCODER="h264_nvenc -preset 1 -profile:v 2 -level 0 -2pass 1 ${VIDEO_BITRATE} ${GOP} -qmin 0 -qmax 25 -pix_fmt yuv420p";
;;
*) echo "ungültige Auswahl";
PRESS_ENTER;
INPUT_LOOP="1";
;;
esac
done
}
DEST_AUDIO_ENCODER() {
INPUT_LOOP="1";
while [ ${INPUT_LOOP} -eq 1 ];
do
INPUT_LOOP="0";
echo -e "\n[1] copy";
echo "[2] ac3 -b:a 384k";
echo "[3] aac -b:a 384k";
read -p "Auswahl Audio-Encoder: " AUDIO_ENCODER_FOR_DEST;
case "${AUDIO_ENCODER_FOR_DEST}" in
1) DESTINATION_AUDIO_ENCODER="copy";
;;
2) DESTINATION_AUDIO_ENCODER="ac3 -b:a 384k";
;;
3) DESTINATION_AUDIO_ENCODER="aac -b:a 384k";
;;
*) echo "ungültige Auswahl";
PRESS_ENTER;
INPUT_LOOP="1";
;;
esac
done
${CLEAR};
}
#RUN_MODE_CHOICE() {
#read -p "Automatischer Modus oder interaktiver Modus? a/i [i]: " AUTINT;
#
#if [ -z ${AUTINT} ]; then
# AUTINT="i";
#fi
#
#case ${AUTINT} in
# a) RUN_MODE="AUTO"; MAIN;
# ;;
# i) RUN_MODE="MANU"; MAIN;
# ;;
# *) echo "Unbekannte Auswahl ${AUTINT}"; exit 1
# ;;
#esac
#}
SELECT_FILM() {
echo -e "\nWelchen Titel möchten Sie bearbeiten?";
INPUT_LOOP="0";
while [ ${INPUT_LOOP} -eq 0 ];
do
for MENU in ${SOURCE_VIDEO_DIR}*.ts;
do
if [ -z "${MENU}" ]; then
echo "Es wurden keine Titel gefunden!";
PRESS_ENTER; exit 0;
fi
echo "${MENU}" >> ${POSTPRODUCTION_DIR}.film_choice;
done
cat -n ${POSTPRODUCTION_DIR}.film_choice;
read -p "Bitte treffen Sie eine Auswahl [1]: " FILM_CHOICE;
if [ -z "${FILM_CHOICE}" ]; then
FILM_CHOICE="1";
fi
SELECTED_FILM=$(sed -ne "${FILM_CHOICE}p" ${POSTPRODUCTION_DIR}.film_choice);
if [ -z "${SELECTED_FILM}" ]; then
echo "ungültige Auswahl...";
PRESS_ENTER;
else
INPUT_LOOP="1";
fi
rm ${POSTPRODUCTION_DIR}.film_choice > /dev/null 2>&1;
done
}
MODIFY_DESTINATION_NAME() {
DESTINATION_FILM_NAME=$(echo ${SELECTED_FILM} | grep -o '[^/]*.ts$' | tr [:upper:] [:lower:] | sed -e 's/\.ts//g');
echo "vorgeschlagener Titel für die Lagerung: ${DESTINATION_FILM_NAME}";
read -p "Möchten Sie den Titel ändern? j/n [n]: " CHOICE;
if [ "${CHOICE}" = "j" ]; then
read -p "Neuer Titel (ohne Dateierweiterung): " DESTINATION_FILM_NAME;
fi
}
FIND_STREAMS() {
echo "automatische Auswahl der Audio/Video-Streams.";
ffmpeg -i ${SELECTED_FILM} >> ${POSTPRODUCTION_DIR}.stream_info 2>&1;
VIDEO_STREAM=$(grep Video ${POSTPRODUCTION_DIR}.stream_info | grep -o '#0:[0-9]' | sed -e 's/#//g');
if [ $(egrep 'Stream.*Audio.*ac3' ${POSTPRODUCTION_DIR}.stream_info > /dev/null 2>&1; echo ${?}) -eq 0 ]; then
AUDIO_STREAM=$(egrep 'Stream.*Audio.*ac3' ${POSTPRODUCTION_DIR}.stream_info | grep -o '#0:[0-9]' | sed -e 's/#//g');
else
AUDIO_STREAM=$(egrep 'Stream.*Audio.*mp2' ${POSTPRODUCTION_DIR}.stream_info | grep -o '#0:[0-9]' | sed -e 's/#//g');
fi
for STREAM in "${VIDEO_STREAM}" "${AUDIO_STREAM}";
do
if [ -z ${STREAM} ]; then
echo "kein passender Audio/Video-Stream gefunden. Das Programm wird beendet."; exit 1;
rm ${POSTPRODUCTION_DIR}.stream_info > /dev/null 2>&1;
fi
done
}
DETECT_CROP() {
CROP_LOOP="1";
while [ ${CROP_LOOP} -eq 1 ];
do
echo "Berechne Filmränder. Bitte warten...";
CROP=$(ffmpeg ${FFMPEG_LOGLEVEL} -i "${SELECTED_FILM}" -map ${VIDEO_STREAM} -ss 00:"${CROP_AT}":00.000 -t 00:01:00.000 -vf "yadif=0:-1:0,${FILTER_SCALE}" -an -f mpegts - 2>&1 | ffmpeg -i - -vf "cropdetect=24:2:0" -f null - 2>&1 | awk '/crop/ { print $NF }' | tail -1 | egrep -o '?(-|)[0-9]+:?(-|)[0-9]+:[0-9]+:[0-9]+$');
W="1024";
H=$(echo ${CROP} | awk -F: '{ print $2 }');
let H=${H}-8;
X="0";
Y=$(echo ${CROP} | awk -F: '{ print $4 }');
let Y=${Y}+4;
CROP_THIS=$(echo "w=${W}:h=${H}:x=${X}:y=${Y}");
${CLEAR};
for CHECK_CROP in "${H}" "${Y}";
do
if [ -z "${CHECK_CROP}" ]; then
CROP_FAIL="FAIL";
CALC_BLACK="j";
break;
fi
done
if [ ! -z "${CROP_FAIL}" ]; then
echo "###################################################";
echo "Die errechneten Werte der Bildränder sind ungültig.";
echo -e "###################################################\n";
echo "Sie müssen daher an einer anderen Position berechnen lassen.";
else
echo "folgende Bilddimensionen wurden errechnet";
echo -e "-----------------------------------------\n";
echo "Bild breite : ${W}";
echo "Bild höhe : ${H}";
echo "Bild beginnt auf der X-Achse (horizontal) bei: ${X}";
echo -e "Bild beginnt auf der Y-Achse (vertikal) bei : ${Y}\n";
echo "Es kann sein, das die Werte nicht stimmen, da es an der Voreingestellten Position des Films sehr dunkel ist oder gerade Werbung lief.";
echo "Schauen Sie sich den Film an und suchen Sie im Zweifelsfall selber eine geeignete Position.";
read -p "Möchten Sie bei einer anderen Position des Films die Ränder erneut berechnen lassen? j/n [n]: " CALC_BLACK;
if [ -z "${CALC_BLACK}" ]; then
CALC_BLACK="n";
fi
fi
CHECK_INPUT="0";
while [ "${CHECK_INPUT}" -eq 0 ];
do
case "${CALC_BLACK}" in
j) echo -e "Berechnet wurde der letzte Durchgang bei Minute ${CROP_AT}.\n";
echo "!! Geben sie Zahlen unter 10 immer mit zwei Dezimalstellen ein. Bsp: 05";
echo -e "!! Die Position zum berechnen muss zwischen 00 und 59 Minuten liegen.\n";
read -p "An welcher Position des Films soll erneut berechnet werden (Minute) : " CROP_AT;
if [ $(echo "${CROP_AT}" | egrep '[0-9]{2}' > /dev/null 2>&1; echo ${?}) -eq 0 ]; then
CHECK_INPUT="1";
else
${CLEAR};
echo "Die Eingabe war ungültig. Bitte noch einmal und lesen Sie diesmal den Text genau.";
PRESS_ENTER;
continue;
fi
if [ "${CROP_AT}" -gt 59 ]; then
${CLEAR};
echo "Die Eingabe war ungültig. Bitte noch einmal und lesen Sie diesmal den Text genau.";
PRESS_ENTER;
CHECK_INPUT="0";
continue;
fi
;;
n) CROP_LOOP="0";
CHECK_INPUT="1";
;;
*) echo "ungültige Eingabe"; exit 1;
;;
esac
done
unset CROP_FAIL;
unset CALC_BLACK;
done
}
SUMMARY() {
${CLEAR};
echo -e "## Zusammenfassung aller Einstellungen ##\n";
echo "Ausgewählter Film : ${SELECTED_FILM}";
echo "Dateiname Ablage : ${DESTINATION_FILM_NAME}${DESTINATION_CONTAINER_FORMAT}";
echo "Post V : ${POSTPRODUCTION_VIDEO_ENCODER}";
echo "Post A : ${POSTPRODUCTION_AUDIO_ENCODER}";
echo "Ablage V : ${DESTINATION_VIDEO_ENCODER}";
echo -e "Ablage A : ${DESTINATION_AUDIO_ENCODER}\n";
echo "Videospur : $(grep "#${VIDEO_STREAM}.*Video:" ${POSTPRODUCTION_DIR}.stream_info | sed -e 's/ //g')";
echo "Audiospur : $(grep "#${AUDIO_STREAM}.*Audio:" ${POSTPRODUCTION_DIR}.stream_info | sed -e 's/ //g')";
echo -e "Bilddimensionen : ${CROP_THIS}\n";
read -p "Damit Fortfahren?: j/n [n]: " CONTINUE;
if [ "${CONTINUE}" != "j" ]; then
exit 0;
fi
rm ${POSTPRODUCTION_DIR}.stream_info > /dev/null 2>&1;
}
COPY_FILM_TO_POSTPRODUCTION() {
echo -e "\nKopiere Film. Bitte warten...";
ffmpeg ${FFMPEG_LOGLEVEL} -i ${SELECTED_FILM} -map ${VIDEO_STREAM} -map ${AUDIO_STREAM} ${ONLY_ONE_MINUTE} -c:v ${POSTPRODUCTION_VIDEO_ENCODER} -sws_flags lanczos -vf "yadif=1:-1:0,${FILTER_SCALE},crop=${CROP_THIS}" -r 25 -c:a ${POSTPRODUCTION_AUDIO_ENCODER} ${POSTPRODUCTION_DIR}${POSTPRODUCTION_TEMP_NAME}${POSTPRODUCTION_CONTAINER_FORMAT};
sync;
}
POSTPRODUCTION() {
CHECK_POST="0";
echo -e "\nStarte Postproduktion...";
echo "Der Film wurde kopiert. Nun wird ShotCut starten und Sie können die Filmabschnitte markieren und in die Wiedergabeliste legen.";
echo "Speichern Sie das Projekt danach im Verzeichnis \"${POSTPRODUCTION_DIR}\" und beenden ShotCut.";
echo -e "Unmittelbar danach arbeitet das Script weiter.";
PRESS_ENTER;
while [ ${CHECK_POST} -eq 0 ];
do
rm ${POSTPRODUCTION_DIR}*.mlt > /dev/null 2>&1;
${SHOTCUT_EXECUTABLE} ${POSTPRODUCTION_DIR}${POSTPRODUCTION_TEMP_NAME}${POSTPRODUCTION_CONTAINER_FORMAT} > /dev/null 2>&1;
if [ -s "${POSTPRODUCTION_DIR}"*\.mlt ]; then
if [ $(egrep 'entry.*producer[0-9]{1,2}\"' "${POSTPRODUCTION_DIR}"*\.mlt > /dev/null 2>&1; echo ${?}) -eq 0 ]; then
CHECK_POST="1";
else
echo "Das Projekt wurde zwar gespeichert, ist aber unvollständig.";
PRESS_ENTER;
fi
else
echo "Keine Projekt-Datei gefunden.";
PRESS_ENTER;
fi
done
}
#SORT_FILES() {
#}
TRIM_FILM() {
echo -e "\nTrimme Film. Bitte warten...";
TRIM_LOOP="0";
LEAD_ZERO="0";
while [ ${TRIM_LOOP} -eq 0 ];
do
TRIM=$(egrep "entry.*producer${MOVIE_PARTS}\"" ${POSTPRODUCTION_DIR}*.mlt | egrep -o '(in=|out=).*[0-9][0-9]:[0-9][0-9]:[0-9][0-9],[0-9][0-9][0-9]\"' | sed -e 's/,/./g' | sed -e 's/"//g' | sed -e 's/in=/\-ss /g' | sed -e 's/out=/-to /g');
if [ -z "${TRIM}" ]; then
TRIM_LOOP="1";
else
if [ "${MOVIE_PARTS}" -gt 9 ]; then
unset LEAD_ZERO;
fi
ffmpeg ${FFMPEG_LOGLEVEL} -i ${POSTPRODUCTION_DIR}${POSTPRODUCTION_TEMP_NAME}${POSTPRODUCTION_CONTAINER_FORMAT} -c:v copy ${TRIM} -c:a copy ${POSTPRODUCTION_DIR}${TRIM_OUTPUT_NAME}${LEAD_ZERO}${MOVIE_PARTS}${POSTPRODUCTION_CONTAINER_FORMAT};
sync;
let MOVIE_PARTS=${MOVIE_PARTS}+1;
fi
done
rm ${POSTPRODUCTION_DIR}*.mlt;
}
CONCAT_OUTPUTS() {
if [ "${MOVIE_PARTS}" -eq 1 ]; then
mv ${POSTPRODUCTION_DIR}${TRIM_OUTPUT_NAME}00${POSTPRODUCTION_CONTAINER_FORMAT} ${POSTPRODUCTION_DIR}output${POSTPRODUCTION_CONTAINER_FORMAT};
else
echo -e "\nFüge Film-Schnipsel wieder zusammen. Bitte warten...";
ffmpeg ${FFMPEG_LOGLEVEL} -f concat -safe 0 -i <(for FILES in ${POSTPRODUCTION_DIR}${TRIM_OUTPUT_NAME}*; do echo "file '${FILES}'"; done) -c:v copy -c:a copy ${POSTPRODUCTION_DIR}output${POSTPRODUCTION_CONTAINER_FORMAT};
sync;
fi
rm ${POSTPRODUCTION_DIR}${POSTPRODUCTION_TEMP_NAME}${POSTPRODUCTION_CONTAINER_FORMAT} > /dev/null 2>&1;
rm ${POSTPRODUCTION_DIR}${TRIM_OUTPUT_NAME}* > /dev/null 2>&1;
}
STORAGE_FILM() {
echo -e "\nBeginne mit dem Encodieren für die Ablage.";
if [ "$(echo ${DESTINATION_VIDEO_ENCODER} | grep -o '^libx264')" = "libx264" ]; then
echo "Pass 1";
ffmpeg ${FFMPEG_LOGLEVEL} -i ${POSTPRODUCTION_DIR}output.mov -c:v ${DESTINATION_VIDEO_ENCODER} -pass 1 -passlogfile ${POSTPRODUCTION_DIR}ffmpeg2pass -an -f rawvideo -y /dev/null;
echo "Pass 2";
ffmpeg ${FFMPEG_LOGLEVEL} -i ${POSTPRODUCTION_DIR}output.mov -c:v ${DESTINATION_VIDEO_ENCODER} -pass 2 -passlogfile ${POSTPRODUCTION_DIR}ffmpeg2pass -c:a ${DESTINATION_AUDIO_ENCODER} "${DESTINATION_DIR}${DESTINATION_FILM_NAME}${DESTINATION_CONTAINER_FORMAT}";
rm ${POSTPRODUCTION_DIR}ffmpeg2pass*;
else
ffmpeg ${FFMPEG_LOGLEVEL} -i ${POSTPRODUCTION_DIR}output.mov -c:v ${DESTINATION_VIDEO_ENCODER} -c:a ${DESTINATION_AUDIO_ENCODER} "${DESTINATION_DIR}${DESTINATION_FILM_NAME}${DESTINATION_CONTAINER_FORMAT}";
fi
rm ${POSTPRODUCTION_DIR}output.mov;
echo "Fertig";
}
PRESS_ENTER() {
read -p "Bitte drücken Sie die Enter-Taste um fortzufahren... " DUMMY;
}
MAIN() {
if [ ${DEBUG} -eq 1 ]; then
set -xo;
ONLY_ONE_MINUTE="-ss 00:08:00.000 -t 00:01:00.000";
unset CLEAR;
unset FFMPEG_LOGLEVEL;
fi
#if [ ${RUN_MODE} = "AUTO" ]; then
# echo "Automatischer Durchlauf";
#else
# echo "Manueler Durchlauf";
#fi
CHECK_ENV;
ENCODER_CHOICE;
SELECT_FILM;
MODIFY_DESTINATION_NAME;
FIND_STREAMS;
DETECT_CROP;
SUMMARY;
COPY_FILM_TO_POSTPRODUCTION;
POSTPRODUCTION;
TRIM_FILM;
CONCAT_OUTPUTS;
STORAGE_FILM;
exit 0;
}
MAIN;
Alles anzeigen
Viel Spaß...