ffmpeg를 사용하여 MPEG 비디오를 10 분 단위로 분할하려면 어떻게해야합니까?


63

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


새 태그를 수용 해 주신 @StevenD에게 특별한 감사를드립니다.
Gabriel

답변:


69
$ 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하여 오디오 스트림 만 다시 처리하여 비디오를 그대로 유지할 수 있습니다.

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


1
"각 클립마다 정확히 10 분을 자르는 것"이 ​​좋습니다.
Chris

-show_packets 매개 변수를 사용하면 더 정확하게 만들 수 있습니다.
rogerdpack 2016 년

루프에 위를 두는 방법?
kRazzy R

i이 cmd를 사용하면 오른쪽으로 분할되지만 이제 비디오 및 오디오가 SYNC에 도움이되지 않습니다
Sunil Chaudhary

@SunilChaudhary : A / V 파일 형식은 복잡하며 모든 소프트웨어가 동일한 방식을 구현하지는 않으므로 ffmpeg오디오 및 비디오 스트림 간의 동기화를 유지해야하는 정보 가 소스 파일에 없을 수 있습니다. 이 문제가 발생하면 문제를 피하는 더 복잡한 분할 방법 이 있습니다.
워렌 영

53

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

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

2
비디오 품질 을 중요 하게 생각하면 실제로 정확한 분할을 제공합니다 . 특정 시간을 기준으로 분할하는 대신 요청 된 시간에 가장 가까운 키 프레임에서 분할되므로 각각의 새 세그먼트는 항상 키 프레임으로 시작합니다.
Malvineous

3
단위는 무엇입니까? 8 초? 8 분? 8 시간?
user1133275

1
@ user1133275의 두 번째
Jon

2
Mac에서는 N 출력 비디오 청크가 생성되었지만 그중 첫 번째 만 유효하고 볼 수있는 MP4라는 것을 알았습니다. 다른 N-1 덩어리는 오디오가없는 빈 비디오 (모두 검은 색)였습니다. 작동하려면 reset_timestamps 플래그를 다음과 같이 추가해야했습니다. ffmpeg -i input.mp4 -c copy -map 0 -segment_time 8 -f segment -reset_timestamps 1 output % 03d.mp4.
jarmod

3
추가하면 -reset_timestamps 1저에게 문제가 해결 된다는 것을 발견했습니다
jlarsch

6

앞서 동일한 문제에 직면하고 간단한 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)

1
다음에 댓글로 작성해주세요. 링크 전용 답변은 여기서 마음에 들지 않으며 사이트를 광고하는 경우에도 마찬가지입니다. 소스 코드가있는 오픈 소스 프로젝트 인 경우 예외 일 수 있지만 이제는 답변 제거에 투표 하지 않기 때문에 내 검토 권한을 위험에 빠뜨릴 수 있습니다. 그리고 네, 의견을 게시 할 수는 없지만 5 개의 공감대를 모은 후에는 매우 빠릅니다.
user259412

2
안녕하세요. 사이트에 오신 것을 환영합니다. 링크 전용 답변을 게시하지 마십시오. 스크립트 자체가 큰 대답을 할 수는 있지만 이에 대한 링크는 답이 아닙니다. 답을 가리키는 푯말 입니다. 더 자세한 내용은 여기참조하십시오 . 당신이 친절하게 링크를 줬기 때문에, 나는 당신의 대답의 본문에 스크립트를 포함 시켰습니다. 당신이 그것에 반대하는 경우, 답변을 모두 삭제하십시오.
terdon

4

실제로 동일한 청크를 생성하려면 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

이것이 정답입니다. 친구를 공유해 주셔서 감사합니다!
Stephan Ahlf

4

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

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 명령의 소스와 목록입니다.


3

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

$ man ffmpeg | grep -C1 position

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

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


0
#!/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

0

ffmpeg에 내장 된 것을 사용하여 정확하게이 작업을 수행하십시오.

ffmpeg -i invid.mp4 -threads 3 -vcodec copy -f segment -segment_time 2 cam_out_h264%04d.mp4

대략 2 초 척으로 분할되고 관련 키 프레임에서 분할되며 cam_out_h2640001.mp4, cam_out_h2640002.mp4 등의 파일로 출력됩니다.


왜이 답변이 투표에 실패했는지 이해하지 못합니다. 잘 작동하는 것 같습니다.
Costin
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.