Skip to content

Commit c5f2a54

Browse files
WyattBlueclaude
andcommitted
Fix #2149 Set colorspace and color_range ...
Set colorspace and color_range metadata on reformatted frames When VideoFrame.reformat() is called with explicit dst_colorspace or dst_color_range parameters, the output frame's metadata now correctly reflects these values. Previously, the swscale conversion was performed but the frame metadata was not updated. Changes: - Add SWS_CS_* to AVColorSpace mapping for correct metadata translation - Only set frame metadata when user explicitly specifies dst parameters - Preserve source frame metadata when dst params are not specified Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 5f0e312 commit c5f2a54

2 files changed

Lines changed: 42 additions & 1 deletion

File tree

av/video/reformatter.pxd

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ cdef class VideoReformatter:
1010
cdef _reformat(self, VideoFrame frame, int width, int height,
1111
lib.AVPixelFormat format, int src_colorspace,
1212
int dst_colorspace, int interpolation,
13-
int src_color_range, int dst_color_range)
13+
int src_color_range, int dst_color_range,
14+
bint set_dst_colorspace, bint set_dst_color_range)

av/video/reformatter.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,24 @@ def _resolve_enum_value(value, enum_class, default):
5858
raise ValueError(f"Cannot convert {value} to {enum_class.__name__}")
5959

6060

61+
# Mapping from SWS_CS_* (swscale colorspace) to AVColorSpace (frame metadata).
62+
# Note: SWS_CS_ITU601, SWS_CS_ITU624, SWS_CS_SMPTE170M, and SWS_CS_DEFAULT all have
63+
# the same value (5), so we map 5 -> AVCOL_SPC_SMPTE170M as the most common case.
64+
# SWS_CS_DEFAULT is handled specially by not setting frame metadata.
65+
_SWS_CS_TO_AVCOL_SPC: dict = {} # Populated at module load time
66+
67+
68+
def _init_colorspace_map():
69+
# Must be called after lib constants are available
70+
_SWS_CS_TO_AVCOL_SPC[lib.SWS_CS_ITU709] = lib.AVCOL_SPC_BT709
71+
_SWS_CS_TO_AVCOL_SPC[lib.SWS_CS_FCC] = lib.AVCOL_SPC_FCC
72+
_SWS_CS_TO_AVCOL_SPC[lib.SWS_CS_ITU601] = lib.AVCOL_SPC_SMPTE170M
73+
_SWS_CS_TO_AVCOL_SPC[lib.SWS_CS_SMPTE240M] = lib.AVCOL_SPC_SMPTE240M
74+
75+
76+
_init_colorspace_map()
77+
78+
6179
@cython.cclass
6280
class VideoReformatter:
6381
"""An object for reformatting size and pixel format of :class:`.VideoFrame`.
@@ -123,6 +141,10 @@ def reformat(
123141
dst_color_range, ColorRange, 0
124142
)
125143

144+
# Track whether user explicitly specified destination metadata
145+
set_dst_colorspace: cython.bint = dst_colorspace is not None
146+
set_dst_color_range: cython.bint = dst_color_range is not None
147+
126148
return self._reformat(
127149
frame,
128150
width or frame.ptr.width,
@@ -133,6 +155,8 @@ def reformat(
133155
c_interpolation,
134156
c_src_color_range,
135157
c_dst_color_range,
158+
set_dst_colorspace,
159+
set_dst_color_range,
136160
)
137161

138162
@cython.cfunc
@@ -147,10 +171,16 @@ def _reformat(
147171
interpolation: cython.int,
148172
src_color_range: cython.int,
149173
dst_color_range: cython.int,
174+
set_dst_colorspace: cython.bint,
175+
set_dst_color_range: cython.bint,
150176
):
151177
if frame.ptr.format < 0:
152178
raise ValueError("Frame does not have format set.")
153179

180+
# Save original values to set on the output frame (before swscale conversion)
181+
frame_dst_colorspace = dst_colorspace
182+
frame_dst_color_range = dst_color_range
183+
154184
# The definition of color range in pixfmt.h and swscale.h is different.
155185
src_color_range = 1 if src_color_range == ColorRange.JPEG.value else 0
156186
dst_color_range = 1 if dst_color_range == ColorRange.JPEG.value else 0
@@ -231,6 +261,16 @@ def _reformat(
231261
new_frame._copy_internal_attributes(frame)
232262
new_frame._init(dst_format, width, height)
233263

264+
# Set the colorspace and color_range on the output frame only if explicitly specified
265+
if set_dst_colorspace and frame_dst_colorspace in _SWS_CS_TO_AVCOL_SPC:
266+
new_frame.ptr.colorspace = cython.cast(
267+
lib.AVColorSpace, _SWS_CS_TO_AVCOL_SPC[frame_dst_colorspace]
268+
)
269+
if set_dst_color_range:
270+
new_frame.ptr.color_range = cython.cast(
271+
lib.AVColorRange, frame_dst_color_range
272+
)
273+
234274
with cython.nogil:
235275
lib.sws_scale(
236276
self.ptr,

0 commit comments

Comments
 (0)