Ant
Ant

Reputation: 1143

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)

Upvotes: 3

Views: 5227

Answers (4)

LetzerWille
LetzerWille

Reputation: 5658

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])

Upvotes: 1

zhark
zhark

Reputation: 441

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

Upvotes: 3

Ant
Ant

Reputation: 1143

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?

Upvotes: 4

Biggyboiii
Biggyboiii

Reputation: 5

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.

Upvotes: 0

Related Questions