Вопрос или проблема
В настоящее время я создаю скрипт, чтобы обрезать мои видео до чуть менее 60 секунд. Поэтому, если у меня есть видеофрагмент ровно 60 секунд, я обрезаю его ровно на 1 кадр, чтобы он оказался чуть ниже 60 секунд, например 59.99. Если у меня есть видео, превышающее 60 секунд, сначала я ограничиваю его, используя этот bash-скрипт:
#!/bin/bash
input_video="$1"
output_dir="$(dirname "$input_video")"
output_video="${output_dir}/$(basename "$input_video" .mp4)-capped-60-seconds.mp4"
duration=$(ffmpeg -i "$input_video" 2>&1 | grep -oP '(?<=Duration: )[^,]+' | awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }')
echo "Продолжительность видео: $duration секунд"
ffmpeg -i "$input_video" -ss 00:00:00 -to 00:01:00 -c copy "${output_dir}/temp_60sec.mp4"
new_duration=$(ffmpeg -i "${output_dir}/temp_60sec.mp4" 2>&1 | grep -oP '(?<=Duration: )[^,]+' | awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }')
echo "Новая продолжительность обрезанного видео: $new_duration"
Но это, похоже, не работает. Если у меня есть видео с duration = 60.09
, оно не обрезается до 60 секунд. new_duration
все еще совпадает с оригинальным 60.09. Как я могу добиться обрезки, которая всегда будет делать длину видео чуть меньше 60 секунд? Это полный скрипт, который я сейчас использую, и он работает только иногда. Это вообще возможно без повторного кодирования?
#!/bin/bash
input_video="$1"
output_dir="$(dirname "$input_video")"
output_video="${output_dir}/$(basename "$input_video" .mp4)-capped-60-seconds.mp4"
duration=$(ffmpeg -i "$input_video" 2>&1 | grep -oP '(?<=Duration: )[^,]+' | awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }')
if (( $(echo "$duration < 60" | bc -l) )); then
echo "Длительность видео меньше 60 секунд, никаких действий не требуется."
exit 0
fi
echo "Продолжительность видео: $duration секунд"
frame_rate=$(ffmpeg -i "$input_video" 2>&1 | grep -oP '(?<=, )\d+(\.\d+)? fps' | grep -oP '\d+(\.\d+)?')
echo "Частота кадров: $frame_rate fps"
frame_duration=$(echo "scale=6; 1 / $frame_rate" | bc)
echo "Длительность одного кадра: $frame_duration секунд"
ffmpeg -i "$input_video" -ss 00:00:00 -to 00:01:00 -c copy "${output_dir}/temp_60sec.mp4"
new_duration=$(echo "scale=6; 60 - $frame_duration" | bc)
echo "Новая продолжительность: $new_duration секунд"
ffmpeg -i "${output_dir}/temp_60sec.mp4" -ss 00:00:00 -to "$new_duration" -c copy "$output_video"
rm "${output_dir}/temp_60sec.mp4"
new_duration=$(ffmpeg -i "$output_video" 2>&1 | grep -oP '(?<=Duration: )[^,]+' | awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }')
echo "Новая продолжительность обрезанного видео: $new_duration"
mv $output_video $input_video
echo "Итоговое видео сохранено, перезаписано в: $input_video"
Хорошо, мне удалось решить это, используя этот скрипт. Сначала ограничите его до 60 секунд, а затем уберите ровно 1 кадр, чтобы длина видео была чуть менее 60 секунд:
#!/bin/bash
input_video="$1"
output_dir="$(dirname "$input_video")"
output_video="${output_dir}/$(basename "$input_video" .mp4)-capped-60-seconds.mp4"
# Шаг 1: Получить продолжительность видео с помощью ffprobe
duration=$(ffprobe -v 0 -of compact=p=0:nk=1 -show_entries format=duration -i "$input_video")
if (( $(echo "$duration < 60" | bc -l) )); then
echo "Длительность видео меньше 60 секунд, никаких действий не требуется."
exit 0
fi
echo "Продолжительность видео: $duration секунд"
# Шаг 2: Получите частоту кадров и переведите в десятичный формат
frame_rate=$(ffprobe -v 0 -of compact=p=0:nk=1 -select_streams v:0 -show_entries stream=r_frame_rate -i "$input_video")
frame_rate_decimal=$(echo "scale=6; $frame_rate" | bc -l)
echo "Частота кадров: $frame_rate_decimal fps"
# Шаг 3: Рассчитайте длительность одного кадра
frame_duration=$(echo "scale=6; 1 / $frame_rate_decimal" | bc)
echo "Длительность одного кадра: $frame_duration секунд"
# Шаг 4: Обрезка видео до ровно 60 секунд
ffmpeg -i "$input_video" -ss 00:00:00 -to 00:01:00 -c copy "${output_dir}/temp_60sec.mp4"
# Шаг 5: Рассчитайте новую продолжительность (60 секунд минус один кадр)
new_duration=$(echo "scale=6; 60 - $frame_duration" | bc)
echo "Новая целевая продолжительность: $new_duration секунд"
# Шаг 6: Обрезка видео до новой продолжительности (60 - 1 кадр)
ffmpeg -i "${output_dir}/temp_60sec.mp4" -ss 00:00:00 -to "$new_duration" -c copy "$output_video"
# Шаг 7: Очистка и перезапись оригинального файла
rm "${output_dir}/temp_60sec.mp4"
final_duration=$(ffprobe -v 0 -of compact=p=0:nk=1 -show_entries format=duration -i "$output_video")
echo "Новая продолжительность обрезанного видео: $final_duration секунд"
mv "$output_video" "$input_video"
echo "Итоговое видео сохранено, перезаписывая: $input_video"
Ответ или решение
Чтобы ограничить длину видео чуть ниже 60 секунд с использованием FFmpeg и без повторной кодировки, можно воспользоваться комбинацией команд для получения необходимых параметров. В этом ответе мы подробно рассмотрим подход к осуществлению данной задачи, включая оптимизацию вашего Bash-скрипта.
Описание проблемы
Ваша основная цель заключается в том, чтобы обрезать видео, которое длится 60 секунд или больше, до значения, чуть ниже 60 секунд, например, 59.99 секунд. Если видео менее 60 секунд, никаких действий предпринимать не следует.
Подход к решению
Ваш изначальный скрипт имеет несколько допущений и недостатков в реализации. Давайте выделим ключевые моменты и исправим их, чтобы добиться нужного результата.
-
Получение длительности видео: Используйте
ffprobe
для получения точной информации о длине видео. Это быстрее и надежнее, чем парсинг выводаffmpeg
. -
Определение частоты кадров: Задействуйте
ffprobe
для получения частоты кадров, что потребуется для расчета длительности одного кадра. -
Ограничение длины видео: Вам нужно обрезать видео до точно 60 секунд и затем уменьшить его длину на один кадр, чтобы получить необходимый результат.
Оптимизированный скрипт
#!/bin/bash
input_video="$1"
output_dir="$(dirname "$input_video")"
output_video="${output_dir}/$(basename "$input_video" .mp4)-capped-60-seconds.mp4"
# Шаг 1: Получить длительность видео с помощью ffprobe
duration=$(ffprobe -v 0 -of compact=p=0:nk=1 -show_entries format=duration -i "$input_video")
if (( $(echo "$duration < 60" | bc -l) )); then
echo "Длительность видео меньше 60 секунд, действия не требуются."
exit 0
fi
echo "Длительность видео: $duration секунд"
# Шаг 2: Получить частоту кадров
frame_rate=$(ffprobe -v 0 -of compact=p=0:nk=1 -select_streams v:0 -show_entries stream=r_frame_rate -i "$input_video")
frame_rate_decimal=$(echo "scale=6; $frame_rate" | bc -l)
echo "Частота кадров: $frame_rate_decimal fps"
# Шаг 3: Вычислить длительность одного кадра
frame_duration=$(echo "scale=6; 1 / $frame_rate_decimal" | bc)
echo "Длительность одного кадра: $frame_duration секунд"
# Шаг 4: Обрезать видео до 60 секунд
ffmpeg -i "$input_video" -ss 0 -to 60 -c copy "${output_dir}/temp_60sec.mp4"
# Шаг 5: Вычислить новую величину длительности (60 секунд минус один кадр)
new_duration=$(echo "scale=6; 60 - $frame_duration" | bc)
echo "Новая целевая длительность: $new_duration секунд"
# Шаг 6: Обрезать видео до новой длительности (60 - 1 кадр)
ffmpeg -i "${output_dir}/temp_60sec.mp4" -ss 0 -to "$new_duration" -c copy "$output_video"
# Шаг 7: Удаление временного файла и переименование
rm "${output_dir}/temp_60sec.mp4"
final_duration=$(ffprobe -v 0 -of compact=p=0:nk=1 -show_entries format=duration -i "$output_video")
echo "Новая длительность видео после обрезки: $final_duration секунд"
mv "$output_video" "$input_video"
echo "Финальное видео сохранено, перезапись: $input_video"
Заключение
Теперь ваш скрипт выполняет все необходимые действия, чтобы обрезать видео до чуть меньше 60 секунд без повторной кодировки. Он использует ffprobe
для получения необходимых данных, что позволяет избежать потенциальных ошибок парсинга. Убедитесь, что на целевой машине установлен FFmpeg и bc
, чтобы обеспечить корректное выполнение скрипта.
Этот оптимизированный подход поможет вам легко и быстро обрабатывать видеофайлы, сохраняя их эффективность и качество.