I am new to intermediate python and I am trying to find if a downloaded video has sound, every video I download has an audio codec but I want to get the decibel of sound that audio has in that particular video.
For example, this 'FFmpeg' command line script allows me to get the full info:
ffmpeg -hide_banner -i testvideo.mp4 -af volumedetect -vn -f null - 2>&1
this yields the below result in my command prompt(windows user here with win 11)
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'testvideo.mp4':
Metadata:
major_brand : mp42
minor_version : 0
compatible_brands: mp42mp41isomavc1
creation_time : 2022-04-12T23:21:45.000000Z
Duration: 00:00:40.58, start: 0.000000, bitrate: 4104 kb/s
Stream #0:0[0x1](und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709, progressive), 1920x1080, 3846 kb/s, 29.97 fps, 29.97 tbr, 30k tbn (default)
Metadata:
creation_time : 2022-04-12T23:21:45.000000Z
handler_name : L-SMASH Video Handler
vendor_id : [0][0][0][0]
encoder : AVC Coding
Stream #0:1[0x2](und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 253 kb/s (default)
Metadata:
creation_time : 2022-04-12T23:21:45.000000Z
handler_name : L-SMASH Audio Handler
vendor_id : [0][0][0][0]
Stream mapping:
Stream #0:1 -> #0:0 (aac (native) -> pcm_s16le (native))
Press [q] to stop, [?] for help
Output #0, null, to 'pipe:':
Metadata:
major_brand : mp42
minor_version : 0
compatible_brands: mp42mp41isomavc1
encoder : Lavf59.35.100
Stream #0:0(und): Audio: pcm_s16le, 48000 Hz, stereo, s16, 1536 kb/s (default)
Metadata:
creation_time : 2022-04-12T23:21:45.000000Z
handler_name : L-SMASH Audio Handler
vendor_id : [0][0][0][0]
encoder : Lavc59.56.100 pcm_s16le
size=N/A time=00:00:40.55 bitrate=N/A speed=1.22e+03x
video:0kB audio:7608kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown
[Parsed_volumedetect_0 @ 0000026609be08c0] n_samples: 3895296
[Parsed_volumedetect_0 @ 0000026609be08c0] mean_volume: -91.0 dB
[Parsed_volumedetect_0 @ 0000026609be08c0] max_volume: -91.0 dB
[Parsed_volumedetect_0 @ 0000026609be08c0] histogram_91db: 3895296
As you can see there are 'parsed_volumedetect' values with dB which has a mean value of -91 dB which means the audio has no sound, i.e., the video has audio but there is no sound.
Now I am trying to do the same in python and I want to get just the mean volume value to be stored in a variable so that I can check if the video has any sound in it.
I have seen the subprocess codes so far but when I try to run my code in VS-Code - python 3.11:
import subprocess
result = subprocess.run(["ffmpeg", "-hide_banner", "-af", "volumedetect", "-vn", "-f", "null", "testvideo1.mp4"],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
shell=True)
print(result.stdout)
It says that:
PS C:\Users\balaj\OneDrive\Documents\Programming language\python files> c:; cd 'c:\Users\balaj\OneDrive\Documents\Programming language\python files'; & 'C:\Python311\python.exe' 'c:\Users\balaj\.vscode\extensions\ms-python.python-2022.20.2\pythonFiles\lib\python\debugpy\adapter/../..\debugpy\launcher' '51760' '--' 'c:\Users\balaj\OneDrive\Documents\Programming language\python files\devproject\sample.py'
b"Output #0, null, to 'testvideo1.mp4':\r\nOutput file #0 does not contain any stream\r\n"
Any help is much appreciated. Sorry for the long post... TIA!!!
Just a quick update: The result is the same for video files that have sound(I tested in VLC) and don't have sound.
Another update:
I have changed the subprocess.run
code to the exact same as I called in the cmd windows:
result = subprocess.run(["ffmpeg", "-hide_banner","-i","testvideo-sound.mp4", "-af", "volumedetect", "-vn", "-f", "null", "-2>&1"]
Now the result is this :
b'The handle could not be duplicated\r\nduring redirection of handle 1.\r\n'
This seems to work for me. I tried it with a few different video files (mp4) I had lying around.
import subprocess
import re
files = [
# list of file paths
]
def find_volume(s):
# regex to find volume in a string
m = re.search("(?:max_volume: )(?P<volume>.+ dB)", s, flags=re.IGNORECASE)
if not m:
return None
return m.groupdict()["volume"]
for filepath in files:
r = subprocess.run(f'ffmpeg -i "{filepath}" -filter:a volumedetect -f null /dev/null',
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
shell=True
)
# "volume" will be either None or a string;
# if it is none, we go to do to the "else" body
# instead, because None if 'falsy'.
if volume := find_volume(r.stdout.decode("utf-8")):
print(f'Video file "{filepath}" has a max volume of {volume}')
else:
print(f'Video file "{filepath}" has no audio')