Class: MP4::Cache::Memory
- Inherits:
-
Object
- Object
- MP4::Cache::Memory
- Defined in:
- lib/mp4/cache/memory.rb
Overview
Drop-in metadata cache backed by Ruby data structures.
Implements the method-style API (add_track, tracks, chunks, segments, add_sample, each_sample, add_ftyp/ftyp, add_dinf_content/dinf_content, add_stsd_content/stsd_content, add_elst_list_item/elst_entries, add_segment_sample, segment_samples).
Deliberately does NOT emulate raw SQL: #query and #execute raise — see docs/CACHE_MIGRATION.md for the list of call sites (layout writers, Nolan segmentation, DASH manifest) that still speak SQL against MP4::Cache::SQLite and need method-API replacements before this backend can drive the full pipeline.
Instance Attribute Summary collapse
-
#ftyp ⇒ Object
readonly
Returns the value of attribute ftyp.
Instance Method Summary collapse
- #add_chunk(track_id:, chunk_index:, samples_per_chunk:, sample_counter:, description_index:, offset:) ⇒ Object
- #add_dinf_content(track_id:, content:) ⇒ Object
- #add_elst_list_item(track_id:, track_duration:, media_time:, media_rate:, media_rate_fraction:) ⇒ Object
- #add_ftyp(major_brand:, minor_version:, compatible_brands:) ⇒ Object
- #add_sample(track_id:, chunk_index:, sample_index:, chunk_offset:, sample_delta:, delta_sum:, absolute_delta_sum:, composition_offset:, sample_size:, sample_offset: 0, original_chunk_offset: 0, is_keyframe: 'FALSE') ⇒ Object
- #add_segment_sample(track_id:, segment_index:, chunk_index:, sample_index:, chunk_offset:, sample_delta:, delta_sum:, absolute_delta_sum:, composition_offset:, sample_size:, sample_offset: 0, original_chunk_offset: 0, is_keyframe: 'FALSE') ⇒ Object
- #add_stsd_content(track_id:, content:) ⇒ Object
- #add_track(**opts) ⇒ Object
- #chunks(track_id) ⇒ Object
- #dinf_content(track_id) ⇒ Object
- #each_sample(track_id, &block) ⇒ Object
- #each_sample_ordered_across_tracks ⇒ Object
- #each_track(&block) ⇒ Object
- #elst_entries(track_id) ⇒ Object
- #first_sample(track_id) ⇒ Object
- #first_sample_size(track_id) ⇒ Object
- #first_video_track_id ⇒ Object
-
#initialize(_location = nil) ⇒ Memory
constructor
A new instance of Memory.
- #keyframes_grouped_by_second(track_id) ⇒ Object
- #last_sample(track_id) ⇒ Object
- #max_segment_duration_ticks(track_id) ⇒ Object
- #min_sample_offset ⇒ Object
- #next_track_id ⇒ Object
- #query(*_args) ⇒ Object (also: #execute)
- #sample_at(track_id, sample_index) ⇒ Object
- #sample_c_delta_pairs(track_id) ⇒ Object
- #sample_chunk_indices(track_id) ⇒ Object
- #sample_count(track_id) ⇒ Object
- #sample_delta_pairs(track_id) ⇒ Object
- #sample_delta_sum_ms(track_id, sample_index) ⇒ Object
- #sample_size_list(track_id) ⇒ Object
- #samples(track_id) ⇒ Object
- #samples_between_ms(track_id, from_ms, to_ms) ⇒ Object
- #segment_bytes_sizes_offsets(track_id, segment_index) ⇒ Object
- #segment_duration_ticks(track_id, segment_index) ⇒ Object
- #segment_duration_ticks_by_index(track_id) ⇒ Object
- #segment_earliest_presentation_ticks(track_id, segment_index) ⇒ Object
- #segment_samples(track_id, segment_index) ⇒ Object
- #segment_size_bytes(track_id, segment_index) ⇒ Object
- #segments(track_id) ⇒ Object
- #stsd_content(track_id) ⇒ Object
- #sum_delta_between(track_id, from_sample_index, to_sample_index) ⇒ Object
- #sync_sample_indices(track_id) ⇒ Object
- #track(track_id) ⇒ Object
- #track_sample_delta_sum(track_id) ⇒ Object
- #tracks ⇒ Object
- #tracks_asc ⇒ Object
- #transaction ⇒ Object
- #uniform_sample_size?(track_id) ⇒ Boolean
Constructor Details
#initialize(_location = nil) ⇒ Memory
Returns a new instance of Memory.
15 16 17 18 19 20 21 22 23 24 |
# File 'lib/mp4/cache/memory.rb', line 15 def initialize(_location = nil) @ftyp = nil @tracks = {} @chunks = Hash.new { |h, k| h[k] = [] } @samples = Hash.new { |h, k| h[k] = [] } @segment_samples = Hash.new { |h, k| h[k] = [] } @elst = Hash.new { |h, k| h[k] = [] } @dinf = {} @stsd = {} end |
Instance Attribute Details
#ftyp ⇒ Object (readonly)
Returns the value of attribute ftyp.
45 46 47 |
# File 'lib/mp4/cache/memory.rb', line 45 def ftyp @ftyp end |
Instance Method Details
#add_chunk(track_id:, chunk_index:, samples_per_chunk:, sample_counter:, description_index:, offset:) ⇒ Object
83 84 85 86 87 88 89 90 91 92 |
# File 'lib/mp4/cache/memory.rb', line 83 def add_chunk(track_id:, chunk_index:, samples_per_chunk:, sample_counter:, description_index:, offset:) @chunks[track_id] << { 'track_id' => track_id, 'chunk_index' => chunk_index, 'samples_per_chunk' => samples_per_chunk, 'sample_counter' => sample_counter, 'description_index' => description_index, 'offset' => offset, } end |
#add_dinf_content(track_id:, content:) ⇒ Object
162 163 164 |
# File 'lib/mp4/cache/memory.rb', line 162 def add_dinf_content(track_id:, content:) @dinf[track_id.to_i] = content end |
#add_elst_list_item(track_id:, track_duration:, media_time:, media_rate:, media_rate_fraction:) ⇒ Object
148 149 150 151 152 153 154 155 156 |
# File 'lib/mp4/cache/memory.rb', line 148 def add_elst_list_item(track_id:, track_duration:, media_time:, media_rate:, media_rate_fraction:) @elst[track_id] << { 'track_id' => track_id, 'track_duration' => track_duration, 'media_time' => media_time, 'media_rate' => media_rate, 'media_rate_fraction' => media_rate_fraction, } end |
#add_ftyp(major_brand:, minor_version:, compatible_brands:) ⇒ Object
37 38 39 40 41 42 43 |
# File 'lib/mp4/cache/memory.rb', line 37 def add_ftyp(major_brand:, minor_version:, compatible_brands:) @ftyp = { 'major_brand' => major_brand.to_s, 'minor_version' => minor_version.to_i, 'compatible_brands' => compatible_brands.to_s, } end |
#add_sample(track_id:, chunk_index:, sample_index:, chunk_offset:, sample_delta:, delta_sum:, absolute_delta_sum:, composition_offset:, sample_size:, sample_offset: 0, original_chunk_offset: 0, is_keyframe: 'FALSE') ⇒ Object
102 103 104 105 106 107 108 109 110 111 |
# File 'lib/mp4/cache/memory.rb', line 102 def add_sample(track_id:, chunk_index:, sample_index:, chunk_offset:, sample_delta:, delta_sum:, absolute_delta_sum:, composition_offset:, sample_size:, sample_offset: 0, original_chunk_offset: 0, is_keyframe: 'FALSE') @samples[track_id] << sample_row( track_id: track_id, chunk_index: chunk_index, sample_index: sample_index, sample_size: sample_size, sample_offset: sample_offset, chunk_offset: chunk_offset, original_chunk_offset: original_chunk_offset, sample_delta: sample_delta, delta_sum: delta_sum, absolute_delta_sum: absolute_delta_sum, composition_offset: composition_offset, is_keyframe: is_keyframe, ) end |
#add_segment_sample(track_id:, segment_index:, chunk_index:, sample_index:, chunk_offset:, sample_delta:, delta_sum:, absolute_delta_sum:, composition_offset:, sample_size:, sample_offset: 0, original_chunk_offset: 0, is_keyframe: 'FALSE') ⇒ Object
121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/mp4/cache/memory.rb', line 121 def add_segment_sample(track_id:, segment_index:, chunk_index:, sample_index:, chunk_offset:, sample_delta:, delta_sum:, absolute_delta_sum:, composition_offset:, sample_size:, sample_offset: 0, original_chunk_offset: 0, is_keyframe: 'FALSE') row = sample_row( track_id: track_id, chunk_index: chunk_index, sample_index: sample_index, sample_size: sample_size, sample_offset: sample_offset, chunk_offset: chunk_offset, original_chunk_offset: original_chunk_offset, sample_delta: sample_delta, delta_sum: delta_sum, absolute_delta_sum: absolute_delta_sum, composition_offset: composition_offset, is_keyframe: is_keyframe, ) row['segment_index'] = segment_index @segment_samples[track_id] << row end |
#add_stsd_content(track_id:, content:) ⇒ Object
170 171 172 |
# File 'lib/mp4/cache/memory.rb', line 170 def add_stsd_content(track_id:, content:) @stsd[track_id.to_i] = content end |
#add_track(**opts) ⇒ Object
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/mp4/cache/memory.rb', line 47 def add_track(**opts) row = { 'track_id' => opts.fetch(:track_id), 'handler_type' => opts.fetch(:handler_type), 'handler_name' => opts.fetch(:handler_name), 'have_composition_time' => opts.fetch(:have_composition_time), 'timescale' => opts.fetch(:timescale), 'creation_time' => opts.fetch(:creation_time), 'width' => opts.fetch(:width), 'height' => opts.fetch(:height), 'volume' => opts.fetch(:volume), 'channel_count' => opts.fetch(:channel_count), 'par' => opts.fetch(:par), 'sar' => opts.fetch(:sar), 'codec' => opts.fetch(:codec), 'max_bitrate' => opts.fetch(:max_bitrate), 'avg_bitrate' => opts.fetch(:avg_bitrate), 'sample_rate' => opts.fetch(:sample_rate), 'mime_type' => opts.fetch(:mime_type), 'alternate_group' => opts.fetch(:alternate_group), } @tracks[row['track_id']] = row end |
#chunks(track_id) ⇒ Object
94 95 96 |
# File 'lib/mp4/cache/memory.rb', line 94 def chunks(track_id) @chunks[track_id] end |
#dinf_content(track_id) ⇒ Object
166 167 168 |
# File 'lib/mp4/cache/memory.rb', line 166 def dinf_content(track_id) @dinf[track_id.to_i] end |
#each_sample(track_id, &block) ⇒ Object
113 114 115 |
# File 'lib/mp4/cache/memory.rb', line 113 def each_sample(track_id, &block) @samples[track_id].each(&block) end |
#each_sample_ordered_across_tracks ⇒ Object
264 265 266 267 |
# File 'lib/mp4/cache/memory.rb', line 264 def each_sample_ordered_across_tracks rows = @samples.values.flatten.sort_by { |s| [s['sample_index'], s['track_id']] } rows.each { |s| yield({ 'track_id' => s['track_id'], 'size' => s['size'], 'offset' => s['offset'] }) } end |
#each_track(&block) ⇒ Object
98 99 100 |
# File 'lib/mp4/cache/memory.rb', line 98 def each_track(&block) @chunks.keys.uniq.each(&block) end |
#elst_entries(track_id) ⇒ Object
158 159 160 |
# File 'lib/mp4/cache/memory.rb', line 158 def elst_entries(track_id) @elst[track_id] end |
#first_sample(track_id) ⇒ Object
191 192 193 |
# File 'lib/mp4/cache/memory.rb', line 191 def first_sample(track_id) @samples[track_id].min_by { |s| s['sample_index'] } end |
#first_sample_size(track_id) ⇒ Object
247 248 249 250 |
# File 'lib/mp4/cache/memory.rb', line 247 def first_sample_size(track_id) first = first_sample(track_id) first && first['size'] end |
#first_video_track_id ⇒ Object
178 179 180 |
# File 'lib/mp4/cache/memory.rb', line 178 def first_video_track_id @tracks.values.select { |t| t['handler_type'] == 'vide' }.map { |t| t['track_id'] }.min end |
#keyframes_grouped_by_second(track_id) ⇒ Object
222 223 224 225 226 227 228 229 230 231 |
# File 'lib/mp4/cache/memory.rb', line 222 def keyframes_grouped_by_second(track_id) buckets = {} @samples[track_id].each do |s| next unless s['keyframe'] == 'TRUE' bucket = s['delta_sum_millisec'].to_i / 1000 buckets[bucket] ||= { 'sample_index' => s['sample_index'], 'delta_sum' => s['delta_sum'] } end buckets.values end |
#last_sample(track_id) ⇒ Object
195 196 197 |
# File 'lib/mp4/cache/memory.rb', line 195 def last_sample(track_id) @samples[track_id].max_by { |s| s['sample_index'] } end |
#max_segment_duration_ticks(track_id) ⇒ Object
297 298 299 300 |
# File 'lib/mp4/cache/memory.rb', line 297 def max_segment_duration_ticks(track_id) by_seg = segment_duration_ticks_by_index(track_id) by_seg.map { |r| r['duration'] }.max end |
#min_sample_offset ⇒ Object
269 270 271 |
# File 'lib/mp4/cache/memory.rb', line 269 def min_sample_offset @samples.values.flatten.map { |s| s['offset'] }.min end |
#next_track_id ⇒ Object
182 183 184 185 |
# File 'lib/mp4/cache/memory.rb', line 182 def next_track_id max = @tracks.keys.max max && max + 1 end |
#query(*_args) ⇒ Object Also known as: execute
30 31 32 33 34 |
# File 'lib/mp4/cache/memory.rb', line 30 def query(*_args) raise NotImplementedError, 'MP4::Cache::Memory does not support raw SQL queries. ' \ 'Use the method-style API instead (see docs/CACHE_MIGRATION.md).' end |
#sample_at(track_id, sample_index) ⇒ Object
199 200 201 |
# File 'lib/mp4/cache/memory.rb', line 199 def sample_at(track_id, sample_index) @samples[track_id].find { |s| s['sample_index'] == sample_index } end |
#sample_c_delta_pairs(track_id) ⇒ Object
256 257 258 |
# File 'lib/mp4/cache/memory.rb', line 256 def sample_c_delta_pairs(track_id) @samples[track_id].sort_by { |s| s['sample_index'] }.map { |s| { 'sample_index' => s['sample_index'], 'c_delta' => s['c_delta'] } } end |
#sample_chunk_indices(track_id) ⇒ Object
260 261 262 |
# File 'lib/mp4/cache/memory.rb', line 260 def sample_chunk_indices(track_id) @samples[track_id].sort_by { |s| s['sample_index'] }.map { |s| s['chunk_index'] } end |
#sample_count(track_id) ⇒ Object
187 188 189 |
# File 'lib/mp4/cache/memory.rb', line 187 def sample_count(track_id) @samples[track_id].size end |
#sample_delta_pairs(track_id) ⇒ Object
252 253 254 |
# File 'lib/mp4/cache/memory.rb', line 252 def sample_delta_pairs(track_id) @samples[track_id].sort_by { |s| s['sample_index'] }.map { |s| { 'sample_index' => s['sample_index'], 'delta' => s['delta'] } } end |
#sample_delta_sum_ms(track_id, sample_index) ⇒ Object
203 204 205 206 |
# File 'lib/mp4/cache/memory.rb', line 203 def sample_delta_sum_ms(track_id, sample_index) row = sample_at(track_id, sample_index) row && row['delta_sum_millisec'] end |
#sample_size_list(track_id) ⇒ Object
239 240 241 |
# File 'lib/mp4/cache/memory.rb', line 239 def sample_size_list(track_id) @samples[track_id].sort_by { |s| s['sample_index'] }.map { |s| s['size'] } end |
#samples(track_id) ⇒ Object
117 118 119 |
# File 'lib/mp4/cache/memory.rb', line 117 def samples(track_id) @samples[track_id] end |
#samples_between_ms(track_id, from_ms, to_ms) ⇒ Object
233 234 235 236 237 |
# File 'lib/mp4/cache/memory.rb', line 233 def samples_between_ms(track_id, from_ms, to_ms) @samples[track_id] .select { |s| s['delta_sum_millisec'] >= from_ms && s['delta_sum_millisec'] <= to_ms } .sort_by { |s| s['sample_index'] } end |
#segment_bytes_sizes_offsets(track_id, segment_index) ⇒ Object
302 303 304 305 306 307 |
# File 'lib/mp4/cache/memory.rb', line 302 def segment_bytes_sizes_offsets(track_id, segment_index) @segment_samples[track_id] .select { |s| s['segment_index'] == segment_index } .sort_by { |s| s['sample_index'] } .map { |s| { 'size' => s['size'], 'offset' => s['offset'] } } end |
#segment_duration_ticks(track_id, segment_index) ⇒ Object
273 274 275 276 277 |
# File 'lib/mp4/cache/memory.rb', line 273 def segment_duration_ticks(track_id, segment_index) @segment_samples[track_id] .select { |s| s['segment_index'] == segment_index } .sum { |s| s['delta'].to_i } end |
#segment_duration_ticks_by_index(track_id) ⇒ Object
292 293 294 295 |
# File 'lib/mp4/cache/memory.rb', line 292 def segment_duration_ticks_by_index(track_id) grouped = @segment_samples[track_id].group_by { |s| s['segment_index'] } grouped.map { |i, ss| { 'segment_index' => i, 'duration' => ss.sum { |s| s['delta'].to_i } } }.sort_by { |r| r['segment_index'] } end |
#segment_earliest_presentation_ticks(track_id, segment_index) ⇒ Object
279 280 281 282 283 284 |
# File 'lib/mp4/cache/memory.rb', line 279 def segment_earliest_presentation_ticks(track_id, segment_index) @segment_samples[track_id] .select { |s| s['segment_index'] == segment_index } .map { |s| s['delta_sum'] } .min end |
#segment_samples(track_id, segment_index) ⇒ Object
142 143 144 145 146 |
# File 'lib/mp4/cache/memory.rb', line 142 def segment_samples(track_id, segment_index) @segment_samples[track_id] .select { |s| s['segment_index'] == segment_index } .sort_by { |s| s['sample_index'] } end |
#segment_size_bytes(track_id, segment_index) ⇒ Object
286 287 288 289 290 |
# File 'lib/mp4/cache/memory.rb', line 286 def segment_size_bytes(track_id, segment_index) @segment_samples[track_id] .select { |s| s['segment_index'] == segment_index } .sum { |s| s['size'].to_i } end |
#segments(track_id) ⇒ Object
134 135 136 137 138 139 140 |
# File 'lib/mp4/cache/memory.rb', line 134 def segments(track_id) @segment_samples[track_id] .map { |s| s['segment_index'] } .uniq .sort .map { |i| { 'segment_index' => i } } end |
#stsd_content(track_id) ⇒ Object
174 175 176 |
# File 'lib/mp4/cache/memory.rb', line 174 def stsd_content(track_id) @stsd[track_id.to_i] end |
#sum_delta_between(track_id, from_sample_index, to_sample_index) ⇒ Object
208 209 210 211 212 |
# File 'lib/mp4/cache/memory.rb', line 208 def sum_delta_between(track_id, from_sample_index, to_sample_index) @samples[track_id] .select { |s| s['sample_index'] >= from_sample_index && s['sample_index'] <= to_sample_index } .sum { |s| s['delta'].to_i } end |
#sync_sample_indices(track_id) ⇒ Object
218 219 220 |
# File 'lib/mp4/cache/memory.rb', line 218 def sync_sample_indices(track_id) @samples[track_id].select { |s| s['keyframe'] == 'TRUE' }.map { |s| s['sample_index'] }.sort end |
#track(track_id) ⇒ Object
79 80 81 |
# File 'lib/mp4/cache/memory.rb', line 79 def track(track_id) @tracks.fetch(track_id) end |
#track_sample_delta_sum(track_id) ⇒ Object
214 215 216 |
# File 'lib/mp4/cache/memory.rb', line 214 def track_sample_delta_sum(track_id) @samples[track_id].sum { |s| s['delta'].to_i } end |
#tracks ⇒ Object
71 72 73 |
# File 'lib/mp4/cache/memory.rb', line 71 def tracks @tracks.values.sort_by { |t| t['handler_type'].to_s }.reverse end |
#tracks_asc ⇒ Object
75 76 77 |
# File 'lib/mp4/cache/memory.rb', line 75 def tracks_asc @tracks.values.sort_by { |t| t['track_id'] } end |
#transaction ⇒ Object
26 27 28 |
# File 'lib/mp4/cache/memory.rb', line 26 def transaction yield end |
#uniform_sample_size?(track_id) ⇒ Boolean
243 244 245 |
# File 'lib/mp4/cache/memory.rb', line 243 def uniform_sample_size?(track_id) @samples[track_id].map { |s| s['size'] }.uniq.size == 1 end |