воскресенье, 13 февраля 2011 г.

Развлечение на Bash

На днях занимался генерацией превьюшек для фильмов. Почти все современные ТВ умеют смотреть фильмы по сети через DLNA сервис, мой же, с пропатченной прошивкой, смотрит фильмы, которые расшарены через самбу(Samba). Превьюшки телек генерит неумело и кадр выбирает как попало, к тому же хранит их у себя в памяти, которой не очень-то и много.

Итак, задача нагенерить jpeg-файлов. Особенность в том, что расширение jpeg-файла должно быть размером соответсвующего ему видеофайла, который поделили на 1000. Если размер больше 4Гб, то надо сначала вычесть 232 из размера, а потом делить. Думал, писать программку на C++, но подумалось,что скриптовые языки тоже не зря придумали. Результатом стали два скрипта на bash, которые можно видеть в конце.

Для геренации превьюшек существует небольшой опенсорс проект — FFMpegThumbnailer. Интересен он тем, что там реализован алгоритм выбора удачного кадра. В отличие от тех сложных идей, с которыми приходилось встречаться ранее, вроде поиска лица, кожи и т.п., тут реализована очень простая идея. Считается гистограмма по сотне кадров и усредняется. Потом выбирается кадр, гистограмма которого наиболее близка к средней. Работает алгоритм на удивление хорошо.

Скрипт генерации одной превьюшки:
#!/bin/bash
# gen_th.sh

FILENAME=$1
# get file size in bytes
printf "$FILENAME\t"
FILESIZE=$(stat -c%s "$FILENAME" 2>/dev/null)
if [ $? -ne 0 ] ; then
 echo "[FAIL]" # no file or access denied
 exit 1
fi

# generate file extension, so Samsung TV could use thumbnail
if [ $FILESIZE -lt 4294967296 ] ; then
 EXT=$(( $FILESIZE / 1000 ))
else
 EXT=$(( ($FILESIZE - 4294967296) / 1000))
fi


NEWFN="${FILENAME%.*}.$EXT"
if [ -f "$NEWFN" ] ; then
 echo "[SKIP]" # already exist
 exit 3
else
 ffmpegthumbnailer -s230 -cjpeg -i"$FILENAME" -o"$NEWFN" 2>/dev/null
 if [ $? -eq 0 ] ; then
  echo "[OK]"
  exit 0
 else
  echo "[FAIL]"
  exit 1
 fi
fi

Генерация сразу для кучи файлов рекурсивно (используется предыдущий):
#!/bin/bash
# gen_all.sh

# Absolute path to this script. /home/user/bin/foo.sh
SCRIPT=$(readlink -f "$0")
# Absolute path this script is in. /home/user/bin
SCRIPTPATH=`dirname "$SCRIPT"`

FAILED=0
OK=0
SKIPPED=0
count=0

# set custom delimiter for the strings
IFS=$'\n'

# process each file
for NAME in $(find $1/ -type f -regex ".*\.\(avi\|mkv\|ts\)$")
do
 "$SCRIPTPATH/gen_th.sh" "$NAME"
 ecode=$? # keep exit code here because every command will change $?
 if [ "${ecode}" -eq "1" ] ; then let "FAILED+=1"; fi
 if [ "${ecode}" -eq "0" ] ; then let "OK+=1"; fi
 if [ "${ecode}" -eq "3" ] ; then let "SKIPPED+=1"; fi
 let "count+=1"
done

# display results
echo "Total files processed: $count"
echo "Skipped: $SKIPPED"
echo "Failed: $FAILED"
echo "Images generated: $OK"

Все исходные коды проекта доступны на GitHub.

Комментировать в ВКонтакте