ffmpeg를 사용하여 MPEG 비디오를 10 분 단위로 분할하려면 어떻게해야합니까? 게시해야하는 경우가 종종 있습니다.

오픈 소스 또는 활발한 개발자 커뮤니티에서 대형 비디오 세그먼트를 온라인으로 게시해야하는 경우가 종종 있습니다. (미팅 동영상, 캠프 아웃, 기술 강연 …) 비디오 개발자가 아니라 개발자이기 때문에 프리미엄 Vimeo 계정에서 추가 스크래치를 없애고 싶지 않습니다. 그러면 12.5GB (1:20:00) MPEG 기술 토크 비디오를 가져 와서 비디오 공유 사이트에 쉽게 업로드 할 수 있도록 00:10:00 세그먼트로 분할하는 방법은 무엇입니까?



답변

$ ffmpeg -i source-file.foo -ss 0 -t 600 first-10-min.m4v
$ ffmpeg -i source-file.foo -ss 600 -t 600 second-10-min.m4v
$ ffmpeg -i source-file.foo -ss 1200 -t 600 third-10-min.m4v
...

이것을 반복해서 스크립트로 묶는 것은 어렵지 않습니다.

ffprobe호출 시작 출력 시간을 기준으로 반복 횟수를 계산하려고 하면 클립 시작시의 평균 비트 전송률과 클립의 파일 크기에서 추정 되는 -count_frames인수를 지정 하지 않는 한 클립의 파일 크기 로 추정 되므로 작업 속도가 상당히 느려집니다. .

알아야 할 또 다른 사항-ss 은 명령 행 에서 옵션 의 위치가 중요하다는 것 입니다. 내가 지금 가지고있는 곳은 느리지 만 정확합니다. 이 답변의 첫 번째 버전은 빠르지 만 부정확 한 대안을 제시했습니다. 링크 된 기사는 또한 매우 빠르지 만 여전히 정확한 대안을 설명합니다.

그 외에도, 각 클립마다 정확히 10 분을 자르고 싶지는 않습니다. 그것은 문장의 한가운데서, 심지어 단어까지도 잘릴 것입니다. 비디오 편집기 나 플레이어를 사용하여 10 분 간격으로 자연스러운 컷 포인트를 찾아야한다고 생각합니다.

파일이 YouTube에서 직접 사용할 수있는 형식으로되어 있다고 가정하면 세그먼트를 얻기 위해 다시 인코딩 할 필요가 없습니다. “cut ffmpeg“코덱을 사용하여 자연 컷 포인트 오프셋을 로 전달하여 인코딩되지 않은 A / V를 건드리지 않도록 지시합니다.

$ ffmpeg -i source.m4v -ss 0 -t 593.3 -c copy part1.m4v
$ ffmpeg -i source.m4v -ss 593.3 -t 551.64 -c copy part2.m4v
$ ffmpeg -i source.m4v -ss 1144.94 -t 581.25 -c copy part3.m4v
...

-c copy인수는 모든 입력 스트림 (오디오, 비디오 및 잠재적으로 자막과 같은 다른 것)을 그대로 출력으로 복사하도록 지시합니다. 간단한 A / V 프로그램의 경우 더 자세한 플래그 -c:v copy -c:a copy또는 이전 스타일 플래그와 같습니다 -vcodec copy -acodec copy. 스트림 중 하나만 복사하고 다른 스트림은 다시 인코딩하려는 경우 더 자세한 스타일을 사용합니다. 예를 들어, 몇 년 전 H.264 비디오로 비디오를 압축하지만 오디오를 비 압축 PCM 으로 남겨 두는 QuickTime 파일의 일반적인 관행이있었습니다 . 이러한 파일을 오늘 살펴본 경우 파일을 현대화 -c:v copy -c:a aac하여 오디오 스트림 만 다시 처리하여 비디오를 그대로 유지할 수 있습니다.

첫 번째 이후 이후의 모든 명령에 대한 시작 지점은 이전 명령의 시작 지점에 이전 명령의 지속 시간을 더한 것입니다.


답변

한 줄 해결책은 다음과 같습니다 .

ffmpeg -i input.mp4 -c copy -map 0 -segment_time 00:20:00 -f segment output%03d.mp4

이것이 정확한 분할을 제공하지는 않지만 사용자의 요구에 맞아야합니다. 대신에 지정된 시간 이후에 첫 번째 프레임에서 잘라 내고 segment_time위의 코드에서 20 분 후입니다.

첫 번째 청크 만 재생할 수 있음을 발견 -reset_timestamps 1하면 주석에 언급 된대로 추가 하십시오.

ffmpeg -i input.mp4 -c copy -map 0 -segment_time 00:20:00 -f segment -reset_timestamps 1 output%03d.mp4

답변

앞서 동일한 문제에 직면하고 간단한 Python 스크립트를 작성하여 (FFMpeg 사용) 그렇게하십시오. https://github.com/c0decracker/video-splitter 에서 사용 가능 하며 아래에 붙여 넣습니다.

#!/usr/bin/env python
import subprocess
import re
import math
from optparse import OptionParser
length_regexp = 'Duration: (\d{2}):(\d{2}):(\d{2})\.\d+,'
re_length = re.compile(length_regexp)
def main():
    (filename, split_length) = parse_options()
    if split_length <= 0:
        print "Split length can't be 0"
        raise SystemExit
    output = subprocess.Popen("ffmpeg -i '"+filename+"' 2>&1 | grep 'Duration'",
                              shell = True,
                              stdout = subprocess.PIPE
    ).stdout.read()
    print output
    matches = re_length.search(output)
    if matches:
        video_length = int(matches.group(1)) * 3600 + \
                       int(matches.group(2)) * 60 + \
                       int(matches.group(3))
        print "Video length in seconds: "+str(video_length)
    else:
        print "Can't determine video length."
        raise SystemExit
    split_count = int(math.ceil(video_length/float(split_length)))
    if(split_count == 1):
        print "Video length is less then the target split length."
        raise SystemExit
    split_cmd = "ffmpeg -i '"+filename+"' -vcodec copy "
    for n in range(0, split_count):
        split_str = ""
        if n == 0:
            split_start = 0
        else:
            split_start = split_length * n
            split_str += " -ss "+str(split_start)+" -t "+str(split_length) + \
                         " '"+filename[:-4] + "-" + str(n) + "." + filename[-3:] + \
                         "'"
    print "About to run: "+split_cmd+split_str
    output = subprocess.Popen(split_cmd+split_str, shell = True, stdout =
                              subprocess.PIPE).stdout.read()
def parse_options():
    parser = OptionParser()
    parser.add_option("-f", "--file",
                      dest = "filename",
                      help = "file to split, for example sample.avi",
                      type = "string",
                      action = "store"
    )
    parser.add_option("-s", "--split-size",
                      dest = "split_size",
                      help = "split or chunk size in seconds, for example 10",
                      type = "int",
                      action = "store"
    )
    (options, args) = parser.parse_args()
    if options.filename and options.split_size:
        return (options.filename, options.split_size)
    else:
        parser.print_help()
        raise SystemExit
if __name__ == '__main__':
    try:
        main()
    except Exception, e:
        print "Exception occured running main():"
        print str(e)

답변

실제로 동일한 청크를 생성하려면 ffmpeg가 모든 청크의 첫 번째 프레임에 i- 프레임을 생성하도록 강제해야하므로 0.5 초 청크 생성 에이 명령을 사용할 수 있습니다.

ffmpeg -hide_banner  -err_detect ignore_err -i input.mp4 -r 24 -codec:v libx264  -vsync 1  -codec:a aac  -ac 2  -ar 48k  -f segment   -preset fast  -segment_format mpegts  -segment_time 0.5 -force_key_frames  "expr: gte(t, n_forced * 0.5)" out%d.mkv

답변

대체읽을 수있는 방법이 될 것이다

ffmpeg -i input.mp4 -ss 00:00:00 -to 00:10:00 -c copy output1.mp4
ffmpeg -i input.mp4 -ss 00:10:00 -to 00:20:00 -c copy output2.mp4

/**
* -i  input file
* -ss start time in seconds or in hh:mm:ss
* -to end time in seconds or in hh:mm:ss
* -c codec to use
*/

다음 은 일반적으로 사용되는 FFmpeg 명령의 소스와 목록입니다.


답변

대체 형식의 정확한 문장 부호는 -ss mm:ss.xxx입니다. 나는 직관적이지만 잘못된 mm:ss:xx것을 사용하려고 노력하면서 몇 시간 동안 고심했다 .

$ man ffmpeg | grep -C1 position

-ss position
주어진 시간 위치 (초)를 찾습니다 . “hh : mm : ss [.xxx]”구문도 지원됩니다.

여기여기를 참조 하십시오 .


답변

#!/bin/bash

if [ "X$1" == "X" ]; then
    echo "No file name for split, exiting ..."
    exit 1
fi

if [ ! -f "$1" ]; then
    echo "The file '$1' doesn't exist. exiting ..."
    exit 1
fi

duration=$(ffmpeg -i "$1" 2>&1 | grep Duration | sed 's/^.*Duration: \(.*\)\..., start.*$/\1/' | awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }')      #'
split_time=${split_time:-55}
time=0
part=1

filename=${file%%.*}
postfix=${file##*.}

while [ ${time} -le ${duration} ]; do

echo    ffmpeg -i "$1" -vcodec copy -ss ${time} -t ${split_time} "${filename}-${part}.${postfix}"
    (( part++ ))
    (( time = time + split_time ))

done