thekid / dialog

Dialog photoblog
2 stars 1 forks source link

Read meta data from videos #12

Closed thekid closed 10 months ago

thekid commented 1 year ago

See also:

thekid commented 1 year ago

POC

<?php

use io\File;
use util\cmd\Console;

function atom($f) {
  $atom= unpack('Nlength/a4name', $f->read(8));
  if (0 === $atom['length']) {
    $atom['length']= $f->size() - $f->tell();
  } else if (1 === $atom['length']) {
    $atom['length']= unpack('J', $f->read(8))[1];
  }
  return $atom;
}

function atoms($f) {
  static $CONTAINERS= ['moov' => 1, 'udta' => 1, 'ilst' => 1];

  while (($offset= $f->tell()) < $f->size()) {
    yield $offset => $atom= atom($f);
    $end= $offset + $atom['length'];

    if (isset($CONTAINERS[$atom['name']])) {
      while (($offset= $f->tell()) < $end) {
        yield $offset => ['parent' => $atom['name']] + $child= atom($f, false);

        $f->seek($offset + $child['length'], SEEK_SET);
      }
    }

    $f->seek($end, SEEK_SET);
  }
}

$f= new File($argv[1]);
$f->open(File::READ);
try {
  foreach (atoms($f) as $offset => $atom) {
    Console::writeLinef(
      '@%012d %s%s: %d bytes',
      $offset,
      isset($atom['parent']) ? $atom['parent'].'.' : '',
      $atom['name'],
      $atom['length']
    );
  }
} finally {
  $f->close();
}

Results for a video filmed on iOS

$ xp atoms.script.php Albums/4.5_islands-2019/horikawacho_2019-05-24/20190524_112729000_iOS.MOV
@000000000000 ftyp: 20 bytes
@000000000020 wide: 8 bytes
@000000000028 mdat: 26162772 bytes
@000026162800 moov: 21092 bytes
@000026162808 moov.mvhd: 108 bytes
@000026162916 moov.trak: 12382 bytes
@000026175298 moov.trak: 5848 bytes
@000026181146 moov.trak: 1678 bytes
@000026182824 moov.trak: 629 bytes
@000026183453 moov.meta: 439 bytes

Results for a Windows phone video

$ xp atoms.script.php Albums/sudamerica-2018/rumbling_through_the_night-2018-12-14/WP_20181214_23_23_30_Pro.mp4
@000000000000 ftyp: 24 bytes
@000000000024 uuid: 40 bytes
@000000000064 mdat: 70401394 bytes
@000070401458 moov: 34089 bytes
@000070401466 moov.mvhd: 108 bytes
@000070401574 moov.trak: 15807 bytes
@000070417381 moov.trak: 18069 bytes
@000070435450 moov.udta: 97 bytes

We can now use the mvhd atom to extract duration and creation time, see here and the meta and udta atoms to extract further information as described here and here

thekid commented 1 year ago

Windows phone encodes GEO information inside udta as follows:

[
  length => 29
  name => "�xyz"
  data => util.Bytes(21)@{\000\021\025\307-40.6754-65.0117/}
]
thekid commented 1 year ago

iOS encodes GEO information inside meta as follows:

 [
  length => 201
  name => "keys"
  data => util.Bytes(193)@{\000\000\000\000\000\000\000\005\000\000\000,mdtacom.apple.quicktime.location.ISO6709\000\000\000 mdtacom.apple.quicktime.make\000\000\000!mdtacom.apple.quicktime.model\000\000\000$mdtacom.apple.quicktime.software\000\000\000(mdtacom.apple.quicktime.creationdate}
], [
  length => 196
  name => "ilst"
  data => util.Bytes(188)@{\000\000\0002\000\000\000\001\000\000\000*data\000\000\000\001\000\000\000\000+34.3924+132.4644+010.789/\000\000\000\035\000\000\000\002\000\000\000\025data\000\000\000\001\000\000\000\000Apple\000\000\000!\000\000\000\003\000\000\000\031data\000\000\000\001\000\000\000\000iPhone XS\000\000\000\034\000\000\000\004\000\000\000\024data\000\000\000\001\000\000\000\00012.2\000\000\0000\000\000\000\005\000\000\000(data\000\000\000\001\000\000\000\0002019-05-24T20:27:29+0900}
]
thekid commented 1 year ago

Canon embeds EXIF data inside udta as follows:

[
  length => 65552
  name => "CNTH"
  data => util.Bytes(65544)@{\000\000B\331CNDA\377\330\377\341\r\302Exif\000\000II*...
]

...where \377\330\377 (following CNDA) is the start of image marker, which we should be able to parse the img.io.MetaDataReader class from https://github.com/xp-framework/imaging

thekid commented 1 year ago

Android embeds a preview image (which I save to cover.jpeg) and uses the same (c)xyz udta key as Windows Phone:

$ xp atoms.script.php 'C:\Users\timmf\Downloads\VID_20221211_175220.mp4'
@000000000000 ftyp: 24 bytes
@000000000024 free: 2163 bytes
@000000002187 free: 403108 bytes
@000000405295 mdat: 2440188 bytes
@000002845483 moov: 1150801 bytes
@000002845491 moov.mvhd: 108 bytes
@000002845599 moov.udta: 1148676 bytes
@000002845607 moov.udta.©xyz: 30 bytes
> parsed := util.Bytes(22)@{\000\022\025\307+XX.XXXX+0YY.YYYY/}
@000002845637 moov.udta.mcvr: 1148638 bytes
> parsed := io.File(uri= C:\Tools\Cygwin\home\timmf\devel\dialog\cover.jpeg, mode= wb)
@000003994275 moov.meta: 237 bytes
@000003994283 moov.meta.hdlr: 33 bytes
@000003994316 moov.meta.keys: 100 bytes
> parsed := [
  1 => "mdta:com.android.version"
  2 => "mdta:com.android.manufacturer"
  3 => "mdta:com.android.model"
]
@000003994416 moov.meta.ilst: 96 bytes
> parsed := [
  1 => ["12"]
  2 => ["Xiaomi"]
  3 => ["M2002J9G"]
]
@000003994512 moov.trak: 1113 bytes
@000003995625 moov.trak: 659 bytes

See also https://stackoverflow.com/questions/28916329/mp4-video-file-with-gps-location

Thanks @kiesel for the sample video!

thekid commented 10 months ago

Implemented in https://github.com/thekid/dialog/releases/tag/v1.16.0