基于QuickTime 文件格式规范,我整理了一个概念验证 Python 脚本,用于粗略地转换时间戳,让我现在可以通过:
#!/usr/bin/env python
import datetime
import sys
def int_bytes(raw):
value = 0
for byte in raw:
value <<= 8
value += ord(byte)
return value
def bytes_int(value, size=4):
raw = []
for byte in range(size):
raw.append(chr((value >> 8*byte) % 256))
return ''.join(reversed(raw))
ATOM_FORMAT = (
('atom_size', 4, int_bytes),
('type', 4, str),
('version', 1, int_bytes),
('flags', 3, int_bytes),
('creation_time', 4, int_bytes),
('modification_time', 4, int_bytes),
# that's all I need for now, and is common
# between tkhd, mvhd and mdhd
# ...
)
ATOM_TYPES = ('tkhd', 'mvhd', 'mdhd')
TIMESTAMP_EPOCH = datetime.datetime(1904, 1, 1, 0, 0)
def from_timestamp(timestamp):
return TIMESTAMP_EPOCH + datetime.timedelta(0, timestamp)
def to_timestamp(datetime_obj):
return int((datetime_obj - TIMESTAMP_EPOCH).total_seconds())
def shift_dates(mp4, atom_type, delta):
# TODO: refactor
mp4.seek(0)
data = mp4.read() # TODO: don't load whole file
type_index = -1
while True:
try:
type_index = data.index(atom_type, type_index+1)
except ValueError:
if type_index < 0:
raise RuntimeError('Cannot find atom: {}'.format(atom_type))
else:
break
else:
sys.stdout.write(
' Found {} at {}\n'.format(atom_type, type_index))
offset = type_index - ATOM_FORMAT[0][1]
header_data = {}
offsets = {}
for field, size, convert in ATOM_FORMAT:
offsets[field] = offset
offset += size
header_data[field] = convert(data[offsets[field]:][:size])
for field in ('creation_time', 'modification_time'):
original = from_timestamp(header_data[field])
shifted = original + delta
mp4.seek(offsets[field])
mp4.write(bytes_int(to_timestamp(shifted)))
sys.stdout.write(
' {}: {} -> {}\n'.format(field, original, shifted))
if __name__ == '__main__':
try:
filename = sys.argv[1]
days, seconds = map(int, sys.argv[2:])
except (IndexError, TypeError, ValueError):
sys.stderr.write(
"USAGE: {} mp4_file days seconds\n".format(
sys.argv[0]
)
)
sys.exit(1)
try:
f = open(filename, 'rwb+')
except IOError:
sys.stderr.write("ERROR: cannot open {}\n".format(filename))
sys.exit(1)
else:
delta = datetime.timedelta(days, seconds)
sys.stdout.write(
'Shifting timestamps of {} by {!r}:\n'.format(filename, delta))
for atom_type in ATOM_TYPES:
shift_dates(f, atom_type, delta)
f.close()
sys.stdout.write('Done.\n')