Skip to content

Commit 49b7c14

Browse files
committed
Fix segfault when printing closed video_stream
1 parent 5bbd3b1 commit 49b7c14

File tree

7 files changed

+59
-10
lines changed

7 files changed

+59
-10
lines changed

av/audio/stream.pyi

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ class _Layout:
1818

1919
class AudioStream(Stream):
2020
codec_context: AudioCodecContext
21+
def encode(self, frame: AudioFrame | None = None) -> list[Packet]: ...
22+
def decode(self, packet: Packet | None = None) -> list[AudioFrame]: ...
23+
2124
# From codec context
2225
frame_size: int
2326
sample_rate: int
@@ -27,5 +30,5 @@ class AudioStream(Stream):
2730
type: Literal["audio"]
2831
format: _Format
2932
layout: _Layout
30-
def encode(self, frame: AudioFrame | None = None) -> list[Packet]: ...
31-
def decode(self, packet: Packet | None = None) -> list[AudioFrame]: ...
33+
34+
def close(self, strict: bool = True) -> None: ...

av/codec/context.pxd

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ cdef class CodecContext:
4949
cdef _setup_decoded_frame(self, Frame, Packet)
5050

5151
# Implemented by base for the generic send/recv API.
52-
# Note that the user cannot send without recieving. This is because
53-
# _prepare_frames_for_encode may expand a frame into multiple (e.g. when
52+
# Note that the user cannot send without receiving. This is because
53+
# `_prepare_frames_for_encode` may expand a frame into multiple (e.g. when
5454
# resampling audio to a higher rate but with fixed size frames), and the
5555
# send/recv buffer may be limited to a single frame. Ergo, we need to flush
5656
# the buffer as often as possible.

av/codec/context.pyx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,10 +193,11 @@ cdef class CodecContext:
193193

194194
@property
195195
def extradata(self):
196+
if self.ptr is NULL:
197+
return None
196198
if self.ptr.extradata_size > 0:
197199
return <bytes>(<uint8_t*>self.ptr.extradata)[:self.ptr.extradata_size]
198-
else:
199-
return None
200+
return None
200201

201202
@extradata.setter
202203
def extradata(self, data):
@@ -222,10 +223,14 @@ cdef class CodecContext:
222223

223224
@property
224225
def is_encoder(self):
226+
if self.ptr is NULL:
227+
return False
225228
return lib.av_codec_is_encoder(self.ptr.codec)
226229

227230
@property
228231
def is_decoder(self):
232+
if self.ptr is NULL:
233+
return False
229234
return lib.av_codec_is_decoder(self.ptr.codec)
230235

231236
cpdef open(self, bint strict=True):

av/container/streams.pyi

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ class StreamContainer:
1616
other: tuple[Stream, ...]
1717

1818
def __init__(self) -> None: ...
19-
def add_stream(self, stream: Stream) -> None: ...
2019
def __len__(self) -> int: ...
2120
def __iter__(self) -> Iterator[Stream]: ...
2221
@overload

av/video/codeccontext.pyx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ cdef class VideoCodecContext(CodecContext):
7878

7979
@property
8080
def width(self):
81+
if self.ptr is NULL:
82+
return 0
8183
return self.ptr.width
8284

8385
@width.setter
@@ -87,6 +89,8 @@ cdef class VideoCodecContext(CodecContext):
8789

8890
@property
8991
def height(self):
92+
if self.ptr is NULL:
93+
return 0
9094
return self.ptr.height
9195

9296
@height.setter

av/video/stream.pyi

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ class VideoStream(Stream):
1717
sample_aspect_ratio: Fraction | None
1818
display_aspect_ratio: Fraction | None
1919
codec_context: VideoCodecContext
20+
21+
def encode(self, frame: VideoFrame | None = None) -> list[Packet]: ...
22+
def encode_lazy(self, frame: VideoFrame | None = None) -> Iterator[Packet]: ...
23+
def decode(self, packet: Packet | None = None) -> list[VideoFrame]: ...
24+
2025
# from codec context
2126
format: VideoFormat
2227
width: int
@@ -36,6 +41,4 @@ class VideoStream(Stream):
3641
colorspace: int
3742
type: Literal["video"]
3843

39-
def encode(self, frame: VideoFrame | None = None) -> list[Packet]: ...
40-
def encode_lazy(self, frame: VideoFrame | None = None) -> Iterator[Packet]: ...
41-
def decode(self, packet: Packet | None = None) -> list[VideoFrame]: ...
44+
def close(self, strict: bool = True) -> None: ...

tests/test_streams.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,41 @@ def test_selection(self) -> None:
3535
data = container.streams.data[0]
3636
assert data == container.streams.best("data")
3737

38+
def test_printing_closed_video_stream(self) -> None:
39+
input_ = av.open(
40+
fate_suite("amv/MTV_high_res_320x240_sample_Penguin_Joke_MTV_from_WMV.amv")
41+
)
42+
container = av.open("out.mkv", "w")
43+
44+
video_stream = container.add_stream("h264", rate=30)
45+
# encoder = video_stream.codec.name + ""
46+
47+
video_stream.width = input_.streams.video[0].width
48+
video_stream.height = input_.streams.video[0].height
49+
video_stream.pix_fmt = "yuv420p"
50+
51+
for frame in input_.decode(video=0):
52+
container.mux(video_stream.encode(frame))
53+
break
54+
55+
encoder = "libx264"
56+
repr = f"{video_stream}"
57+
assert repr.startswith(f"<av.VideoStream #0 {encoder}, yuv420p 160x120 at ")
58+
assert repr.endswith(">")
59+
60+
# repr = f"{video_stream}"
61+
# assert repr.startswith(f"<av.VideoStream #0 {encoder}, yuv420p 160x120 at ")
62+
# assert repr.endswith(">")
63+
64+
video_stream.close()
65+
66+
repr = f"{video_stream}"
67+
assert repr.startswith(f"<av.VideoStream #0 {encoder}, yuv420p 0x0 at ")
68+
assert repr.endswith(">")
69+
70+
container.close()
71+
input_.close()
72+
3873
# def test_side_data(self) -> None:
3974
# container = av.open(fate_suite("mov/displaymatrix.mov"))
4075
# video = container.streams.video[0]

0 commit comments

Comments
 (0)