Class: MP4

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

Defined Under Namespace

Modules: Binary, Cache, Constants, Ext, Layout, Metadata, Nolan, Parser, Source, Structure, XLSX

Constant Summary collapse

LOG_LEVELS =
{
  'silent'  => Logger::UNKNOWN + 1,
  'error'   => Logger::ERROR,
  'warn'    => Logger::WARN,
  'info'    => Logger::INFO,
  'debug'   => Logger::DEBUG,
  'verbose' => Logger::DEBUG,
}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path: nil, url: nil, source: nil, cache_location: nil, cache_backend: nil) ⇒ MP4

Returns a new instance of MP4.



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/mp4.rb', line 37

def initialize(path: nil, url: nil, source: nil, cache_location: nil, cache_backend: nil)
  @source = source
  @source ||= MP4::Source::Local.new(path) if path
  @source ||= MP4::Source::Url.new(url)    if url

  raise 'source is not defined' unless @source

  @cache_location = cache_location
  @cache_backend  = cache_backend || (cache_location ? :sqlite : :memory)

  return if @cache_location && File.exist?(@cache_location.to_s)

  memoize_mp4
  segmentize_mp4
end

Instance Attribute Details

#cache_backendObject (readonly)

Returns the value of attribute cache_backend.



7
8
9
# File 'lib/mp4.rb', line 7

def cache_backend
  @cache_backend
end

#cache_locationObject (readonly)

Returns the value of attribute cache_location.



7
8
9
# File 'lib/mp4.rb', line 7

def cache_location
  @cache_location
end

#sourceObject (readonly)

Returns the value of attribute source.



7
8
9
# File 'lib/mp4.rb', line 7

def source
  @source
end

Class Method Details

.loggerObject



18
19
20
21
22
23
24
25
26
27
# File 'lib/mp4.rb', line 18

def self.logger
  @logger ||= begin
    log = Logger.new($stdout)
    log.level = LOG_LEVELS.fetch(ENV.fetch('MP4_LOG_LEVEL', 'info').downcase, Logger::INFO)
    log.formatter = proc do |severity, _time, _prog, msg|
      "[mp4-rb #{severity.ljust(5)}] #{msg}\n"
    end
    log
  end
end

.rootObject



33
34
35
# File 'lib/mp4.rb', line 33

def self.root
  Pathname.new(__dir__).join('..')
end

.verbose?Boolean

Returns:

  • (Boolean)


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

def self.verbose?
  ENV.fetch('MP4_LOG_LEVEL', 'info').downcase == 'verbose'
end

Instance Method Details

#build_dash_init_segment_binary(track_id) ⇒ Object



91
92
93
# File 'lib/mp4.rb', line 91

def build_dash_init_segment_binary(track_id)
  M4S::Layout::InitMp4Builder.call(cache, track_id).generate
end

#build_dash_manifest_xml(init_segment_path:, segment_path:) ⇒ Object



87
88
89
# File 'lib/mp4.rb', line 87

def build_dash_manifest_xml(init_segment_path:, segment_path:)
  M4S::Dash::Manifest.generate(cache, init_segment_path: init_segment_path, segment_path: segment_path)
end

#build_dash_segment_binary(track_id, segment_index) ⇒ Object



95
96
97
# File 'lib/mp4.rb', line 95

def build_dash_segment_binary(track_id, segment_index)
  M4S::Layout::Builder.call(cache, source, track_id, segment_index).generate
end

#build_xslx(output_path) ⇒ Object



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

def build_xslx(output_path)
  ::MP4::XLSX::Builder.call(mp4: self, output_path: output_path)
end

#cacheObject



57
58
59
# File 'lib/mp4.rb', line 57

def cache
  @cache ||= Cache.build(cache_backend, cache_location)
end

#compute_segmentsObject



67
68
69
# File 'lib/mp4.rb', line 67

def compute_segments
  MP4::Nolan::Service.compute_segments(cache)
end

#save_fmp4_segments_to(output_path) ⇒ Object



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/mp4.rb', line 99

def save_fmp4_segments_to(output_path)
  base_path = Pathname.new(output_path)
  FileUtils.mkdir_p(base_path)

  cache.tracks.each_with_index do |track, index|
    t = M4S::Layout::InitMp4Builder.call(cache, track.fetch('track_id'))

    file_name = "init-#{index}.mp4"
    file_path = base_path.join(file_name)
    File.open(file_path, 'wb') { |file| file.write(t.generate) }
  end

  cache.tracks.each do |track|
    track_id = track.fetch('track_id')

    cache.segments(track_id).each do |segment|
      segment_index = segment.fetch('segment_index')

      t = M4S::Layout::Builder.call(cache, source, track_id, segment.fetch('segment_index'))
      file_name = "chunk-#{track_id.pred}-#{format('%05d', segment_index.next)}.m4s"
      file_path = base_path.join(file_name)

      MP4.logger.debug { "writing #{file_path}" }

      File.open(file_path, 'wb') { |file| file.write(t.generate) }
    end
  end

  File.open(base_path.join('manifest.mpd'), 'w') do |file|
    file.write(M4S::Dash::Manifest.generate(cache, init_segment_path: nil, segment_path: nil))
  end

  File.open(base_path.join('general_playlist.m3u8'), 'w') do |file|
    file.write(M4S::M3U8::GeneralPlaylist.generate(cache))
  end

  cache.tracks.each do |track|
    content = M4S::M3U8::MediaPlaylist.generate(cache, track, init_segment_path: nil, segment_path: nil)
    file_name = "playlist-#{track.fetch('track_id').pred}.m3u8"
    File.open(base_path.join(file_name), 'w') do |file|
      file.write(content)
    end
  end
end

#save_full_to(output_path) ⇒ Object



61
62
63
64
65
# File 'lib/mp4.rb', line 61

def save_full_to(output_path)
  t = Layout::Builder.call(cache, cache, source)

  File.open(output_path, 'wb') { |file| file.write(t.generate) }
end

#save_segments_to(output_path) ⇒ Object



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/mp4.rb', line 71

def save_segments_to(output_path)
  base_path = Pathname.new(output_path)

  FileUtils.mkdir_p(base_path)

  cached_segments = MP4::Nolan::Service.divide_into_segments(cache)

  cached_segments.each_with_index do |cached_part, index|
    t = Layout::Builder.call(cache, cached_part, source)

    File.open(base_path.join("#{index}.mp4"), 'wb') do |file|
      file.write(t.generate)
    end
  end
end