Files
fmviewer3/project/fm_viewer/data/rm_video_list.cpp
2026-02-21 17:11:31 +09:00

2031 lines
61 KiB
C++

#include "rm_video_list.h"
#include <qdir.h>
#include <qapplication.h>
#include <QUrl>
#include <QRegularExpression>
//#include "rm_uimanager.h"
#include "rm_video_item_2ch.h"
#include "../ui/rm_dialog_progress.h"
//#include "../widgets/rm_toolbutton.h"
#include "../core/rm_play_process.h"
#include "rm_overwrite.h"
#if (DETECT_USB_CHANGE)
#include "../core/rm_usb.h"
#endif
#include <QDataStream>
#include <QStringList>
#if (RM_MODEL == RM_MODEL_TYPE_AN6000)
#include "../data/an6000_decode.h"
#endif
#if (SUPPORT_AVI_FIX_DURL)
#include "rm_avirepair.h"
#endif
#if (PLAYER_ONLY_LIBRARY_MODE)
RMVideoFileList* RMVideoFileList::_instance = NULL;
#endif // PLAYER_ONLY_LIBRARY_MODE
// SORT
#if !(USE_1HOUR_FILTER)
bool RMVideoItemCompare(RMVideoItem * s1 , RMVideoItem * s2)
{
// 동일할 경우 상시 먼저 ..
if(s1->startTime() == s2->startTime())
{
return (int)s1->type < (int)s2->type;
}
return s1->startTime() < s2->startTime();
}
#else // #if !(USE_1HOUR_FILTER)
bool RMVideoItemCompare(RMVideoItem * s1 , RMVideoItem * s2)
{
// 2022/07/12 역순으로 변경
return RMVideoFileList::instance()->bSortDsc ? s2->startTime() < s1->startTime() : s1->startTime() < s2->startTime();
}
#endif #if !(USE_1HOUR_FILTER)
int RMVideoFileList::n_lastPercent = -1;
#if (FILE_FORMAT_MOV && FILE_FORMAT_AVI)
QList<QString> RMVideoFileList::fileFilters = QList<QString>() << QString("*.AVI") << QString("*.MOV") << QString("*.MP4");
#elif (FILE_FORMAT_AVI)
QList<QString> RMVideoFileList::fileFilters = QList<QString>() << QString("*.AVI");
#elif (FILE_FORMAT_MOV)
#if (USE_FFMPEG_PW)
#if (SUPPORT_MP4TB4)
QList<QString> RMVideoFileList::fileFilters = QList<QString>() << QString("*.TB4") << QString("*.MP4");
#else // SUPPORT_MP4TB4
QList<QString> RMVideoFileList::fileFilters = QList<QString>() << QString("*.TB4");// << QString("*.MP4");
#endif // SUPPORT_MP4TB4
#else // USE_FFMPEG_PW
QList<QString> RMVideoFileList::fileFilters = QList<QString>() << QString("*.MOV") << QString("*.MP4");
#endif // USE_FFMPEG_PW
#endif
RMVideoFileList::RMVideoFileList(QObject* parent) : QObject(parent)
{
_filter = FILTER_NORMAL | FILTER_EVENT;
_playItem = NULL;
#if (USE_1HOUR_FILTER)
b1HourList = true; // 1시간 단위 리스트
bSortDsc = true; // (기본값) 표시=최신순으로 정렬, 시간순으로 정렬
#endif // USE_1HOUR_FILTER
#if (SUPPORT_LOADING_CANCEL)
bCancelLoading = false; // 로딩 취소
#endif
}
// 모델별 채널 처리
#if !(RECURSIVE_APPEND_FILE)
void RMVideoFileList::_appendFrontRear(QString& folder,QList<QUrl>& list)
{
// 신규 모델 처리시 ELSE 를 사용하지 않는다
#if (RM_MODEL == RM_MODEL_TYPE_NX_DRW22)
const QList<QString> folders = QList<QString>() << "F" << "R";
#elif (RM_MODEL == RM_MODEL_TYPE_ADT_CAPS)
const QList<QString> folders = QList<QString>() << "1" << "2";
#endif
foreach (QString each_folder, folders)
{
QString current_front = QDir::cleanPath(folder + PATH_COMPONENT + each_folder);
if(QFile::exists(current_front))
{
QDir fdir(current_front);
fdir.setNameFilters(RMVideoFileList::fileFilters); // 확장자 필터
QStringList allFiles = fdir.entryList();
foreach (const QString &str, allFiles)
{
if(str == "." || str == "..")
{
continue;
}
QDateTime dateTime;
QString filePath = QDir::cleanPath(current_front + PATH_COMPONENT + str);
// static 이라 일단 중복되어도 추가함.
if( RMVideoItem::IsFeasible(filePath,&dateTime) != (int)TYPE_UNDEFINED)
{
list.append(QUrl::fromLocalFile(filePath));
}
}
}
}
}
#endif // #if !(RECURSIVE_APPEND_FILE)
#if (RM_MODEL == RM_MODEL_TYPE_AN6000)
void RMVideoFileList::removeTemps()
{
for(int i=0;i<_items.size();i++){
_items.at(i)->removeTemp();
}
}
#endif //
bool RMVideoFileList::addItem(QString filePath,QDateTime* pDateTime,GROUP_TYPE type)
{
QString cleanPath = QDir::cleanPath(filePath);
#if (TRI_CHANNEL || PENTA_CHANNEL)
int ch = 1;
RMVideoFileList::_checkChannelInfo(filePath,&ch);
#else // TRI_CHANNEL
#if (FORCE_2CH || SINGLE_CH_VIEWER)
bool isCH2 = false;
#else // !FORCE_2CH
bool isCH2 = false;
#if (SUPPORT_2CH)
bool is2CH = false; // 1FILE 2CH 영상
RMVideoFileList::_checkChannelInfo(filePath,&isCH2,&is2CH);
#else // SUPPORT_2CH
RMVideoFileList::_checkChannelInfo(filePath,&isCH2);
#endif // SUPPORT_2CH
#endif // FORCE_2CH
#endif // TRI_CHANNEL
// 무조건 중복체크 해야함
if(itemExist(cleanPath) == true)
{
//qInfo() << "exist skip:" << cleanPath << __FUNCTION__;
return false;
}
#if (SUPPORT_AVI_FIX_DURL)
// 복구 필요
if(cleanPath.endsWith("_EXIT.AVI",Qt::CaseInsensitive)) {
QString destPath;
if (RMAVIRepair::repairV50(filePath,destPath) == true)
{
if(destPath.isEmpty() == false) // true 일 경우 복구할 필요 없음
{
cleanPath = destPath;
}
}
else {
return false;
}
}
#endif
#if (TRI_CHANNEL || PENTA_CHANNEL)
RMVideoItem* item = new RMVideoItem(cleanPath,ch,pDateTime);
#else
RMVideoItem* item = new RMVideoItem(cleanPath,isCH2,pDateTime);
#endif
if(item->isValid() == true)
{
item->type = type;
item->dropItem = true;
// 전방, 또는 후방 파일 확인해서 추가
#if !(FORCE_2CH || SINGLE_CH_VIEWER)
if(_addOtherChannelFile(item))
{
#if (CHECK_REAR_DURATION) // 후방도 재생시간 확인하여 제거
if(item->isRearDuration() == false) {
delete item;
item = NULL;
}
#endif // CHECK_REAR_DURATION
}
#endif // FORCE_2CH
#if (CHECK_VIDEO_BITRATE)
item->testBitrate2CH();
// 후방 파일만 존재하는 경우 후방 파일의 bitrate 가 < 미만일 경우
if(item->anyFilePath().length() == 0) {
delete item;
item = NULL;
}
#endif
#if (SUPPORT_2CH)
if(is2CH) {
item->filePathCH2 = item->filePath;
}
#endif // SUPPORT_2CH
if(item != NULL) {
_items.append(item);
}
}
else
{
delete item;
return false;
}
return true;
}
void RMVideoFileList::checkedItems(QList<RMVideoItem*>& items)
{
foreach (RMVideoItem* item, filteredItems()) {
#if (SINGLE_SAVE_CHECK_FILE)
if(item->checked) { // 선택된 파일이 존재할 경우 1개만 리턴
items.append(item);
return;
}
#else // SINGLE_SAVE_CHECK_FILE
#if (SINGLE_SAVE_FILE)
if(item == _playItem) {
items.append(item);
}
#else
if(item->checked) {
items.append(item);
}
#endif
#endif // SINGLE_SAVE_CHECK_FILE
}
#if (SINGLE_SAVE_CHECK_FILE)
// 선택된 파일이 없을 경우 재생중인
foreach (RMVideoItem* item, filteredItems()) {
if(item == _playItem) {
item->checked = true;
items.append(item);
}
}
#endif // SINGLE_SAVE_CHECK_FILE
}
RMVideoItem* RMVideoFileList::searchPlayItem(QString prefix)
{
foreach (RMVideoItem* item, filteredItems()) {
//qInfo() << item->fileName << prefix << __FUNCTION__;
if(item->fileName.startsWith(prefix)) {
return item;
}
}
return NULL;
}
void RMVideoFileList::_backupOverwrite(QString target,QString src,int countLeft)
{
bool bCopy = true;
if(QFile::exists(target)) {
// 확인 처리 해야할 경우
if (RMOverwrite::check(OVERWRITE_OPTION_ASK))
{
QFileInfo info(target);
QString baseName = info.baseName();
#if (0)
// 파일 인덱스 존재할 경우 제거
// NNF_191125-092637-003801_2
QStringList ls = baseName.split("-",QString::SkipEmptyParts);
if(ls.length() == 3)
{
ls.removeLast();
}
baseName = ls.join("-");
#endif //
RMOverwrite::currentFileName = baseName + "." + info.suffix();
RMOverwrite::currentCount = countLeft;
emit backupPaused(true);
RMOverwrite::lock();
RMOverwrite::wait();
RMOverwrite::unlock();
emit backupPaused(false);
// 작업 취소
if(RMOverwrite::check(OVERWRITE_OPTION_CANCEL)) {
return;
}
}
// 확인 처리 후..
if (RMOverwrite::check(OVERWRITE_OPTION_SKIP)) {
bCopy = false;
}
else if (RMOverwrite::check(OVERWRITE_OPTION_WRITE)) {
bCopy = true;
//qInfo() << "remove:" << target;
QFile::remove(target);
}
// // 전체가 아닐 경우 다시 ASK 로 변경
// if(RMOverwrite::check(OVERWRITE_OPTION_ALL) == false) {
// RMOverwrite::gCurrent = OVERWRITE_OPTION_ASK;
// }
}
if(bCopy) {
QFile::copy(src,target);
#if (RM_MODEL == RM_MODEL_TYPE_AN6000) // 디코딩
if(is_encrypted_an6000(target.toStdWString().c_str())) {
decrypt_an6000(target.toStdWString().c_str());
}
#endif //
}
}
void RMVideoFileList::backup(QString dest)
{
QList<RMVideoItem*> items;
checkedItems(items);
if(items.isEmpty()){
return;
}
RMOverwrite::reset(); // 초기화
emit backupStarted();
QCoreApplication::processEvents();
QThread::msleep(1);
RMVideoFileList::n_lastPercent = -1;
int index = 0;
int count = items.count();
//qInfo() << __FUNCTION__;
foreach (RMVideoItem* item, items) {
#if (SINGLE_SAVE_FILE && !SINGLE_SAVE_CHECK_FILE)
if(item != _playItem) {
continue;
}
qInfo() << __FUNCTION__ << item->anyFilePath();
#else
if(item->checked == false) {
continue;
}
#endif
if(item->filePath.isEmpty() == false)
{
QFileInfo info(item->filePath);
QString target = QDir::cleanPath(dest + QDir::separator() + info.baseName() + "." + info.suffix());
_backupOverwrite(target,item->filePath,items.count() - index);
// 취소
if(RMOverwrite::check(OVERWRITE_OPTION_CANCEL)) {
break;
}
}
#if !(DUAL_CH_FILE || SINGLE_CH_VIEWER)
if(item->filePathCH2.isEmpty() == false)
{
QFileInfo info(item->filePathCH2);
QString target = QDir::cleanPath(dest + QDir::separator() + info.baseName() + "." + info.suffix()).toUpper();
_backupOverwrite(target,item->filePathCH2,items.count() - index);
// 취소
if(RMOverwrite::check(OVERWRITE_OPTION_CANCEL)) {
break;
}
}
#endif
#if (PENTA_CHANNEL)
if(item->filePathCH3.isEmpty() == false)
{
QFileInfo info(item->filePathCH3);
QString target = QDir::cleanPath(dest + QDir::separator() + info.baseName() + "." + info.suffix()).toUpper();
_backupOverwrite(target,item->filePathCH3,items.count() - index);
// 취소
if(RMOverwrite::check(OVERWRITE_OPTION_CANCEL)) {
break;
}
}
if(item->filePathCH4.isEmpty() == false)
{
QFileInfo info(item->filePathCH4);
QString target = QDir::cleanPath(dest + QDir::separator() + info.baseName() + "." + info.suffix()).toUpper();
_backupOverwrite(target,item->filePathCH4,items.count() - index);
// 취소
if(RMOverwrite::check(OVERWRITE_OPTION_CANCEL)) {
break;
}
}
if(item->filePathCH5.isEmpty() == false)
{
QFileInfo info(item->filePathCH5);
QString target = QDir::cleanPath(dest + QDir::separator() + info.baseName() + "." + info.suffix()).toUpper();
_backupOverwrite(target,item->filePathCH5,items.count() - index);
// 취소
if(RMOverwrite::check(OVERWRITE_OPTION_CANCEL)) {
break;
}
}
#endif // PENTA_CHANNEL
// 중복 처리 선택 옵션에서 전체가 아닐 경우 다시 ASK 로 변경
if(RMOverwrite::check(OVERWRITE_OPTION_ALL) == false) {
RMOverwrite::gCurrent = OVERWRITE_OPTION_ASK;
}
int percent = (int)(((double)++index) / ((double)count) * (double)(PROGRESS_BAR_MAX));
LOG_INFO << "backup:" << item->anyFilePath() << " percent:" << percent;
if(percent != RMVideoFileList::n_lastPercent)
{
RMVideoFileList::n_lastPercent = percent;
emit updateProgress(percent);
QCoreApplication::processEvents();
QThread::usleep(1);
// unsigned long nSleep = (RMVideoFileList::n_lastPercent == -1) ? 100 : 5;
}
}
emit backupEnd();
}
//! \brief 리스트 삭제하고 이벤트 전달
void RMVideoFileList::clearList()
{
emit listUpdateStarted(true);
#if (RM_MODEL == RM_MODEL_TYPE_TBD360 || RM_MODEL_EMT_KR) // RM_MODEL == RM_MODEL_TYPE_XLDR_88 ||
_filter = FILTER_NORMAL;
#else // RM_MODEL_TYPE_TBD360
_filter = FILTER_ALL;
#endif // RM_MODEL_TYPE_TBD360
_items.clear();
QCoreApplication::processEvents();
#if (USE_1HOUR_FILTER)
set1HourList(b1HourList,false,NULL); // 정렬에 따른 1시간 필터 처리
#else // USE_1HOUR_FILTER
// _filtered Item 처리
_updateItemsByFilter();
#endif // #if (USE_1HOUR_FILTER)
#if (USE_1HOUR_FILTER)
count1HourItems();
#endif
QCoreApplication::processEvents();
QThread::usleep(1);
emit listUpdateEnd(true,NULL);
emit loadListEnd();
}
void RMVideoFileList::loadFromList(QList<QUrl> list,bool bPlayFirstAdded)
{
Q_UNUSED(bPlayFirstAdded)
emit listUpdateStarted(true);
#if (SUPPORT_LOADING_CANCEL)
bCancelLoading = false;
#endif // #if (SUPPORT_LOADING_CANCEL)
if(list.isEmpty()) {
#if (USE_1HOUR_FILTER)
count1HourItems();
#endif
emit listUpdateEnd(true,NULL);
return;
}
#if (RM_MODEL == RM_MODEL_TYPE_TBD360 || RM_MODEL_EMT_KR) // RM_MODEL_EMT_KR = 실제로는 maxType 선택됨
_filter = FILTER_NORMAL;
#else // RM_MODEL_TYPE_TBD360
_filter = FILTER_ALL;
#endif // RM_MODEL_TYPE_TBD360
_items.clear();
QCoreApplication::processEvents();
RMVideoFileList::n_lastPercent = -1;
int index = 0;
int count = list.count();
#if (DETECT_USB_CHANGE)
// 경로가 USB 일 경우 드라이버 지정
QString path = list.first().toLocalFile();
RMApp::instance()->usb->setOpenDriveWithPath(path);
//((WindowMain*)RMApp::instance()->pMainWindow)->_usb->setOpenDriveWithFolder();
#endif // DETECT_USB_CHANGE
foreach (const QUrl &url, list)
{
int percent = (int)(((double)++index) / ((double)count) * (double)(PROGRESS_BAR_MAX));
if(percent % 2 == 0 && percent != RMVideoFileList::n_lastPercent)
{
RMVideoFileList::n_lastPercent = percent;
emit updateProgress(percent);
// SLEEP 기능 제거하면 썸네일 표시안됨???
//QThread::usleep(1);
//QCoreApplication::processEvents();
}
QString fileName = url.toLocalFile();
QDateTime dateTime;
RMVideoFileList::GROUP_TYPE type = ( RMVideoFileList::GROUP_TYPE)RMVideoItem::IsFeasible(fileName,&dateTime);
if(type == RMVideoFileList::TYPE_UNDEFINED)
{
continue;
}
if(addItem(fileName,&dateTime,type) == true) // 경로에 / \ 가 동시에 들어가면 안됨..
{
}
#if (SUPPORT_LOADING_CANCEL)
if(bCancelLoading) {
break;
}
#endif // #if (SUPPORT_LOADING_CANCEL)
}
#if (USE_1HOUR_FILTER)
setSortList(bSortDsc); // 정렬처리
set1HourList(b1HourList,false,NULL); // 정렬에 따른 1시간 필터 처리
#else // USE_1HOUR_FILTER
qSort(_items.begin(),_items.end(),RMVideoItemCompare);
#if (RM_MODEL_EMT_KR)
// 로딩된 파일 중 가장 많은 파일이 존재하는 녹화타입 선택
selectMaxCountFilter();
#endif // RM_MODEL_EMT_KR
// _filtered Item 처리
_updateItemsByFilter();
#endif // #if (USE_1HOUR_FILTER)
#if (USE_1HOUR_FILTER)
count1HourItems();
#endif
QCoreApplication::processEvents();
QThread::usleep(1);
emit listUpdateEnd(true,NULL);
#if (PLAY_FIRST_LOADED)
int old = getPlayIndex();
// TELEBIT 는 간편/전체 리스트 검색 (현재 리스트와 동일하지 않을 수 있음)
if(_filteredItems.count() > 0)
{
emit playItemFound(_filteredItems.first(),old);
}
#endif
emit loadListEnd();
}
#if (RM_MODEL_EMT_KR)
int RMVideoFileList::currentFilterIndex()
{
QList<FILTER> filters = QList<FILTER>() << FILTER_NORMAL << FILTER_EVENT << FILTER_PARK << FILTER_PARK_EVENT << FILTER_MANUAL << FILTER_MYBOX;
for(int i=0;i<filters.size();i++) {
//qInfo() << "currentFilterIndex:" << filters[i] << _filter << __FUNCTION__;
if(filters[i] == _filter) {
return i;
}
}
qInfo() << "currentFilterIndex:0" << __FUNCTION__;
return 0;
}
void RMVideoFileList::selectMaxCountFilter()
{
// 각 타입별 개수 확인
int filterCount[10] = {0,};
foreach (RMVideoItem* item, _items) {
if(item->type == RMVideoFileList::TYPE_UNDEFINED) {
continue;
}
filterCount[item->type] += 1;
}
int maxType = 0;
int maxCount = 0;
for(int i=0;i<10;i++) {
if(filterCount[i] > maxCount) {
maxCount = filterCount[i];
maxType = i;
}
}
_filter = filterTypeFromGroupType((GROUP_TYPE)maxType);
//qInfo() << "MAX TYPE:" << _filter << __FUNCTION__;
}
#endif // RM_MODEL_EMT_KR
#ifdef _DEBUG
void RMVideoFileList::print()
{
foreach (RMVideoItem* item, _items) {
item->print();
}
}
#endif
RMVideoFileList::GROUP_TYPE RMVideoFileList::checkGroupTypeFromFolderPath(QString folderPath)
{
QStringList parts = folderPath.split(PATH_COMPONENT);
QString name = parts.last();
name = name.toUpper();
// 모델은 폴더만으로 파일 종류 구분 불가
if(name.contains("Event",Qt::CaseInsensitive) == true)
{
return TYPE_EVENT;
}
else if(name.contains("Normal",Qt::CaseInsensitive) == true) // 상시
{
return TYPE_NORMAL;
}
return TYPE_UNDEFINED;
}
bool RMVideoFileList::isRootPath(QString folderPath)
{
QDir dir(folderPath);
dir.setFilter(QDir::Dirs);
QStringList allDirs = dir.entryList();
foreach (const QString &str, allDirs)
{
if(str == "." || str == "..")
{
continue;
}
QString eachFolderPath = folderPath + PATH_COMPONENT + str;
if(RMVideoFileList::checkGroupTypeFromFolderPath(eachFolderPath) != RMVideoFileList::TYPE_UNDEFINED)
{
return true;
}
}
return false;
}
#if (RECURSIVE_APPEND_FILE)
void RMVideoFileList::appendFolderToList(QString folderPath,QList<QUrl>& list,int depth)
{
if(RECURSIVE_FOLDER_MAX_DEPTH > depth) {
QDir dir(folderPath);
dir.setFilter(QDir::Dirs);
foreach (const QString& eachFolder, dir.entryList()) {
if(eachFolder == "." || eachFolder == "..")
{
continue;
}
// 폴더는 탐색
QString fullPath = QDir::cleanPath(folderPath + PATH_COMPONENT + eachFolder);
RMVideoFileList::appendFolderToList(fullPath,list,depth+1);
}
} else {
//qInfo() << "SKIP:" << folderPath;
}
QDir dirFile(folderPath);
dirFile.setNameFilters(RMVideoFileList::fileFilters);
QStringList allFiles = dirFile.entryList();
foreach (const QString& eachFile, allFiles) {
QDateTime dateTime;
QString fullPath = QDir::cleanPath(folderPath + PATH_COMPONENT + eachFile);
// static 이라 일단 중복되어도 추가함.
if( RMVideoItem::IsFeasible(fullPath,&dateTime) != (int)TYPE_UNDEFINED)
{
list.append(QUrl::fromLocalFile(fullPath));
}
}
}
#else
void RMVideoFileList::appendToList(QString folderPath,QList<QUrl>& list, bool baseFolderOnly)
{
if(baseFolderOnly == true)
{
RMVideoFileList::GROUP_TYPE type = RMVideoFileList::checkGroupTypeFromFolderPath(folderPath);
if(type == TYPE_UNDEFINED)
{
return;
}
}
QDir dir(folderPath);
dir.setNameFilters(RMVideoFileList::fileFilters);
QStringList allFiles = dir.entryList();
foreach (const QString &str, allFiles)
{
QDateTime dateTime;
QString filePath = QDir::cleanPath(folderPath + PATH_COMPONENT + str);
// static 이라 일단 중복되어도 추가함.
if( RMVideoItem::IsFeasible(filePath,&dateTime) != (int)TYPE_UNDEFINED)
{
list.append(QUrl::fromLocalFile(filePath));
}
else
{
//qInfo() << "NO FEASIBLE" << filePath;
}
}
// Front, Rear 폴더 탐색
_appendFrontRear(folderPath,list);
}
void RMVideoFileList::appendFolderToList(QString rootFolderPath,QList<QUrl>& list)
{
QDir dir(rootFolderPath);
dir.setFilter(QDir::Dirs);
QStringList allDirs = dir.entryList();
// 자체 폴더도 추가
RMVideoFileList::appendToList(rootFolderPath,list,false);
foreach (const QString &str, allDirs)
{
if(str == "." || str == "..")
{
continue;
}
QString eachFolderPath = QDir::cleanPath(rootFolderPath + PATH_COMPONENT + str);
//qInfo() << "appendToList check:" << eachFolderPath;
if(RMVideoFileList::checkGroupTypeFromFolderPath(eachFolderPath) != RMVideoFileList::TYPE_UNDEFINED)
{
//qInfo() << "appendToList" << eachFolderPath;
RMVideoFileList::appendToList(eachFolderPath,list,true); // root 에서 처리하면 기본 폴더만 추가
}
// 채널 폴더 (EVENT 등 폴더 내부에서 열기)
else if (str == QString("1") || str == QString("2")) {
RMVideoFileList::appendToList(eachFolderPath,list,false);
}
}
}
#endif
bool RMVideoFileList::_searchItem(bool next, RMVideoItem** item,int fromIndex)
{
*item = NULL;
if(fromIndex == -1 && _playItem == NULL)
{
return false;
}
QList<RMVideoItem*>& items = filteredItems();
int offset = next ? 1 : -1; // next, previous
int index = fromIndex == -1 ? (items.indexOf(_playItem) + offset) : (fromIndex + offset);
#if (RM_MODEL_EMT_KR)
if(index < -1) {
#else
if(index < 0) {
#endif
return false;
}
else if (next == true && index >= items.count()) {
#if (PROFILE_VIDEO_FILE_LOADING)
LOG_VLOAD << "VIDEO LIST PLAY PROFILE DONE" << LOG_VLOAD_T2;
// 1회만 반복함
return false;
#endif
index = 0;
}
#if (RM_MODEL_EMT_KR)
else if (index < 0) {
index = items.size() - 1;
}
#endif
*item = items.at(index);
return true;
}
RMVideoItem* RMVideoFileList::nexItem(bool bNext)
{
RMVideoItem* item = NULL;
if(_playItem != NULL && _searchItem(bNext,&item,getPlayIndex()))
{
return item;
}
return NULL;
}
bool RMVideoFileList::isNextPlayItemExist(bool bNext)
{
if(_playItem == NULL)
{
return false;
}
RMVideoItem* item = NULL;
return _searchItem(bNext,&item,getPlayIndex());
}
RMVideoItem* RMVideoFileList::itemWithPath(QString filePath)
{
#if (FORCE_2CH || SINGLE_CH_VIEWER)
QString search = QFileInfo(filePath).baseName();
#else
QString search = RMVideoItem::fileNameWithoutChannel(filePath);
#endif
foreach (RMVideoItem* item, _items)
{
#if (FORCE_2CH || SINGLE_CH_VIEWER)
QString compare = QFileInfo(item->anyFilePath()).baseName();
#else
QString compare = RMVideoItem::fileNameWithoutChannel(item->anyFilePath());
#endif
if(search.compare(compare,Qt::CaseInsensitive) == 0) {
return item;
}
}
return NULL;
}
void RMVideoFileList::playItem(RMVideoItem* item)
{
if(item != NULL) {
int old = getPlayIndex();
emit playItemFound(item,old);
}
}
void RMVideoFileList::onPlayNextVideo(int fromIndex)
{
#if (FFMPEG_VTHREAD_DEBUG)
emit playNoMoreItem(); // 더이상 플레이할 아이템 없음
return;
#endif // 연속재생 방지
//qInfo() << "onPlayNextVideo:" << fromIndex;
if((fromIndex == -1 && _playItem == NULL) || RMPlayProcess::bProcessing)
{
return;
}
int old = getPlayIndex();
//qInfo() << "onPlayNextVideo old index:" << old;
RMVideoItem* item = NULL;
if(_searchItem(true,&item,fromIndex) == true) // 탐색
{
emit playItemFound(item,old); // 탐색완료 -> 다음파일
}
else
{
emit playNoMoreItem(); // 더이상 플레이할 아이템 없음
}
}
void RMVideoFileList::onPlayPreviousVideo(int fromIndex)
{
if((fromIndex == -1 && _playItem == NULL) || RMPlayProcess::bProcessing)
{
return;
}
int old = getPlayIndex();
RMVideoItem* item = NULL;
if(_searchItem(false,&item,fromIndex) == true) // 탐색
{
emit playItemFound(item,old); // 탐색완료
}
else
{
emit playNoMoreItem(); // 더이상 플레이할 아이템 없음
}
}
#if (USE_DATE_TIME_LIST)
QDate RMVideoFileList::getFirstDate()
{
if(_items.isEmpty()) {
return QDate();
}
return _items.first()->startTime().date();
}
void RMVideoFileList::getMonthList(int year, int month, QSet<int>& ret, QList<RMVideoItem*>& dayItems)
{
dayItems.clear();
ret.clear();
// 이전달 마지막일
QDate after = QDate(year,month,1).addDays(-1);
// 다음달 1일
QDate before = QDate(year,month,1).addMonths(1);
for(int i=0;i<_items.size();i++) {
QDate st = _items.at(i)->startTime().date();
if(st > after && st < before) {
dayItems.append(_items.at(i)); // 해당일자 아이템 추가
ret.insert(st.day()); // 날짜 추가
}
}
}
#endif// USE_DATE_TIME_LIST
int RMVideoFileList::removeItem(RMVideoItem* item)
{
// 필터된 아이템의 인덱스를 리턴한다
int index = _filteredItems.indexOf(item);
_filteredItems.removeAt(index);
// 전체 아이템에서도 제거
_items.removeOne(item);
return index;
}
#if (USE_1HOUR_FILTER)
RMVideoItem* RMVideoFileList::findFirstItemIn1Hour(RMVideoItem* searchItem)
{
if(searchItem == NULL) {
return NULL;
}
// 최신순으로 표시할 경우
//
QDateTime fromDateTime = simpleFromDateTime(searchItem->startTime());
foreach (RMVideoItem* item, _items)
{
if(bSortDsc) {
int secsTo = item->startTime().secsTo(fromDateTime);
if (secsTo > 0 && secsTo < 3600)
{
return item;
}
} else {
if (item->startTime().secsTo(fromDateTime) <= 0 )
{
return item;
}
}
}
return NULL; // 발생할 수 없음..
}
QDateTime RMVideoFileList::simpleFromDateTime(QDateTime& startTime)
{
int hour = startTime.time().hour();
int perHour = 1;//3600 / 3600;
if(perHour != 1)
{
hour = hour - (hour % perHour);
}
return bSortDsc ? QDateTime(startTime.date(),QTime(hour,0,0)).addSecs(3600) : QDateTime(startTime.date(),QTime(hour,0,0));
}
QDateTime RMVideoFileList::simpleFromDateTime2(QDateTime& startTime)
{
int hour = startTime.time().hour();
int perHour = 1;//3600 / 3600;
if(perHour != 1)
{
hour = hour - (hour % perHour);
}
return (!bSortDsc) ? QDateTime(startTime.date(),QTime(hour,0,0)).addSecs(3600) : QDateTime(startTime.date(),QTime(hour,0,0));
}
void RMVideoFileList::load1HourList(QList<RMVideoItem*>& res)
{
res.clear();
// N 시간 단위로 파일 추가
QDateTime lastDateTime = QDateTime();
foreach (RMVideoItem* item, _items) {
item->checked = false;
QDateTime currentTime = item->startTime();
if(bSortDsc) {
if(lastDateTime.isValid() == true && -lastDateTime.secsTo(currentTime) < 3600 ) {
continue;
}
}
else {
if(lastDateTime.isValid() == true && lastDateTime.secsTo(currentTime) < 3600 ) {
continue;
}
}
res.append(item);
lastDateTime = simpleFromDateTime(item->startTime());
}
}
void RMVideoFileList::load1HourInList(QDateTime dt, QList<RMVideoItem*>& res)
{
res.clear();
QDateTime fromDateTime = simpleFromDateTime(dt);
foreach (RMVideoItem* item, _items)
{
qint64 diff = fromDateTime.secsTo(item->startTime());
if(bSortDsc) {
diff *= -1;
}
if(diff >= 0 && diff < 3600)
{
res.append(item);
}
}
}
QPair<QString,QString> RMVideoFileList::getThumbnailPath(RMVideoItem* item)
{
// C:\stroage\testing\TELEBIT\20231116_TB4000_SD3\Video\2023_11\15\17\20231115-172447_PSR0_0000.tb4
// C:\stroage\testing\TELEBIT\20231116_TB4000_SD3\Photo\2023_11\15\17\20231115-172447_PSR0_0000_0.jpg
// C:\stroage\testing\TELEBIT\20231116_TB4000_SD3\Photo\2023_11\15\17\20231115-172447_PSR0_0000_1.jpg
QString src = item->filePath;
if(QFile::exists(item->filePath)){
int idx = src.lastIndexOf("Video");
if(idx >= 0) {
QString base = src.replace(idx,5,"Photo");
QString f = base;
f.replace(".tb4","_0.jpg");
if(!QFile::exists(f)) {
f = "";
}
QString s = base;
s.replace(".tb4","_1.jpg");
if(!QFile::exists(s)) {
s = "";
}
return QPair<QString,QString>(f,s);
}
}
return QPair<QString,QString>("","");
}
void RMVideoFileList::loadThumbnails(QList<RMVideoItem*>& src, QList<QPair<QString,QString>>& res)
{
res.clear();
// Thumbnail 과 동기화를 위해 존재하지 않는 아이템은 제거
QList<RMVideoItem*> removes = QList<RMVideoItem*>();
foreach (RMVideoItem* item, src)
{
QPair<QString,QString> paths = getThumbnailPath(item);
if(paths.first.isEmpty() && paths.second.isEmpty()) {
// removes.append(item); // 제거하지 않고 NO_THUMBNAIL 추가
// continue;
// 문제는 NO IMAGE 로는 이미지 선택시 재생할 수 없음..
QString name = QFileInfo(item->filePath).baseName();
paths = QPair<QString,QString>(name+"_0",name+"_1");
// ":/image/no_thumbnail.png",":/image/no_thumbnail.png"
}
res.append(paths);
}
// Thumbnail 과 동기화를 위해 존재하지 않는 아이템은 제거
//foreach (RMVideoItem* item, removes)
//{
// src.removeOne(item);
//}
}
void RMVideoFileList::count1HourItems()
{
count1Hour = 0;
// N 시간 단위로 파일 추가
QDateTime lastDateTime = QDateTime();
foreach (RMVideoItem* item, _items) {
QDateTime currentTime = item->startTime();
if(bSortDsc) {
if(lastDateTime.isValid() == true && -lastDateTime.secsTo(currentTime) < 3600 ) {
continue;
}
}
else {
if(lastDateTime.isValid() == true && lastDateTime.secsTo(currentTime) < 3600 ) {
continue;
}
}
count1Hour++;
lastDateTime = simpleFromDateTime(item->startTime());
}
}
void RMVideoFileList::set1HourList(bool b1Hour, bool bEmit, RMVideoItem* selected)
{
if(bEmit) {
emit listUpdateStarted(false);
}
_filteredItems.clear();
b1HourList = b1Hour;
if(b1HourList) {
load1HourList(_filteredItems);
} else {
foreach (RMVideoItem* item, _items) {
item->checked = false;
_filteredItems.append(item);
}
}
// N 시간 단위로 파일 추가
// QDateTime lastDateTime = QDateTime();
// foreach (RMVideoItem* item, _items) {
// if(b1Hour) {
// QDateTime currentTime = item->startTime();
// if(bSortDsc) {
// if(lastDateTime.isValid() == true && -lastDateTime.secsTo(currentTime) < 3600 ) {
// continue;
// }
// }
// else {
// if(lastDateTime.isValid() == true && lastDateTime.secsTo(currentTime) < 3600 ) {
// continue;
// }
// }
// }
// item->checked = false;
// _filteredItems.append(item);
// if(b1Hour) {
// lastDateTime = simpleFromDateTime(item->startTime());
// }
// }
setPlayItem(NULL);
if(bEmit) {
emit listUpdateEnd(false,findFirstItemIn1Hour(selected));
}
}
void RMVideoFileList::setSortList(bool bDsc)
{
bSortDsc = bDsc;
qSort(_items.begin(),_items.end(),RMVideoItemCompare);
}
#endif // #if (USE_1HOUR_FILTER)
/*
bool RMVideoFileList::_updateChannel(QString filePath,bool isCH2)
{
QString srcName = _checkChannelInfo(filePath,NULL);
foreach (RMVideoItem* item, _items)
{
QString destPath = isCH2 ? item->filePath : item->filePathCH2;
bool destIsCH2 = false;
QString destName = _checkChannelInfo(destPath,&destIsCH2);
if(srcName.compare(destName,Qt::CaseInsensitive) == 0 && isCH2 != destIsCH2)
{
if (isCH2 == true)
{
item->filePathCH2 = filePath;
}
else
{
item->filePath = filePath;
}
return true;
}
}
return false;
}
*/
#if !(USE_1HOUR_FILTER)
void RMVideoFileList::setFilterSingle(RMVideoFileList::FILTER filter)
{
_filter = filter;
_updateItemsByFilter();
}
void RMVideoFileList::setFilter(RMVideoFileList::FILTER filter, bool on)
{
if(on){
_filter = _filter | filter;
}
else {
_filter = _filter & ~filter;
}
_updateItemsByFilter();
}
void RMVideoFileList::clearChecked()
{
foreach (RMVideoItem* item, _items) {
item->checked = false;
}
}
void RMVideoFileList::_updateItemsByFilter()
{
emit listUpdateStarted(false);
_filteredItems.clear();
foreach (RMVideoItem* item, _items) {
// 전방이 없을 경우 강제 전방 표시
// Player 의 Rearswap 사용
// if(item->isSingleChannel() && item->filePath.isEmpty()) {
// item->filePath = item->filePathCH2;
// item->filePathCH2 = "";
// item->forceSwap = true;
// } else {
// item->forceSwap = false;
// }
item->checked = false;
RMVideoFileList::FILTER filter = filterTypeFromGroupType(item->type);
if(checkFilter(filter)) {
_filteredItems.append(item);
}
}
setPlayItem(NULL);
emit listUpdateEnd(false,NULL);
}
#endif // #if !(USE_1HOUR_FILTER)
// 모델별로 채널 확인해서 처리해야 하는 기능
bool RMVideoFileList::itemExist(QString filePath)
{
#if (RM_MODEL_EMT_KR)
QString search = filePath;
#else // RM_MODEL_EMT_KR
#if (FORCE_2CH || SINGLE_CH_VIEWER)
QString search = QFileInfo(filePath).baseName();
#else // FORCE_2CH
QString search = RMVideoItem::fileNameWithoutChannel(filePath);
#endif // FORCE_2CH
#endif // !RM_MODEL_EMT_KR
foreach (RMVideoItem* item, _items)
{
#if (RM_MODEL_EMT_KR)
// MYBOX 가 존재하니 단순 경로만 비교
if(search.compare(item->filePath,Qt::CaseInsensitive) == 0) {
return true;
}
#else // RM_MODEL_EMT_KR
#if (FORCE_2CH || SINGLE_CH_VIEWER)
QString compare = QFileInfo(item->anyFilePath()).baseName();
#else
QString compare = RMVideoItem::fileNameWithoutChannel(item->anyFilePath());
#endif
if(search.compare(compare,Qt::CaseInsensitive) == 0) {
return true;
}
#endif // !RM_MODEL_EMT_KR
}
return false;//_updateChannel(filePath,isCH2);
}
// 멀티파일(채널) 포멧 확인하고 날짜제외 제거하고 리턴
#if (RM_MODEL == RM_MODEL_TYPE_NX_DRW22)
QString RMVideoFileList::_checkChannelInfo(QString filePath,bool *isCH2)
{
QString baseName = QFileInfo(filePath).baseName();
if(isCH2 != NULL)
{
*isCH2 = baseName.endsWith("R",Qt::CaseInsensitive);
}
// 동일한 영상/전후방 비교용으로만 사용됨
baseName.truncate(baseName.length()-1);
return baseName;
}
// 전방 또는 후방 파일 확인해서 추가
bool RMVideoFileList::_addOtherChannelFile(RMVideoItem *item) {
QString filePath = item->anyFilePath();
QString fileFolder = QFileInfo(filePath).absolutePath();
bool isCH2 = false;
QString searchFileName;
QString baseName = RMVideoFileList::_checkChannelInfo(filePath,&isCH2);
// 탐색할 파일명
searchFileName = baseName + (isCH2 ? "F.mov" : "R.mov");
QStringList searchPaths = QStringList() << QDir::cleanPath((fileFolder + QDir::separator() + searchFileName));
// 폴더의 폴더
QString fileFolderFolder = QFileInfo(fileFolder).baseName();
if(fileFolderFolder == "F" || fileFolderFolder == "R") {
QString searchFolder = (fileFolderFolder == "F" ? "R" : "F");
searchPaths << (QDir::cleanPath(QFileInfo(fileFolder).absolutePath() + QDir::separator() + searchFolder + QDir::separator() + searchFileName));
}
// 탐색폴더 확인
foreach (QString path, searchPaths) {
if( QFile::exists(path) ) {
if(isCH2) {
item->filePath = path;
}
else {
item->filePathCH2 = path;
}
return true;
}
}
return false;
}
QString RMVideoFileList::groupTypeFromFilePath(QString filePath,RMVideoFileList::GROUP_TYPE* type)
{
QString cleanPath = filePath.toUpper();
QString baseName = QFileInfo(cleanPath).baseName();
// FILE200713-101324-000001F.MOV
// EMER200713-101525-000003F.MOV
QRegExp regexp("^[F,E][I,M][L,E][E,R][0-9]{6}-[0-9]{6}-[0-9]{6}[F,R]");
if(!regexp.exactMatch(baseName)) {
*type = TYPE_UNDEFINED;
return "";
}
if(baseName.startsWith("F")) {
*type = TYPE_NORMAL;
}
else {
*type = TYPE_EVENT;
}
return baseName.mid(4,13);
}
#elif (RM_MODEL == RM_MODEL_TYPE_MH9000)
QString RMVideoFileList::groupTypeFromFilePath(QString filePath,RMVideoFileList::GROUP_TYPE* type)
{
QString baseName = QFileInfo(QDir::cleanPath(filePath)).baseName();
QRegExp regexp("^(:?NORM|EVEN|PARK|MANU)[0-9]{6}-[0-9]{6}(:?FR|LS|RR|RS|IN)"); // EX->IN 으로 변경
regexp.setCaseSensitivity(Qt::CaseInsensitive);
// 240523-174742
if(regexp.exactMatch(baseName)) {
if(baseName.startsWith("EVEN")) {
*type = TYPE_EVENT;
}
else if(baseName.startsWith("PARK")) {
*type = TYPE_PARKING;
}
else if(baseName.startsWith("MANU")) {
*type = TYPE_MANUAL;
}else {
*type = TYPE_NORMAL;
}
}
return baseName;
}
QString RMVideoFileList::_checkChannelInfo(QString filePath,int *ch)
{
QString baseName = QFileInfo(filePath).baseName();
QRegExp regexp("^(:?NORM|EVEN|PARK|MANU)[0-9]{6}-[0-9]{6}(:?FR|LS|RR|RS|IN)"); // EX->IN 으로 변경
if(regexp.exactMatch(baseName)) {
if(baseName.endsWith("FR")) {
*ch = (int)RMApp::ChannelFront;
}
else if(baseName.endsWith("RR")) {
*ch = (int)RMApp::ChannelRear;
}
else if(baseName.endsWith("LS")) {
*ch = (int)RMApp::ChannelLeft;
}
else if(baseName.endsWith("RS")) {
*ch = (int)RMApp::ChannelRight;
}
else if(baseName.endsWith("IN")) {
*ch = (int)RMApp::ChannelSub;
}
}
// 동일한 영상/전후방 비교용으로만 사용됨
baseName.truncate(baseName.length()-2);
return baseName;
}
bool RMVideoFileList::_addOtherChannelFile(RMVideoItem *item) {
QString filePath = item->anyFilePath();
QString fileFolder = QFileInfo(filePath).absolutePath();
// 탐색폴더 확인
bool found = false;
int ch = -1;
QString baseName = RMVideoFileList::_checkChannelInfo(filePath,&ch);
if(ch >= 0 && ch <= 4) {
//QStringList searchFileNames = QStringList();
QStringList searchFileNames = QStringList() << "FR.mp4" << "RR.mp4" << "LS.mp4" << "RS.mp4" << "IN.mp4"; // EX->IN
// RR 이 먼저 확인되어도 모두 처리가능하도록 변경
// searchFileNames.removeAt(ch);
foreach (QString path, searchFileNames) {
//qInfo() << baseName << path << __FUNCTION__;
QString fullPath = QDir::cleanPath((fileFolder + QDir::separator() + baseName + path));
if( QFile::exists(fullPath) ) {
int fc = 0;
RMVideoFileList::_checkChannelInfo(fullPath,&fc);
if(fc == 0) {
item->filePath = fullPath;
}
else if (fc == 1) {
item->filePathCH2 = fullPath;
}
else if (fc == 2) {
item->filePathCH3 = fullPath;
}
else if (fc == 3) {
item->filePathCH4 = fullPath;
}
else if (fc == 4) {
item->filePathCH5 = fullPath;
}
found = true;
}
}
}
return found;
}
#elif (RM_MODEL_EMT_KR)
RMVideoFileList::GROUP_TYPE RMVideoFileList::parseName(QString baseName, QDateTime* dateTime, int* tag)
{
if(tag != NULL) {
*tag = 0;
}
if(dateTime != NULL) {
dateTime->swap(QDateTime()); // INVALID
}
// 상시폴더
// 250610_18h14m35s_Driving_sde.MP4 (일본향 sdexit, 파일 생성시)
// 250610_18h02m35s_Driving.MP4 (정상 종료)
// 250610_18h00m30s_Driving_res.MP4 (복구 파일)
// 상시 이벤트
// 250610_18h18m29s_EventShock.MP4 (상시 이벤트)
// 매뉴얼 이벤트
// 250610_18h17m36s_Manual.MP4 (매뉴얼 이벤트 )
// 주차폴더
// 250610_18h18m09s_Parking.MP4
// 250610_18h18m09s_Parkin_htp.MP4 (고온종료)
// 250610_18h18m09s_Parkin_lbp.MP4 (차단전압)"
// ParkingShock 이 Parking 보다 먼저 사용되어야 함
static QRegularExpression rx("^(?P<ymd>\\d{6})_(?P<hour>\\d{2})h(?P<minutes>\\d{2})m(?P<second>\\d{2})s_(?P<type>Driving|EventShock|EventAI|Manual|ParkingShock|Parking)(?:_(?P<tag>res|htp|lbp|RC|CD))?",QRegularExpression::CaseInsensitiveOption);
QRegularExpressionMatch match = rx.match(baseName);
if (!match.isValid()) {
return TYPE_UNDEFINED;
}
// 날짜 처리
if(dateTime != NULL) {
QString dateString = "20" + match.captured("ymd") + "_" + match.captured("hour") + match.captured("minutes") + match.captured("second");
dateTime->swap(QDateTime::fromString(dateString,"yyyyMMdd_HHmmss"));
}
// 태그 처리
if(tag != NULL && !match.captured("tag").isEmpty()) {
QString tagString = match.captured("tag").toLower();
// res = 1|htp = 2|lbp = 3
if(tagString == "res") {
*tag = 1;
}
else if(tagString == "htp") {
*tag = 2;
}
else if(tagString == "lbp") {
*tag = 3;
}
else if(tagString == "rc") { //후방추돌
*tag = 4;
}
else if(tagString == "cd") { // 끼어들기
*tag = 5;
}
}
QString typeString = match.captured("type").toLower();
//qInfo() << baseName << typeString << match.captured("tag") << __FUNCTION__;
if(typeString == "driving") {
return TYPE_NORMAL;
} else if (typeString == "eventshock" || typeString == "eventai") {
return TYPE_EVENT;
} else if (typeString == "manual") {
return TYPE_MANUAL;
} else if (typeString == "parking") {
return TYPE_PARKING;
} else if (typeString == "parkingshock") {
return TYPE_PARKING_EVENT;
}
return TYPE_UNDEFINED;
}
QString RMVideoFileList::groupTypeFromFilePath(QString filePath,RMVideoFileList::GROUP_TYPE* type)
{
// 20250528_13h42m07s_Driving
QString baseName = QFileInfo(QDir::cleanPath(filePath)).baseName();
*type = parseName(baseName,NULL,NULL);
QString folderName = QFileInfo(filePath).dir().dirName().toUpper();
if(folderName == "MYBOX") {
*type = RMVideoFileList::TYPE_MY_BOX;
}
return baseName;
}
#elif (RM_MODEL == RM_MODEL_TYPE_TB4000)
QString RMVideoFileList::groupTypeFromFilePath(QString filePath,RMVideoFileList::GROUP_TYPE* type)
{
QString baseName = QFileInfo(QDir::cleanPath(filePath)).baseName();
// 20230815-070258_REC_0000.mp4
*type = TYPE_NORMAL;
return baseName;
}
#elif (RM_MODEL == RM_MODEL_TYPE_KEIYO1 || RM_MODEL == RM_MODEL_TYPE_MBJ5010 || RM_MODEL == RM_MODEL_TYPE_FC_DR232W || RM_MODEL == RM_MODEL_TYPE_BV2000)
#if !(FORCE_2CH)
#if (TRI_CHANNEL)
QString RMVideoFileList::_checkChannelInfo(QString filePath,int *ch)
#else
QString RMVideoFileList::_checkChannelInfo(QString filePath,bool *isCH2, bool *is2CH)
#endif
{
QString baseName = QFileInfo(filePath).baseName();
#if (TRI_CHANNEL)
if(ch != NULL)
{
if(baseName.endsWith("R",Qt::CaseInsensitive)) {
*ch = 2;
}
else if(baseName.endsWith("I",Qt::CaseInsensitive)) {
*ch = 3;
}
else {
*ch = 1;
}
}
#else // TRI_CHANNEL
if(isCH2 != NULL)
{
if(baseName.endsWith("R",Qt::CaseInsensitive)) {
*isCH2 = true;
}
else {
*isCH2 = false;
}
}
#endif // #if (TRI_CHANNEL)
#if (SUPPORT_2CH)
if(is2CH != NULL)
{
*is2CH = baseName.endsWith("W",Qt::CaseInsensitive);
}
#endif // SUPPORT_2CH
// 동일한 영상/전후방 비교용으로만 사용됨
baseName.truncate(baseName.length()-1);
return baseName;
}
// 전방 또는 후방 파일 확인해서 추가
bool RMVideoFileList::_addOtherChannelFile(RMVideoItem *item) {
QString filePath = item->anyFilePath();
QString fileFolder = QFileInfo(filePath).absolutePath();
#if (TRI_CHANNEL)
int ch;
QString baseName = RMVideoFileList::_checkChannelInfo(filePath,&ch);
QStringList searchFileNames = QStringList();
if (ch == 1) {
searchFileNames << "R.mp4" << "I.mp4";
}
else if (ch == 2) {
searchFileNames << "F.mp4" << "I.mp4";
}
else if (ch == 3) {
searchFileNames << "F.mp4" << "R.mp4";
}
//QStringList searchPaths = QStringList() << QDir::cleanPath((fileFolder + QDir::separator() + searchFileName));
// 탐색폴더 확인
bool found = false;
foreach (QString path, searchFileNames) {
QString fullPath = QDir::cleanPath((fileFolder + QDir::separator() + baseName + path));
if( QFile::exists(fullPath) ) {
int fc = 0;
RMVideoFileList::_checkChannelInfo(fullPath,&fc);
if(fc == 1) {
item->filePath = fullPath;
}
else if (fc == 2) {
item->filePathCH2 = fullPath;
}
else if (fc == 3) {
item->filePathCH3 = fullPath;
}
found = true;
}
}
return found;
#else
QString searchFileName;
bool isCH2 = false;
QString baseName = RMVideoFileList::_checkChannelInfo(filePath,&isCH2);
// 탐색할 파일명
searchFileName = baseName + (isCH2 ? "F.mp4" : "R.mp4");
QStringList searchPaths = QStringList() << QDir::cleanPath((fileFolder + QDir::separator() + searchFileName));
// 폴더의 폴더
// QString fileFolderFolder = QFileInfo(fileFolder).baseName();
// if(fileFolderFolder == "F" || fileFolderFolder == "R") {
// QString searchFolder = (fileFolderFolder == "F" ? "R" : "F");
// searchPaths << (QDir::cleanPath(QFileInfo(fileFolder).absolutePath() + QDir::separator() + searchFolder + QDir::separator() + searchFileName));
// }
// 탐색폴더 확인
foreach (QString path, searchPaths) {
if( QFile::exists(path) ) {
if(isCH2) {
item->filePath = path;
}
else {
item->filePathCH2 = path;
}
return true;
}
}
return false;
#endif
}
#endif // #if !(FORCE_2CH)
QString RMVideoFileList::groupTypeFromFilePath(QString filePath,RMVideoFileList::GROUP_TYPE* type)
{
QString cleanPath = filePath.toUpper();
QString baseName = QFileInfo(cleanPath).baseName();
#if (SUB_MODEL_KEIYO_360)
// NORM230517-104840I.MP4
QRegExp regexp("^(NORM|EVEN)[0-9]{6}-[0-9]{6}[I,W]");
if(!regexp.exactMatch(baseName)) {
*type = TYPE_UNDEFINED;
return "";
}
if(baseName.startsWith("NORM")) {
*type = TYPE_NORMAL;
}
else if (baseName.startsWith("EVEN")) {
*type = TYPE_EVENT;
}
return baseName.mid(4,6+1+6);
#else // #if (SUB_MODEL_KEIYO_360)
// NORM201029-085403F
// EVEN201029-085721F
// MANU201029-090032F
// PARK201029-091752F
// PEVE190116-173653F.MP4 // 주차 이벤트
// PNOR190120-211045F.MP4 // 주차 상시
#if (SUPPORT_2CH || FORCE_2CH)
QRegExp regexp("^[N,E,M,P][O,V,A,E,N][R,E,N,V,O][M,N,U,K,E,R][0-9]{6}-[0-9]{6}[F,R,W]");
#else
QRegExp regexp("^[N,E,M,P][O,V,A,E,N][R,E,N,V,O][M,N,U,K,E,R][0-9]{6}-[0-9]{6}[F,R]");
#endif
if(!regexp.exactMatch(baseName)) {
*type = TYPE_UNDEFINED;
return "";
}
if(baseName.startsWith("NORM")) {
*type = TYPE_NORMAL;
}
else if (baseName.startsWith("EVEN")) {
*type = TYPE_EVENT;
}
else if (baseName.startsWith("MANU")) {
*type = TYPE_MANUAL;
}
else if (baseName.startsWith("PARK")) {
*type = TYPE_PARKING;
}
else if (baseName.startsWith("PNOR")) { // 벤츠2
*type = TYPE_PARKING;
}
else if (baseName.startsWith("PEVE")) { // 벤츠2
*type = TYPE_PARKING_EVENT;
}
else {
*type = TYPE_NORMAL;
}
return baseName.mid(4,13);
#endif // #else // #if (SUB_MODEL_KEIYO_360)
}
#elif (RM_MODEL == RM_MODEL_TYPE_ADT_CAPS && !SUB_MODEL_CARROT_EMT)
QString RMVideoFileList::_checkChannelInfo(QString filePath,bool *isCH2, bool *is2CH)
{
QString baseName = QFileInfo(filePath).baseName();
if(isCH2 != NULL)
{
*isCH2 = baseName.contains("_R_",Qt::CaseInsensitive);
}
// 동일한 영상/전후방 비교용으로만 사용됨
baseName = baseName.replace("_R_","_#CH#_").replace("_F_","_#CH#_");
return baseName;
}
bool RMVideoFileList::_addOtherChannelFile(RMVideoItem *item) {
QString filePath = item->anyFilePath();
QString fileFolder = QFileInfo(filePath).absolutePath();
bool isCH2 = false;
QString searchFileName;
QString baseName = RMVideoFileList::_checkChannelInfo(filePath,&isCH2);
// 탐색할 파일명
searchFileName = baseName.replace("_#CH#_",isCH2 ? "_F_" : "_R_") + "." + FILE_EXT;
QStringList searchPaths = QStringList() << QDir::cleanPath((fileFolder + QDir::separator() + searchFileName));
// 폴더의 폴더
QString fileFolderFolder = QFileInfo(fileFolder).baseName();
if(fileFolderFolder == "1" || fileFolderFolder == "2") {
QString searchFolder = (fileFolderFolder == "1" ? "2" : "1");
searchPaths << (QDir::cleanPath(QFileInfo(fileFolder).absolutePath() + QDir::separator() + searchFolder + QDir::separator() + searchFileName));
}
// 탐색폴더 확인
foreach (QString path, searchPaths) {
if( QFile::exists(path) ) {
if(isCH2) {
item->filePath = path;
}
else {
item->filePathCH2 = path;
}
}
}
return true;
}
QString RMVideoFileList::groupTypeFromFilePath(QString filePath,RMVideoFileList::GROUP_TYPE* type)
{
QString cleanPath = filePath.toUpper();
QString baseName = QFileInfo(cleanPath).baseName();
// 2020-06-12-17h-10m-05s_F_normal.mp4
QRegExp regexp("^[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}H-[0-9]{2}M-[0-9]{2}S_[F,R]_[A-Z]*");
if(!regexp.exactMatch(baseName)) {
*type = TYPE_UNDEFINED;
return "";
}
if(baseName.endsWith("_NORMAL",Qt::CaseInsensitive)) {
*type = TYPE_NORMAL;
}
else {
*type = TYPE_EVENT;
}
baseName.truncate(22);
// 2020-06-12-17H-10M-05S
return baseName;
}
#elif (RM_MODEL == RM_MODEL_TYPE_XLDR_88 || SUB_MODEL_CARROT_EMT)
QString RMVideoFileList::groupTypeFromFilePath(QString filePath,RMVideoFileList::GROUP_TYPE* type)
{
QString cleanPath = filePath.toUpper();
QString baseName = QFileInfo(cleanPath).baseName();
// S200727_110830FE_Exit
// S200526_110830FP
// S200727_100830FE_Exit
// S200827_100830FN_Exit
// 2021/07/21 추가
// S210217_001749FN_TEMP
// + _TEMP,_RCVR,_MENU,_LBAT
// S210217_001548FPE : 주차 충격
QRegExp regexp("^[S][0-9]{6}_[0-9]{6}[F][P,E,N,PE][_EXIT,_TEMP,_RCVR,_MENU,_LBAT,_PKTM]*"); //
if(!regexp.exactMatch(baseName)) {
qInfo() << "FILE ERROR:" << baseName << __FUNCTION__ << __LINE__;
*type = TYPE_UNDEFINED;
return "";
}
QString typeString = baseName.mid(14,2).toUpper();
if(typeString == "FN") {
*type = TYPE_NORMAL;
}
else if(typeString == "FP") {
if (baseName.length() > 16 && baseName.mid(16,1).toUpper() == "E") { // FPE (PARKING EVENT)
*type = TYPE_PARKING; // 주차 충격은 주차로 이동
}
else {
*type = TYPE_PARKING;
}
}
else {
*type = TYPE_EVENT;
}
// qInfo() << filePath << "TYPE:" << *type;
return baseName.mid(1,13);
}
#elif (RM_MODEL == RM_MODEL_TYPE_AN6000)
int RMVideoFileList::parseSerial(QString baseName)
{
// 2023-12-31-07h-14m-07s_normal_1(1) 모두 처리
static QRegularExpression rx("^(?P<datetime>\\d{4}-\\d{2}-\\d{2}-\\d{2}h-\\d{2}m-\\d{2}s)_normal(_(?P<index>\\d{1,2}))?(\\((?P<index2>\\d{1,2})\\))?",QRegularExpression::CaseInsensitiveOption);
QRegularExpressionMatch match = rx.match(baseName);
if (!match.isValid()) {
return -1;
}
// 날짜 처리 -> 필요없음
//if(0) {
//dateTime->swap(QDateTime::fromString(match.captured("datetime"),"yyyy-MM-dd-HH'h'-mm'm'-ss's'"));
//}
int serial = 0;
if(!match.captured("index").isEmpty()) { // _1
serial = match.captured("index").toInt();
}
if(!match.captured("index2").isEmpty()) { // _1(2)
serial += match.captured("index2").toInt() + 100;
}
return serial;
}
QString RMVideoFileList::groupTypeFromFilePath(QString filePath,RMVideoFileList::GROUP_TYPE* type, int* serial)
{
QString cleanPath = filePath.toUpper();
QString baseName = QFileInfo(cleanPath).baseName();
*serial = parseSerial(baseName);
if(*serial < 0) {
*type = TYPE_UNDEFINED;
return "";
}
if(baseName.contains("_NORMAL",Qt::CaseInsensitive)) {
*type = TYPE_NORMAL;
} else {
*type = TYPE_EVENT;
}
baseName.truncate(22);
return baseName;
/*
// 2020-06-12-17h-10m-05s_F_normal.mp4
// 2020-06-12-17h-10m-05s_F_normal(1).mp4 2024/09/24 추가
// 2020-06-12-17h-10m-05s_F_normal_1.mp4 2026/09/06 추가
// 2020-06-12-17h-10m-05s_F_normal_1(1).mp4 2026/09/06 추가
// QRegExp 는 '(' 문자는 [\(] 로만 처리됨
QRegExp regexp("^[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}H-[0-9]{2}M-[0-9]{2}S_NORMAL([\(]{1}[1-9]{1}[\)]{1})?");
regexp.setCaseSensitivity(Qt::CaseInsensitive);
if(!regexp.exactMatch(baseName)) {
//qInfo() << "NO GROUP MATCH:" << baseName << __FUNCTION__;
*type = TYPE_UNDEFINED;
return "";
}
//qInfo() << baseName << __FUNCTION__;
// (1)~(9) 처리
if(baseName.endsWith(")")) {
QStringList lst = baseName.split("(");
QString number = lst.last().replace(")","");
if(serial != NULL) {
*serial = number.toInt();
}
baseName = lst.first();
//qInfo() << "BASENAME:" << baseName << number << __FUNCTION__;
}
if(baseName.endsWith("_NORMAL",Qt::CaseInsensitive)) {
*type = TYPE_NORMAL;
}
else {
*type = TYPE_EVENT;
}
baseName.truncate(22);
// 2020-06-12-17H-10M-05S
return baseName;
*/
} // RM_MODEL_TYPE_AN6000
#elif (RM_MODEL == RM_MODEL_TYPE_XLDR_88 || SUB_MODEL_CARROT_EMT)
QString RMVideoFileList::groupTypeFromFilePath(QString filePath,RMVideoFileList::GROUP_TYPE* type)
{
QString cleanPath = filePath.toUpper();
QString baseName = QFileInfo(cleanPath).baseName();
// S200727_110830FE_Exit
// S200526_110830FP
// S200727_100830FE_Exit
// S200827_100830FN_Exit
// 2021/07/21 추가
// S210217_001749FN_TEMP
// + _TEMP,_RCVR,_MENU,_LBAT
// S210217_001548FPE : 주차 충격
QRegExp regexp("^[S][0-9]{6}_[0-9]{6}[F][P,E,N,PE][_EXIT,_TEMP,_RCVR,_MENU,_LBAT,_PKTM]*"); //
if(!regexp.exactMatch(baseName)) {
qInfo() << "FILE ERROR:" << baseName << __FUNCTION__ << __LINE__;
*type = TYPE_UNDEFINED;
return "";
}
QString typeString = baseName.mid(14,2).toUpper();
if(typeString == "FN") {
*type = TYPE_NORMAL;
}
else if(typeString == "FP") {
if (baseName.length() > 16 && baseName.mid(16,1).toUpper() == "E") { // FPE (PARKING EVENT)
*type = TYPE_PARKING; // 주차 충격은 주차로 이동
}
else {
*type = TYPE_PARKING;
}
}
else {
*type = TYPE_EVENT;
}
// qInfo() << filePath << "TYPE:" << *type;
return baseName.mid(1,13);
}
#elif (RM_MODEL == RM_MODEL_TYPE_TBD360)
#if (!FORCE_2CH || (RM_MODEL == RM_MODEL_TYPE_XLDR_88 || SUB_MODEL_CARROT_EMT))
QString RMVideoFileList::_checkChannelInfo(QString filePath,bool *isCH2, bool *is2CH)
{
QString baseName = QFileInfo(filePath).baseName();
if(isCH2 != NULL)
{
*isCH2 = baseName.contains("_R_",Qt::CaseInsensitive);
}
// 동일한 영상/전후방 비교용으로만 사용됨
baseName = baseName.replace("_R_","_#CH#_").replace("_F_","_#CH#_");
return baseName;
}
bool RMVideoFileList::_addOtherChannelFile(RMVideoItem *item) {
QString filePath = item->anyFilePath();
QString fileFolder = QFileInfo(filePath).absolutePath();
bool isCH2 = false;
QString searchFileName;
QString baseName = RMVideoFileList::_checkChannelInfo(filePath,&isCH2);
// 탐색할 파일명
searchFileName = baseName.replace("_#CH#_",isCH2 ? "_F_" : "_R_") + "." + FILE_EXT;
QStringList searchPaths = QStringList() << QDir::cleanPath((fileFolder + QDir::separator() + searchFileName));
// 폴더의 폴더
QString fileFolderFolder = QFileInfo(fileFolder).baseName();
if(fileFolderFolder == "Front" || fileFolderFolder == "Rear") {
QString searchFolder = (fileFolderFolder == "Front" ? "Rear" : "Front");
searchPaths << (QDir::cleanPath(QFileInfo(fileFolder).absolutePath() + QDir::separator() + searchFolder + QDir::separator() + searchFileName));
}
// 탐색폴더 확인
foreach (QString path, searchPaths) {
if( QFile::exists(path) ) {
if(isCH2) {
item->filePath = path;
}
else {
item->filePathCH2 = path;
}
return true;
}
}
return false;
}
#endif // #if (!FORCE_2CH || (RM_MODEL == RM_MODEL_TYPE_XLDR_88 || SUB_MODEL_CARROT_EMT))
QString RMVideoFileList::groupTypeFromFilePath(QString filePath,RMVideoFileList::GROUP_TYPE* type)
{
QString cleanPath = filePath.toUpper();
QString baseName = QFileInfo(cleanPath).baseName();
#if (TANDF_360_TEST)
// NNF_210212-101925-000055_1.MP4
QRegExp regexp("^[A-Z]{3}_[0-9]{6}-[0-9]{6}-[0-9]{6}_[1,2]{1}");
if(!regexp.exactMatch(baseName)) {
*type = TYPE_UNDEFINED;
return "";
}
*type = TYPE_NORMAL;
// NNF_210212-101925-000055_1
return baseName.mid(4,6+1+6);
#elif (THINKWARE_DEMO)
// 20231102_141152E .MP4
QRegExp regexp("^[0-9]{8}_[0-9]{6}[E,M,N]{1}");
if(!regexp.exactMatch(baseName)) {
*type = TYPE_UNDEFINED;
return "";
}
*type = TYPE_NORMAL;
// NNF_210212-101925-000055_1
return baseName.mid(0,15);
#elif (TEST_FILENAME)
QRegExp regexp("^TEST_[0-9]{8}-[0-9]{6}");
if(!regexp.exactMatch(baseName)) {
*type = TYPE_UNDEFINED;
return "";
}
*type = TYPE_NORMAL;
return baseName.mid(5,8+1+6);
#elif (MH_360_TEST)
// NORM230517-104840I.MP4
QRegExp regexp("^NORM[0-9]{6}-[0-9]{6}I");
if(!regexp.exactMatch(baseName)) {
*type = TYPE_UNDEFINED;
return "";
}
*type = TYPE_NORMAL;
return baseName.mid(4,6+1+6);
#else // TANDF_360_TEST
// 20201208_215045_F_Nor.AVI
// 20201208_151241_F_Gsn.AVI
QRegExp regexp("^[0-9]{8}_[0-9]{6}_[F,R]_[A-z]*");
if(!regexp.exactMatch(baseName)) {
*type = TYPE_UNDEFINED;
return "";
}
if(baseName.endsWith("_Nor",Qt::CaseInsensitive)) {
*type = TYPE_NORMAL;
}
else if(baseName.endsWith("_Gsn",Qt::CaseInsensitive)) {
*type = TYPE_EVENT;
}
else {
*type = TYPE_NORMAL;
}
baseName.truncate(15);
// 20201208_151241
#endif // TANDF_360_TEST
return baseName;
}
#endif // MODEL