def test_stco(self): with TemporaryFile() as f: f.write(b'\x00\x00\x00\xf8\x73\x74\x63\x6f\x00\x00\x00\x00\x00\x00\x00' b'\x3a\x00\x00\x23\xfb\x00\x04\xc3\x1e\x00\x05\x7b\x55\x00\x07' b'\x84\x5c\x00\x0a\xb0\xcd\x00\x0d\x4b\xf3\x00\x0e\x84\xa6\x00' b'\x0f\xf5\x77\x00\x15\x34\x84\x00\x17\x28\xb4\x00\x19\xd8\x75' b'\x00\x1a\xd8\x1f\x00\x1d\x4e\xdd\x00\x20\x54\xd7\x00\x22\xa4' b'\x7a\x00\x25\x96\x16\x00\x28\x33\x6c\x00\x29\x40\x03\x00\x2a' b'\xaa\x83\x00\x2b\xf2\x0f\x00\x2e\xae\xfd\x00\x30\x9f\x80\x00' b'\x32\xb1\xf9\x00\x34\x72\x12\x00\x37\x52\x66\x00\x39\x45\xf8' b'\x00\x3a\xd0\x8a\x00\x3c\xf1\x95\x00\x40\x2b\xec\x00\x42\x13' b'\xba\x00\x44\x1e\xbe\x00\x46\x06\x7d\x00\x49\x4a\xbd\x00\x4b' b'\x14\x65\x00\x4c\xf8\xd1\x00\x4f\x06\x25\x00\x52\x4c\x9a\x00' b'\x54\x2c\xc5\x00\x56\x6b\x81\x00\x58\xb9\xa9\x00\x5c\x23\xdb' b'\x00\x5d\xeb\x67\x00\x5f\x7d\x79\x00\x61\x14\x59\x00\x64\x13' b'\xb2\x00\x66\x20\xb7\x00\x68\x05\x1e\x00\x69\xc7\x05\x00\x6d' b'\x98\x41\x00\x70\x9a\xaa\x00\x72\x8c\xcd\x00\x74\xa7\x2e\x00' b'\x78\x88\xb8\x00\x7b\x56\x2d\x00\x7d\x6a\xc0\x00\x7f\xd0\x96' b'\x00\x83\x41\xdc\x00\x84\xd6\xf4') f.seek(0) reader = StreamReader(f) size = reader.read32() _ = reader.read32() stco = parse_stco(reader, size) self.assertNotEqual(stco, None) self.assertEqual(stco.entry_count, 58)
def test_stts(self): with TemporaryFile() as f: f.write(b'\x00\x00\x00\x18\x73\x74\x74\x73\x00' b'\x00\x00\x00\x00\x00\x00\x01\x00\x00\x02\xb0\x00\x00\x03\xe8') f.seek(0) reader = StreamReader(f) size = reader.read32() _ = reader.read32() stts = parse_stts(reader, size) self.assertNotEqual(stts, None) self.assertEqual(stts.entry_count, 1)
def test_stss(self): with TemporaryFile() as f: f.write(b'\x00\x00\x00\x4c\x73\x74\x73\x73\x00\x00\x00\x00\x00' b'\x00\x00\x0f\x00\x00\x00\x01\x00\x00\x00\x31\x00\x00' b'\x00\x61\x00\x00\x00\x91\x00\x00\x00\xc1\x00\x00\x00' b'\xf1\x00\x00\x01\x21\x00\x00\x01\x51\x00\x00\x01\x81' b'\x00\x00\x01\xb1\x00\x00\x01\xe1\x00\x00\x02\x11\x00\x00\x02\x41' b'\x00\x00\x02\x71\x00\x00\x02\xa1') f.seek(0) reader = StreamReader(f) size = reader.read32() _ = reader.read32() stss = parse_stss(reader, size) self.assertNotEqual(stss, None)
def test_ftyp(self): with TemporaryFile() as f: f.write(b"\x00\x00\x00\x18\x66\x74\x79\x70\x69\x73\x6f\x36\x00\x00\x00\x01\x69\x73\x6f\x36\x64\x61\x73\x68") f.seek(0) reader = StreamReader(f) size = reader.read32() _ = reader.read32() ftyp = parse_ftyp(reader, size) self.assertNotEqual(ftyp, None) self.assertEqual(ftyp.size, 24) self.assertEqual(ftyp.major_brand, 'iso6') self.assertEqual(len(ftyp.compatible_brands), 2) self.assertEqual(ftyp.compatible_brands[0], 'iso6') self.assertEqual(ftyp.compatible_brands[1], 'dash')
def test_stsc(self): with TemporaryFile() as f: f.write(b'\x00\x00\x00\x34\x73\x74\x73\x63\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00' b'\x00\x01\x00\x00\x00\x0d\x00\x00\x00\x01\x00\x00\x00' b'\x02\x00\x00\x00\x0c\x00\x00\x00\x01\x00\x00\x00\x3a' b'\x00\x00\x00\x03\x00\x00\x00\x01') f.seek(0) reader = StreamReader(f) size = reader.read32() _ = reader.read32() stsc = parse_stsc(reader, size) self.assertNotEqual(stsc, None) self.assertEqual(stsc.entry_count, 3) self.assertEqual(stsc.first_chunk[0], 1) self.assertEqual(stsc.samples_per_chunk[0], 13) self.assertEqual(stsc.sample_description_index[0], 1)
class BoxParser: def __init__(self, file): self.reader = StreamReader(file) self.root = None self.frame_gen = None def parse(self): if not self.root: self.root = RootBox() size = self.reader.read32() type = self.reader.read32_as_str() while not self.reader.reached_eof(): if type == "ftyp": self.root.ftyp = parse_typ(self.reader, size, FileTypeBox) elif type == "styp": self.root.styp = parse_typ(self.reader, size, SegmentTypeBox) elif type == "moov": self.root.moov = parse_moov(self.reader, size) elif type == "free": self.root.free = parse_free(self.reader, size) elif type == "mdat": self.root.mdats.append(parse_mdat(self.reader, size)) elif type == "PLEP": self.root.plep = parse_plep(self.reader, size) else: raise InvalidBoxError("type %s unknown" % type, None) if self.reader.reached_eof(): break # At the end of current box parsing, the file pointer will be # ready to read the size and type of the next box size = self.reader.read32() type = self.reader.read32_as_str() # Either box parsing was successful or it has errors def get_tree(self): return self.root def get_all_info(self): # TODO abhi: not sure if the metadata should be structured as a dict # or something else. For now, just return a dict. out = {} if self.root is None: self.parse() out["duration"] = self.root.get_duration() out["timescale"] = self.root.get_timescale() out["brands"] = self.root.get_compatible_brands() out["created"] = self.root.get_creation_time() out["modified"] = self.root.get_modification_time() out["tracks"] = self.root.get_all_tracks() out["is_fragmented"] = self.root.has_fragments() # out["is_progressive"] = not sure what this means out["has_iod"] = self.root.has_iods() return out def get_frames(self, media_type): # media_type can be either audio, video or both if not self.root: self.parse() # TODO abhi: Implement a FrameGenerator # self.frame_gen = FrameGenerator(media_type, self.root.get_all_tracks(), self.root.mdats) # return self.frame_gen return None def get_nalu_gen(self, trak): # TODO abhi: we can't just be using any mdat box # However, for now, assume we are dealing with non fMP4 files return NALUGenerator(self.reader, trak, self.root.mdats[0])