Search code examples
pythonvideoffmpeg

ffmpeg black screen issue for video video generation from a list of frames


I used a video to generate a list of frames from it, then I wanted to create multiple videos from this list of frames. I've set starting and ending frames indexes for each "sub video", so for example, indexes = [[0, 64], [64, 110], [110, 234], [234, 449]], and those indexes will help my code generate 4 videos of various durations. The idea is to decompose the original video into multiple sub videos. My code is working just fine, the video generated.

But every sub video start with multiple seconds of black screen, only the first generated video (so the one using indexes[0] for starting and ending frames) is generated without this black screen part. I've tried changing the frame rate for each sub_video, according to the number of frames and things like that, but I didn't work. You can find my code below

for i, (start_idx, end_idx) in enumerate(self.video_frames_indexes):
    if end_idx - start_idx > 10:
        shape = cv2.imread(f'output/video_reconstitution/{video_name}/final/frame_{start_idx}.jpg').shape
        os.system(f'ffmpeg -r 30 -s {shape[0]}x{shape[1]} -i output/video_reconstitution/{video_name}/final/frame_%d.JPG'
              f' -vf "select=between(n\,{start_idx}\,{end_idx})" -vcodec libx264 -crf 25'
              f' output/video_reconstitution/IMG_7303/sub_videos/serrage_{i}.mp4')

Just the ffmpeg command

ffmpeg -r 30 -s {shape[0]}x{shape[1]} -i output/video_reconstitution/{video_name}/final/frame_%d.JPG -vf "select=between(n\,{start_idx}\,{end_idx})" -vcodec libx264 -crf 25 output/video_reconstitution/IMG_7303/sub_videos/serrage_{i}.mp4

Solution

  • There are few issues that might cause black screen issue at the start.

    Few video players shows black video when the timestamps are not starting from zero.
    select filter doesn't reset the timestamps to start form zero, and just pass them through.
    That means that files that doesn't start from the first frame starts with timestamps that are not zero.
    For testing, we may use FFprobe (from command line):

    ffprobe -select_streams v:0 -show_entries packet=pts_time -of compact=p=0 serrage_2.mp4


    For removing the offset, we may use setpts after select filter:
    -vf "select=between(n\,{start_idx}\,{end_idx}),setpts=PTS-STARTPTS"

    The expression PTS-STARTPTS subtracts the first PTS (timestamp) from all the timestamps (- is a minus sign not a dash).


    We don't suppose to use -s {shape[0]}x{shape[1]} for JPEG images, because the input size of the image is know.
    In case we really want to, we have to swap the indices: -s {shape[1]}x{shape[0]}.
    shape[1] is the width, and shape[0] is the height...


    For testing we may use FFmpeg for creating 500 JPEG images:

    ffmpeg -y -f lavfi -i testsrc=size=192x108:rate=1:duration=500 frame_%d.jpg

    (Note that the first index is 1).


    Updated code sample:

    import os
    import cv2
    
    indexes = [[1, 64], [64, 110], [110, 234], [234, 449]]
    
    for i, (start_idx, end_idx) in enumerate(indexes):
        if end_idx - start_idx > 10:
            shape = cv2.imread(f'/Tmp/jpeg_frames/frame_{start_idx}.jpg').shape
            os.system(f'ffmpeg -y -r 30 -s {shape[1]}x{shape[0]} -i /Tmp/jpeg_frames/frame_%d.JPG'
                  f' -vf "select=between(n\,{start_idx}\,{end_idx}),setpts=PTS-STARTPTS" -vcodec libx264 -crf 25'
                  f' /Tmp/jpeg_frames/serrage_{i}.mp4')
    

    In case the JPEG images have different resolutions, it may also be a reason for depredated MP4 files.
    In case of different resolutions, it is recommended to resize the images using OpenCV for example (not using FFmpeg).