0

Doing a python compression program for school work, can't get my head around why I am getting this error: '[NULL @ 000002486ae7b180] Unable to find a suitable output format for' followed by a file path the user selects I think is has something to do with the ' i = ffmpeg ' part but I am not entirely sure. Any help is appreciated :)

import os, ffmpeg
##import section:this part is where I import all of the modules I will use
import  tkinter
import shutil
from tkinter import filedialog
import os


def fileSelect():                                                                           #start of fileSelect function
    global startingLocation                                                                 #declares startingLocation as global variable
    global originalName                                                                     #declares originalName as global variable
    global fileType                                                                         #declares fileType as global variable
    startingLocation = filedialog.askopenfilename(initialdir="/", title="Select file",      #tkinter function that opens file explorer, lets user select file saves the file path as a variable
                    filetypes=(("video files", "*.mp4"),("images", "*.jpg*")))
    originalName = os.path.basename(startingLocation)                                       #os function that gets the actaul file name from the path string
    print (originalName)                                                                    #print statement to check if originalName has been found
    fileType = startingLocation.split('.')                                                  #splits original name where any full stop in found and saves array as variable
    fileType = fileType[-1]                                                                 #changes variable to have the str value of the final item in the array; the file type
    fileType = '.' + fileType                                                               #adds fullstop to the start of the file type so i dont have to repeatedly do it
    print (fileType)                                                                        #print statement to check file type is found correctly

def outputSelect():                                                                         #start of outputSelect function
     global outputLocation                                                                  #declares outputLocation as global variable
     outputLocation = filedialog.askdirectory(initialdir="/", title="Select folder")        #tkinter function that opens file explorer, lets the user select of folder as saves the folder path as a variable

def fileNewName():                                                                          #start of fileNewName function
    global customName                                                                       #declares customName as global variable
    customName = input("Enter the end name of your file")                                   #simple code assigning user input to the custom name vairable
    customName = customName + fileType                                                      #add the fileType onto the end of the custom name

def compress():                                                                             #start of compress function
    fileSelect()                                                                            #calls the fileSelect function
    outputSelect()                                                                          #calls the outputSelect function
    fileNewName()
    global src
    global dst                                                                           #calls the fileNewName function
    src = startingLocation                                                                  #assigns startingLocation str as src, so the shutil module is able to use it in a cleaner way
    dst = outputLocation                                                                    #assigns outputLocation dst as src, so the shutil module is able to use it in a cleaner way
    shutil.copy(src, dst)                                                                   #shutil command that copies the file from src to dst
    src = outputLocation + '/' + originalName                                               #reassigns src as the location of the file copy
    dst = outputLocation + '/' + customName                                                 #reassigns dst as the location of the file copy but with a new name
    shutil.move(src,dst)


def compress_video(video_full_path, output_file_name, target_size):
    # Reference: https://en.wikipedia.org/wiki/Bit_rate#Encoding_bit_rate
    min_audio_bitrate = 32000
    max_audio_bitrate = 256000

    probe = ffmpeg.probe(video_full_path)
    # Video duration, in s.
    duration = float(probe['format']['duration'])
    # Audio bitrate, in bps.
    audio_bitrate = float(next((s for s in probe['streams'] if s['codec_type'] == 'audio'), None)['bit_rate'])
    # Target total bitrate, in bps.
    target_total_bitrate = (target_size * 1024 * 8) / (1.073741824 * duration)

    # Target audio bitrate, in bps
    if 10 * audio_bitrate > target_total_bitrate:
        audio_bitrate = target_total_bitrate / 10
        if audio_bitrate < min_audio_bitrate < target_total_bitrate:
            audio_bitrate = min_audio_bitrate
        elif audio_bitrate > max_audio_bitrate:
            audio_bitrate = max_audio_bitrate
    # Target video bitrate, in bps.
    video_bitrate = target_total_bitrate - audio_bitrate

    i = ffmpeg.input(video_full_path)
    ffmpeg.output(i, os.devnull,
                  **{'c:v': 'libx264', 'b:v': video_bitrate, 'pass': 1, 'f': 'mp4'}
                  ).overwrite_output().run()
    ffmpeg.output(i, output_file_name,
                  **{'c:v': 'libx264', 'b:v': video_bitrate, 'pass': 2, 'c:a': 'aac', 'b:a': audio_bitrate}
                  ).overwrite_output().run()

compress()
compress_video(dst, outputLocation, 3 * 1000)

1 Answers1

0

As @Rotem says, the input argument for the first pass should be string. Try '/dev/null' for Linux/Mac or 'NUL' for Windows. Also the output format needs to be 'null'. See the FFmpeg Wiki entry linked below.

FFmpeg 2-pass h264 encoding reference

kesh
  • 4,515
  • 2
  • 12
  • 20
  • The wiki says I need to have the first pass output format as: -pass 1 -an -f null /dev/null && \ but as I'm using python unsure as of the correct syntax to use to write this, as typing 'null NUL' does not work and gives me Requested output format 'null NUL' is not a suitable output format NUL: Invalid argument – Alex.Foster Jan 26 '22 at 22:29
  • Okay I jigged it around more and I just now get 'permission denied' . This seems like a very bad thing – Alex.Foster Jan 26 '22 at 22:41
  • Could post how exactly you are calling the subprocess each time (should be calling twice)? Just so you know `&& \ ` should not be part of your call. Also, you may get permission denied if you have no write privilege to Python's current directory (`os.getcwd()`). If that's the case, look into `-passlogfile` FFmpeg option. – kesh Jan 26 '22 at 22:59
  • https://imgur.com/a/GCF272J the rest of the code is currently unchanged. I'm pretty sure I have write permission to python's directory, and idk what -passlogfile FFmpeg option is referring to. Sorry, I genuinely might need you to dumb it down a bit – Alex.Foster Jan 26 '22 at 23:28
  • Is there way to print how ffmpeg is invoked using subprocess? Also, what's the context of "permission denied"? – kesh Jan 26 '22 at 23:31
  • I can give you the stuff it outputs to the terminal: https://pastebin.com/FMHn7Mbn – Alex.Foster Jan 26 '22 at 23:35
  • ok update again no i am getting no errors at all BUT the file is just exactly the same size with out being compressed – Alex.Foster Jan 27 '22 at 00:12
  • you likely need to add `-y` flag on the pass 2 to overwrite the existing file. That's what the "permission denied" error is about. – kesh Jan 27 '22 at 00:43
  • another thing is that if your target bitrate is too high, it won't make any changes to the file size – kesh Jan 27 '22 at 00:47
  • My target bitrate is about a 10th of the original file bit rate, so im not sure whats causing the issue. – Alex.Foster Jan 27 '22 at 01:08
  • The fact I get back info like this about the output file makes me think the file is just being put somewhere that I don't know, because the file size and bitrate is lower than the original file https://imgur.com/a/tzozEMr – Alex.Foster Jan 27 '22 at 01:10
  • ok now its weirder. When i run it outside of visual studios, it works and just outputs the file in the same directory as the program – Alex.Foster Jan 27 '22 at 01:27
  • on the bit rate, check the log to see if you have something like ``` [libx264 @ 000001f7b045a5c0] Error: 2pass curve failed to converge [libx264 @ 000001f7b045a5c0] target: 2000.00 kbit/s, expected: 847.66 kbit/s, avg QP: 0.0409 [libx264 @ 000001f7b045a5c0] try reducing target bitrate ``` – kesh Jan 27 '22 at 01:48
  • I think there might have been, but I've botched the code and it works now. Its a bit rough but it will get the job done. Thank you for your help though, i can show you my finished code if you would like. – Alex.Foster Jan 27 '22 at 01:58
  • Glad you got it working! – kesh Jan 27 '22 at 02:34