Python .avi to .mp4 - python

I need some python code for this function. I have installed ffmpeg in Ubuntu.
def convert_avi_to_mp4(avi_file.avi):
mp4_file = None
#some code to do the job
return mp4_file
Thank you

you can do something like this..
import os
def convert_avi_to_mp4(avi_file_path, output_name):
os.popen("ffmpeg -i '{input}' -ac 2 -b:v 2000k -c:a aac -c:v libx264 -b:a 160k -vprofile high -bf 0 -strict experimental -f mp4 '{output}.mp4'".format(input = avi_file_path, output = output_name))
return True

Related

How can I run an ffmpeg command in a python script?

I want to overlay a transparent video over top of an image using ffmpeg and python
I am able to do this successfully through terminal, but I cannot get ffmpeg commands to work in python. The following command produces the result that I want in terminal when I am in the directory with the files there.
ffmpeg -i head1.png -i hdmiSpitting.mov -filter_complex "[0:v][1:v] overlay=0:0" -pix_fmt yuv420p -:a copy output3.mov
In python, my code is simple:
import os
import subprocess
command = "ffmpeg -i head1.png -i hdmiSpitting.mov -filter_complex \"[0:v][1:v] overlay=0:0\" -pix_fmt yuv420p -c:a copy output3.mov"
subprocess.call(command,shell=True)
The code runs, there is no indication of an error, but no output is produced.
What am I missing here?
In Windows one line with spaces should work, but in Linux we have to pass the arguments as list.
We can build the command as a list:
command = ['ffmpeg', '-i', 'head1.png', '-i', 'hdmiSpitting.mov', '-filter_complex', '[0:v][1:v]overlay=0:0', '-pix_fmt', 'yuv420p', '-c:a', 'copy', 'output3.mov']
We may also use shlex.split:
import shlex
command = shlex.split('ffmpeg -i head1.png -i hdmiSpitting.mov -filter_complex "[0:v][1:v] overlay=0:0" -pix_fmt yuv420p -c:a copy output3.mov')
Adding -y argument:
If the output file output3.mov already exists, FFmpeg prints a message:
File 'output3.mov' already exists. Overwrite? [y/N]
And waits for the user to press y.
In some development environments we can't see the message.
Add -y for overwriting the output if already exists (without asking):
command = shlex.split('ffmpeg -y -i head1.png -i hdmiSpitting.mov -filter_complex "[0:v][1:v] overlay=0:0" -pix_fmt yuv420p -c:a copy output3.mov')
Path issues:
There are cases when ffmpeg executable is not in the execution path.
Using full path may be necessary.
Example for Windows (assuming ffmpeg.exe is in c:\FFmpeg\bin):
command = shlex.split('c:\\FFmpeg\\bin\\ffmpeg.exe -y -i head1.png -i hdmiSpitting.mov -filter_complex "[0:v][1:v] overlay=0:0" -pix_fmt yuv420p -c:a copy output3.mov')
In Linux, the default path is /usr/bin/ffmpeg.
Using shell=True is not recommended and considered "unsafe".
For details see Security Considerations.
The default is False, so we may use subprocess.call(command).
Note: subprocess.run supposes to replace subprocess.call.
See this post for details.
Creating log file by adding -report argument:
In some development environments we can't see FFmpeg messages, which are printed to the console (written to stderr).
Adding -report argument creates a log file with name like ffmpeg-20220624-114156.log.
The log file may tell us what went wrong when we can't see the console.
Example:
import subprocess
import shlex
subprocess.run(shlex.split('ffmpeg -y -i head1.png -i hdmiSpitting.mov -filter_complex "[0:v][1:v] overlay=0:0" -pix_fmt yuv420p -c:a copy output3.mov -report'))
I ended up using os.system() instead of subprocess and got the results I wanted before returning to see answers on this question. The answer from Rotem is incredibly useful and does solve my issue as well, with the added information of -y parameter.
I'll paste my entire code here, as it may be useful to someone in the future.
import os
os.chdir('/Users/Todd/Desktop/ffmpeg')
background = "01Background/Untitled_Artwork7.png"
backgear = "02Backgear/Surfboard-01.png"
head = "03Head/Goldhead_Goldshell1.png"
eye = "04eye/Keye-01.png"
outfit = "05Outfit/Summershirt-01.png"
headgear = "06Headgear/PopejoyHair_Goldshell1.png"
mouth = "hdmiSpitting.mov"
frontgear = "08Frontgear/TreePot-01.png"
# takes a list of 3 or more files and creates ffmpeg command to overlay them in order
# the first element in the list will be the lowest Z element (farthest back)
def generateCommand(files = []):
command = "ffmpeg"
i = 0
count = 0
alphabet = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o"]
for file in files:
command += " -i " + file
count += 1
command += " -filter_complex \"[0][1]overlay[a];["
while i < count-3:
command += alphabet[i] + "][" + str(i+2) + "]overlay[" + alphabet[i+1] + "];["
i += 1
command += alphabet[i] + "][" + str(i+2) + "]overlay\""
command += " -pix_fmt yuv420p -c:a copy output3.mov"
return command
# Takes two files and overlays file1 over file2
# This is a separate function because of the different command syntax for less than 3 files
def overlayTwoLayers(file1, file2):
command = "ffmpeg -i " + file1 + " -i " + file2 + " -filter_complex \"[0:v][1:v] overlay=0:0\" -pix_fmt yuv420p -c:a copy output3.mov"
os.system(command)
# Call this function with a list of files you want to be compiled
def generateImage(files):
command = generateCommand(files)
os.system(command)
files = []
files.append(background)
files.append(backgear)
files.append(head)
files.append(eye)
files.append(outfit)
files.append(headgear)
files.append(mouth)
files.append(frontgear)
generateImage(files)
print("done")
subprocess.call will get the status code but not output
may be you can use subprocess.Popen
In [1]: import subprocess
In [2]: res=subprocess.Popen("ifconfig|grep 192",shell=True,stdout=subprocess.PIPE)
...: res.stdout.read()
Out[2]: b' inet addr:192.168.45.134 Bcast:192.168.45.255 Mask:255.255.255.0\n'

Determine video bitrate Python [duplicate]

This question already has answers here:
Getting FFProbe Information With Python
(7 answers)
How to extract the bitrate and other statistics of a video file with Python
(2 answers)
Closed last year.
I have a video downloaded from Telegram and I need to determine its bitrate.
I have moviepy (pip install moviepy, not the developer version).
Also, I have ffmpeg, but I don't know how to use it in python.
Also, any other library would work for me.
import moviepy.editor as mp
video = mp.VideoFileClip('vid.mp4')
mp3 = video.audio
if mp3 is not None:
mp3.write_audiofile("vid_audio.mp3")
mp3_size = os.path.getsize("vid_audio.mp3")
vid_size = os.path.getsize('vid.mp4')
duration = video.duration
bitrate = int((((vid_size - mp3_size)/duration)/1024*8))
http://timivanov.ru/kak-uznat-bitrate-i-fps-video-ispolzuya-python-i-ffmpeg/
try this:
def get_bitrate(file):
try:
probe = ffmpeg.probe(file)
video_bitrate = next(s for s in probe['streams'] if s['codec_type'] == 'video')
bitrate = int(int(video_bitrate['bit_rate']) / 1000)
return bitrate
except Exception as er:
return er
Here is a solution using FFprobe:
Execute ffprobe (command line tool) as sub-process and read the content of stdout.
Use the argument -print_format json for getting the output in JSON format.
For getting only the bit_rate entry, add argument -show_entries stream=bit_rate.
Convert the returned string to dictionary using dict = json.loads(data).
Get the bitrate from the dictionary and convert it to int: bit_rate = int(dict['streams'][0]['bit_rate']).
The code sample creates a sample video file for testing (using FFmpeg), and get the bitrate (using FFprobe):
import subprocess as sp
import shlex
import json
input_file_name = 'test.mp4'
# Build synthetic video for testing:
################################################################################
sp.run(shlex.split(f'ffmpeg -y -f lavfi -i testsrc=size=320x240:rate=30 -f lavfi -i sine=frequency=400 -f lavfi -i sine=frequency=1000 -filter_complex amerge -vcodec libx264 -crf 17 -pix_fmt yuv420p -acodec aac -ar 22050 -t 10 {input_file_name}'))
################################################################################
# Use FFprobe for
# Execute ffprobe (to get specific stream entries), and get the output in JSON format
data = sp.run(shlex.split(f'ffprobe -v error -select_streams v:0 -show_entries stream=bit_rate -print_format json {input_file_name}'), stdout=sp.PIPE).stdout
dict = json.loads(data) # Convert data from JSON string to dictionary
bit_rate = int(dict['streams'][0]['bit_rate']) # Get the bitrate.
print(f'bit_rate = {bit_rate}')
Notes:
For some video containers like MKV, there is no bit_rate information so different solution is needed.
The code sample assumes that ffmpeg and ffprobe (command line tools) are in the execution path.
Solution for containers that has no bit_rate information (like MKV):
Based on the following post, we can sum the size of all the video packets.
We can also sum all the packets durations.
The average bitrate equals: total_size_in_bits / total_duration_in_seconds.
Here is a code sample for computing average bitrate for MKV video file:
import subprocess as sp
import shlex
import json
input_file_name = 'test.mkv'
# Build synthetic video for testing (MKV video container):
################################################################################
sp.run(shlex.split(f'ffmpeg -y -f lavfi -i testsrc=size=320x240:rate=30 -f lavfi -i sine=frequency=400 -f lavfi -i sine=frequency=1000 -filter_complex amerge -vcodec libx264 -crf 17 -pix_fmt yuv420p -acodec aac -ar 22050 -t 10 {input_file_name}'))
################################################################################
# https://superuser.com/questions/1106343/determine-video-bitrate-using-ffmpeg
# Calculating the bitrate by summing all lines except the last one, and dividing by the value in the last line.
data = sp.run(shlex.split(f'ffprobe -select_streams v:0 -show_entries packet=size,duration -of compact=p=0:nk=1 -print_format json {input_file_name}'), stdout=sp.PIPE).stdout
dict = json.loads(data) # Convert data from JSON string to dictionary
# Sum total packets size and total packets duration.
sum_packets_size = 0
sum_packets_duration = 0
for p in dict['packets']:
sum_packets_size += float(p['size']) # Sum all the packets sizes (in bytes)
sum_packets_duration += float(p['duration']) # Sum all the packets durations (in mili-seconds).
# bitrate is the total_size / total_duration (multiply by 1000 because duration is in msec units, and by 8 for converting from bytes to bits).
bit_rate = (sum_packets_size / sum_packets_duration) * 8*1000
print(f'bit_rate = {bit_rate}')

Python: Using FFMPEG to create a video using a series of images

I am fairly new to Python and this is my first time using ffmpeg (basing this code on a colleagues).
I have some code that takes a snapshot of the screen every X seconds and then creates a video out of these files however I am having issues when creating the video. This is my code for generating the video:
def create_video(screen_resolution, image_location, subfolder, count, preset, qaulity, duplicated_frame, video_path, video_filename):
video_path = path + video_path
{1}\\%d.jpg -vcodec libx264 -preset {2} -crf {3} -r {4} -pix_fmt yuv420p {5}\\{6}.mp4"
proc = subprocess.Popen("C:\\ffmpeg\\ffmpeg.exe -r 1 -f image2 -s %s \
-pattern_type sequence \
-start_number 1 \
-i %s%s\\%s%d.png \
-vcodec libx264 \
-preset %s \
-crf %s \
-r %s \
-pix_fmt yuv420p \
%s\\%s.mp4" %
(screen_resolution, image_location, subfolder, count, preset[2], qaulity, duplicated_frame, video_path, video_filename), shell = True)
and the error I am getting is:
TypeError: %d format: a number is required, not str
I have tried searching for a solution but not getting anywhere fast. I know the %d is looking for an input from the list at the end but from what I can see this is also the way to tell the FFMPEG to use all files in the image location.
Hopefully someone can help with this, apologies if this is a duplicate question but as mentioned, I have tried searching, a lot.
Don't try to manually construct shell strings. Let subprocess do that for you.
You can pass an array of command line arguments, and Popen will take care of properly escaping everything:
proc = subprocess.Popen(
[
"C:/ffmpeg/ffmpeg.exe",
"-r", "1",
"-f", "image2",
"-s", screen_resolution,
"-i", os.path.join(path, image_location, subfolder, "%d" + extension),
"-preset", preset[1],
"-crf", str(qaulity),
"-r", str(duplicated_frame),
"-pix_fmt", "yuv420p",
os.path.join(video_path, video_filename)
]
)

FFmpeg stream giving me "Alsa" errors

Of late I have been trying different ways of telling my Raspberry Pi to send video to YouTube live stream. One of the things I wanted to be able to do is boot the Pi up, and it automatically starts the live stream on its own. The advantages to this are huge (won't have to tote around keyboard/mouse to start the stream, or have to ssh into the Pi to start the stream).
Now what I did to accomplish this was to make a Python program that pipes the stream from my encoder(FFmpeg) directly to the stream. My goal was to make the program work, and then, set it to run automatically. But every time I run the file in my terminal this is my result:
Traceback (most recent call last):
File "stream.py", line 22, in <module>
stream.stdin.close()
NameError: name 'stream' is not defined
[h264 # 0x19ed450] Could not find codec parameters for stream 0 (Video: h264, none): unspecified size
Consider increasing the value for the 'analyzeduration' and 'probesize' options
pi#raspberrypi:~ $ Input #0, h264, from 'pipe:':
Duration: N/A, bitrate: N/A
Stream #0:0: Video: h264, none, 25 tbr, 1200k tbn, 50 tbc
Unknown input format: 'alsa'
Now I think I can fix I can fix some of those errors, but the biggest thing that worries me there is: the fact that "alsa" is unknown. I installed "libsasound" which is supposed to make Alsa usable, but that clearly did not help.
I am using Python 3.
This is my syntax for this program:
import subprocess
import picamera
import time
YOUTUBE="rtmp://a.rtmp.youtube.com/live2/"
KEY = ("MY PERSONAL ENCODER KEY")
stream_cmd = 'ffmpeg -f h264 -r 25 -i - -itsoffset 5.5 -fflags nobuffer -f alsa -ac 1 -i hw:1,0 -vcodec copy -acodec aac -ac 1 -ar 8000 -ab 32k -map 0:0 -map 1:0 -strict experimental -f flv ' + YOUTUBE + KEY
stream_pipe = subprocess.Popen(stream_cmd, shell=True, stdin=subprocess.PIPE)
camera = picamera.PiCamera(resolution=(640, 480), framerate=25)
try:
now = time.strftime("%Y-%m-%d-%H:%M:%S")
camera.framerate = 25
camera.vflip = True
camera.hflip = True
camera.start_recording(stream.stdin, format='h264', bitrate = 2000000)
while True:
camera.wait_recording(1)
except KeyboardInterrupt:
camera.stop_recording()
finally:
camera.close()
stream.stdin.close()
stream.wait()
print("Camera safely shut down")
print("Good bye")
Now maybe I am missing something simple here, but I don't know what. I have tried many ideas (e.g. replacing Alsa with some other input, naming the "stream" function.) I have no idea.

Python. How to pass parameters to Subprocess.Popen in a wrapper function (Linux)

I have a working BASH script with the process but I would like to migrate it to Python for flexibility, power and learning.
So In short I would like to write a function to wrap the Subprocess.open() function, but for different cases:
The main process to launch is the 'ffmpeg' programme.
The final objective it's to get to lunch lines like this:
ffmpeg -y -ss 0 -t 5 -i "MP3 sourceName" -codec:a copy "MP3 Output filename"
The params to pass are changing, following the audio process chain.
Till now i stuck in the following code:
#The 'Wrapping' function:
def proces(*args):
arg = ''.join(args)
print ("DEBUG- ffmpeg ",arg)
#these lines are for debug purpose only
try :
p = subprocess.Popen(["ffmpeg", arg], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
response, err = p.communicate()
return response, err
except:
pass
#Here the initially call in the main programme
def A_Process (files):
proces( " -y -ss 0 -t 3 -i ","\"%s\" "%(files), "%s"%(Param['ACOPY']), " \"%s\""%(fCH) ), " \"%s\""%(fCH) ] )
#-y param to indicate yes to overwrite
#-ss = param to indicate start time: decimal
#-t = param to indicate time length: decimal
#files is the MP3 sourceName as a string (pure string so i need the "")
#Param['ACOPY']= "copy" string i think?
#fCH = "Output MP3 filename" also string
#### ... more irrelevant code....
#### Launching the code gives me a print:
DEBUG- ffmpeg -y -ss 0 -t 3 -i "/media/temps/scp/fs/1.mp3" -codec:a copy "1_output.mp3"
##and stderr gives:
b'ffmpeg version 3.3.3-static http://johnvansickle.com/ffmpeg/ Copyright (c)(...etc...)...
... \n[NULL # 0x32861c0] Unable to find a suitable output
format for \' -y -ss 0 -t 3 -i "/media/temps/scp/fs/1.mp3" -codec:a copy "1_output.mp3"\'\n
-y -ss 0 -t 3 -i "/media/temps/scp/fs/1.mp3" -codec:a copy ""1_output.mp3": **Invalid argument**\n'
When i copy the DEBUG- line printed in the output and launch in Terminal, i get no errors.
I think the problem comes from the true params (-y -ss -t ) are been passed as a string but i don't know how to pass the params and its values
So someone have any ideas?

Categories

Resources