ffmpeg: Como determinar a extensão de saída automaticamente (-c: uma cópia)

1

No ffmpeg, é possível determinar a extensão de áudio automaticamente ao extrair (copiar) o fluxo de áudio de um vídeo?

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

o áudio dentro de movie.mkv pode ser qualquer formato (mpeg3, aac, flac, wav, vorbis, etc.)

    
por Azevedo 09.03.2018 / 13:08

2 respostas

1

Há uma diferença entre os contêineres e a codificação. O m4v é um contêiner, assim como WAV, WMA, WMV, AAC, etc. Todos eles suportam várias codificações. Mas existem alguns padrões gerais. ffprobe pode ajudar.

A extração de áudio de arquivos de vídeo usando o ffmpeg é abordada muito bem aqui: link

Nele, há um exemplo de como você pode fazer o que está procurando, em alguns casos, usando ffprobe e sed:

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

Na página vinculada, o texto acima parece estar corrompido por codificação html. Eu tentei consertar isso. Pode ser simplificado para um único arquivo:

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

Mas, se você não estiver usando sed e um shell bash, isso não funcionará. (ou seja, não funciona no Windows). Também não funcionará se a codificação no arquivo de vídeo não for mapeada normalmente para uma extensão de arquivo. No windows, você provavelmente poderia criar um powershell ou vbscript que faria a mesma coisa.

    
por 09.03.2018 / 17:50
1

Encontrando as mesmas necessidades, criei o seguinte script 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];
}

Esse script foi criado para manipular arquivos que não estão no diretório atual, sejam eles referenciados por caminhos completos ou relativos.

Não estou muito feliz porque o código é muito longo para uma tarefa tão simples. Se alguém pode torná-lo mais conciso, você é muito bem-vindo:)

Na verdade, o código mais complexo não é sobre o ffmpeg, mas sobre SplFileInfo (que tem um < em> horrible API, como o script acima pode demonstrar).

Para um script relacionado, eu tinha dado uma pathinfo() tentativa, mas é locale-aware e unexpectly perdeu alguns arquivos, então para mim é um não-não.

    
por 04.07.2018 / 23:27

Tags