Page 1 of 1

GPS Extraction Advice

Posted: Wed May 10, 2017 3:27 pm
by diplonics2
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)

Re: GPS Extraction Advice

Posted: Wed May 10, 2017 3:32 pm
by James Heinrich
Are you able to provide a small sample file so I can see what you're seeing?

Re: GPS Extraction Advice

Posted: Wed May 10, 2017 3:37 pm
by diplonics2
Yep, approx 150MB in size.
Generating download link now and shall send on in a moment.

Thanks,

Paul

Re: GPS Extraction Advice

Posted: Wed May 10, 2017 3:51 pm
by diplonics2
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

Re: GPS Extraction Advice

Posted: Wed May 10, 2017 3:59 pm
by James Heinrich
I have grabbed your sample file, thanks. I'll take a look and get back to you.

Re: GPS Extraction Advice

Posted: Wed May 10, 2017 4:16 pm
by diplonics2
Appreciate this, I'll delete the download URL now.
Heading for train now so going offline till tomorrow.

Re: GPS Extraction Advice

Posted: Wed May 10, 2017 6:28 pm
by James Heinrich
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

Re: GPS Extraction Advice

Posted: Wed May 10, 2017 9:08 pm
by James Heinrich
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.

Re: GPS Extraction Advice

Posted: Thu May 11, 2017 9:18 am
by diplonics2
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.

Re: GPS Extraction Advice

Posted: Tue Apr 10, 2018 3:14 pm
by diplonics2
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.

Re: GPS Extraction Advice

Posted: Tue Apr 10, 2018 3:55 pm
by James Heinrich
Can you send a sample file to info@getid3.org and I'll take a look.

Re: GPS Extraction Advice

Posted: Wed Apr 11, 2018 1:48 pm
by James Heinrich
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.

Re: GPS Extraction Advice

Posted: Wed Apr 11, 2018 2:30 pm
by James Heinrich
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.

Re: GPS Extraction Advice

Posted: Thu Apr 12, 2018 1:02 pm
by diplonics2
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.

Re: GPS Extraction Advice

Posted: Mon Jun 04, 2018 11:42 am
by Pratyay Mukherjee
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?

Re: GPS Extraction Advice

Posted: Mon Jun 04, 2018 3:43 pm
by James Heinrich
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
        )
)

Re: GPS Extraction Advice

Posted: Tue Jun 05, 2018 10:55 am
by Pratyay Mukherjee
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!

Re: GPS Extraction Advice

Posted: Tue Jun 05, 2018 11:16 am
by James Heinrich
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.