1

Можно ли в ffmpeg автоматически определять расширение звука при извлечении (копировании) аудиопотока из видео?

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

аудио внутри movie.mkv может быть любого формата (mpeg3, aac, flac, wav, vorbis и т. д.)

2 ответа2

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 вы, вероятно, могли бы придумать powershell или vbscript, который бы делал то же самое.

1

Столкнувшись с такими же потребностями, я создал следующий 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, а о SplFileInfo (у которого ужасный API, как может продемонстрировать приведенный выше скрипт).

Для связанного скрипта я попробовал простой старый pathinfo() , но он учитывает локали и неожиданно пропустил некоторые файлы, так что для меня это нет-нет.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .