Coverage for pyguymer3/media/print_MP4_atoms.py: 3%
29 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-08 18:47 +0000
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-08 18:47 +0000
1#!/usr/bin/env python3
3# Define function ...
4def print_MP4_atoms(
5 fname,
6 /,
7):
8 # NOTE: The following websites have some very useful information on how to
9 # parse MP4 files - the first just forgot to say that integers are
10 # big-endian.
11 # * http://atomicparsley.sourceforge.net/mpeg-4files.html
12 # * https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html
13 # * https://wiki.multimedia.cx/index.php/QuickTime_container
15 # Import standard modules ...
16 import os
17 import re
18 import struct
20 # Import sub-functions ...
21 from ..convert_bytes_to_pretty_bytes import convert_bytes_to_pretty_bytes
23 # Open MP4 read-only ...
24 with open(fname, "rb") as fObj:
25 # Create short-hand ...
26 fsize = os.path.getsize(fname) # [B]
28 # Set trigger ...
29 foundFTYP = False
31 # Loop over entire contents of MP4 ...
32 while fObj.tell() < fsize:
33 # Attempt to read 4 bytes as a big-endian un-signed 32-bit integer ...
34 val, = struct.unpack(">I", fObj.read(4)) # [B]
35 off = 4 # [B]
37 # Extract atom name ...
38 name = fObj.read(4).decode("utf-8")
39 off += 4 # [B]
41 # Check that it matches the pattern ...
42 if re.match(r"[a-z][a-z][a-z][a-z]", name) is None:
43 raise Exception(f"\"{name}\" is not an atom name in \"{fname}\"") from None
45 # Check that it is a MP4 file ...
46 if not foundFTYP and name != "ftyp":
47 raise Exception(f"\"{fname}\" is not a MP4") from None
49 # Set trigger ...
50 foundFTYP = True
52 # Check the length ...
53 if val == 0:
54 # NOTE: This atom runs until EOF.
56 # Print summary ...
57 print(f"{name} is the remainder of the file")
59 # Stop looping ...
60 break
62 # Check the length ...
63 if val == 1:
64 # NOTE: This atom has 64-bit sizes.
66 # Attempt to read 8 bytes as a big-endian un-signed 64-bit
67 # integer ...
68 val, = struct.unpack(">Q", fObj.read(8)) # [B]
69 off += 8 # [B]
71 # Print summary ...
72 size, units = convert_bytes_to_pretty_bytes(val)
73 print(f"{name} is {size:6.1f} {units:3s} long (as a 64-bit atom)")
74 else:
75 # Print summary ...
76 size, units = convert_bytes_to_pretty_bytes(val)
77 print(f"{name} is {size:6.1f} {units:3s} long (as a 32-bit atom)")
79 # Skip to the end of the atom ...
80 fObj.seek(val - off, os.SEEK_CUR)