@@ -109,7 +109,7 @@ package Image::ExifTool::QuickTime;
109109 The tags below are extracted from timed metadata in QuickTime and other
110110 formats of video files when the ExtractEmbedded option is used. Although
111111 most of these tags are combined into the single table below, ExifTool
112- currently reads 74 different formats of timed GPS metadata from video files.
112+ currently reads 76 different formats of timed GPS metadata from video files.
113113 } ,
114114 VARS => { NO_ID => 1 },
115115 GPSLatitude => { PrintConv => ' Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")' , RawConv => ' $$self{FoundGPSLatitude} = 1; $val' },
@@ -1420,9 +1420,10 @@ ($)
14201420 } elsif ($type eq ' gps ' ) { # (ie. GPSDataList tag)
14211421
14221422 if ($buff =~ / ^....freeGPS /s ) {
1423- # process by brute scan instead if ExtractEmbedded >= 3
1424- # (some videos don't reference all freeGPS info from 'gps ' table, eg. INNOV)
1425- last if $eeOpt >= 3;
1423+ # parse freeGPS data unless done already in brute-force scan
1424+ # (some videos don't reference all freeGPS info from 'gps ' table, eg. INNOV,
1425+ # and some videos don't put 'gps ' data in mdat, eg XGODY 12" 4K Dashcam)
1426+ last if $$et {FoundGPSByScan };
14261427 # decode "freeGPS " data (Novatek and others)
14271428 ProcessFreeGPS($et , {
14281429 DataPt => \$buff ,
@@ -2049,9 +2050,41 @@ ($$$)
20492050 }
20502051 }
20512052
2052- } else {
2053+ } elsif ( $$dataPt =~ m < ^.{23}( \d {4})/( \d {2})/( \d {2}) ( \d {2}):( \d {2}):( \d {2}) [N|S] > s ) {
20532054
20542055 $debug and $et -> FoundTag(GPSType => 16);
2056+ # XGODY 12" 4K Dashcam
2057+ # 0000: 00 00 00 a8 66 72 65 65 47 50 53 20 98 00 00 00 [....freeGPS ....]
2058+ # 0010: 6e 6f 72 6d 61 6c 3a 32 30 32 34 2f 30 35 2f 32 [normal:2024/05/2]
2059+ # 0020: 32 20 30 32 3a 35 34 3a 32 39 20 4e 3a 34 32 2e [2 02:54:29 N:42.]
2060+ # 0030: 33 38 32 34 37 30 20 57 3a 38 33 2e 33 38 39 35 [382470 W:83.3895]
2061+ # 0040: 37 30 20 35 33 2e 36 20 6b 6d 2f 68 20 78 3a 2d [70 53.6 km/h x:-]
2062+ # 0050: 30 2e 30 32 20 79 3a 30 2e 39 39 20 7a 3a 30 2e [0.02 y:0.99 z:0.]
2063+ # 0060: 31 30 20 41 3a 32 36 39 2e 32 20 48 3a 32 34 35 [10 A:269.2 H:245]
2064+ # 0070: 2e 35 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [.5..............]
2065+ ($yr ,$mon ,$day ,$hr ,$min ,$sec ) = ($1 ,$2 ,$3 ,$4 ,$5 ,$6 );
2066+ $$dataPt =~ s /\0 +$// ; # remove trailing nulls
2067+ my @a = split ' ' , substr ($$dataPt ,43);
2068+ $ddd = 1;
2069+ foreach (@a ) {
2070+ unless (/ ^([A-Z]):([-+]?\d +(\.\d +)?)$ /i ) {
2071+ # (the "km/h" after spd is display units? because the value is stored in knots)
2072+ defined $lon and not defined $spd and /^\d+\.\d+$/ and $spd = $_ * $knotsToKph ;
2073+ next ;
2074+ }
2075+ ($1 eq ' N' or $1 eq ' S' ) and $lat = $2 , $latRef = $1 , next ;
2076+ ($1 eq ' E' or $1 eq ' W' ) and $lon = $2 , $lonRef = $1 , next ;
2077+ ($1 eq ' x' or $1 eq ' y' or $1 eq ' z' ) and push (@acc ,$2 ), next ;
2078+ $1 eq ' A' and $trk = $2 , next ; # (verified, but why 'A'?)
2079+ # seen 'H' - one might expect altitude ('H'eight), but it doesn't fit
2080+ # the sample data, so save all other information as an "Unknown_X" tag
2081+ $$tagTbl {$1 } or AddTagToTable($tagTbl , $1 , { Name => " Unknown_$1 " , Unknown => 1 });
2082+ push (@xtra , $1 => $2 ), next ;
2083+ }
2084+
2085+ } else {
2086+
2087+ $debug and $et -> FoundTag(GPSType => 17);
20552088 # (look for binary GPS as stored by Nextbase 512G, ref PH)
20562089 # 0000: 00 00 80 00 66 72 65 65 47 50 53 20 78 01 00 00 [....freeGPS x...]
20572090 # 0010: 78 2e 78 78 00 00 00 00 00 00 00 00 00 00 00 00 [x.xx............]
@@ -2115,7 +2148,7 @@ ($$$)
21152148 my $time = sprintf (' %.2d:%.2d:%sZ' ,$hr ,$min ,$sec );
21162149 $et -> HandleTag($tagTbl , GPSTimeStamp => $time );
21172150 }
2118- if (defined $lat ) {
2151+ if (defined $lat and defined $lon ) {
21192152 # lat/long are in DDDMM.MMMM format unless $ddd is set
21202153 ConvertLatLon($lat , $lon ) unless $ddd ;
21212154 $et -> HandleTag($tagTbl , GPSLatitude => $lat * ($latRef eq ' S' ? -1 : 1));
@@ -2680,6 +2713,53 @@ ($$$)
26802713 return 1;
26812714}
26822715
2716+ # ------------------------------------------------------------------------------
2717+ # Process Kenwood Dashcam trailer (forum16229)
2718+ # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
2719+ # Returns: 1 on success
2720+ # Sample data (chained 512-byte records starting like this):
2721+ # 0000: 43 43 43 43 43 43 43 43 43 43 43 43 43 43 47 50 [CCCCCCCCCCCCCCGP]
2722+ # 0010: 53 44 41 54 41 2d 2d 32 30 32 34 30 37 31 31 31 [SDATA--202407111]
2723+ # 0020: 32 30 34 31 32 4e 35 30 2e 36 31 32 33 38 36 30 [20412N50.6123860]
2724+ # 0030: 36 37 37 45 38 2e 37 30 32 37 31 38 30 39 38 39 [677E8.7027180989]
2725+ # 0040: 35 33 33 2e 30 30 30 30 30 30 30 30 30 30 30 30 [533.000000000000]
2726+ # 0050: 2e 30 30 30 30 30 30 30 30 30 30 30 30 30 2e 30 [.0000000000000.0]
2727+ # 0060: 31 39 39 39 39 39 39 39 35 35 33 2d 30 2e 30 39 [19999999553-0.09]
2728+ # 0070: 30 30 30 30 30 30 33 35 37 2d 30 2e 31 34 30 30 [000000357-0.1400]
2729+ # 0080: 30 30 30 30 30 35 39 47 50 53 44 41 54 41 2d 2d [0000059GPSDATA--]
2730+ sub ProcessKenwoodTrailer ($$$)
2731+ {
2732+ my ($et , $dirInfo , $tagTbl ) = @_ ;
2733+ my $raf = $$dirInfo {RAF };
2734+ my $buff ;
2735+ # current file position is 8 bytes into the 14 C's, so test the next 6:
2736+ $raf -> Read($buff , 14) and $buff eq ' CCCCCCCCCCCCCC' or return 0;
2737+ $et -> VerboseDir(' Kenwood trailer' , undef , undef );
2738+ unless ($$et {OPTIONS }{ExtractEmbedded }) {
2739+ $et -> WarnOnce(' Use the ExtractEmbedded option to extract timed GPSData from Kenwood trailer' ,3);
2740+ return 1;
2741+ }
2742+ while ($raf -> Read($buff , 121) and $buff =~ / ^GPSDATA--(\d {4})(\d {2})(\d {2})(\d {2})(\d {2})(\d {2})/ ) {
2743+ FoundSomething($et , $tagTbl );
2744+ $et -> HandleTag($tagTbl , GPSDateTime => " $1 :$2 :$3 $4 :$5 :$6 " );
2745+ my $i = 9 + 14;
2746+ my ($val , @acc , $tag );
2747+ foreach $tag (qw( GPSLatitude GPSLongitude GPSSpeed unk acc acc acc) ) {
2748+ $val = substr ($buff , $i , 14); $i += 14;
2749+ next if $tag eq ' unk' ;
2750+ my $hemi ;
2751+ $hemi = $1 if $val =~ s / ^([NSEW])// ;
2752+ $val =~ / ^[-+]?\d +\.\d +$ / or next ;
2753+ $tag eq ' acc' and push (@acc ,$val ), next ;
2754+ $val = -$val if $hemi and ($hemi eq ' S' or $hemi eq ' W' );
2755+ $et -> HandleTag($tagTbl , $tag => $val );
2756+ }
2757+ $et -> HandleTag($tagTbl , Accelerometer => " @acc " ) if @acc == 3;
2758+ }
2759+ delete $$et {DOC_NUM };
2760+ return 1;
2761+ }
2762+
26832763# ------------------------------------------------------------------------------
26842764# Process 'gps ' atom containing NMEA from Pittasoft Blackvue dashcam (ref PH)
26852765# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
@@ -3353,6 +3433,7 @@ ($)
33533433 }
33543434 my $dirInfo = { DataPt => \$buff , DataPos => $pos + $dataPos , DirLen => $len };
33553435 ProcessFreeGPS($et , $dirInfo , $tagTbl );
3436+ $$et {FoundGPSByScan } = 1;
33563437 }
33573438 $pos += $len ;
33583439 $buf2 = substr ($buff , $len );
0 commit comments