Skip to content

Commit 70695f5

Browse files
committed
Expose the duration field for frames
Signed-off-by: Max Ehrlich <max.ehr@gmail.com>
1 parent fc1402c commit 70695f5

File tree

5 files changed

+42
-0
lines changed

5 files changed

+42
-0
lines changed

av/frame.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ class SideData(TypedDict, total=False):
99
class Frame:
1010
dts: int | None
1111
pts: int | None
12+
duration: int | None
1213
time_base: Fraction
1314
side_data: SideData
1415
opaque: object

av/frame.pyx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ cdef class Frame:
5353
if self.ptr.pts != lib.AV_NOPTS_VALUE:
5454
self.ptr.pts = lib.av_rescale_q(self.ptr.pts, self._time_base, dst)
5555

56+
if self.ptr.duration != 0:
57+
self.ptr.duration = lib.av_rescale_q(self.ptr.duration, self._time_base, dst)
58+
5659
self._time_base = dst
5760

5861
@property
@@ -95,6 +98,24 @@ cdef class Frame:
9598
else:
9699
self.ptr.pts = value
97100

101+
@property
102+
def duration(self):
103+
"""
104+
The duration of the frame in :attr:`time_base` units
105+
106+
:type: int
107+
"""
108+
if self.ptr.duration == 0:
109+
return None
110+
return self.ptr.duration
111+
112+
@duration.setter
113+
def duration(self, value):
114+
if value is None:
115+
self.ptr.duration = 0
116+
else:
117+
self.ptr.duration = value
118+
98119
@property
99120
def time(self):
100121
"""

av/video/frame.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class PictureType(IntEnum):
3030
class VideoFrame(Frame):
3131
format: VideoFormat
3232
pts: int
33+
duration: int
3334
planes: tuple[VideoPlane, ...]
3435
pict_type: int
3536
colorspace: int

include/libavcodec/avcodec.pxd

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,8 @@ cdef extern from "libavcodec/avcodec.h" nogil:
461461
AVColorTransferCharacteristic color_trc
462462
AVColorSpace colorspace
463463

464+
int64_t duration
465+
464466
cdef AVFrame* avcodec_alloc_frame()
465467

466468
cdef struct AVPacket:

tests/test_videoframe.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,23 @@ def test_opaque() -> None:
4949
assert type(frame.opaque) is tuple and len(frame.opaque) == 2
5050

5151

52+
def test_frame_duration_matches_packet() -> None:
53+
with av.open(fate_suite("h264/interlaced_crop.mp4")) as container:
54+
packet_durations = []
55+
frame_durations = []
56+
for packet in container.demux():
57+
if packet.pts is not None:
58+
packet_durations.append((packet.pts, packet.duration))
59+
for frame in packet.decode():
60+
if frame.pts is not None:
61+
frame_durations.append((frame.pts, frame.duration))
62+
63+
packet_durations.sort(key=lambda x: x[0])
64+
frame_durations.sort(key=lambda x: x[0])
65+
66+
assert all(pd[1] == fd[1] for pd, fd in zip(packet_durations, frame_durations))
67+
68+
5269
def test_invalid_pixel_format() -> None:
5370
with pytest.raises(ValueError, match="not a pixel format: '__unknown_pix_fmt'"):
5471
VideoFrame(640, 480, "__unknown_pix_fmt")

0 commit comments

Comments
 (0)