GPS Extraction Advice

The place for "I can't figure out how to..." questions.
Post Reply
diplonics2
User
Posts: 7
Joined: Wed May 10, 2017 3:14 pm
Are you a spambot?: no
Location: Maynooth, Ireland

GPS Extraction Advice

Post by diplonics2 » Wed May 10, 2017 3:27 pm

I have .MOV video with embedded GPS data and need to extract it.
getID3 has identified that there is a moov gps atom but I'm not sure if you could recommend the next steps to decode the data element.
In module.audio-video.quicktime.php I can trap the gps atom as a new switch case in the QuicktimeParseAtom() method.
While I've tried all manner of conversion options in the getid3_lib class to convert the gps atom data I still can't get human understandable results so am not sure if you can recommend an approach that might help here.
If it helps understand the gps data format then the python code below might assist as it can decode some of the GPS elements but I don't fully understand the struct unpack elements such that I can do an effective conversion to PHP.
Any advice/help would be much appreciated.
I can also provide the sample video if it helps.

Thanks in advance,

Paul

Code from: https://sergei.nz/files/nvtk_mp42gpx.py

Code: Select all

def get_gps_atom_info(eight_bytes):
    atom_pos,atom_size=struct.unpack('>II',eight_bytes)
    return int(atom_pos),int(atom_size)

def get_gps_atom(gps_atom_info,f):
    atom_pos,atom_size=gps_atom_info
    f.seek(atom_pos)
    data=f.read(atom_size)
    expected_type='free'
    expected_magic='GPS '
    atom_size1,atom_type,magic=struct.unpack_from('>I4s4s',data)
    atom_type=atom_type.decode()
    magic=magic.decode()
    #sanity:
    if atom_size != atom_size1 or atom_type != expected_type or magic != expected_magic:
        print("Error! skipping atom at %x (expected size:%d, actual size:%d, expected type:%s, actual type:%s, expected magic:%s, actual maigc:%s)!" % (int(atom_pos),atom_size,atom_size1,expected_type,atom_type,expected_magic,magic))
        return

    hour,mins,secs,activeReception,q,w,north,g,west,test1,test2,test3,test4= struct.unpack_from('<36xIIIcxxcdc7xdcxxxxfIII',data, 12)
    print(hour,mins,secs,activeReception,q,w,north,g,west,test1,test2,test3,test4)
    #active=active.decode()
    #latitude_b=latitude_b.decode()
    #longitude_b=longitude_b.decode()
    #print(latitude_b)
    #print(longitude_b)
    '''time=fix_time(hour,minute,second,year,month,day)
    latitude=fix_coordinates(latitude_b,latitude)
    longitude=fix_coordinates(longitude_b,longitude)
    speed=fix_speed(speed)

    print(latitude)
    #it seems that A indicate reception
    if active != 'A':
        print("Skipping: lost GPS satelite reception. Time: %s." % time)
        return

    return (latitude,longitude,time,speed)'''

def process_file(in_file):
    global gps_data
    print("Processing file '%s'..." % in_file)
    with open(in_file, "rb") as f:
        offset = 0
        while True:
            atom_pos = f.tell()
            atom_size, atom_type = get_atom_info(f.read(8))
            if atom_size == 0:
                break

            if atom_type == 'moov':
                print("Found moov atom...")
                sub_offset = offset+8

                while sub_offset < (offset + atom_size):
                    sub_atom_pos = f.tell()
                    sub_atom_size, sub_atom_type = get_atom_info(f.read(8))

                    print(sub_atom_type)
                    if str(sub_atom_type) == 'gps ':
                        print("Found gps chunk descriptor atom...")
                        gps_offset = 16 + sub_offset # +16 = skip headers
                        f.seek(gps_offset,0)
                        count = 0
                        while gps_offset < ( sub_offset + sub_atom_size):
                            print("Count is now  " + str(count))
                            count+=1
                            gps_data.append(get_gps_atom(get_gps_atom_info(f.read(8)),f))
                            gps_offset += 8
                            f.seek(gps_offset,0)

                    sub_offset += sub_atom_size
                    f.seek(sub_offset,0)

            offset += atom_size
            f.seek(offset,0)

James Heinrich
getID3() v1 developer
Posts: 1475
Joined: Fri May 04, 2001 4:00 pm
Are you a spambot?: no
Location: Northern Ontario, Canada
Contact:

Re: GPS Extraction Advice

Post by James Heinrich » Wed May 10, 2017 3:32 pm

Are you able to provide a small sample file so I can see what you're seeing?

diplonics2
User
Posts: 7
Joined: Wed May 10, 2017 3:14 pm
Are you a spambot?: no
Location: Maynooth, Ireland

Re: GPS Extraction Advice

Post by diplonics2 » Wed May 10, 2017 3:37 pm

Yep, approx 150MB in size.
Generating download link now and shall send on in a moment.

Thanks,

Paul

diplonics2
User
Posts: 7
Joined: Wed May 10, 2017 3:14 pm
Are you a spambot?: no
Location: Maynooth, Ireland

Re: GPS Extraction Advice

Post by diplonics2 » Wed May 10, 2017 3:51 pm

https://filesender.heanet.ie/1.7/?vid=5 ... 006e0df8d7

Just to note this is coming from my University email which I tried to register my first account with but never got the activation email for, (probably caught in spam!!)
Also attached is a txt file with the JSON output from the python code of the GPS that is in this file.

Paul
Attachments
GPS Data.txt
(34.06 KiB) Downloaded 153 times

James Heinrich
getID3() v1 developer
Posts: 1475
Joined: Fri May 04, 2001 4:00 pm
Are you a spambot?: no
Location: Northern Ontario, Canada
Contact:

Re: GPS Extraction Advice

Post by James Heinrich » Wed May 10, 2017 3:59 pm

I have grabbed your sample file, thanks. I'll take a look and get back to you.

diplonics2
User
Posts: 7
Joined: Wed May 10, 2017 3:14 pm
Are you a spambot?: no
Location: Maynooth, Ireland

Re: GPS Extraction Advice

Post by diplonics2 » Wed May 10, 2017 4:16 pm

Appreciate this, I'll delete the download URL now.
Heading for train now so going offline till tomorrow.

James Heinrich
getID3() v1 developer
Posts: 1475
Joined: Fri May 04, 2001 4:00 pm
Are you a spambot?: no
Location: Northern Ontario, Canada
Contact:

Re: GPS Extraction Advice

Post by James Heinrich » Wed May 10, 2017 6:28 pm

In my research I also came across the sample code you provided, which seems to have been written by Sergei Franco and is available at https://sergei.nz/files/nvtk_mp42gpx.py
Just giving credit where due

James Heinrich
getID3() v1 developer
Posts: 1475
Joined: Fri May 04, 2001 4:00 pm
Are you a spambot?: no
Location: Northern Ontario, Canada
Contact:

Re: GPS Extraction Advice

Post by James Heinrich » Wed May 10, 2017 9:08 pm

I have looked at your file, and made some additions to getID3 at:
https://github.com/JamesHeinrich/getID3 ... e9f4bdd3be

You can now look at the track list in [quicktime][gps_track] which should contain the most interesting bits.

diplonics2
User
Posts: 7
Joined: Wed May 10, 2017 3:14 pm
Are you a spambot?: no
Location: Maynooth, Ireland

Re: GPS Extraction Advice

Post by diplonics2 » Thu May 11, 2017 9:18 am

Okay, that looks brilliant so thanks a lot.
Will pull the code and try it now.
Also, didn't mean for you to do all the work as trying to work through and understand how GPS can be encoded/decoded in Video streams.
This gives me a significant head-start though so will read through your code thoroughly.

Also, as you point out, credit due to Sergei Franco for the original python.
Just for clarity the source for the Python I provided was from another developer who'd changed it to slightly to handle the sample video discussed here.
At the line "hour,mins,secs,activeReception,......." the byte unpack sequence and the byte chunks have been changed but I only discovered the original connection myself yesterday and should have provided crediting as noted.

Thanks again.

diplonics2
User
Posts: 7
Joined: Wed May 10, 2017 3:14 pm
Are you a spambot?: no
Location: Maynooth, Ireland

Re: GPS Extraction Advice

Post by diplonics2 » Tue Apr 10, 2018 3:14 pm

ID3 V1.15 causing GPS handling and fread memory issues, have reverted back to version 1.14.

The GPS Handling was obviously an upgrade change and its new format is fine.
I was reading in the $file_info['quicktime']['gps_track']['gprmc'] string, but this seems to be gone in version 1.15.
Have since found it in $file_info['quicktime']['moov']['subatoms']['gps_entries']['raw']['gprmc'].
Anyway, is there anything else that has changed here that could mess with this change?

The bigger issue is the following error:

Caught exception: cannot fread(10 from 0) that is more than available PHP memory

This message appears only with V1.15, V1.14 handles the same file in same script on same server with no issues but V1.15 crashes.
What is weird is running V1.15 or V1.14 in their own test php file and all works fine:

Code: Select all

require_once ('scripts/getid3/getid3.php');
$page_encoding = 'UTF-8';
$remote_file = 'media/test_file.MOV';
$getID3 = new getID3;
$getID3->setOption(array('encoding' => $page_encoding));
$file_info = $getID3->analyze($remote_file);
getid3_lib::CopyTagsToComments($file_info);
//echo '<pre>'.htmlentities(print_r($file_info['quicktime']['gps_track'], true), ENT_SUBSTITUTE).'</pre>';
if(isset($file_info['quicktime']['gps_track']) && count($file_info['quicktime']['gps_track']) > 1){
	if(isset($file_info['quicktime']['gps_track']['gprmc'])){
		foreach($file_info['quicktime']['gps_track'] as $value){
			echo $value['gprmc'];
		}
	}else{
		foreach($file_info['quicktime']['moov']['subatoms'] as $value){
			if(isset($value['gps_entries'])){
				foreach($value['gps_entries'] as $inner_value){
					echo $inner_value['raw']['gprmc'];
				}
				break;
			}
		}
	}
}else{
	echo print_r($file_info['quicktime'], true);
}
V1.14 is handled in the nested if case while V1.15 in the else case
However, when I put this code into my main script V1.15 fails with the above error message and V1.14 is fine.
The only other work the main script is doing to managing the file coming in as a POSTed upload.
If I can help debug this issue let me know but not sure where to turn next and happy enough to fall back onto V1.14.

James Heinrich
getID3() v1 developer
Posts: 1475
Joined: Fri May 04, 2001 4:00 pm
Are you a spambot?: no
Location: Northern Ontario, Canada
Contact:

Re: GPS Extraction Advice

Post by James Heinrich » Tue Apr 10, 2018 3:55 pm

Can you send a sample file to info@getid3.org and I'll take a look.

James Heinrich
getID3() v1 developer
Posts: 1475
Joined: Fri May 04, 2001 4:00 pm
Are you a spambot?: no
Location: Northern Ontario, Canada
Contact:

Re: GPS Extraction Advice

Post by James Heinrich » Wed Apr 11, 2018 1:48 pm

I got your sample file and it seems to parse no problem. I'm not sure exactly which version you're using (v1.15 isn't a real version number, I assume you mean v1.9.15, but I'm not sure if you're using the the release version (v1.9.15-201709291043) or the current version (v1.9.15-201803161257) or something in between. Based on your quoted error message, which was only added about a month ago, I assume a recent/current version.
If so, can you please modify said error message (getid3.php line 1981) to include the memory limit it doesn't like. I suspect perhaps your PHP configuration has it set to some unexpected value, perhaps -1 ?

Code: Select all

throw new getid3_exception('cannot fread('.$bytes.' from '.$this->ftell().') that is more than available PHP memory ('.$this->getid3->memory_limit.')', 10);
If you memory_limit is indeed -1 then changing line before the above one (line 1980) to this should fix the problem:

Code: Select all

if (($this->getid3->memory_limit > 0) && ($bytes > $this->getid3->memory_limit)) {
Please confirm this fixes your issue and I'll apply the change.

James Heinrich
getID3() v1 developer
Posts: 1475
Joined: Fri May 04, 2001 4:00 pm
Are you a spambot?: no
Location: Northern Ontario, Canada
Contact:

Re: GPS Extraction Advice

Post by James Heinrich » Wed Apr 11, 2018 2:30 pm

I have pushed this change:
https://github.com/JamesHeinrich/getID3 ... 3c42e23b9b

I also added support for the thumbnail built into your sample file in the "frea" atom.

diplonics2
User
Posts: 7
Joined: Wed May 10, 2017 3:14 pm
Are you a spambot?: no
Location: Maynooth, Ireland

Re: GPS Extraction Advice

Post by diplonics2 » Thu Apr 12, 2018 1:02 pm

You're absolutely correct about the lot, the version numbers I had wrong forgetting the 9 element.
The memory limit is the issue as I have the following:

ini_set('memory_limit', '-1');

at the top of the embedded script that V1.9.15 was failing in.
I've just tested the latest version - 1.9.15-201804111020 - and its working fine with handling both the memory and changed 'GPRMC' elements.

Thumbnail support is interesting, will look further into this when I can.

Pratyay Mukherjee
User
Posts: 2
Joined: Mon Jun 04, 2018 8:33 am
Are you a spambot?: no

Re: GPS Extraction Advice

Post by Pratyay Mukherjee » Mon Jun 04, 2018 11:42 am

Since there is only one gps location stored for each time stamp, is there any way we can get to know what is the location of this point in the frame?

James Heinrich
getID3() v1 developer
Posts: 1475
Joined: Fri May 04, 2001 4:00 pm
Are you a spambot?: no
Location: Northern Ontario, Canada
Contact:

Re: GPS Extraction Advice

Post by James Heinrich » Mon Jun 04, 2018 3:43 pm

If present, you can look at the array $info['quicktime']['gps_track'] and walk through each timestamp and know what is where when.
The 'gps_track' array will contain data something like:

Code: Select all

Array (
    [2017-02-14 09:43:24.000] => Array (
            [latitude] => 43.700155
            [longitude] => -7.63319
            [speed_kmh] => 0
            [heading] => 173.41
        )
    [2017-02-14 09:43:25.000] => Array (
            [latitude] => 43.700237
            [longitude] => -7.63373
            [speed_kmh] => 0.01852
            [heading] => 174.23
        )
)

Pratyay Mukherjee
User
Posts: 2
Joined: Mon Jun 04, 2018 8:33 am
Are you a spambot?: no

Re: GPS Extraction Advice

Post by Pratyay Mukherjee » Tue Jun 05, 2018 10:55 am

I am new to this field. But can you tell me whether the gps co-ordinates of each pixel of a particular frame are recorded or not? And another humble request......please can anyone please tell me how to use the above python code to extract the complete data in the gps atom. Thanks again!

James Heinrich
getID3() v1 developer
Posts: 1475
Joined: Fri May 04, 2001 4:00 pm
Are you a spambot?: no
Location: Northern Ontario, Canada
Contact:

Re: GPS Extraction Advice

Post by James Heinrich » Tue Jun 05, 2018 11:16 am

Not sure what you mean by "each pixel of a particular frame" -- all pixels making up any frame will presumably be taken at the same instant and therefore have the same GPS coordinates. And even then, coordinates are not recorded separately for each frame but rather once per second. It's up to you to figure out when a particular frame occurs, and interpolate the closest GPS log entries.

getID3 is PHP, I can't provide any Python help.

Post Reply