Search code examples
pythonffmpegsubprocessaudio-video-sync

how to merge audio and video file in python using ffmpeg?


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

Solution

  • 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.