Main Menu

Recent posts

#1
A friend asked me if I could decode his dashcam GPS data.
It's an MP4 video with a "gps " atom.
The gps atom contains:
  Two digit version: 01 01 = 1.1
  4 byte number of records
  Then a set of 8 bytes per record: file offset for data and data size.

Each data record begins with "LIGOGPSINFO" and is followed by encoded bytes.
E.g.:
LIGOGPSINFO
####;
4FDF1/D9/E8 D=LGJL98 :L11.1=1JD8 ALEDD.GFDF=D FF.1? km/h xLD.E yL-D.D zLD.E
It looks like Phil tried to look at this encoding earlier and gave up. (https://exiftool.org/forum/index.php?topic=15387.0)

I think I've made progress on decoding it. But it's not perfect...
00068960   DA FB BA 8C  B1 66 FB B1  00 00 40 00  66 72 65 65  .....f....@.free
00068970   47 50 53 20  98 00 00 00  4C 49 47 4F  47 50 53 49  GPS ....LIGOGPSI
00068980   4E 46 4F 00  00 00 00 0D  0A 00 00 00  23 23 23 23  NFO.........####
00068990   3B 00 A0 34  46 44 46 31  2F 44 39 2F  45 38 20 44  ;..4FDF1/D9/E8 D
000689A0   3D 4C 47 4A  4C 39 38 20  3A 4C 31 31  2E 31 3D 31  =LGJL98 :L11.1=1
000689B0   4A 44 38 20  41 4C 45 44  44 2E 47 46  44 46 3D 44  JD8 ALEDD.GFDF=D
000689C0   20 46 46 2E  31 3F 20 6B  6D 2F 68 20  78 4C 44 2E   FF.1? km/h xLD.
000689D0   45 20 79 4C  2D 44 2E 44  20 7A 4C 44  2E 45 00 00  E yL-D.D zLD.E..
00004000 = block size
 "freeGPS " + 4 more bytes (unknown)
 "LIGOGPSINFO" + null
  00000d = length of header
    Header is the "####;" and ends and the "4".

Then comes the text string: "FDF1/D9/E8 D=LGJL98 :L11.1=1JD8 ALEDD.GFDF=D FF.1? km/h xLD.E yL-D.D zLD.E"
The "FDF1 is the giveaway. In the video, the dashboard time is "2024/05/19". This looks like a simple caesar cipher:
// vc is the character I want to print
        if (vc < ' ') { break; } // binary
        // substitution cipher!
        switch(vc)
          {
          case 'L': vc=':'; break;
          case '8': vc='9'; break;
          case '=': vc='8'; break;
          case 'J': vc='7'; break;
          case '?': vc='6'; break;
          case '9': vc='5'; break;
          case '1': vc='4'; break;
          case 'G': vc='3'; break;
          case 'F': vc='2'; break;
          case 'E': vc='1'; break;
          case 'D': vc='0'; break;
          case 'A': vc='-'; break;
          case ':': vc='+'; break;
          case '/': break;
          case '.': break;
          case '-': break;
          case ' ': break;
          default:  break;
          }
This decodes the string as "2024/05/19 08:37:59 +:44.484709 -:100.320280 22.46 km/h x:0.1 y:-0.0 z:0.1"
The date and time matches the video.

The weird things:
  • The GPS location appears to be off by +4,-6.5 (for my sample video, it should be around 40.48,-106.82; downtown Steamboat Springs, Colorado). The 4 degrees offset for latitude seems perfect, but the longitude is a fraction more; using -6.5 places it within 0.3 miles of the location.
  • The speed in the video says 25.8 mph. That should be 41.5 km/h, but the data encoded as 22.46 km/h. Either it's lying about encoding as km/h, or it's off by 19 km/h, or they changed the decoding.
I suspect that they are using hard-coded math offsets for obfuscation.

What I don't know:
  • Does the caesar cipher remain consistent with all videos, or is it per-video?
  • Are the math adjustments hard-coded or embedded in some of the bytes that I skipped as unknown?

Now for the really bad news: I can't share the video I'm looking at, and I don't have any others for comparison.

Does Phil have any examples he can check?
#2
Newbies / Re: How to extract the content...
Last post by StarGeek - Today at 04:34:29 PM
Use the -S (-veryShort) option or the -p (-printFormat) option.

I haven't been able to figure out how to make it work on more than one file at a time and am waiting to see if Phil can offer any insight, but I suspect the weather is very good in his area ATM and he's out birding.
#3
Newbies / Re: How to extract the content...
Last post by FixEUser - Today at 03:34:01 PM
Thanks for your detailed answer and your efforts.

QuoteThat sounds like you didn't save the complete config file.  Line 9675 in the code is checking for the the very last line of the config file
You are right! My fault, the last line was missing for unknown reasons.

Please remember that I really would prefer the content of the specified tag itself:
e5d018af-3a9d-4ed9-9593-a7b0b63c4579
not the output of the entire line:
[Composite]    MusicBrainzTRMId                : e5d018af-3a9d-4ed9-9593-a7b0b63c4579

Otherwise, I would still have to use an additional batch to strip away the part before the : (colon) to get the needed part after the : (colon) and the space.
#4
Newbies / Re: How to extract the content...
Last post by StarGeek - Today at 02:55:40 PM
Running the command on multiple files, I do see there is a problem where subsequent files will return an entry containing HASH(0x...) for previously defined tags, so I'll have to work on that.  But on single files, it works correctly.

Quote from: FixEUser on Today at 04:48:34 AMThere are at least 3 problems with your script:

1) It doesn't return any other value then without -config join_tags_ID3-config

I'm not quite sure what you mean here. It still extracts all the rest of the data normally and as I said, extracting just the data you want will probably require a config file. Maybe Phil can figure out some way of extracting just the data for a specific copy of the tag without requiring it to be the exact same copy location each time, but I can't think of any way to do that.

Quote2) It returns an error "join_tags_ID3.config did not return a true value at Image/ExifTool.pm line 9675."

That sounds like you didn't save the complete config file.  Line 9675 in the code is checking for the the very last line of the config file
1; #end
Quote3) For other readers: The config file needs to be named exactly the same as you use it in the command line:
exiftool -config join_tags_ID3.config -G1 -a -s -UserDefinedText Y:\temp\x\y\test.mp3

I'm not sure what your point is. If you're using a config file with the -Config option, you have to use the same name you use when you saved it to disk.

Quote@StarGeek: I'm not sure if you understand my initial question:

I asked for a solution to get 1 (one) specific tag content, for example "MusicBrainz Album Artist Id".
The idea would be a (not yet working) syntax like
exiftool -config specificID3.config -G1 -a -s -UserDefinedText("MusicBrainz Album Artist Id") music.mp3and then get only this value:
d87e52c5-bb8d-4da8-b941-9f4928627dc8

How could I achieve that?

That's what my config file attempts to do. At least, once I fix the bug. It would take the UserDefinedText that contains "(MusicBrainz Album Artist Id)" and return the contents of that as a tag called MusicBrainzAlbumArtistId.

It works correctly on a single file, just not in batch ATM.
Example, the UserDefinedText that contains "(MusicBrainz TRM Id)" creates a tag called MusicBrainzTRMId which containts the value of "e5d018af-3a9d-4ed9-9593-a7b0b63c4579".
C:\>exiftool -config join_tags_ID3.config -G1 -a -s -MusicBrainzTRMId "Y:/Music/Music/Sorted/Albums/Journey/Greatest_Hits/01-Only_the_Young.mp3"
[Composite]     MusicBrainzTRMId                : e5d018af-3a9d-4ed9-9593-a7b0b63c4579

Listing all the "MusicBrainz" tags created
C:\>exiftool -config join_tags_ID3.config -G1 -a -s -UserDefinedText -*Brainz* "Y:/Music/Music/Sorted/Albums/Journey/Greatest_Hits/01-Only_the_Young.mp3"
[ID3v2_3]       UserDefinedText                 : (MusicBrainz TRM Id) e5d018af-3a9d-4ed9-9593-a7b0b63c4579
[ID3v2_3]       UserDefinedText                 : (MusicBrainz Artist Id) abd506e1-6f2b-4d6f-b937-92c267f6f88b
[ID3v2_3]       UserDefinedText                 : (MusicBrainz Album Id) 43ca7ac8-bd36-4468-bab4-004a9a34de66
[ID3v2_3]       UserDefinedText                 : (MusicBrainz Album Type) compilation
[ID3v2_3]       UserDefinedText                 : (MusicBrainz Album Status) official
[ID3v2_3]       UserDefinedText                 : (MusicBrainz Album Artist Id)
[ID3v2_3]       UserDefinedText                 : (MusicBrainz Album Release Country) US
[Composite]     MusicBrainzAlbumReleaseCountry  : US
[Composite]     MusicBrainzTRMId                : e5d018af-3a9d-4ed9-9593-a7b0b63c4579
[Composite]     MusicBrainzArtistId             : abd506e1-6f2b-4d6f-b937-92c267f6f88b
[Composite]     MusicBrainzAlbumId              : 43ca7ac8-bd36-4468-bab4-004a9a34de66
[Composite]     MusicBrainzAlbumType            : compilation
[Composite]     MusicBrainzAlbumStatus          : official
[Composite]     MusicBrainzAlbumArtistId        :

QuoteAre you sure that
my $table = Image::ExifTool::GetTagTable('Image::ExifTool::Composite');
is the correct way to get the UserDefinedText content?

This isn't getting the UserDefinedText. This is the part that creates the new tags.  The new tags get added to the Composite group.

The parts that get the UserDefinedText are
my $nameTag = 'UserDefinedText';and
            Desire => {
                0 => $nameTag,

Final edit: I haven't been able to fix the HASH problem when using it on multiple files, so I'm bugging Phil about fixing it.
#5
Quote from: maxardis on Today at 02:21:13 PMJust one question.  I cannot seem to find any mention of ConvertUnixTime() in man pages nor by googling (only mentioned on some other threads on this forum).  And you also say that it is an internal function.  So I guess it means that it might "disappear" with some future version of exiftool right?  It is kind of a hack, sort of, right?

Yes, the only way to know about it is to dig through the source code or have seen Phil use it in the past.  I seriously doubt it will disappear, as it is a basic part of so much of the exiftool code and would most likely require a huge rewrite of a lot of code to remove it. And Phil strives hard to make sure any changes are backwards compatible. For example, the -AddTagsFromFile option no longer appears in the documentation, as it has been replaced with a different format, but it still will work if you use it.

Hack? I wouldn't say so. It's more of a function that isn't documented outside of the source code

Quote
Quote from: StarGeek on Today at 12:13:34 PMYou don't mention your OS, but you appear to be on Mac/Linux

I am on Linux if that is important, yes.  I can translate Windows CMD into Linux one no problem.  It's pretty much the same

It's important when it comes to quoting, as double quotes on Linux/Mac will change something like $DateTimeOriginal from an exiftool tag into a shell variable.  It's also important with something like the asterisk, as using it as an exiftool wildcard such as -*Date will cause file globbing on Mac/Linux unless single quotes are used '-*Date'
#6
That works perfectly.  Thank you very much.

Just one question.  I cannot seem to find any mention of ConvertUnixTime() in man pages nor by googling (only mentioned on some other threads on this forum).  And you also say that it is an internal function.  So I guess it means that it might "disappear" with some future version of exiftool right?  It is kind of a hack, sort of, right?

Quote from: StarGeek on Today at 12:13:34 PMYou don't mention your OS, but you appear to be on Mac/Linux

I am on Linux if that is important, yes.  I can translate Windows CMD into Linux one no problem.  It's pretty much the same
#7
Quote from: maxardis on Today at 06:52:02 AMI am trying to do this:

exiftool -p '${exif:DateTimeOriginal;ShiftTime(${exif:OffsetTimeOriginal}, -1)} ${directory}/${filename}' ./dir/*

And what I get is a warning:
Warning: Bareword "OffsetTimeOriginal" not allowed while "strict subs" in use for 'exif:DateTimeOriginal'.  I don't understand that message and I don't know what I am doing wrong, nor how to fix it.

Nice attempt, but it isn't that simple to access a different tag within the processing of another tag. The error message is directly from the underlying Perl processor. Accessing a second tag from within a tag can be done, but I think this would be a better way.

First, instead of using DateTimeOriginal, use SubSecDateTimeOriginal.  This is a Composite tag that combines DateTimeOriginal, OffsetTimeOriginal, and SubSecTimeOriginal.  Then the DateFmt helper function is used to convert that to a Unix time stamp, the internal exiftool function of ConvertUnixTime is used to convert that to a normal date in the standard EXIF format (colons instead of hyphens), and DateFmt is used again to convert that into the format you're looking for.

Example
C:\>exiftool -G1 -a -s -SubSecDateTimeOriginal y:\!temp\Test4.jpg
[Composite]     SubSecDateTimeOriginal          : 2024:05:20 12:00:00-07:00

C:\>exiftool -p "${SubSecDateTimeOriginal;DateFmt('%s');$_=ConvertUnixTime($_);DateFmt('%Y-%m-%dT%H:%M:%S')}" y:\!temp\Test4.jpg
2024-05-20T19:00:00

You don't mention your OS, but you appear to be on Mac/Linux.  The above command would be for Windows CMD, so you'll have to swap double/single quotes.
#8
Newbies / Re: How to extract the content...
Last post by FixEUser - Today at 08:56:52 AM
I think, I have found a workaround with a Windows batch file.
This may be a little bit more convenient for Windows users.

Write (copy & paste) this code in a batch file and name it like
ShowContentOfThisUndefinedText.cmd

ECHO OFF
@CLS
SETLOCAL enabledelayedexpansion
FOR /F "tokens=2 delims=)" %%M IN ('   
    exiftool.exe -a -UserDefinedText -ext mp3 -recurse . ^| findstr /c:%1 /i
    ') DO (   
          SET LeftTrimmedValue=%%M         
          ECHO !LeftTrimmedValue:~1!         
          )

This batch file assumes, that exiftool.exe is in the same directory as the batch.
Otherwise, you have to write the full path to your exiftool.exe like
C:\Path\To\My\exiftool.exe

Then copy the batch file to the root directory in which the mp3 files to be checked are stored.

Open a CMD window in this root directory.

Start the batch file and add the string you want to search for in the UndefinedText ID3 tag like this:
ShowContentOfThisUndefinedText "musicbrainz album artist id"and you get the wanted
d87e52c5-bb8d-4da8-b941-9f4928627dc8

Explanations for the batch code:
1) exiftool shows all the ID3 TXXX tags (aka "UserDefinedText") for all mp3 files recursively
2) the Windows command FINDSTR searches for all lines that contain the given string like "musicbrainz album artist id" case-IN-sensitive
3) to get only the tag content (neither "UserDefinedText" nor the tag name in brackets), only the second token after the closing bracket will be outputted
3) to left trim the found content, the first character will be stripped away

Remark:
This batch is only intended for MP3 files and the ID3 TXXX "UserDefinedText" tag.
#9
Hello,

What I would like is to generate dates(*) in UTC (in ISO format) from exif tags for all files in directory without modifying files.

Similarly to a tool like sha1sum I would like to write something like: exiftool (magic options here...) ./directory/* and get output to stdout like so:

2024-01-01T10:10:10  ./directory/file1.jpg
2024-01-01T10:11:10  ./directory/file2.jpg
2024-01-02T10:10:10  ./directory/file3.jpg
...

Output similar to sha1sum but instead of hashes I get dates.

(*) When I say dates what I mean exactly is combination of tags exif:DateTimeOriginal + exif:OffsetTimeOriginal.


What I was able to figure out is:

$ exiftool -d '%Y-%m-%dT%H:%M:%S' -p '${exif:DateTimeOriginal}${exif:OffsetTimeOriginal} ${directory}/${filename}' ./dir/*

This gives me almost what I want.  It gives me the information to stdout in format similar to sha1sum, it gives me dates in ISO format, but it doesn't give me the dates in UTC.

By googling around I can see that there is something like ShiftTime() function that maybe I could use, but unfortunately I cannot make it work.  So I am here for your help.


I am trying to do this:

exiftool -p '${exif:DateTimeOriginal;ShiftTime(${exif:OffsetTimeOriginal}, -1)} ${directory}/${filename}' ./dir/*

And what I get is a warning: Warning: Bareword "OffsetTimeOriginal" not allowed while "strict subs" in use for 'exif:DateTimeOriginal'.  I don't understand that message and I don't know what I am doing wrong, nor how to fix it.

Hope you can help me here.  Thank you in advance.
#10
Newbies / Re: How to extract the content...
Last post by FixEUser - Today at 04:48:34 AM
Thank you for your attempt.

There are at least 3 problems with your script:

1) It doesn't return any other value then without -config join_tags_ID3-config
2) It returns an error "join_tags_ID3.config did not return a true value at Image/ExifTool.pm line 9675."
3) For other readers: The config file needs to be named exactly the same as you use it in the command line:
exiftool -config join_tags_ID3.config -G1 -a -s -UserDefinedText Y:\temp\x\y\test.mp3

@StarGeek: I'm not sure if you understand my initial question:

I asked for a solution to get 1 (one) specific tag content, for example "MusicBrainz Album Artist Id".
The idea would be a (not yet working) syntax like
exiftool -config specificID3.config -G1 -a -s -UserDefinedText("MusicBrainz Album Artist Id") music.mp3and then get only this value:
d87e52c5-bb8d-4da8-b941-9f4928627dc8

How could I achieve that?

PS
Are you sure that
my $table = Image::ExifTool::GetTagTable('Image::ExifTool::Composite');
is the correct way to get the UserDefinedText content?