Skip to content

Commit 24cd274

Browse files
committed
feat: add audio channels to presets
* Also fixes some issues with MPEG-DASH timings (uses avoid_negative_ts 'make_zero' instead of the asetpts filter). * Also adds some new methods to check for the FFmpeg and FFprobe binary versions. Refs: ARC-11152
1 parent a802096 commit 24cd274

9 files changed

Lines changed: 190 additions & 8 deletions

File tree

lib/ffmpeg.rb

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ def ffmpeg_binary=(path)
100100
end
101101

102102
@ffmpeg_binary = path
103+
@ffmpeg_version = nil
103104
end
104105

105106
# Get the path to the ffmpeg binary.
@@ -110,6 +111,27 @@ def ffmpeg_binary
110111
@ffmpeg_binary ||= which('ffmpeg')
111112
end
112113

114+
# Get the version of the ffmpeg binary.
115+
#
116+
# @return [String] The version string (e.g., "4.4.6", "8.0")
117+
def ffmpeg_version
118+
@ffmpeg_version ||= begin
119+
stdout, = FFMPEG::IO.capture3(ffmpeg_binary, '-version')
120+
stdout[/ffmpeg version (\d+\.\d+(?:\.\d+)?)/i, 1]
121+
end
122+
end
123+
124+
# Check if the ffmpeg version matches the given pattern.
125+
#
126+
# @param pattern [String, Regexp] The version pattern to match.
127+
# @return [Boolean] True if the ffmpeg version matches the pattern, false otherwise.
128+
def ffmpeg_version?(pattern)
129+
return false unless ffmpeg_version
130+
return pattern.match?(pattern) if pattern.is_a?(Regexp)
131+
132+
ffmpeg_version.start_with?(pattern.to_s)
133+
end
134+
113135
# Safely captures the standard output and the standard error of the ffmpeg command.
114136
#
115137
# @param args [Array<String>] The arguments to pass to ffmpeg.
@@ -198,6 +220,28 @@ def ffprobe_binary=(path)
198220
end
199221

200222
@ffprobe_binary = path
223+
@ffprobe_version = nil
224+
end
225+
226+
# Get the version of the ffprobe binary.
227+
#
228+
# @return [String] The version string (e.g., "4.4.6", "8.0")
229+
def ffprobe_version
230+
@ffprobe_version ||= begin
231+
stdout, = FFMPEG::IO.capture3(ffprobe_binary, '-version')
232+
stdout[/ffprobe version (\d+\.\d+(?:\.\d+)?)/i, 1]
233+
end
234+
end
235+
236+
# Check if the ffprobe version matches the given pattern.
237+
#
238+
# @param pattern [String, Regexp] The version pattern to match.
239+
# @return [Boolean] True if the ffprobe version matches the pattern, false otherwise.
240+
def ffprobe_version?(pattern)
241+
return false unless ffprobe_version
242+
return pattern.match?(ffprobe_version) if pattern.is_a?(Regexp)
243+
244+
ffprobe_version.start_with?(pattern.to_s)
201245
end
202246

203247
# Safely captures the standard output and the standard error of the ffmpeg command.

lib/ffmpeg/presets/aac.rb

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ def aac_128k(
1313
metadata: nil,
1414
threads: FFMPEG.threads,
1515
audio_sample_rate: 48_000,
16+
audio_channels: 2,
1617
&
1718
)
1819
AAC.new(
@@ -21,6 +22,7 @@ def aac_128k(
2122
metadata:,
2223
threads:,
2324
audio_sample_rate:,
25+
audio_channels:,
2426
audio_bit_rate: '128k',
2527
&
2628
)
@@ -32,6 +34,7 @@ def aac_192k(
3234
metadata: nil,
3335
threads: FFMPEG.threads,
3436
audio_sample_rate: 48_000,
37+
audio_channels: 2,
3538
&
3639
)
3740
AAC.new(
@@ -40,6 +43,7 @@ def aac_192k(
4043
metadata:,
4144
threads:,
4245
audio_sample_rate:,
46+
audio_channels:,
4347
audio_bit_rate: '192k',
4448
&
4549
)
@@ -51,6 +55,7 @@ def aac_320k(
5155
metadata: nil,
5256
threads: FFMPEG.threads,
5357
audio_sample_rate: 48_000,
58+
audio_channels: 2,
5459
&
5560
)
5661
AAC.new(
@@ -59,6 +64,7 @@ def aac_320k(
5964
metadata:,
6065
threads:,
6166
audio_sample_rate:,
67+
audio_channels:,
6268
audio_bit_rate: '320k',
6369
&
6470
)
@@ -67,12 +73,14 @@ def aac_320k(
6773

6874
# Preset to encode AAC audio files.
6975
class AAC < Preset
70-
attr_reader :threads, :audio_bit_rate, :audio_sample_rate
76+
attr_reader :threads, :audio_bit_rate, :audio_sample_rate, :audio_channels
7177

7278
# @param name [String] The name of the preset.
7379
# @param filename [String] The filename format of the output.
7480
# @param metadata [Object] The metadata to associate with the preset.
7581
# @param audio_bit_rate [String] The audio bit rate to use.
82+
# @param audio_sample_rate [Integer] The audio sample rate to use.
83+
# @param audio_channels [Integer, nil] The number of audio channels to use (nil to preserve source).
7684
# @yield The block to execute to compose the command arguments.
7785
def initialize(
7886
name: nil,
@@ -81,11 +89,13 @@ def initialize(
8189
threads: FFMPEG.threads,
8290
audio_bit_rate: '128k',
8391
audio_sample_rate: 48_000,
92+
audio_channels: 2,
8493
&
8594
)
8695
@threads = threads
8796
@audio_bit_rate = audio_bit_rate
8897
@audio_sample_rate = audio_sample_rate
98+
@audio_channels = audio_channels
8999
preset = self
90100

91101
super(name:, filename:, metadata:) do
@@ -101,6 +111,7 @@ def initialize(
101111
map media.audio_mapping_id do
102112
audio_bit_rate preset.audio_bit_rate
103113
audio_sample_rate preset.audio_sample_rate
114+
audio_channels preset.audio_channels if preset.audio_channels
104115
end
105116
end
106117
end

lib/ffmpeg/presets/dash.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ def initialize(
3131
use_template 1
3232
use_timeline 1
3333
segment_duration preset.segment_duration
34+
avoid_negative_ts 'make_zero'
3435

3536
muxing_flags 'frag_keyframe+empty_moov+default_base_moof'
3637
map_chapters '-1'

lib/ffmpeg/presets/dash/aac.rb

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ def aac_128k(
1515
threads: FFMPEG.threads,
1616
segment_duration: 4,
1717
audio_sample_rate: 48_000,
18+
audio_channels: 2,
1819
&
1920
)
2021
AAC.new(
@@ -24,6 +25,7 @@ def aac_128k(
2425
threads:,
2526
segment_duration:,
2627
audio_sample_rate:,
28+
audio_channels:,
2729
audio_bit_rate: '128k',
2830
&
2931
)
@@ -36,6 +38,7 @@ def aac_192k(
3638
threads: FFMPEG.threads,
3739
segment_duration: 4,
3840
audio_sample_rate: 48_000,
41+
audio_channels: 2,
3942
&
4043
)
4144
AAC.new(
@@ -45,6 +48,7 @@ def aac_192k(
4548
threads:,
4649
segment_duration:,
4750
audio_sample_rate:,
51+
audio_channels:,
4852
audio_bit_rate: '192k',
4953
&
5054
)
@@ -57,6 +61,7 @@ def aac_320k(
5761
threads: FFMPEG.threads,
5862
segment_duration: 4,
5963
audio_sample_rate: 48_000,
64+
audio_channels: 2,
6065
&
6166
)
6267
AAC.new(
@@ -66,6 +71,7 @@ def aac_320k(
6671
threads:,
6772
segment_duration:,
6873
audio_sample_rate:,
74+
audio_channels:,
6975
audio_bit_rate: '320k',
7076
&
7177
)
@@ -74,12 +80,14 @@ def aac_320k(
7480

7581
# Preset to encode DASH AAC audio files.
7682
class AAC < DASH
77-
attr_reader :audio_bit_rate, :audio_sample_rate
83+
attr_reader :audio_bit_rate, :audio_sample_rate, :audio_channels
7884

7985
# @param name [String] The name of the preset.
8086
# @param filename [String] The filename format of the output.
8187
# @param metadata [Object] The metadata to associate with the preset.
8288
# @param audio_bit_rate [String] The audio bit rate to use.
89+
# @param audio_sample_rate [Integer] The audio sample rate to use.
90+
# @param audio_channels [Integer, nil] The number of audio channels to use (nil to preserve source).
8391
# @yield The block to execute to compose the command arguments.
8492
def initialize(
8593
name: nil,
@@ -89,10 +97,12 @@ def initialize(
8997
segment_duration: 4,
9098
audio_bit_rate: '128k',
9199
audio_sample_rate: 48_000,
100+
audio_channels: 2,
92101
&
93102
)
94103
@audio_bit_rate = audio_bit_rate
95104
@audio_sample_rate = audio_sample_rate
105+
@audio_channels = audio_channels
96106
preset = self
97107

98108
super(
@@ -111,6 +121,7 @@ def initialize(
111121
map media.audio_mapping_id do
112122
audio_bit_rate preset.audio_bit_rate
113123
audio_sample_rate preset.audio_sample_rate
124+
audio_channels preset.audio_channels if preset.audio_channels
114125
end
115126
end
116127
end

lib/ffmpeg/presets/dash/h264.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -308,10 +308,9 @@ def initialize(
308308
end
309309

310310
map media.audio_mapping_id do
311-
# Reset the audio stream's timestamps to start from 0.
312-
filter Filter.new(:audio, 'asetpts', expr: 'PTS-STARTPTS')
313311
audio_bit_rate h264_presets.first.audio_bit_rate
314312
audio_sample_rate h264_presets.first.audio_sample_rate
313+
audio_channels h264_presets.first.audio_channels if h264_presets.first.audio_channels
315314
end
316315
end
317316
end

0 commit comments

Comments
 (0)