ffmpeg: So ermitteln Sie die Ausgabeerweiterung automatisch (-c:a Kopie)

ffmpeg: So ermitteln Sie die Ausgabeerweiterung automatisch (-c:a Kopie)

Ist es in ffmpeg möglich, die Audioerweiterung beim Extrahieren (Kopieren) des Audiostreams aus einem Video automatisch zu bestimmen?

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

Der Ton in movie.mkv kann jedes beliebige Format haben (mpeg3, aac, flac, wav, vorbis usw.)

Antwort1

Es gibt einen Unterschied zwischen Containern und der Kodierung. m4v ist ein Container, ebenso wie WAV, WMA, WMV, AAC usw. Sie alle unterstützen mehrere Kodierungen. Es gibt jedoch einige allgemeine Muster. ffprobe kann helfen.

Das Extrahieren von Audio aus Videodateien mit ffmpeg wird hier sehr ausführlich behandelt: https://gist.github.com/protrolium/e0dbd4bb0f1a396fcb55

Darin finden Sie ein Beispiel, wie Sie das gewünschte Ergebnis in einigen Fällen mithilfe von ffprobe und sed erzielen können:

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

Auf der verlinkten Seite schien das Obige durch die HTML-Kodierung beschädigt zu sein. Ich habe versucht, es zu reparieren. Es könnte wahrscheinlich für eine einzelne Datei vereinfacht werden auf:

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

Wenn Sie jedoch weder sed noch eine Bash-Shell verwenden, funktioniert dies nicht. (Unter Windows funktioniert es also nicht.) Es funktioniert auch nicht, wenn die Kodierung in der Videodatei nicht allgemein einer Dateierweiterung zugeordnet werden kann. Unter Windows könnten Sie wahrscheinlich eine Powershell oder ein VBScript verwenden, das dasselbe tut.

Antwort2

Sie können die Erweiterung nicht automatisch erkennen, aber FFMPEG kann automatisch erkennen, welcher Muxer für einen bestimmten Ausgabecontainer verwendet werden soll, und einige Muxer (hauptsächlich für Audio und Untertitel) können nur Streams eines bestimmten Typs (Codec) verarbeiten.

Außerdem versucht FFMPEG, den „besten“ (normalerweise am besten geeigneten) Stream auszuwählen, wenn Sie ihn nicht ausreichend angeben. Und wenn Sie keine Neukodierung zulassen, sind die einzigen geeigneten Streams diejenigen, die vom Muxer unterstützt werden.

*.AC3Das bedeutet, wenn Sie FFMPEG beispielsweise anweisen, die Datei ohne Neukodierung zu speichern ( -c copy), aber nicht angeben, welcher Stream verarbeitet werden soll -map, wird versucht, den ersten geeigneten Stream zu verwenden oder einen Fehler zu melden, wenn kein solcher Stream vorhanden ist. Und wenn Sie -mapeinen Parameter verwenden, um einen ungeeigneten Stream anzugeben, wird ebenfalls ein Fehler gemeldet.

So können Sie diese Funktionen beispielsweise nutzen, um nur den DTS-Stream zu extrahieren, unabhängig davon, an welcher Position in der Datei er sich befindet:

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

Oder wenn Sie wissen, dass Ihre Datei DTS-, AC3- und AAC-Streams enthält, aber nicht wissen, in welcher Reihenfolge:

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

Dadurch werden alle genannten Dateien erstellt, aber nur diejenigen, die einem geeigneten Stream in der Eingabedatei entsprechen, enthalten den Stream. Danach müssen Sie nur noch die leeren Dateien löschen und die verbleibenden verwenden.


Im Windows-Befehl (Batch) können Sie stattdessen dies überprüfen ERRORLEVEL(entweder 0auf Erfolg oder 1auf Fehler) und nur die Dateien behalten, die erfolgreich extrahiert wurden:

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

Beachten Sie jedoch, dass für bestimmte Multiplexer einige Einschränkungen gelten können:

  • Der MP3-Muxer funktioniert ohne -mapParameter nur für Dateien, die genau einen MP3-Stream enthalten. Um MP3 aus einer Datei mit mehreren Audiodaten zu extrahieren, müssen Sie also mehrere Aufrufe verwenden -map 0:a:Xund jeden Audiostream ausprobieren, bis Sie den richtigen gefunden haben.
  • Der AC3-Multiplexer wird bei AC3 verwendet, kann jedoch auch MP3- und MP2-Streams verarbeiten. Wenn die Datei also sowohl AC3- als auch MP3/MP2-Streams enthält, werden beide (oder der erste) extrahiert und die Erweiterung ignoriert.
  • Möglicherweise gibt es noch andere Grenzen, aber ich habe sie noch nicht gefunden.

Aktualisieren:Hier sind einige Ideen, wie Sie das Problem mit AC3- und MP2/MP3-Streams lösen können.

In Windows Batch können Sie %~zXdie Größe einer Eingabedatei lesen und if A LSS Bzwei Zahlen vergleichen.Für Linux prüfenDas.

Idee A) Sie können alle (Audio-)Streams aus der Datei als out-1.ac3, out-2.ac3 usw. extrahieren und dann den größten suchen (vorausgesetzt, AC3 ist größer als MP2 oder MP3 gleicher Länge).

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

Und die keep_largerCharge wird sein:

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

Jetzt wird die größte Datei als out.ac3 gespeichert

Idee 2) Das Programm LAME kann WAVE- und MPEG-Audio als Eingabe akzeptieren (und in MP3 konvertieren), schlägt jedoch bei AC3 fehl. Sie können also ca. 5 Minuten des Streams extrahieren und LAME diese verarbeiten lassen (verwenden Sie Parameter -ffür die schnellste Verarbeitung). Bei WAVE oder MPEG ist das Ergebnis groß (1 MB+), bei AC3 jedoch sehr klein (ca. 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

Die keep_if_largerCharge wird sein:

if %~z2 LSS %1 del %2

Wenn der ausgewählte Stream nun AC3 war, konnte LAME ihn nicht in ein akzeptabel großes MP3 konvertieren und wir konnten MP2 in AC3 umbenennen. Andernfalls löschen wir die MP2- und MP3-Datei und versuchen es mit einem anderen Stream.

Antwort3

Da ich auf dieselben Anforderungen gestoßen bin, habe ich das folgende PHP-Skript erstellt:

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

Dieses Skript ist so konzipiert, dass es mit Dateien umgehen kann, die sich nicht im aktuellen Verzeichnis befinden, unabhängig davon, ob auf sie durch vollständige oder relative Pfade verwiesen wird.

Ich bin nicht wirklich zufrieden, da der Code für eine so einfache Aufgabe zu lang ist. Wenn ihn jemand prägnanter machen kann, ist er herzlich willkommen :)

Tatsächlich handelt es sich beim komplexesten Code nicht um ffmpeg, sondern umSplFileInfo(mit einerschrecklichAPI, wie das obige Skript zeigen kann).

Für ein verwandtes Skript hatte ich das gute altepathinfo()ein Versuch, aber es ist lokalisierungsabhängig und hat unerwartet einige Dateien übersehen, also ist es für mich ein No-Go.

verwandte Informationen