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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions