How do I integrate the FFMPEG commands with Python Script? - python

I am trying to extract the frames when the scene changes in an .mp4 video.
The package that I am using is FFMPEG.
FFMPEG predominantly works on the CLI and I am trying to integrate it with Python3.x
The command I am using in the CLI is:
ffmpeg -i {0} -vf "select=gt(scene\,0.5), scale=640:360" -vsync vfr frame%d.png
The output comes out just fine with the CLI execution.
But I want to use same command in a Python script, how do I do that and what should be the code?
Being an amateur in the field, currently grappling with this!

You could execute that command from Python via subprocess module, of course, but it would better to use library like https://github.com/kkroening/ffmpeg-python

I would recommend PyAV. it's a proper wrapper around ffmpeg's libraries.
the other mentioned packages use the "subprocess" approach, which is limited and inefficient. these libraries may be more convenient than plain ffmpeg APIs.

Thanks for the help!
This is the snippet of code I'm currently using and it gives the results as I require.
I have added a functionality for timestamp generation of the frames in addition to the frame formation using scene change detection
===========================================================================
> # FFMPEG Package call through script
> # need to change the location in the cmd post -vsync vfr to the location where the frames are to be stored
> # the location should be same as where the videos are located
============================================================================
inputf = []
for filename in os.listdir(path):
file= filename.split('.')[0] # Splits the file at the extension and stores it without .mp4 extension
input_file = path + filename
inputf.append(input_file) # Creates a list of all the files read
for x in range (0, len(inputf)):
cmd = f'ffmpeg -i {inputf[x]} -filter_complex "select=gt(scene\,0.2), scale=640:360, metadata=print:file=time_{file}.txt" -vsync vfr {path where the videos are located}\\{file}_frame%d.jpg'
os.system(cmd)
x=x+1
print("Done") # Takes time will loop over all the videos

Related

Using os.system() to convert audio files sample rate

I have started working on an NLP project, and at the start of this, I need to downsample the audio files. To do this I have found one script that can do it automatically, but though I can use it to downsample my audio I'm struggling to understand how it's working.
def convert_audio(audio_path, target_path, remove=False):
"""This function sets the audio `audio_path` to:
- 16000Hz Sampling rate
- one audio channel ( mono )
Params:
audio_path (str): the path of audio wav file you want to convert
target_path (str): target path to save your new converted wav file
remove (bool): whether to remove the old file after converting
Note that this function requires ffmpeg installed in your system."""
os.system(f"ffmpeg -i {audio_path} -ac 1 -ar 16000 {target_path}")
# os.system(f"ffmpeg -i {audio_path} -ac 1 {target_path}")
if remove:
os.remove(audio_path)
this is the code that's giving my trouble, I don't understand how the 4th line from the bottom works, I believe that is the line that resamples the audio files.
The repo this is inside of :
https://github.com/x4nth055/pythoncode-tutorials/
if anyone has information on how this is done I'd love to know, or if there are better ways to downsample audio files! Thanks
Have you ever used ffmpeg? the docs clearly show the options(maybe need audio expertise to understand)
-ac[:stream_specifier] channels (input/output,per-stream) Set the number of audio channels. For output streams it is set by default to
the number of input audio channels. For input streams this option only
makes sense for audio grabbing devices and raw demuxers and is mapped
to the corresponding demuxer options.
-ar[:stream_specifier] freq (input/output,per-stream) Set the audio sampling frequency. For output streams it is set by default to the
frequency of the corresponding input stream. For input streams this
option only makes sense for audio grabbing devices and raw demuxers
and is mapped to the corresponding demuxer options.
Explanations for os.system
Execute the command (a string) in a subshell...on Windows, the return
value is that returned by the system shell after running command. The
shell is given by the Windows environment variable COMSPEC: it is
usually cmd.exe, which returns the exit status of the command run; on
systems using a non-native shell, consult your shell documentation.
for better understanding, suggest print the command
cmd_str = f"ffmpeg -i {audio_path} -ac 1 -ar 16000 {target_path}"
print(cmd_str) # then you can copy paste to cmd/bash and run
os.system(cmd_str)

Manipulating image output from shell in python without storing external file

I am trying to store an image that is the result of ffmpeg.
Using this command, I have frame.png as an external file output:
ffmpeg -flags2 +export_mvs -i video.avi -vf 'select=gte(n\,200),codecview=mv=pf+bf+bb' -vframes 1 frame.png
I want to be able to load the frame.png directly into python, maybe using openCV but without saving it in the computer.
I thought of something like this:
cmd = "ffmpeg -flags2 +export_mvs -i video.avi -vf 'select=gte(n\,200),codecview=mv=pf+bf+bb' -vframes 1 frame.png"
img = cv.imread(sp.Popen(cmd, shell=True, stdout = sp.PIPE, stderr = sp.PIPE).communicate()[0])
But I get an error:
TypeError: bad argument type for built-in operation
Any clue how to do this? The idea is, no frame.png should be generated as a file.
You can set the output file as /dev/stdout (you might need to specify the output format with -f)
Then you redirect your output to your python script like so
ffmpeg options /dev/stdout | python your_script.py
Then you can read this question to see how you can read an image from a file object. Just replace StringIO with sys.stdin

Using ffmpeg with subprocess python

I am trying to download videos from Wistia and I managed to download them but in .bin&amp format ; I'd like to convert them to .mp4 in order to use OpenCV. For this I am calling ffmpeg with subprocess on Python but I get 1 as the value for the return code, meaning the process has failed. Any idea why, and how I can change this...?
Code is the following:
import subprocess
infile = filename #a bin&amp file
outfile = filename[:-7]+'mp4'
subprocess.run(['ffmpeg', '-i', infile, outfile],shell=True)
I get :
CompletedProcess(args=['ffmpeg', '-i', '58c63bccfcc1c150646c261caad97a58ced4b5e3.bin&amp', '58c63bccfcc1c150646c261caad97a58ced4b5e3.mp4'], returncode=1)
Also, it works in the command prompt...
Thank you for your help,
Sincerely,
Try to use Popen. I I'm using FFmpeg in python and it works well
process_ = subprocess.Popen(ffmpeg_cmd, start_new_session=True)

FFmpeg-split.py can't determine video length

First, I'm not a developer. I'm trying to split a movie into 1 minute clips usinf ffmpeg-split.py python script. I made sure FFmpeg is installed it trying a simple command and it worked like magic:
ffmpeg -i soccer.mp4 -ss 00:00:00 -codec copy -t 10 soccer1.mp4
A new video file was created in the same folder.
I saved the FFmpeg-split.py in the same dir, updated python PATH and typed the following command:
python ffmpeg-split.py -f soccer.mp4 -s 10
what I got back was:
can't determine video length
I believe it just can't find the file. I switched video files and even deleted it and got the same message.
Any ideas?
first time I've seen that name!? Because I believe you were able to run ffmpeg from the command line and execute basic python stuff I recommend following my example as it should avoid any weird directory.connection.stuff in the given file (which i ignored). "Earlier that day": Let me ignore the .py script and share as follows:
Assuming you ran
ffmpeg -i soccer.mp4 ...stuff... soccer1.mp4
from a windows.command.line...
It would be better to write
ffmpeg -t 10 -i "Z:\\full\\input\\path.mp4" -c copy "Z:\\full\\output\\path.mp4"
This says, run ffmpeg, -t=input.duration.seconds, -i=input.file.next,
"fullinpath" in quotes cause spaces etc., -c=all.codecs, copy=atlantian.magic.trick,
"fulloutpath" also to be safe, nothing else!
"Piping" through python to windows works great for this:
import subprocess as subprocess
def pegRunner(cmd): #Takes a list of strings we'll pass to windows.
command = [x for x in cmd] # peg short for mpeg, shoulda used meg.gem.gepm.gipper.translyvania.otheroptions
result = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
output, err = result.communicate()
print result.wait()
return "pegRannered"
#########
# Find the duration from properties or something. If you need to do this
# often it's more complicated. Let's say you found 4mins33secs.
############
leng = 4*60+33 # time in seconds
last_dur = int(leng%60) #remaining time after the 4 one.min.vids
if last_dur == 0: num_vids = int(leng/60)
else: num_vids = int(leng/60)+1
for i in range(num_vids):
da_command = ['ffmpeg']
da_command.append('-ss')
da_command.append(str(i*60))
da_command.append('-t')
if i != num_vids: da_command.append('60')
else: da_command.append(str(last_dur))
da_command.append('-i')
da_command.append('Z:\\full\\input\\path.mp4') #this format!
da_command.append('-c')
da_command.append('copy')
#optionally to overwrite!!!! da_command.append('-y')
da_command.append('Z:\\full\\output\\path\\filename_'+str(i)+'.mp4')
print pegRunner(da_command)
print "Finished "+str(i)+" filez."
This should handle the 1.min pieces and provide a good starting place for ffmpeg from python.

capture screenshot/frame of a video file

is there a way to capture a single frame of a video file in python?
it could also be done by command line. im using handbrakecli to convert the videos,
but i would need some screenshots of it too.
thank you
You should first check out PyFFmpeg.
PyFFmpeg is a wrapper around FFmpeg's
libavcodec, libavformat and libavutil
libraries whose main purpose is to
provide access to individual frames of
video files of various formats
(including MPEG and DIVX encoded
videos). It also provides access to
audio data.
It is also possible using ffmpeg, so call that using subprocess. A simple search will give you the command required to extract a frame from a video file. Just call that command using subprocess and that should do it.
>>> import subprocess
>>> import shlex # to split the command that follows
>>> command = 'ffmpeg -i sample.avi' # your command goes here
>>> subprocess.call(shlex.split(command))
The similar procedure applies to handbrakecli or whatever you might use. Just call the appropriate command.

Categories