2

I would just like a True if a video has audio or False if it does not have audio.

I feel like I'm almost there using subprocess.

I get info about the video file running ffprobe and splitting the results into a list.
I've tried matching a string that does or does not have audio in the list but this is not giving me consistant results.

from subprocess import Popen, PIPE
import subprocess

b = '/path/to/mp4'
'0:1' in str(subprocess.run(['ffprobe', b], stdout=PIPE, stderr=PIPE).stderr.split()[-20])  

The line above checks if there is a second stream in the video file in the 20th from the last line. Like I said, not always consistent.
I'm just having trouble requesting or parsing what I'm getting from ffmprobe.

Here is everything returned from ffprobe instead of just the 20th from the last item.

b = '/path/to/mp4'
subprocess.run(['ffprobe', b], stdout=PIPE, stderr=PIPE).stderr.split()  

returns...

[b'ffprobe',
 b'version',
 b'4.2.4-1ubuntu0.1',
 b'Copyright',
 b'(c)',
 b'2007-2020',
 b'the',
 b'FFmpeg',
 b'developers',
 b'built',
 b'with',
 b'gcc',
 b'9',
 b'(Ubuntu',
 b'9.3.0-10ubuntu2)',
 b'configuration:',
 b'--prefix=/usr',
 b'--extra-version=1ubuntu0.1',
 b'--toolchain=hardened',
 b'--libdir=/usr/lib/x86_64-linux-gnu',
 b'--incdir=/usr/include/x86_64-linux-gnu',
 b'--arch=amd64',
 b'--enable-gpl',
 b'--disable-stripping',
 b'--enable-avresample',
 b'--disable-filter=resample',
 b'--enable-avisynth',
 b'--enable-gnutls',
 b'--enable-ladspa',
 b'--enable-libaom',
 b'--enable-libass',
 b'--enable-libbluray',
 b'--enable-libbs2b',
 b'--enable-libcaca',
 b'--enable-libcdio',
 b'--enable-libcodec2',
 b'--enable-libflite',
 b'--enable-libfontconfig',
 b'--enable-libfreetype',
 b'--enable-libfribidi',
 b'--enable-libgme',
 b'--enable-libgsm',
 b'--enable-libjack',
 b'--enable-libmp3lame',
 b'--enable-libmysofa',
 b'--enable-libopenjpeg',
 b'--enable-libopenmpt',
 b'--enable-libopus',
 b'--enable-libpulse',
 b'--enable-librsvg',
 b'--enable-librubberband',
 b'--enable-libshine',
 b'--enable-libsnappy',
 b'--enable-libsoxr',
 b'--enable-libspeex',
 b'--enable-libssh',
 b'--enable-libtheora',
 b'--enable-libtwolame',
 b'--enable-libvidstab',
 b'--enable-libvorbis',
 b'--enable-libvpx',
 b'--enable-libwavpack',
 b'--enable-libwebp',
 b'--enable-libx265',
 b'--enable-libxml2',
 b'--enable-libxvid',
 b'--enable-libzmq',
 b'--enable-libzvbi',
 b'--enable-lv2',
 b'--enable-omx',
 b'--enable-openal',
 b'--enable-opencl',
 b'--enable-opengl',
 b'--enable-sdl2',
 b'--enable-libdc1394',
 b'--enable-libdrm',
 b'--enable-libiec61883',
 b'--enable-nvenc',
 b'--enable-chromaprint',
 b'--enable-frei0r',
 b'--enable-libx264',
 b'--enable-shared',
 b'libavutil',
 b'56.',
 b'31.100',
 b'/',
 b'56.',
 b'31.100',
 b'libavcodec',
 b'58.',
 b'54.100',
 b'/',
 b'58.',
 b'54.100',
 b'libavformat',
 b'58.',
 b'29.100',
 b'/',
 b'58.',
 b'29.100',
 b'libavdevice',
 b'58.',
 b'8.100',
 b'/',
 b'58.',
 b'8.100',
 b'libavfilter',
 b'7.',
 b'57.100',
 b'/',
 b'7.',
 b'57.100',
 b'libavresample',
 b'4.',
 b'0.',
 b'0',
 b'/',
 b'4.',
 b'0.',
 b'0',
 b'libswscale',
 b'5.',
 b'5.100',
 b'/',
 b'5.',
 b'5.100',
 b'libswresample',
 b'3.',
 b'5.100',
 b'/',
 b'3.',
 b'5.100',
 b'libpostproc',
 b'55.',
 b'5.100',
 b'/',
 b'55.',
 b'5.100',
 b'Input',
 b'#0,',
 b'mov,mp4,m4a,3gp,3g2,mj2,',
 b'from',
 b"'/media/iii/Q2/tor/Reddit/Subs/unexpected/l2aifial2ir51.mp4':",
 b'Metadata:',
 b'major_brand',
 b':',
 b'isom',
 b'minor_version',
 b':',
 b'512',
 b'compatible_brands:',
 b'isomiso2avc1mp41',
 b'encoder',
 b':',
 b'Lavf58.29.100',
 b'Duration:',
 b'00:00:16.27,',
 b'start:',
 b'0.000000,',
 b'bitrate:',
 b'1341',
 b'kb/s',
 b'Stream',
 b'#0:0(und):',
 b'Video:',
 b'h264',
 b'(Main)',
 b'(avc1',
 b'/',
 b'0x31637661),',
 b'yuv420p,',
 b'384x480',
 b'[SAR',
 b'1:1',
 b'DAR',
 b'4:5],',
 b'1205',
 b'kb/s,',
 b'30',
 b'fps,',
 b'30',
 b'tbr,',
 b'12k',
 b'tbn,',
 b'60',
 b'tbc',
 b'(default)',
 b'Metadata:',
 b'handler_name',
 b':',
 b'Bento4',
 b'Video',
 b'Handler',
 b'Stream',
 b'#0:1(und):',
 b'Audio:',
 b'aac',
 b'(LC)',
 b'(mp4a',
 b'/',
 b'0x6134706D),',
 b'48000',
 b'Hz,',
 b'stereo,',
 b'fltp,',
 b'128',
 b'kb/s',
 b'(default)',
 b'Metadata:',
 b'handler_name',
 b':',
 b'Bento4',
 b'Sound',
 b'Handler']  

I have also tried modifying this function from one that returns the duration of video files to one that returns True or False for audio because this is probably a less hacky way of getting the bool. I'm a bit out of my depth trying to use these different options.

def get_length(filename):
    result = subprocess.run(["ffprobe", "-v", "error", "-show_entries",
                             "format=duration", "-of",
                             "default=noprint_wrappers=1:nokey=1", filename],
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT)
    return float(result.stdout)
Ant
  • 933
  • 2
  • 17
  • 33
  • Does this answer your question? [Using ffprobe to check audio-only files](https://stackoverflow.com/questions/32278277/using-ffprobe-to-check-audio-only-files) – mkrieger1 Oct 07 '20 at 17:14
  • Seems more to be a question of using the proper parameters for ffprobe rather than a Python problem. – mkrieger1 Oct 07 '20 at 17:16
  • @mkrieger1 Thank you, but no, not quite. This is for "Using ffprobe to check audio-only files". Also, it doesn't seem to be in for Python. It's useful and I could probably get what I want from that answer if I hacked away at it for an hour or two. – Ant Oct 07 '20 at 17:17
  • @mkrieger1 "for ffprobe rather than a Python problem" I'm just using ffprobe because that's what I tried. I know better than to ask a question on StackOverflow without posting an attempt. I need to check if a video has audio in Python. With or without ffprobe but in Python. Not commandline. – Ant Oct 07 '20 at 17:19
  • The command that is shown in the answer tells you whether the file has video *or* audio, it works in both cases. It returns a JSON data structure which can be easily parsed in Python, see https://stackoverflow.com/questions/7771011/how-to-parse-json-in-python. – mkrieger1 Oct 07 '20 at 17:20
  • When I put ['Audio'] at the end of the `subprocess.run(['ffprobe', b], stdout=PIPE, stderr=PIPE)` line I get `TypeError: 'CompletedProcess' object is not subscriptable`. The `type` this code returns is `subprocess.CompletedProcess`. This can be parsed like a JSON? It's not a JSON. If it can be parsed and you want to put it in an answer I'll give you the green check. – Ant Oct 07 '20 at 17:23
  • How to get the output of the command as a string is explained for example here: https://stackoverflow.com/questions/2502833/store-output-of-subprocess-popen-call-in-a-string If you have a string you can convert it to a dictionary as described in the other question about JSON. – mkrieger1 Oct 07 '20 at 17:27
  • Thank you for the continued attention to the OP, but after applying the solutions in both the links I get `JSONDecodeError: Expecting value: line 1 column 1 (char 0)`. The string is full of '\n's. It's a mess. – Ant Oct 07 '20 at 21:18

4 Answers4

1

I believe I found the option to sort any video file with an output of 1 (for True, has sound), or 0 (for False, does not have sound) by passing nb_streams in the format option below.
Used a combination of this answer and the docs for ffmpeg to figure this out.

def has_audio(filename):
    result = subprocess.run(["ffprobe", "-v", "error", "-show_entries",
                             "format=nb_streams", "-of",
                             "default=noprint_wrappers=1:nokey=1", filename],
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT)
    return (int(result.stdout) -1)

The code actually returns the number of streams. 2 for two streams, video and audio, or 1 for just video. I subtracted a one because I want the bool answer.
This should probably not be used to sort audio only files. Though I wonder if 1 for just video and 2 for audio and video is always the case for known video files. Can a video file have 3 or more streams?

Ant
  • 933
  • 2
  • 17
  • 33
  • yes a video file can have 3+ streams (e.g. audio tracks in multiple languages). Thanks for the helpful Python wrapper breakdown! – zhark May 06 '21 at 15:37
1

Building on previous answers, this will check each stream to see if at least one has an "audio" type codec. Please note that an audio stream may be present but silent, in which case this will still return True.

from subprocess import check_output
import json

def has_audio_streams(file_path):
  command = ['ffprobe', '-show_streams',
           '-print_format', 'json', file_path]
  output = check_output(command)
  parsed = json.loads(output)
  streams = parsed['streams']
  audio_streams = list(filter((lambda x: x['codec_type'] == 'audio'), streams))
  return len(audio_streams) > 0
zhark
  • 391
  • 3
  • 8
0

The answer isn't too hard.

MAC

pip3 install ffprobe

WINDOWS

pip install ffprobe

LINUX

pip install ffprobe

USAGE

ffprobe -show_streams -print_format json input.mov should work.

  • Thank you. But what about in Python? I tried to adapt this into subprocess like so `subprocess.run(["ffprobe", "-show_streams", "-print_format", "json", b], stdout=PIPE, stderr=PIPE)` but Python says the type is still `subprocess.CompletedProcess`. – Ant Oct 07 '20 at 21:00
0

One can use pymediainfo module for this.

def has_audio(p):
    from pymediainfo import MediaInfo
    fileInfo = MediaInfo.parse(p)
    return any([track.track_type == 'Audio' for track in fileInfo.tracks])
LetzerWille
  • 5,355
  • 4
  • 23
  • 26