format_parser 2.3.0 → 2.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +21 -0
- data/README.md +13 -6
- data/format_parser.gemspec +1 -0
- data/lib/format_parser/version.rb +1 -1
- data/lib/io_utils.rb +18 -33
- data/lib/parsers/cr3_parser/decoder.rb +2 -2
- data/lib/parsers/cr3_parser.rb +13 -11
- data/lib/parsers/heif_parser.rb +46 -46
- data/lib/parsers/iso_base_media_file_format/box.rb +80 -0
- data/lib/parsers/iso_base_media_file_format/decoder.rb +348 -377
- data/lib/parsers/iso_base_media_file_format/utils.rb +89 -0
- data/lib/parsers/mov_parser/decoder.rb +53 -0
- data/lib/parsers/mov_parser.rb +48 -0
- data/lib/parsers/mp4_parser.rb +80 -0
- data/lib/parsers/pdf_parser.rb +5 -2
- data/lib/parsers/webp_parser.rb +2 -2
- data/spec/format_parser_spec.rb +1 -1
- data/spec/parsers/cr3_parser_spec.rb +3 -3
- data/spec/parsers/iso_base_media_file_format/box_spec.rb +399 -0
- data/spec/parsers/iso_base_media_file_format/decoder_spec.rb +117 -151
- data/spec/parsers/iso_base_media_file_format/utils_spec.rb +632 -0
- data/spec/parsers/mov_parser_spec.rb +139 -0
- data/spec/parsers/mp4_parser_spec.rb +188 -0
- data/spec/parsers/pdf_parser_spec.rb +37 -23
- metadata +25 -5
- data/lib/parsers/moov_parser/decoder.rb +0 -353
- data/lib/parsers/moov_parser.rb +0 -165
- data/spec/parsers/moov_parser_spec.rb +0 -144
| @@ -1,7 +1,7 @@ | |
| 1 | 
            -
            # This class provides generic methods for parsing file formats based on QuickTime-style " | 
| 1 | 
            +
            # This class provides generic methods for parsing file formats based on QuickTime-style "boxes", such as those seen in
         | 
| 2 2 | 
             
            # the ISO base media file format (ISO/IEC 14496-12), a.k.a MPEG-4, and those that extend it (MP4, CR3, HEIF, etc.).
         | 
| 3 3 | 
             
            #
         | 
| 4 | 
            -
            # For more information on  | 
| 4 | 
            +
            # For more information on boxes, see https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap1/qtff1.html
         | 
| 5 5 | 
             
            # or https://b.goeswhere.com/ISO_IEC_14496-12_2015.pdf.
         | 
| 6 6 | 
             
            #
         | 
| 7 7 | 
             
            # TODO: The vast majority of the methods have been commented out here. This decision was taken to expedite the release
         | 
| @@ -9,66 +9,38 @@ | |
| 9 9 | 
             
            #   entirety. We should migrate existing formats that are based on the ISO base media file format and reintroduce these
         | 
| 10 10 | 
             
            #   methods with tests down-the-line.
         | 
| 11 11 |  | 
| 12 | 
            +
            require 'matrix'
         | 
| 13 | 
            +
            require 'parsers/iso_base_media_file_format/box'
         | 
| 14 | 
            +
             | 
| 12 15 | 
             
            module FormatParser
         | 
| 13 16 | 
             
              module ISOBaseMediaFileFormat
         | 
| 14 17 | 
             
                class Decoder
         | 
| 15 18 | 
             
                  include FormatParser::IOUtils
         | 
| 16 19 |  | 
| 17 | 
            -
                   | 
| 18 | 
            -
             | 
| 19 | 
            -
                      super
         | 
| 20 | 
            -
                      self.fields ||= {}
         | 
| 21 | 
            -
                      self.children ||= []
         | 
| 22 | 
            -
                    end
         | 
| 23 | 
            -
             | 
| 24 | 
            -
                    # Find and return the first descendent (using depth-first search) of a given type.
         | 
| 25 | 
            -
                    #
         | 
| 26 | 
            -
                    # @param [Array<String>] types
         | 
| 27 | 
            -
                    # @return [Atom, nil]
         | 
| 28 | 
            -
                    def find_first_descendent(types)
         | 
| 29 | 
            -
                      children.each do |child|
         | 
| 30 | 
            -
                        return child if types.include?(child.type)
         | 
| 31 | 
            -
                        if (descendent = child.find_first_descendent(types))
         | 
| 32 | 
            -
                          return descendent
         | 
| 33 | 
            -
                        end
         | 
| 34 | 
            -
                      end
         | 
| 35 | 
            -
                      nil
         | 
| 36 | 
            -
                    end
         | 
| 37 | 
            -
             | 
| 38 | 
            -
                    # Find and return all descendents of a given type.
         | 
| 39 | 
            -
                    #
         | 
| 40 | 
            -
                    # @param [Array<String>] types
         | 
| 41 | 
            -
                    # @return [Array<Atom>]
         | 
| 42 | 
            -
                    def select_descendents(types)
         | 
| 43 | 
            -
                      children.map do |child|
         | 
| 44 | 
            -
                        descendents = child.select_descendents(types)
         | 
| 45 | 
            -
                        types.include?(child.type) ? [child] + descendents : descendents
         | 
| 46 | 
            -
                      end.flatten
         | 
| 47 | 
            -
                    end
         | 
| 48 | 
            -
                  end
         | 
| 49 | 
            -
             | 
| 20 | 
            +
                  # Attempt to build the ISOBMFF box tree represented in the given IO object.
         | 
| 21 | 
            +
                  #
         | 
| 50 22 | 
             
                  # @param [Integer] max_read
         | 
| 51 | 
            -
                  # @param [IO, FormatParser::IOConstraint] io
         | 
| 52 | 
            -
                  # @return [Array< | 
| 53 | 
            -
                  def  | 
| 23 | 
            +
                  # @param [IO, StringIO, FormatParser::IOConstraint] io
         | 
| 24 | 
            +
                  # @return [Array<Box>]
         | 
| 25 | 
            +
                  def build_box_tree(max_read, io = nil)
         | 
| 54 26 | 
             
                    @buf = FormatParser::IOConstraint.new(io) if io
         | 
| 55 27 | 
             
                    raise ArgumentError, "IO missing - supply a valid IO object" unless @buf
         | 
| 56 | 
            -
                     | 
| 28 | 
            +
                    boxes = []
         | 
| 57 29 | 
             
                    max_pos = @buf.pos + max_read
         | 
| 58 30 | 
             
                    loop do
         | 
| 59 31 | 
             
                      break if @buf.pos >= max_pos
         | 
| 60 | 
            -
                       | 
| 61 | 
            -
                      break unless  | 
| 62 | 
            -
                       | 
| 32 | 
            +
                      box = parse_box
         | 
| 33 | 
            +
                      break unless box
         | 
| 34 | 
            +
                      boxes << box
         | 
| 63 35 | 
             
                    end
         | 
| 64 | 
            -
                     | 
| 36 | 
            +
                    boxes
         | 
| 65 37 | 
             
                  end
         | 
| 66 38 |  | 
| 67 39 | 
             
                  protected
         | 
| 68 40 |  | 
| 69 | 
            -
                  # A mapping of  | 
| 70 | 
            -
                  # and return the  | 
| 71 | 
            -
                   | 
| 41 | 
            +
                  # A mapping of box types to their respective parser methods. Each method must take a single Integer parameter, size,
         | 
| 42 | 
            +
                  # and return the box's fields and children where appropriate as a Hash and Array of Boxes respectively.
         | 
| 43 | 
            +
                  BOX_PARSERS = {
         | 
| 72 44 | 
             
                    # 'bxml' => :bxml,
         | 
| 73 45 | 
             
                    # 'co64' => :co64,
         | 
| 74 46 | 
             
                    # 'cprt' => :cprt,
         | 
| @@ -81,9 +53,9 @@ module FormatParser | |
| 81 53 | 
             
                    # 'fiin' => :fiin,
         | 
| 82 54 | 
             
                    # 'fire' => :fire,
         | 
| 83 55 | 
             
                    # 'fpar' => :fpar,
         | 
| 84 | 
            -
                     | 
| 56 | 
            +
                    'ftyp' => :typ,
         | 
| 85 57 | 
             
                    # 'gitn' => :gitn,
         | 
| 86 | 
            -
                     | 
| 58 | 
            +
                    'hdlr' => :hdlr,
         | 
| 87 59 | 
             
                    # 'hmhd' => :hmhd,
         | 
| 88 60 | 
             
                    # 'iinf' => :iinf,
         | 
| 89 61 | 
             
                    # 'iloc' => :iloc,
         | 
| @@ -91,7 +63,7 @@ module FormatParser | |
| 91 63 | 
             
                    # 'ipro' => :ipro,
         | 
| 92 64 | 
             
                    # 'iref' => :iref,
         | 
| 93 65 | 
             
                    # 'leva' => :leva,
         | 
| 94 | 
            -
                     | 
| 66 | 
            +
                    'mdhd' => :mdhd,
         | 
| 95 67 | 
             
                    'mdia' => :container,
         | 
| 96 68 | 
             
                    'meco' => :container,
         | 
| 97 69 | 
             
                    # 'mehd' => :mehd,
         | 
| @@ -104,7 +76,7 @@ module FormatParser | |
| 104 76 | 
             
                    'moof' => :container,
         | 
| 105 77 | 
             
                    'moov' => :container,
         | 
| 106 78 | 
             
                    'mvex' => :container,
         | 
| 107 | 
            -
                     | 
| 79 | 
            +
                    'mvhd' => :mvhd,
         | 
| 108 80 | 
             
                    'nmhd' => :empty,
         | 
| 109 81 | 
             
                    # 'padb' => :padb,
         | 
| 110 82 | 
             
                    'paen' => :container,
         | 
| @@ -131,16 +103,16 @@ module FormatParser | |
| 131 103 | 
             
                    # 'stri' => :stri,
         | 
| 132 104 | 
             
                    'strk' => :container,
         | 
| 133 105 | 
             
                    # 'stsc' => :stsc,
         | 
| 134 | 
            -
                     | 
| 106 | 
            +
                    'stsd' => :stsd,
         | 
| 135 107 | 
             
                    # 'stsh' => :stsh,
         | 
| 136 108 | 
             
                    # 'stss' => :stss,
         | 
| 137 109 | 
             
                    # 'stsz' => :stsz,
         | 
| 138 | 
            -
                     | 
| 110 | 
            +
                    'stts' => :stts,
         | 
| 139 111 | 
             
                    # 'styp' => :typ,
         | 
| 140 112 | 
             
                    # 'stz2' => :stz2,
         | 
| 141 113 | 
             
                    # 'subs' => :subs,
         | 
| 142 114 | 
             
                    # 'tfra' => :tfra,
         | 
| 143 | 
            -
                     | 
| 115 | 
            +
                    'tkhd' => :tkhd,
         | 
| 144 116 | 
             
                    'trak' => :container,
         | 
| 145 117 | 
             
                    # 'trex' => :trex,
         | 
| 146 118 | 
             
                    # 'tsel' => :tsel,
         | 
| @@ -152,69 +124,74 @@ module FormatParser | |
| 152 124 | 
             
                    # 'xml ' => :xml,
         | 
| 153 125 | 
             
                  }
         | 
| 154 126 |  | 
| 155 | 
            -
                  # Parse the  | 
| 127 | 
            +
                  # Parse the box at the IO's current position.
         | 
| 156 128 | 
             
                  #
         | 
| 157 | 
            -
                  # @return [ | 
| 158 | 
            -
                  def  | 
| 129 | 
            +
                  # @return [Box, nil]
         | 
| 130 | 
            +
                  def parse_box
         | 
| 159 131 | 
             
                    position = @buf.pos
         | 
| 160 | 
            -
             | 
| 161 | 
            -
                    size = read_int_32
         | 
| 132 | 
            +
                    size = read_int
         | 
| 162 133 | 
             
                    type = read_string(4)
         | 
| 163 | 
            -
             | 
| 134 | 
            +
             | 
| 135 | 
            +
                    if size == 1
         | 
| 136 | 
            +
                      size = read_int(n: 8)
         | 
| 137 | 
            +
                    elsif size == 0
         | 
| 138 | 
            +
                      size = @buf.size - position
         | 
| 139 | 
            +
                    end
         | 
| 140 | 
            +
             | 
| 164 141 | 
             
                    body_size = size - (@buf.pos - position)
         | 
| 165 | 
            -
                     | 
| 166 | 
            -
             | 
| 167 | 
            -
                    if self.class:: | 
| 168 | 
            -
                      fields, children = method(self.class:: | 
| 169 | 
            -
                      if @buf.pos !=  | 
| 170 | 
            -
                        # We should never end up in this state. If we do, it likely indicates a bug in the  | 
| 171 | 
            -
                        warn("Unexpected IO position after parsing #{type}  | 
| 172 | 
            -
                        @buf.seek( | 
| 142 | 
            +
                    next_box_position = position + size
         | 
| 143 | 
            +
             | 
| 144 | 
            +
                    if self.class::BOX_PARSERS.include?(type)
         | 
| 145 | 
            +
                      fields, children = method(self.class::BOX_PARSERS[type]).call(body_size)
         | 
| 146 | 
            +
                      if @buf.pos != next_box_position
         | 
| 147 | 
            +
                        # We should never end up in this state. If we do, it likely indicates a bug in the box's parser method.
         | 
| 148 | 
            +
                        warn("Unexpected IO position after parsing #{type} box at position #{position}. Box size: #{size}. Expected position: #{next_box_position}. Actual position: #{@buf.pos}.")
         | 
| 149 | 
            +
                        @buf.seek(next_box_position)
         | 
| 173 150 | 
             
                      end
         | 
| 174 | 
            -
                       | 
| 151 | 
            +
                      Box.new(type, position, size, fields, children)
         | 
| 175 152 | 
             
                    else
         | 
| 176 153 | 
             
                      skip_bytes(body_size)
         | 
| 177 | 
            -
                       | 
| 154 | 
            +
                      Box.new(type, position, size)
         | 
| 178 155 | 
             
                    end
         | 
| 179 156 | 
             
                  rescue FormatParser::IOUtils::InvalidRead
         | 
| 180 157 | 
             
                    nil
         | 
| 181 158 | 
             
                  end
         | 
| 182 159 |  | 
| 183 | 
            -
                  # Parse any  | 
| 160 | 
            +
                  # Parse any box that serves as a container, with only children and no fields of its own.
         | 
| 184 161 | 
             
                  def container(size)
         | 
| 185 | 
            -
                    [nil,  | 
| 162 | 
            +
                    [nil, build_box_tree(size)]
         | 
| 186 163 | 
             
                  end
         | 
| 187 164 |  | 
| 188 | 
            -
                  # Parse only an  | 
| 165 | 
            +
                  # Parse only an box's version and flags, skipping the remainder of the box's body.
         | 
| 189 166 | 
             
                  def empty(size)
         | 
| 190 167 | 
             
                    fields = read_version_and_flags
         | 
| 191 168 | 
             
                    skip_bytes(size - 4)
         | 
| 192 169 | 
             
                    [fields, nil]
         | 
| 193 170 | 
             
                  end
         | 
| 194 171 |  | 
| 195 | 
            -
                  # Parse a binary XML  | 
| 172 | 
            +
                  # Parse a binary XML box.
         | 
| 196 173 | 
             
                  # def bxml(size)
         | 
| 197 174 | 
             
                  #   fields = read_version_and_flags.merge({
         | 
| 198 | 
            -
                  #     data: (size - 4).times.map {  | 
| 175 | 
            +
                  #     data: (size - 4).times.map { read_int(n: 1) }
         | 
| 199 176 | 
             
                  #   })
         | 
| 200 177 | 
             
                  #   [fields, nil]
         | 
| 201 178 | 
             
                  # end
         | 
| 202 179 |  | 
| 203 | 
            -
                  # Parse a chunk large offset  | 
| 180 | 
            +
                  # Parse a chunk large offset box.
         | 
| 204 181 | 
             
                  # def co64(_)
         | 
| 205 182 | 
             
                  #   fields = read_version_and_flags
         | 
| 206 | 
            -
                  #   entry_count =  | 
| 183 | 
            +
                  #   entry_count = read_int
         | 
| 207 184 | 
             
                  #   fields.merge!({
         | 
| 208 185 | 
             
                  #     entry_count: entry_count,
         | 
| 209 | 
            -
                  #     entries: entry_count.times.map { { chunk_offset:  | 
| 186 | 
            +
                  #     entries: entry_count.times.map { { chunk_offset: read_int(n: 8) } }
         | 
| 210 187 | 
             
                  #   })
         | 
| 211 188 | 
             
                  #   [fields, nil]
         | 
| 212 189 | 
             
                  # end
         | 
| 213 190 |  | 
| 214 | 
            -
                  # Parse a copyright  | 
| 191 | 
            +
                  # Parse a copyright box.
         | 
| 215 192 | 
             
                  # def cprt(size)
         | 
| 216 193 | 
             
                  #   fields = read_version_and_flags
         | 
| 217 | 
            -
                  #   tmp =  | 
| 194 | 
            +
                  #   tmp = read_int(n: 2)
         | 
| 218 195 | 
             
                  #   fields.merge!({
         | 
| 219 196 | 
             
                  #     language: [(tmp >> 10) & 0x1F, (tmp >> 5) & 0x1F, tmp & 0x1F],
         | 
| 220 197 | 
             
                  #     notice: read_string(size - 6)
         | 
| @@ -222,45 +199,45 @@ module FormatParser | |
| 222 199 | 
             
                  #   [fields, nil]
         | 
| 223 200 | 
             
                  # end
         | 
| 224 201 |  | 
| 225 | 
            -
                  # Parse a composition to decode  | 
| 202 | 
            +
                  # Parse a composition to decode box.
         | 
| 226 203 | 
             
                  # def cslg(_)
         | 
| 227 204 | 
             
                  #   fields = read_version_and_flags
         | 
| 228 205 | 
             
                  #   version = fields[:version]
         | 
| 229 206 | 
             
                  #   fields.merge!({
         | 
| 230 | 
            -
                  #     composition_to_dts_shift: version == 1 ?  | 
| 231 | 
            -
                  #     least_decode_to_display_delta: version == 1 ?  | 
| 232 | 
            -
                  #     greatest_decode_to_display_delta: version == 1 ?  | 
| 233 | 
            -
                  #     composition_start_time: version == 1 ?  | 
| 234 | 
            -
                  #     composition_end_time: version == 1 ?  | 
| 207 | 
            +
                  #     composition_to_dts_shift: version == 1 ? read_int(n: 8) : read_int,
         | 
| 208 | 
            +
                  #     least_decode_to_display_delta: version == 1 ? read_int(n: 8) : read_int,
         | 
| 209 | 
            +
                  #     greatest_decode_to_display_delta: version == 1 ? read_int(n: 8) : read_int,
         | 
| 210 | 
            +
                  #     composition_start_time: version == 1 ? read_int(n: 8) : read_int,
         | 
| 211 | 
            +
                  #     composition_end_time: version == 1 ? read_int(n: 8) : read_int,
         | 
| 235 212 | 
             
                  #   })
         | 
| 236 213 | 
             
                  #   [fields, nil]
         | 
| 237 214 | 
             
                  # end
         | 
| 238 215 |  | 
| 239 | 
            -
                  # Parse a composition time to sample  | 
| 216 | 
            +
                  # Parse a composition time to sample box.
         | 
| 240 217 | 
             
                  # def ctts(_)
         | 
| 241 218 | 
             
                  #   fields = read_version_and_flags
         | 
| 242 | 
            -
                  #   entry_count =  | 
| 219 | 
            +
                  #   entry_count = read_int
         | 
| 243 220 | 
             
                  #   fields.merge!({
         | 
| 244 221 | 
             
                  #     entry_count: entry_count,
         | 
| 245 222 | 
             
                  #     entries: entry_count.times.map do
         | 
| 246 223 | 
             
                  #       {
         | 
| 247 | 
            -
                  #         sample_count:  | 
| 248 | 
            -
                  #         sample_offset:  | 
| 224 | 
            +
                  #         sample_count: read_int,
         | 
| 225 | 
            +
                  #         sample_offset: read_int
         | 
| 249 226 | 
             
                  #       }
         | 
| 250 227 | 
             
                  #     end
         | 
| 251 228 | 
             
                  #   })
         | 
| 252 229 | 
             
                  #   [fields, nil]
         | 
| 253 230 | 
             
                  # end
         | 
| 254 231 |  | 
| 255 | 
            -
                  # Parse a data reference  | 
| 232 | 
            +
                  # Parse a data reference box.
         | 
| 256 233 | 
             
                  # def dref(size)
         | 
| 257 234 | 
             
                  #   fields = read_version_and_flags.merge({
         | 
| 258 | 
            -
                  #     entry_count:  | 
| 235 | 
            +
                  #     entry_count: read_int
         | 
| 259 236 | 
             
                  #   })
         | 
| 260 | 
            -
                  #   [fields,  | 
| 237 | 
            +
                  #   [fields, build_box_tree(size - 8)]
         | 
| 261 238 | 
             
                  # end
         | 
| 262 239 |  | 
| 263 | 
            -
                  # Parse a data reference URL entry  | 
| 240 | 
            +
                  # Parse a data reference URL entry box.
         | 
| 264 241 | 
             
                  # def dref_url(size)
         | 
| 265 242 | 
             
                  #   fields = read_version_and_flags.merge({
         | 
| 266 243 | 
             
                  #     location: read_string(size - 4)
         | 
| @@ -268,7 +245,7 @@ module FormatParser | |
| 268 245 | 
             
                  #   [fields, nil]
         | 
| 269 246 | 
             
                  # end
         | 
| 270 247 |  | 
| 271 | 
            -
                  # Parse a data reference URN entry  | 
| 248 | 
            +
                  # Parse a data reference URN entry box.
         | 
| 272 249 | 
             
                  # def dref_urn(size)
         | 
| 273 250 | 
             
                  #   fields = read_version_and_flags
         | 
| 274 251 | 
             
                  #   name, location = read_bytes(size - 4).unpack('Z2')
         | 
| @@ -279,58 +256,58 @@ module FormatParser | |
| 279 256 | 
             
                  #   [fields, nil]
         | 
| 280 257 | 
             
                  # end
         | 
| 281 258 |  | 
| 282 | 
            -
                  # Parse an FEC reservoir  | 
| 259 | 
            +
                  # Parse an FEC reservoir box.
         | 
| 283 260 | 
             
                  # def fecr(_)
         | 
| 284 261 | 
             
                  #   fields = read_version_and_flags
         | 
| 285 262 | 
             
                  #   version = fields[:version]
         | 
| 286 | 
            -
                  #   entry_count = version == 0 ?  | 
| 263 | 
            +
                  #   entry_count = version == 0 ? read_int(n: 2) : read_int
         | 
| 287 264 | 
             
                  #   fields.merge!({
         | 
| 288 265 | 
             
                  #     entry_count: entry_count,
         | 
| 289 266 | 
             
                  #     entries: entry_count.times.map do
         | 
| 290 267 | 
             
                  #       {
         | 
| 291 | 
            -
                  #         item_id: version == 0 ?  | 
| 292 | 
            -
                  #         symbol_count:  | 
| 268 | 
            +
                  #         item_id: version == 0 ? read_int(n: 2) : read_int,
         | 
| 269 | 
            +
                  #         symbol_count: read_int(n: 1)
         | 
| 293 270 | 
             
                  #       }
         | 
| 294 271 | 
             
                  #     end
         | 
| 295 272 | 
             
                  #   })
         | 
| 296 273 | 
             
                  # end
         | 
| 297 274 |  | 
| 298 | 
            -
                  # Parse an FD item information  | 
| 275 | 
            +
                  # Parse an FD item information box.
         | 
| 299 276 | 
             
                  # def fiin(size)
         | 
| 300 277 | 
             
                  #   fields = read_version_and_flags.merge({
         | 
| 301 | 
            -
                  #     entry_count:  | 
| 278 | 
            +
                  #     entry_count: read_int(n: 2)
         | 
| 302 279 | 
             
                  #   })
         | 
| 303 | 
            -
                  #   [fields,  | 
| 280 | 
            +
                  #   [fields, build_box_tree(size - 6)]
         | 
| 304 281 | 
             
                  # end
         | 
| 305 282 |  | 
| 306 | 
            -
                  # Parse a file reservoir  | 
| 283 | 
            +
                  # Parse a file reservoir box.
         | 
| 307 284 | 
             
                  # def fire(_)
         | 
| 308 285 | 
             
                  #   fields = read_version_and_flags
         | 
| 309 | 
            -
                  #   entry_count = version == 0 ?  | 
| 286 | 
            +
                  #   entry_count = version == 0 ? read_int(n: 2) : read_int
         | 
| 310 287 | 
             
                  #   fields.merge!({
         | 
| 311 288 | 
             
                  #     entry_count: entry_count,
         | 
| 312 289 | 
             
                  #     entries: entry_count.times.map do
         | 
| 313 290 | 
             
                  #       {
         | 
| 314 | 
            -
                  #         item_id: version == 0 ?  | 
| 315 | 
            -
                  #         symbol_count:  | 
| 291 | 
            +
                  #         item_id: version == 0 ? read_int(n: 2) : read_int,
         | 
| 292 | 
            +
                  #         symbol_count: read_int
         | 
| 316 293 | 
             
                  #       }
         | 
| 317 294 | 
             
                  #     end
         | 
| 318 295 | 
             
                  #   })
         | 
| 319 296 | 
             
                  #   [fields, nil]
         | 
| 320 297 | 
             
                  # end
         | 
| 321 298 |  | 
| 322 | 
            -
                  # Parse a file partition  | 
| 299 | 
            +
                  # Parse a file partition box.
         | 
| 323 300 | 
             
                  # def fpar(_)
         | 
| 324 301 | 
             
                  #   fields = read_version_and_flags
         | 
| 325 302 | 
             
                  #   version = fields[:version]
         | 
| 326 303 | 
             
                  #   fields.merge!({
         | 
| 327 | 
            -
                  #     item_id: version == 0 ?  | 
| 328 | 
            -
                  #     packet_payload_size:  | 
| 329 | 
            -
                  #     fec_encoding_id: skip_bytes(1) {  | 
| 330 | 
            -
                  #     fec_instance_id:  | 
| 331 | 
            -
                  #     max_source_block_length:  | 
| 332 | 
            -
                  #     encoding_symbol_length:  | 
| 333 | 
            -
                  #     max_number_of_encoding_symbols:  | 
| 304 | 
            +
                  #     item_id: version == 0 ? read_int(n: 2) : read_int,
         | 
| 305 | 
            +
                  #     packet_payload_size: read_int(n: 2),
         | 
| 306 | 
            +
                  #     fec_encoding_id: skip_bytes(1) { read_int(n: 1) },
         | 
| 307 | 
            +
                  #     fec_instance_id: read_int(n: 2),
         | 
| 308 | 
            +
                  #     max_source_block_length: read_int(n: 2),
         | 
| 309 | 
            +
                  #     encoding_symbol_length: read_int(n: 2),
         | 
| 310 | 
            +
                  #     max_number_of_encoding_symbols: read_int(n: 2),
         | 
| 334 311 | 
             
                  #   })
         | 
| 335 312 | 
             
                  #   # TODO: Parse scheme_specific_info, entry_count and entries { block_count, block_size }.
         | 
| 336 313 | 
             
                  #   skip_bytes(size - 20)
         | 
| @@ -338,10 +315,10 @@ module FormatParser | |
| 338 315 | 
             
                  #   [fields, nil]
         | 
| 339 316 | 
             
                  # end
         | 
| 340 317 |  | 
| 341 | 
            -
                  # Parse a group ID to name  | 
| 318 | 
            +
                  # Parse a group ID to name box.
         | 
| 342 319 | 
             
                  # def gitn(size)
         | 
| 343 320 | 
             
                  #   fields = read_version_and_flags
         | 
| 344 | 
            -
                  #   entry_count =  | 
| 321 | 
            +
                  #   entry_count = read_int(n: 2)
         | 
| 345 322 | 
             
                  #   fields.merge!({
         | 
| 346 323 | 
             
                  #     entry_count: entry_count
         | 
| 347 324 | 
             
                  #   })
         | 
| @@ -350,43 +327,43 @@ module FormatParser | |
| 350 327 | 
             
                  #   [fields, nil]
         | 
| 351 328 | 
             
                  # end
         | 
| 352 329 |  | 
| 353 | 
            -
                  # Parse a handler  | 
| 354 | 
            -
                   | 
| 355 | 
            -
             | 
| 356 | 
            -
             | 
| 357 | 
            -
             | 
| 358 | 
            -
             | 
| 359 | 
            -
             | 
| 360 | 
            -
                   | 
| 330 | 
            +
                  # Parse a handler box.
         | 
| 331 | 
            +
                  def hdlr(size)
         | 
| 332 | 
            +
                    fields = read_version_and_flags.merge({
         | 
| 333 | 
            +
                      handler_type: skip_bytes(4) { read_string(4) },
         | 
| 334 | 
            +
                      name: skip_bytes(12) { read_string(size - 24) }
         | 
| 335 | 
            +
                    })
         | 
| 336 | 
            +
                    [fields, nil]
         | 
| 337 | 
            +
                  end
         | 
| 361 338 |  | 
| 362 | 
            -
                  # Parse a hint media header  | 
| 339 | 
            +
                  # Parse a hint media header box.
         | 
| 363 340 | 
             
                  # def hmhd(_)
         | 
| 364 341 | 
             
                  #   fields = read_version_and_flags.merge({
         | 
| 365 | 
            -
                  #     max_pdu_size:  | 
| 366 | 
            -
                  #     avg_pdu_size:  | 
| 367 | 
            -
                  #     max_bitrate:  | 
| 368 | 
            -
                  #     avg_bitrate:  | 
| 342 | 
            +
                  #     max_pdu_size: read_int(n: 2),
         | 
| 343 | 
            +
                  #     avg_pdu_size: read_int(n: 2),
         | 
| 344 | 
            +
                  #     max_bitrate: read_int,
         | 
| 345 | 
            +
                  #     avg_bitrate: read_int
         | 
| 369 346 | 
             
                  #   })
         | 
| 370 347 | 
             
                  #   skip_bytes(4)
         | 
| 371 348 | 
             
                  #   [fields, nil]
         | 
| 372 349 | 
             
                  # end
         | 
| 373 350 |  | 
| 374 | 
            -
                  # Parse an item info  | 
| 351 | 
            +
                  # Parse an item info box.
         | 
| 375 352 | 
             
                  # def iinf(size)
         | 
| 376 353 | 
             
                  #   fields = read_version_and_flags.merge({
         | 
| 377 | 
            -
                  #     entry_count: version == 0 ?  | 
| 354 | 
            +
                  #     entry_count: version == 0 ? read_int(n: 2) : read_int
         | 
| 378 355 | 
             
                  #   })
         | 
| 379 | 
            -
                  #   [fields,  | 
| 356 | 
            +
                  #   [fields, build_box_tree(size - 8)]
         | 
| 380 357 | 
             
                  # end
         | 
| 381 358 |  | 
| 382 | 
            -
                  # Parse an item location  | 
| 359 | 
            +
                  # Parse an item location box.
         | 
| 383 360 | 
             
                  # def iloc(_)
         | 
| 384 361 | 
             
                  #   fields = read_version_and_flags
         | 
| 385 | 
            -
                  #   tmp =  | 
| 362 | 
            +
                  #   tmp = read_int(n: 2)
         | 
| 386 363 | 
             
                  #   item_count = if version < 2
         | 
| 387 | 
            -
                  #      | 
| 364 | 
            +
                  #     read_int(n: 2)
         | 
| 388 365 | 
             
                  #   elsif version == 2
         | 
| 389 | 
            -
                  #      | 
| 366 | 
            +
                  #     read_int
         | 
| 390 367 | 
             
                  #   end
         | 
| 391 368 | 
             
                  #   offset_size = (tmp >> 12) & 0x7
         | 
| 392 369 | 
             
                  #   length_size = (tmp >> 8) & 0x7
         | 
| @@ -400,15 +377,15 @@ module FormatParser | |
| 400 377 | 
             
                  #     items: item_count.times.map do
         | 
| 401 378 | 
             
                  #       item = {
         | 
| 402 379 | 
             
                  #         item_id: if version < 2
         | 
| 403 | 
            -
                  #            | 
| 380 | 
            +
                  #           read_int(n: 2)
         | 
| 404 381 | 
             
                  #         elsif version == 2
         | 
| 405 | 
            -
                  #            | 
| 382 | 
            +
                  #           read_int
         | 
| 406 383 | 
             
                  #         end
         | 
| 407 384 | 
             
                  #       }
         | 
| 408 | 
            -
                  #       item[:construction_method] =  | 
| 409 | 
            -
                  #       item[:data_reference_index] =  | 
| 385 | 
            +
                  #       item[:construction_method] = read_int(n: 2) & 0x7 if version == 1 || version == 2
         | 
| 386 | 
            +
                  #       item[:data_reference_index] = read_int(n: 2)
         | 
| 410 387 | 
             
                  #       skip_bytes(base_offset_size) # TODO: Dynamically parse base_offset based on base_offset_size
         | 
| 411 | 
            -
                  #       extent_count =  | 
| 388 | 
            +
                  #       extent_count = read_int(n: 2)
         | 
| 412 389 | 
             
                  #       item[:extent_count] = extent_count
         | 
| 413 390 | 
             
                  #       # TODO: Dynamically parse extent_index, extent_offset and extent_length based on their respective sizes.
         | 
| 414 391 | 
             
                  #       skip_bytes(extent_count * (offset_size + length_size))
         | 
| @@ -417,34 +394,34 @@ module FormatParser | |
| 417 394 | 
             
                  #   })
         | 
| 418 395 | 
             
                  # end
         | 
| 419 396 |  | 
| 420 | 
            -
                  # Parse an item info entry  | 
| 397 | 
            +
                  # Parse an item info entry box.
         | 
| 421 398 | 
             
                  # def infe(size)
         | 
| 422 | 
            -
                  #   # TODO: This  | 
| 399 | 
            +
                  #   # TODO: This box is super-complicated with optional and/or version-dependent fields and children.
         | 
| 423 400 | 
             
                  #   empty(size)
         | 
| 424 401 | 
             
                  # end
         | 
| 425 402 |  | 
| 426 | 
            -
                  # Parse an item protection  | 
| 403 | 
            +
                  # Parse an item protection box.
         | 
| 427 404 | 
             
                  # def ipro(size)
         | 
| 428 405 | 
             
                  #   fields = read_version_and_flags.merge({
         | 
| 429 | 
            -
                  #     protection_count:  | 
| 406 | 
            +
                  #     protection_count: read_int(n: 2)
         | 
| 430 407 | 
             
                  #   })
         | 
| 431 | 
            -
                  #   [fields,  | 
| 408 | 
            +
                  #   [fields, build_box_tree(size - 6)]
         | 
| 432 409 | 
             
                  # end
         | 
| 433 410 |  | 
| 434 | 
            -
                  # Parse an item reference  | 
| 411 | 
            +
                  # Parse an item reference box.
         | 
| 435 412 | 
             
                  # def iref(_)
         | 
| 436 | 
            -
                  #   [read_version_and_flags,  | 
| 413 | 
            +
                  #   [read_version_and_flags, build_box_tree(size - 4)]
         | 
| 437 414 | 
             
                  # end
         | 
| 438 415 |  | 
| 439 | 
            -
                  # Parse a level assignment  | 
| 416 | 
            +
                  # Parse a level assignment box.
         | 
| 440 417 | 
             
                  # def leva(_)
         | 
| 441 418 | 
             
                  #   fields = read_version_and_flags
         | 
| 442 | 
            -
                  #   level_count =  | 
| 419 | 
            +
                  #   level_count = read_int(n: 1)
         | 
| 443 420 | 
             
                  #   fields.merge!({
         | 
| 444 421 | 
             
                  #     level_count: level_count,
         | 
| 445 422 | 
             
                  #     levels: level_count.times.map do
         | 
| 446 | 
            -
                  #       track_id =  | 
| 447 | 
            -
                  #       tmp =  | 
| 423 | 
            +
                  #       track_id = read_int
         | 
| 424 | 
            +
                  #       tmp = read_int(n: 1)
         | 
| 448 425 | 
             
                  #       assignment_type = tmp & 0x7F
         | 
| 449 426 | 
             
                  #       level = {
         | 
| 450 427 | 
             
                  #         track_id: track_id,
         | 
| @@ -452,14 +429,14 @@ module FormatParser | |
| 452 429 | 
             
                  #         assignment_type: assignment_type
         | 
| 453 430 | 
             
                  #       }
         | 
| 454 431 | 
             
                  #       if assignment_type == 0
         | 
| 455 | 
            -
                  #         level[:grouping_type] =  | 
| 432 | 
            +
                  #         level[:grouping_type] = read_int
         | 
| 456 433 | 
             
                  #       elsif assignment_type == 1
         | 
| 457 434 | 
             
                  #         level.merge!({
         | 
| 458 | 
            -
                  #           grouping_type:  | 
| 459 | 
            -
                  #           grouping_type_parameter:  | 
| 435 | 
            +
                  #           grouping_type: read_int,
         | 
| 436 | 
            +
                  #           grouping_type_parameter: read_int
         | 
| 460 437 | 
             
                  #         })
         | 
| 461 438 | 
             
                  #       elsif assignment_type == 4
         | 
| 462 | 
            -
                  #         level[:sub_track_id] =  | 
| 439 | 
            +
                  #         level[:sub_track_id] = read_int
         | 
| 463 440 | 
             
                  #       end
         | 
| 464 441 | 
             
                  #       level
         | 
| 465 442 | 
             
                  #     end
         | 
| @@ -467,87 +444,87 @@ module FormatParser | |
| 467 444 | 
             
                  #   [fields, nil]
         | 
| 468 445 | 
             
                  # end
         | 
| 469 446 |  | 
| 470 | 
            -
                  # Parse a media header  | 
| 471 | 
            -
                   | 
| 472 | 
            -
             | 
| 473 | 
            -
             | 
| 474 | 
            -
             | 
| 475 | 
            -
             | 
| 476 | 
            -
             | 
| 477 | 
            -
             | 
| 478 | 
            -
             | 
| 479 | 
            -
             | 
| 480 | 
            -
             | 
| 481 | 
            -
             | 
| 482 | 
            -
             | 
| 483 | 
            -
             | 
| 484 | 
            -
                   | 
| 447 | 
            +
                  # Parse a media header box.
         | 
| 448 | 
            +
                  def mdhd(_)
         | 
| 449 | 
            +
                    fields = read_version_and_flags
         | 
| 450 | 
            +
                    version = fields[:version]
         | 
| 451 | 
            +
                    fields.merge!({
         | 
| 452 | 
            +
                      creation_time: version == 1 ? read_int(n: 8) : read_int,
         | 
| 453 | 
            +
                      modification_time: version == 1 ? read_int(n: 8) : read_int,
         | 
| 454 | 
            +
                      timescale: read_int,
         | 
| 455 | 
            +
                      duration: version == 1 ? read_int(n: 8) : read_int,
         | 
| 456 | 
            +
                    })
         | 
| 457 | 
            +
                    tmp = read_int(n: 2)
         | 
| 458 | 
            +
                    fields[:language] = [(tmp >> 10) & 0x1F, (tmp >> 5) & 0x1F, tmp & 0x1F]
         | 
| 459 | 
            +
                    skip_bytes(2)
         | 
| 460 | 
            +
                    [fields, nil]
         | 
| 461 | 
            +
                  end
         | 
| 485 462 |  | 
| 486 | 
            -
                  # Parse a movie extends header  | 
| 463 | 
            +
                  # Parse a movie extends header box.
         | 
| 487 464 | 
             
                  # def mehd(_)
         | 
| 488 465 | 
             
                  #   fields = read_version_and_flags
         | 
| 489 466 | 
             
                  #   version = fields[:version]
         | 
| 490 | 
            -
                  #   fields[:fragment_duration] = version == 1 ?  | 
| 467 | 
            +
                  #   fields[:fragment_duration] = version == 1 ? read_int(n: 8) : read_int
         | 
| 491 468 | 
             
                  #   [fields, nil]
         | 
| 492 469 | 
             
                  # end
         | 
| 493 470 |  | 
| 494 | 
            -
                  # Parse an metabox relation  | 
| 471 | 
            +
                  # Parse an metabox relation box.
         | 
| 495 472 | 
             
                  # def mere(_)
         | 
| 496 473 | 
             
                  #   fields = read_version_and_flags.merge({
         | 
| 497 | 
            -
                  #     first_metabox_handler_type:  | 
| 498 | 
            -
                  #     second_metabox_handler_type:  | 
| 499 | 
            -
                  #     metabox_relation:  | 
| 474 | 
            +
                  #     first_metabox_handler_type: read_int,
         | 
| 475 | 
            +
                  #     second_metabox_handler_type: read_int,
         | 
| 476 | 
            +
                  #     metabox_relation: read_int(n: 1)
         | 
| 500 477 | 
             
                  #   })
         | 
| 501 478 | 
             
                  #   [fields, nil]
         | 
| 502 479 | 
             
                  # end
         | 
| 503 480 |  | 
| 504 | 
            -
                  # Parse a meta  | 
| 481 | 
            +
                  # Parse a meta box.
         | 
| 505 482 | 
             
                  # def meta(size)
         | 
| 506 483 | 
             
                  #   fields = read_version_and_flags
         | 
| 507 | 
            -
                  #   [fields,  | 
| 484 | 
            +
                  #   [fields, build_box_tree(size - 4)]
         | 
| 508 485 | 
             
                  # end
         | 
| 509 486 |  | 
| 510 | 
            -
                  # Parse a movie fragment header  | 
| 487 | 
            +
                  # Parse a movie fragment header box.
         | 
| 511 488 | 
             
                  # def mfhd(_)
         | 
| 512 489 | 
             
                  #   fields = read_version_and_flags.merge({
         | 
| 513 | 
            -
                  #     sequence_number:  | 
| 490 | 
            +
                  #     sequence_number: read_int
         | 
| 514 491 | 
             
                  #   })
         | 
| 515 492 | 
             
                  #   [fields, nil]
         | 
| 516 493 | 
             
                  # end
         | 
| 517 494 |  | 
| 518 | 
            -
                  # Parse a movie fragment random access offset  | 
| 495 | 
            +
                  # Parse a movie fragment random access offset box.
         | 
| 519 496 | 
             
                  # def mfro(_)
         | 
| 520 497 | 
             
                  #   fields = read_version_and_flags.merge({
         | 
| 521 | 
            -
                  #     size:  | 
| 498 | 
            +
                  #     size: read_int
         | 
| 522 499 | 
             
                  #   })
         | 
| 523 500 | 
             
                  #   [fields, nil]
         | 
| 524 501 | 
             
                  # end
         | 
| 525 502 |  | 
| 526 | 
            -
                  # Parse a movie header  | 
| 527 | 
            -
                   | 
| 528 | 
            -
             | 
| 529 | 
            -
             | 
| 530 | 
            -
             | 
| 531 | 
            -
             | 
| 532 | 
            -
             | 
| 533 | 
            -
             | 
| 534 | 
            -
             | 
| 535 | 
            -
             | 
| 536 | 
            -
             | 
| 537 | 
            -
             | 
| 538 | 
            -
             | 
| 539 | 
            -
             | 
| 540 | 
            -
             | 
| 541 | 
            -
                   | 
| 503 | 
            +
                  # Parse a movie header box.
         | 
| 504 | 
            +
                  def mvhd(_)
         | 
| 505 | 
            +
                    fields = read_version_and_flags
         | 
| 506 | 
            +
                    version = fields[:version]
         | 
| 507 | 
            +
                    fields.merge!({
         | 
| 508 | 
            +
                      creation_time: version == 1 ? read_int(n: 8) : read_int,
         | 
| 509 | 
            +
                      modification_time: version == 1 ? read_int(n: 8) : read_int,
         | 
| 510 | 
            +
                      timescale: read_int,
         | 
| 511 | 
            +
                      duration: version == 1 ? read_int(n: 8) : read_int,
         | 
| 512 | 
            +
                      rate: read_fixed_point(n: 4),
         | 
| 513 | 
            +
                      volume: read_fixed_point(n: 2, signed: true),
         | 
| 514 | 
            +
                      matrix: skip_bytes(10) { read_matrix },
         | 
| 515 | 
            +
                      next_trak_id: skip_bytes(24) { read_int },
         | 
| 516 | 
            +
                    })
         | 
| 517 | 
            +
                    [fields, nil]
         | 
| 518 | 
            +
                  end
         | 
| 542 519 |  | 
| 543 | 
            -
                  # Parse a padding bits  | 
| 520 | 
            +
                  # Parse a padding bits box.
         | 
| 544 521 | 
             
                  # def padb(_)
         | 
| 545 522 | 
             
                  #   fields = read_version_and_flags
         | 
| 546 | 
            -
                  #   sample_count =  | 
| 523 | 
            +
                  #   sample_count = read_int
         | 
| 547 524 | 
             
                  #   fields.merge!({
         | 
| 548 525 | 
             
                  #     sample_count: sample_count,
         | 
| 549 526 | 
             
                  #     padding: ((sample_count + 1) / 2).times.map do
         | 
| 550 | 
            -
                  #       tmp =  | 
| 527 | 
            +
                  #       tmp = read_int(n: 1)
         | 
| 551 528 | 
             
                  #       {
         | 
| 552 529 | 
             
                  #         padding_1: tmp >> 4,
         | 
| 553 530 | 
             
                  #         padding_2: tmp & 0x07
         | 
| @@ -557,170 +534,170 @@ module FormatParser | |
| 557 534 | 
             
                  #   [fields, nil]
         | 
| 558 535 | 
             
                  # end
         | 
| 559 536 |  | 
| 560 | 
            -
                  # Parse a progressive download information  | 
| 537 | 
            +
                  # Parse a progressive download information box.
         | 
| 561 538 | 
             
                  # def pdin(size)
         | 
| 562 539 | 
             
                  #   fields = read_version_and_flags.merge({
         | 
| 563 540 | 
             
                  #     entries: ((size - 4) / 8).times.map do
         | 
| 564 541 | 
             
                  #       {
         | 
| 565 | 
            -
                  #         rate:  | 
| 566 | 
            -
                  #         initial_delay:  | 
| 542 | 
            +
                  #         rate: read_int,
         | 
| 543 | 
            +
                  #         initial_delay: read_int
         | 
| 567 544 | 
             
                  #       }
         | 
| 568 545 | 
             
                  #     end
         | 
| 569 546 | 
             
                  #   })
         | 
| 570 547 | 
             
                  #   [fields, nil]
         | 
| 571 548 | 
             
                  # end
         | 
| 572 549 |  | 
| 573 | 
            -
                  # Parse a primary item  | 
| 550 | 
            +
                  # Parse a primary item box.
         | 
| 574 551 | 
             
                  # def pitm(_)
         | 
| 575 552 | 
             
                  #   fields = read_version_and_flags.merge({
         | 
| 576 | 
            -
                  #     item_id: version == 0 ?  | 
| 553 | 
            +
                  #     item_id: version == 0 ? read_int(n: 2) : read_int
         | 
| 577 554 | 
             
                  #   })
         | 
| 578 555 | 
             
                  #   [fields, nil]
         | 
| 579 556 | 
             
                  # end
         | 
| 580 557 |  | 
| 581 | 
            -
                  # Parse a producer reference time  | 
| 558 | 
            +
                  # Parse a producer reference time box.
         | 
| 582 559 | 
             
                  # def prft(_)
         | 
| 583 560 | 
             
                  #   fields = read_version_and_flags
         | 
| 584 561 | 
             
                  #   version = fields[:version]
         | 
| 585 562 | 
             
                  #   fields.merge!({
         | 
| 586 | 
            -
                  #     reference_track_id:  | 
| 587 | 
            -
                  #     ntp_timestamp:  | 
| 588 | 
            -
                  #     media_time: version == 0 ?  | 
| 563 | 
            +
                  #     reference_track_id: read_int,
         | 
| 564 | 
            +
                  #     ntp_timestamp: read_int(n: 8),
         | 
| 565 | 
            +
                  #     media_time: version == 0 ? read_int : read_int(n: 8)
         | 
| 589 566 | 
             
                  #   })
         | 
| 590 567 | 
             
                  #   [fields, nil]
         | 
| 591 568 | 
             
                  # end
         | 
| 592 569 |  | 
| 593 | 
            -
                  # Parse a sample auxiliary information offsets  | 
| 570 | 
            +
                  # Parse a sample auxiliary information offsets box.
         | 
| 594 571 | 
             
                  # def saio(_)
         | 
| 595 572 | 
             
                  #   fields = read_version_and_flags
         | 
| 596 573 | 
             
                  #   version = field[:version]
         | 
| 597 574 | 
             
                  #   flags = fields[:flags]
         | 
| 598 575 | 
             
                  #   fields.merge!({
         | 
| 599 | 
            -
                  #     aux_info_type:  | 
| 600 | 
            -
                  #     aux_info_type_parameter:  | 
| 576 | 
            +
                  #     aux_info_type: read_int,
         | 
| 577 | 
            +
                  #     aux_info_type_parameter: read_int
         | 
| 601 578 | 
             
                  #   }) if flags & 0x1
         | 
| 602 | 
            -
                  #   entry_count =  | 
| 579 | 
            +
                  #   entry_count = read_int
         | 
| 603 580 | 
             
                  #   fields.merge!({
         | 
| 604 581 | 
             
                  #     entry_count: entry_count,
         | 
| 605 | 
            -
                  #     offsets: entry_count.times.map { version == 0 ?  | 
| 582 | 
            +
                  #     offsets: entry_count.times.map { version == 0 ? read_int : read_int(n: 8) }
         | 
| 606 583 | 
             
                  #   })
         | 
| 607 584 | 
             
                  #   [fields, nil]
         | 
| 608 585 | 
             
                  # end
         | 
| 609 586 |  | 
| 610 | 
            -
                  # Parse a sample auxiliary information sizes  | 
| 587 | 
            +
                  # Parse a sample auxiliary information sizes box.
         | 
| 611 588 | 
             
                  # def saiz(_)
         | 
| 612 589 | 
             
                  #   fields = read_version_and_flags
         | 
| 613 590 | 
             
                  #   flags = fields[:flags]
         | 
| 614 591 | 
             
                  #   fields.merge!({
         | 
| 615 | 
            -
                  #     aux_info_type:  | 
| 616 | 
            -
                  #     aux_info_type_parameter:  | 
| 592 | 
            +
                  #     aux_info_type: read_int,
         | 
| 593 | 
            +
                  #     aux_info_type_parameter: read_int
         | 
| 617 594 | 
             
                  #   }) if flags & 0x1
         | 
| 618 | 
            -
                  #   default_sample_info_size =  | 
| 619 | 
            -
                  #   sample_count =  | 
| 595 | 
            +
                  #   default_sample_info_size = read_int(n: 1)
         | 
| 596 | 
            +
                  #   sample_count = read_int
         | 
| 620 597 | 
             
                  #   fields.merge!({
         | 
| 621 598 | 
             
                  #     default_sample_info_size: default_sample_info_size,
         | 
| 622 599 | 
             
                  #     sample_count: sample_count
         | 
| 623 600 | 
             
                  #   })
         | 
| 624 | 
            -
                  #   fields[:sample_info_sizes] = sample_count.times.map {  | 
| 601 | 
            +
                  #   fields[:sample_info_sizes] = sample_count.times.map { read_int(n: 1) } if default_sample_info_size == 0
         | 
| 625 602 | 
             
                  #   [fields, nil]
         | 
| 626 603 | 
             
                  # end
         | 
| 627 604 |  | 
| 628 | 
            -
                  # Parse a sample to group  | 
| 605 | 
            +
                  # Parse a sample to group box.
         | 
| 629 606 | 
             
                  # def sbgp(_)
         | 
| 630 607 | 
             
                  #   fields = read_version_and_flags
         | 
| 631 | 
            -
                  #   fields[:grouping_type] =  | 
| 632 | 
            -
                  #   fields[:grouping_type_parameter] =  | 
| 633 | 
            -
                  #   entry_count =  | 
| 608 | 
            +
                  #   fields[:grouping_type] = read_int
         | 
| 609 | 
            +
                  #   fields[:grouping_type_parameter] = read_int if fields[:version] == 1
         | 
| 610 | 
            +
                  #   entry_count = read_int
         | 
| 634 611 | 
             
                  #   fields.merge!({
         | 
| 635 612 | 
             
                  #     entry_count: entry_count,
         | 
| 636 613 | 
             
                  #     entries: entry_count.times.map do
         | 
| 637 614 | 
             
                  #       {
         | 
| 638 | 
            -
                  #         sample_count:  | 
| 639 | 
            -
                  #         group_description_index:  | 
| 615 | 
            +
                  #         sample_count: read_int,
         | 
| 616 | 
            +
                  #         group_description_index: read_int
         | 
| 640 617 | 
             
                  #       }
         | 
| 641 618 | 
             
                  #     end
         | 
| 642 619 | 
             
                  #   })
         | 
| 643 620 | 
             
                  #   [fields, nil]
         | 
| 644 621 | 
             
                  # end
         | 
| 645 622 |  | 
| 646 | 
            -
                  # Parse a scheme type  | 
| 623 | 
            +
                  # Parse a scheme type box.
         | 
| 647 624 | 
             
                  # def schm(_)
         | 
| 648 625 | 
             
                  #   fields = read_version_and_flags.merge({
         | 
| 649 626 | 
             
                  #     scheme_type: read_string(4),
         | 
| 650 | 
            -
                  #     scheme_version:  | 
| 627 | 
            +
                  #     scheme_version: read_int,
         | 
| 651 628 | 
             
                  #   })
         | 
| 652 | 
            -
                  #   fields[:scheme_uri] = (size - 12).times.map {  | 
| 629 | 
            +
                  #   fields[:scheme_uri] = (size - 12).times.map { read_int(n: 1) } if flags & 0x1 != 0
         | 
| 653 630 | 
             
                  #   [fields, nil]
         | 
| 654 631 | 
             
                  # end
         | 
| 655 632 |  | 
| 656 | 
            -
                  # Parse an independent and disposable samples  | 
| 633 | 
            +
                  # Parse an independent and disposable samples box.
         | 
| 657 634 | 
             
                  # def sdtp(size)
         | 
| 658 | 
            -
                  #   # TODO: Parsing this  | 
| 635 | 
            +
                  #   # TODO: Parsing this box needs the sample_count from the sample size box (`stsz`).
         | 
| 659 636 | 
             
                  #   empty(size)
         | 
| 660 637 | 
             
                  # end
         | 
| 661 638 |  | 
| 662 | 
            -
                  # Parse an FD session group  | 
| 639 | 
            +
                  # Parse an FD session group box.
         | 
| 663 640 | 
             
                  # def segr(_)
         | 
| 664 | 
            -
                  #   num_session_groups =  | 
| 641 | 
            +
                  #   num_session_groups = read_int(n: 2)
         | 
| 665 642 | 
             
                  #   fields = {
         | 
| 666 643 | 
             
                  #     num_session_groups: num_session_groups,
         | 
| 667 644 | 
             
                  #     session_groups: num_session_groups.times.map do
         | 
| 668 | 
            -
                  #       entry_count =  | 
| 645 | 
            +
                  #       entry_count = read_int(n: 1)
         | 
| 669 646 | 
             
                  #       session_group = {
         | 
| 670 647 | 
             
                  #         entry_count: entry_count,
         | 
| 671 | 
            -
                  #         entries: entry_count.times.map { { group_id:  | 
| 648 | 
            +
                  #         entries: entry_count.times.map { { group_id: read_int } }
         | 
| 672 649 | 
             
                  #       }
         | 
| 673 | 
            -
                  #       num_channels_in_session_group =  | 
| 650 | 
            +
                  #       num_channels_in_session_group = read_int(n: 2)
         | 
| 674 651 | 
             
                  #       session_group.merge({
         | 
| 675 652 | 
             
                  #         num_channels_in_session_group: num_channels_in_session_group,
         | 
| 676 | 
            -
                  #         channels: num_channels_in_session_group.times.map { { hint_track_id:  | 
| 653 | 
            +
                  #         channels: num_channels_in_session_group.times.map { { hint_track_id: read_int } }
         | 
| 677 654 | 
             
                  #       })
         | 
| 678 655 | 
             
                  #     end
         | 
| 679 656 | 
             
                  #   }
         | 
| 680 657 | 
             
                  #   [fields, nil]
         | 
| 681 658 | 
             
                  # end
         | 
| 682 659 |  | 
| 683 | 
            -
                  # Parse a sample group description  | 
| 660 | 
            +
                  # Parse a sample group description box.
         | 
| 684 661 | 
             
                  # def sgpd(_)
         | 
| 685 662 | 
             
                  #   fields = read_version_and_flags
         | 
| 686 663 | 
             
                  #   version = fields[:version]
         | 
| 687 | 
            -
                  #   fields[:grouping_type] =  | 
| 688 | 
            -
                  #   fields[:default_length] =  | 
| 689 | 
            -
                  #   fields[:default_sample_description_index] =  | 
| 690 | 
            -
                  #   entry_count =  | 
| 664 | 
            +
                  #   fields[:grouping_type] = read_int
         | 
| 665 | 
            +
                  #   fields[:default_length] = read_int if version == 1
         | 
| 666 | 
            +
                  #   fields[:default_sample_description_index] = read_int if version >= 2
         | 
| 667 | 
            +
                  #   entry_count = read_int
         | 
| 691 668 | 
             
                  #   fields.merge!({
         | 
| 692 669 | 
             
                  #     entry_count: entry_count,
         | 
| 693 670 | 
             
                  #     entries: entry_count.times.map do
         | 
| 694 671 | 
             
                  #       entry = {}
         | 
| 695 | 
            -
                  #       entry[:description_length] =  | 
| 696 | 
            -
                  #       entry[: | 
| 672 | 
            +
                  #       entry[:description_length] = read_int if version == 1 && fields[:default_length] == 0
         | 
| 673 | 
            +
                  #       entry[:box] = parse_box
         | 
| 697 674 | 
             
                  #     end
         | 
| 698 675 | 
             
                  #   })
         | 
| 699 676 | 
             
                  #   [fields, nil]
         | 
| 700 677 | 
             
                  # end
         | 
| 701 678 |  | 
| 702 | 
            -
                  # Parse a segment index  | 
| 679 | 
            +
                  # Parse a segment index box.
         | 
| 703 680 | 
             
                  # def sidx(_)
         | 
| 704 681 | 
             
                  #   fields = read_version_and_flags.merge({
         | 
| 705 | 
            -
                  #     reference_id:  | 
| 706 | 
            -
                  #     timescale:  | 
| 682 | 
            +
                  #     reference_id: read_int,
         | 
| 683 | 
            +
                  #     timescale: read_int
         | 
| 707 684 | 
             
                  #   })
         | 
| 708 685 | 
             
                  #   version = fields[:version]
         | 
| 709 686 | 
             
                  #   fields.merge!({
         | 
| 710 | 
            -
                  #     earliest_presentation_time: version == 0 ?  | 
| 711 | 
            -
                  #     first_offset: version == 0 ?  | 
| 687 | 
            +
                  #     earliest_presentation_time: version == 0 ? read_int : read_int(n: 8),
         | 
| 688 | 
            +
                  #     first_offset: version == 0 ? read_int : read_int(n: 8),
         | 
| 712 689 | 
             
                  #   })
         | 
| 713 | 
            -
                  #   reference_count = skip_bytes(2) {  | 
| 690 | 
            +
                  #   reference_count = skip_bytes(2) { read_int(n: 2) }
         | 
| 714 691 | 
             
                  #   fields.merge!({
         | 
| 715 692 | 
             
                  #     reference_count: reference_count,
         | 
| 716 693 | 
             
                  #     references: reference_count.times.map do
         | 
| 717 | 
            -
                  #       tmp =  | 
| 694 | 
            +
                  #       tmp = read_int
         | 
| 718 695 | 
             
                  #       reference = {
         | 
| 719 696 | 
             
                  #         reference_type: tmp >> 31,
         | 
| 720 697 | 
             
                  #         referenced_size: tmp & 0x7FFFFFFF,
         | 
| 721 | 
            -
                  #         subsegment_duration:  | 
| 698 | 
            +
                  #         subsegment_duration: read_int
         | 
| 722 699 | 
             
                  #       }
         | 
| 723 | 
            -
                  #       tmp =  | 
| 700 | 
            +
                  #       tmp = read_int
         | 
| 724 701 | 
             
                  #       reference.merge({
         | 
| 725 702 | 
             
                  #         starts_with_sap: tmp >> 31,
         | 
| 726 703 | 
             
                  #         sap_type: (tmp >> 28) & 0x7,
         | 
| @@ -731,27 +708,27 @@ module FormatParser | |
| 731 708 | 
             
                  #   [fields, nil]
         | 
| 732 709 | 
             
                  # end
         | 
| 733 710 |  | 
| 734 | 
            -
                  # Parse a sound media header  | 
| 711 | 
            +
                  # Parse a sound media header box.
         | 
| 735 712 | 
             
                  # def smhd(_)
         | 
| 736 713 | 
             
                  #   fields = read_version_and_flags.merge({
         | 
| 737 | 
            -
                  #     balance:  | 
| 714 | 
            +
                  #     balance: read_fixed_point(n: 2, signed: true),
         | 
| 738 715 | 
             
                  #   })
         | 
| 739 716 | 
             
                  #   skip_bytes(2)
         | 
| 740 717 | 
             
                  #   [fields, nil]
         | 
| 741 718 | 
             
                  # end
         | 
| 742 719 |  | 
| 743 | 
            -
                  # Parse a subsegment index  | 
| 720 | 
            +
                  # Parse a subsegment index box.
         | 
| 744 721 | 
             
                  # def ssix(_)
         | 
| 745 722 | 
             
                  #   fields = read_version_and_flags
         | 
| 746 | 
            -
                  #   subsegment_count =  | 
| 723 | 
            +
                  #   subsegment_count = read_int
         | 
| 747 724 | 
             
                  #   fields.merge!({
         | 
| 748 725 | 
             
                  #     subsegment_count: subsegment_count,
         | 
| 749 726 | 
             
                  #     subsegments: subsegment_count.times.map do
         | 
| 750 | 
            -
                  #       range_count =  | 
| 727 | 
            +
                  #       range_count = read_int
         | 
| 751 728 | 
             
                  #       {
         | 
| 752 729 | 
             
                  #         range_count: range_count,
         | 
| 753 730 | 
             
                  #         ranges: range_count.times.map do
         | 
| 754 | 
            -
                  #           tmp =  | 
| 731 | 
            +
                  #           tmp = read_int
         | 
| 755 732 | 
             
                  #           {
         | 
| 756 733 | 
             
                  #             level: tmp >> 24,
         | 
| 757 734 | 
             
                  #             range_size: tmp & 0x00FFFFFF
         | 
| @@ -763,142 +740,142 @@ module FormatParser | |
| 763 740 | 
             
                  #   [fields, nil]
         | 
| 764 741 | 
             
                  # end
         | 
| 765 742 |  | 
| 766 | 
            -
                  # Parse a chunk offset  | 
| 743 | 
            +
                  # Parse a chunk offset box.
         | 
| 767 744 | 
             
                  # def stco(_)
         | 
| 768 745 | 
             
                  #   fields = read_version_and_flags
         | 
| 769 | 
            -
                  #   entry_count =  | 
| 746 | 
            +
                  #   entry_count = read_int
         | 
| 770 747 | 
             
                  #   fields.merge!({
         | 
| 771 748 | 
             
                  #     entry_count: entry_count,
         | 
| 772 | 
            -
                  #     entries: entry_count.times.map { { chunk_offset:  | 
| 749 | 
            +
                  #     entries: entry_count.times.map { { chunk_offset: read_int } }
         | 
| 773 750 | 
             
                  #   })
         | 
| 774 751 | 
             
                  #   [fields, nil]
         | 
| 775 752 | 
             
                  # end
         | 
| 776 753 |  | 
| 777 | 
            -
                  # Parse a degradation priority  | 
| 754 | 
            +
                  # Parse a degradation priority box.
         | 
| 778 755 | 
             
                  # def stdp(size)
         | 
| 779 | 
            -
                  #   # TODO: Parsing this  | 
| 756 | 
            +
                  #   # TODO: Parsing this box needs the sample_count from the sample size box (`stsz`).
         | 
| 780 757 | 
             
                  #   empty(size)
         | 
| 781 758 | 
             
                  # end
         | 
| 782 759 |  | 
| 783 | 
            -
                  # Parse a sub track information  | 
| 760 | 
            +
                  # Parse a sub track information box.
         | 
| 784 761 | 
             
                  # def stri(size)
         | 
| 785 762 | 
             
                  #   fields = read_version_and_flags.merge({
         | 
| 786 | 
            -
                  #     switch_group:  | 
| 787 | 
            -
                  #     alternate_group:  | 
| 788 | 
            -
                  #     sub_track_id:  | 
| 789 | 
            -
                  #     attribute_list: ((size - 12) / 4).times.map {  | 
| 763 | 
            +
                  #     switch_group: read_int(n: 2),
         | 
| 764 | 
            +
                  #     alternate_group: read_int(n: 2),
         | 
| 765 | 
            +
                  #     sub_track_id: read_int,
         | 
| 766 | 
            +
                  #     attribute_list: ((size - 12) / 4).times.map { read_int }
         | 
| 790 767 | 
             
                  #   })
         | 
| 791 768 | 
             
                  #   [fields, nil]
         | 
| 792 769 | 
             
                  # end
         | 
| 793 770 |  | 
| 794 | 
            -
                  # Parse a sample to chunk  | 
| 771 | 
            +
                  # Parse a sample to chunk box.
         | 
| 795 772 | 
             
                  # def stsc(_)
         | 
| 796 773 | 
             
                  #   fields = read_version_and_flags
         | 
| 797 | 
            -
                  #   entry_count =  | 
| 774 | 
            +
                  #   entry_count = read_int
         | 
| 798 775 | 
             
                  #   fields.merge!({
         | 
| 799 776 | 
             
                  #     entry_count: entry_count,
         | 
| 800 777 | 
             
                  #     entries: entry_count.times.map do
         | 
| 801 778 | 
             
                  #       {
         | 
| 802 | 
            -
                  #         first_chunk:  | 
| 803 | 
            -
                  #         samples_per_chunk:  | 
| 804 | 
            -
                  #         sample_description_index:  | 
| 779 | 
            +
                  #         first_chunk: read_int,
         | 
| 780 | 
            +
                  #         samples_per_chunk: read_int,
         | 
| 781 | 
            +
                  #         sample_description_index: read_int
         | 
| 805 782 | 
             
                  #       }
         | 
| 806 783 | 
             
                  #     end
         | 
| 807 784 | 
             
                  #   })
         | 
| 808 785 | 
             
                  #   [fields, nil]
         | 
| 809 786 | 
             
                  # end
         | 
| 810 787 |  | 
| 811 | 
            -
                  # Parse a sample descriptions  | 
| 812 | 
            -
                   | 
| 813 | 
            -
             | 
| 814 | 
            -
             | 
| 815 | 
            -
             | 
| 816 | 
            -
             | 
| 817 | 
            -
                   | 
| 788 | 
            +
                  # Parse a sample descriptions box.
         | 
| 789 | 
            +
                  def stsd(size)
         | 
| 790 | 
            +
                    fields = read_version_and_flags.merge({
         | 
| 791 | 
            +
                      entry_count: read_int
         | 
| 792 | 
            +
                    })
         | 
| 793 | 
            +
                    [fields, build_box_tree(size - 8)]
         | 
| 794 | 
            +
                  end
         | 
| 818 795 |  | 
| 819 | 
            -
                  # Parse a shadow sync sample  | 
| 796 | 
            +
                  # Parse a shadow sync sample box.
         | 
| 820 797 | 
             
                  # def stsh(_)
         | 
| 821 798 | 
             
                  #   fields = read_version_and_flags
         | 
| 822 | 
            -
                  #   entry_count =  | 
| 799 | 
            +
                  #   entry_count = read_int
         | 
| 823 800 | 
             
                  #   fields.merge!({
         | 
| 824 801 | 
             
                  #     entry_count: entry_count,
         | 
| 825 802 | 
             
                  #     entries: entry_count.times.map {
         | 
| 826 803 | 
             
                  #       {
         | 
| 827 | 
            -
                  #         shadowed_sample_number:  | 
| 828 | 
            -
                  #         sync_sample_number:  | 
| 804 | 
            +
                  #         shadowed_sample_number: read_int,
         | 
| 805 | 
            +
                  #         sync_sample_number: read_int
         | 
| 829 806 | 
             
                  #       }
         | 
| 830 807 | 
             
                  #     }
         | 
| 831 808 | 
             
                  #   })
         | 
| 832 809 | 
             
                  #   [fields, nil]
         | 
| 833 810 | 
             
                  # end
         | 
| 834 811 |  | 
| 835 | 
            -
                  # Parse a sync sample  | 
| 812 | 
            +
                  # Parse a sync sample box.
         | 
| 836 813 | 
             
                  # def stss(_)
         | 
| 837 814 | 
             
                  #   fields = read_version_and_flags
         | 
| 838 | 
            -
                  #   entry_count =  | 
| 815 | 
            +
                  #   entry_count = read_int
         | 
| 839 816 | 
             
                  #   fields.merge!({
         | 
| 840 817 | 
             
                  #     entry_count: entry_count,
         | 
| 841 | 
            -
                  #     entries: entry_count.times.map { { sample_number:  | 
| 818 | 
            +
                  #     entries: entry_count.times.map { { sample_number: read_int } }
         | 
| 842 819 | 
             
                  #   })
         | 
| 843 820 | 
             
                  #   [fields, nil]
         | 
| 844 821 | 
             
                  # end
         | 
| 845 822 |  | 
| 846 | 
            -
                  # Parse a sample size  | 
| 823 | 
            +
                  # Parse a sample size box.
         | 
| 847 824 | 
             
                  # def stsz(_)
         | 
| 848 825 | 
             
                  #   fields = read_version_and_flags
         | 
| 849 | 
            -
                  #   sample_size =  | 
| 850 | 
            -
                  #   sample_count =  | 
| 826 | 
            +
                  #   sample_size = read_int
         | 
| 827 | 
            +
                  #   sample_count = read_int
         | 
| 851 828 | 
             
                  #   fields.merge!({
         | 
| 852 829 | 
             
                  #     sample_size: sample_size,
         | 
| 853 830 | 
             
                  #     sample_count: sample_count,
         | 
| 854 831 | 
             
                  #   })
         | 
| 855 | 
            -
                  #   fields[:entries] = sample_count.times.map { { entry_size:  | 
| 832 | 
            +
                  #   fields[:entries] = sample_count.times.map { { entry_size: read_int } } if sample_size == 0
         | 
| 856 833 | 
             
                  #   [fields, nil]
         | 
| 857 834 | 
             
                  # end
         | 
| 858 835 |  | 
| 859 | 
            -
                  # Parse a decoding time to sample  | 
| 860 | 
            -
                   | 
| 861 | 
            -
             | 
| 862 | 
            -
             | 
| 863 | 
            -
             | 
| 864 | 
            -
             | 
| 865 | 
            -
             | 
| 866 | 
            -
             | 
| 867 | 
            -
             | 
| 868 | 
            -
             | 
| 869 | 
            -
             | 
| 870 | 
            -
             | 
| 871 | 
            -
             | 
| 872 | 
            -
             | 
| 873 | 
            -
                   | 
| 836 | 
            +
                  # Parse a decoding time to sample box.
         | 
| 837 | 
            +
                  def stts(_)
         | 
| 838 | 
            +
                    fields = read_version_and_flags
         | 
| 839 | 
            +
                    entry_count = read_int
         | 
| 840 | 
            +
                    fields.merge!({
         | 
| 841 | 
            +
                      entry_count: entry_count,
         | 
| 842 | 
            +
                      entries: entry_count.times.map do
         | 
| 843 | 
            +
                        {
         | 
| 844 | 
            +
                          sample_count: read_int,
         | 
| 845 | 
            +
                          sample_delta: read_int
         | 
| 846 | 
            +
                        }
         | 
| 847 | 
            +
                      end
         | 
| 848 | 
            +
                    })
         | 
| 849 | 
            +
                    [fields, nil]
         | 
| 850 | 
            +
                  end
         | 
| 874 851 |  | 
| 875 | 
            -
                  # Parse a compact sample size  | 
| 852 | 
            +
                  # Parse a compact sample size box.
         | 
| 876 853 | 
             
                  # def stz2(size)
         | 
| 877 854 | 
             
                  #   fields = read_version_and_flags.merge({
         | 
| 878 | 
            -
                  #     field_size: skip_bytes(3) {  | 
| 879 | 
            -
                  #     sample_count:  | 
| 855 | 
            +
                  #     field_size: skip_bytes(3) { read_int(n: 1) },
         | 
| 856 | 
            +
                  #     sample_count: read_int
         | 
| 880 857 | 
             
                  #   })
         | 
| 881 858 | 
             
                  #   # TODO: Handling for parsing entry sizes dynamically based on field size.
         | 
| 882 859 | 
             
                  #   skip_bytes(size - 12)
         | 
| 883 860 | 
             
                  #   [fields, nil]
         | 
| 884 861 | 
             
                  # end
         | 
| 885 862 |  | 
| 886 | 
            -
                  # Parse a sub-sample information  | 
| 863 | 
            +
                  # Parse a sub-sample information box.
         | 
| 887 864 | 
             
                  # def subs(_)
         | 
| 888 865 | 
             
                  #   fields = read_version_and_flags
         | 
| 889 | 
            -
                  #   entry_count =  | 
| 866 | 
            +
                  #   entry_count = read_int
         | 
| 890 867 | 
             
                  #   fields[:entries] = entry_count.times.map do
         | 
| 891 | 
            -
                  #     sample_delta =  | 
| 892 | 
            -
                  #     subsample_count =  | 
| 868 | 
            +
                  #     sample_delta = read_int
         | 
| 869 | 
            +
                  #     subsample_count = read_int(n: 2)
         | 
| 893 870 | 
             
                  #     {
         | 
| 894 871 | 
             
                  #       sample_delta: sample_delta,
         | 
| 895 872 | 
             
                  #       subsample_count: subsample_count,
         | 
| 896 873 | 
             
                  #       subsample_information: subsample_count.times.map do
         | 
| 897 874 | 
             
                  #         {
         | 
| 898 | 
            -
                  #           subsample_size: version == 1 ?  | 
| 899 | 
            -
                  #           subsample_priority:  | 
| 900 | 
            -
                  #           discardable:  | 
| 901 | 
            -
                  #           codec_specific_parameters:  | 
| 875 | 
            +
                  #           subsample_size: version == 1 ? read_int : read_int(n: 2),
         | 
| 876 | 
            +
                  #           subsample_priority: read_int(n: 1),
         | 
| 877 | 
            +
                  #           discardable: read_int(n: 1),
         | 
| 878 | 
            +
                  #           codec_specific_parameters: read_int
         | 
| 902 879 | 
             
                  #         }
         | 
| 903 880 | 
             
                  #       end
         | 
| 904 881 | 
             
                  #     }
         | 
| @@ -906,17 +883,17 @@ module FormatParser | |
| 906 883 | 
             
                  #   [fields, nil]
         | 
| 907 884 | 
             
                  # end
         | 
| 908 885 |  | 
| 909 | 
            -
                  # Parse a track fragment random access  | 
| 886 | 
            +
                  # Parse a track fragment random access box.
         | 
| 910 887 | 
             
                  # def tfra(_)
         | 
| 911 888 | 
             
                  #   fields = read_version_and_flags
         | 
| 912 889 | 
             
                  #   version = fields[:version]
         | 
| 913 | 
            -
                  #   fields[:track_id] =  | 
| 890 | 
            +
                  #   fields[:track_id] = read_int
         | 
| 914 891 | 
             
                  #   skip_bytes(3)
         | 
| 915 | 
            -
                  #   tmp =  | 
| 892 | 
            +
                  #   tmp = read_int(n: 1)
         | 
| 916 893 | 
             
                  #   size_of_traf_number = (tmp >> 4) & 0x3
         | 
| 917 894 | 
             
                  #   size_of_trun_number = (tmp >> 2) & 0x3
         | 
| 918 895 | 
             
                  #   size_of_sample_number = tmp & 0x3
         | 
| 919 | 
            -
                  #   entry_count =  | 
| 896 | 
            +
                  #   entry_count = read_int
         | 
| 920 897 | 
             
                  #   fields.merge!({
         | 
| 921 898 | 
             
                  #     size_of_traf_number: size_of_traf_number,
         | 
| 922 899 | 
             
                  #     size_of_trun_number: size_of_trun_number,
         | 
| @@ -924,8 +901,8 @@ module FormatParser | |
| 924 901 | 
             
                  #     entry_count: entry_count,
         | 
| 925 902 | 
             
                  #     entries: entry_count.times.map do
         | 
| 926 903 | 
             
                  #       entry = {
         | 
| 927 | 
            -
                  #         time: version == 1 ?  | 
| 928 | 
            -
                  #         moof_offset: version == 1 ?  | 
| 904 | 
            +
                  #         time: version == 1 ? read_int(n: 8) : read_int,
         | 
| 905 | 
            +
                  #         moof_offset: version == 1 ? read_int(n: 8) : read_int
         | 
| 929 906 | 
             
                  #       }
         | 
| 930 907 | 
             
                  #       # TODO: Handling for parsing traf_number, trun_number and sample_number dynamically based on their sizes.
         | 
| 931 908 | 
             
                  #       skip_bytes(size_of_traf_number + size_of_trun_number + size_of_sample_number + 3)
         | 
| @@ -935,74 +912,74 @@ module FormatParser | |
| 935 912 | 
             
                  #   [fields, nil]
         | 
| 936 913 | 
             
                  # end
         | 
| 937 914 |  | 
| 938 | 
            -
                  # Parse a track header  | 
| 939 | 
            -
                   | 
| 940 | 
            -
             | 
| 941 | 
            -
             | 
| 942 | 
            -
             | 
| 943 | 
            -
             | 
| 944 | 
            -
             | 
| 945 | 
            -
             | 
| 946 | 
            -
             | 
| 947 | 
            -
             | 
| 948 | 
            -
             | 
| 949 | 
            -
             | 
| 950 | 
            -
             | 
| 951 | 
            -
             | 
| 952 | 
            -
             | 
| 953 | 
            -
             | 
| 954 | 
            -
             | 
| 955 | 
            -
                   | 
| 915 | 
            +
                  # Parse a track header box.
         | 
| 916 | 
            +
                  def tkhd(_)
         | 
| 917 | 
            +
                    fields = read_version_and_flags
         | 
| 918 | 
            +
                    version = fields[:version]
         | 
| 919 | 
            +
                    fields.merge!({
         | 
| 920 | 
            +
                      creation_time: version == 1 ? read_int(n: 8) : read_int,
         | 
| 921 | 
            +
                      modification_time: version == 1 ? read_int(n: 8) : read_int,
         | 
| 922 | 
            +
                      track_id: read_int,
         | 
| 923 | 
            +
                      duration: skip_bytes(4) { version == 1 ? read_int(n: 8) : read_int },
         | 
| 924 | 
            +
                      layer: skip_bytes(8) { read_int(n: 2) },
         | 
| 925 | 
            +
                      alternate_group: read_int(n: 2),
         | 
| 926 | 
            +
                      volume: read_fixed_point(n: 2, signed: true),
         | 
| 927 | 
            +
                      matrix: skip_bytes(2) { read_matrix },
         | 
| 928 | 
            +
                      width: read_fixed_point(n: 4),
         | 
| 929 | 
            +
                      height: read_fixed_point(n: 4)
         | 
| 930 | 
            +
                    })
         | 
| 931 | 
            +
                    [fields, nil]
         | 
| 932 | 
            +
                  end
         | 
| 956 933 |  | 
| 957 | 
            -
                  # Parse a track extends  | 
| 934 | 
            +
                  # Parse a track extends box.
         | 
| 958 935 | 
             
                  # def trex(_)
         | 
| 959 936 | 
             
                  #   fields = read_version_and_flags.merge({
         | 
| 960 | 
            -
                  #     track_id:  | 
| 961 | 
            -
                  #     default_sample_description_index:  | 
| 962 | 
            -
                  #     default_sample_duration:  | 
| 963 | 
            -
                  #     default_sample_size:  | 
| 964 | 
            -
                  #     default_sample_flags:  | 
| 937 | 
            +
                  #     track_id: read_int,
         | 
| 938 | 
            +
                  #     default_sample_description_index: read_int,
         | 
| 939 | 
            +
                  #     default_sample_duration: read_int,
         | 
| 940 | 
            +
                  #     default_sample_size: read_int,
         | 
| 941 | 
            +
                  #     default_sample_flags: read_int
         | 
| 965 942 | 
             
                  #   })
         | 
| 966 943 | 
             
                  #   [fields, nil]
         | 
| 967 944 | 
             
                  # end
         | 
| 968 945 |  | 
| 969 | 
            -
                  # Parse a track selection  | 
| 946 | 
            +
                  # Parse a track selection box.
         | 
| 970 947 | 
             
                  # def tsel(size)
         | 
| 971 948 | 
             
                  #   fields = read_version_and_flags.merge({
         | 
| 972 | 
            -
                  #     switch_group:  | 
| 973 | 
            -
                  #     attribute_list: ((size - 8) / 4).times.map {  | 
| 949 | 
            +
                  #     switch_group: read_int,
         | 
| 950 | 
            +
                  #     attribute_list: ((size - 8) / 4).times.map { read_int }
         | 
| 974 951 | 
             
                  #   })
         | 
| 975 952 | 
             
                  #   [fields, nil]
         | 
| 976 953 | 
             
                  # end
         | 
| 977 954 |  | 
| 978 | 
            -
                  # Parse a file/segment type compatibility  | 
| 979 | 
            -
                   | 
| 980 | 
            -
             | 
| 981 | 
            -
             | 
| 982 | 
            -
             | 
| 983 | 
            -
             | 
| 984 | 
            -
             | 
| 985 | 
            -
             | 
| 986 | 
            -
             | 
| 987 | 
            -
                   | 
| 955 | 
            +
                  # Parse a file/segment type compatibility box.
         | 
| 956 | 
            +
                  def typ(size)
         | 
| 957 | 
            +
                    compatible_brands_count = (size - 8) / 4
         | 
| 958 | 
            +
                    fields = {
         | 
| 959 | 
            +
                      major_brand: read_string(4),
         | 
| 960 | 
            +
                      minor_version: read_int,
         | 
| 961 | 
            +
                      compatible_brands: compatible_brands_count.times.map { read_string(4) }
         | 
| 962 | 
            +
                    }
         | 
| 963 | 
            +
                    [fields, nil]
         | 
| 964 | 
            +
                  end
         | 
| 988 965 |  | 
| 989 | 
            -
                  # Parse a UUID  | 
| 966 | 
            +
                  # Parse a UUID box.
         | 
| 990 967 | 
             
                  def uuid(size)
         | 
| 991 968 | 
             
                    fields = { usertype: read_bytes(16).unpack('H*').first }
         | 
| 992 969 | 
             
                    skip_bytes(size - 16)
         | 
| 993 970 | 
             
                    [fields, nil]
         | 
| 994 971 | 
             
                  end
         | 
| 995 972 |  | 
| 996 | 
            -
                  # Parse a video media header  | 
| 973 | 
            +
                  # Parse a video media header box.
         | 
| 997 974 | 
             
                  # def vmhd(_)
         | 
| 998 975 | 
             
                  #   fields = read_version_and_flags.merge({
         | 
| 999 | 
            -
                  #     graphics_mode:  | 
| 1000 | 
            -
                  #     op_color: (1..3).map {  | 
| 976 | 
            +
                  #     graphics_mode: read_int(n: 2),
         | 
| 977 | 
            +
                  #     op_color: (1..3).map { read_int(n: 2) }
         | 
| 1001 978 | 
             
                  #   })
         | 
| 1002 979 | 
             
                  #   [fields, nil]
         | 
| 1003 980 | 
             
                  # end
         | 
| 1004 981 |  | 
| 1005 | 
            -
                  # Parse an XML  | 
| 982 | 
            +
                  # Parse an XML box.
         | 
| 1006 983 | 
             
                  # def xml(size)
         | 
| 1007 984 | 
             
                  #   fields = read_version_and_flags.merge({
         | 
| 1008 985 | 
             
                  #     xml: read_string(size - 4)
         | 
| @@ -1017,22 +994,16 @@ module FormatParser | |
| 1017 994 | 
             
                  #
         | 
| 1018 995 | 
             
                  # See https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap4/qtff4.html#//apple_ref/doc/uid/TP40000939-CH206-18737.
         | 
| 1019 996 | 
             
                  def read_matrix
         | 
| 1020 | 
            -
                     | 
| 1021 | 
            -
                      if i % 3 == 2
         | 
| 1022 | 
            -
                        read_fixed_point_32_2_30
         | 
| 1023 | 
            -
                      else
         | 
| 1024 | 
            -
                        read_fixed_point_32
         | 
| 1025 | 
            -
                      end
         | 
| 1026 | 
            -
                    end
         | 
| 997 | 
            +
                    Matrix.build(3) { |_, c| read_fixed_point(fractional_digits: c % 3 == 2 ? 30 : 16, signed: true) }
         | 
| 1027 998 | 
             
                  end
         | 
| 1028 999 |  | 
| 1029 | 
            -
                  # Parse an  | 
| 1000 | 
            +
                  # Parse an box's version and flags.
         | 
| 1030 1001 | 
             
                  #
         | 
| 1031 | 
            -
                  # It's common for  | 
| 1002 | 
            +
                  # It's common for boxes to begin with a single byte representing the version followed by three bytes representing any
         | 
| 1032 1003 | 
             
                  # associated flags. Both of these are often 0.
         | 
| 1033 1004 | 
             
                  def read_version_and_flags
         | 
| 1034 1005 | 
             
                    {
         | 
| 1035 | 
            -
                      version:  | 
| 1006 | 
            +
                      version: read_int(n: 1),
         | 
| 1036 1007 | 
             
                      flags: read_bytes(3)
         | 
| 1037 1008 | 
             
                    }
         | 
| 1038 1009 | 
             
                  end
         |