Skip to content

Commit c10fc65

Browse files
committed
Fixed HWAccel so we don't share contexts between streams
1 parent a4854a3 commit c10fc65

3 files changed

Lines changed: 42 additions & 24 deletions

File tree

av/codec/hwaccel.pyx

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import copy
12
import weakref
23
from enum import IntEnum
34

@@ -7,6 +8,7 @@ from av.codec.codec cimport Codec
78
from av.dictionary cimport _Dictionary
89
from av.error cimport err_check
910
from av.video.format cimport get_video_format
11+
1012
from av.dictionary import Dictionary
1113

1214

@@ -94,34 +96,32 @@ cpdef hwdevices_available():
9496

9597

9698
cdef class HWAccel:
97-
def __init__(self, device_type, device=None, codec=None, allow_software_fallback=True, options=None):
99+
def __init__(self, device_type, device=None, allow_software_fallback=True, options=None):
98100
if isinstance(device_type, HWDeviceType):
99101
self._device_type = device_type
100102
elif isinstance(device_type, str):
101103
self._device_type = int(lib.av_hwdevice_find_type_by_name(device_type))
104+
elif isinstance(device_type, int):
105+
self._device_type = device_type
102106
else:
103107
raise ValueError("Unknown type for device_type")
104108

105109
self._device = device
106110
self.allow_software_fallback = allow_software_fallback
107111
self.options = {} if not options else dict(options)
108112
self.ptr = NULL
109-
self.codec = codec
110113
self.config = None
111114

112-
if codec:
113-
self._initialize_hw_context()
114-
115-
def _initialize_hw_context(self):
115+
def _initialize_hw_context(self, Codec codec not None):
116116
cdef HWConfig config
117-
for config in self.codec.hardware_configs:
117+
for config in codec.hardware_configs:
118118
if not (config.ptr.methods & lib.AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX):
119119
continue
120120
if self._device_type and config.device_type != self._device_type:
121121
continue
122122
break
123123
else:
124-
raise NotImplementedError(f"No supported hardware config for {self.codec}")
124+
raise NotImplementedError(f"No supported hardware config for {codec}")
125125

126126
self.config = config
127127

@@ -142,9 +142,14 @@ cdef class HWAccel:
142142
if self.ptr:
143143
raise RuntimeError("Hardware context already initialized")
144144

145-
self.codec = codec
146-
self._initialize_hw_context()
147-
return self
145+
ret = HWAccel(
146+
device_type=self._device_type,
147+
device=self._device,
148+
allow_software_fallback=self.allow_software_fallback,
149+
options=self.options
150+
)
151+
ret._initialize_hw_context(codec)
152+
return ret
148153

149154
def __dealloc__(self):
150155
if self.ptr:

examples/basics/hw_decode.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,14 @@
3030
)
3131

3232
if HW_DEVICE is None:
33-
av.codec.hwaccel.dump_hwdevices()
34-
print("Please set HW_DEVICE.")
33+
print(
34+
f"Please set HW_DEVICE. Options are: {str(av.codec.hwaccel.hwdevices_available())}"
35+
)
3536
exit()
3637

37-
assert HW_DEVICE in av.codec.hwaccel.hwdevices_available, f"{HW_DEVICE} not available."
38+
assert (
39+
HW_DEVICE in av.codec.hwaccel.hwdevices_available()
40+
), f"{HW_DEVICE} not available."
3841

3942
print("Decoding in software (auto threading)...")
4043

tests/test_decode.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@
88

99
import av
1010

11+
# This import is needed to make the test_decoded_time_base test pass when we run only this test file.
12+
# Not sure why.
13+
from av.subtitles import subtitle
14+
1115
from .common import TestCase, fate_suite
1216

1317

1418
@functools.cache
1519
def make_h264_test_video(path: str) -> None:
16-
"""Generates a black H264 test video for testing hardware decoding."""
20+
"""Generates a black H264 test video with two streams for testing hardware decoding."""
1721

1822
# We generate a file here that's designed to be as compatible as possible with hardware
1923
# encoders. Hardware encoders are sometimes very picky and the errors we get are often
@@ -23,21 +27,27 @@ def make_h264_test_video(path: str) -> None:
2327
# 8-bit yuv420p.
2428
pathlib.Path(path).parent.mkdir(parents=True, exist_ok=True)
2529
output_container = av.open(path, "w")
26-
stream = output_container.add_stream("libx264", rate=24)
27-
assert isinstance(stream, av.VideoStream)
28-
stream.width = 1280
29-
stream.height = 720
30-
stream.pix_fmt = "yuv420p"
30+
31+
streams = []
32+
for _ in range(2):
33+
stream = output_container.add_stream("libx264", rate=24)
34+
assert isinstance(stream, av.VideoStream)
35+
stream.width = 1280
36+
stream.height = 720
37+
stream.pix_fmt = "yuv420p"
38+
streams.append(stream)
3139

3240
for _ in range(24):
3341
frame = av.VideoFrame.from_ndarray(
3442
np.zeros((720, 1280, 3), dtype=np.uint8), format="rgb24"
3543
)
36-
for packet in stream.encode(frame):
37-
output_container.mux(packet)
44+
for stream in streams:
45+
for packet in stream.encode(frame):
46+
output_container.mux(packet)
3847

39-
for packet in stream.encode():
40-
output_container.mux(packet)
48+
for stream in streams:
49+
for packet in stream.encode():
50+
output_container.mux(packet)
4151

4252
output_container.close()
4353

0 commit comments

Comments
 (0)