ffmpeg: Cómo determinar la extensión de salida automáticamente (-c:a copy)

ffmpeg: Cómo determinar la extensión de salida automáticamente (-c:a copy)

En ffmpeg, ¿es posible determinar la extensión de audio automáticamente al extraer (copiar) la secuencia de audio de un video?

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

el audio dentro de movie.mkv puede tener cualquier formato (mpeg3, aac, flac, wav, vorbis, etc.)

Respuesta1

Hay una diferencia entre contenedores y la codificación. m4v es un contenedor, al igual que WAV, WMA, WMV, AAC, etc. Todos admiten múltiples codificaciones. Pero existen algunos patrones generales. ffprobe puede ayudar.

La extracción de audio de archivos de video usando ffmpeg se trata detalladamente aquí: https://gist.github.com/protrolium/e0dbd4bb0f1a396fcb55

En eso, hay un ejemplo de cómo podrías hacer lo que buscas, en algunos casos, usando ffprobe y 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

En la página vinculada, lo anterior parecía estar dañado por la codificación HTML. Intenté arreglarlo. Probablemente podría simplificarse para un solo archivo para:

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

Pero, si no estás usando sed y un shell bash, esto no funcionará. (es decir, no funcionará en Windows). Tampoco funcionará si la codificación del archivo de vídeo no se asigna comúnmente a una extensión de archivo. En Windows, probablemente se podría crear un powershell o vbscript que haría lo mismo.

Respuesta2

No puede detectar automáticamente la extensión, pero FFMPEG puede detectar automáticamente qué muxer usar para un contenedor de salida determinado y algunos muxer (principalmente para audio y subtítulos) pueden procesar solo transmisiones de un tipo específico (códec).

Además, FFMPEG intenta seleccionar la "mejor" secuencia (normalmente la más adecuada), si no la especifica lo suficiente. Y si no permite la recodificación, las únicas transmisiones adecuadas son aquellas admitidas por el muxer.

Significa que si le dice a FFMPEG, por ejemplo, que guarde el archivo *.AC3sin volver a codificarlo ( -c copy), pero no especifica qué secuencia procesar usando -map, intentará usar la primera secuencia adecuada; o arrojar un error si no existe dicha secuencia. Y si usa -mapun parámetro para especificar una secuencia inadecuada, también generará un error.

Por lo tanto, puede utilizar estas funciones, por ejemplo, para extraer solo la secuencia DTS independientemente de en qué posición del archivo se encuentre:

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

O si sabe que su archivo contiene transmisiones DTS, AC3 y AAC pero no sabe en qué orden:

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

Esto creará todos los archivos mencionados, pero solo aquellos que coincidan con una secuencia adecuada en el archivo de entrada contendrán la secuencia. Entonces, después de esto, solo necesita eliminar los archivos vacíos y usar lo que queda.


En el comando de Windows (por lotes), puede verificar ERRORLEVEL(que es 0exitoso o 1fallido) y conservar solo los archivos que se extrajeron exitosamente:

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

Sin embargo, tenga en cuenta que para muxers específicos puede haber algunas limitaciones:

  • MP3 muxer funciona sin -mapparámetros sólo para archivos que contienen exactamente una secuencia de mp3. Entonces, para extraer mp3 de un archivo de audio múltiple, debe usar varias llamadas -map 0:a:Xy probar cada transmisión de audio hasta encontrar la correcta.
  • AC3 muxer se usa en AC3 pero también puede procesar transmisiones MP3 y MP2, por lo que si el archivo contiene transmisiones AC3 y MP3/MP2, extraerá ambas (o la primera) ignorando la extensión.
  • Es posible que también existan otros límites, pero aún no los he encontrado.

Actualizar:Aquí hay algunas ideas sobre cómo resolver el problema con las transmisiones AC3 y MP2/MP3.

Dentro de Windows Batch puede utilizarlo %~zXpara leer el tamaño de un archivo de entrada y if A LSS Bcomparar dos números.Para comprobar Linuxeste.

Idea A) puede extraer todas las transmisiones (de audio) del archivo como out-1.ac3, out-2.ac3, etc. y luego encontrar la más grande (suponiendo que AC3 sea más grande que MP2 o MP3 de la misma longitud).

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

Y el keep_largerlote será:

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

Ahora el archivo más grande se guarda como out.ac3

Idea 2) El programa LAME puede aceptar audio WAVE y MPEG como entrada (y convertirlos a MP3), pero fallará en AC3. Por lo tanto, puede extraer ~5 minutos de la transmisión y dejar que LAME la procese (use el parámetro -fpara un procesamiento más rápido). Si es WAVE o MPEG, el resultado será grande (1 MB+) pero si es AC3, el resultado será muy pequeño (~5 kB).

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

El keep_if_largerlote será:

if %~z2 LSS %1 del %2

Ahora, si la transmisión seleccionada era AC3, LAME no pudo convertirla a MP3 aceptablemente grande y pudimos cambiar el nombre de MP2 a AC3. De lo contrario, eliminamos los archivos MP2 y MP3 e intentamos con otra transmisión.

Respuesta3

Al encontrar las mismas necesidades, he creado el siguiente 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 está diseñado para que pueda manejar archivos que no están en el directorio actual, ya sea que se haga referencia a ellos mediante rutas completas o relativas.

No estoy muy contento porque el código es demasiado largo para una tarea tan sencilla. Si alguien puede hacerlo más conciso, de nada :)

En realidad, el código más complejo no trata sobre ffmpeg, sino sobreSplFileInfo(que tiene unhorribleAPI, como puede demostrar el script anterior).

Para un guión relacionado, le había dado el viejo y simplepathinfo()Lo intenté, pero tiene en cuenta la configuración regional e inesperadamente omitió algunos archivos, así que para mí es un no-no.

información relacionada