pythonmultithreadingfilemergemoviepy

merge mp3 and mp4 file with moviepy is slow


i use moviepy for merge mp3 and mp4 file. it's working good but the process is so slow. i use multi threads but the main process(merging) is still slow. i search and know that i use ffmpeg but i get error at first try and i cant fix it. so i came back to moviepy and is there any way to speed up this process?

my code is so simple: just read the name of my files and get them and merge mp3 file with mute mp4 files. btw i coded with threading and as i say it have not progress. (Of course, there is a possibility that I don't drink well with Multi-Thread! God knows!)

import threading
from os import walk
from moviepy.editor import VideoFileClip, AudioFileClip
from concurrent.futures import ThreadPoolExecutor


def merge_audio_and_video(name):
    print(f"Processing {name}...")
    video = VideoFileClip(name + '.mp4')
    audio = AudioFileClip(name + '.m4a')
    final_video = video.set_audio(audio)
    final_video.write_videofile(
        f'./merge/{name}.mp4', codec="libx264", audio_codec="aac"
    )


filenames = next(walk('.'), (None, None, []))[2]
mp4_files = [file for file in filenames if file.endswith('.mp4')]
mp4_files_without_extension = [file.replace('.mp4', '') for file in mp4_files]

with ThreadPoolExecutor(max_workers=1) as executor:
    futures = [executor.submit(merge_audio_and_video, name)
               for name in mp4_files_without_extension]

    for future in futures:
        future.result() 
print("All video files have been processed.")

output and it/s show the speed of process for the file 20MB:

t:  77%|█████████   | 16138/20868 [12:07<02:25, 32.52it/s, now=None]

this is all detail of mp4 file:

{
        "abr": 0,
        "acodec": "none",
        "aspect_ratio": 1.78,
        "audio_ext": "none",
        "container": "mp4_dash",
        "downloader_options": {
          "http_chunk_size": 10485760
        },
        "dynamic_range": "SDR",
        "ext": "mp4",
        "filesize": 116113997,
        "filesize_approx": 116113932,
        "format": "298 - 1280x720 (720p60)",
        "format_id": "298",
        "format_note": "720p60",
        "fps": 60,
        "has_drm": false,
        "height": 720,
        "id": "66b3bba14f6a5",
        "language_preference": -1,
        "protocol": "https",
        "quality": 8,
        "resolution": "1280x720",
        "source_preference": -1,
        "tbr": 1215.164,
        "vbr": 1215.164,
        "vcodec": "avc1.640020",
        "video_ext": "mp4",
        "width": 1280
}

and this is m4a file detail:

{      
        "abr": 129.479,
        "acodec": "mp4a.40.2",
        "asr": 44100,
        "audio_channels": 2,
        "audio_ext": "m4a",
        "container": "m4a_dash",
        "downloader_options": {
          "http_chunk_size": 10485760
        },
        "ext": "m4a",
        "filesize": 12373283,
        "filesize_approx": 12373223,
        "format": "140 - audio only (medium)",
        "format_id": "140",
        "format_note": "medium",
        "has_drm": false,
        "id": "66b3bba146875",
        "language": "en",
        "language_preference": -1,
        "protocol": "https",
        "quality": 3,
        "resolution": "audio only",
        "source_preference": -1,
        "tbr": 129.479,
        "vbr": 0,
        "vcodec": "none",
        "video_ext": "none"
}

i try the acodec from data that i get but throw error:

final_video.write_videofile(
        f'./merge/{name}.mp4', codec='libx264', audio_codec="mp4a.40.2"
    )
raise ValueError(
ValueError: The audio_codec you chose is unknown by MoviePy. You should report this. In the meantime, you can specify a temp_audiofile with the right extension in write_videofile.

edit final:

import subprocess

def merge_audio_and_video(name):
    print(f"Processing {name}...")
    command = [
        'ffmpeg', '-i', f'{name}.mp4', '-i', f'{name}.m4a', 
        '-c:v', 'copy', '-c:a', 'aac', f'./merge/{name}.mp4'
    ]
    subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)

so i fix ffmpeg error and i use it. it have good speed. but is there any way to handle this without using ffmpeg??


Solution

  • Use subprocess to run ffmpeg in the shell. If your task is to add additional audio or video track to the file then ffmpeg is the best tool to use. It will have least overheads.

    import subprocess
    
    input = 'ffmpeg -i video1.mp4 -i audio1.m4a -c copy output.mp4'
    
    quiet = False
    stream = subprocess.DEVNULL if quiet else None
    
    subprocess.call(input, 
                    stdout = stream,
                    stderr = stream,
                    shell = True)