4

In ffmpeg, is it possible to determine the audio extension automatically when extracting (copying) the audio stream form a video?

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

the audio inside movie.mkv may be any format (mpeg3, aac, flac, wav, vorbis, etc..)

| improve this question | | | | |
  • 1
    Not possible... – Gyan Mar 9 '18 at 13:20
  • -ac is the audio channels option. If you try to use that to set stream copy mode you'll get an error: Expected number for ac but found: copy. Instead use -c:a \ -codec:a \ -acodec. – llogan Mar 9 '18 at 17:51
  • Thanks, I fixed it. c:a – Azevedo Mar 9 '18 at 20:49
  • As a workaround, you could use .mkv for the output container, as it supports aac, opus (seems to work nowadays), etc. Wouldn't really recommend this, though. – Gras Double Jul 3 '18 at 13:51
2

There's a difference between containers and the encoding. m4v is a container, as is WAV, WMA, WMV, AAC, etc. They all support multiple encodings. But, there are some general patterns. ffprobe can help.

Extracting audio from video files using ffmpeg is covered very thoroughly here: https://gist.github.com/protrolium/e0dbd4bb0f1a396fcb55

In that, there is an example of how you could do what you are seeking, in some cases, using ffprobe and 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

In the linked page, the above appeared to be corrupted by html encoding. I've attempted to fix it. It could likely be simplified for a single file to:

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

But, if you aren't using sed and a bash shell, then this won't work. (ie. won't work on windows). It also won't work if the encoding in the video file doesn't map commonly to a file extension. In windows, you could probably come up with a powershell or vbscript that would do the same thing.

| improve this answer | | | | |
  • 2
    No need for sed: ffprobe -loglevel error -select_streams a:0 -show_entries stream=codec_name -of csv=p=0 input.foo. But, as you mentioned, this is a fragile way to do this as the codec name won't always equate to a usable file extension. – llogan Mar 9 '18 at 18:03
  • I like that better, too. Still I don't know of a way to use the output as part of another command without a unix shell of some kind. – Garr Godfrey Mar 9 '18 at 18:40
  • Finally the answer I was searching for. But having to use such code is just insane! – Gras Double Jul 3 '18 at 13:40
1

Encountering the same needs, I have crafted the following PHP script:

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

This script is designed so that it can handle files that are not in the current directory, whether they are referenced by full or relative paths.

I'm not really happy as the code is too long for such a simple task. If someone can make it more concise, you are very welcome :)

Actually, the most complex code is not about ffmpeg, but about SplFileInfo (which has an horrible API, as the above script may demonstrate).

For a related script I had given the plain old pathinfo() a try, but it is locale-aware and it unexpectly missed some files, so for me it's a no-no.

| improve this answer | | | | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.