Skip to content

Commit db2f244

Browse files
committed
feat!: add support for using zscale in the scale filter
BREAKING CHANGE: The initializer signature of the Scale filter changed significantly.
1 parent 6694696 commit db2f244

6 files changed

Lines changed: 171 additions & 49 deletions

File tree

lib/ffmpeg/filters/scale.rb

Lines changed: 109 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,42 @@
55
module FFMPEG
66
module Filters # rubocop:disable Style/Documentation
77
class << self
8-
def scale(width: nil, height: nil, force_original_aspect_ratio: nil, flags: nil)
9-
Scale.new(width:, height:, force_original_aspect_ratio:, flags:)
8+
def scale(
9+
zlib: false,
10+
width: nil,
11+
height: nil,
12+
algorithm: nil,
13+
in_color_space: nil,
14+
out_color_space: nil,
15+
in_color_range: nil,
16+
out_color_range: nil,
17+
in_color_primaries: nil,
18+
out_color_primaries: nil,
19+
in_color_transfer: nil,
20+
out_color_transfer: nil,
21+
in_chroma_location: nil,
22+
out_chroma_location: nil
23+
)
24+
Scale.new(
25+
zlib:,
26+
width:,
27+
height:,
28+
algorithm:,
29+
in_color_space:,
30+
out_color_space:,
31+
in_color_range:,
32+
out_color_range:,
33+
in_color_primaries:,
34+
out_color_primaries:,
35+
in_color_transfer:,
36+
out_color_transfer:,
37+
in_chroma_location:,
38+
out_chroma_location:
39+
)
1040
end
1141
end
1242

13-
# The Scale class uses the scale filter
43+
# The Scale class uses the scale (or zscale) filter
1444
# to resize a multimedia stream.
1545
class Scale < Filter
1646
NEAREST_DIMENSION = -1
@@ -25,7 +55,7 @@ class << self
2555
# @param max_width [Numeric] The maximum width to fit.
2656
# @param max_height [Numeric] The maximum height to fit.
2757
# @return [FFMPEG::Filters::Scale] The scale filter.
28-
def contained(media, max_width: nil, max_height: nil)
58+
def contained(media, max_width: nil, max_height: nil, **kwargs)
2959
unless media.is_a?(FFMPEG::Media)
3060
raise ArgumentError,
3161
"Unknown media format #{media.class}, expected #{FFMPEG::Media}"
@@ -52,18 +82,38 @@ def contained(media, max_width: nil, max_height: nil)
5282
end
5383

5484
if width.negative? || height.negative?
55-
Filters.scale(width:, height:)
85+
new(width:, height:, **kwargs)
5686
elsif media.calculated_aspect_ratio > Rational(width, height)
57-
Filters.scale(width:, height: -2)
87+
new(width:, height: -2, **kwargs)
5888
else
59-
Filters.scale(width: -2, height:)
89+
new(width: -2, height:, **kwargs)
6090
end
6191
end
6292
end
6393

64-
attr_reader :width, :height, :force_original_aspect_ratio, :flags
65-
66-
def initialize(width: nil, height: nil, force_original_aspect_ratio: nil, flags: nil)
94+
attr_reader :width, :height, :algorithm,
95+
:in_color_space, :out_color_space,
96+
:in_color_range, :out_color_range,
97+
:in_color_primaries, :out_color_primaries,
98+
:in_color_transfer, :out_color_transfer,
99+
:in_chroma_location, :out_chroma_location
100+
101+
def initialize(
102+
zlib: false,
103+
width: nil,
104+
height: nil,
105+
algorithm: nil,
106+
in_color_space: nil,
107+
out_color_space: nil,
108+
in_color_range: nil,
109+
out_color_range: nil,
110+
in_color_primaries: nil,
111+
out_color_primaries: nil,
112+
in_color_transfer: nil,
113+
out_color_transfer: nil,
114+
in_chroma_location: nil,
115+
out_chroma_location: nil
116+
)
67117
if !width.nil? && !width.is_a?(Numeric) && !width.is_a?(String)
68118
raise ArgumentError, "Unknown width format #{width.class}, expected #{Numeric} or #{String}"
69119
end
@@ -72,32 +122,63 @@ def initialize(width: nil, height: nil, force_original_aspect_ratio: nil, flags:
72122
raise ArgumentError, "Unknown height format #{height.class}, expected #{Numeric} or #{String}"
73123
end
74124

75-
if !force_original_aspect_ratio.nil? && !force_original_aspect_ratio.is_a?(String)
76-
raise ArgumentError,
77-
"Unknown force_original_aspect_ratio format #{force_original_aspect_ratio.class}, expected #{String}"
78-
end
79-
80-
if !flags.nil? && !flags.is_a?(Array)
81-
raise ArgumentError, "Unknown flags format #{flags.class}, expected #{Array}"
82-
end
83-
84125
@width = width
85126
@height = height
86-
@force_original_aspect_ratio = force_original_aspect_ratio
87-
@flags = flags
127+
@algorithm = algorithm
128+
@in_color_space = in_color_space
129+
@out_color_space = out_color_space
130+
@in_color_range = in_color_range
131+
@out_color_range = out_color_range
132+
@in_color_primaries = in_color_primaries
133+
@out_color_primaries = out_color_primaries
134+
@in_color_transfer = in_color_transfer
135+
@out_color_transfer = out_color_transfer
136+
@in_chroma_location = in_chroma_location
137+
@out_chroma_location = out_chroma_location
138+
139+
super(:video, zlib ? 'zscale' : 'scale')
140+
end
88141

89-
super(:video, 'scale')
142+
def zlib?
143+
@name.start_with?('z')
90144
end
91145

92146
protected
93147

94148
def format_kwargs
95-
super(
96-
w: @width,
97-
h: @height,
98-
force_original_aspect_ratio: @force_original_aspect_ratio,
99-
flags: @flags
100-
)
149+
if zlib?
150+
super(
151+
w: @width,
152+
h: @height,
153+
f: @algorithm,
154+
min: @in_color_space,
155+
m: @out_color_space,
156+
rin: @in_color_range,
157+
r: @out_color_range,
158+
pin: @in_color_primaries,
159+
p: @out_color_primaries,
160+
tin: @in_color_transfer,
161+
t: @out_color_transfer,
162+
cin: @in_chroma_location,
163+
c: @out_chroma_location
164+
)
165+
else
166+
super(
167+
w: @width,
168+
h: @height,
169+
flags: @algorithm && [@algorithm],
170+
in_color_matrix: @in_color_space,
171+
out_color_matrix: @out_color_space,
172+
in_range: @in_color_range,
173+
out_range: @out_color_range,
174+
in_primaries: @in_color_primaries,
175+
out_primaries: @out_color_primaries,
176+
in_transfer: @in_color_transfer,
177+
out_transfer: @out_color_transfer,
178+
in_chroma_loc: @in_chroma_location,
179+
out_chroma_loc: @out_chroma_location
180+
)
181+
end
101182
end
102183
end
103184
end

lib/ffmpeg/media.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,15 @@ def local?
190190
@default_video_stream = video_streams.find(&:default?) || video_streams.first
191191
end
192192

193+
# Whether the media is HDR (High Dynamic Range).
194+
#
195+
# @return [Boolean]
196+
autoload def hdr?
197+
default_video_stream&.color_primaries == 'bt2020' &&
198+
default_video_stream&.color_space == 'bt2020nc' &&
199+
%w[smpte2084 arib-std-b67].include?(default_video_stream&.color_transfer)
200+
end
201+
193202
# Whether the media is rotated (based on the default video stream).
194203
# (e.g. 90°, 180°, 270°)
195204
#
@@ -282,6 +291,13 @@ def local?
282291
default_video_stream&.calculated_pixel_aspect_ratio
283292
end
284293

294+
# Returns the pixel format of the default video stream (if any).
295+
#
296+
# @return [String, nil]
297+
autoload def pixel_format
298+
default_video_stream&.pixel_format
299+
end
300+
285301
# Returns the color range of the default video stream (if any).
286302
#
287303
# @return [String, nil]

lib/ffmpeg/presets/h264.rb

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ def h264_144p(
1919
frame_rate: 30,
2020
constant_rate_factor: 28,
2121
pixel_format: 'yuv420p',
22+
zlib: true,
2223
&
2324
)
2425
H264.new(
@@ -34,6 +35,7 @@ def h264_144p(
3435
pixel_format:,
3536
max_width: 256,
3637
max_height: 144,
38+
zlib:,
3739
&
3840
)
3941
end
@@ -49,6 +51,7 @@ def h264_240p(
4951
frame_rate: 30,
5052
constant_rate_factor: 28,
5153
pixel_format: 'yuv420p',
54+
zlib: true,
5255
&
5356
)
5457
H264.new(
@@ -64,6 +67,7 @@ def h264_240p(
6467
pixel_format:,
6568
max_width: 426,
6669
max_height: 240,
70+
zlib:,
6771
&
6872
)
6973
end
@@ -79,6 +83,7 @@ def h264_360p(
7983
frame_rate: 30,
8084
constant_rate_factor: 28,
8185
pixel_format: 'yuv420p',
86+
zlib: true,
8287
&
8388
)
8489
H264.new(
@@ -94,6 +99,7 @@ def h264_360p(
9499
pixel_format:,
95100
max_width: 640,
96101
max_height: 360,
102+
zlib:,
97103
&
98104
)
99105
end
@@ -109,6 +115,7 @@ def h264_480p(
109115
frame_rate: 30,
110116
constant_rate_factor: 27,
111117
pixel_format: 'yuv420p',
118+
zlib: true,
112119
&
113120
)
114121
H264.new(
@@ -124,6 +131,7 @@ def h264_480p(
124131
pixel_format:,
125132
max_width: 854,
126133
max_height: 480,
134+
zlib:,
127135
&
128136
)
129137
end
@@ -139,6 +147,7 @@ def h264_720p(
139147
frame_rate: 60,
140148
constant_rate_factor: 27,
141149
pixel_format: 'yuv420p',
150+
zlib: true,
142151
&
143152
)
144153
H264.new(
@@ -154,6 +163,7 @@ def h264_720p(
154163
pixel_format:,
155164
max_width: 1280,
156165
max_height: 720,
166+
zlib:,
157167
&
158168
)
159169
end
@@ -169,6 +179,7 @@ def h264_1080p(
169179
frame_rate: 60,
170180
constant_rate_factor: 27,
171181
pixel_format: 'yuv420p',
182+
zlib: true,
172183
&
173184
)
174185
H264.new(
@@ -184,6 +195,7 @@ def h264_1080p(
184195
pixel_format:,
185196
max_width: 1920,
186197
max_height: 1080,
198+
zlib:,
187199
&
188200
)
189201
end
@@ -199,6 +211,7 @@ def h264_1440p(
199211
frame_rate: 60,
200212
constant_rate_factor: 26,
201213
pixel_format: 'yuv420p',
214+
zlib: true,
202215
&
203216
)
204217
H264.new(
@@ -214,6 +227,7 @@ def h264_1440p(
214227
pixel_format:,
215228
max_width: 2560,
216229
max_height: 1440,
230+
zlib:,
217231
&
218232
)
219233
end
@@ -229,6 +243,7 @@ def h264_4k(
229243
frame_rate: 60,
230244
constant_rate_factor: 26,
231245
pixel_format: 'yuv420p',
246+
zlib: true,
232247
&
233248
)
234249
H264.new(
@@ -244,6 +259,7 @@ def h264_4k(
244259
pixel_format:,
245260
max_width: 3840,
246261
max_height: 2160,
262+
zlib:,
247263
&
248264
)
249265
end
@@ -266,6 +282,7 @@ class H264 < Preset
266282
# @param pixel_format [String] The pixel format to use.
267283
# @param max_width [Integer] The maximum width of the video.
268284
# @param max_height [Integer] The maximum height of the video.
285+
# @param zlib [Boolean] Whether to use zlib for the scale filter.
269286
# @yield The block to execute to compose the command arguments.
270287
def initialize(
271288
name: nil,
@@ -280,6 +297,7 @@ def initialize(
280297
pixel_format: 'yuv420p',
281298
max_width: nil,
282299
max_height: nil,
300+
zlib: true,
283301
&
284302
)
285303
if max_width && !max_width.is_a?(Numeric)
@@ -299,6 +317,7 @@ def initialize(
299317
@pixel_format = pixel_format
300318
@max_width = max_width
301319
@max_height = max_height
320+
@zlib = zlib
302321
preset = self
303322

304323
super(name:, filename:, metadata:) do
@@ -350,7 +369,7 @@ def format_filter
350369
def scale_filter(media)
351370
return unless @max_width || @max_height
352371

353-
Filters::Scale.contained(media, max_width: @max_width, max_height: @max_height)
372+
Filters::Scale.contained(media, zlib: @zlib, max_width: @max_width, max_height: @max_height)
354373
end
355374
end
356375
end

0 commit comments

Comments
 (0)