您的位置:首页 > 其它

Gps与时间信息的提取

2007-12-04 11:09 281 查看
大家在跟踪定位的系统中,可能碰到这样的问题:视频采集之后形成的文件,在此文件中包括 gps数据信息,然后将根据该文件中的gps信息提取出来,然后然后根据这些信息在平面上将轨迹绘制出来,在这里,我主要讲解 mpeg文件的基本格式以及gps信息的提取,关于绘制,将gps信息转换的坐标的问题,读者可以自己查阅相关的资料 。

先看看文件序列头饰如何的定义 ,我也是从大网上搜索到的,不再进行翻译,我想对于任何一个开发者来说这点英文不算什么,你自己翻译。
sequence header

this contains information related to one or more "group-of-pictures"

byte# data details
==================================================================
1-4 sequence header in hex 000001b3
code
12 bits horizontal size in pixels
12 bits vertical size in pixels
4 bits pel aspect ratio see below
18 bits picture rate see below
1 bit marker bit always 1
10 bits vbv buffer size minimum buffer needed to decode this
sequence of pictures; in 16kb units
1 bit constrained
parameter flag
1 bit load intra 0: false; 1: true (matrix follows)
quantizer matrix
64 bytes intra quantizer optional
matrix
1 bit load nonintra 0: false; 1: true (matrix follows)
quantizer matrix
64 bytes nonintra quantizer optional
matrix
- squence extension optional
data
- user data optional application-dependent data
===================================================================

aspect raios are defined by a code which represents the height and
width of the video image.
picture rates are also defined by a code that represents the number
of pictures that may be displayed each second.

each group of pictures has a header that contains one "i picture"
and zero or more b and p pictures. the header is concerned with
the time synchronisation for the first picture in this group, and
the closeness of the previous group to this one.

/*****************************************************************/

for picture rate:
1 = 23.976 frames/sec
2 = 24
3 = 25
4 = 29.97
5 = 30
6 = 50
7 = 59.94
8 = 60

here gives an example. below is hex dump of first 256 bytes of
the first video frame of test.mpg from xingmpeg.

00 00 01 b3 16 00 f0 c4 02 a3 20 a5 10 12 12 14
14 14 16 16 16 16 18 18 19 18 18 1a 1b 1b 1b 1b
1a 1c 1d 1e 1e 1e 1d 1c 1e 1f 20 21 21 20 1f 1e
21 23 23 24 23 23 21 25 26 27 27 26 25 29 2a 2a
2a 29 2d 2d 2d 2d 30 31 30 34 34 38 16 00 f0 c4
00 00 01 b8 00 08 00 00 00 00 01 00 00 0a 72 00
00 00 01 01 13 f9 50 02 bc b2 b8 be 68 8b a4 9f
c5 b5 ca 00 56 76 39 65 f2 30 8b a6 9d 50 69 e7
da fe 13 cf b7 ff 8f f4 ce 7b fa 0e f0 66 ae 1c
5d e7 00 c8 0a 92 b9 29 3c 21 23 f1 d6 40 13 06
f0 10 10 c6 27 80 a0 34 e1 c8 e4 0f 74 91 da c4
03 a0 dc 03 12 60 18 49 27 1d d4 bc 67 0e 54 8c
96 fc 5d c0 06 e0 1a 72 11 7c 9a 8d c9 45 89 6d
cd c4 0b 63 dc 90 18 24 00 ec 84 90 18 10 c9 3b
1e a7 60 3c 9d 74 80 76 05 0b 02 81 a9 29 39 68
53 8f 59 f1 bf 93 fb a0 04 01 bc b0 ce 18 e1 25

sequence header = (hex) 00 00 01 b3
horizontal size = 0x160 = 352
vertical size = 0x0f0 = 240
pel aspect ratio = [i don't know]
picture rate = 4 = 29.97 frames/sec
marker bit = 1

如果你连这个都看不懂,那建议你不用往下看,因为下面连这样的清楚的讲解都没有。当我们知道了文件的头以及连续动画图片的数据信息,(在例子代码中有视频与音频数据的分解提取)。

理解上面的上面头文件格式之后,让我们来实现测定 MPEG Stream Type

关于DWORD_SWAP 与NextStartCode 在源码中都有,你只要明白这么做,具体为什么这么做,似乎也是你自己该研究的问题,或许你有更好的方法,抛砖引玉 (这样你会更深刻的理解与操作mpeg文件)。

int CheckMPEGStreamType (const char * mpgfile)
{
char sDataBuf [32768];
char *sPtr;
DWORD dwLeft, dwPackStartCode;
FILE * fp;
int iRet = -1;
BOOL bMP4 = false;

fp = fopen (mpgfile, "rb");
if (!fp)
return iRet;
dwLeft = fread (sDataBuf, 1, 32768, fp);
fclose (fp);

sPtr = sDataBuf;

if (DWORD_SWAP(*(UNALIGNED DWORD *)sPtr) == 0x000001BA)
{ // MPEG-1 Syatem, MPEG-2 program or MPEG-4 Program
// check if MPEG-1 or MPEG-2(MPEG-4)
if ((*(PBYTE)(sPtr+4) & 0x40) == 0x0) { // MPEG-1 Syatem
iRet = 1;
} else { // Check if MPEG-2 or MPEG-4
bMP4 = false;
while (dwLeft >= 4)
{
NextStartCode ((const BYTE **)&sPtr, &dwLeft);
dwPackStartCode = DWORD_SWAP(*(UNALIGNED DWORD *)sPtr);
if (dwPackStartCode == 0x000001b6) { // MPEG-4 stuff
bMP4 = true;
break;
}
dwLeft --;
sPtr ++;
}
if (bMP4)
iRet = 4;
else
iRet = 2;
}
} else if (*sPtr == 0x47)
{ // MPEG-2 Transport or MPEG-4 Transport
while (dwLeft >= 188 && *(char *)sPtr == 0x47)
{
DWORD dwSubLeft;
char * sSubPtr;
dwSubLeft = dwLeft;
sSubPtr = sPtr;
while (dwSubLeft >= 4) {
NextStartCode ((const BYTE **)&sSubPtr, &dwSubLeft);
dwPackStartCode = DWORD_SWAP(*(UNALIGNED DWORD *)sSubPtr);
if (dwPackStartCode == 0x000001b6) { // MPEG-4 stuff
bMP4 = true;
break;
}
dwSubLeft --;
sSubPtr ++;
}
if (bMP4) {
iRet = 5;
break;
}
dwLeft -= 188;
sPtr += 188;;
}
if (iRet == -1) // Can not find MPEG-4 Stuff, so MPEG-2
iRet = 3;
} else // We ignore ES checking in this code
iRet = -1;

return iRet;
}

检测完文件之后,后面将进行 gps与时间的信息提取。在mpeg文件中,是允许添加用户自定义信息的,这部分信息,你可以写gps、时间、还有其它的相关的你关注的东西,在播放软件中将跳过这些信息,并不影响视频的正常播放(前题,你写进去之后,能确保信息的正确性,就是数据块要自己控制大小,否则在解析的时候会有问题)。
看看一般的厂家都是怎么写这个用户自定义信息的。
Sector description

Offset
Size
Description
0x26
4
0x00, 0x00, 0x01, 0xbf – private stream header
0x2a
2
0x08, 0xe8 – data length
0x2c
2280
User data block
文件开始头为 000001bf 这个是 自定义数据的开始头
然后后面紧跟的两个字 为当前自定义数据的长度
譬如 00 00 01 bf 07 d4 ( 在察看mpeg文件的时候建议使用16进制文件编辑器)
00 00 01 bf 为标志 07 d4 为 当前的自定义的字节长度 包括 开始的标志头

在下面紧跟的就是自定义的数据
第一个字为标示如果是11 那么代表的是 记录的时间
紧跟的一个字是代表的当前块的长度
譬如 下面的
1. Record time block (0x11)
Offset
Size
Description
0x00
1
0x11 – Block ID
0x01
1
0x06 – block length
0x02
1
year, offset from year 2000
0x03
1
month
0x04
1
date
0x05
1
hour
0x06
1
minute
0x07
1
second
譬如下面的时间信息 11 06 07 06 16 0d 2c 22
分表代表的是 0x11 –时间标志
06 当前时间块的长度
07 年份 –基准2000(不同的厂家不一样的设定)
06 月份
16 日期
0d 小时 24进制
2c 分钟
22 秒

不做详细的讲解,你需要到什么自己就分析什么,不需要的就忽略掉。如何的分析? 上面已经有例子。
User data block
Offset
Size
Description
0x00
1
Block ID, from 0x11 to 0xf0
0x01
1
Block size in bytes
0x02
Block size
Block data
2. Record time block (0x11)
Offset
Size
Description
0x00
1
0x11 – Block ID
0x01
1
0x06 – block length
0x02
1
year, offset from year 2000
0x03
1
month
0x04
1
date
0x05
1
hour
0x06
1
minute
0x07
1
second
3. User ID block (0x12)
Offset
Size
Description
0x00
1
0x12 – Block ID
0x01
1
0x40 – block length
0x02
64
User ID
4. Vehicle ID block (0x13)
Offset
Size
Description
0x00
1
0x13 – Block ID
0x01
1
0x40 – block length
0x02
64
Vehicle ID
5. Event information block(0x14)
Offset
Size
Description
0x00
1
0x14 – Block ID
0x01
1
0x44 – block length
0x02
4
Event status
0x06
40
Event information
6. Data block index block
Offset
Size
Description
0x00
1
0x15 – Block ID
0x01
1
N*5 – block length
0x02
1
Block1 type
0x03
4
Block1 offset
0x07
1
Block2 type
0x08
4
Block2 offset



2+N*5
1
Block N type
3+N*5
4
Block N offset
7. Route ID block (0x16)
Offset
Size
Description
0x00
1
0x16 – Block ID
0x01
1
0x40 – block length
0x02
64
Vehicle ID
8. Speed information block (0x17)
Offset
Size
Description
0x00
1
0x17 – Block ID
0x01
1
0x03 – block length
0x02
1
Speed low byte
0x03
1
Speed hi byte
0x04
1
0: KM/H 1: MPH
9. Display Information block (0x18)
Offset
Size
Description
0x00
1
0x18 – Block ID
0x01
1
0x45 – block length
0x02
1
Information ID, 0~31
0x03
2
X position
0x05
2
Y position
0x07
64
Information
10. GPS data information (0x19)
Offset
Size
Description
0x00
1
0x19 – Block ID
0x01
1
0x80 – block length
0x02
1
Information data length
0x03
128
Information
11. Route ID block (0x1a)
Offset
Size
Description
0x00
1
0x1a – Block ID
0x01
1
0x40 – block length
0x02
64
Vehicle ID
下面我们将进行 时间与gps信息的提取

while (curPos<dwLen)
{
DWORD curid = pbData[curPos];
switch (curid)
{
case 0x11: //2. Record time block
{
DWORD curBlock = pbData[curPos+1];
int m_nYear = pbData[curPos+2]+2000;
int m_nMonth = pbData[curPos+3];
int m_nDate = pbData[curPos+4];
int m_nHour = pbData[curPos+5];
int m_nMin = pbData[curPos+6];
int m_nSec = pbData[curPos+7];
curPos += curBlock+2;

CString m_str;
m_str.Format(_T("%d-%d-%d %d:%d:%d/n/t"),m_nYear,m_nMonth,m_nDate,m_nHour,m_nMin,m_nSec);

COleDateTime datB;
datB.ParseDateTime(m_str);
m_str= datB.Format(_T("%Y-%m-%d ")) +datB.Format(_T("%H:%M:%S"));
if(m_strTimeInfo != _T(""))
{
m_strTimeInfo+=_T("$");
}
m_strTimeInfo+=m_str;

}
break;
case 0x12://User ID block
{
DWORD curBlock = pbData[curPos+1];
curPos += curBlock+2;

}
break;
case 0x13://Vehicle ID block
{
DWORD curBlock = pbData[curPos+1];

curPos += curBlock+2;
}
break;
case 0x14://Event information block
{
DWORD curBlock = pbData[curPos+1];

curPos += curBlock+2;
}
break;
case 0x15://Data block index block
{
DWORD curBlock = pbData[curPos+1];
curPos += curBlock*5+2;

}
break;
case 0x16://Route ID block
{
DWORD curBlock = pbData[curPos+1];

curPos += curBlock+2;

}
break;
case 0x17://Speed information block
{
DWORD curBlock = pbData[curPos+1];

curPos += curBlock+2;

}
break;
case 0x18://Display Information block
{
DWORD curBlock = pbData[curPos+1];
curPos += curBlock+2;

}
break;
case 0x19://GPS data information
{
//块长度
DWORD curBlock = pbData[curPos+1];
//数据长度
DWORD m_infolength = pbData[curPos+2];

//应该根据信息的长度来提取GPRMC数据
CString m_stInfo;
BYTE sGpsBuf[32768*2];

CopyMemory (sGpsBuf, pbData+curPos+3, m_infolength);
sGpsBuf[m_infolength+1]='/0';

//将提取的数据转换场字符串准备下一步的分割操作

m_stInfo = sGpsBuf;
m_strGpsInfo +=m_stInfo;

//解析gprms
curPos += curBlock+2+1;

}
break;
case 0x1a://Route ID block
{
DWORD curBlock = pbData[curPos+1];

curPos += curBlock+2;
}
break;
default:
goto enblockid;

break;
}

关于 gps信息的 是按照国际通用的 gprmc格式 ,
格式如下:
$GPRMC,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>,<12>*hh<CR><LF>

<1> UTC时间,hhmmss(时分秒)格式
<2> 定位状态,A=有效定位,V=无效定位
<3> 纬度ddmm.mmmm(度分)格式(前面的0也将被传输)
<4> 纬度半球N(北半球)或S(南半球)
<5> 经度dddmm.mmmm(度分)格式(前面的0也将被传输)
<6> 经度半球E(东经)或W(西经)
<7> 地面速率(000.0~999.9节,前面的0也将被传输)
<8> 地面航向(000.0~359.9度,以真北为参考基准,前面的0也将被传输)
<9> UTC日期,ddmmyy(日月年)格式
<10> 磁偏角(000.0~180.0度,前面的0也将被传输)
<11> 磁偏角方向,E(东)或W(西)
<12> 模式指示(仅NMEA0183 3.00版本输出,A=自主定位,D=差分,E=估算,N=数据无效)

数据提取显示如下:
我们使用$符号来分割时间信息 : "2007-07-03 14:13:46$2007-07-03 14:13:47$2007-07-03 14:13:48$2007-07-03 14:13:49$2007-07-03 14:13:50$2007-07-03 14:13:51$2007-07-03 14:13:52$2007-07-03 14:13:53$2007-07-03 14:13:54$2007-07-03 14:13 ……

同样使用$符号来分割gps 信息
GPRMC,093001.100,A,2232.9689,N,11356.2107,E,8.56,269.56,300306,,,A*6C$ GPRMC,093020.095,A,2232.9674,N,11356.2021,E,9.62,265.79,300306,,,A*62 …

在代码中没有全部作非法的检查,仅仅是为了完成了功能。在使用中引起任何的问题与本人无关 。

如何 使用源码? 很简单,看下面的例子
CParseMpegData m_parse ( m_str );
m_parse . ParseMPEGStream ();
CString m_strTime = m_parse . GetTimeString ();
CString m_strGps = m_parse . GetGpsData ();
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: