1539 lines
47 KiB
C++
1539 lines
47 KiB
C++
#include "rm_format_mov.h"
|
|
#if (FILE_FORMAT_MOV)
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
|
|
#if (RM_MODEL_EMT_KR)
|
|
#include <QRegularExpression>
|
|
#include <QDateTime>
|
|
#endif // RM_MODEL_EMT_KR
|
|
|
|
// NMEA PARSER
|
|
#if (RM_MODEL == RM_MODEL_TYPE_ADT_CAPS || \
|
|
RM_MODEL == RM_MODEL_TYPE_BV2000 || \
|
|
RM_MODEL == RM_MODEL_TYPE_KEIYO1 || \
|
|
RM_MODEL == RM_MODEL_TYPE_MBJ5010 || \
|
|
RM_MODEL_EMT_KR ||\
|
|
RM_MODEL == RM_MODEL_TYPE_FC_DR232W)
|
|
#include "rm_sensordata.h"
|
|
#include "fm_parse_gps.h"
|
|
#endif
|
|
|
|
#if !defined(SWAP_4BYTE)
|
|
#define SWAP_4BYTE(num) (((num>>24)&0xff) | ((num<<8)&0xff0000) | ((num>>8)&0xff00) | ((num<<24)&0xff000000))
|
|
#endif
|
|
|
|
#define B2L_LONG(cs) ((((long)cs[0])<<24)|(((long)cs[1])<<16)|(((long)cs[2])<<8)|(((long)cs[3])))
|
|
|
|
#if (RM_MODEL == RM_MODEL_TYPE_TB4000)
|
|
#include <libcrypt_tb.h>
|
|
#include "../rm_app.h"
|
|
#endif // TB4000
|
|
|
|
//long inline rm_byte_to_long(const unsigned char* tag)
|
|
//{
|
|
// return ((((long)tag[0])<<24)|(((long)tag[1])<<16)|(((long)tag[2])<<8)|(((long)tag[3])));
|
|
//}
|
|
|
|
|
|
void *memmem(const void *src, size_t src_len,
|
|
const void * const search, const size_t search_len)
|
|
{
|
|
if (src == NULL) return NULL; // or assert(haystack != NULL);
|
|
if (src_len == 0) return NULL;
|
|
if (search == NULL) return NULL; // or assert(needle != NULL);
|
|
if (search_len == 0) return NULL;
|
|
|
|
for (const char *h = (const char*)src;
|
|
src_len >= search_len;
|
|
++h, --src_len) {
|
|
if (!memcmp(h, search, search_len)) {
|
|
return (void*)h;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//void inline rm_long_to_byte(long value, unsigned char* byte)
|
|
//{
|
|
// byte[0] = ((value >> 24) & 0xFF);
|
|
// byte[1] = ((value >> 16) & 0xFF);
|
|
// byte[2] = ((value >> 8) & 0xFF);
|
|
// byte[3] = ((value) & 0xFF);
|
|
//}
|
|
|
|
// 파일타입: "ftyp"
|
|
// 컨테이너 타입: "moov", "trak", "udta", "tref", "imap", "mdia", "minf", "stbl", "edts", "mdra", "rmra", "imag", "vnrp", "dinf"
|
|
/*
|
|
const long g_containerTypes[] =
|
|
{
|
|
B2L_LONG("moov"),
|
|
B2L_LONG("trak"),
|
|
B2L_LONG("udta"),
|
|
B2L_LONG("tref"),
|
|
B2L_LONG("imap"),
|
|
B2L_LONG("mdia"),
|
|
B2L_LONG("minf"),
|
|
B2L_LONG("stbl"),
|
|
B2L_LONG("edts"),
|
|
B2L_LONG("mdra"),
|
|
B2L_LONG("rmra"),
|
|
B2L_LONG("imag"),
|
|
B2L_LONG("vnrp"),
|
|
B2L_LONG("dinf"),
|
|
//B2L_LONG("stsd"), // -> avc1
|
|
};
|
|
*/
|
|
|
|
|
|
// normal size
|
|
long inline rm_byte_to_long(const unsigned char* tag)
|
|
{
|
|
return ((((long)tag[0])<<24)|(((long)tag[1])<<16)|(((long)tag[2])<<8)|(((long)tag[3])));
|
|
}
|
|
long inline rm_read_number(RMfile file)
|
|
{
|
|
unsigned char buffer[5] = {0,};
|
|
RMfread(buffer,4,1,file);
|
|
return rm_byte_to_long(buffer);
|
|
}
|
|
long inline rm_byte16_to_long(const unsigned char* tag)
|
|
{
|
|
return ((((long)tag[0])<<8)|(((long)tag[1])));
|
|
}
|
|
|
|
// extended size
|
|
long inline rm_byte64_to_long(const unsigned char* tag)
|
|
{
|
|
return (long) ((((int64_t)tag[0])<<56)| \
|
|
(((int64_t)tag[1])<<48)| \
|
|
(((int64_t)tag[2])<<40)| \
|
|
(((int64_t)tag[3])<<32)| \
|
|
(((int64_t)tag[4])<<24)| \
|
|
(((int64_t)tag[5])<<16)| \
|
|
(((int64_t)tag[6])<<8) | \
|
|
(((int64_t)tag[7])<<0));
|
|
}
|
|
|
|
|
|
void inline rm_long_to_byte(long value, unsigned char* byte)
|
|
{
|
|
byte[0] = ((value >> 24) & 0xFF);
|
|
byte[1] = ((value >> 16) & 0xFF);
|
|
byte[2] = ((value >> 8) & 0xFF);
|
|
byte[3] = ((value) & 0xFF);
|
|
}
|
|
|
|
|
|
|
|
const long MOV_HEADER_ATOM = B2L_LONG("mvhd");
|
|
const long MOV_UDAT_ATOM = B2L_LONG("udat");
|
|
|
|
#if (RM_USE_MP4_SUBTITLE)
|
|
// sub title
|
|
const long MOV_MDAT_ATOM = B2L_LONG("mdat");
|
|
#endif
|
|
|
|
typedef struct _MOV_HEADER
|
|
{
|
|
char dump[12]; // version(1) + flags(3) + creation time(4) + modification time(4)
|
|
long time_scale; // 60000
|
|
long duration; // 1823985
|
|
} MOV_HEADER;
|
|
|
|
MOVFormat::MOVFormat(RMfile in,VideoReadMode mode, VideoPreInfo* info)
|
|
{
|
|
_isValid = true;
|
|
_file = in;
|
|
|
|
_readMode = mode;
|
|
_preInfo = info;
|
|
#if (RM_MODEL != RM_MODEL_TYPE_MH9000 && !RM_MODEL_EMT_KR)
|
|
_gps0Count = 0;
|
|
#endif //
|
|
_gsenCount = 0;
|
|
|
|
#if (RM_MODEL == RM_MODEL_TYPE_NX_DRW22)
|
|
_gps0 = 0;
|
|
_zyx = 0;
|
|
#elif (RM_MODEL == RM_MODEL_TYPE_ADT_CAPS || \
|
|
RM_MODEL == RM_MODEL_TYPE_XLDR_88 || \
|
|
RM_MODEL == RM_MODEL_TYPE_BV2000 || \
|
|
RM_MODEL == RM_MODEL_TYPE_KEIYO1 || \
|
|
RM_MODEL == RM_MODEL_TYPE_MBJ5010 || \
|
|
RM_MODEL == RM_MODEL_TYPE_FC_DR232W)
|
|
_nmea = NULL;
|
|
_sens = NULL;
|
|
#elif (RM_MODEL_EMT_KR)
|
|
_nmea = NULL;
|
|
#endif // MODELS
|
|
#if (RM_MODEL == RM_MODEL_TYPE_TB4000)
|
|
_nmea = NULL;
|
|
#endif
|
|
|
|
|
|
if(_preInfo != NULL) {
|
|
_preInfo->bDuration = false;
|
|
_preInfo->duration = 0;
|
|
#if (CHECK_VIDEO_BITRATE)
|
|
_preInfo->movSize = 0;
|
|
_preInfo->bMOVSize = false;
|
|
#endif
|
|
}
|
|
_isValid = false;
|
|
_file = in;
|
|
if(_readMode == VideoReadDuration)
|
|
{
|
|
parse_duration();
|
|
}
|
|
#if (CHECK_VIDEO_BITRATE)
|
|
#if !defined(BBEXTRACT)
|
|
else if (_readMode == AVIReadMOVSize)
|
|
{
|
|
parse_bitrate();
|
|
}
|
|
#endif // BBEXTRACT
|
|
#endif // CHECK_VIDEO_BITRATE
|
|
else if (_readMode == VideoReadSensor)
|
|
{
|
|
parse_sensor();
|
|
}
|
|
}
|
|
bool MOVFormat::duration(RMfile in, VideoPreInfo* info)
|
|
{
|
|
MOVFormat mov = MOVFormat(in,VideoReadDuration,info);
|
|
return mov.isValid();
|
|
}
|
|
|
|
#if (CHECK_VIDEO_BITRATE)
|
|
#if !defined(BBEXTRACT)
|
|
bool MOVFormat::movSize(RMfile in, VideoPreInfo* info)
|
|
{
|
|
MOVFormat mov = MOVFormat(in,AVIReadMOVSize,info);
|
|
return mov.isValid();
|
|
}
|
|
#endif // #if !defined(BBEXTRACT)
|
|
#endif // CHECK_VIDEO_BITRATE
|
|
|
|
MOVFormat::~MOVFormat()
|
|
{
|
|
#if (RM_MODEL == RM_MODEL_TYPE_NX_DRW22)
|
|
if(_gps0 != NULL) {
|
|
free(_gps0);
|
|
}
|
|
if(_zyx != NULL) {
|
|
free(_zyx);
|
|
}
|
|
#elif(RM_MODEL == RM_MODEL_TYPE_ADT_CAPS ||\
|
|
RM_MODEL == RM_MODEL_TYPE_XLDR_88 ||\
|
|
RM_MODEL == RM_MODEL_TYPE_BV2000 || \
|
|
RM_MODEL == RM_MODEL_TYPE_KEIYO1 ||\
|
|
RM_MODEL == RM_MODEL_TYPE_MBJ5010 || \
|
|
RM_MODEL == RM_MODEL_TYPE_FC_DR232W)
|
|
if(_nmea != NULL) {
|
|
free(_nmea);
|
|
_nmea = NULL;
|
|
}
|
|
if(_sens != NULL) {
|
|
free(_sens);
|
|
}
|
|
#elif (RM_MODEL == RM_MODEL_TYPE_TB4000)
|
|
if(_nmea != NULL) {
|
|
free(_nmea);
|
|
_nmea = NULL;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// TAG Parser
|
|
void MOVFormat::parse_all()
|
|
{
|
|
// 탐색 시작
|
|
RMfseek(_file,0,SEEK_END);
|
|
parse(0,RMftell(_file));
|
|
}
|
|
bool MOVFormat::parse_duration()
|
|
{
|
|
if(_preInfo == NULL) {
|
|
return false;
|
|
}
|
|
|
|
init_parser();
|
|
set_tags("moov","mvhd",NULL);
|
|
parse_all();
|
|
|
|
if(_stop_parse == false)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
long mvhd_offset = _tag_offset_list[1];
|
|
RMfseek(_file,mvhd_offset+8,SEEK_SET);
|
|
//long mvhd_size = _tag_size_list[1];
|
|
|
|
MOV_HEADER header = {0,};
|
|
RMfread(&header,sizeof(_MOV_HEADER),1,_file);
|
|
_preInfo->duration = (unsigned int)((double)(SWAP_4BYTE(header.duration)) * 1000 / (double)SWAP_4BYTE(header.time_scale));
|
|
_preInfo->bDuration = true;
|
|
_isValid = true;
|
|
return true;
|
|
}
|
|
#if !defined(BBEXTRACT)
|
|
void MOVFormat::parse_avc1(long offset, long size)
|
|
{
|
|
Q_UNUSED(size)
|
|
|
|
// AVC Decoder Configuration Record
|
|
RMfseek(_file,offset+32,SEEK_SET);
|
|
unsigned char width_height_buffer[5] = {0,};
|
|
RMfread(width_height_buffer,4,1,_file);
|
|
|
|
_preInfo->width = (int)rm_byte16_to_long(&width_height_buffer[0]);
|
|
_preInfo->height = (int)rm_byte16_to_long(&width_height_buffer[2]);
|
|
|
|
// qInfo() << "WH:" << _preInfo->width << _preInfo->height;
|
|
}
|
|
#if (CHECK_VIDEO_BITRATE)
|
|
bool MOVFormat::parse_bitrate()
|
|
{
|
|
//RMfseek(_file,0,SEEK_END);
|
|
if(_preInfo == NULL) {
|
|
return false;
|
|
}
|
|
|
|
if(_preInfo->bDuration == false)
|
|
{
|
|
parse_duration();
|
|
}
|
|
|
|
init_parser();
|
|
// 1. moov->trak->mdia->minf->stbl->stsd->avc1 순서대로 탐색
|
|
// https://developer.apple.com/library/content/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-25691
|
|
set_tags("moov","trak","mdia","minf","stbl","stsd","avc1",NULL);
|
|
|
|
// 탐색 시작
|
|
parse_all();
|
|
|
|
if(_stop_parse == false)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// AVC1 TAG (MODEL: CS_92WQH)
|
|
long avc1_offset = _tag_offset_list[_tag_count-1];
|
|
long avc1_size = _tag_size_list[_tag_count-1];
|
|
|
|
// AVC1 확인 (width/height)
|
|
if(avc1_offset != 0 &&
|
|
avc1_size > 8)
|
|
{
|
|
parse_avc1(avc1_offset,avc1_size);
|
|
}
|
|
|
|
long stbl_offset = _tag_offset_list[4];
|
|
long stbl_size = _tag_size_list[4];
|
|
|
|
// 'stco' Chunk Offsets Box - Sample To Chunk Box
|
|
init_parser();
|
|
|
|
set_tags("stbl","stsz",NULL);
|
|
|
|
parse(stbl_offset,stbl_size,0);
|
|
if(_stop_parse == false)
|
|
{
|
|
qInfo() << "stbl stsz search error!" << __FUNCTION__;
|
|
return false;
|
|
}
|
|
|
|
long stsz_offset = _tag_offset_list[1];
|
|
//long stsz_size = _tag_size_list[1];
|
|
// size + type + version:1 + flags:3 + sample size:4 + number of entry: 4 + ....
|
|
|
|
RMfseek(_file,stsz_offset+8+4+4,SEEK_SET);
|
|
unsigned char long_buffer[5] = {0,};
|
|
RMfread(long_buffer,4,1,_file);
|
|
|
|
long numFrames = rm_byte_to_long(long_buffer);
|
|
if(numFrames == 0) {
|
|
|
|
_preInfo->bMOVSize = false;
|
|
_preInfo->movSize = 0;
|
|
return false;
|
|
}
|
|
|
|
unsigned char* size_buffer = (unsigned char*)malloc(numFrames * 4);
|
|
RMfread(size_buffer,4,numFrames,_file);
|
|
|
|
long totalSize = 0;
|
|
for(int i=0;i<numFrames;i++)
|
|
{
|
|
totalSize += rm_byte_to_long(&size_buffer[i*4]);
|
|
}
|
|
free(size_buffer);
|
|
|
|
_preInfo->bMOVSize = true;
|
|
_preInfo->movSize = (unsigned int)totalSize;
|
|
return true;
|
|
}
|
|
#endif // #if (CHECK_VIDEO_BITRATE)
|
|
#endif // #if !defined(BBEXTRACT)
|
|
void MOVFormat::parse(unsigned int offset, unsigned int length, int depth)
|
|
{
|
|
if(_stop_parse == true || _tag_count <= depth)
|
|
{
|
|
return;
|
|
}
|
|
|
|
unsigned int off = offset;
|
|
unsigned int stop_at = offset + length;
|
|
while (off < stop_at)
|
|
{
|
|
if(_stop_parse == true)
|
|
{
|
|
return;
|
|
}
|
|
|
|
RMfseek(_file,off,SEEK_SET);
|
|
|
|
// 1. ATOM 크기 확인 (unsigned 를 사용해야함)
|
|
unsigned char atom_size_buffer[5] = {0,};
|
|
unsigned char atom_type_buffer[5] = {0,};
|
|
|
|
RMfread(atom_size_buffer,4,1,_file);
|
|
long atom_size = rm_byte_to_long(atom_size_buffer);
|
|
|
|
// apple 문서상 atom 의 최소 크기는 8임
|
|
// eg. model 92 ..mp4 - mdat tag
|
|
// extended size field
|
|
// 1, which means that the actual size is given in the extended size field,
|
|
// an optional 64-bit field that follows the type field.
|
|
// This accommodates media data atoms that contain more than 2^32 bytes.
|
|
// type 뒤에 64 비트로 크기 지정 (extended size field)
|
|
bool extendedSize = false;
|
|
if(atom_size == 1)
|
|
{
|
|
extendedSize = true;
|
|
}
|
|
|
|
// 0, which is allowed only for a top-level atom,
|
|
// designates the last atom in the file and indicates
|
|
// that the atom extends to the end of the file.
|
|
if(atom_size == 0)
|
|
{
|
|
RMfread(atom_type_buffer,4,1,_file);
|
|
//#if !defined(BBEXTRACT)
|
|
// qInfo() << "ZERO ATOM:" << (char*) atom_type_buffer;
|
|
//#endif
|
|
return;
|
|
}
|
|
|
|
if (RMftell(_file) == stop_at)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// 2. ATOM 타입 확인
|
|
RMfread(atom_type_buffer,4,1,_file);
|
|
|
|
long atom_type = rm_byte_to_long(atom_type_buffer);
|
|
|
|
if(extendedSize) {
|
|
unsigned char atom_extended_size_buffer[9] = {0,};
|
|
RMfread(atom_extended_size_buffer,8,1,_file);
|
|
atom_size = rm_byte64_to_long(atom_extended_size_buffer);
|
|
}
|
|
|
|
long add_offset = 0;
|
|
|
|
// "stsd" 의 경우 뒤에 추가 8 BYTE 존재 (Version:1,Flags:3,Number of entries:4)
|
|
if(atom_type == rm_byte_to_long((unsigned char*)"stsd"))
|
|
{
|
|
add_offset = 8;
|
|
}
|
|
|
|
// qInfo() << "###" << (char*)atom_type_buffer;
|
|
|
|
// 3. 지정된 ATOM 일 경우 내부탐색 (recursive)
|
|
if (atom_type == _tag_list[depth])
|
|
{
|
|
// if(_readMode == VideoReadSensor)
|
|
// {
|
|
// qInfo() << (char*)atom_type_buffer << "OFFSET:" << off << "SIZE:" << atom_size;
|
|
// }
|
|
|
|
//#if(DEBUG_MOV_STBL)
|
|
//qInfo() << "tag:" << (char*)atom_type_buffer << " depth:" << depth << " offset:" << off;
|
|
//#endif
|
|
|
|
// 각 tag 의 offset, size 저장
|
|
_tag_offset_list[depth] = off;
|
|
_tag_size_list[depth] = atom_size;
|
|
#if (RM_MODEL == RM_MODEL_TYPE_TB4000)
|
|
if((depth+1) == _tag_count && check_tag_value(atom_type,off,atom_size))
|
|
#else
|
|
if((depth+1) == _tag_count && check_tag_value(atom_type,offset,atom_size))
|
|
#endif
|
|
{
|
|
_stop_parse = true;
|
|
return;
|
|
}
|
|
|
|
// 하위 tag 탐색
|
|
parse(RMftell(_file)+add_offset,atom_size,depth+1);
|
|
}
|
|
off += atom_size;
|
|
}
|
|
}
|
|
bool MOVFormat::check_tag_value(long atom_type, unsigned int offset, long size)
|
|
{
|
|
if(strlen(_tag_search_value) == 0)
|
|
{
|
|
return true;
|
|
}
|
|
#if (RM_MODEL == RM_MODEL_TYPE_TB4000 || RM_MODEL == RM_MODEL_TYPE_MH9000)
|
|
// hdlr 타입 확인
|
|
if(atom_type == rm_byte_to_long((unsigned char*)"hdlr"))
|
|
{
|
|
const long data_offset = 8;
|
|
fseek(_file,offset+data_offset,SEEK_SET);
|
|
|
|
unsigned char tag_value[RM_MOV_MAX_TAG_VALUE_SIZE] = {0,};
|
|
|
|
#if (RM_MODEL == RM_MODEL_TYPE_MH9000)
|
|
const long read_size = RM_MOV_MAX_TAG_VALUE_SIZE;
|
|
fread(tag_value,read_size,1,_file);
|
|
void* found = memmem(tag_value,RM_MOV_MAX_TAG_VALUE_SIZE,_tag_search_value,strlen(_tag_search_value));
|
|
#else // RM_MODEL_TYPE_TB4000
|
|
const long read_size = MIN(RM_MOV_MAX_TAG_VALUE_SIZE,size-data_offset);
|
|
fread(tag_value,read_size,1,_file);
|
|
void* found = memmem(tag_value,size-data_offset,_tag_search_value,strlen(_tag_search_value));
|
|
#endif // RM_MODEL_TYPE_TB4000
|
|
return (found != NULL);
|
|
}
|
|
#else //
|
|
// STSD 타입 확인 (video, audio, subtitle)
|
|
if(atom_type == rm_byte_to_long((unsigned char*)"stsd"))
|
|
{
|
|
// https://developer.apple.com/library/content/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-61112
|
|
// 사양과는 다른것 같음
|
|
// size(4) + type(4) + version(3) + flag(1) + entry count(4) + Sample description size (4)
|
|
const long data_offset = 20;
|
|
|
|
fseek(_file,offset + data_offset,SEEK_SET);
|
|
|
|
unsigned char tag_value[RM_MOV_MAX_TAG_VALUE_SIZE] = {0,};
|
|
|
|
long read_size = MIN(RM_MOV_MAX_TAG_VALUE_SIZE,size-data_offset);
|
|
fread(tag_value,read_size,1,_file);
|
|
|
|
return (memcmp(_tag_search_value,tag_value,strlen(_tag_search_value)) == 0);
|
|
}
|
|
#endif // SStarMeta
|
|
return false;
|
|
}
|
|
|
|
void MOVFormat::set_tags(const char* tag,...)
|
|
{
|
|
init_parser();
|
|
va_list args;
|
|
|
|
// initialize valist for num number of arguments
|
|
va_start(args, tag);
|
|
|
|
const char* each_tag = tag;
|
|
int index = 0;
|
|
while (each_tag != NULL)
|
|
{
|
|
// qInfo() << each_tag;
|
|
_tag_list[index] = rm_byte_to_long((unsigned char*)each_tag);
|
|
index++;
|
|
|
|
each_tag = va_arg(args, const char*);
|
|
}
|
|
_tag_count = index;
|
|
|
|
// clean memory reserved for valist
|
|
va_end(args);
|
|
|
|
}
|
|
|
|
#if (RM_MODEL == 1) // NEXTEC
|
|
bool MOVFormat::parse_sensor()
|
|
{
|
|
init_parser();
|
|
|
|
// 1. gseo
|
|
// user 데이터 확인 완료
|
|
set_tags("gseo",NULL);
|
|
// 탐색 시작
|
|
parse_all();
|
|
if(_stop_parse == false)
|
|
{
|
|
return false;
|
|
}
|
|
long gseo_offset = _tag_offset_list[0];
|
|
long gseo_size = _tag_size_list[0];
|
|
_gsenCount = (gseo_size - 8) / 6;
|
|
if(_gsenCount > 0)
|
|
{
|
|
//qInfo() << __FUNCTION__ << QString().sprintf("GSEO OFFSET:%X",gseo_offset+15);
|
|
RMfseek(_file,gseo_offset+15,SEEK_SET);
|
|
_zyx = (int16_t*)malloc(6 * _gsenCount );
|
|
RMfread(_zyx,6,_gsenCount,_file);
|
|
}
|
|
// for(int i=0;i<_gsenCount;i++) {
|
|
// qInfo() << "X" << _zyx[i*3+2] << "Y" << _zyx[i*3+1] << "Z" << _zyx[i*3+0];
|
|
// }
|
|
|
|
// 1682
|
|
|
|
// 1. gpsa
|
|
// user 데이터 확인 완료
|
|
set_tags("gpsa",NULL);
|
|
// 탐색 시작
|
|
parse_all();
|
|
if(_stop_parse == false)
|
|
{
|
|
return false;
|
|
}
|
|
long gpsa_offset = _tag_offset_list[0];
|
|
long gpsa_size = _tag_size_list[0];
|
|
|
|
//qInfo() << gpsa_offset << gpsa_size;
|
|
RMfseek(_file,gpsa_offset+gpsa_size,SEEK_SET);
|
|
unsigned char csize[5] = {0,};
|
|
RMfread(csize,4,1,_file);
|
|
_gps0Count = (B2L_LONG(csize) - 8) / 32;
|
|
if(_gps0Count > 0)
|
|
{
|
|
unsigned char gps0[5] = {0,};
|
|
RMfread(gps0,4,1,_file);
|
|
if(B2L_LONG(gps0) != B2L_LONG("gps0"))
|
|
{
|
|
return false;
|
|
}
|
|
_gps0 = (GPS0*)malloc(sizeof(_GPS0) * _gps0Count );
|
|
RMfread(_gps0,32,_gps0Count,_file);
|
|
//qInfo() << "test:" << gpsCount << _gps0[gpsCount-1].lon;
|
|
}
|
|
// for(int i=0;i<_gps0Count;i++) {
|
|
// qInfo() << "LAT" << _gps0[i].lat << "LON:" << _gps0[i].lon;
|
|
// }
|
|
return true;
|
|
}
|
|
#elif (RM_MODEL == RM_MODEL_TYPE_BV2000 ||\
|
|
RM_MODEL == RM_MODEL_TYPE_KEIYO1 ||\
|
|
RM_MODEL == RM_MODEL_TYPE_MBJ5010 ||\
|
|
RM_MODEL == RM_MODEL_TYPE_FC_DR232W)
|
|
void MOVFormat::process_subtitle(const char* subtitle)
|
|
{
|
|
//qInfo() << subtitle;
|
|
|
|
NMEA_INFO* nmea = (NMEA_INFO*)_nmea;
|
|
NMEA_INFO* nmea_cur = &nmea[_gps0Count];
|
|
float* sensor = (float*)_sens;
|
|
float* sensor_cur = &sensor[_gsenCount * 3];
|
|
QString s = QString(subtitle);
|
|
QStringList ls = s.split(",");
|
|
if(_sensorFPS <= 0) {
|
|
_sensorFPS = ls.size() - 1; // 벤츠2
|
|
//qInfo() << "SENSOR FPS:" << _sensorFPS << __FUNCTION__;
|
|
}
|
|
|
|
//if(is10FPS == false && ls.size() != ((SENSOR_INTERVAL * SENSOR_FPS) + 1))
|
|
if(ls.size() != 41 && ls.size() != 11)
|
|
{
|
|
//qInfo() << _sensorFPS;
|
|
//qInfo() << "!!!!!!!!!!!!!!!:" << s << ls.size() << "!=41,11" << LOG_FL;
|
|
return;
|
|
}
|
|
for(int i=0;i<(_sensorFPS+1);i++)
|
|
{
|
|
QString line = ls.at(i);
|
|
|
|
QStringList is = line.split("/");
|
|
if(is.size() != 8) {
|
|
// 00:02:04 시간값
|
|
//qInfo() << "ERROR:" << is.size() << "!=8" << line << LOG_FL;
|
|
continue;
|
|
}
|
|
|
|
// GPS
|
|
if(i == 0 || i == _sensorFPS)
|
|
{
|
|
//qInfo() << _gps0Count << is;
|
|
nmea_cur->Longitude = is.at(2).toFloat();
|
|
nmea_cur->Latitude = is.at(3).toFloat();
|
|
nmea_cur->nStatus = is.at(1) == "1" ? 1 : 0;
|
|
nmea_cur->Speed = is.at(4).toInt();
|
|
nmea_cur++;
|
|
_gps0Count++;
|
|
}
|
|
// SENSOR
|
|
bool bs = false;
|
|
|
|
// float x = (float)((int8_t)(is.at(6).toUInt(&bs,16)) << 24 >> 24);
|
|
// float y = (float)((int8_t)(is.at(7).toUInt(&bs,16)) << 24 >> 24);
|
|
// float z = (float)((int8_t)(is.at(5).toUInt(&bs,16)) << 24 >> 24);
|
|
|
|
//uint nHex = sValue.toUInt(&bStatus,16);
|
|
float x = ((float)(is.at(6).toInt(&bs,16)));// Y->X
|
|
float y = ((float)(is.at(7).toInt(&bs,16)));// Z->Y
|
|
float z = ((float)(is.at(5).toInt(&bs,16)));// X->Z
|
|
|
|
#if (RM_MODEL == RM_MODEL_TYPE_MBJ5010 || RM_MODEL == RM_MODEL_TYPE_KEIYO1 || RM_MODEL == RM_MODEL_TYPE_FC_DR232W)
|
|
if(_sensorFPS < 40) {
|
|
// 요청은 X<->Y 교체였으나 실제로는 ZX 교체
|
|
// 상하(녹색):Y, 전후(노랑):Z, 좌우(빨강):X
|
|
//qInfo() << "XY SWAP:" << __FUNCTION__ << __LINE__;
|
|
float temp = x;
|
|
x = z;
|
|
z = temp;
|
|
}
|
|
#endif
|
|
|
|
if(x > 0x1F)
|
|
{
|
|
x -= 0x100;
|
|
}
|
|
if(y > 0x1F)
|
|
{
|
|
y -= 0x100;
|
|
}
|
|
if(z > 0x1F)
|
|
{
|
|
z -= 0x100;
|
|
}
|
|
|
|
sensor_cur[(i*3)+0] = x / 16.0f; // X
|
|
sensor_cur[(i*3)+1] = y / 16.0f; // Y
|
|
sensor_cur[(i*3)+2] = z / 16.0f; // Z
|
|
//qInfo() <<is.at(6) << is.at(7) << is.at(5) << "," << x << "," << y << "," << z << "," << sensor_cur[(i*3)+0] << "," << sensor_cur[(i*3)+1] << "," << sensor_cur[(i*3)+2];
|
|
//qInfo() << is.at(5) << sensor_cur[(i*3)+0]<< is.at(6) << sensor_cur[(i*3)+1] << is.at(7) << sensor_cur[(i*3)+2];
|
|
_gsenCount++;
|
|
|
|
}
|
|
//qInfo() << s;
|
|
|
|
}
|
|
|
|
bool MOVFormat::parse_sensor()
|
|
{
|
|
_gps0Count = 0;
|
|
_gsenCount = 0;
|
|
_sensorFPS = -1;
|
|
|
|
init_parser();
|
|
set_tags("moov","trak","mdia","minf","stbl","stsd",NULL);
|
|
memcpy(_tag_search_value,"tx3g",strlen("tx3g")); // 확인필요
|
|
|
|
parse_all();
|
|
if(_stop_parse == false)
|
|
{
|
|
return false;
|
|
}
|
|
#if (DEBUG_SENSOR_LOG)
|
|
FILE* flog = fopen("c:\\home\\temp\\video_log.txt","w+t");
|
|
#endif
|
|
|
|
// 2. 자막 trak 탐색 종료 후 탐색된 moov->trak->mdia->stbl을 확인하여, size 와 offset 이 0 이 아닌 경우
|
|
unsigned int stbl_offset = _tag_offset_list[4];
|
|
long stbl_size = _tag_size_list[4];
|
|
if(stbl_offset != 0 &&
|
|
stbl_size != 0)
|
|
{
|
|
// 3. 다시 parser 를 초기화 하고 stsz (각 자막별 크기) 확인
|
|
// https://developer.apple.com/library/content/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-25715
|
|
set_tags("stbl","stsz",NULL);
|
|
|
|
// stbl 부터 탐색 시작
|
|
parse(stbl_offset,stbl_size);
|
|
|
|
// 탐색된 stsz 에 subtitle의 size 들이 포함되어 있음
|
|
unsigned int stsz_offset = _tag_offset_list[1];
|
|
long stsz_size = _tag_size_list[1];
|
|
|
|
// 사이즈가 존재할 경우
|
|
if(stsz_offset != 0 &&
|
|
stsz_size != 0)
|
|
{
|
|
// 자막 개수 확인
|
|
long count = 0;
|
|
long sample_size = 0;
|
|
|
|
// 자막별 크기 (전체 동일한 경우과 개별 크기가 별도로 저장된 경우가 있음)
|
|
unsigned char *sample_size_list = NULL;
|
|
|
|
// 자막별 옵셋 (모두 별도로 저장되어 있음)
|
|
unsigned char *sample_offset_list = NULL;
|
|
|
|
// size(4) + type(4) + version/flag(4) + Sample size (모든 샘플 크기가 동일할 경우) + Number of entry(4)
|
|
|
|
// 개수를 확인
|
|
RMfseek(_file,stsz_offset+12,SEEK_SET);
|
|
sample_size = rm_read_number(_file);
|
|
count = rm_read_number(_file);
|
|
|
|
// 0 개 이상일 경우 또는 sample size 가 0 보다 클 경우
|
|
if(count > 0 || sample_size > 0)
|
|
{
|
|
int maxCount = (count + 1);
|
|
// 2초 간격으로 2초씩 저장됨
|
|
_nmea = malloc(sizeof(_NMEA_INFO) * maxCount);
|
|
_sens = malloc((sizeof(float) * 3) * maxCount * MAX_SENSOR_FPS);
|
|
|
|
// 자막 레코드 별로 사이즈 별도 처리 하는 경우
|
|
if(sample_size == 0)
|
|
{
|
|
// 각 자막 크기 저장 공간 할당 + 읽어 오기
|
|
sample_size_list = (unsigned char*)malloc(4 * count);
|
|
RMfread(sample_size_list,4 * count,1,_file);
|
|
}
|
|
|
|
// 다시 (moov->trak->mdia->)stbl->stco() 를 확인하여 각 자막별 옵셋 확인
|
|
// https://developer.apple.com/library/content/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-25715
|
|
set_tags("stbl","stco",NULL);
|
|
|
|
// stbl 부터 탐색 시작
|
|
parse(stbl_offset,stbl_size);
|
|
|
|
// stco 에 subtitle의 offset 들이 포함되어 있음
|
|
unsigned int stco_offset = _tag_offset_list[1];
|
|
long stco_size = _tag_size_list[1];
|
|
if(stco_offset != 0 &&
|
|
stco_size != 0)
|
|
{
|
|
// size(4) + type(4) + version/flag(4) + Entry Number + ....
|
|
RMfseek(_file,stco_offset+12,SEEK_SET);
|
|
|
|
|
|
// 개수 확인
|
|
long offset_count = rm_read_number(_file);
|
|
|
|
// 개별 사이즈가 있는 경우 최소 개수 확인
|
|
if(sample_size == 0)
|
|
{
|
|
count = MIN(count,offset_count);
|
|
}
|
|
// 없는 경우 offset 개수가 자막 개수
|
|
else
|
|
{
|
|
count = offset_count;
|
|
}
|
|
// 옵셋 정보 할당 + 읽어오기
|
|
sample_offset_list = (unsigned char*)malloc(4 * count);
|
|
RMfread(sample_offset_list,4 * count,1,_file);
|
|
|
|
// 옵셋 + 크기로 자막 읽기
|
|
for(int i=0;i<count;i++)
|
|
{
|
|
long each_offset = rm_byte_to_long(&sample_offset_list[i*4]);
|
|
long each_size = sample_size == 0 ? rm_byte_to_long(&sample_size_list[i*4]) : sample_size;
|
|
|
|
// 2초 간격으로 저장되어 중간에 2 있음
|
|
// 이것도 엉터리임
|
|
// if(each_size <= 2) {
|
|
// continue;
|
|
// }
|
|
// KEIYO 버퍼크기 엉터리임..
|
|
each_size = qMax((long)2048,each_size);
|
|
|
|
//qInfo() << QString().sprintf("%X",each_offset);
|
|
|
|
char* subtitle = (char*)malloc(each_size + 1);
|
|
memset(subtitle,0,each_size + 1);
|
|
|
|
// offset 앞에 2byte 크기가 있음..
|
|
RMfseek(_file,each_offset+2,SEEK_SET);
|
|
RMfread(subtitle,each_size-2,1,_file);
|
|
//qInfo() << subtitle;
|
|
//subtitles.push_back(subtitle);
|
|
|
|
#if (DEBUG_SENSOR_LOG)
|
|
fprintf(flog,"%08X:%08X:\r\n",each_offset+2,(each_offset+2) + each_size-2);
|
|
fprintf(flog,subtitle);
|
|
fprintf(flog,"\r\n");
|
|
#endif
|
|
|
|
process_subtitle(subtitle);
|
|
}
|
|
free(sample_offset_list);
|
|
}
|
|
|
|
if(sample_size_list != NULL)
|
|
{
|
|
free(sample_size_list);
|
|
sample_size_list = NULL;
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
#if (DEBUG_SENSOR_LOG)
|
|
fclose(flog);
|
|
#endif
|
|
return true;
|
|
}
|
|
#elif (RM_MODEL == RM_MODEL_TYPE_ADT_CAPS && !SUB_MODEL_CARROT_EMT)
|
|
bool MOVFormat::parse_sensor()
|
|
{
|
|
init_parser();
|
|
|
|
set_tags("udat",NULL);
|
|
// 탐색 시작
|
|
parse_all();
|
|
if(_stop_parse == false)
|
|
{
|
|
return false;
|
|
}
|
|
long udat_offset = _tag_offset_list[0];
|
|
|
|
// GPSR BOX SIZE 를 확인하여 GPS 읽어 오기
|
|
RMfseek(_file,udat_offset+8,SEEK_SET);
|
|
unsigned char gpsr_size_char[5] = {0,};
|
|
|
|
RMfread(&gpsr_size_char,4,1,_file);
|
|
uint32_t gpsr_size = B2L_LONG(gpsr_size_char);
|
|
|
|
//qInfo() << "GPSR_SIZE" << gpsr_size << __FUNCTION__;
|
|
|
|
RMfseek(_file,udat_offset+12,SEEK_SET);
|
|
unsigned char gpsr[5] = {0,};
|
|
RMfread(gpsr,4,1,_file);
|
|
if(B2L_LONG(gpsr) != B2L_LONG("GPSR"))
|
|
{
|
|
qInfo() << "NO GPS" << __FUNCTION__;
|
|
}
|
|
RMfseek(_file,udat_offset+16,SEEK_SET);
|
|
uint32_t doffset = 14;
|
|
_gps0Count = 0;
|
|
|
|
int max_gps_count = gpsr_size / 30;
|
|
|
|
NMEA_INFO* nmea = (NMEA_INFO*)malloc(sizeof(_NMEA_INFO) * max_gps_count);
|
|
while(doffset < gpsr_size) {
|
|
|
|
// 프레임 NO ??
|
|
uint32_t frame;
|
|
RMfread(&frame,4,1,_file);
|
|
|
|
// DATA SIZE 읽어 오기
|
|
uint32_t dsize = 0;
|
|
RMfread(&dsize,4,1,_file);
|
|
|
|
// ??? 영역으로 이동시 탈출
|
|
if(dsize > 256) {
|
|
break;
|
|
}
|
|
|
|
char rmc[1024] = {0,};
|
|
RMfread(&rmc,dsize,1,_file);
|
|
|
|
if(FMParseGPS::ParseRMC(rmc,&nmea[_gps0Count],strlen(rmc))) {
|
|
_gps0Count += 1;
|
|
}
|
|
doffset += (8 + dsize);
|
|
}
|
|
|
|
if(_gps0Count > 0) {
|
|
_nmea = malloc(sizeof(_NMEA_INFO) * _gps0Count);
|
|
memcpy(_nmea,nmea,sizeof(_NMEA_INFO)*_gps0Count);
|
|
}
|
|
free(nmea);
|
|
|
|
//qInfo() << QString().sprintf("%X",RMftell(_file));
|
|
|
|
unsigned char gsen_size_char[5] = {0,};
|
|
|
|
RMfread(&gsen_size_char,4,1,_file);
|
|
uint32_t gsen_size = B2L_LONG(gsen_size_char);
|
|
|
|
_gsenCount = gsen_size / 24;
|
|
//qInfo() << QString().sprintf("%d (%d)",gsen_size,_gsenCount);
|
|
|
|
unsigned char sens[5] = {0,};
|
|
RMfread(sens,4,1,_file);
|
|
if(B2L_LONG(sens) != B2L_LONG("SENS"))
|
|
{
|
|
qInfo() << "NO SENSOR" << __FUNCTION__;
|
|
}
|
|
|
|
_sens = (SEN*)malloc(sizeof(_SEN) * _gsenCount);
|
|
RMfread(_sens,sizeof(_SEN),_gsenCount,_file);
|
|
return true;
|
|
}
|
|
#elif (RM_MODEL == RM_MODEL_TYPE_XLDR_88)
|
|
/*
|
|
bool MOVFormat::parse_sensor()
|
|
{
|
|
init_parser();
|
|
|
|
// 1. udat
|
|
// user 데이터 확인 완료
|
|
set_tags("udat",NULL);
|
|
// 탐색 시작
|
|
parse_all();
|
|
if(_stop_parse == false)
|
|
{
|
|
return false;
|
|
}
|
|
unsigned int udat_offset = _tag_offset_list[0];
|
|
long udat_size = _tag_size_list[0];
|
|
if(udat_offset != 0 && udat_size != 0)
|
|
{
|
|
RMfseek(_file,udat_offset+8,SEEK_SET);
|
|
uint8_t *buffer = (uint8_t*)malloc(udat_size-8);
|
|
RMfread(buffer,udat_size-8,1,_file);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
*/
|
|
#elif (RM_MODEL == RM_MODEL_TYPE_TB4000)
|
|
bool MOVFormat::parse_sensor()
|
|
{
|
|
init_parser();
|
|
|
|
/* TRACK 중 hdrl 의 내용에 SStarMeta 가 존재하는 TRACK 확인하여
|
|
|
|
// 2023/11/30 현재 시간값은 모두 0 이라고함
|
|
|
|
처리 */
|
|
|
|
|
|
//
|
|
set_tags("moov","trak","mdia","hdlr",NULL);
|
|
memcpy(_tag_search_value,"SStarMeta",strlen("SStarMeta"));
|
|
|
|
// 탐색 시작
|
|
parse_all();
|
|
if(_stop_parse == false)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// TRAK (root) 확인
|
|
long trak_offset = _tag_offset_list[1];
|
|
long trak_size = _tag_size_list[1];
|
|
|
|
// ENSZ
|
|
init_parser();
|
|
|
|
set_tags("trak","mdia","minf","stbl","ensz",NULL); // 암호화된 stsz => ensz 확인
|
|
|
|
parse(trak_offset,trak_size,0);
|
|
if(_stop_parse == false)
|
|
{
|
|
return false;
|
|
}
|
|
long ensz_offset = _tag_offset_list[4];
|
|
long ensz_size = _tag_size_list[4] - 8; // REMOVE TAG+SIZE
|
|
|
|
unsigned char* ensz = (unsigned char*)malloc(ensz_size);
|
|
unsigned char* ensz_decrypted = (unsigned char*)malloc(ensz_size);
|
|
memset(ensz,0,ensz_size);
|
|
memset(ensz_decrypted,0,ensz_size);
|
|
|
|
RMfseek(_file,ensz_offset+8,SEEK_SET); // TAG+SIZE
|
|
RMfread(ensz,ensz_size,1,_file);
|
|
|
|
QString password = RMApp::password();
|
|
// 암호화 해제
|
|
tb_decrypt((unsigned char*)password.toLatin1().data(), password.length(),ensz,ensz_size,ensz_decrypted);
|
|
|
|
// QString b;
|
|
// for(int i=0;i<ensz_size;i++) {
|
|
// b += QString().sprintf("%02X,",ensz_decrypted[i]);
|
|
// }
|
|
// qInfo() << "UNTIL:" << b << count << __FUNCTION__;
|
|
long count = rm_byte_to_long((const unsigned char*)&ensz_decrypted[8]);
|
|
|
|
free(ensz);
|
|
free(ensz_decrypted);
|
|
if(count > 10000 || count < 0) {
|
|
return false;
|
|
}
|
|
|
|
// ENCO
|
|
init_parser();
|
|
set_tags("trak","mdia","minf","stbl","enco",NULL);
|
|
parse(trak_offset,trak_size,0);
|
|
if(_stop_parse == false)
|
|
{
|
|
return false;
|
|
}
|
|
long enco_offset = _tag_offset_list[4];
|
|
long enco_size = _tag_size_list[4] - 8;
|
|
unsigned char* enco = (unsigned char*)malloc(enco_size);
|
|
unsigned char* enco_decrypted = (unsigned char*)malloc(enco_size);
|
|
RMfseek(_file,enco_offset+8,SEEK_SET);
|
|
RMfread(enco,enco_size,1,_file);
|
|
tb_decrypt((unsigned char*)password.toLatin1().data(), password.length(),enco,enco_size,enco_decrypted);
|
|
free(enco);
|
|
|
|
_nmea = (GPSINFOCHUCK_TELEBIT*)malloc(sizeof(_GPSINFOCHUCK_TELEBIT) * count );
|
|
|
|
// 센서 데이터 읽기
|
|
for(int i=0;i<count;i++)
|
|
{
|
|
//unsigned char* offset_buffer = enco_decrypted[i*4];
|
|
|
|
GPSINFOCHUCK_TELEBIT* item = &_nmea[i];
|
|
|
|
long offset = rm_byte_to_long(&enco_decrypted[8+i*4]);
|
|
//qInfo() << "offset:" << QString().sprintf("%X",offset) << __FUNCTION__;
|
|
RMfseek(_file,offset,SEEK_SET);
|
|
RMfread(item,sizeof(_GPSINFOCHUCK_TELEBIT),1,_file);
|
|
|
|
//qInfo() << "offset:" << offset << item->dwLat << item->dwLon << QString().sprintf("%.6f",item->dwLon) << __FUNCTION__;
|
|
}
|
|
free(enco_decrypted);
|
|
_gps0Count = count;
|
|
return false;
|
|
}
|
|
#elif (RM_MODEL == RM_MODEL_TYPE_MH9000)
|
|
bool MOVFormat::parse_sensor() {
|
|
|
|
init_parser();
|
|
//
|
|
set_tags("moov","trak","mdia","hdlr",NULL);
|
|
memcpy(_tag_search_value,"SStarMeta",strlen("SStarMeta"));
|
|
|
|
// 탐색 시작
|
|
parse_all();
|
|
if(_stop_parse == false)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// TRAK (root) 확인
|
|
long trak_offset = _tag_offset_list[1];
|
|
long trak_size = _tag_size_list[1];
|
|
|
|
// ENSZ
|
|
init_parser();
|
|
|
|
set_tags("trak","mdia","minf","stbl","stco",NULL);
|
|
|
|
parse(trak_offset,trak_size,0);
|
|
if(_stop_parse == false)
|
|
{
|
|
return false;
|
|
}
|
|
long stco_offset = _tag_offset_list[4];
|
|
long stco_size = _tag_size_list[4] - 8; // REMOVE TAG+SIZE
|
|
|
|
RMfseek(_file,stco_offset+8+4,SEEK_SET);
|
|
unsigned char long_buffer[5] = {0,};
|
|
RMfread(long_buffer,4,1,_file);
|
|
long numFrames = rm_byte_to_long(long_buffer);
|
|
|
|
// 256 - 8 = 248
|
|
//qInfo() << "stco_offset:" << stco_offset << "stco_size" << stco_size << "numFrames:" << numFrames << __FUNCTION__;
|
|
|
|
// size + type + version:1 + flags:3 + sample size:4 + number of entry: 4 + ....
|
|
//RMfseek(_file,stco_offset+8+4+4,SEEK_SET);
|
|
|
|
// 2. SIZE OFFSET 읽기
|
|
unsigned char* offset_buffer = (unsigned char*)malloc(numFrames * 4);
|
|
RMfread(offset_buffer,4,numFrames,_file);
|
|
|
|
// 개수 확인
|
|
//long offset_count = rm_read_number(_file);
|
|
|
|
// unsigned char* stco = (unsigned char*)malloc(stco_size);
|
|
// memset(stco,0,stco_size);
|
|
|
|
|
|
// RMfseek(_file,stco_offset+8,SEEK_SET); // TAG+SIZE
|
|
// RMfread(stco,stco_size,1,_file);
|
|
|
|
// long count = rm_byte_to_long((const unsigned char*)&stco[8]);
|
|
|
|
_sens = (gps_chunk_t*)malloc(sizeof(_gps_chunk_t) * numFrames );
|
|
|
|
// 센서 데이터 읽기
|
|
for(int i=0;i<numFrames;i++)
|
|
{
|
|
gps_chunk_t* item = &_sens[i];
|
|
long offset = rm_byte_to_long(&offset_buffer[i*4]);
|
|
RMfseek(_file,offset,SEEK_SET);
|
|
RMfread(item,sizeof(_gps_chunk_t),1,_file);
|
|
//qInfo() << i << item->speed << QString().sprintf("%X",offset);
|
|
//qInfo() << "i:" << i << item->year << item->mon << item->mday << __FUNCTION__;
|
|
}
|
|
free(offset_buffer);
|
|
_gsenCount = numFrames;
|
|
return false;
|
|
}
|
|
#elif (RM_MODEL_EMT_KR)
|
|
bool MOVFormat::parse_sensor() {
|
|
|
|
init_parser();
|
|
|
|
// mov.set_tags("moov","trak","mdia","minf","stbl","stsd",NULL);
|
|
_gsenCount = 0;
|
|
|
|
init_parser();
|
|
set_tags("moov","trak","mdia","minf","stbl","stsd",NULL);
|
|
memcpy(_tag_search_value,"text",strlen("text"));
|
|
|
|
parse_all();
|
|
if(_stop_parse == false)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// 2. 자막 trak 탐색 종료 후 탐색된 moov->trak->mdia->stbl을 확인하여, size 와 offset 이 0 이 아닌 경우
|
|
unsigned int stbl_offset = _tag_offset_list[4];
|
|
long stbl_size = _tag_size_list[4];
|
|
if(stbl_offset != 0 &&
|
|
stbl_size != 0)
|
|
{
|
|
// 3. 다시 parser 를 초기화 하고 stsz (각 자막별 크기) 확인
|
|
// https://developer.apple.com/library/content/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-25715
|
|
set_tags("stbl","stsz",NULL);
|
|
|
|
// stbl 부터 탐색 시작
|
|
parse(stbl_offset,stbl_size);
|
|
|
|
// 탐색된 stsz 에 subtitle의 size 들이 포함되어 있음
|
|
unsigned int stsz_offset = _tag_offset_list[1];
|
|
long stsz_size = _tag_size_list[1];
|
|
|
|
// 사이즈가 존재할 경우
|
|
if(stsz_offset != 0 &&
|
|
stsz_size != 0)
|
|
{
|
|
// 자막 개수 확인
|
|
long count = 0;
|
|
long sample_size = 0;
|
|
|
|
// 자막별 크기 (전체 동일한 경우과 개별 크기가 별도로 저장된 경우가 있음)
|
|
unsigned char *sample_size_list = NULL;
|
|
|
|
// 자막별 옵셋 (모두 별도로 저장되어 있음)
|
|
unsigned char *sample_offset_list = NULL;
|
|
|
|
// size(4) + type(4) + version/flag(4) + Sample size (모든 샘플 크기가 동일할 경우) + Number of entry(4)
|
|
|
|
// 개수를 확인
|
|
RMfseek(_file,stsz_offset+12,SEEK_SET);
|
|
sample_size = rm_read_number(_file);
|
|
count = rm_read_number(_file);
|
|
|
|
// 0 개 이상일 경우 또는 sample size 가 0 보다 클 경우
|
|
if(count > 0 || sample_size > 0)
|
|
{
|
|
int maxCount = (count + 1);
|
|
// 2초 간격으로 2초씩 저장됨
|
|
_nmea = malloc(sizeof(_NMEA_INFO) * maxCount);
|
|
//_sens = malloc((sizeof(float) * 3) * maxCount); // * MAX_SENSOR_FPS
|
|
|
|
// 자막 레코드 별로 사이즈 별도 처리 하는 경우
|
|
if(sample_size == 0)
|
|
{
|
|
// 각 자막 크기 저장 공간 할당 + 읽어 오기
|
|
sample_size_list = (unsigned char*)malloc(4 * count);
|
|
RMfread(sample_size_list,4 * count,1,_file);
|
|
}
|
|
|
|
// 다시 (moov->trak->mdia->)stbl->stco() 를 확인하여 각 자막별 옵셋 확인
|
|
// https://developer.apple.com/library/content/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-25715
|
|
set_tags("stbl","stco",NULL);
|
|
|
|
// stbl 부터 탐색 시작
|
|
parse(stbl_offset,stbl_size);
|
|
|
|
// stco 에 subtitle의 offset 들이 포함되어 있음
|
|
unsigned int stco_offset = _tag_offset_list[1];
|
|
long stco_size = _tag_size_list[1];
|
|
if(stco_offset != 0 &&
|
|
stco_size != 0)
|
|
{
|
|
// size(4) + type(4) + version/flag(4) + Entry Number + ....
|
|
RMfseek(_file,stco_offset+12,SEEK_SET);
|
|
|
|
|
|
// 개수 확인
|
|
long offset_count = rm_read_number(_file);
|
|
|
|
// 개별 사이즈가 있는 경우 최소 개수 확인
|
|
if(sample_size == 0)
|
|
{
|
|
count = MIN(count,offset_count);
|
|
}
|
|
// 없는 경우 offset 개수가 자막 개수
|
|
else
|
|
{
|
|
count = offset_count;
|
|
}
|
|
// 옵셋 정보 할당 + 읽어오기
|
|
sample_offset_list = (unsigned char*)malloc(4 * count);
|
|
RMfread(sample_offset_list,4 * count,1,_file);
|
|
|
|
// 옵셋 + 크기로 자막 읽기
|
|
for(int i=0;i<count;i++)
|
|
{
|
|
long each_offset = rm_byte_to_long(&sample_offset_list[i*4]);
|
|
long each_size = sample_size == 0 ? rm_byte_to_long(&sample_size_list[i*4]) : sample_size;
|
|
|
|
// 2초 간격으로 저장되어 중간에 2 있음
|
|
// 이것도 엉터리임
|
|
// if(each_size <= 2) {
|
|
// continue;
|
|
// }
|
|
// KEIYO 버퍼크기 엉터리임..
|
|
each_size = qMax((long)2048,each_size);
|
|
|
|
//qInfo() << QString().sprintf("%X",each_offset);
|
|
|
|
|
|
char* subtitle = (char*)malloc(each_size + 1);
|
|
memset(subtitle,0,each_size + 1);
|
|
|
|
// offset 앞에 2byte 크기가 있음..
|
|
RMfseek(_file,each_offset+2,SEEK_SET);
|
|
RMfread(subtitle,each_size-2,1,_file);
|
|
//qInfo() << subtitle;
|
|
//subtitles.push_back(subtitle);
|
|
|
|
#if (DEBUG_SENSOR_LOG)
|
|
fprintf(flog,"%08X:%08X:\r\n",each_offset+2,(each_offset+2) + each_size-2);
|
|
fprintf(flog,subtitle);
|
|
fprintf(flog,"\r\n");
|
|
#endif
|
|
|
|
process_subtitle(subtitle);
|
|
}
|
|
free(sample_offset_list);
|
|
}
|
|
|
|
if(sample_size_list != NULL)
|
|
{
|
|
free(sample_size_list);
|
|
sample_size_list = NULL;
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
#if (DEBUG_SENSOR_LOG)
|
|
fclose(flog);
|
|
#endif
|
|
return true;
|
|
} // EMT_KR
|
|
void MOVFormat::process_subtitle(const char* subtitle)
|
|
{
|
|
//qInfo() << subtitle << __FUNCTION__;
|
|
// NM5000:2025-09-01 16:57:30 X: 0.00 Y: 0.00 Z:-0.00 018.0T 13.7V - --.------ - ---.------ - ---.-km/h E:255 M:255
|
|
// NM5000:2025-09-01 16:57:30 X: 0.01 Y: 0.00 Z: 0.01 018.0T 13.7V - --.------ - ---.------ - ---.-km/h E:255 M:255
|
|
// NM5000:2025-09-01 16:57:27 X:-0.16 Y: 0.13 Z: 0.00 018.0T 13.7V - --.------ - ---.------ - ---.-km/h E:003 M:255
|
|
#if (USE_TRIGGER)
|
|
static QRegularExpression rx("(?P<model>[^:]*):(?P<ymd>\\d{4}-\\d{2}-\\d{2})\\s{0,2}(?P<hms>\\d{2}:\\d{2}:\\d{2}).*X:\\s{0,2}(?P<x>[\\-0-9.]*).*Y:\\s{0,2}(?P<y>[\\-0-9.]*).*Z:\\s{0,2}(?P<z>[\\-0-9.]*)\\s{0,2}(?P<t>[-0-9.]*)T\\s{0,2}(?P<v>[\\-0-9.]*)V\\s{0,2}(?P<gps>[G|-])\\s{0,2}(?P<lat>[\\-0-9.]*)\\s{0,2}[N|-]\\s{0,2}(?P<lon>[\\-0-9.]*)\\s[E|-]\\s{0,2}(?P<speed>[\\-0-9.]*)km(.+E:(?P<E>\\d{3}).+M:(?P<M>\\d{3}))?",QRegularExpression::CaseInsensitiveOption);
|
|
#else // USE_TRIGGER
|
|
static QRegularExpression rx("(?P<model>[^:]*):(?P<ymd>\\d{4}-\\d{2}-\\d{2})\\s{0,2}(?P<hms>\\d{2}:\\d{2}:\\d{2}).*X:\\s{0,2}(?P<x>[\\-0-9.]*).*Y:\\s{0,2}(?P<y>[\\-0-9.]*).*Z:\\s{0,2}(?P<z>[\\-0-9.]*)\\s{0,2}(?P<t>[0-9.]*)T\\s{0,2}(?P<v>[\\-0-9.]*)V\\s{0,2}(?P<gps>[G|-])\\s{0,2}(?P<lat>[\\-0-9.]*)\\s{0,2}[N|-]\\s{0,2}(?P<lon>[\\-0-9.]*)\\s[E|-]\\s{0,2}(?P<speed>[\\-0-9.]*)km",QRegularExpression::CaseInsensitiveOption);
|
|
#endif // USE_TRIGGER
|
|
QRegularExpressionMatch match = rx.match(subtitle);
|
|
if (!match.isValid()) {
|
|
return;
|
|
}
|
|
|
|
#if (RM_MODEL_EMT_KR)
|
|
if(modelName.isEmpty()) {
|
|
modelName = match.captured("model");
|
|
}
|
|
#endif // #if (RM_MODEL_EMT_KR)
|
|
|
|
//QString c0 = match.captured("ymd");
|
|
|
|
QString dateString = match.captured("ymd") + "_" + match.captured("hms");
|
|
QDateTime dt = QDateTime::fromString(dateString,"yyyy-MM-dd_HH:mm:ss");
|
|
dt.addSecs(9 * 3600); // 9시간 추가
|
|
|
|
if(!dt.isValid()) {
|
|
qInfo() << dateString << subtitle << __FUNCTION__;
|
|
}
|
|
|
|
NMEA_INFO* nmea = (NMEA_INFO*)_nmea;
|
|
NMEA_INFO* nmea_cur = &nmea[_gsenCount];
|
|
nmea_cur->nYear = dt.date().year();
|
|
nmea_cur->nMonth = dt.date().month();
|
|
nmea_cur->nDay = dt.date().day();
|
|
nmea_cur->nHour = dt.time().hour();
|
|
nmea_cur->nMin = dt.time().minute();
|
|
nmea_cur->nSec = dt.time().second();
|
|
bool bSuccess;
|
|
nmea_cur->x = match.captured("x").toFloat(&bSuccess);
|
|
nmea_cur->y = match.captured("y").toFloat(&bSuccess);
|
|
nmea_cur->z = match.captured("z").toFloat(&bSuccess);
|
|
//qInfo() << "x:" << match.captured("x") << "y:" << match.captured("y") << "z:" << match.captured("z") << __FUNCTION__;
|
|
|
|
nmea_cur->Temperature = match.captured("t").toFloat(&bSuccess);
|
|
nmea_cur->Voltage = match.captured("v").toFloat(&bSuccess);
|
|
|
|
#if (USE_TRIGGER)
|
|
bool bt;
|
|
nmea_cur->eTrigger = match.captured("E").toInt(&bt);
|
|
if(!bt) {
|
|
nmea_cur->eTrigger = 255;
|
|
}
|
|
nmea_cur->mTrigger = match.captured("M").toInt(&bt);
|
|
if(!bt) {
|
|
nmea_cur->mTrigger = 255;
|
|
}
|
|
#endif // USE_TRIGGER
|
|
|
|
|
|
bool bSuccess2;
|
|
nmea_cur->Longitude = match.captured("lon").toDouble(&bSuccess2);
|
|
nmea_cur->Latitude = match.captured("lat").toDouble(&bSuccess2);
|
|
nmea_cur->Speed = match.captured("speed").toDouble(&bSuccess2);
|
|
nmea_cur->nStatus = (nmea_cur->Longitude > 121.0f) && (nmea_cur->Longitude < 150.0f) && (nmea_cur->Latitude > 25.0f) && (nmea_cur->Latitude < 40.0f);
|
|
|
|
if(!bSuccess) {
|
|
// NM5000:2025-05-28 13:56:27 X: 0.00 Y: 0.00 Z: 0.00 012.0T 12.5V - --.------ - ---.------ - ---.-km/h E:255 V:903 S:36419k,0
|
|
//qInfo() << "ERROR:" << subtitle << __FUNCTION__;
|
|
}
|
|
_gsenCount += 1;
|
|
|
|
} // EMT_KR
|
|
#else // DUMMY
|
|
bool MOVFormat::parse_sensor() {
|
|
return false;
|
|
}
|
|
#endif // MODEL
|
|
/*
|
|
bool MOVFormat::parse_sensor()
|
|
{
|
|
init_parser();
|
|
// 1. moov->trak->mdia->minf->stbl->stsd->gpmd 순서대로 탐색
|
|
// user 데이터 확인 완료
|
|
set_tags("moov","trak","mdia","minf","stbl","stsd","gpmd",NULL);
|
|
|
|
// 탐색 시작
|
|
parse_all();
|
|
|
|
if(_stop_parse == false)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// STBL (root) 확인
|
|
long stbl_offset = _tag_offset_list[4];
|
|
long stbl_size = _tag_size_list[4];
|
|
|
|
// 'stco' Chunk Offsets Box - Sample To Chunk Box
|
|
init_parser();
|
|
|
|
set_tags("stbl","stsz",NULL);
|
|
|
|
parse(stbl_offset,stbl_size,0);
|
|
if(_stop_parse == false)
|
|
{
|
|
#if !defined(BBEXTRACT)
|
|
qInfo() << "stbl stsz search error!" << __FUNCTION__;
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
long stsz_offset = _tag_offset_list[1];
|
|
|
|
// size + type + version:1 + flags:3 + sample size:4 + number of entry: 4 + ....
|
|
RMfseek(_file,stsz_offset+8+4+4,SEEK_SET);
|
|
unsigned char long_buffer[5] = {0,};
|
|
RMfread(long_buffer,4,1,_file);
|
|
|
|
long numFrames = rm_byte_to_long(long_buffer);
|
|
if(numFrames == 0) {
|
|
return false;
|
|
}
|
|
|
|
_gpsenCount = numFrames;
|
|
|
|
// 1. SIZE BUFFER 먼저 읽기
|
|
unsigned char* size_buffer = (unsigned char*)malloc(numFrames * 4);
|
|
RMfread(size_buffer,4,numFrames,_file);
|
|
|
|
init_parser();
|
|
set_tags("stbl","stco",NULL);
|
|
|
|
parse(stbl_offset,stbl_size,0);
|
|
if(_stop_parse == false)
|
|
{
|
|
#if !defined(BBEXTRACT)
|
|
qInfo() << "stbl stco search error!" << __FUNCTION__;
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
long stco_offset = _tag_offset_list[1];
|
|
// size + type + version:1 + flags:3 + sample size:4 + number of entry: 4 + ....
|
|
RMfseek(_file,stco_offset+8+4+4,SEEK_SET);
|
|
|
|
// 2. SIZE OFFSET 읽기
|
|
unsigned char* offset_buffer = (unsigned char*)malloc(numFrames * 4);
|
|
RMfread(offset_buffer,4,numFrames,_file);
|
|
|
|
_gpsen = (GPSNR*)malloc(sizeof(_GPSNR) * numFrames );
|
|
#if !defined(BBEXTRACT)
|
|
//qInfo() << "SIZE OF _GPSNR:" << sizeof(_GPSNR);
|
|
#endif
|
|
// 센서 데이터 읽기
|
|
for(int i=0;i<numFrames;i++)
|
|
{
|
|
GPSNR* item = &_gpsen[i];
|
|
long offset = rm_byte_to_long(&offset_buffer[i*4]);
|
|
RMfseek(_file,offset,SEEK_SET);
|
|
RMfread(item,sizeof(_GPSNR),1,_file);
|
|
#if !defined(BBEXTRACT)
|
|
//qInfo() << i << item->idx << "GPS:" << item->byYear << item->byMon << item->byDay << item->byHour << item->byMin << item->bySec;
|
|
#endif
|
|
//qInfo() << i << ":" << rm_byte_to_long(&offset_buffer[i*4]) << rm_byte_to_long(&size_buffer[i*4]);
|
|
//qInfo() << item->byYear << item->byMon << item->byDay << item->byHour << item->byMin << item->bySec << item->dwLat << item->dwLon;
|
|
}
|
|
|
|
//qInfo() << "SIZE _GPSNR: " << sizeof(_GPSNR);
|
|
|
|
// 해제
|
|
free(offset_buffer);
|
|
free(size_buffer);
|
|
//free(_gpsen);
|
|
|
|
return true;
|
|
}
|
|
*/
|
|
void MOVFormat::init_parser()
|
|
{
|
|
// 초기화
|
|
//_tag_search_value_exist = false;
|
|
memset(_tag_search_value,0,RM_MOV_MAX_TAG_VALUE_SIZE);
|
|
|
|
memset(_tag_list,0,sizeof(long) * RM_MOV_MAX_DEPTH);
|
|
_tag_count = 0;
|
|
|
|
memset(_tag_offset_list,0,sizeof(unsigned int) * RM_MOV_MAX_DEPTH);
|
|
memset(_tag_size_list,0,sizeof(long) * RM_MOV_MAX_DEPTH);
|
|
|
|
_stop_parse = false;
|
|
}
|
|
#endif // #if (FILE_FORMAT_MOV)
|