def process_saio(self, data): "Process saio and possibly change offset by size_change if needed." version_flags = str_to_uint32(data[8:12]) version = version_flags >> 24 flags = version_flags & 0xffffff pos = 12 if flags & 0x1: pos += 8 entry_count = str_to_uint32(data[pos:pos + 4]) pos += 4 output = data[:pos] delta_offset = self.size_change if version == 0: for i in range(entry_count): offset = str_to_uint32(data[pos:pos + 4]) pos += 4 output += uint32_to_str(offset + delta_offset) if i == 0: self.new_saio_value = offset + delta_offset else: for i in range(entry_count): offset = str_to_uint64(data[pos:pos + 8]) pos += 8 output += uint64_to_str(offset + delta_offset) if i == 0: self.new_saio_value = offset + delta_offset return output
def process_tfdt(self, data): """Generate new timestamps for tfdt and change size of boxes above if needed. Try to keep in 32 bits if possible.""" version = data[8] if self.track_timescale is not None: tfdt_offset = self.offset * self.track_timescale else: tfdt_offset = 0 if version == 0: # 32-bit baseMediaDecodeTime base_media_decode_time = str_to_uint32(data[12:16]) new_base_media_decode_time = base_media_decode_time + tfdt_offset if new_base_media_decode_time < 4294967296: output = data[:12] output += uint32_to_str(new_base_media_decode_time) else: # print "Forced to change to 64-bit tfdt." self.size_change = 4 output = uint32_to_str( str_to_uint32(data[:4]) + self.size_change) output += data[4:8] output += b'\x01' output += data[9:12] output += uint64_to_str(new_base_media_decode_time) else: # 64-bit # print "Staying at 64-bit tfdt." output = data[:12] base_media_decode_time = str_to_uint64(data[12:20]) new_base_media_decode_time = base_media_decode_time + tfdt_offset output += uint64_to_str(new_base_media_decode_time) self.tfdt_value = new_base_media_decode_time return output
def process_sidx(self, data): "Process sidx data and add to output." if not KEEP_SIDX: return b"" output = b"" version = data[8] timescale = str_to_uint32(data[16:20]) if version == 0: # print("Changing sidx version to 1") size = str_to_uint32(data[0:4]) sidx_size_expansion = 8 output += uint32_to_str(size + sidx_size_expansion) output += data[4:8] output += b'\x01' output += data[9:20] earliest_presentation_time = str_to_uint32(data[20:24]) first_offset = str_to_uint32(data[24:28]) else: output += data[0:20] earliest_presentation_time = str_to_uint64(data[20:28]) first_offset = str_to_uint64(data[28:36]) new_presentation_time = earliest_presentation_time + timescale * self.offset output += uint64_to_str(new_presentation_time) output += uint64_to_str(first_offset) if version == 0: output += data[28:] else: output += data[36:] return output
def process_tfhd(self, data): "Process tfhd (assuming that we know the ttml size size)." tf_flags = str_to_uint32(data[8:12]) & 0xffffff pos = 16 if tf_flags & 0x01: raise MediaSegmentFilterError( "base-data-offset-present not supported in ttml segments") if tf_flags & 0x02: pos += 4 if tf_flags & 0x08: self.default_sample_duration = str_to_uint32(data[pos:pos + 4]) pos += 4 elif self.ttml_size: raise MediaSegmentFilterError( "Cannot handle ttml segments with default_sample_duration absent" ) output = data[:pos] if self.ttml_size: if tf_flags & 0x10: # old_ttml__size = str_to_uint32(data[pos:pos+4]) output += uint32_to_str(self.ttml_size) # print("Changed ttml sample size from %d to %d" % (old_ttml__size, self.ttml_size)) pos += 4 else: raise MediaSegmentFilterError( "Cannot handle ttml segments if default_sample_size_offset is absent" ) if self.ttml_size: output += data[pos:] else: output = data return output
def parse_trun(self, data, pos): "Parse trun box and find position of data_offset." flags = str_to_uint32(data[8:12]) & 0xffffff data_offset_present = flags & 1 if data_offset_present: self.trun_data_offset = str_to_uint32(data[16:20]) self.trun_data_offset_in_traf = pos + 16 - self.traf_start
def process_trex(self, data): # track_id = str_to_uint32(data[12:16]) self.default_sample_description_index = str_to_uint32(data[16:20]) self.default_sample_duration = str_to_uint32(data[20:24]) self.default_sample_size = str_to_uint32(data[24:28]) self.default_sample_flags = str_to_uint32(data[28:32]) return data
def process_tkhd(self, data): "Filter track header box and find track_id." assert self.track_id is None, "Multiple tracks in init file %s. Not supported." % self.filename version = data[8] if version == 0: self._track_id = str_to_uint32(data[20:24]) elif version == 1: self._track_id = str_to_uint32(data[28:32]) return data
def process_mvhd(self, data): "Set duration in mvhd." version = data[8] output = b"" if version == 1: self.movie_timescale = str_to_uint32(data[28:32]) output += data[:32] output += b'\x00' * 8 # duration output += data[40:] else: # version = 0 self.movie_timescale = str_to_uint32(data[20:24]) output += data[:24] output += b'\x00' * 4 # duration output += data[28:] return output
def process_tfhd(self, data): "Process a tfhd box and set trackID, defaultSampleDuration and defaultSampleSize" tf_flags = str_to_uint32(data[8:12]) & 0xffffff assert tf_flags == 0x020018, "Can only handle certain tf_flags combinations" output = data[:12] output += uint32_to_str(self.track_id) output += uint32_to_str(self.default_sample_duration) output += uint32_to_str(len(self.ttml_data)) return output
def process_tfdt_to_64bit(self, data, output): """Generate new timestamps for tfdt and change size of boxes above if needed. Note that the input output will be returned and can have another size.""" version = data[8] tfdt_offset = self.offset * self.track_timescale if version == 0: # 32-bit baseMediaDecodeTime self.size_change = 4 output = uint32_to_str(str_to_uint32(data[:4]) + self.size_change) output += data[4:8] output += b'\x01' output += data[9:12] base_media_decode_time = str_to_uint32(data[12:16]) else: # 64-bit output = data[:12] base_media_decode_time = str_to_uint64(data[12:20]) new_base_media_decode_time = base_media_decode_time + tfdt_offset output += uint64_to_str(new_base_media_decode_time) self.tfdt_value = new_base_media_decode_time return output
def process_trun(self, data): "Get total duration from trun. Fix offset if self.size_change is non-zero." flags = str_to_uint32(data[8:12]) & 0xffffff sample_count = str_to_uint32(data[12:16]) pos = 16 data_offset_present = False if flags & 0x1: # Data offset present data_offset_present = True pos += 4 if flags & 0x4: pos += 4 # First sample flags present sample_duration_present = flags & 0x100 sample_size_present = flags & 0x200 sample_flags_present = flags & 0x400 sample_comp_time_present = flags & 0x800 duration = 0 for _ in range(sample_count): if sample_duration_present: duration += str_to_uint32(data[pos:pos + 4]) pos += 4 elif self.default_sample_duration is not None: duration += self.default_sample_duration if sample_size_present: pos += 4 if sample_flags_present: pos += 4 if sample_comp_time_present: pos += 4 self.duration = duration # Modify data_offset output = data[:16] if data_offset_present and self.size_change > 0: offset = str_to_sint32(data[16:20]) offset += self.size_change output += sint32_to_str(offset) else: output += data[16:20] output += data[20:] return output
def find_and_process_mdat(self, data): "Change the ttml part of mdat and update mdat size. Return full new data." pos = 0 output = b"" while pos < len(data): size = str_to_uint32(data[pos:pos + 4]) boxtype = data[pos + 4:pos + 8] if boxtype != b'mdat': output += data[pos:pos + size] else: output += self.update_ttml_mdat(data[pos:pos + size]) pos += size return output
def process_styp(self, data): "Process styp and make sure lmsg presence follows the lmsg flag parameter. Add scte35 box if appropriate" lmsg = self.lmsg output = b"" size = str_to_uint32(data[:4]) pos = 8 brands = [] while pos < size: brand = data[pos:pos + 4] if brand != b"lmsg": brands.append(brand) pos += 4 if lmsg: brands.append(b"lmsg") new_size = 8 + 4 * len(brands) output += uint32_to_str(new_size) output += b"styp" for brand in brands: output += brand scte35box = self.create_scte35box() output += scte35box return output
def process_trun(self, data): """Process trun box.""" # pylint: disable=too-many-locals,too-many-branches,too-many-statements output = data[:16] flags = str_to_uint32(data[8:12]) & 0xffffff sample_count = str_to_uint32(data[12:16]) pos = 16 # data_offset_present = False if flags & 0x1: # Data offset present # data_offset_present = True self.trun_offset = str_to_uint32(data[16:20]) output += uint32_to_str(self.trun_offset) pos += 4 if flags & 0x4: pos += 4 # First sample flags present sample_duration_present = flags & 0x100 sample_size_present = flags & 0x200 sample_flags_present = flags & 0x400 sample_comp_time_present = flags & 0x800 sample_time_tfdt = self.tfdt orig_sample_pos = 0 for i in range(sample_count): duration = 0 size = 0 flags = 0 comp_time = 0 if sample_duration_present: duration = str_to_uint32(data[pos:pos + 4]) pos += 4 if sample_size_present: size = str_to_uint32(data[pos:pos + 4]) pos += 4 if sample_flags_present: flags = str_to_uint32(data[pos:pos + 4]) pos += 4 if sample_comp_time_present: comp_time = str_to_uint32(data[pos:pos + 4]) pos += 4 start_time = 0 if i == 0: start_time = (sample_time_tfdt) / float(self.time_scale) else: start_time = (sample_time_tfdt + comp_time) / float( self.time_scale) end_time = (sample_time_tfdt + comp_time + duration) / float( self.time_scale) # start_time = (sample_time_tfdt) / float(self.time_scale) # end_time = (sample_time_tfdt + duration) / float(self.time_scale) # print("startTime:", start_time, "(", comp_time, ")", ", endTime:", end_time) scc_samples = self.get_scc_data(start_time, end_time) orig_sample_pos += size if len(scc_samples): print( " ", i, "SampleTime: " + str( (sample_time_tfdt) / float(self.time_scale)), "num samples to add: ", len(scc_samples)) scc_generated_data = generate_data(scc_samples) self.scc_map.append({ 'pos': orig_sample_pos, 'scc': scc_generated_data, 'len': len(scc_generated_data) }) size += len(scc_generated_data) if sample_duration_present: output += uint32_to_str(duration) if sample_size_present: output += uint32_to_str(size) if sample_flags_present: output += uint32_to_str(flags) if sample_comp_time_present: output += uint32_to_str(comp_time) sample_time_tfdt += duration return output
def process_mdhd(self, data): "Process mdhd to get track_timscale." self._track_timescale = str_to_uint32(data[20:24]) return data
def check_box(self, data): "Check the type of box starting at position pos." size = str_to_uint32(data[:4]) boxtype = data[4:8] return (size, boxtype)