Class: HLSManifest
Constant Summary
Constants included from HLSConstants
HLSConstants::EXTINF, HLSConstants::EXTM3U, HLSConstants::EXT_X_ALLOW_CACHE, HLSConstants::EXT_X_CUE_IN, HLSConstants::EXT_X_CUE_OUT, HLSConstants::EXT_X_DISCONTINUITY, HLSConstants::EXT_X_ENDLIST, HLSConstants::EXT_X_FAXS_CM, HLSConstants::EXT_X_KEY, HLSConstants::EXT_X_KEY_ATTRS, HLSConstants::EXT_X_MEDIA, HLSConstants::EXT_X_MEDIA_SEQUENCE, HLSConstants::EXT_X_PLAYLIST_TYPE, HLSConstants::EXT_X_STREAM_INF, HLSConstants::EXT_X_STREAM_INF_ATTRS, HLSConstants::EXT_X_TARGETDURATION, HLSConstants::EXT_X_VERSION, HLSConstants::FIXNUM_MAX, HLSConstants::SUPPORTED_M3U8_VERSIONS
Instance Attribute Summary collapse
-
#cue_out_items ⇒ Object
Returns the value of attribute cue_out_items.
-
#line_items ⇒ Object
Returns the value of attribute line_items.
-
#media_sequence ⇒ Object
Returns the value of attribute media_sequence.
-
#playlist_type ⇒ Object
Returns the value of attribute playlist_type.
-
#target_duration ⇒ Object
Returns the value of attribute target_duration.
-
#version ⇒ Object
Returns the value of attribute version.
Instance Method Summary collapse
- #allow_cache? ⇒ Boolean
- #build ⇒ Object
- #empty? ⇒ Boolean
-
#initialize(content = nil) ⇒ HLSManifest
constructor
A new instance of HLSManifest.
- #is_live? ⇒ Boolean
- #is_master? ⇒ Boolean
- #length ⇒ Object
- #media_length ⇒ Object
- #parse(content) ⇒ Object
Constructor Details
#initialize(content = nil) ⇒ HLSManifest
Returns a new instance of HLSManifest.
119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/hls_manifest.rb', line 119 def initialize(content = nil) @allow_cache = nil @media_sequence = nil @playlist_type = nil @target_duration = nil @version = nil @is_live = true @line_items = [] @cue_out_items = [] parse(content) end |
Instance Attribute Details
#cue_out_items ⇒ Object
Returns the value of attribute cue_out_items.
117 118 119 |
# File 'lib/hls_manifest.rb', line 117 def cue_out_items @cue_out_items end |
#line_items ⇒ Object
Returns the value of attribute line_items.
116 117 118 |
# File 'lib/hls_manifest.rb', line 116 def line_items @line_items end |
#media_sequence ⇒ Object
Returns the value of attribute media_sequence.
112 113 114 |
# File 'lib/hls_manifest.rb', line 112 def media_sequence @media_sequence end |
#playlist_type ⇒ Object
Returns the value of attribute playlist_type.
113 114 115 |
# File 'lib/hls_manifest.rb', line 113 def playlist_type @playlist_type end |
#target_duration ⇒ Object
Returns the value of attribute target_duration.
114 115 116 |
# File 'lib/hls_manifest.rb', line 114 def target_duration @target_duration end |
#version ⇒ Object
Returns the value of attribute version.
115 116 117 |
# File 'lib/hls_manifest.rb', line 115 def version @version end |
Instance Method Details
#allow_cache? ⇒ Boolean
248 249 250 251 |
# File 'lib/hls_manifest.rb', line 248 def allow_cache? # Not raising an error here in case the EXT-X-ALLOW-CACHE tag is supported for master-level m3u8s @allow_cache.nil? || @allow_cache == "YES" end |
#build ⇒ Object
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
# File 'lib/hls_manifest.rb', line 273 def build if is_master? return <<-EOT.remove_extra_spaces #{EXTM3U} #{@line_items.map { |item| item.build }.join("\n")} EOT else return <<-EOT.remove_extra_spaces #{EXTM3U} #{EXT_X_ALLOW_CACHE + ":" + @allow_cache unless @allow_cache.nil?} #{EXT_X_MEDIA_SEQUENCE + ":" + @media_sequence.to_s unless @media_sequence.nil?} #{EXT_X_PLAYLIST_TYPE + ":" + @playlist_type unless @playlist_type.nil?} #{EXT_X_TARGETDURATION + ":" + @target_duration.to_s unless @target_duration.nil?} #{EXT_X_VERSION + ":" + @version.to_s unless @version.nil?} #{@line_items.map { |item| item.build }.join("\n")} #{EXT_X_ENDLIST unless is_live?} EOT end end |
#empty? ⇒ Boolean
244 245 246 |
# File 'lib/hls_manifest.rb', line 244 def empty? @line_items.empty? end |
#is_live? ⇒ Boolean
263 264 265 266 |
# File 'lib/hls_manifest.rb', line 263 def is_live? raise NoMethodError, "A master-level manifest cannot access this method" if is_master? @is_live end |
#is_master? ⇒ Boolean
268 269 270 271 |
# File 'lib/hls_manifest.rb', line 268 def is_master? # Note: This currently assumes an empty m3u8 is NOT a master m3u8 !@line_items.empty? && @line_items[0].instance_of?(HLSPlaylist) end |
#length ⇒ Object
253 254 255 |
# File 'lib/hls_manifest.rb', line 253 def length @line_items.length end |
#media_length ⇒ Object
257 258 259 260 261 |
# File 'lib/hls_manifest.rb', line 257 def media_length # Note(bz): Should we be raising errors here? I'm not sure what our standard or the Ruby standard is raise NoMethodError, "A master-level manifest cannot access this method" if is_master? @line_items.inject(0) { |sum, x| sum + x.duration } end |
#parse(content) ⇒ Object
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 |
# File 'lib/hls_manifest.rb', line 132 def parse(content) return if content.nil? || content.empty? last_attributes = [] last_key = nil last_playlist_or_media_segment = nil = [] last_sequence = 0 # Remove any blank lines (including lines with spaces just to be safe) lines = content.split("\n").map(&:strip).reject(&:empty?) lines.each do |line| if line.start_with?(EXT_X_TARGETDURATION) @target_duration = line.split(":").last.to_i elsif line.start_with?(EXT_X_MEDIA_SEQUENCE) @media_sequence = line.split(":").last.to_i elsif line.start_with?(EXT_X_PLAYLIST_TYPE) @playlist_type = line.split(":").last elsif line.start_with?(EXT_X_ENDLIST) @is_live = false elsif line.start_with?(EXT_X_ALLOW_CACHE) @allow_cache = line.split(":").last elsif line.start_with?(EXT_X_VERSION) @version = line.split(":").last.to_i elsif line.start_with?(EXT_X_STREAM_INF) attributes = {} parse_csv(line.split(":").last).each do |value| attribute = value.split("=") attributes[attribute[0].strip.upcase] = attribute[1].strip end playlist = HLSPlaylist.new playlist.bandwidth = attributes[EXT_X_STREAM_INF_ATTRS[:BANDWIDTH]] playlist.program_id = attributes[EXT_X_STREAM_INF_ATTRS[:PROGRAM_ID]] playlist.codecs = attributes[EXT_X_STREAM_INF_ATTRS[:CODECS]] playlist.resolution = attributes[EXT_X_STREAM_INF_ATTRS[:RESOLUTION]] playlist.audio = attributes[EXT_X_STREAM_INF_ATTRS[:AUDIO]] playlist.video = attributes[EXT_X_STREAM_INF_ATTRS[:VIDEO]] playlist.bandwidth = playlist.bandwidth.to_i if playlist.bandwidth playlist.program_id = playlist.program_id if playlist.program_id playlist.codecs = parse_csv(playlist.codecs) if playlist.codecs last_playlist_or_media_segment = playlist @line_items.push(playlist) elsif line.start_with?(EXTINF) attributes = parse_csv(line.split(":").last) media_segment = HLSMediaSegment.new media_segment.duration = attributes[0].to_f media_segment.title = attributes[1] media_segment.key = last_key media_segment.sequence = last_sequence last_sequence += 1 if last_playlist_or_media_segment && last_playlist_or_media_segment.key = last_key media_segment.show_key = false end last_playlist_or_media_segment = media_segment @line_items.push(media_segment) elsif line.start_with?(EXT_X_DISCONTINUITY) last_attributes.push({ :key => 'discontinuity', :val => true }) elsif line.start_with?(EXT_X_CUE_OUT) last_attributes.push({ :key => 'is_cue_out', :val => true }) cue_dur = line.match('DURATION=(\d*)') last_attributes.push({ :key => 'cue_out_duration', :val => cue_dur[1].to_i }) if cue_dur # may or may not contain a duration. elsif line.start_with?(EXT_X_CUE_IN) last_attributes.push({ :key => 'is_cue_in', :val => true }) elsif line.start_with?(EXT_X_KEY) attributes = {} parse_csv(line.split(":", 2).last).each do |attribute| attribute = attribute.split("=") attributes[attribute[0].strip.upcase] = attribute[1].strip end key = HLSKey.new(attributes[EXT_X_KEY_ATTRS[:METHOD]], attributes[EXT_X_KEY_ATTRS[:URI]], attributes[EXT_X_KEY_ATTRS[:IV]]) key.uri.gsub!("\"", "") if key.uri key.iv = key.iv.hex if key.iv last_key = key elsif line.start_with?("#") next if EXTM3U == line # Handle other unhandled tags or comments # Note: Assume that all unhandled tags belong to the sub playlist or media segment that follow # This means that trailing tags are not parsed # TODO: Add support for other manifest level tags that don't belong to individual line items .push(line) else # We should be dealing with the location of a sub playlist or a media segment here # Note: Assume that a manifest cannot contain a mixture of sub playlists and media segments if last_playlist_or_media_segment.instance_of?(HLSMediaSegment) last_attributes.each{|x| last_playlist_or_media_segment.send("#{x[:key]}=", x[:val]) } @cue_out_items.push(last_playlist_or_media_segment) if last_playlist_or_media_segment.is_cue_out end last_playlist_or_media_segment.location = line last_playlist_or_media_segment. = last_attributes.clear = [] end end end |