Как писать видео с переменной частотой кадров на Python

#python #opencv #ffmpeg

#питон #opencv #ffmpeg

Вопрос:

У меня есть кадры в приложении Python с относительными отметками времени с начала видео. Есть ли способ записать видео с переменной частотой кадров, используя эти временные метки?

Я уже пробовал opencv-python и scikit video, но, похоже, он не может записать правильные временные метки на случай изменения кадров в секунду во время видео.

Ответ №1:

OpenCV не является медиа-библиотекой. это не дает вам такой гибкости.

Взгляните на ПьЯВ

расширенный пример для пользовательских PTS:

 #!/usr/bin/env python3

# extended from https://github.com/PyAV-Org/PyAV/blob/main/examples/numpy/generate_video.py
# added: custom PTS

import numpy as np
import av
from fractions import Fraction

total_frames = 20

container = av.open('test.mp4', mode='w')

stream = container.add_stream('mpeg4', rate=25) # alibi frame rate
stream.width = 480
stream.height = 320
stream.pix_fmt = 'yuv420p'

# we'll show frames for 0.5 or 1.0 seconds
# my_pts will say when the next frame is due
# time_base is somewhat arbitrary. this is milliseconds. nice round numbers. I could use 1/2 for my desired PTSes.
stream.codec_context.time_base = Fraction(1, 1000)
my_pts = 0
# ffmpeg time is "complicated". read more at https://github.com/PyAV-Org/PyAV/blob/main/docs/api/time.rst

for frame_i in range(total_frames):

    img = np.empty((480, 320, 3))
    img[:, :, 0] = 0.5   0.5 * np.sin(2 * np.pi * (0 / 3   frame_i / total_frames))
    img[:, :, 1] = 0.5   0.5 * np.sin(2 * np.pi * (1 / 3   frame_i / total_frames))
    img[:, :, 2] = 0.5   0.5 * np.sin(2 * np.pi * (2 / 3   frame_i / total_frames))

    img = np.round(255 * img).astype(np.uint8)
    img = np.clip(img, 0, 255)

    frame = av.VideoFrame.from_ndarray(img, format='rgb24')

    frame.pts = int(my_pts / stream.codec_context.time_base) # seconds -> counts of time_base

    # calculate time of *next* frame, i.e. how long *this* frame will show
    # show three in quick succession, three slowly, repeat
    my_pts  = 0.5 if ((frame_i // 3) % 2 == 0) else 1.0

    for packet in stream.encode(frame):
        container.mux(packet)

# Flush stream
for packet in stream.encode():
    container.mux(packet)

# Close the file
container.close()