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

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

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 do movie.mkv pode ser de qualquer formato (mpeg3, aac, flac, wav, vorbis, etc.)

Responder1

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

A extração de áudio de arquivos de vídeo usando ffmpeg é abordada detalhadamente aqui: https://gist.github.com/protrolium/e0dbd4bb0f1a396fcb55

Nele, há um exemplo de como você poderia fazer o que procura, 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: (...), ./\1/p'`; done

Na página vinculada, o texto acima parecia estar corrompido pela codificação HTML. Eu tentei consertar isso. Provavelmente poderia ser simplificado para um único arquivo para:

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

Responder2

Você não pode detectar automaticamente a extensão, mas o FFMPEG é capaz de detectar automaticamente qual muxer usar para um determinado contêiner de saída e alguns muxers (principalmente para áudio e legendas) podem processar apenas fluxos de um tipo específico (codec).

Além disso, o FFMPEG tenta selecionar o fluxo "melhor" (geralmente o mais adequado), se você não o especificar o suficiente. E se você não permitir a recodificação, os únicos fluxos adequados serão aqueles suportados pelo muxer.

Isso significa que se você disser ao FFMPEG, por exemplo, para salvar o arquivo *.AC3sem recodificá-lo ( -c copy), mas não especificar qual fluxo processar usando -map, ele tentará usar o primeiro fluxo adequado; ou lançar erro se tal fluxo não existir. E se você usar -mapo parâmetro para especificar um fluxo inadequado, isso também gerará um erro.

Portanto, você pode usar esses recursos, por exemplo, para extrair apenas o fluxo DTS, independentemente da posição do arquivo:

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

Ou se você sabe que seu arquivo contém fluxos DTS, AC3 e AAC, mas não sabe em que ordem:

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

Isso criará todos os arquivos mencionados, mas somente aqueles que corresponderem a um fluxo adequado no arquivo de entrada conterão o fluxo. Então, depois disso, você só precisa excluir os arquivos vazios e usar o que resta.


No comando do Windows (lote), você pode verificar ERRORLEVEL(que é 0sucesso ou 1falha) e manter apenas os arquivos que foram extraídos com sucesso:

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
...

No entanto, observe que para muxers específicos pode haver algumas limitações:

  • O muxer de MP3 funciona sem -mapparâmetros apenas para arquivos contendo exatamente um fluxo de mp3. Portanto, para extrair mp3 de um arquivo multi-áudio, você deve usar várias chamadas -map 0:a:Xe tentar cada fluxo de áudio até encontrar o correto.
  • O muxer AC3 é usado no AC3, mas também pode processar fluxos de MP3 e MP2, portanto, se o arquivo contiver fluxos AC3 e MP3/MP2, ele extrairá ambos (ou o primeiro) ignorando a extensão.
  • outros limites também podem existir, mas ainda não os encontrei.

Atualizar:aqui estão algumas idéias de como resolver o problema com streams AC3 e MP2/MP3.

Dentro do Windows Batch você pode usar %~zXpara ler o tamanho de um arquivo de entrada e if A LSS Bcomparar dois números.Para verificação do Linuxesse.

Ideia A) você pode extrair todos os fluxos (de áudio) do arquivo como out-1.ac3, out-2.ac3, etc. e então encontrar o maior (presumindo que AC3 seja maior que MP2 ou MP3 do mesmo comprimento).

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

E o keep_largerlote será:

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

Agora o maior arquivo é salvo como out.ac3

Idéia 2) O programa LAME pode aceitar áudio WAVE e MPEG como entrada (e convertê-los para MP3), mas falhará no AC3. Assim, você pode extrair cerca de 5 minutos do stream e deixar o LAME processá-lo (use param -fpara processamento mais rápido). Se for WAVE ou MPEG, o resultado será grande (1MB+), mas se for AC3, o resultado será muito pequeno (~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

O keep_if_largerlote será:

if %~z2 LSS %1 del %2

Agora, se o stream selecionado fosse AC3, o LAME não seria capaz de convertê-lo para um MP3 aceitavelmente grande e poderíamos renomear MP2 para AC3. Caso contrário, excluímos os arquivos MP2 e MP3 e tentamos outro stream.

Responder3

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];
}

Este script foi projetado para poder 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 puder torná-lo mais conciso, seja muito bem-vindo :)

Na verdade, o código mais complexo não é sobre ffmpeg, mas sobreSplFileInfo(que tem umhorrívelAPI, como o script acima pode demonstrar).

Para um script relacionado eu dei o velhopathinfo()uma tentativa, mas reconhece a localidade e perdeu inesperadamente alguns arquivos, então para mim é proibido.

informação relacionada