Zulko / moviepy

Video editing with Python
https://zulko.github.io/moviepy/
MIT License
12.07k stars 1.51k forks source link

AttributeError: 'NoneType' object has no attribute 'get_frame' #2129

Open kaushal101 opened 3 months ago

kaushal101 commented 3 months ago

Hi,

I have a few video files in a folder. I'm picking up multiple files, fetching several seconds (3 or 4 or 5) of clip, using subclip method. Then I'm gathering these clips using append. I'm then adding some background music and finalising the video.

This was working fine on 6th Jan 2024 (that's when I last used my script). But this has stopped working. I've provided relevant method codes and full error stack below.

I read elsewhere that closing clips might be an issue, so I've commented clip.close. But in Jan my script was working fine with clip.close

Below line started failing in the first place, so I thought I'll ignore the audio from individual clips and just work with background music, but even that isn't working.

background_music = CompositeAudioClip([final_clip.audio, background_music])

import os
from moviepy.editor import VideoFileClip, concatenate_videoclips, AudioFileClip, CompositeVideoClip, ImageClip, CompositeAudioClip
from datetime import datetime
import random
import cv2
from pymediainfo import MediaInfo
import pandas as pd
import imageio_ffmpeg as ffmpeg

def create_video_clip(video_path, start_time, end_time):
    with VideoFileClip(video_path) as clip:
        return clip.subclip(start_time, end_time).volumex(0.1)

def gen_clips(selected_videos, duration):
    clips = []
    for video_path in selected_videos:
        print("processing file: " + video_path)

        #clip = VideoFileClip(video_path)
        try:
            original_duration = get_video_duration(video_path)
            if original_duration > duration:
                start_time = random.uniform(0, original_duration - duration)
                end_time = start_time + duration    # End time in seconds
                clip = create_video_clip(video_path, start_time, end_time)
                #if not clip.audio == None:
                clips.append(clip)
                #clip.close()
        except Exception as e:
            print(f"Error processing file {video_path}: {e}")
    return clips

def main():
    excel_file_path = r"C:\Kaushal\video projects\videogen.xlsx"
    # Read Excel file into a pandas DataFrame
    df = pd.read_excel(excel_file_path, sheet_name="Sheet1")
    row = 0
    num_vids = df.shape[0]
    while row<=num_vids-1:
        if not pd.isna(df.iloc[row,0]):
            folder_path = df.iloc[row,0]
            if not pd.isna(df.iloc[row,1]):
                start_date = df.iloc[row,1]
            if not pd.isna(df.iloc[row,2]):
                end_date = df.iloc[row,2]
            if not pd.isna(df.iloc[row,3]):
                markers = df.iloc[row,3]
            #folder_path = r"E:\To be backed up\2019.01 AUS and previous"
            #start_date = datetime(2018, 12, 23,0,1)  # Set your start date
            #end_date = datetime(2018, 12, 23,23,59)   # Set your end date
            output_video_path = r"C:\Kaushal\video projects"
            #markers = 1

            # select background music
            background_music, duration, bgm_name = choose_bgm(markers)

            # Select videos based on modified date
            selected_videos = select_videos_by_date(folder_path, start_date, end_date)

            # Define the clips for each selected video
            clips = gen_clips(selected_videos, duration)

            # Concatenate selected clips
            final_clip = concatenate_videoclips(clips, method="compose")
            #final_clip = concatenate_videoclips(clips, method="chain")

            # ensure background music doesn't go beyond video
            #if final_clip.audio != None:
            #background_music = CompositeAudioClip([final_clip.audio, background_music])
            background_music = background_music.set_end(final_clip.duration)

            # Apply a fade-out at the end of the audio
            background_music = background_music.audio_fadeout(5)  # Adjust the duration of the fade-out as needed

            # Add background music (replace "background_music.mp3" with your music file)
            final_clip = final_clip.set_audio(background_music)

            # add logo
            final_clip = add_logo(final_clip)

            #compute output filename
            output_video_path = get_targetfile(folder_path,start_date,bgm_name, output_video_path, row)    #os.path.join(output_video_path, )
            # Write the final video file
            final_clip.write_videofile(output_video_path, codec="libx264", audio_codec="mp3")

        row += 1

Expected Behavior

A video file should get procuded

Actual Behavior

An error is thrown:

processing file: E:\To be backed up\2019.02 Oxford\PANA5378.MP4 processing file: E:\To be backed up\2019.02 Oxford\PANA5379.MP4 Moviepy - Building video C:\Kaushal\video projects\2019.02 Oxford 2.2.2019 Bet on it 0.mp4. MoviePy - Writing audio in 2019.02 Oxford 2.2.2019 Bet on it 0TEMP_MPY_wvf_snd.mp3 MoviePy - Done. Moviepy - Writing video C:\Kaushal\video projects\2019.02 Oxford 2.2.2019 Bet on it 0.mp4

t: 0%| | 0/385 [00:00<?, ?it/s, now=None]Traceback (most recent call last): File "C:\Kaushal\Stable-Diffusion\vid_stitch.py", line 196, in main() File "C:\Kaushal\Stable-Diffusion\vid_stitch.py", line 189, in main final_clip.write_videofile(output_video_path, codec="libx264", audio_codec="mp3") File "", line 2, in write_videofile File "C:\Users\KD\AppData\Local\Programs\Python\Python310\lib\site-packages\moviepy\decorators.py", line 54, in requires_duration return f(clip, *a, k) File "", line 2, in write_videofile File "C:\Users\KD\AppData\Local\Programs\Python\Python310\lib\site-packages\moviepy\decorators.py", line 135, in use_clip_fps_by_default return f(clip, *new_a, *new_kw) File "", line 2, in write_videofile File "C:\Users\KD\AppData\Local\Programs\Python\Python310\lib\site-packages\moviepy\decorators.py", line 22, in convert_masks_to_RGB return f(clip, a, k) File "C:\Users\KD\AppData\Local\Programs\Python\Python310\lib\site-packages\moviepy\video\VideoClip.py", line 300, in write_videofile ffmpeg_write_video(self, filename, fps, codec, File "C:\Users\KD\AppData\Local\Programs\Python\Python310\lib\site-packages\moviepy\video\io\ffmpeg_writer.py", line 220, in ffmpeg_write_video for t,frame in clip.iter_frames(logger=logger, with_times=True, File "C:\Users\KD\AppData\Local\Programs\Python\Python310\lib\site-packages\moviepy\Clip.py", line 472, in iter_frames frame = self.get_frame(t) File "", line 2, in get_frame File "C:\Users\KD\AppData\Local\Programs\Python\Python310\lib\site-packages\moviepy\decorators.py", line 89, in wrapper return f(*new_a, new_kw) File "C:\Users\KD\AppData\Local\Programs\Python\Python310\lib\site-packages\moviepy\Clip.py", line 93, in get_frame return self.make_frame(t) File "C:\Users\KD\AppData\Local\Programs\Python\Python310\lib\site-packages\moviepy\video\compositing\CompositeVideoClip.py", line 111, in make_frame f = c.blit_on(f, t) File "C:\Users\KD\AppData\Local\Programs\Python\Python310\lib\site-packages\moviepy\video\VideoClip.py", line 527, in blit_on img = self.get_frame(ct) File "", line 2, in get_frame File "C:\Users\KD\AppData\Local\Programs\Python\Python310\lib\site-packages\moviepy\decorators.py", line 89, in wrapper return f(*new_a, *new_kw) File "C:\Users\KD\AppData\Local\Programs\Python\Python310\lib\site-packages\moviepy\Clip.py", line 93, in get_frame return self.make_frame(t) File "C:\Users\KD\AppData\Local\Programs\Python\Python310\lib\site-packages\moviepy\video\compositing\CompositeVideoClip.py", line 111, in make_frame f = c.blit_on(f, t) File "C:\Users\KD\AppData\Local\Programs\Python\Python310\lib\site-packages\moviepy\video\VideoClip.py", line 527, in blit_on img = self.get_frame(ct) File "", line 2, in get_frame File "C:\Users\KD\AppData\Local\Programs\Python\Python310\lib\site-packages\moviepy\decorators.py", line 89, in wrapper return f(new_a, new_kw) File "C:\Users\KD\AppData\Local\Programs\Python\Python310\lib\site-packages\moviepy\Clip.py", line 93, in get_frame return self.make_frame(t) File "C:\Users\KD\AppData\Local\Programs\Python\Python310\lib\site-packages\moviepy\Clip.py", line 136, in newclip = self.set_make_frame(lambda t: fun(self.get_frame, t)) File "C:\Users\KD\AppData\Local\Programs\Python\Python310\lib\site-packages\moviepy\Clip.py", line 187, in return self.fl(lambda gf, t: gf(t_func(t)), apply_to, File "", line 2, in get_frame File "C:\Users\KD\AppData\Local\Programs\Python\Python310\lib\site-packages\moviepy\decorators.py", line 89, in wrapper return f(*new_a, **new_kw) File "C:\Users\KD\AppData\Local\Programs\Python\Python310\lib\site-packages\moviepy\Clip.py", line 93, in get_frame return self.make_frame(t) File "C:\Users\KD\AppData\Local\Programs\Python\Python310\lib\site-packages\moviepy\video\io\VideoFileClip.py", line 113, in self.make_frame = lambda t: self.reader.get_frame(t) AttributeError: 'NoneType' object has no attribute 'get_frame'

Steps to Reproduce the Problem

Specifications

keikoro commented 3 months ago

Please check if the problem persists when you try with pre-release v2.0.0.dev2.

kaushal101 commented 3 months ago

After a number of trial and errors, I was able to make it work once again with 1.0.3, so thought I would share my analysis.

I stripped down all the code and went to very basic, and to my surprise below code worked. So I knew the issue is with some specific part of my original logic, but the basic functionality works.

clip1 = VideoFileClip(r"E:\To be backed up\2019.02 Oxford\PANA5378.MP4").subclip(0, 5).volumex(0.1)
clip2 = VideoFileClip(r"E:\To be backed up\2019.02 Oxford\PANA5379.MP4").subclip(0, 5).volumex(0.1)

clips = []
clips.append (clip1)
clips.append (clip2)
final_clip = concatenate_videoclips(clips, method="compose")
output_video_path = r"c:\kaushal\video projects\test1.mp4"
final_clip.write_videofile(output_video_path, codec="libx264", fps=30)

From here, I started adding back my original logic one after the other, making sure the code works at every point. And this is what is my conclusion. It's the way I'm using subclip functionality that was breaking the process.

If I use the functionality in one single method like below, it is working:

def gen_clips(selected_videos, duration):
    clips = []
    for video_path in selected_videos:

        print("processing file: " + video_path)
        try:
            original_duration = get_video_duration(video_path)
            if original_duration > duration:
                start_time = random.uniform(0, original_duration - duration)
                end_time = start_time + duration    # End time in seconds
                clip = VideoFileClip(video_path).subclip(start_time,end_time).volumex(0.4)
                clips.append(clip)
        except Exception as e:
            print(f"Error processing file {video_path}: {e}")
    return clips

But if I'm using the same functionality by calling a different function like below, it doesn't work. This particular methods don't throw any errors, but the final write_videofile method fails with the error from my original post.

def create_video_clip(video_path, start_time, end_time):
    with VideoFileClip(video_path) as clip:
        return clip.subclip(start_time, end_time).volumex(0.1)

def gen_clips(selected_videos, duration):
    clips = []
    for video_path in selected_videos:
        print("processing file: " + video_path)

        try:
            original_duration = get_video_duration(video_path)
            if original_duration > duration:
                start_time = random.uniform(0, original_duration - duration)
                end_time = start_time + duration    # End time in seconds
                clip = create_video_clip(video_path, start_time, end_time)
                clips.append(clip)
                #clip.close()
        except Exception as e:
            print(f"Error processing file {video_path}: {e}")
    return clips

I don't fully understand why it's behaving like this, because it's basically the same functionality. Something might be going wrong in handling over the clips from one method to the other perhaps?

ardyli commented 3 months ago

Exception in thread Thread-7 (audio2video2rtsp): Traceback (most recent call last): File "/opt/service/miniconda3/envs/pytorch_env/lib/python3.10/threading.py", line 1009, in _bootstrap_inner self.run() File "/opt/service/miniconda3/envs/pytorch_env/lib/python3.10/threading.py", line 946, in run self._target(*self._args, self._kwargs) File "/data_group/gatkadmin/ai/suziren/Speech2SadTalker/server_Speech2SadTalker_one.py", line 495, in audio2video2rtsp result = animate_from_coeff.generate(data, save_dir, pic_path, crop_info, \ File "/data_group/gatkadmin/ai/suziren/Speech2SadTalker/src/facerender/animate_one.py", line 195, in generate make_animation(source_image, source_semantics, target_semantics, File "/data_group/gatkadmin/ai/suziren/Speech2SadTalker/src/facerender/modules/make_animation_one.py", line 584, in make_animation final_video.write_videofile(file_name, codec='libx264', fps=25, audio_bitrate="24k",bitrate='57k') #, audio_codec='aac' File "", line 2, in write_videofile File "/opt/service/miniconda3/envs/pytorch_env/lib/python3.10/site-packages/moviepy/decorators.py", line 56, in requires_duration return f(clip, *a, *k) File "", line 2, in write_videofile File "/opt/service/miniconda3/envs/pytorch_env/lib/python3.10/site-packages/moviepy/decorators.py", line 135, in use_clip_fps_by_default return f(clip, new_a, new_kw) File "", line 2, in write_videofile File "/opt/service/miniconda3/envs/pytorch_env/lib/python3.10/site-packages/moviepy/decorators.py", line 24, in convert_masks_to_RGB return f(clip, *a, k) File "", line 2, in write_videofile File "/opt/service/miniconda3/envs/pytorch_env/lib/python3.10/site-packages/moviepy/decorators.py", line 89, in wrapper return f(*new_a, *new_kw) File "/opt/service/miniconda3/envs/pytorch_env/lib/python3.10/site-packages/moviepy/video/VideoClip.py", line 332, in write_videofile self.audio.write_audiofile( File "", line 2, in write_audiofile File "/opt/service/miniconda3/envs/pytorch_env/lib/python3.10/site-packages/moviepy/decorators.py", line 56, in requires_duration return f(clip, a, k) File "", line 2, in write_audiofile File "/opt/service/miniconda3/envs/pytorch_env/lib/python3.10/site-packages/moviepy/decorators.py", line 89, in wrapper return f(*new_a, new_kw) File "/opt/service/miniconda3/envs/pytorch_env/lib/python3.10/site-packages/moviepy/audio/AudioClip.py", line 230, in write_audiofile return ffmpeg_audiowrite( File "", line 2, in ffmpeg_audiowrite File "/opt/service/miniconda3/envs/pytorch_env/lib/python3.10/site-packages/moviepy/decorators.py", line 56, in requires_duration return f(clip, *a, *k) File "/opt/service/miniconda3/envs/pytorch_env/lib/python3.10/site-packages/moviepy/audio/io/ffmpeg_audiowriter.py", line 204, in ffmpeg_audiowrite for chunk in clip.iter_chunks( File "/opt/service/miniconda3/envs/pytorch_env/lib/python3.10/site-packages/moviepy/audio/AudioClip.py", line 90, in iter_chunks yield self.to_soundarray( File "", line 2, in to_soundarray File "/opt/service/miniconda3/envs/pytorch_env/lib/python3.10/site-packages/moviepy/decorators.py", line 56, in requires_duration return f(clip, a, k) File "/opt/service/miniconda3/envs/pytorch_env/lib/python3.10/site-packages/moviepy/audio/AudioClip.py", line 138, in to_soundarray snd_array = self.get_frame(tt) File "", line 2, in get_frame File "/opt/service/miniconda3/envs/pytorch_env/lib/python3.10/site-packages/moviepy/decorators.py", line 89, in wrapper return f(*new_a, new_kw) File "/opt/service/miniconda3/envs/pytorch_env/lib/python3.10/site-packages/moviepy/Clip.py", line 93, in get_frame return self.make_frame(t) File "/opt/service/miniconda3/envs/pytorch_env/lib/python3.10/site-packages/moviepy/audio/AudioClip.py", line 320, in make_frame sounds = [ File "/opt/service/miniconda3/envs/pytorch_env/lib/python3.10/site-packages/moviepy/audio/AudioClip.py", line 321, in c.get_frame(t - c.start) np.array([part]).T File "", line 2, in get_frame File "/opt/service/miniconda3/envs/pytorch_env/lib/python3.10/site-packages/moviepy/decorators.py", line 89, in wrapper return f(new_a, new_kw) File "/opt/service/miniconda3/envs/pytorch_env/lib/python3.10/site-packages/moviepy/Clip.py", line 93, in get_frame return self.make_frame(t) File "/opt/service/miniconda3/envs/pytorch_env/lib/python3.10/site-packages/moviepy/Clip.py", line 136, in newclip = self.set_make_frame(lambda t: fun(self.get_frame, t)) File "/opt/service/miniconda3/envs/pytorch_env/lib/python3.10/site-packages/moviepy/Clip.py", line 187, in return self.fl(lambda gf, t: gf(t_func(t)), apply_to, File "", line 2, in get_frame File "/opt/service/miniconda3/envs/pytorch_env/lib/python3.10/site-packages/moviepy/decorators.py", line 89, in wrapper return f(*new_a, **new_kw) File "/opt/service/miniconda3/envs/pytorch_env/lib/python3.10/site-packages/moviepy/Clip.py", line 93, in get_frame return self.make_frame(t) File "/opt/service/miniconda3/envs/pytorch_env/lib/python3.10/site-packages/moviepy/audio/io/AudioFileClip.py", line 88, in self.make_frame = lambda t: self.reader.get_frame(t) AttributeError: 'NoneType' object has no attribute 'get_frame'

ardyli commented 3 months ago

Specifications Python Version: 3.10.9 MoviePy Version: v2.0.0.dev2. Platform Name: Linux Platform Version: Ubuntu Server 22

ardyli commented 3 months ago

The issue arises when synthesizing video clips from image and audio clips, as the program always checks whether there is a ‘get_frame’ attribute in the audio object. If not, it will raise an error message: AttributeError: ‘NoneType’ object has no attribute ‘get_frame’.

问题出现在用图像片段与音频片段合成视频片段时,程序都会去检测音频对象中有没有get_frame属性。如没有都会抛出AttributeError: 'NoneType' object has no attribute 'get_frame' 出错信息。