Parsed: 112335

  public function QuicktimeParseNikonNCTG($atom_data) {
    // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG
    // Nikon-specific QuickTime tags found in the NCDT atom of MOV videos from some Nikon cameras such as the Coolpix S8000 and D5100
    // Data is stored as records of:
    // * 4 bytes record type
    // * 2 bytes size of data field type:
    //     0x0001 = flag   (size field *= 1-byte)
    //     0x0002 = char   (size field *= 1-byte)
    //     0x0003 = DWORD+ (size field *= 2-byte), values are stored CDAB
    //     0x0004 = QWORD+ (size field *= 4-byte), values are stored EFGHABCD
    //     0x0005 = float  (size field *= 8-byte), values are stored aaaabbbb where value is aaaa/bbbb; possibly multiple sets of values appended together
    //     0x0007 = bytes  (size field *= 1-byte), values are stored as ??????
    //     0x0008 = ?????  (size field *= 2-byte), values are stored as ??????
    // * 2 bytes data size field
    // * ? bytes data (string data may be null-padded; datestamp fields are in the format "2011:05:25 20:24:15")
    // all integers are stored BigEndian

    $NCTGtagName = array(
      0x00000001 => 'Make',
      0x00000002 => 'Model',
      0x00000003 => 'Software',
      0x00000011 => 'CreateDate',
      0x00000012 => 'DateTimeOriginal',
      0x00000013 => 'FrameCount',
      0x00000016 => 'FrameRate',
      0x00000022 => 'FrameWidth',
      0x00000023 => 'FrameHeight',
      0x00000032 => 'AudioChannels',
      0x00000033 => 'AudioBitsPerSample',
      0x00000034 => 'AudioSampleRate',
      0x02000001 => 'MakerNoteVersion',
      0x02000005 => 'WhiteBalance',
      0x0200000b => 'WhiteBalanceFineTune',
      0x0200001e => 'ColorSpace',
      0x02000023 => 'PictureControlData',
      0x02000024 => 'WorldTime',
      0x02000032 => 'UnknownInfo',
      0x02000083 => 'LensType',
      0x02000084 => 'Lens',
    );

    $offset = 0;
    $data = null;
    $datalength = strlen($atom_data);
    $parsed = array();
    while ($offset < $datalength) {
      $record_type       = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 4));  $offset += 4;
      $data_size_type    = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2));  $offset += 2;
      $data_size         = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2));  $offset += 2;
      switch ($data_size_type) {
        case 0x0001: // 0x0001 = flag   (size field *= 1-byte)
          $data = getid3_lib::BigEndian2Int(substr($atom_data, $offset, $data_size * 1));
          $offset += ($data_size * 1);
          break;
        case 0x0002: // 0x0002 = char   (size field *= 1-byte)
          $data = substr($atom_data, $offset, $data_size * 1);
          $offset += ($data_size * 1);
          $data = rtrim($data, "\x00");
          break;
        case 0x0003: // 0x0003 = DWORD+ (size field *= 2-byte), values are stored CDAB
          $data = '';
          for ($i = $data_size - 1; $i >= 0; $i--) {
            $data .= substr($atom_data, $offset + ($i * 2), 2);
          }
          $data = getid3_lib::BigEndian2Int($data);
          $offset += ($data_size * 2);
          break;
        case 0x0004: // 0x0004 = QWORD+ (size field *= 4-byte), values are stored EFGHABCD
          $data = '';
          for ($i = $data_size - 1; $i >= 0; $i--) {
            $data .= substr($atom_data, $offset + ($i * 4), 4);
          }
          $data = getid3_lib::BigEndian2Int($data);
          $offset += ($data_size * 4);
          break;
        case 0x0005: // 0x0005 = float  (size field *= 8-byte), values are stored aaaabbbb where value is aaaa/bbbb; possibly multiple sets of values appended together
          $data = array();
          for ($i = 0; $i < $data_size; $i++) {
            $numerator    = getid3_lib::BigEndian2Int(substr($atom_data, $offset + ($i * 8) + 0, 4));
            $denomninator = getid3_lib::BigEndian2Int(substr($atom_data, $offset + ($i * 8) + 4, 4));
            if ($denomninator == 0) {
              $data[$i] = false;
            } else {
              $data[$i] = (double) $numerator / $denomninator;
            }
          }
          $offset += (8 * $data_size);
          if (count($data) == 1) {
            $data = $data[0];
          }
          break;
        case 0x0007: // 0x0007 = bytes  (size field *= 1-byte), values are stored as ??????
          $data = substr($atom_data, $offset, $data_size * 1);
          $offset += ($data_size * 1);
          break;
        case 0x0008: // 0x0008 = ?????  (size field *= 2-byte), values are stored as ??????
          $data = substr($atom_data, $offset, $data_size * 2);
          $offset += ($data_size * 2);
          break;
        default:
          echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: '.$data_size_type.'<br>';
          break 2;
      }

      switch ($record_type) {
        case 0x00000011: // CreateDate
        case 0x00000012: // DateTimeOriginal
          $data = strtotime($data);
          break;
        case 0x0200001e: // ColorSpace
          switch ($data) {
            case 1:
              $data = 'sRGB';
              break;
            case 2:
              $data = 'Adobe RGB';
              break;
          }
          break;
        case 0x02000023: // PictureControlData
          $PictureControlAdjust = array(0=>'default', 1=>'quick', 2=>'full');
          $FilterEffect = array(0x80=>'off', 0x81=>'yellow', 0x82=>'orange',    0x83=>'red', 0x84=>'green',  0xff=>'n/a');
          $ToningEffect = array(0x80=>'b&w', 0x81=>'sepia',  0x82=>'cyanotype', 0x83=>'red', 0x84=>'yellow', 0x85=>'green', 0x86=>'blue-green', 0x87=>'blue', 0x88=>'purple-blue', 0x89=>'red-purple', 0xff=>'n/a');
          $data = array(
            'PictureControlVersion'     =>                           substr($data,  0,  4),
            'PictureControlName'        =>                     rtrim(substr($data,  4, 20), "\x00"),
            'PictureControlBase'        =>                     rtrim(substr($data, 24, 20), "\x00"),
            //'?'                       =>                           substr($data, 44,  4),
            'PictureControlAdjust'      => $PictureControlAdjust[ord(substr($data, 48,  1))],
            'PictureControlQuickAdjust' =>                       ord(substr($data, 49,  1)),
            'Sharpness'                 =>                       ord(substr($data, 50,  1)),
            'Contrast'                  =>                       ord(substr($data, 51,  1)),
            'Brightness'                =>                       ord(substr($data, 52,  1)),
            'Saturation'                =>                       ord(substr($data, 53,  1)),
            'HueAdjustment'             =>                       ord(substr($data, 54,  1)),
            'FilterEffect'              =>         $FilterEffect[ord(substr($data, 55,  1))],
            'ToningEffect'              =>         $ToningEffect[ord(substr($data, 56,  1))],
            'ToningSaturation'          =>                       ord(substr($data, 57,  1)),
          );
          break;
        case 0x02000024: // WorldTime
          // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#WorldTime
          // timezone is stored as offset from GMT in minutes
          $timezone = getid3_lib::BigEndian2Int(substr($data, 0, 2));
          if ($timezone & 0x8000) {
            $timezone = 0 - (0x10000 - $timezone);
          }
          $timezone /= 60;

          $dst = (bool) getid3_lib::BigEndian2Int(substr($data, 2, 1));
          switch (getid3_lib::BigEndian2Int(substr($data, 3, 1))) {
            case 2:
              $datedisplayformat = 'D/M/Y'; break;
            case 1:
              $datedisplayformat = 'M/D/Y'; break;
            case 0:
            default:
              $datedisplayformat = 'Y/M/D'; break;
          }

          $data = array('timezone'=>floatval($timezone), 'dst'=>$dst, 'display'=>$datedisplayformat);
          break;
        case 0x02000083: // LensType
          $data = array(
            //'_'  => $data,
            'mf' => (bool) ($data & 0x01),
            'd'  => (bool) ($data & 0x02),
            'g'  => (bool) ($data & 0x04),
            'vr' => (bool) ($data & 0x08),
          );
          break;
      }
      $tag_name = (isset($NCTGtagName[$record_type]) ? $NCTGtagName[$record_type] : '0x'.str_pad(dechex($record_type), 8, '0', STR_PAD_LEFT));
      $parsed[$tag_name] = $data;
    }
    return $parsed;
  }