Question

I recently discovered that one of my cameras had its internal clock set almost two-years behind the actual date/time, which upset the ordering of photographs and videos in my library timeline. I quickly discovered the wonderful batch-update functionality of exiftool's date/time shift feature. Unfortunately however it does not yet support modification of MP4 video files:

$ exiftool -AllDates+="0:0:729 3:17:0" test.mp4
Error: Writing of MP4 files is not yet supported - test.mp4
    0 image files updated
    1 files weren't updated due to errors

How can I perform similar batch-date-shifting functionality for MP4 video files?

Was it helpful?

Solution

Based on the QuickTime File Format Specification I put together a proof-of-concept Python script for crude shifting of the timestamps to get me by for now:

#!/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')

OTHER TIPS

ExifTool now has the ability to edit timestamps and many other tags in MP4/MOV video files. But as the AllDates tag only edits three specific tags, two of which don't appear in video files very often, only one tag in a video file is likely to be edited by the above command (see note about AllDates on the Shortcut Tag Names page).

To edit all time related tags the following command could be used:
exiftool -Time:All+="0:0:729 3:17:0" test.mp4

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top