Skip to content

How do I compute pts for additional frames of a video generated from an existing one #1063

@iongion

Description

@iongion

Overview

  • I want to traverse a video and after frame 10 - add 10 more frames made from current 10-nth frame onto which I draw an overlay and then resume and remux the rest of the frames.
  • I don't know how to compute pts for new added frames and the rest

I am using the example provided by @jlaine at #473 with great help to know how to compute pts.

I've came with this formula hardly digging

def frame_to_pts(frame, time_base, frame_rate, start_time):
    return int((frame + start_time * time_base * frame_rate) / (time_base * frame_rate))

then

import av
import cv2


# open input and find video stream
input_container = av.open('input.mp4', 'r')
input_stream = input_container.streams.get(video=0)[0]

# open output and add a video stream
output_container = av.open('output.mp4', 'w')
output_stream = output_container.add_stream('h264', rate=input_stream.rate)

frame_current = 0

for frame in input_container.decode(input_stream):
    # perform edge detection
    img = frame.to_ndarray(format='bgr24')
    img = cv2.cvtColor(cv2.Canny(img, 100, 200), cv2.COLOR_GRAY2BGR)

    if frame_current > 10 and frame_current <= 20:
        # add 10 more frames
        for i in range(10):
            # compute new pts
            pts_computed = frame_to_pts(frame_current, frame.time_base, input_stream.average_rate, input_stream.start_time)
            # rebuild a VideoFrame, preserving timing information
            new_frame = av.VideoFrame.from_ndarray(img, format='bgr24')
            new_frame.pts = pts_computed
            new_frame.time_base = frame.time_base
            # encode and mux
            for packet in output_stream.encode(new_frame):
                output_container.mux(packet)
            # advance one frame
            frame_current += 1
    else:
        # compute new pts
        pts_computed = frame_to_pts(frame_current, frame.time_base, input_stream.average_rate, input_stream.start_time)
        # rebuild a VideoFrame, preserving timing information
        old_frame = av.VideoFrame.from_ndarray(img, format='bgr24')
        old_frame.pts = pts_computed
        old_frame.time_base = frame.time_base
        # encode and mux
        for packet in output_stream.encode(old_frame):
            output_container.mux(packet)
            # advance one frame
        frame_current += 1


# flush and close output
for packet in output_stream.encode(None):
    output_container.mux(packet)
output_container.close()

Expected behavior

The formula above seems to work for h264 codec, but I have no confidence in it -

Actual behavior

Switching to h264_nvenc instead of h264 gets me pts < dts errors.

Investigation

Tried other codecs hevc_nvenc - seems to work

Research

I have done the following:

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions