Class: FFmpegCore::Probe

Inherits:
Object
  • Object
show all
Defined in:
lib/ffmpeg_core/probe.rb

Overview

Probe video metadata using ffprobe

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path) ⇒ Probe

Returns a new instance of Probe.



11
12
13
14
15
16
# File 'lib/ffmpeg_core/probe.rb', line 11

def initialize(path)
  @path = path.to_s
  @metadata = nil
  validate_file!
  probe!
end

Instance Attribute Details

#metadataObject (readonly)

Returns the value of attribute metadata.



9
10
11
# File 'lib/ffmpeg_core/probe.rb', line 9

def 
  @metadata
end

#pathObject (readonly)

Returns the value of attribute path.



9
10
11
# File 'lib/ffmpeg_core/probe.rb', line 9

def path
  @path
end

Instance Method Details

#aspect_ratioObject



106
107
108
# File 'lib/ffmpeg_core/probe.rb', line 106

def aspect_ratio
  video_stream&.dig("display_aspect_ratio")
end

#audio_channel_layoutObject



138
139
140
# File 'lib/ffmpeg_core/probe.rb', line 138

def audio_channel_layout
  audio_stream&.dig("channel_layout")
end

#audio_channelsObject



134
135
136
# File 'lib/ffmpeg_core/probe.rb', line 134

def audio_channels
  audio_stream&.dig("channels")
end

#audio_codecObject



50
51
52
# File 'lib/ffmpeg_core/probe.rb', line 50

def audio_codec
  audio_stream&.dig("codec_name")
end

#audio_sample_rateObject



130
131
132
# File 'lib/ffmpeg_core/probe.rb', line 130

def audio_sample_rate
  audio_stream&.dig("sample_rate")&.to_i
end

#audio_streamObject

Audio stream metadata



34
35
36
# File 'lib/ffmpeg_core/probe.rb', line 34

def audio_stream
  @audio_stream ||= streams.find { |s| s["codec_type"] == "audio" }
end

#audio_streamsObject



110
111
112
# File 'lib/ffmpeg_core/probe.rb', line 110

def audio_streams
  streams.select { |s| s["codec_type"] == "audio" }
end

#bitrateObject

Bitrate in kb/s



24
25
26
# File 'lib/ffmpeg_core/probe.rb', line 24

def bitrate
  @metadata.dig("format", "bit_rate")&.to_i&./(1000)
end

#chaptersObject



118
119
120
# File 'lib/ffmpeg_core/probe.rb', line 118

def chapters
  @metadata.fetch("chapters", [])
end

#durationObject

Duration in seconds



19
20
21
# File 'lib/ffmpeg_core/probe.rb', line 19

def duration
  @metadata.dig("format", "duration")&.to_f
end

#exifObject

EXIF metadata: merges format-level and video stream tags (FFmpeg 8.1+)



155
156
157
158
159
# File 'lib/ffmpeg_core/probe.rb', line 155

def exif
  format_tags = @metadata.dig("format", "tags") || {}
  stream_tags = video_stream&.dig("tags") || {}
  format_tags.merge(stream_tags)
end

#format_nameObject



122
123
124
# File 'lib/ffmpeg_core/probe.rb', line 122

def format_name
  @metadata.dig("format", "format_name")
end

#frame_rateObject



70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/ffmpeg_core/probe.rb', line 70

def frame_rate
  return nil unless video_stream

  # Parse frame rate (e.g., "30000/1001" or "30")
  r_frame_rate = video_stream["r_frame_rate"]
  return nil unless r_frame_rate

  if r_frame_rate.include?("/")
    num, den = r_frame_rate.split("/").map(&:to_f)
    num / den
  else
    r_frame_rate.to_f
  end
end

#has_audio?Boolean

Returns:

  • (Boolean)


150
151
152
# File 'lib/ffmpeg_core/probe.rb', line 150

def has_audio?
  !audio_stream.nil?
end

#has_video?Boolean

Returns:

  • (Boolean)


146
147
148
# File 'lib/ffmpeg_core/probe.rb', line 146

def has_video?
  !video_stream.nil?
end

#heightObject



62
63
64
65
66
67
68
# File 'lib/ffmpeg_core/probe.rb', line 62

def height
  val = video_stream&.dig("height")
  return val unless val
  return val if (rotation || 0) % 180 == 0

  video_stream&.dig("width")
end

#pixel_formatObject



142
143
144
# File 'lib/ffmpeg_core/probe.rb', line 142

def pixel_format
  video_stream&.dig("pix_fmt")
end

#resolutionObject



85
86
87
88
89
# File 'lib/ffmpeg_core/probe.rb', line 85

def resolution
  return nil unless width && height

  "#{width}x#{height}"
end

#rotationObject



91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/ffmpeg_core/probe.rb', line 91

def rotation
  return nil unless video_stream

  # Try to find rotation in tags (common in MP4/MOV)
  tags = video_stream.fetch("tags", {})
  return tags["rotate"].to_i if tags["rotate"]

  # Try side_data_list (common in some newer formats)
  side_data = video_stream.fetch("side_data_list", []).find { |sd| sd.key?("rotation") }
  return side_data["rotation"].to_i if side_data

  # Default to 0 if not found
  0
end

#subtitle_streamsObject



114
115
116
# File 'lib/ffmpeg_core/probe.rb', line 114

def subtitle_streams
  streams.select { |s| s["codec_type"] == "subtitle" }
end

#tagsObject



126
127
128
# File 'lib/ffmpeg_core/probe.rb', line 126

def tags
  @metadata.dig("format", "tags") || {}
end

#valid?Boolean

Returns:

  • (Boolean)


161
162
163
# File 'lib/ffmpeg_core/probe.rb', line 161

def valid?
  !video_stream.nil?
end

#video_codecObject



38
39
40
# File 'lib/ffmpeg_core/probe.rb', line 38

def video_codec
  video_stream&.dig("codec_name")
end

#video_levelObject



46
47
48
# File 'lib/ffmpeg_core/probe.rb', line 46

def video_level
  video_stream&.dig("level")
end

#video_profileObject



42
43
44
# File 'lib/ffmpeg_core/probe.rb', line 42

def video_profile
  video_stream&.dig("profile")
end

#video_streamObject

Video stream metadata



29
30
31
# File 'lib/ffmpeg_core/probe.rb', line 29

def video_stream
  @video_stream ||= streams.find { |s| s["codec_type"] == "video" }
end

#widthObject



54
55
56
57
58
59
60
# File 'lib/ffmpeg_core/probe.rb', line 54

def width
  val = video_stream&.dig("width")
  return val unless val
  return val if (rotation || 0) % 180 == 0

  video_stream&.dig("height")
end