Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Duration floating point round-off causes "IndexError: list index out of range" in write_videofile #646

Closed
pkarp0 opened this issue Sep 26, 2017 · 12 comments · Fixed by #1195
Labels
bug Issues that report (apparent) bugs.

Comments

@pkarp0
Copy link

pkarp0 commented Sep 26, 2017

The sample code creates a color clip with a duration of 2.24, a size of [1080, 1080] and fps=50.0
The clip writes properly after CompositeVideoClip but if it is concatenated then the duration changes slightly and writing fails.

>>> vclip = ColorClip([1080, 1080], color=[255, 128, 64], duration=2.24)
>>> vclip.duration
2.24
>>> vclip.size
(1080, 1080)
>>> comp = CompositeVideoClip([vclip])
>>> comp = comp.set_fps(50.0)
>>> comp.fps
50.0
>>> comp.write_videofile('comp.mp4', preset='ultrafast')

The file writes successfully

>>> concat = concatenate([comp])
>>> concat.duration
2.2400000000000002
>>> concat.write_videofile('concat.mp4', preset='ultrafast')

The write_videofile() fails with IndexError

  Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<decorator-gen-51>", line 2, in write_videofile
  File "/usr/local/lib/python3.6/site-packages/moviepy/decorators.py", line 54, in requires_duration
    return f(clip, *a, **k)
  File "<decorator-gen-50>", line 2, in write_videofile
  File "/usr/local/lib/python3.6/site-packages/moviepy/decorators.py", line 137, in use_clip_fps_by_default
    return f(clip, *new_a, **new_kw)
  File "<decorator-gen-49>", line 2, in write_videofile
  File "/usr/local/lib/python3.6/site-packages/moviepy/decorators.py", line 22, in convert_masks_to_RGB
    return f(clip, *a, **k)
  File "/usr/local/lib/python3.6/site-packages/moviepy/video/VideoClip.py", line 349, in write_videofile
    progress_bar=progress_bar)
  File "/usr/local/lib/python3.6/site-packages/moviepy/video/io/ffmpeg_writer.py", line 209, in ffmpeg_write_video
    fps=fps, dtype="uint8"):
  File "/usr/local/lib/python3.6/site-packages/tqdm/_tqdm.py", line 833, in __iter__
    for obj in iterable:
  File "/usr/local/lib/python3.6/site-packages/moviepy/Clip.py", line 475, in generator
    frame = self.get_frame(t)
  File "<decorator-gen-14>", line 2, in get_frame
  File "/usr/local/lib/python3.6/site-packages/moviepy/decorators.py", line 89, in wrapper
    return f(*new_a, **new_kw)
  File "/usr/local/lib/python3.6/site-packages/moviepy/Clip.py", line 95, in get_frame
    return self.make_frame(t)
  File "/usr/local/lib/python3.6/site-packages/moviepy/video/compositing/concatenate.py", line 83, in make_frame
    return clips[i].get_frame(t - tt[i])
IndexError: list index out of range
@tburrows13
Copy link
Collaborator

Can confirm this problem, thanks for spotting!

@pkarp0
Copy link
Author

pkarp0 commented Oct 2, 2017

@tburrows13 Who are you asking for confirmation?

@tburrows13
Copy link
Collaborator

I meant that I can confirm this problem, sorry!

@pkarp0
Copy link
Author

pkarp0 commented Oct 2, 2017

@tburrows13 Great. Thanks for your help.

@keikoro keikoro added the bug Issues that report (apparent) bugs. label Nov 25, 2017
@AidasLiaudanskas
Copy link

Can confirm this is still happening. the output of concatenate_videoclips assumes there's one more frame than there are in the clips that were merged. Seems to happen because of the float precision when the time of certain clip cannot be represented exactly.

@AidasLiaudanskas
Copy link

Temporary workaround if anyone's still stuck:

    final_clip = concatenate_videoclips(list_of_clips)
    try:
        final_clip.write_videofile(save_path,  threads=6, logger=None)
        logger.info("Saved .mp4 without Exception at {}".format(save_path))
    except IndexError:
        # Short by one frame, so get rid on the last frame:
        final_clip = final_clip.subclip(t_end=(clip.duration - 1.0/final_clip.fps))
        final_clip.write_videofile(save_path, threads=6, logger=None)
        logger.info("Saved .mp4 after Exception at {}".format(save_path))
    except Exception as e:
        logger.warning("Exception {} was raised!!".format(e))  

@ierezell
Copy link

Still get the same IndexError problem but the @AidasLiaudanskas patch works perfectly fine ! Thanks

@sunilk-n
Copy link

I'm still getting this issue now... Could you please look into this?

1 similar comment
@sunilk-n
Copy link

I'm still getting this issue now... Could you please look into this?

@Ethan0625
Copy link

I'm still getting this issue now... Could you please look into this?

I fixed this issue in manually.

There is a prbloem in File concatenate.py, line 66

timings = np.cumsum([0] + [clip.duration for clip in clips]

'clip.duration' is not always an integer.

so if you change 'clip.duration' to integer thing, that problem will solve.

But I don't know this solution make another problem.

@tbermudez10
Copy link

Hello, I am still having issues with this, ehile using the latest version (v1.0.3) and the patch suggested before

( final_clip = concatenate_videoclips(list_of_clips)
try:
final_clip.write_videofile(save_path, threads=6, logger=None)
logger.info("Saved .mp4 without Exception at {}".format(save_path))
except IndexError:
# Short by one frame, so get rid on the last frame:
final_clip = final_clip.subclip(t_end=(clip.duration - 1.0/final_clip.fps))
final_clip.write_videofile(save_path, threads=6, logger=None)
logger.info("Saved .mp4 after Exception at {}".format(save_path))
except Exception as e:
logger.warning("Exception {} was raised!!".format(e)) )

Any news on how it will / can be fixed?

@LindaLawton
Copy link

I just posted a question on Stack overflow write_videofile throws list index out of range - but works I am wondering if it may be related to this

I have a bunch of videos in a directory 1.mp4, 2.mp4 .... I am merging them into output_1_2.mp4.

The following code works, but ..

Its throwning an error that i cant seem to figure out.

Merging: videos/1.mp4 videos/2.mp4 videos/output_1_2.mp4

Length of clips list: 2

Moviepy - Building video videos/output_1_2.mp4.

MoviePy - Writing audio in output_1_2TEMP_MPY_wvf_snd.mp3

t: 0%| | 0/499 [00:00<?, ?it/s, now=None]MoviePy - Done.

Moviepy - Writing video videos/output_1_2.mp4

Error writing final clip: list index out of range

Again it works the files are merged, im just trying to understand the why

final_clip.write_videofile(output_path, codec='libx264')

is throwing an list index out of range error.

As the code works i can probably just ignore it. This question is more out of curiosity.

Code

import os
import re

from moviepy.editor import VideoFileClip, concatenate_videoclips


def merge_videos_in_directory(directory):
    # Get a list of all files in the directory
    files = os.listdir(directory)

    # Filter out files that start with "output"
    files = [file for file in files if not file.startswith("output")]

    # Sort the files based on numeric part in the filename
    files.sort(key=lambda x: int(re.search(r'\d+', x).group()))

    # Print the list of files for debugging
    print("Files in directory:", files)

    # Iterate through the files in pairs
    for i in range(0, len(files), 2):
        # Check if there are at least two files remaining
        if i + 1 < len(files):
            # Get the paths of the two videos to merge
            video1_path = os.path.join(directory, files[i])
            video2_path = os.path.join(directory, files[i + 1])

            # Create the output file name
            output_name = f"output_{files[i][:-4]}_{files[i + 1][:-4]}.mp4"
            output_path = os.path.join(directory, output_name)

            # Merge the two videos
            print("Merging:", video1_path, video2_path, output_path)
            try:
                merge_videos(video1_path, video2_path, output_path)
            except Exception as e:
                print("Error merging videos:", e)


def merge_videos(video1_path, video2_path, output_path):
    # Load the video clips
    video1 = VideoFileClip(video1_path)
    video2 = VideoFileClip(video2_path)

    # Print the lengths of the clips list for debugging
    print("Length of clips list:", len([video1, video2]))

    # Concatenate the video clips
    final_clip = concatenate_videoclips([video1, video2])

    try:
        # Write the final clip to a file
        final_clip.write_videofile(output_path, codec='libx264')  # exception thrown here.

    except Exception as e:
        # Handle any errors
        print("Error writing final clip:", e)
        # Close the clips if an error occurs
        final_clip.close()
        video1.close()
        video2.close()
    finally:
        # Close the clips
        final_clip.close()
        video1.close()
        video2.close()


# Example usage:
videos_dir = "videos/"
merge_videos_in_directory(videos_dir)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Issues that report (apparent) bugs.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants