Search code examples
pythonsubprocessffprobe

How to check if a video has sound in Python?


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)

Solution

  • 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?