18

I' ve a .MOV video sent by a phone messanger app. Can I retrieve the real creation data of the file and the author? I tried with ffprobe, mediainfo and similar tool but give me only the date when I download it.

Manuel Castro
  • 1,633
  • 3
  • 24
  • 38

5 Answers5

12

So, I updated MMM's code to Python3 and improved a few things.

def get_mov_timestamps(filename):
    ''' Get the creation and modification date-time from .mov metadata.

        Returns None if a value is not available.
    '''
    from datetime import datetime as DateTime
    import struct

    ATOM_HEADER_SIZE = 8
    # difference between Unix epoch and QuickTime epoch, in seconds
    EPOCH_ADJUSTER = 2082844800

    creation_time = modification_time = None

    # search for moov item
    with open(filename, "rb") as f:
        while True:
            atom_header = f.read(ATOM_HEADER_SIZE)
            #~ print('atom header:', atom_header)  # debug purposes
            if atom_header[4:8] == b'moov':
                break  # found
            else:
                atom_size = struct.unpack('>I', atom_header[0:4])[0]
                f.seek(atom_size - 8, 1)

        # found 'moov', look for 'mvhd' and timestamps
        atom_header = f.read(ATOM_HEADER_SIZE)
        if atom_header[4:8] == b'cmov':
            raise RuntimeError('moov atom is compressed')
        elif atom_header[4:8] != b'mvhd':
            raise RuntimeError('expected to find "mvhd" header.')
        else:
            f.seek(4, 1)
            creation_time = struct.unpack('>I', f.read(4))[0] - EPOCH_ADJUSTER
            creation_time = DateTime.fromtimestamp(creation_time)
            if creation_time.year < 1990:  # invalid or censored data
                creation_time = None

            modification_time = struct.unpack('>I', f.read(4))[0] - EPOCH_ADJUSTER
            modification_time = DateTime.fromtimestamp(modification_time)
            if modification_time.year < 1990:  # invalid or censored data
                modification_time = None

    return creation_time, modification_time

and...

Wouldn't you know it, just as I finished I found how to do it with exiftool, which I use for similar tasks with .jpg files. :-/

⏵ exiftool -time:all img_3904.mov
Stevoisiak
  • 23,794
  • 27
  • 122
  • 225
Gringo Suave
  • 29,931
  • 6
  • 88
  • 75
11

I wrote a quick Python 2 script that can obtain the creation and modification timestamps, since those are easy to find. Finding the author is a bit harder because it can be stored in several different ways. Example usage:

$ ./mov-timestamps.py file.mov
creation date: 2013-03-29 16:14:01
modification date: 2013-03-29 16:14:13

Sometimes you might see a date of 1/1/1904. That means the timestamp is 0. If you see a date of 1/1/1970, the file was probably generated by FFmpeg, which doesn't store this metadata for security reasons.

#!/usr/bin/python

import datetime
import struct
import sys

ATOM_HEADER_SIZE = 8
# difference between Unix epoch and QuickTime epoch, in seconds
EPOCH_ADJUSTER = 2082844800

if len(sys.argv) < 2:
    print "USAGE: mov-length.py <file.mov>"
    sys.exit(1)

# open file and search for moov item
f = open(sys.argv[1], "rb")
while 1:
    atom_header = f.read(ATOM_HEADER_SIZE)
    if atom_header[4:8] == 'moov':
        break
    else:
        atom_size = struct.unpack(">I", atom_header[0:4])[0]
        f.seek(atom_size - 8, 1)

# found 'moov', look for 'mvhd' and timestamps
atom_header = f.read(ATOM_HEADER_SIZE)
if atom_header[4:8] == 'cmov':
    print "moov atom is compressed"
elif atom_header[4:8] != 'mvhd':
    print "expected to find 'mvhd' header"
else:
    f.seek(4, 1)
    creation_date = struct.unpack(">I", f.read(4))[0]
    modification_date = struct.unpack(">I", f.read(4))[0]
    print "creation date:",
    print datetime.datetime.utcfromtimestamp(creation_date - EPOCH_ADJUSTER)
    print "modification date:",
    print datetime.datetime.utcfromtimestamp(modification_date - EPOCH_ADJUSTER)
Stevoisiak
  • 23,794
  • 27
  • 122
  • 225
Multimedia Mike
  • 12,660
  • 5
  • 46
  • 62
  • Thanks for the script. I run it but it gives me the date in which I downloaded it on pc. So I think that the metadata are been overwritten. – Manuel Castro Jan 28 '14 at 10:35
  • If you're game, you could extend the script so that it digs a little further into the file and finds the creation/modification times of the trak atoms. – Multimedia Mike Jan 29 '14 at 04:07
  • I don' t know how to do this. Can you link me something(tutorial, guide)? – Manuel Castro Jan 29 '14 at 10:24
  • The creation_date becomes 0, when running the script. I searched the binary code and found no "moov". So don't believe that this script will work. But thank you for trying to help me out. – Norfeldt Jan 30 '14 at 12:56
  • @Norfeldt A QuickTime/MP4 file absolutely needs to have a 'moov' and an 'mdat' item. If either of those are missing, it's not a valid file. – Multimedia Mike Jan 30 '14 at 16:33
  • @MultimediaMike I made a mistake by just opening it with notepad++ and searching for `moov` and then close it. Clearly not the right way to do it. – Norfeldt Jan 31 '14 at 10:59
  • True. Use a program called a hex viewer or hex editor. – Multimedia Mike Jan 31 '14 at 18:26
  • @MultimediaMike I tried this and it worked fine for my mov files, but for the mp4 file, it says that the creation date is in the year 1946. It should be 2012. Am I doing something wrong? My file is here: https://www.dropbox.com/s/6abygjx0dikiqf4/2012-04-16%2020.44.38.mp4?dl=0 – b-ryce Sep 20 '14 at 03:41
  • @MultimediaMike your script works great for videos made with GoPro, but i'm trying it with .mov videos from iPhone and looks like dates are stored in UTC timezone. It looks strange because when I'm looking on the apple's live photo (image + small video), I see date for photo stored in local time, but date for movie is stored in UTC. Thought maybe Apple is storing date in local timezone somewhere else (not in mvhd)? – Serafim Suhenky Aug 28 '16 at 18:08
  • @Multimedia Mike: thank you. This does work with Python2 but with 3 it gives an error: ```Traceback (most recent call last): File "mov_timestamp.py", line 22, in atom_size = struct.unpack(">I", atom_header[0:4])[0] struct.error: unpack requires a buffer of 4 bytes ``` Any idea how to change it for Python3? Thanks! – Vlad K. Jul 05 '22 at 12:03
  • 1
    I had to replace the line" `if atom_header[4:8] == 'moov':` with `if atom_header[4:8].decode('utf-8') == 'moov':` to make the first block match the 'moov' target in a .mp4 from Canon (Python 3.10) – Rodrigo Jun 11 '23 at 04:57
  • Thanks for the update. I'm sure I wrote this against Python2 back in the day, which was looser about encodings. – Multimedia Mike Jun 11 '23 at 05:15
7

Did you try hachoir? Install it with pip install hachoir, and then, at the command line:

$ hachoir-metadata IMG_9395.MOV

which returns e.g.

Metadata:
- Duration: 2 sec 220 ms
- Image width: 1440 pixels
- Image height: 1080 pixels
- Creation date: 2020-04-15 20:22:57
- Last modification: 2020-04-15 20:22:58
- Comment: Play speed: 100.0%
- Comment: User volume: 100.0%
- MIME type: video/quicktime
- Endianness: Big endian

You can also use it in Python if you prefer:

from hachoir.parser import createParser
from hachoir.metadata import extractMetadata


def creation_date(filename):
    parser = createParser(filename)
    metadata = extractMetadata(parser)
    return metadata.get('creation_date')
Marc Wouts
  • 669
  • 6
  • 8
4

Here is a version that is not pure python but instead requires libmediainfo that is a part of the mediainfo tool.

import pymediainfo
import sys

media_info = pymediainfo.MediaInfo.parse(sys.argv[1])

#For the first track - otherwise iterate over each track
print(' Encoded date {}'.format(track[0].encoded_date))
print(' Tagged date {}'.format(track[0].tagged_date))
Lars Nordin
  • 2,785
  • 1
  • 22
  • 25
0

QuickTime stores the video creation date in the MediaCreateDate XMP field or at least this is what Exiv2 thinks it is in. That said you can use py3exiv2 to read the creation date from the MOV file metadata.

import time
import pyexiv2

from datetime import datetime

# The XMP field that QuickTime stores media creation date in
MEDIA_CREATE_KEY = 'Xmp.video.MediaCreateDate'

# HFS Epoch offset, difference between 01-01-1904 and 01-01-1970
HFS_EPOCH_OFFSET = 2082844800

def get_ctime_exiv2_mov(fpath: str) -> datetime:
    ret = None

    metadata = pyexiv2.ImageMetadata(fpath)
    metadata.read()

    if MEDIA_CREATE_KEY in metadata:
        _ts_hfs = metadata[MEDIA_CREATE_KEY].raw_value.strip()

        if _ts_hfs and _ts_hfs.isnumeric():
            ts_hfs = int(_ts_hfs)

            if ts_hfs > HFS_EPOCH_OFFSET:
                ts_unix = ts_hfs - HFS_EPOCH_OFFSET

                if ts_unix < time.time():
                    ret = datetime.fromtimestamp(ts_unix)

    return ret