ffmpeg: Как я могу ограничить длину видео чуть ниже 60 секунд без повторного кодирования?

Вопрос или проблема

В настоящее время я создаю сценарий, чтобы ограничить длительность моих видео чуть ниже 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"

Пояснение ключевых моментов

  1. Получение информации о видео: Мы извлекаем длительность видео и частоту кадров, что является основой для вычисления длины одного кадра. Это важно, потому что именно эта информация поможет обрезать до нужной длительности.

  2. Обрезка видео: Используется команда ffmpeg, чтобы обрезать видео до 59.99 секунд, удаляя один кадр от полной 60-секундной длинны.

  3. Проверка результатов: После обрезки мы проверяем длительность итогового видео, чтобы убедиться, что операция выполнена корректно.

Заключение

С данным скриптом вы сможете эффективно обрезать видео до чуть менее 60 секунд, сохраняя оригинальное качество без повторной кодировки. Это решение оптимизирует процесс и минимизирует время обработки, которое особенно важно в сценариях автоматизации. Удачи в вашем проекте!

Оцените материал
Добавить комментарий

Капча загружается...