Вопрос или проблема
В настоящее время я создаю сценарий, чтобы ограничить длительность моих видео чуть ниже 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 секунд с помощью FFmpeg без повторного кодирования
В вашей задаче стоит цель обрезать видео до времени, чуть ниже 60 секунд, используя FFmpeg, и при этом избежать повторной кодировки. Давайте рассмотрим, как это можно сделать эффективно и корректно.
Понимание проблемы
Вы уже написали сценарий, который успешно реализует основную логику, однако, он не обрезает видео, если длительность превышает 60 секунд. Ошибка заключается в том, что вы пытаетесь обрезать видео до 60 секунд, но не учитываете количество кадров в секундах и продолжительность одного кадра.
Следующий шаг – это установить правильную длительность обрезки, которая будет равна 60 секунд минус длительность одного кадра. Так как вы хотите обработать видео с помощью параметра -c copy
, это делает процесс более быстрым и менее ресурсоемким, так как не происходит повторного кодирования.
Оптимизированный скрипт
Вот исправленный и оптимизированный скрипт на 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 }')
# Проверяем, нужно ли обрезать
if (( $(echo "$duration < 60" | bc -l) )); then
echo "Video is less than 60 seconds, no action needed."
exit 0
fi
echo "Video duration: $duration seconds"
# Получаем частоту кадров
frame_rate=$(ffmpeg -i "$input_video" 2>&1 | grep -oP '(?<=, )\d+(\.\d+)? fps' | grep -oP '\d+(\.\d+)?')
echo "Frame rate: $frame_rate fps"
# Вычисляем длительность одного кадра
frame_duration=$(echo "scale=6; 1 / $frame_rate" | bc)
# Обрезаем видео до 60 - длительность одного кадра
new_duration=$(echo "scale=6; 60 - $frame_duration" | bc)
# Обрезаем видео
ffmpeg -i "$input_video" -ss 00:00:00 -to "$new_duration" -c copy "$output_video"
# Проверяем новую продолжительность
final_duration=$(ffmpeg -i "$output_video" 2>&1 | grep -oP '(?<=Duration: )[^,]+' | awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }')
echo "New trimmed video duration: $final_duration seconds"
# Заменяем исходное видео на новое
mv "$output_video" "$input_video"
echo "Final video saved, overwritten to: $input_video"
Пояснение ключевых моментов
-
Получение информации о видео: Мы извлекаем длительность видео и частоту кадров, что является основой для вычисления длины одного кадра. Это важно, потому что именно эта информация поможет обрезать до нужной длительности.
-
Обрезка видео: Используется команда
ffmpeg
, чтобы обрезать видео до 59.99 секунд, удаляя один кадр от полной 60-секундной длинны. -
Проверка результатов: После обрезки мы проверяем длительность итогового видео, чтобы убедиться, что операция выполнена корректно.
Заключение
С данным скриптом вы сможете эффективно обрезать видео до чуть менее 60 секунд, сохраняя оригинальное качество без повторной кодировки. Это решение оптимизирует процесс и минимизирует время обработки, которое особенно важно в сценариях автоматизации. Удачи в вашем проекте!