Class: FFMPEG::Media

Inherits:
Object
  • Object
show all
Defined in:
lib/ffmpeg/media.rb

Overview

Represents a media file and provides access to its metadata

Examples:

Basic usage

media = FFMPEG::Media.new("/path/to/video.mp4")
media.duration     # => 120.5
media.resolution   # => "1920x1080"
media.video_codec  # => "h264"
media.audio_codec  # => "aac"

Checking properties

media.valid?       # => true
media.video?       # => true
media.audio?       # => true
media.hd?          # => true

Transcoding

media.transcode("/path/to/output.webm", video_codec: "libvpx-vp9")

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path) ⇒ Media

Create a new Media instance

Parameters:

  • path (String)

    path to the media file

Raises:



41
42
43
44
45
# File 'lib/ffmpeg/media.rb', line 41

def initialize(path)
  @path = File.expand_path(path)
  validate_file!
  probe!
end

Instance Attribute Details

#formatHash (readonly)

Returns format information.

Returns:

  • (Hash)

    format information



35
36
37
# File 'lib/ffmpeg/media.rb', line 35

def format
  @format
end

#pathString (readonly)

Returns path to the media file.

Returns:

  • (String)

    path to the media file



26
27
28
# File 'lib/ffmpeg/media.rb', line 26

def path
  @path
end

#rawHash (readonly)

Returns raw ffprobe output.

Returns:

  • (Hash)

    raw ffprobe output



29
30
31
# File 'lib/ffmpeg/media.rb', line 29

def raw
  @raw
end

#streamsArray<Stream> (readonly)

Returns all streams in the media.

Returns:

  • (Array<Stream>)

    all streams in the media



32
33
34
# File 'lib/ffmpeg/media.rb', line 32

def streams
  @streams
end

Instance Method Details

#audioStream?

Returns primary audio stream.

Returns:

  • (Stream, nil)

    primary audio stream



118
119
120
# File 'lib/ffmpeg/media.rb', line 118

def audio
  audio_streams.find(&:default) || audio_streams.first
end

#audio?Boolean

Returns true if media has audio.

Returns:

  • (Boolean)

    true if media has audio



128
129
130
# File 'lib/ffmpeg/media.rb', line 128

def audio?
  audio_streams.any?
end

#audio_bit_rateInteger?

Returns audio bit rate.

Returns:

  • (Integer, nil)

    audio bit rate



217
218
219
# File 'lib/ffmpeg/media.rb', line 217

def audio_bit_rate
  audio&.bit_rate
end

#audio_channelsInteger?

Returns number of audio channels.

Returns:

  • (Integer, nil)

    number of audio channels



207
208
209
# File 'lib/ffmpeg/media.rb', line 207

def audio_channels
  audio&.channels
end

#audio_codecString?

Returns audio codec name.

Returns:

  • (String, nil)

    audio codec name



197
198
199
# File 'lib/ffmpeg/media.rb', line 197

def audio_codec
  audio&.codec
end

#audio_streamsArray<Stream>

Returns all audio streams.

Returns:

  • (Array<Stream>)

    all audio streams



103
104
105
# File 'lib/ffmpeg/media.rb', line 103

def audio_streams
  @audio_streams ||= streams.select(&:audio?)
end

#bit_rateInteger?

Returns bit rate in bits/sec.

Returns:

  • (Integer, nil)

    bit rate in bits/sec



58
59
60
# File 'lib/ffmpeg/media.rb', line 58

def bit_rate
  format["bit_rate"]&.to_i
end

#channel_layoutString?

Returns audio channel layout.

Returns:

  • (String, nil)

    audio channel layout



212
213
214
# File 'lib/ffmpeg/media.rb', line 212

def channel_layout
  audio&.channel_layout
end

#creation_timeTime?

Returns creation time if available.

Returns:

  • (Time, nil)

    creation time if available



88
89
90
91
92
93
# File 'lib/ffmpeg/media.rb', line 88

def creation_time
  time_str = tags["creation_time"]
  Time.parse(time_str) if time_str
rescue ArgumentError
  nil
end

#detect_scenes(threshold: 0.3) ⇒ Array<Hash>

Detect scene changes in the video

Parameters:

  • threshold (Float) (defaults to: 0.3)

    scene detection threshold (0.0-1.0)

Returns:

  • (Array<Hash>)

    array of scene change points



266
267
268
269
# File 'lib/ffmpeg/media.rb', line 266

def detect_scenes(threshold: 0.3)
  detector = SceneDetector.new(self)
  detector.detect(threshold: threshold)
end

#durationFloat?

Returns duration in seconds.

Returns:

  • (Float, nil)

    duration in seconds



53
54
55
# File 'lib/ffmpeg/media.rb', line 53

def duration
  format["duration"]&.to_f
end

#extract_keyframes(output_dir:, interval: nil, count: nil, timestamps: nil) ⇒ Array<String>

Extract keyframes from the video

Parameters:

  • output_dir (String)

    directory for extracted frames

  • interval (Float, nil) (defaults to: nil)

    interval between frames in seconds

  • count (Integer, nil) (defaults to: nil)

    number of frames to extract

  • timestamps (Array<Float>, nil) (defaults to: nil)

    specific timestamps

Returns:

  • (Array<String>)

    paths to extracted frames



279
280
281
282
283
284
285
286
287
288
289
# File 'lib/ffmpeg/media.rb', line 279

def extract_keyframes(output_dir:, interval: nil, count: nil, timestamps: nil)
  extractor = KeyframeExtractor.new(self)

  if timestamps
    extractor.extract_at_timestamps(timestamps, output_dir: output_dir)
  elsif count
    extractor.extract_count(count, output_dir: output_dir)
  else
    extractor.extract_at_intervals(interval: interval || 5.0, output_dir: output_dir)
  end
end

#format_long_nameString?

Returns format long name.

Returns:

  • (String, nil)

    format long name



73
74
75
# File 'lib/ffmpeg/media.rb', line 73

def format_long_name
  format["format_long_name"]
end

#format_nameString?

Returns container format name.

Returns:

  • (String, nil)

    container format name



68
69
70
# File 'lib/ffmpeg/media.rb', line 68

def format_name
  format["format_name"]
end

#frame_countInteger?

Returns approximate total frame count.

Returns:

  • (Integer, nil)

    approximate total frame count



224
225
226
227
228
# File 'lib/ffmpeg/media.rb', line 224

def frame_count
  return nil unless duration && frame_rate

  (duration * frame_rate).round
end

#frame_rateFloat?

Returns video frame rate.

Returns:

  • (Float, nil)

    video frame rate



155
156
157
# File 'lib/ffmpeg/media.rb', line 155

def frame_rate
  video&.frame_rate
end

#frame_to_timestamp(frame_number) ⇒ Float?

Calculate timestamp for a specific frame

Parameters:

  • frame_number (Integer)

    frame number (0-indexed)

Returns:

  • (Float, nil)

    timestamp in seconds



233
234
235
236
237
# File 'lib/ffmpeg/media.rb', line 233

def frame_to_timestamp(frame_number)
  return nil unless frame_rate && frame_rate.positive?

  frame_number.to_f / frame_rate
end

#hd?Boolean

Returns true if video is HD (720p+).

Returns:

  • (Boolean)

    true if video is HD (720p+)



185
186
187
# File 'lib/ffmpeg/media.rb', line 185

def hd?
  video&.hd? || false
end

#heightInteger?

Returns video height.

Returns:

  • (Integer, nil)

    video height



140
141
142
# File 'lib/ffmpeg/media.rb', line 140

def height
  video&.height
end

#landscape?Boolean

Returns true if video is landscape.

Returns:

  • (Boolean)

    true if video is landscape



180
181
182
# File 'lib/ffmpeg/media.rb', line 180

def landscape?
  video&.landscape? || false
end

#pixel_formatString?

Returns pixel format.

Returns:

  • (String, nil)

    pixel format



170
171
172
# File 'lib/ffmpeg/media.rb', line 170

def pixel_format
  video&.pixel_format
end

#portrait?Boolean

Returns true if video is portrait.

Returns:

  • (Boolean)

    true if video is portrait



175
176
177
# File 'lib/ffmpeg/media.rb', line 175

def portrait?
  video&.portrait? || false
end

#resolutionString?

Returns resolution as “WIDTHxHEIGHT”.

Returns:

  • (String, nil)

    resolution as “WIDTHxHEIGHT”



145
146
147
# File 'lib/ffmpeg/media.rb', line 145

def resolution
  video&.resolution
end

#rotationInteger?

Returns video rotation in degrees.

Returns:

  • (Integer, nil)

    video rotation in degrees



165
166
167
# File 'lib/ffmpeg/media.rb', line 165

def rotation
  video&.rotation
end

#sample_rateInteger?

Returns audio sample rate.

Returns:

  • (Integer, nil)

    audio sample rate



202
203
204
# File 'lib/ffmpeg/media.rb', line 202

def sample_rate
  audio&.sample_rate
end

#screenshot(timestamp, output_path, resolution: nil) ⇒ String

Take a screenshot at a specific timestamp

Parameters:

  • timestamp (Float)

    timestamp in seconds

  • output_path (String)

    path for output image

  • resolution (String, nil) (defaults to: nil)

    output resolution (e.g., “640x480”)

Returns:

  • (String)

    path to the screenshot



298
299
300
301
# File 'lib/ffmpeg/media.rb', line 298

def screenshot(timestamp, output_path, resolution: nil)
  extractor = KeyframeExtractor.new(self)
  extractor.extract_at_timestamps([timestamp], output_dir: File.dirname(output_path)).first
end

#sizeInteger?

Returns file size in bytes.

Returns:

  • (Integer, nil)

    file size in bytes



63
64
65
# File 'lib/ffmpeg/media.rb', line 63

def size
  format["size"]&.to_i || File.size(path)
end

#subtitle_streamsArray<Stream>

Returns all subtitle streams.

Returns:

  • (Array<Stream>)

    all subtitle streams



108
109
110
# File 'lib/ffmpeg/media.rb', line 108

def subtitle_streams
  @subtitle_streams ||= streams.select(&:subtitle?)
end

#tagsHash

Returns format tags (title, artist, etc.).

Returns:

  • (Hash)

    format tags (title, artist, etc.)



78
79
80
# File 'lib/ffmpeg/media.rb', line 78

def tags
  format["tags"] || {}
end

#timestamp_to_frame(timestamp) ⇒ Integer?

Calculate frame number for a specific timestamp

Parameters:

  • timestamp (Float)

    timestamp in seconds

Returns:

  • (Integer, nil)

    frame number (0-indexed)



242
243
244
245
246
# File 'lib/ffmpeg/media.rb', line 242

def timestamp_to_frame(timestamp)
  return nil unless frame_rate

  (timestamp.to_f * frame_rate).round
end

#titleString?

Returns media title from tags.

Returns:

  • (String, nil)

    media title from tags



83
84
85
# File 'lib/ffmpeg/media.rb', line 83

def title
  tags["title"]
end

#to_hHash

Returns serializable representation.

Returns:

  • (Hash)

    serializable representation



313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
# File 'lib/ffmpeg/media.rb', line 313

def to_h
  {
    path: path,
    duration: duration,
    size: size,
    format: format_name,
    video: video ? {
      codec: video_codec,
      resolution: resolution,
      frame_rate: frame_rate,
      bit_rate: video_bit_rate
    } : nil,
    audio: audio ? {
      codec: audio_codec,
      sample_rate: sample_rate,
      channels: audio_channels,
      bit_rate: audio_bit_rate
    } : nil,
    streams: streams.map(&:to_s)
  }
end

#to_sString

Returns human-readable description.

Returns:

  • (String)

    human-readable description



304
305
306
307
308
309
310
# File 'lib/ffmpeg/media.rb', line 304

def to_s
  parts = ["Media: #{File.basename(path)}"]
  parts << "Duration: #{format_duration(duration)}" if duration
  parts << "Resolution: #{resolution}" if resolution
  parts << "Codecs: #{video_codec}/#{audio_codec}" if video_codec || audio_codec
  parts.join(" | ")
end

#transcode(output_path, **options) {|Float| ... } ⇒ Media

Transcode media to a new file

Parameters:

  • output_path (String)

    path to output file

  • options (Hash)

    transcoding options

Yields:

  • (Float)

    progress percentage (0-100)

Returns:

  • (Media)

    the transcoded media

Raises:



256
257
258
259
# File 'lib/ffmpeg/media.rb', line 256

def transcode(output_path, **options, &block)
  transcoder = Transcoder.new(self, output_path, **options)
  transcoder.run(&block)
end

#uhd?Boolean

Returns true if video is 4K (2160p+).

Returns:

  • (Boolean)

    true if video is 4K (2160p+)



190
191
192
# File 'lib/ffmpeg/media.rb', line 190

def uhd?
  video&.uhd? || false
end

#valid?Boolean

Returns true if media was successfully parsed.

Returns:

  • (Boolean)

    true if media was successfully parsed



48
49
50
# File 'lib/ffmpeg/media.rb', line 48

def valid?
  @valid
end

#videoStream?

Returns primary video stream.

Returns:

  • (Stream, nil)

    primary video stream



113
114
115
# File 'lib/ffmpeg/media.rb', line 113

def video
  video_streams.find(&:default) || video_streams.first
end

#video?Boolean

Returns true if media has video.

Returns:

  • (Boolean)

    true if media has video



123
124
125
# File 'lib/ffmpeg/media.rb', line 123

def video?
  video_streams.any?
end

#video_bit_rateInteger?

Returns video bit rate.

Returns:

  • (Integer, nil)

    video bit rate



160
161
162
# File 'lib/ffmpeg/media.rb', line 160

def video_bit_rate
  video&.bit_rate
end

#video_codecString?

Returns video codec name.

Returns:

  • (String, nil)

    video codec name



150
151
152
# File 'lib/ffmpeg/media.rb', line 150

def video_codec
  video&.codec
end

#video_streamsArray<Stream>

Returns all video streams.

Returns:

  • (Array<Stream>)

    all video streams



98
99
100
# File 'lib/ffmpeg/media.rb', line 98

def video_streams
  @video_streams ||= streams.select(&:video?)
end

#widthInteger?

Returns video width.

Returns:

  • (Integer, nil)

    video width



135
136
137
# File 'lib/ffmpeg/media.rb', line 135

def width
  video&.width
end