0

I want to make a python script to make a video in FFmpeg. I try a lot of solutions for example but I do not know Why I do not have permission to write. I have not any idea because ffmpeg errors are a little bit tricky

my code:

import os
import cv2

def calculate_quote_position(quote, max_length, video_width, video_height):
    if len(quote) > max_length:
        print("Quote is too long for the video.")
        return
    
    # Determine font size and alignment
    font = cv2.FONT_HERSHEY_SIMPLEX
    font_scale = 1.0
    thickness = 2
    (text_width, text_height), _ = cv2.getTextSize(quote, font, font_scale, thickness)
    
    # Calculate position for the quote
    x = int((video_width - text_width) / 2)
    y = int((video_height + text_height) / 2)
    
    return (x, y, text_width, text_height)

def create_video_from_audio_quote_picture(music_file, quote, font, font_size, font_color, background_picture, output_file, video_duration, video_width, video_height, max_length):
    quote_position = calculate_quote_position(quote, max_length, video_width, video_height)
    if not quote_position:
        print("The quote is too long for the video.")
        return
    # Create the FFmpeg command
    command = f"ffmpeg -loop 1 -t {video_duration} -i {background_picture} -i {music_file} -vf drawtext='fontfile={font}':text={quote}:fontcolor={font_color}:fontsize={font_size}:x={quote_position[0]}:y={quote_position[1]}' -shortest -c:v libx264 -c:a aac {output_file}"

    # Execute the command
    os.system(command)

music_file = "music.mp3"
quote = "This is a quote"
font = "Raleway-Bold.ttf"
font_size = 20
font_color = "white"
background_picture = "nature.png"
output_file = r"C:\Users\Lukas\Dokumenty\python_scripts\Billionare livestyle\output.mp4"
video_duration = 30
video_width = 1920
video_height = 1080
max_length = 30

create_video_from_audio_quote_picture(music_file, quote, font, font_size, font_color, background_picture, output_file, video_duration, video_width, video_height, max_length)

my error:

Input #0, image2, from 'nature.png':
  Duration: 00:00:00.04, start: 0.000000, bitrate: 569438 kb/s
  Stream #0:0: Video: mjpeg (Baseline), yuvj444p(pc, bt470bg/unknown/unknown), 1920x1080 [SAR 72:72 DAR 16:9], 25 fps, 25 tbr, 25 tbn, 25 tbc
Input #1, mp3, from 'music.mp3':
  Metadata:
    date            : 2020:12:15 01:00:00
    title           : Otnicka - Peaky Blinder (lyrics) | i am not outsider i'm a peaky blinder
    artist          : Jigzaw
    encoder         : Lavf58.29.100
  Duration: 00:02:31.78, start: 0.023021, bitrate: 128 kb/s
  Stream #1:0: Audio: mp3, 48000 Hz, stereo, fltp, 128 kb/s
    Metadata:
      encoder         : Lavf
  Stream #1:1: Video: mjpeg (Baseline), yuvj420p(pc, bt470bg/unknown/unknown), 320x240 [SAR 1:1 DAR 4:3], 90k tbr, 90k tbn, 90k tbc (attached pic)
    Metadata:
      comment         : Other
[NULL @ 000001575c0b0100] Unable to find a suitable output format for 'is'
is: Invalid argument
Barmar
  • 741,623
  • 53
  • 500
  • 612
Test000
  • 31
  • 3
  • 1
    There is a problem with how the arguments are passed to the ffmpeg command; as is, the `quote` variable is splitting into multiple arguments to the ffmpeg command, and the ffmpeg command is treating the 2nd word in `quote` (in this case, `is`) as an argument specifying the output format. – Joshua Voskamp Jan 26 '23 at 20:29

1 Answers1

0

TL;DR

The problem is most likely unintended argument parsing (splitting on whitespace) when passing text to os.system -- instead use subprocess (either call or run as appropriate) with the intended arguments, as per this answer.


This appears to be something where the variable

quote = "This is a quote"

is inadvertently being passed in the command to ffmpeg as separate arguments This, is, a, and quote, and ffmpeg is treating is as an output format specifier. Try the redefined create_video_from_audio_quote_picture function below, which escapes the quotes around the drawtext arg:

def create_video_from_audio_quote_picture(
    music_file,
    quote,
    font,
    font_size,
    font_color,
    background_picture,
    output_file,
    video_duration,
    video_width,
    video_height,
    max_length,
):
    quote_position = calculate_quote_position(
        quote, max_length, video_width, video_height
    )
    if not quote_position:
        print("The quote is too long for the video.")
        return
    # Create the FFmpeg command
    command = f"ffmpeg -loop 1 -t {video_duration}"
    command += f" -i {background_picture} -i {music_file}"
    command += f" -vf drawtext=\"fontfile={font}:text={quote}:fontcolor={font_color}:fontsize={font_size}:x={quote_position[0]}:y={quote_position[1]}\""
    #                          ^---------------------------------- quote this part with escaped double-quotes ----------------------------------------^
    command += f" -shortest -c:v libx264 -c:a aac {output_file}"

    os.system(command)

Note: the same could also happen with any of the other arguments you are passing; say you instead had

background_picture = "a nature picture.png"

as your command string is constructed, that would be parsed in os.sytem(command) as multiple arguments a, nature, and picture.png. The most correct approach here is to use subprocess and pass it arguments using subprocess.call as per this answer

Joshua Voskamp
  • 1,855
  • 1
  • 10
  • 13