[Script Tool] Extract frames from video, add srt subtitles for reading, and add background audio

1. Article directory

After reading this article, you will be able to learn the following:

  1. Batch video frame extraction;
  2. Add srt subtitles;
  3. Add srt dubbing;
  4. Add background music;
  5. Combine multiple video clips into a new video;

Effect:

2. Install dependencies

First install the video processing libraries opencv-python and moviepy. The installation method is pip install xxx.

py file at the top (the functions used below will not be described in detail in the import process):

from moviepy.editor import VideoFileClip, CompositeAudioClip, CompositeVideoClip, concatenate_videoclips
from cv2 import VideoCapture, VideoWriter, VideoWriter_fourcc, CAP_PROP_FRAME_COUNT

2. Video frame extraction

cv2.VideoCapture:

Function: Used to capture video frames from video files or cameras.

Explanation: This function allows you to create a video capture object to read video frames from the specified video source. You can pass the path to the video file or the index of the camera as a parameter and then use that to read the video frame by frame.

cv2.VideoWriter:

Function: Used to write video frames to a new video file.

Explanation: This function allows you to create a video writer object to write video frames to a new video file. You can specify the output file name, video codec, frame rate, resolution and other parameters. This is very useful when editing videos and saving processed videos.

cv2.VideoWriter_fourcc:

Function: Used to specify the four-character code (FourCC) of the video codec.

Explanation: FourCC is a 4-byte code used to identify the video codec. Different codecs have different FourCC codes. By using this function you can select the codec to use in the output video.

CAP_PROP_FRAME_COUNT:

Function: Used to obtain the total number of frames in the video file.

Explanation: CAP_PROP_FRAME_COUNT is a property of the cv2.VideoCapture object, used to obtain the total number of frames in the video file. This is useful for things like determining the duration of a video and looping the video.

Complete code block:

# Single video frame extraction
def video_extract_frame(video_path, out_path):

  #Open video file
  vc = VideoCapture(video_path)

  #The total number of frames in the video
  total_frame = int(vc.get(CAP_PROP_FRAME_COUNT))

  video = VideoFileClip(video_path, audio=False)

  # # Create a video writing object. Set video width and height, frame rate, output path
  fourcc = VideoWriter_fourcc(*'mp4v')
  videoWriter = VideoWriter(out_path, fourcc, VIDEO_FPS, (video.w, video.h))

  if vc.isOpened():
      status, frame = vc.read()
  else:
      status=False
      print("The video was not opened successfully!")
      vc.release()
      video.close()
      videoWriter.release()
      return False

  if status:
      for index in trange(total_frame, desc='Frame extraction progress'):
          # Read video frames and write output video
          status, frame = vc.read()
          if index % VIDEO_FPS == 0:
              skip_frames = tool.get_unique_random_numbers(index, VIDEO_FPS)
          if index in skip_frames:
              continue
          videoWriter.write(frame)

  videoWriter.release()
  vc.release()
  video.close()

Call the frame extraction function above:

video_extract_frame('./test.mp4', 'result.mp4')

If there are multiple videos that need to extract frames, you only need to call them in a loop:

 for index, video_file in enumerate(['1.mp4', '2.mp4', '3.mp4']):
    
    //Get the video file name in the link, such as 1,2,3
    video_name = tool.get_file_name(video_file)
    //Assemble into a new directory and new name (according to your needs)
    out_path = "{}{}.mp4".format(frame_path, video_name)

    videoTool.video_extract_frame(video_file, out_path)
    print("\
Item {}, video frame extraction completed: {}".format(index + 1, out_path))

3. Add srt subtitles;

Complete sample code:

# subtitle snippet
def generate_textclip(text, width, params, start, duration) -> TextClip:

    return TextClip(
        text,
        font=params.get('font'),
        align=params.get('align'),
        fontsize=params.get('fontsize'),
        color=params.get('color', '#ffbd00'),
        size=(width, params.get('height')),
        stroke_color=params.get('stroke_color'),
        stroke_width=params.get('stroke_width')
    ).set_position((params['location']['x'], params['location']['y'])).set_duration(duration).set_start(start)


# Add srt subtitles
def add_srt(video_clip, params):

  if not (isfile(params["srt_path"]) and params["srt_path"].endswith('.srt')):
      print('Subtitles only support srt format!')
      return []
  else:
      # Get the width and height of the video
      v_width, v_height = video_clip.w, video_clip.h
      # All subtitle clips
      txts = []
      content = tool.read_srt(params["srt_path"])
      sequences = tool.get_sequences(content)

      max_count = len(sequences)

      max_duration = video_clip.duration

      srt_text = params["srt_text"]

      for index, line in enumerate(sequences):
          print("Subtitle generation progress:{}/{}".format(index, max_count))
          if len(line) < 3:
              continue
          start = line[1].split(' --> ')[0]
          end = line[1].split(' --> ')[1]

          start = tool.str_float_time(start)
          end = tool.str_float_time(end)

          start, end = map(float, (start, end))

          if start >= max_duration:
              break
          if end >= max_duration:
              end = max_duration

          duration=end-start

          txt_srt = generate_textclip(line[2], (v_width - 20), srt_text, start, duration)

          txts.append(txt_srt)

      print("\
Subtitle conversion completed...")

      return txts

tool.read_srt code:

def read_srt(self, path):
    content = ""
    with open(path, 'r', encoding='UTF-8') as f:
        content = f.read()
        return content

tool.get_sequences code:

# Subtitle splitting
def get_sequences(self, content):
    sequences = content.split('\
\
')
    sequences = [sequence.split('\
') for sequence in sequences]
    #Remove null values from each sentence
    sequences = [list(filter(None, sequence)) for sequence in sequences]
    #Remove overall null values
    return list(filter(None, sequences))

tool.str_float_time code:

# Format string numbers into time
def str_float_time(self, str):
    str_list = str.split(':')
    hour = int(str_list[0])
    minute = int(str_list[1])
    second = int(str_list[2].split(',')[0])
    minsecond = int(str_list[2].split(',')[1])
    allTime = hour * 60 * 60 + minute * 60 + second + minsecond / 1000
    return allTime

4. Add background music, srt audio, and export

# frame_video_paths: is the video clip after frame extraction
def merge_videos_with_bgm(params, frame_video_paths, out_path):
    # Use VideoFileClip to load all input video files
    video_clips = [VideoFileClip(file) for file in frame_video_paths]

    final_clip = concatenate_videoclips(video_clips)

    bgm_music = audioTool.get_audio({<!-- -->
        "volume": params.get("bgm_volume", 0.1), #Keep the background audio down
        "duration": final_clip.duration,
        "audio_path": params['bgm_path']
    })

    # Load main music (reading audio)
    main_music = audioTool.get_audio({<!-- -->
        "volume": params.get("audio_volume", 1.0),# Read the audio louder
        "duration": final_clip.duration,
        "audio_path": params['audio_path']
    })


    # Mix music into final video slice
    final_clip = final_clip.set_audio(CompositeAudioClip([main_music, bgm_music]))

    video_srt = videoTextTool.add_srt(final_clip, params)


    final_clip.extend(video_srt)

    # composite video
    video = CompositeVideoClip(final_clip)

    output_path = path.join(out_path, "result.mp4")

    # Output final video
    video.write_videofile(output_path)


For more exciting crawler cases and tool source codes, search the public account “A snail running hard
The complete source code of the actual combat has been uploaded to the WeChat public account, and it will be more exciting to share in the future!