나는 이것에 대해 한동안 고민해 왔습니다. 어쩌면 여러분 중 한 분이 저를 도와주실 수 있을 것입니다.
비디오의 특정 위치에서 비디오 프레임을 추출하려고 합니다. 이를 위해 위치는 "hh:mm:ss" 형식으로 텍스트 파일에 한 줄에 하나씩 저장됩니다. bash 스크립트는 파일의 줄을 읽고 각 줄에 대해 ffmpeg를 실행합니다.
문제는 ffmpeg가 "정상적으로" 실행되는지 여부에 따라 텍스트 파일에서 읽은 입력이 변경된다는 것입니다. ffmpeg를 실행하지 않는 경우 다른 명령(예: ls)을 실행하고 잘못된 설정으로 ffmpeg를 실행하면(즉시 종료되도록) 모든 것이 들여쓰기로 진행됩니다. ffmpeg를 사용하면 타임스탬프 파일에서 읽는 것이 유효하지 않습니다.
내 추측으로는 ffmpeg가 bash 스크립트가 실행되는 환경을 어지럽히는 것 같지만 운영 체제 설계에 따르면 이것이 가능하지 않아야 합니다.
다음은 최소한의 예와 그 출력입니다.
스크립트 extract.sh
:
OUTDIR=samples
for f in test.txt; do # in reality a proper filter such as "f in video-*.txt"
BASE=${f%%.txt}
VIDEO=${BASE}.mkv
cd $OUTDIR
while read ss; do
echo "ffmpeg -i ../${VIDEO} -vsync 0 -ss ${ss} -t 00:00:01 '${BASE}-${ss}-%02d.png'"
(ffmpeg -i ../invalid.mkv -vsync 0 -ss ${ss} -t 00:00:01 "${BASE}-${ss}-%02d.png" 2>&1) >> ${BASE}.log
#(ffmpeg -i ../${VIDEO} -vsync 0 -ss ${ss} -t 00:00:01 "${BASE}-${ss}-%02d.png" 2>&1) >> ${BASE}.log
done < ../${f}
cd ..
done
타임스탬프 파일 test.txt
:
00:00:42
00:01:20
00:02:20
00:04:20
00:05:56
00:06:40
스크립트의 첫 번째 ffmpeg 명령은 잘못된 명령입니다(잘못된 비디오 파일). 활성화되면 다음에서 입력을 올바르게 읽습니다 test.txt
.
$ ./extract.sh
ffmpeg -i ../test.mkv -vsync 0 -ss 00:00:30 -t 00:00:01 'test-00:00:30-%02d.png'
ffmpeg -i ../test.mkv -vsync 0 -ss 00:00:42 -t 00:00:01 'test-00:00:42-%02d.png'
ffmpeg -i ../test.mkv -vsync 0 -ss 00:01:20 -t 00:00:01 'test-00:01:20-%02d.png'
ffmpeg -i ../test.mkv -vsync 0 -ss 00:02:20 -t 00:00:01 'test-00:02:20-%02d.png'
ffmpeg -i ../test.mkv -vsync 0 -ss 00:04:20 -t 00:00:01 'test-00:04:20-%02d.png'
ffmpeg -i ../test.mkv -vsync 0 -ss 00:05:56 -t 00:00:01 'test-00:05:56-%02d.png'
ffmpeg -i ../test.mkv -vsync 0 -ss 00:06:40 -t 00:00:01 'test-00:06:40-%02d.png'
첫 번째(잘못된) ffmpeg 명령을 비활성화하고 두 번째(올바른) 명령을 활성화한 후 읽은 타임스탬프가 test.txt
올바르지 않습니다.
$ ./extract.sh
ffmpeg -i ../test.mkv -vsync 0 -ss 00:00:30 -t 00:00:01 'test-00:00:30-%02d.png'
ffmpeg -i ../test.mkv -vsync 0 -ss -t 00:00:01 'test--%02d.png'
ffmpeg -i ../test.mkv -vsync 0 -ss 00:01:20 -t 00:00:01 'test-00:01:20-%02d.png'
ffmpeg -i ../test.mkv -vsync 0 -ss 05:56 -t 00:00:01 'test-05:56-%02d.png'
어떤 아이디어가 있나요?
답변1
변수를 사용할 때는 큰따옴표를 사용하세요. 예를 들어,
cd "$OUTDIR"
ffmpeg
:
출력 파일 이름이 마음에 들지 않습니다 . 따라서$ss
( ) 에서 해당 항목을 제거 하거나 다른 항목( )${ss//:/}
으로 변경하십시오 .${ss//:/_}
ffmpeg -i "../$VIDEO" -vsync 0 -ss "$ss" -t 00:00:01 "$BASE-${ss//:/}-%02d.png" >> "$BASE.log" 2>&1 </dev/null
../
심볼릭 링크를 통해 처리하는 경우 상위 디렉터리 구성이 중단될 수 있습니다. 따라서samples
디렉터리에서 작업하는 것보다 해당 디렉터리 로 이동하는 것이 좋습니다 .일반적으로 변수 이름을 모두 대문자로 사용하지 마십시오. 쉘 관리 변수 이름( 등)과 충돌할 수
$PATH
있습니다$SECONDS
.
함께 모아서,
#!/bin/bash
outdir=samples
for vt in video-*.txt
do
base=${vt%.txt}
video=$base.mkv
while read ss
do
echo "ffmpeg -i '$video' -vsync 0 -ss '$ss' -t 00:00:01 '$outdir/$base-${ss//:/}-%02d.png'" >&2
ffmpeg -i "$video" -vsync 0 -ss "$ss" -t 00:00:01 "$outdir/$base-${ss//:/}-%02d.png" >> "$outdir/$base.log" 2>&1
done <"$vt"
done
내 예제 파일에서는 각 샘플 포인트에 대해 50개의 PNG 파일을 생성합니다.
답변2
조금 늦을 수도 있지만 문제는 ffmpeg
표준 입력에서도 읽어서 while-read-loop에서 읽은 데이터를 소비한다는 것입니다.
이상한 타임 스탬프 혼란에 빠졌습니다. 이 문제를 해결하려면 ffmpeg
다음 두 가지 방법으로 명령의 표준 입력을 리디렉션할 수 있습니다.
옵션 1: 명령줄에서 ffmpeg 뒤에 -nostdin을 입력합니다.
예:
while read f; do ffmpeg -nostdin -i "$f" -codec copy "${f%.*}.mp4" ; done
옵션 2: ffmpeg 명령줄 끝에 < /dev/null 배치
예:
while read f; do ffmpeg -i "$f" -codec copy "${f%.*}.mp4" < /dev/null; done
여기에서 이 솔루션을 찾았습니다.