-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnvtk_mp42gpx.py
184 lines (155 loc) · 6.56 KB
/
nvtk_mp42gpx.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#!/usr/bin/env python
#
# Author: Sergei Franco (sergei at sergei.nz)
# License: GPL3
# Warranty: NONE! Use at your own risk!
# Disclaimer: I am no programmer!
# Description: this script will crudely extract embedded GPS data from Novatek generated MP4 files.
#
import os, struct, sys, argparse, glob
gps_data = []
gpx = ''
in_file = ''
out_file = ''
force = False
def check_out_file(out_file,force):
if os.path.isfile(out_file) and not force:
print("Error specified out file '%s' exists, specify '-f' to overwrite it!" % out_file)
sys.exit(1)
def check_in_file(in_file):
in_files=[]
for f in in_file:
# glob needed if for some reason quoted glob is passed, or script is run on the most popular proprietary inferior OS
for f1 in glob.glob(f):
if os.path.isdir(f1):
print("Directory '%s' specified as input, listing..." % f1)
for f2 in os.listdir(f1):
f3 = os.path.join(f1,f2)
if os.path.isfile(f3):
print("Queueing file '%s' for processing..." % f3)
in_files.append(f3)
elif os.path.isfile(f1):
print("Queueing file '%s' for processing..." % f1)
in_files.append(f1)
else:
# Catch all for typos...
print("Skipping invalid input '%s'..." % f1)
return in_files
def get_args():
p = argparse.ArgumentParser(description='This script will attempt to extract GPS data from Novatek MP4 file and output it in GPX format')
p.add_argument('-i',metavar='input',nargs='+',help='input file(s), globs (eg: *) or directory(ies)')
p.add_argument('-o',metavar='output',nargs=1,help='output file (single)')
p.add_argument('-f',action='store_true',help='overwrite output file if exists')
args=p.parse_args(sys.argv[1:])
out_file=args.o[0]
check_out_file(out_file,args.f)
in_file=check_in_file(args.i)
return (in_file,out_file)
def fix_time(hour,minute,second,year,month,day):
return "%d-%02d-%02dT%02d:%02d:%02dZ" % ((year+2000),int(month),int(day),int(hour),int(minute),int(second))
def fix_coordinates(hemisphere,coordinate):
# Novatek stores coordinates in odd DDDmm.mmmm format
minutes = coordinate % 100.0
degrees = coordinate - minutes
coordinate = degrees / 100.0 + (minutes / 60.0)
if hemisphere == 'S' or hemisphere == 'W':
return -1*float(coordinate)
else:
return float(coordinate)
def fix_speed(speed):
# 1 knot = 0.514444 m/s
return speed * float(0.514444)
def get_atom_info(eight_bytes):
try:
atom_size,atom_type=struct.unpack('>I4s',eight_bytes)
except struct.error:
return 0,''
return int(atom_size),atom_type.decode()
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,minute,second,year,month,day,active,latitude_b,longitude_b,unknown2,latitude,longitude,speed = struct.unpack_from('<IIIIIIssssfff',data, 16)
active=active.decode()
latitude_b=latitude_b.decode()
longitude_b=longitude_b.decode()
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)
#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 get_gpx(gps_data,out_file):
gpx = '<?xml version="1.0" encoding="UTF-8"?>\n'
gpx += '<gpx version="1.0"\n'
gpx += '\tcreator="Sergei\'s Novatek MP4 GPS parser"\n'
gpx += '\txmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n'
gpx += '\txmlns="http://www.topografix.com/GPX/1/0"\n'
gpx += '\txsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd">\n'
gpx += "\t<name>%s</name>\n" % out_file
gpx += '\t<url>sergei.nz</url>\n'
gpx += "\t<trk><name>%s</name><trkseg>\n" % out_file
for l in gps_data:
if l:
gpx += "\t\t<trkpt lat=\"%f\" lon=\"%f\"><time>%s</time><speed>%f</speed></trkpt>\n" % l
gpx += '\t</trkseg></trk>\n'
gpx += '</gpx>\n'
return gpx
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))
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)
while gps_offset < ( sub_offset + sub_atom_size):
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)
def main():
in_files,out_file=get_args()
for f in in_files:
process_file(f)
gpx=get_gpx(gps_data,out_file)
if gpx:
with open (out_file, "w") as f:
print("Wiriting data to output file '%s'" % out_file)
f.write(gpx)
else:
print("GPS data not found...")
sys.exit(1)
print("Success!")
if __name__ == "__main__":
main()