ffmpeg: 출력 확장자를 자동으로 결정하는 방법(-c:a copy)

ffmpeg: 출력 확장자를 자동으로 결정하는 방법(-c:a copy)

ffmpeg에서 비디오에서 오디오 스트림을 추출(복사)할 때 자동으로 오디오 확장자를 결정할 수 있습니까?

ffmpeg -i movie.mkv -vn -c:a copy audioOnly.{?}

movie.mkv 내부의 오디오는 어떤 형식이든 가능합니다(mpeg3, aac, flac, wav, vorbis 등).

답변1

컨테이너와 인코딩에는 차이가 있습니다. m4v는 WAV, WMA, WMV, AAC 등과 마찬가지로 컨테이너입니다. 모두 다중 인코딩을 지원합니다. 그러나 몇 가지 일반적인 패턴이 있습니다. ffprobe가 도움이 될 수 있습니다.

ffmpeg를 사용하여 비디오 파일에서 오디오를 추출하는 방법은 여기에서 매우 자세히 다룹니다. https://gist.github.com/protrolium/e0dbd4bb0f1a396fcb55

여기에는 ffprobe 및 sed를 사용하여 원하는 작업을 수행할 수 있는 방법에 대한 예가 있습니다.

for file in *mp4 *avi; do ffmpeg -i "$file" -vn -acodec copy "$file".`ffprobe "$file" 2>&1 |sed -rn 's/.Audio: (...), ./\1/p'`; done

링크된 페이지에서는 위의 내용이 html 인코딩으로 인해 손상된 것으로 나타났습니다. 나는 그것을 고치려고 노력했다. 단일 파일은 다음과 같이 단순화될 수 있습니다.

ffmpeg -i "myfile.m4v" -vn -acodec copy "myfile".`ffprobe "myfile.m4v" 2>&1 |sed -rn 's/.Audio: (...), ./\1/p'`

그러나 sed와 bash 쉘을 사용하지 않으면 작동하지 않습니다. (즉, Windows에서는 작동하지 않습니다). 비디오 파일의 인코딩이 일반적으로 파일 확장자에 매핑되지 않는 경우에도 작동하지 않습니다. Windows에서는 아마도 동일한 작업을 수행하는 powershell이나 vbscript를 생각해 낼 수 있습니다.

답변2

확장자를 자동 감지할 수는 없지만 FFMPEG는 지정된 출력 컨테이너에 사용할 다중화기를 자동 감지할 수 있으며 일부 다중화기(주로 오디오 및 자막용)는 특정 유형(코덱)의 스트림만 처리할 수 있습니다.

또한 FFMPEG는 사용자가 충분히 지정하지 않은 경우 "최고"(보통 가장 적합한) 스트림을 선택하려고 시도합니다. 재인코딩을 허용하지 않는 경우 유일한 적합한 스트림은 먹서에서 지원하는 스트림입니다.

예를 들어 FFMPEG에 파일을 다시 인코딩하지 않고 저장하도록 지시했지만 *.AC3( -c copy)을 사용하여 처리할 스트림을 지정하지 않으면 -map첫 번째 적합한 스트림을 사용하려고 시도합니다. 또는 해당 스트림이 없으면 오류가 발생합니다. 그리고 -map매개변수를 사용하여 부적합한 스트림을 지정하면 오류가 발생합니다.

따라서 다음 기능을 사용하여 파일의 어느 위치에 있는지에 관계없이 DTS 스트림만 추출할 수 있습니다.

ffmpeg -i in.mkv -c copy out.dts

또는 파일에 DTS, AC3 및 AAC 스트림이 포함되어 있지만 어떤 순서인지 모르는 경우:

ffmpeg -i in.mkv -c copy -map 0:a:0 out-1.dts
ffmpeg -i in.mkv -c copy -map 0:a:0 out-1.ac3
ffmpeg -i in.mkv -c copy -map 0:a:0 out-1.aac
ffmpeg -i in.mkv -c copy -map 0:a:1 out-2.dts
ffmpeg -i in.mkv -c copy -map 0:a:1 out-2.ac3
ffmpeg -i in.mkv -c copy -map 0:a:1 out-2.aac
ffmpeg -i in.mkv -c copy -map 0:a:2 out-3.dts
ffmpeg -i in.mkv -c copy -map 0:a:2 out-3.ac3
ffmpeg -i in.mkv -c copy -map 0:a:2 out-3.aac

이렇게 하면 언급된 모든 파일이 생성되지만 입력 파일의 적합한 스트림과 일치하는 파일만 스트림을 포함합니다. 따라서 이 후에는 빈 파일을 삭제하고 남은 파일을 사용하면 됩니다.


Windows 명령(배치)에서는 대신 성공 또는 실패 ERRORLEVEL여부를 확인 하고 성공적으로 추출된 파일만 유지할 수 있습니다.01

ffmpeg -i in.mkv -c copy -map 0:a:0 out-1.dts
if [1] == [%ERRORLEVEL%] del out-1.dts
ffmpeg -i in.mkv -c copy -map 0:a:0 out-1.ac3
if [1] == [%ERRORLEVEL%] del out-1.ac3
...

그러나 특정 먹서의 경우 몇 가지 제한 사항이 있을 수 있습니다.

  • MP3 다중화기는 -map정확히 하나의 mp3 스트림을 포함하는 파일에 대해서만 매개변수 없이 작동합니다. 따라서 다중 오디오 파일에서 mp3를 추출하려면 -map 0:a:X올바른 오디오 스트림을 찾을 때까지 각 오디오 스트림을 여러 번 호출하고 시도해야 합니다.
  • AC3 muxer는 AC3에서 사용되지만 MP3 및 MP2 스트림도 처리할 수 있으므로 파일에 AC3 및 MP3/MP2 스트림이 모두 포함되어 있으면 확장자를 무시하고 둘 다(또는 첫 번째 스트림) 추출합니다.
  • 다른 제한도 있을 수 있지만 아직 발견하지 못했습니다.

업데이트:AC3 및 MP2/MP3 스트림 문제를 해결하는 방법에 대한 몇 가지 아이디어가 있습니다.

%~zXWindows Batch 내에서는 입력 파일의 크기를 읽고 if A LSS B두 숫자를 비교하는 데 사용할 수 있습니다 .Linux의 경우 확인이것.

아이디어 A) 파일에서 모든(오디오) 스트림을 out-1.ac3, out-2.ac3 등으로 추출한 다음 가장 큰 스트림을 찾을 수 있습니다(AC3가 동일한 길이의 MP2 또는 MP3보다 크다고 가정).

ffmpeg -i in.mkv -c copy -map 0:a:0 out-0.ac3
ffmpeg -i in.mkv -c copy -map 0:a:1 out-1.ac3
call keep_larger.cmd out-0.ac3 out-1.ac3 out.ac3

배치는 다음과 keep_larger같습니다.

if %~z1 LSS %~z2 goto del
del %2
ren %1 %3
goto end
:del
del %1
ren %2 %3
:end

이제 가장 큰 파일이 out.ac3으로 저장됩니다.

아이디어 2) 프로그램 LAME은 WAVE 및 MPEG 오디오를 입력으로 허용하고 이를 MP3로 변환할 수 있지만 AC3에서는 실패합니다. 따라서 최대 5분의 스트림을 추출하고 LAME가 이를 처리하도록 할 수 있습니다( -f가장 빠른 처리를 위해 param을 사용하십시오). WAVE 또는 MPEG인 경우 결과는 크지만(1MB+) AC3인 경우 결과는 ~5kB로 매우 작습니다.

ffmpeg -i in.mkv -c copy -map 0:a:0 -t "5:00" out.mp2
lame -f out.mp2 out.mp3
call keep_if_larger.cmd 500000 out.mp3
if not exist out.mp3 ren out.mp2 out.ac3
if not exist out.ac3 del out.mp2
if not exist out.ac3 del out.mp3

배치 keep_if_larger는 다음과 같습니다.

if %~z2 LSS %1 del %2

이제 선택한 스트림이 AC3인 경우 LAME는 이를 허용 가능한 크기의 MP3로 변환할 수 없으며 MP2의 이름을 AC3으로 바꿀 수 있습니다. 그렇지 않으면 MP2 및 MP3 파일을 삭제하고 다른 스트림을 시도합니다.

답변3

동일한 요구 사항에 직면하여 다음 PHP 스크립트를 작성했습니다.

isset($argv[1]) || exit('You have to specify a file.');


$file = new SplFileInfo($argv[1]);

$file->isFile() || exit('File not found.');


$input = '"' . $file->getPathname() . '"';


// full path to the containing folder
$full_dir = $file->getPathInfo()->getRealPath();

// filename only: without path, without extension
$base_name = $file->getBasename('.' . $file->getExtension());

// deduce file extension from the audio stream
$output_extension = get_output_extension($file->getPathname());

// combine all that stuff
$output = '"' . $full_dir . '/' . $base_name . '.' . $output_extension . '"';


exec('ffmpeg -i ' . $input . ' -vn -acodec copy ' . $output);


function get_output_extension($file)
{
    $file = '"' . trim($file, '"') . '"';

    $stream_info = shell_exec('ffprobe -v quiet -print_format json -show_streams -select_streams a ' . $file);

    $data = json_decode($stream_info);

    if (!isset($data->streams[0]->codec_name)) {
        exit('Audio not found - ' . $file);
    }

    $audio_format = $data->streams[0]->codec_name;

    $output_extensions = [
        'aac' => 'm4a',
        'mp3' => 'mp3',
        'opus' => 'opus',
        'vorbis' => 'ogg',
    ];

    if (!isset($output_extensions[$audio_format])) {
        exit('Audio not supported - ' . $file);
    }

    return $output_extensions[$audio_format];
}

이 스크립트는 전체 경로 또는 상대 경로로 참조되는지 여부에 관계없이 현재 디렉터리에 없는 파일을 처리할 수 있도록 설계되었습니다.

그런 간단한 작업을 하기에는 코드가 너무 길어서 정말 마음에 들지 않습니다. 누군가가 더 간결하게 만들 수 있다면 매우 환영합니다 :)

사실 가장 복잡한 코드는 ffmpeg에 관한 것이 아니라Spl파일정보(이것은끔찍한위 스크립트에서 볼 수 있듯이 API).

관련 스크립트에 대해 나는 평범하고 오래된 것을 제공했습니다pathinfo()시도해 보았지만 로케일을 인식하고 예기치 않게 일부 파일이 누락되었으므로 저에게는 안 됩니다.

관련 정보