diff --git a/CHANGELOG b/CHANGELOG index b98adf7..9e58f49 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,10 @@ +== 8.1.0-beta.4 2025-10-10 + +Improvements: +* Added audio sample rate adjustment to built-in presets. +* Audio sample rates now work similarly to frame rates - when the input has a lower sample rate than the target, the closest standard sample rate is used. +* Standard audio sample rates supported: 8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000 Hz. + == 8.1.0-beta.3 2025-09-30 Improvements: diff --git a/lib/ffmpeg/command_args.rb b/lib/ffmpeg/command_args.rb index cf6dce5..d0b5023 100644 --- a/lib/ffmpeg/command_args.rb +++ b/lib/ffmpeg/command_args.rb @@ -16,6 +16,10 @@ module FFMPEG # args.to_s # "-map 0:v:0 -c:v:0 libx264 -r 30" class CommandArgs < RawCommandArgs STANDARD_FRAME_RATES = [12, 24, 25, 30, 50, 60, 90, 120, 240].freeze + STANDARD_AUDIO_SAMPLE_RATES = [ + 8000, 11_025, 16_000, 22_050, 32_000, 44_100, + 48_000, 88_200, 96_000, 176_400, 192_000 + ].freeze class << self # Composes a new instance of CommandArgs with the given media. @@ -95,12 +99,23 @@ def audio_bit_rate(target_value, **kwargs) super(adjusted_audio_bit_rate(target_value), **kwargs) end + # Sets the audio sample rate to the minimum of the current audio sample rate and the target value. + # + # @param target_value [Integer] The target sample rate. + # @return [self] + def audio_sample_rate(target_value) + return self if target_value.nil? + + super(adjusted_audio_sample_rate(target_value)) + end + # Returns the minimum of the current frame rate and the target value. # # @param target_value [Integer, Float] The target frame rate. # @return [Numeric] def adjusted_frame_rate(target_value) return target_value if media.frame_rate.nil? + return target_value if media.frame_rate <= 0 return target_value if media.frame_rate > target_value STANDARD_FRAME_RATES.min_by { (_1 - media.frame_rate).abs } @@ -126,6 +141,20 @@ def adjusted_audio_bit_rate(target_value) min_bit_rate(media.audio_bit_rate, target_value) end + # Returns the minimum of the current audio sample rate and the target value. + # Returns the target value if the current sample rate is nil or zero/negative. + # If the media sample rate is lower than the target, returns the closest standard sample rate. + # + # @param target_value [Integer] The target audio sample rate. + # @return [Integer] + def adjusted_audio_sample_rate(target_value) + return target_value if media.audio_sample_rate.nil? + return target_value if media.audio_sample_rate <= 0 + return target_value if media.audio_sample_rate > target_value + + STANDARD_AUDIO_SAMPLE_RATES.min_by { (_1 - media.audio_sample_rate).abs } + end + private def min_bit_rate(*values) diff --git a/lib/ffmpeg/presets/aac.rb b/lib/ffmpeg/presets/aac.rb index af7a4fc..db098d5 100644 --- a/lib/ffmpeg/presets/aac.rb +++ b/lib/ffmpeg/presets/aac.rb @@ -12,6 +12,7 @@ def aac_128k( filename: '%s.m4a', metadata: nil, threads: FFMPEG.threads, + audio_sample_rate: 48_000, & ) AAC.new( @@ -19,6 +20,7 @@ def aac_128k( filename:, metadata:, threads:, + audio_sample_rate:, audio_bit_rate: '128k', & ) @@ -29,6 +31,7 @@ def aac_192k( filename: '%s.m4a', metadata: nil, threads: FFMPEG.threads, + audio_sample_rate: 48_000, & ) AAC.new( @@ -36,6 +39,7 @@ def aac_192k( filename:, metadata:, threads:, + audio_sample_rate:, audio_bit_rate: '192k', & ) @@ -46,6 +50,7 @@ def aac_320k( filename: '%s.m4a', metadata: nil, threads: FFMPEG.threads, + audio_sample_rate: 48_000, & ) AAC.new( @@ -53,6 +58,7 @@ def aac_320k( filename:, metadata:, threads:, + audio_sample_rate:, audio_bit_rate: '320k', & ) @@ -61,7 +67,7 @@ def aac_320k( # Preset to encode AAC audio files. class AAC < Preset - attr_reader :threads, :audio_bit_rate + attr_reader :threads, :audio_bit_rate, :audio_sample_rate # @param name [String] The name of the preset. # @param filename [String] The filename format of the output. @@ -74,10 +80,12 @@ def initialize( metadata: nil, threads: FFMPEG.threads, audio_bit_rate: '128k', + audio_sample_rate: 48_000, & ) @threads = threads @audio_bit_rate = audio_bit_rate + @audio_sample_rate = audio_sample_rate preset = self super(name:, filename:, metadata:) do @@ -92,6 +100,7 @@ def initialize( map media.audio_mapping_id do audio_bit_rate preset.audio_bit_rate + audio_sample_rate preset.audio_sample_rate end end end diff --git a/lib/ffmpeg/presets/dash/aac.rb b/lib/ffmpeg/presets/dash/aac.rb index 13546bc..67dfa5b 100644 --- a/lib/ffmpeg/presets/dash/aac.rb +++ b/lib/ffmpeg/presets/dash/aac.rb @@ -14,6 +14,7 @@ def aac_128k( metadata: nil, threads: FFMPEG.threads, segment_duration: 4, + audio_sample_rate: 48_000, & ) AAC.new( @@ -22,6 +23,7 @@ def aac_128k( metadata:, threads:, segment_duration:, + audio_sample_rate:, audio_bit_rate: '128k', & ) @@ -33,6 +35,7 @@ def aac_192k( metadata: nil, threads: FFMPEG.threads, segment_duration: 4, + audio_sample_rate: 48_000, & ) AAC.new( @@ -41,6 +44,7 @@ def aac_192k( metadata:, threads:, segment_duration:, + audio_sample_rate:, audio_bit_rate: '192k', & ) @@ -52,6 +56,7 @@ def aac_320k( metadata: nil, threads: FFMPEG.threads, segment_duration: 4, + audio_sample_rate: 48_000, & ) AAC.new( @@ -60,6 +65,7 @@ def aac_320k( metadata:, threads:, segment_duration:, + audio_sample_rate:, audio_bit_rate: '320k', & ) @@ -68,7 +74,7 @@ def aac_320k( # Preset to encode DASH AAC audio files. class AAC < DASH - attr_reader :audio_bit_rate + attr_reader :audio_bit_rate, :audio_sample_rate # @param name [String] The name of the preset. # @param filename [String] The filename format of the output. @@ -82,9 +88,11 @@ def initialize( threads: FFMPEG.threads, segment_duration: 4, audio_bit_rate: '128k', + audio_sample_rate: 48_000, & ) @audio_bit_rate = audio_bit_rate + @audio_sample_rate = audio_sample_rate preset = self super( @@ -102,6 +110,7 @@ def initialize( map media.audio_mapping_id do audio_bit_rate preset.audio_bit_rate + audio_sample_rate preset.audio_sample_rate end end end diff --git a/lib/ffmpeg/presets/dash/h264.rb b/lib/ffmpeg/presets/dash/h264.rb index 7c7089b..7330d5b 100644 --- a/lib/ffmpeg/presets/dash/h264.rb +++ b/lib/ffmpeg/presets/dash/h264.rb @@ -20,6 +20,7 @@ def h264_360p( segment_duration: 4, keyframe_interval: 2, audio_bit_rate: '128k', + audio_sample_rate: 48_000, frame_rate: 30, ld_frame_rate: 24, & @@ -32,11 +33,11 @@ def h264_360p( segment_duration:, keyframe_interval:, h264_presets: [ - Presets.h264_360p(audio_bit_rate:, frame_rate:) + Presets.h264_360p(audio_bit_rate:, audio_sample_rate:, frame_rate:) ], ld_h264_presets: [ - Presets.h264_240p(audio_bit_rate:, frame_rate: ld_frame_rate), - Presets.h264_144p(audio_bit_rate:, frame_rate: ld_frame_rate) + Presets.h264_240p(audio_bit_rate:, audio_sample_rate:, frame_rate: ld_frame_rate), + Presets.h264_144p(audio_bit_rate:, audio_sample_rate:, frame_rate: ld_frame_rate) ], & ) @@ -50,6 +51,7 @@ def h264_480p( segment_duration: 4, keyframe_interval: 2, audio_bit_rate: '128k', + audio_sample_rate: 48_000, frame_rate: 30, ld_frame_rate: 24, & @@ -62,12 +64,12 @@ def h264_480p( segment_duration:, keyframe_interval:, h264_presets: [ - Presets.h264_480p(audio_bit_rate:, frame_rate:), - Presets.h264_360p(audio_bit_rate:, frame_rate:) + Presets.h264_480p(audio_bit_rate:, audio_sample_rate:, frame_rate:), + Presets.h264_360p(audio_bit_rate:, audio_sample_rate:, frame_rate:) ], ld_h264_presets: [ - Presets.h264_240p(audio_bit_rate:, frame_rate: ld_frame_rate), - Presets.h264_144p(audio_bit_rate:, frame_rate: ld_frame_rate) + Presets.h264_240p(audio_bit_rate:, audio_sample_rate:, frame_rate: ld_frame_rate), + Presets.h264_144p(audio_bit_rate:, audio_sample_rate:, frame_rate: ld_frame_rate) ], & ) @@ -81,6 +83,7 @@ def h264_720p( segment_duration: 4, keyframe_interval: 2, audio_bit_rate: '128k', + audio_sample_rate: 48_000, ld_frame_rate: 24, sd_frame_rate: 30, hd_frame_rate: 30, @@ -94,13 +97,13 @@ def h264_720p( keyframe_interval:, segment_duration:, h264_presets: [ - Presets.h264_720p(audio_bit_rate:, frame_rate: hd_frame_rate), - Presets.h264_480p(audio_bit_rate:, frame_rate: sd_frame_rate), - Presets.h264_360p(audio_bit_rate:, frame_rate: sd_frame_rate) + Presets.h264_720p(audio_bit_rate:, audio_sample_rate:, frame_rate: hd_frame_rate), + Presets.h264_480p(audio_bit_rate:, audio_sample_rate:, frame_rate: sd_frame_rate), + Presets.h264_360p(audio_bit_rate:, audio_sample_rate:, frame_rate: sd_frame_rate) ], ld_h264_presets: [ - Presets.h264_240p(audio_bit_rate:, frame_rate: ld_frame_rate), - Presets.h264_144p(audio_bit_rate:, frame_rate: ld_frame_rate) + Presets.h264_240p(audio_bit_rate:, audio_sample_rate:, frame_rate: ld_frame_rate), + Presets.h264_144p(audio_bit_rate:, audio_sample_rate:, frame_rate: ld_frame_rate) ], & ) @@ -114,6 +117,7 @@ def h264_1080p( segment_duration: 4, keyframe_interval: 2, audio_bit_rate: '128k', + audio_sample_rate: 48_000, ld_frame_rate: 24, sd_frame_rate: 30, hd_frame_rate: 30, @@ -127,14 +131,14 @@ def h264_1080p( keyframe_interval:, segment_duration:, h264_presets: [ - Presets.h264_1080p(audio_bit_rate:, frame_rate: hd_frame_rate), - Presets.h264_720p(audio_bit_rate:, frame_rate: hd_frame_rate), - Presets.h264_480p(audio_bit_rate:, frame_rate: sd_frame_rate), - Presets.h264_360p(audio_bit_rate:, frame_rate: sd_frame_rate) + Presets.h264_1080p(audio_bit_rate:, audio_sample_rate:, frame_rate: hd_frame_rate), + Presets.h264_720p(audio_bit_rate:, audio_sample_rate:, frame_rate: hd_frame_rate), + Presets.h264_480p(audio_bit_rate:, audio_sample_rate:, frame_rate: sd_frame_rate), + Presets.h264_360p(audio_bit_rate:, audio_sample_rate:, frame_rate: sd_frame_rate) ], ld_h264_presets: [ - Presets.h264_240p(audio_bit_rate:, frame_rate: ld_frame_rate), - Presets.h264_144p(audio_bit_rate:, frame_rate: ld_frame_rate) + Presets.h264_240p(audio_bit_rate:, audio_sample_rate:, frame_rate: ld_frame_rate), + Presets.h264_144p(audio_bit_rate:, audio_sample_rate:, frame_rate: ld_frame_rate) ], & ) @@ -148,6 +152,7 @@ def h264_1440p( segment_duration: 4, keyframe_interval: 2, audio_bit_rate: '128k', + audio_sample_rate: 48_000, ld_frame_rate: 24, sd_frame_rate: 30, hd_frame_rate: 30, @@ -161,15 +166,15 @@ def h264_1440p( keyframe_interval:, segment_duration:, h264_presets: [ - Presets.h264_1440p(audio_bit_rate:, frame_rate: hd_frame_rate), - Presets.h264_1080p(audio_bit_rate:, frame_rate: hd_frame_rate), - Presets.h264_720p(audio_bit_rate:, frame_rate: hd_frame_rate), - Presets.h264_480p(audio_bit_rate:, frame_rate: sd_frame_rate), - Presets.h264_360p(audio_bit_rate:, frame_rate: sd_frame_rate) + Presets.h264_1440p(audio_bit_rate:, audio_sample_rate:, frame_rate: hd_frame_rate), + Presets.h264_1080p(audio_bit_rate:, audio_sample_rate:, frame_rate: hd_frame_rate), + Presets.h264_720p(audio_bit_rate:, audio_sample_rate:, frame_rate: hd_frame_rate), + Presets.h264_480p(audio_bit_rate:, audio_sample_rate:, frame_rate: sd_frame_rate), + Presets.h264_360p(audio_bit_rate:, audio_sample_rate:, frame_rate: sd_frame_rate) ], ld_h264_presets: [ - Presets.h264_240p(audio_bit_rate:, frame_rate: ld_frame_rate), - Presets.h264_144p(audio_bit_rate:, frame_rate: ld_frame_rate) + Presets.h264_240p(audio_bit_rate:, audio_sample_rate:, frame_rate: ld_frame_rate), + Presets.h264_144p(audio_bit_rate:, audio_sample_rate:, frame_rate: ld_frame_rate) ], & ) @@ -183,6 +188,7 @@ def h264_4k( segment_duration: 4, keyframe_interval: 2, audio_bit_rate: '128k', + audio_sample_rate: 48_000, ld_frame_rate: 24, sd_frame_rate: 30, hd_frame_rate: 60, @@ -197,16 +203,16 @@ def h264_4k( segment_duration:, keyframe_interval:, h264_presets: [ - Presets.h264_4k(audio_bit_rate:, frame_rate: uhd_frame_rate), - Presets.h264_1440p(audio_bit_rate:, frame_rate: hd_frame_rate), - Presets.h264_1080p(audio_bit_rate:, frame_rate: hd_frame_rate), - Presets.h264_720p(audio_bit_rate:, frame_rate: hd_frame_rate), - Presets.h264_480p(audio_bit_rate:, frame_rate: sd_frame_rate), - Presets.h264_360p(audio_bit_rate:, frame_rate: sd_frame_rate) + Presets.h264_4k(audio_bit_rate:, audio_sample_rate:, frame_rate: uhd_frame_rate), + Presets.h264_1440p(audio_bit_rate:, audio_sample_rate:, frame_rate: hd_frame_rate), + Presets.h264_1080p(audio_bit_rate:, audio_sample_rate:, frame_rate: hd_frame_rate), + Presets.h264_720p(audio_bit_rate:, audio_sample_rate:, frame_rate: hd_frame_rate), + Presets.h264_480p(audio_bit_rate:, audio_sample_rate:, frame_rate: sd_frame_rate), + Presets.h264_360p(audio_bit_rate:, audio_sample_rate:, frame_rate: sd_frame_rate) ], ld_h264_presets: [ - Presets.h264_240p(audio_bit_rate:, frame_rate: ld_frame_rate), - Presets.h264_144p(audio_bit_rate:, frame_rate: ld_frame_rate) + Presets.h264_240p(audio_bit_rate:, audio_sample_rate:, frame_rate: ld_frame_rate), + Presets.h264_144p(audio_bit_rate:, audio_sample_rate:, frame_rate: ld_frame_rate) ], & ) @@ -305,6 +311,7 @@ def initialize( # Reset the audio stream's timestamps to start from 0. filter Filter.new(:audio, 'asetpts', expr: 'PTS-STARTPTS') audio_bit_rate h264_presets.first.audio_bit_rate + audio_sample_rate h264_presets.first.audio_sample_rate end end end diff --git a/lib/ffmpeg/presets/h264.rb b/lib/ffmpeg/presets/h264.rb index 3a7a087..1b41145 100644 --- a/lib/ffmpeg/presets/h264.rb +++ b/lib/ffmpeg/presets/h264.rb @@ -14,6 +14,7 @@ def h264_144p( metadata: nil, threads: FFMPEG.threads, audio_bit_rate: '128k', + audio_sample_rate: 48_000, video_preset: 'ultrafast', video_profile: 'baseline', frame_rate: 30, @@ -27,6 +28,7 @@ def h264_144p( metadata:, threads:, audio_bit_rate:, + audio_sample_rate:, video_preset:, video_profile:, frame_rate:, @@ -44,6 +46,7 @@ def h264_240p( metadata: nil, threads: FFMPEG.threads, audio_bit_rate: '128k', + audio_sample_rate: 48_000, video_preset: 'ultrafast', video_profile: 'baseline', frame_rate: 30, @@ -57,6 +60,7 @@ def h264_240p( metadata:, threads:, audio_bit_rate:, + audio_sample_rate:, video_preset:, video_profile:, frame_rate:, @@ -74,6 +78,7 @@ def h264_360p( metadata: nil, threads: FFMPEG.threads, audio_bit_rate: '128k', + audio_sample_rate: 48_000, video_preset: 'ultrafast', video_profile: 'baseline', frame_rate: 30, @@ -87,6 +92,7 @@ def h264_360p( metadata:, threads:, audio_bit_rate:, + audio_sample_rate:, video_preset:, video_profile:, frame_rate:, @@ -104,6 +110,7 @@ def h264_480p( metadata: nil, threads: FFMPEG.threads, audio_bit_rate: '128k', + audio_sample_rate: 48_000, video_preset: 'fast', video_profile: 'main', frame_rate: 30, @@ -117,6 +124,7 @@ def h264_480p( metadata:, threads:, audio_bit_rate:, + audio_sample_rate:, video_preset:, video_profile:, frame_rate:, @@ -134,6 +142,7 @@ def h264_720p( metadata: nil, threads: FFMPEG.threads, audio_bit_rate: '128k', + audio_sample_rate: 48_000, video_preset: 'fast', video_profile: 'high', frame_rate: 60, @@ -147,6 +156,7 @@ def h264_720p( metadata:, threads:, audio_bit_rate:, + audio_sample_rate:, video_preset:, video_profile:, frame_rate:, @@ -164,6 +174,7 @@ def h264_1080p( metadata: nil, threads: FFMPEG.threads, audio_bit_rate: '128k', + audio_sample_rate: 48_000, video_preset: 'fast', video_profile: 'high', frame_rate: 60, @@ -177,6 +188,7 @@ def h264_1080p( metadata:, threads:, audio_bit_rate:, + audio_sample_rate:, video_preset:, video_profile:, frame_rate:, @@ -194,6 +206,7 @@ def h264_1440p( metadata: nil, threads: FFMPEG.threads, audio_bit_rate: '128k', + audio_sample_rate: 48_000, video_preset: 'fast', video_profile: 'high', frame_rate: 60, @@ -207,6 +220,7 @@ def h264_1440p( metadata:, threads:, audio_bit_rate:, + audio_sample_rate:, video_preset:, video_profile:, frame_rate:, @@ -224,6 +238,7 @@ def h264_4k( metadata: nil, threads: FFMPEG.threads, audio_bit_rate: '128k', + audio_sample_rate: 48_000, video_preset: 'fast', video_profile: 'high', frame_rate: 60, @@ -237,6 +252,7 @@ def h264_4k( metadata:, threads:, audio_bit_rate:, + audio_sample_rate:, video_preset:, video_profile:, frame_rate:, @@ -251,7 +267,7 @@ def h264_4k( # Preset to encode H.264 video files. class H264 < Preset - attr_reader :threads, :audio_bit_rate, :video_preset, :video_profile, + attr_reader :threads, :audio_bit_rate, :audio_sample_rate, :video_preset, :video_profile, :frame_rate, :constant_rate_factor, :pixel_format, :max_width, :max_height @@ -273,6 +289,7 @@ def initialize( metadata: nil, threads: FFMPEG.threads, audio_bit_rate: '128k', + audio_sample_rate: 48_000, video_preset: 'fast', video_profile: 'high', frame_rate: 30, @@ -292,6 +309,7 @@ def initialize( @threads = threads @audio_bit_rate = audio_bit_rate + @audio_sample_rate = audio_sample_rate @video_preset = video_preset @video_profile = video_profile @frame_rate = frame_rate @@ -321,6 +339,7 @@ def initialize( map media.audio_mapping_id do audio_bit_rate preset.audio_bit_rate + audio_sample_rate preset.audio_sample_rate end end end diff --git a/lib/ffmpeg/version.rb b/lib/ffmpeg/version.rb index 12e60fa..1fb60af 100644 --- a/lib/ffmpeg/version.rb +++ b/lib/ffmpeg/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module FFMPEG - VERSION = '8.1.0-beta.3' + VERSION = '8.1.0-beta.4' end diff --git a/spec/ffmpeg/command_args_spec.rb b/spec/ffmpeg/command_args_spec.rb index 2eac701..a079f5a 100644 --- a/spec/ffmpeg/command_args_spec.rb +++ b/spec/ffmpeg/command_args_spec.rb @@ -21,6 +21,22 @@ module FFMPEG end end + context 'when the media frame rate is zero' do + it 'sets the frame rate to the target value' do + media = instance_double(Media, frame_rate: 0) + args = CommandArgs.compose(media) { frame_rate 60 } + expect(args.to_a).to eq(%w[-r 60]) + end + end + + context 'when the media frame rate is negative' do + it 'sets the frame rate to the target value' do + media = instance_double(Media, frame_rate: -1) + args = CommandArgs.compose(media) { frame_rate 60 } + expect(args.to_a).to eq(%w[-r 60]) + end + end + context 'when the media frame rate is higher than the target value' do it 'sets the frame rate to the target value' do media = instance_double(Media, frame_rate: 60) @@ -32,7 +48,6 @@ module FFMPEG context 'when the media frame rate is lower than the target value' do it 'sets the frame rate to the closest standard value' do { - 0 => 12, 21 => 24, 26 => 25, 29.94 => 30, @@ -157,5 +172,67 @@ module FFMPEG end end end + + describe '#audio_sample_rate' do + context 'when the target value is nil' do + it 'does not set the audio sample rate' do + media = instance_double(Media, audio_sample_rate: 48_000) + args = CommandArgs.compose(media) { audio_sample_rate nil } + expect(args.to_a).to eq(%w[]) + end + end + + context 'when the media audio sample rate is nil' do + it 'sets the audio sample rate to the target value' do + media = instance_double(Media, audio_sample_rate: nil) + args = CommandArgs.compose(media) { audio_sample_rate 48_000 } + expect(args.to_a).to eq(%w[-ar 48000]) + end + end + + context 'when the media audio sample rate is zero' do + it 'sets the audio sample rate to the target value' do + media = instance_double(Media, audio_sample_rate: 0) + args = CommandArgs.compose(media) { audio_sample_rate 48_000 } + expect(args.to_a).to eq(%w[-ar 48000]) + end + end + + context 'when the media audio sample rate is negative' do + it 'sets the audio sample rate to the target value' do + media = instance_double(Media, audio_sample_rate: -1) + args = CommandArgs.compose(media) { audio_sample_rate 48_000 } + expect(args.to_a).to eq(%w[-ar 48000]) + end + end + + context 'when the media audio sample rate is higher than the target value' do + it 'sets the audio sample rate to the target value' do + media = instance_double(Media, audio_sample_rate: 96_000) + args = CommandArgs.compose(media) { audio_sample_rate 48_000 } + expect(args.to_a).to eq(%w[-ar 48000]) + end + end + + context 'when the media audio sample rate is lower than the target value' do + it 'sets the audio sample rate to the closest standard value' do + { + 8000 => 8000, + 10_000 => 11_025, + 12_000 => 11_025, + 20_000 => 22_050, + 44_100 => 44_100, + 45_000 => 44_100, + 47_000 => 48_000, + 90_000 => 88_200, + 100_000 => 96_000 + }.each do |media_sample_rate, expected_value| + media = instance_double(Media, audio_sample_rate: media_sample_rate) + args = CommandArgs.compose(media) { audio_sample_rate 192_000 } + expect(args.to_a).to eq(%W[-ar #{expected_value}]) + end + end + end + end end end