Skip to content

Commit 7cb3a76

Browse files
committed
fix: resolve various MPEG-DASH and logging issues
- Debug logs now contain commands that can actually be copy-paste executed in the shell. - MPEG-DASH presets now don't have any warnings in the output about multiple pixel formats and keyframe intervals. - Reduced default keyframe interval to 1 seconds in MPEG-DASH presets for better quality switching and seeking experience. - Added new Format, SetDAR and SetSAR filters.
1 parent 84c47c8 commit 7cb3a76

17 files changed

+278
-51
lines changed

ffmpeg.gemspec

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Gem::Specification.new do |s|
1717

1818
s.add_dependency('logger', '~> 1.6')
1919
s.add_dependency('multi_json', '~> 1.8')
20+
s.add_dependency('shellwords', '~> 0.2')
2021

2122
s.add_development_dependency('debug')
2223
s.add_development_dependency('rake', '~> 13.2')

lib/ffmpeg.rb

+8-4
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@
33
$LOAD_PATH.unshift File.dirname(__FILE__)
44

55
require 'logger'
6+
require 'shellwords'
67

78
require_relative 'ffmpeg/command_args'
89
require_relative 'ffmpeg/errors'
910
require_relative 'ffmpeg/filter'
11+
require_relative 'ffmpeg/filters/format'
1012
require_relative 'ffmpeg/filters/fps'
1113
require_relative 'ffmpeg/filters/grayscale'
1214
require_relative 'ffmpeg/filters/scale'
15+
require_relative 'ffmpeg/filters/set_dar'
16+
require_relative 'ffmpeg/filters/set_sar'
1317
require_relative 'ffmpeg/filters/silence_detect'
1418
require_relative 'ffmpeg/filters/split'
1519
require_relative 'ffmpeg/io'
@@ -118,7 +122,7 @@ def ffmpeg_binary
118122
# @param args [Array<String>] The arguments to pass to ffmpeg.
119123
# @return [Array<String, Process::Status>] The standard output, the standard error, and the process status.
120124
def ffmpeg_capture3(*args)
121-
logger.debug(self) { "ffmpeg -y #{args.join(' ')}" }
125+
logger.debug(self) { "ffmpeg -y #{Shellwords.join(args)}" }
122126
FFMPEG::IO.capture3(ffmpeg_binary, '-y', *args)
123127
end
124128

@@ -134,7 +138,7 @@ def ffmpeg_capture3(*args)
134138
# @yieldparam wait_thr (+Thread+) The child process thread.
135139
# @return [Process::Status, Array<IO, Thread>]
136140
def ffmpeg_popen3(*args, &)
137-
logger.debug(self) { "ffmpeg -y #{args.join(' ')}" }
141+
logger.debug(self) { "ffmpeg -y #{Shellwords.join(args)}" }
138142
FFMPEG::IO.popen3(ffmpeg_binary, '-y', *args, &)
139143
end
140144

@@ -209,7 +213,7 @@ def ffprobe_binary=(path)
209213
# @return [Array<String, Process::Status>] The standard output, the standard error, and the process status.
210214
# @raise [Errno::ENOENT] If the ffprobe binary cannot be found.
211215
def ffprobe_capture3(*args)
212-
logger.debug(self) { "ffprobe -y #{args.join(' ')}" }
216+
logger.debug(self) { "ffprobe -y #{Shellwords.join(args)}" }
213217
FFMPEG::IO.capture3(ffprobe_binary, '-y', *args)
214218
end
215219

@@ -225,7 +229,7 @@ def ffprobe_capture3(*args)
225229
# @return [Process::Status, Array<IO, Thread>]
226230
# @raise [Errno::ENOENT] If the ffprobe binary cannot be found.
227231
def ffprobe_popen3(*args, &)
228-
logger.debug(self) { "ffprobe -y #{args.join(' ')}" }
232+
logger.debug(self) { "ffprobe -y #{Shellwords.join(args)}" }
229233
FFMPEG::IO.popen3(ffprobe_binary, '-y', *args, &)
230234
end
231235

lib/ffmpeg/filters/format.rb

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# frozen_string_literal: true
2+
3+
module FFMPEG
4+
module Filters # rubocop:disable Style/Documentation
5+
class << self
6+
def format(pixel_formats: nil, color_spaces: nil, color_ranges: nil)
7+
Format.new(pixel_formats:, color_spaces:, color_ranges:)
8+
end
9+
end
10+
11+
# The Format class uses the format filter
12+
# to set the pixel format, color space, and color range of a multimedia stream.
13+
class Format < Filter
14+
attr_reader :pixel_formats, :color_spaces, :color_ranges
15+
16+
def initialize(pixel_formats: nil, color_spaces: nil, color_ranges: nil)
17+
if pixel_formats.nil? && color_spaces.nil? && color_ranges.nil?
18+
raise ArgumentError, 'At least one of pixel_formats, color_spaces, or color_ranges must be set'
19+
end
20+
21+
unless pixel_formats.nil? || pixel_formats.is_a?(String) || pixel_formats.is_a?(Array)
22+
raise ArgumentError, "Unknown pixel_formats format #{pixel_formats.class}, expected #{String} or #{Array}"
23+
end
24+
25+
unless color_spaces.nil? || color_spaces.is_a?(String) || color_spaces.is_a?(Array)
26+
raise ArgumentError, "Unknown color_spaces format #{color_spaces.class}, expected #{String} or #{Array}"
27+
end
28+
29+
unless color_ranges.nil? || color_ranges.is_a?(String) || color_ranges.is_a?(Array)
30+
raise ArgumentError, "Unknown color_ranges format #{color_ranges.class}, expected #{String} or #{Array}"
31+
end
32+
33+
@pixel_formats = pixel_formats
34+
@color_spaces = color_spaces
35+
@color_ranges = color_ranges
36+
37+
super(:video, 'format')
38+
end
39+
40+
protected
41+
42+
def format_kwargs
43+
super(
44+
pix_fmts: @pixel_formats,
45+
color_spaces: @color_spaces,
46+
color_ranges: @color_ranges
47+
)
48+
end
49+
end
50+
end
51+
end

lib/ffmpeg/filters/fps.rb

+2-6
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@
33
require_relative '../filter'
44

55
module FFMPEG
6-
# rubocop:disable Style/Documentation
7-
module Filters
8-
# rubocop:enable Style/Documentation
9-
6+
module Filters # rubocop:disable Style/Documentation
107
class << self
118
def fps(frame_rate)
129
FPS.new(frame_rate)
@@ -20,8 +17,7 @@ class FPS < Filter
2017

2118
def initialize(frame_rate)
2219
unless frame_rate.is_a?(Numeric)
23-
raise ArgumentError,
24-
"Unknown frame_rate format #{frame_rate.class}, expected #{Numeric}"
20+
raise ArgumentError, "Unknown frame_rate format #{frame_rate.class}, expected #{Numeric}"
2521
end
2622

2723
@frame_rate = frame_rate

lib/ffmpeg/filters/grayscale.rb

+3-6
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@
33
require_relative '../filter'
44

55
module FFMPEG
6-
# rubocop:disable Style/Documentation
7-
module Filters
8-
# rubocop:enable Style/Documentation
9-
6+
module Filters # rubocop:disable Style/Documentation
107
class << self
118
def grayscale
129
Grayscale.new
@@ -15,9 +12,9 @@ def grayscale
1512

1613
# The Grayscale class uses the format filter
1714
# to convert a multimedia stream to grayscale.
18-
class Grayscale < Filter
15+
class Grayscale < Format
1916
def initialize
20-
super(:video, 'format', pix_fmts: ['gray'])
17+
super(pixel_formats: 'gray')
2118
end
2219
end
2320
end

lib/ffmpeg/filters/scale.rb

+1-4
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@
33
require_relative '../filter'
44

55
module FFMPEG
6-
# rubocop:disable Style/Documentation
7-
module Filters
8-
# rubocop:enable Style/Documentation
9-
6+
module Filters # rubocop:disable Style/Documentation
107
class << self
118
def scale(width: nil, height: nil, force_original_aspect_ratio: nil, flags: nil)
129
Scale.new(width:, height:, force_original_aspect_ratio:, flags:)

lib/ffmpeg/filters/set_dar.rb

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# frozen_string_literal: true
2+
3+
module FFMPEG
4+
module Filters # rubocop:disable Style/Documentation
5+
class << self
6+
def set_dar(dar) # rubocop:disable Naming/AccessorMethodName
7+
SetDAR.new(dar)
8+
end
9+
end
10+
11+
# The SetDAR class uses the setdar filter
12+
# to set the display aspect ratio of a multimedia stream.
13+
class SetDAR < Filter
14+
attr_reader :dar
15+
16+
def initialize(dar)
17+
unless dar.is_a?(String) || dar.is_a?(Numeric)
18+
raise ArgumentError, "Unknown dar format #{dar.class}, expected #{String} or #{Numeric}"
19+
end
20+
21+
@dar = dar
22+
23+
super(:video, 'setdar')
24+
end
25+
26+
protected
27+
28+
def format_kwargs
29+
@dar.to_s
30+
end
31+
end
32+
end
33+
end

lib/ffmpeg/filters/set_sar.rb

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# frozen_string_literal: true
2+
3+
module FFMPEG
4+
module Filters # rubocop:disable Style/Documentation
5+
class << self
6+
def set_sar(sar) # rubocop:disable Naming/AccessorMethodName
7+
SetSAR.new(sar)
8+
end
9+
end
10+
11+
# The SetSAR class uses the setsar filter
12+
# to set the sample aspect ratio of a multimedia stream.
13+
class SetSAR < Filter
14+
attr_reader :sar
15+
16+
def initialize(sar)
17+
unless sar.is_a?(String) || sar.is_a?(Numeric)
18+
raise ArgumentError, "Unknown sar format #{sar.class}, expected #{String}"
19+
end
20+
21+
@sar = sar
22+
23+
super(:video, 'setsar')
24+
end
25+
26+
protected
27+
28+
def format_kwargs
29+
@sar.to_s
30+
end
31+
end
32+
end
33+
end

lib/ffmpeg/filters/silence_detect.rb

+2-5
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,14 @@
33
require_relative '../filter'
44

55
module FFMPEG
6-
# rubocop:disable Style/Documentation
7-
module Filters
8-
# rubocop:enable Style/Documentation
9-
6+
module Filters # rubocop:disable Style/Documentation
107
class << self
118
def silence_detect(threshold: nil, duration: nil, mono: nil)
129
SilenceDetect.new(threshold:, duration:, mono:)
1310
end
1411
end
1512

16-
# The SilenceDetect class is uses the silencedetect filter
13+
# The SilenceDetect class uses the silencedetect filter
1714
# to detect silent parts in a multimedia stream.
1815
class SilenceDetect < Filter
1916
attr_reader :threshold, :duration, :mono

lib/ffmpeg/filters/split.rb

+2-6
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@
33
require_relative '../filter'
44

55
module FFMPEG
6-
# rubocop:disable Style/Documentation
7-
module Filters
8-
# rubocop:enable Style/Documentation
9-
6+
module Filters # rubocop:disable Style/Documentation
107
class << self
118
def split(output_count)
129
Split.new(output_count)
@@ -20,8 +17,7 @@ class Split < Filter
2017

2118
def initialize(output_count = nil)
2219
unless output_count.nil? || output_count.is_a?(Integer)
23-
raise ArgumentError,
24-
"Unknown output_count format #{output_count.class}, expected #{Integer}"
20+
raise ArgumentError, "Unknown output_count format #{output_count.class}, expected #{Integer}"
2521
end
2622

2723
@output_count = output_count if output_count

lib/ffmpeg/presets/dash.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ module FFMPEG
88
module Presets
99
# Preset to encode DASH media files.
1010
class DASH < Preset
11-
attr_reader :segment_duration, :min_keyframe_interval, :max_keyframe_interval, :scene_change_threshold
11+
attr_reader :segment_duration
1212

1313
# @param name [String] The name of the preset.
1414
# @param filename [String] The filename format of the output.

0 commit comments

Comments
 (0)