2

I am trying to create a python script which merge the audio and video file in one (audio+video) file.

And i am using ffmpeg to achieve this but it is not working and i am getting a errors. while running this script here is my script.

import os
import subprocess
import time
from datetime import datetime
def merge_all():
    
 global p
 p =subprocess.Popen('ffmpeg -i temp_vid.mp4 -i temp_voice.wav -c:v copy -c:a aac -strict experimental - 
 strftime 1 ' + dt_file_name ,stdin=subprocess.PIPE,creationflags = subprocess.CREATE_NO_WINDOW)
  time.sleep(2)
  print('merging done')
  os.remove('temp_vid.mp4')
  os.remove('temp_voice.wav')
  print('file delete done')>

here is the error

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Users\kp\AppData\Local\Programs\Python\Python38\lib\tkinter\__init__.py", line 1883, in __call__
    return self.func(*args)

  File "C:\Users\kp\Desktop\test.py", line 179, in change_icon
    merge_all()

  File "C:\Users\kp\Desktop\test.py", line 104, in merge_all
 
p =subprocess.Popen('ffmpeg -i temp_vid.mp4 -i temp_voice.wav -c:v copy -c:a aac -strict experimental -strftime 1 ' + dt_file_name ,stdin=subprocess.PIPE,creationflags = subprocess.CREATE_NO_WINDOW)

  File "C:\Users\kp\AppData\Local\Programs\Python\Python38\lib\subprocess.py", line 854, in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,

  File "C:\Users\kp\AppData\Local\Programs\Python\Python38\lib\subprocess.py", line 1307, in _execute_child

hp, ht, pid, tid = _winapi.CreateProcess(executable, args,
FileNotFoundError: [WinError 2] The system cannot find the file specified
The Grand J
  • 347
  • 1
  • 5
  • 13
arvind8
  • 80
  • 11

4 Answers4

2

You have this line:

p =subprocess.Popen('ffmpeg -i temp_vid.mp4 -i temp_voice.wav -c:v copy -c:a aac -strict experimental -strftime 1 ' + dt_file_name ,stdin=subprocess.PIPE,creationflags = subprocess.CREATE_NO_WINDOW)

Instead, try this:

cmd = 'ffmpeg -i temp_vid.mp4 -i temp_voice.wav -c:v copy -c:a aac -strict experimental -strftime 1 ' + dt_file_name 
p = subprocess.Popen(cmd.split(), stdin=subprocess.PIPE, creationflags=subprocess.CREATE_NO_WINDOW)

That's assuming those options are correct, of course.

Thanks to @x00 for pointing out that a safer way to split a command would be to use shlex.split(). So:

cmd = 'ffmpeg -i temp_vid.mp4 -i temp_voice.wav -c:v copy -c:a aac -strict experimental -strftime 1 ' + dt_file_name 
p = subprocess.Popen(shlex.split(cmd), stdin=subprocess.PIPE, creationflags=subprocess.CREATE_NO_WINDOW)

The reason this is safer is because the arguments can also contain spaces and str.split() won't account for that. For example:

cmd = 'ffmpeg -i "C:\My long folder name with spaces\temp_vid.mp4"'

This gets split correctly by shlex.split(), but not by str.split().

Grismar
  • 12,597
  • 2
  • 21
  • 37
2

From the docs:

An example of passing some arguments to an external program as a sequence is:

Popen(["/usr/bin/git", "commit", "-m", "Fixes a bug."])

On POSIX, if args is a string, the string is interpreted as the name or path of the program to execute. However, this can only be done if not passing arguments to the program.

Note It may not be obvious how to break a shell command into a sequence of arguments, especially in complex cases. shlex.split() can illustrate how to determine the correct tokenization for args:

>>> import shlex, subprocess
>>> command_line = input()
/bin/vikings -input eggs.txt -output "spam spam.txt" -cmd "echo '$MONEY'"
>>> args = shlex.split(command_line)
>>> print(args)
['/bin/vikings', '-input', 'eggs.txt', '-output', 'spam spam.txt', '-cmd', "echo '$MONEY'"]
>>> p = subprocess.Popen(args) # Success!

Note in particular that options (such as -input) and arguments (such as eggs.txt) that are separated by whitespace in the shell go in separate list elements, while arguments that need quoting or backslash escaping when used in the shell (such as filenames containing spaces or the echo command shown above) are single list elements.

So the correct call to Popen should look like:

command_line = 'ffmpeg -i temp_vid.mp4 -i temp_voice.wav -c:v copy -c:a aac -strict experimental -strftime 1 ' + dt_file_name
p = subprocess.Popen(shlex.split(command_line), ...)

It looks like in your particular case you can use command_line.split() as @Grismar suggested, but I'd say it's error prone approach.

x00
  • 11,008
  • 2
  • 10
  • 34
2

Here is the python code after you have pip install ffmpeg-python in your environment:

import ffmpeg

input_video = ffmpeg.input('./test_Directory/test.webm')

input_audio = ffmpeg.input('./test_Directory/test.webm')

ffmpeg.concat(input_video, input_audio, v=1, a=1).output('./test_Directory/complete.mp4').run()

OR

video = ffmpeg.input('video.mp4')
audio = ffmpeg.input('audio.wav')
out = ffmpeg.output(video, audio, video_path, vcodec='copy', acodec='aac', strict='experimental')
out.run()

OR

cmd = 'ffmpeg -y -i Audio.wav  -r 30 -i Video.h264  -filter:a aresample=async=1 -c:a flac -c:v copy av.mkv'
subprocess.call(cmd, shell=True)                                    
print('Mixing Done')
Dharman
  • 21,838
  • 18
  • 57
  • 107
  • I would personally probably also favour using the library, but from the question it appears that OP already has a working installation of ffmpeg and was looking to use that. Also, not using the library has the advantage of the script working on any machine with Python, without extra dependencies. – Grismar Aug 04 '20 at 23:53
0

First do pip install ffmpeg-python

Then you can combine them using:

import ffmpeg

video = ffmpeg.input('directory_to_video')

audio = ffmpeg.input('directory_to_audio')

ffmpeg.concat(video, audio, v=1, a=1).output('directory_to_save_mp4').run()
olahsymbo
  • 181
  • 1
  • 9