first commit

This commit is contained in:
2026-02-21 17:11:31 +09:00
commit 18b4338361
4001 changed files with 365464 additions and 0 deletions

View File

@@ -0,0 +1,207 @@
#include "fm_base64.h"
#if (ENCODE_CFG_BASE64)
#include <QFile>
static const unsigned char base64_table[65] = "0123456789+/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; // Changed Code...
#define BASE64_MARK_SIZE 4
static const unsigned char base64_start_mark[BASE64_MARK_SIZE] = { 0x1B, 0x9B, 0x1B, 0x9C };
static const unsigned char base64_end_mark[BASE64_MARK_SIZE] = { 0x1B, 0x9C, 0x1B, 0x9D };
/* base64_encode - Base64 encode */
uint8_t* FMBase64::mg_base64_encode(const uint8_t *src, uint32_t len, uint32_t *out_len)
{
uint8_t *out, *pos;
const uint8_t *end, *in;
uint32_t olen;
int line_len;
olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
olen += olen / 72; /* line feeds */
olen++; /* nul termination */
if (olen < len) return NULL; /* integer overflow */
out = (uint8_t*)malloc(olen+(BASE64_MARK_SIZE*2));
memset(out,0,olen+(BASE64_MARK_SIZE*2));
if (out == NULL) return NULL;
end = src + len;
in = src;
pos = out;
line_len = 0;
memcpy((void*)pos, base64_start_mark, BASE64_MARK_SIZE);
pos += BASE64_MARK_SIZE;
while (end - in >= 3) {
*pos++ = base64_table[in[0] >> 2];
*pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
*pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
*pos++ = base64_table[in[2] & 0x3f];
in += 3;
line_len += 4;
if (line_len >= 72) {
*pos++ = '\n';
line_len = 0;
}
}
if (end - in) {
*pos++ = base64_table[in[0] >> 2];
if (end - in == 1) {
*pos++ = base64_table[(in[0] & 0x03) << 4];
*pos++ = '=';
} else {
*pos++ = base64_table[((in[0] & 0x03) << 4) |
(in[1] >> 4)];
*pos++ = base64_table[(in[1] & 0x0f) << 2];
}
*pos++ = '=';
line_len += 4;
}
if (line_len) *pos++ = '\n';
memcpy((void*)pos, base64_end_mark, BASE64_MARK_SIZE);
pos += BASE64_MARK_SIZE;
*pos = '\0';
if (out_len) *out_len = pos - out;
return out;
}
/* base64_decode - Base64 decode */
uint8_t* FMBase64::mg_base64_decode(uint8_t *src, uint32_t len, uint32_t *out_len)
{
uint8_t dtable[256], *out, *pos, block[4], tmp;
uint32_t i, count, olen;
int pad = 0;
if(src == NULL || len <= BASE64_MARK_SIZE*2) return NULL;
if(memcmp((void*)&src[0], base64_start_mark, BASE64_MARK_SIZE) ||
memcmp((void*)&src[len-BASE64_MARK_SIZE], base64_end_mark, BASE64_MARK_SIZE)) {
return NULL;
}
src += BASE64_MARK_SIZE;
len -= BASE64_MARK_SIZE*2;
memset((void*)dtable, 0x80, 256);
for (i = 0; i < sizeof(base64_table) - 1; i++)
dtable[base64_table[i]] = (uint8_t) i;
dtable['='] = 0;
count = 0;
for (i = 0; i < len; i++) {
if (dtable[src[i]] != 0x80) count++;
}
if (count == 0 || count % 4) return NULL;
olen = count / 4 * 3;
//qInfo() << olen << olen + (olen % 2048) << __FUNCTION__;
pos = out = (uint8_t*)malloc(qMax(olen,(uint32_t)4096));
memset(out,0,olen);
if (out == NULL) return NULL;
count = 0;
for (i = 0; i < len; i++) {
tmp = dtable[src[i]];
if (tmp == 0x80) continue;
if (src[i] == '=') pad++;
block[count] = tmp;
count++;
if (count == 4) {
*pos++ = (block[0] << 2) | (block[1] >> 4);
*pos++ = (block[1] << 4) | (block[2] >> 2);
*pos++ = (block[2] << 6) | block[3];
count = 0;
if (pad) {
if (pad == 1) pos--;
else if (pad == 2) pos -= 2;
else {
/* Invalid padding */
free((void*)out);
return NULL;
}
break;
}
}
}
*out_len = pos - out;
return out;
}
// 파일 확인하여 텍스트 스트림으로 처리
QStringList FMBase64::load(QString path, bool* isEncoded)
{
QFile file(path);
if(file.exists() && file.open(QIODevice::ReadOnly))
{
uint32_t len = (uint32_t)(file.size() + (file.size() % 8));
uint8_t* buffer = (uint8_t*)malloc(len);
memset(buffer,0,len);
file.read((char*)buffer,file.size());
uint8_t* text = buffer;
// MARK 확인 , 0x9C, 0x1B, 0x9D
if(buffer[0] == base64_start_mark[0] &&
buffer[1] == base64_start_mark[1] &&
buffer[2] == base64_start_mark[2] &&
buffer[3] == base64_start_mark[3] &&
buffer[file.size()-4] == base64_end_mark[0] &&
buffer[file.size()-3] == base64_end_mark[1] &&
buffer[file.size()-2] == base64_end_mark[2] &&
buffer[file.size()-1] == base64_end_mark[3]) {
*isEncoded = true;
uint32_t outlen = 0;
text = mg_base64_decode(buffer, file.size(),&outlen);
text[outlen] = 0;
} else {
*isEncoded = false;
}
QString lines = QString::fromLatin1((char*)text);
file.close();
if(buffer != text) {
free(text);
}
free(buffer);
return lines.split(QRegExp("[\r\n]"),QString::SkipEmptyParts);
}
return QStringList();
}
void FMBase64::save(QString path, QStringList lines, bool isEncoded)
{
// \r\n
QString str = QString();
for(int i=0;i<lines.size();i++) {
str += lines.at(i);
str += "\r\n";
}
QByteArray ba = str.toLatin1();
const uint8_t * src = (uint8_t*)ba.data();
uint8_t* dest = NULL;
uint32_t out_len = ba.size();
if(true) { // isEncoded) {
dest = mg_base64_encode(src, ba.size(),&out_len);
} else {
size_t ss = ba.size() + (ba.size() % 8);
dest = (uint8_t*)malloc(ss);
memset(dest,0,ss);
memcpy(dest,ba.data(),ba.size());
}
QFile file(path);
if (file.open(QIODevice::WriteOnly)) {
file.write((char*)dest,out_len);
file.close();
}
free(dest);
}
#endif // #if (ENCODE_CFG_BASE64)

View File

@@ -0,0 +1,20 @@
#ifndef FM_BASE64_H
#define FM_BASE64_H
#include "../rm_include.h"
#if (ENCODE_CFG_BASE64)
#if (RM_MODEL == RM_MODEL_TYPE_AN6000)
#include <inttypes.h>
class FMBase64
{
public:
static QStringList load(QString path, bool* isEncoded);
static void save(QString path, QStringList lines, bool isEncoded);
private:
static uint8_t* mg_base64_encode(const uint8_t *src, uint32_t len, uint32_t *out_len);
static uint8_t* mg_base64_decode(uint8_t *src, uint32_t len, uint32_t *out_len);
};
#endif // #if (RM_MODEL == RM_MODEL_TYPE_AN6000)
#endif // ENCODE_CFG_BASE64
#endif // FM_BASE64_H

View File

@@ -0,0 +1,19 @@
echo off
REM set /p id="IN Model: "
REM set /p id="OUT Model: "
REM set IN_MODEL=%1
REM set OUT_MODEL=%2
REM CALL :make_model 51fr,tr41fw
REM CALL :make_model 51fr,tzd201
REM CALL :make_model 61fh,tr61fw
REM CALL :make_model 91fh,tzd202fw
REM CALL :make_model 91fh,tzd203fw
:make_model
copy /y rm_settings_cfg_%~1.h rm_settings_cfg_%~2.h
copy /y rm_settings_cfg_%~1.cpp rm_settings_cfg_%~2.cpp
copy /y window_settings_%~1.h window_settings_%~2.h
copy /y window_settings_%~1.cpp window_settings_%~2.cpp
EXIT /B 0

View File

@@ -0,0 +1,227 @@
#include "rm_admin_passwd.h"
#if (USE_ADMIN_PW_SETTINGS)
#include "rm_include.h"
#include <QStyleOption>
#include <QPainter>
#include <QLabel>
#include <QDebug>
#include <QCheckBox>
#include <QDateTime>
#include <QDir>
#include <QFile>
#include <QLineEdit>
#include "../ui/rm_button.h"
#include "rm_radio_buttons.h"
#include "rm_settings_window_base.h"
#include "../ui/rm_widget_checkbox.h"
RMSettingAdminPW::RMSettingAdminPW(unsigned char* data, QList<int> values, QWidget *parent) : QGroupBox(parent)
{
dataP = data;
setFixedHeight(120);
setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
setObjectName("settings");
// 管理者PW
//setTitle(MKU8("\xe7\xae\xa1\xe7\x90\x86\xe8\x80\x85\x50\x57"));
setTitle(FM_WSTR(L"暗証番号"));
layout = new QVBoxLayout(this);
layout->setAlignment(Qt::AlignVCenter | Qt::AlignLeading);
layout->setContentsMargins(4,2,4,2);
layout->setSpacing(2);
//QSpacerItem* space = new QSpacerItem(0,10);
//layout->addSpacerItem(space);
// 管理者PW保存
#if !(REMOVE_ADMIN_PW_CHECKBOX)
saveCheckbox = new RMWidgetCheckBox(this);
//#if (LIVE_LANGUAGE_CHANGE)
// if(RMLanguage::isJP() == false)
// {
// check->setText("PC Time Sync.");
// }
// else {
//#else
//#endif // LIVE_LANGUAGE_CHANGE
//#if (LIVE_LANGUAGE_CHANGE)
// }
//#endif
saveCheckbox->setText(MKU8("\xe7\xae\xa1\xe7\x90\x86\xe8\x80\x85\x50\x57\xe4\xbf\x9d\xe5\xad\x98"));
#else
// ナイトビジョン
const int lbWidth = 150;
const int rWidth = 360;
//RMRadioButtons* rb;
//QLabel* tl = new QLabel(this);
//static unsigned char dummy = 0;
// saveRadioButton = new QWidget(this);
// saveRadioButton->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
// //setObjectName("test_widget");
// saveRadioButton->setFixedHeight(25);
// QHBoxLayout* qlayout = new QHBoxLayout(saveRadioButton);
// qlayout->setContentsMargins(8,2,8,2);
// qlayout->setSpacing(2);
// qlayout->setAlignment(Qt::AlignJustify);
// QLabel* qtitleLabel = new QLabel(this);
// qtitleLabel->setObjectName("text_normal_label");
// qtitleLabel->setText(FM_WSTR(L"暗証番号設定"));
// qlayout->addWidget(qtitleLabel);
// foreach (QString eachTitle, (QStringList() << "OFF" << "ON")) {
// QRadioButton* btn = new QRadioButton(saveRadioButton);
// btn->setObjectName("settings");
// btn->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
// btn->setText(eachTitle);
// connect(btn,SIGNAL(clicked()),SLOT(onSelected()));
// qlayout->addWidget(btn);
// buttons.append(btn);
// }
// buttons.at(0)->setChecked(true);
saveRadioButton = new RMRadioButtons(this,FM_WSTR(L"暗証番号設定"),(QStringList() << "OFF" << "ON"),dataP,values);
connect(saveRadioButton,SIGNAL(selected(int)),SLOT(onSelected(int)));
layout->addWidget(saveRadioButton);
saveRadioButton->setFixedWidth(rWidth);
saveRadioButton->titleLabel->setFixedWidth(lbWidth);
//qtitleLabel->setFixedWidth(lbWidth);
connect(RMValueUpdater::instance(),SIGNAL(updateByValues()),SLOT(onUpdateByValue()));
//QSpacerItem* space = new QSpacerItem(10,0);
//layout->addSpacerItem(space);
//QLabel* saveCheckbox = new QLabel(this);
//saveCheckbox->setText(MKU8(" \xe7\xae\xa1\xe7\x90\x86\xe8\x80\x85\x50\x57\xe4\xbf\x9d\xe5\xad\x98"));
#endif
//layout->addWidget(saveCheckbox);
// connect(check,SIGNAL(clicked()),SLOT(onCheckBoxTimeSync()));
//saveCheckbox->setStyleSheet("font-size: 13px;color : white;");
QWidget* lw = new QWidget(this);
layout->addWidget(lw);
QHBoxLayout* ll = new QHBoxLayout(lw);
ll->setAlignment(Qt::AlignLeading);
// 管理者PW入力
QLabel* titleLB = new QLabel(lw);
titleLB->setObjectName("text_normal_label");
ll->addWidget(titleLB);
titleLB->setText(FM_WSTR(L"暗証番号変更"));
//titleLB->setText(MKU8("\xe7\xae\xa1\xe7\x90\x86\xe8\x80\x85\x50\x57\xe5\x85\xa5\xe5\x8a\x9b\x3a"));
titleLB->setFixedWidth(lbWidth-10);//100
//titleLB->setStyleSheet("font-size: 13px;color : white;");
editPW = new QLineEdit(lw);
editPW->setEchoMode(QLineEdit::Password);
editPW->setStyleSheet("font-family: Fixedsys;color : #111111;background-color: #DDDDDD;border:1px;border-style:solid;border-color:#313131;");
editPW->setMaxLength(4);
editPW->setFixedWidth(80);
editPW->setValidator( new QIntValidator(0, 9999, this) );
ll->addWidget(editPW);
editPW->setEnabled(*data == 0);
// 端末の管理者パスワードが変更された番号にリセットされます。最大4桁のみ入力可能。
lw = new QWidget(this);
layout->addWidget(lw);
QVBoxLayout* lv = new QVBoxLayout(lw);
ZERO_LAYOUT(lv);
lv->setContentsMargins(11,0,0,0);
lv->setSpacing(3);
lv->setAlignment(Qt::AlignLeading);
// 暗証番号設定をONにしているとき、暗証番号変更4桁の入力が可能。
//QString descString = MKU8("\xe7\xab\xaf\xe6\x9c\xab\xe3\x81\xae\xe7\xae\xa1\xe7\x90\x86\xe8\x80\x85\xe3\x83\x91\xe3\x82\xb9\xe3\x83\xaf\xe3\x83\xbc\xe3\x83\x89\xe3\x81\x8c\xe5\xa4\x89\xe6\x9b\xb4\xe3\x81\x95\xe3\x82\x8c\xe3\x81\x9f\xe7\x95\xaa\xe5\x8f\xb7\xe3\x81\xab\xe3\x83\xaa\xe3\x82\xbb\xe3\x83\x83\xe3\x83\x88\xe3\x81\x95\xe3\x82\x8c\xe3\x81\xbe\xe3\x81\x99\xe3\x80\x82");
QString descString = FM_WSTR(L"暗証番号設定をONにしているとき、");
QLabel* desc = new QLabel(lw);
desc->setText(descString);
lv->addWidget(desc);
desc->setStyleSheet("font-size: 14px; font-weight:bold; color:#eab428;");
QString descString2 = FM_WSTR(L"暗証番号変更4桁の入力が可能。");
//QString descString2 = MKU8("\xe6\x9c\x80\xe5\xa4\xa7\x34\xe6\xa1\x81\xe3\x81\xae\xe3\x81\xbf\xe5\x85\xa5\xe5\x8a\x9b\xe5\x8f\xaf\xe8\x83\xbd\xe3\x80\x82");
QLabel* desc2 = new QLabel(this);
desc2->setText(descString2);
lv->addWidget(desc2);
desc2->setStyleSheet("font-size: 14px; font-weight:bold; color:#eab428;"); // ffc000
//color : white;
//font-size: 14px;
//font-weight: bold;
}
void RMSettingAdminPW::onUpdateByValue() {
// OFF
editPW->setEnabled(*dataP == 0); // ON
if(*dataP == 1) {
editPW->setText("");
}
}
void RMSettingAdminPW::onSelected(int index)
{
editPW->setEnabled(index == 1);
if(index == 0) {
editPW->setText("");
}
}
/*
void RMSettingAdminPW::onSelected()
{
QRadioButton* btn = qobject_cast<QRadioButton*>(sender());
int index = buttons.indexOf(btn);
qInfo() << index << __FUNCTION__;
//*_value = realValue(index);
//emit selected(index);
if(index == 0) {
editPW->setText("");
}
editPW->setEnabled(index == 1);
if (index == 1) {
editPW->setFocus();
}
}
*/
void RMSettingAdminPW::onSave() {
QString pws = editPW->text();
if(pws.length() == 4)
{
QString pwPath = QDir::cleanPath(RMSettingsWindowBase::lastSettingDisk + "//admin.cfg");
if(QFile::exists(pwPath))
{
QFile::remove(pwPath);
}
unsigned char pwb[4] = {0,};
pwb[0] = QString(pws.at(0)).toInt();
pwb[1] = QString(pws[1]).toInt();
pwb[2] = QString(pws[2]).toInt();
pwb[3] = QString(pws[3]).toInt();
pwb[0] ^= 0x80;
pwb[1] ^= 0x80;
pwb[2] ^= 0x80;
pwb[3] ^= 0x80;
QFile f(pwPath);
if(f.open(QIODevice::WriteOnly))
{
f.write((const char*)&pwb[0],4);
f.close();
}
}
}
#endif // #if (USE_ADMIN_PW_SETTINGS)

View File

@@ -0,0 +1,42 @@
#ifndef RM_ADMIN_PASSWD_H
#define RM_ADMIN_PASSWD_H
#if (USE_ADMIN_PW_SETTINGS)
#include <QWidget>
#include <QGroupBox>
#include <QComboBox>
#include <QList>
#include <QVBoxLayout>
#include <QDateEdit>
#include <QTimeEdit>
#include <QTimer>
#define REMOVE_ADMIN_PW_CHECKBOX 1
class RMWidgetCheckBox;
class RMRadioButtons;
class QLabel;
class QRadioButton;
class RMSettingAdminPW : public QGroupBox
{
Q_OBJECT
public:
explicit RMSettingAdminPW(unsigned char* data, QList<int> values, QWidget *parent = nullptr);
QBoxLayout* layout;
QLineEdit* editPW;
#if !(REMOVE_ADMIN_PW_CHECKBOX)
RMWidgetCheckBox* saveCheckbox; // 210924 -> 제거
#endif
RMRadioButtons* saveRadioButton;
//QList<QRadioButton*> buttons;
private:
unsigned char* dataP;
public slots:
void onSave();
void onSelected(int index);
void onUpdateByValue();
};
#endif // USE_ADMIN_PW_SETTINGS
#endif // RM_ADMIN_PASSWD_H

View File

@@ -0,0 +1,116 @@
#include "rm_combo_box.h"
#include <QLabel>
#include <QStyleOption>
#include <QPainter>
#if (USE_JSON_SETTINGS)
#include <QJsonObject>
#include <QJsonArray>
#include "rm_settings_cfg.h"
RMComboBox::RMComboBox(QWidget *parent,QString title,int index) : QWidget(parent), RMValueSelector(index)
{
setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
//setObjectName("test_widget");
layout = new QHBoxLayout(this);
layout->setAlignment(Qt::AlignLeading | Qt::AlignVCenter);
layout->setContentsMargins(0,2,8,2);
layout->setSpacing(3);
if(!title.isEmpty())
{
titleLabel = new QLabel(this);
titleLabel->setObjectName("text_normal_label");
titleLabel->setText(title);
layout->addWidget(titleLabel);
}
else
{
titleLabel = NULL;
}
QJsonObject obj = CFG::items.at(index).toObject();
QStringList spacedItems;
QJsonArray array = obj.value("value_strings").toArray();
for(int i=0;i<array.size();i++)
{
spacedItems.append(" " + array.at(i).toString());
}
comboBox = new QComboBox(this);
comboBox->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
comboBox->setObjectName("settings");
comboBox->addItems(spacedItems);
comboBox->setMaxVisibleItems(100);
int idx = obj.value("current").toInt();
comboBox->setCurrentIndex(realIndex(idx));
connect(comboBox,SIGNAL(currentIndexChanged(int)),SLOT(onSelected(int)));
layout->addWidget(comboBox);
connect(RMValueUpdater::instance(),SIGNAL(updateByValues()),SLOT(onUpdateByValue()));
}
void RMComboBox::onSelected(int index)
{
QJsonObject obj = CFG::items.at(_object).toObject();
obj.insert("current",QJsonValue(realValue(index)));
CFG::items.replace(_object,obj);
emit selected(index);
}
void RMComboBox::onUpdateByValue()
{
QJsonObject obj = CFG::items.at(_object).toObject();
comboBox->setCurrentIndex(realIndex(obj.value("current").toInt()));
}
#else // USE_JSON_SETTINGS
RMComboBox::RMComboBox(QWidget *parent,QString title, QStringList items,unsigned char* value,QList<int> indexMap) : QWidget(parent), RMValueSelector(value,indexMap)
{
setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
//setObjectName("test_widget");
layout = new QHBoxLayout(this);
layout->setAlignment(Qt::AlignLeading | Qt::AlignVCenter);
layout->setContentsMargins(0,2,8,2);
layout->setSpacing(3);
if(title.length() > 0)
{
titleLabel = new QLabel(this);
titleLabel->setObjectName("text_normal_label");
titleLabel->setText(title);
layout->addWidget(titleLabel);
}
else
{
titleLabel = NULL;
}
QStringList spacedItems;
foreach (QString eachItem, items) {
spacedItems.append(" " + eachItem);
}
comboBox = new QComboBox(this);
comboBox->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
comboBox->setObjectName("settings");
comboBox->addItems(spacedItems);
comboBox->setMaxVisibleItems(100);
comboBox->setCurrentIndex(realIndex(*value));
connect(comboBox,SIGNAL(currentIndexChanged(int)),SLOT(onSelected(int)));
layout->addWidget(comboBox);
connect(RMValueUpdater::instance(),SIGNAL(updateByValues()),SLOT(onUpdateByValue()));
}
void RMComboBox::onSelected(int index)
{
*_value = realValue(index);
emit selected(index);
}
void RMComboBox::onUpdateByValue()
{
comboBox->setCurrentIndex(realIndex(*_value));
}
#endif // USE_JSON_SETTINGS
void RMComboBox::paintEvent(QPaintEvent *pe)
{
Q_UNUSED(pe);
QStyleOption o;
o.initFrom(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &o, &p, this);
}

View File

@@ -0,0 +1,32 @@
#ifndef RM_COMBO_BOX_H
#define RM_COMBO_BOX_H
#include <QWidget>
#include <QComboBox>
#include <QList>
#include <QHBoxLayout>
#include <QLabel>
#include "rm_value_selector.h"
class RMComboBox : public QWidget, public RMValueSelector
{
Q_OBJECT
public:
#if (USE_JSON_SETTINGS)
explicit RMComboBox(QWidget *parent,QString title, int index);
#else // USE_JSON_SETTINGS
explicit RMComboBox(QWidget *parent,QString title, QStringList items,unsigned char* value,QList<int> indexMap = QList<int>());
#endif // USE_JSON_SETTINGS
QHBoxLayout* layout;
QComboBox* comboBox;
QLabel* titleLabel;
private:
void paintEvent(QPaintEvent *pe);
signals:
void selected(int index);
private slots:
void onSelected(int index);
void onUpdateByValue();
};
#endif // RM_COMBO_BOX_H

View File

@@ -0,0 +1,43 @@
#include "rm_group_combo_box.h"
#include <QStyleOption>
#include <QPainter>
#include <QLabel>
#include <QDebug>
#include "rm_combo_box.h"
#if (USE_JSON_SETTINGS)
#include <QJsonObject>
#include "rm_settings_cfg.h"
RMGroupComboBox::RMGroupComboBox(QWidget *parent,int index) : QGroupBox(parent)
{
setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
setObjectName("settings");
QJsonObject obj = CFG::items.at(index).toObject();
if(obj.contains("title")) {
setTitle(obj.value("title").toString());
}
layout = new QVBoxLayout(this);
layout->setAlignment(Qt::AlignVCenter | Qt::AlignLeading);
layout->setContentsMargins(8,2,8,2);
layout->setSpacing(3);
comboBox = new RMComboBox(this,"",index);
layout->addWidget(comboBox);
}
#else
RMGroupComboBox::RMGroupComboBox(QWidget *parent,QString title, QStringList titles,unsigned char* value,QList<int> indexMap) : QGroupBox(parent)
{
setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
setObjectName("settings");
setTitle(title);
layout = new QVBoxLayout(this);
layout->setAlignment(Qt::AlignVCenter | Qt::AlignLeading);
layout->setContentsMargins(8,2,8,2);
layout->setSpacing(3);
comboBox = new RMComboBox(this,"",titles,value,indexMap);
layout->addWidget(comboBox);
}
#endif

View File

@@ -0,0 +1,26 @@
#ifndef RM_GROUP_COMBO_BOX_H
#define RM_GROUP_COMBO_BOX_H
#include <QWidget>
#include <QGroupBox>
#include <QComboBox>
#include <QList>
#include <QVBoxLayout>
class RMComboBox;
class QJsonObject;
class RMGroupComboBox : public QGroupBox
{
Q_OBJECT
public:
#if (USE_JSON_SETTINGS)
explicit RMGroupComboBox(QWidget *parent,int index);
#else // USE_JSON_SETTINGS
explicit RMGroupComboBox(QWidget *parent,QString title, QStringList items,unsigned char* value,QList<int> indexMap = QList<int>());
#endif // USE_JSON_SETTINGS
QVBoxLayout* layout;
RMComboBox* comboBox;
};
#endif // RM_GROUP_COMBO_BOX_H

View File

@@ -0,0 +1,70 @@
#include "rm_group_radio_buttons.h"
#include "rm_radio_buttons.h"
#include <QDebug>
#if (USE_JSON_SETTINGS)
#include <QJsonObject>
#include "rm_settings_cfg.h"
RMGroupRadioButtons::RMGroupRadioButtons(QWidget *parent,int object) : QGroupBox(parent)
{
setObjectName("settings");
QJsonObject obj = CFG::items.at(object).toObject();
if(obj.contains("title")) {
setTitle(obj.value("title").toString());
}
layout = new QVBoxLayout(this);
layout->setAlignment(Qt::AlignVCenter | Qt::AlignLeading);
layout->setContentsMargins(8,2,8,2);
layout->setSpacing(3);
radioButtons = new RMRadioButtons(this, "",object);
layout->addWidget(radioButtons);
}
RMGroupRadioButtons::RMGroupRadioButtons(QWidget *parent,int object, bool rowType) : QGroupBox(parent)
{
setObjectName("settings");
QJsonObject obj = CFG::items.at(object).toObject();
if(obj.contains("title")) {
setTitle(obj.value("title").toString());
}
layout = new QVBoxLayout(this);
layout->setAlignment(Qt::AlignVCenter | Qt::AlignLeading);
layout->setContentsMargins(8,2,8,2);
layout->setSpacing(3);
radioButtons = new RMRadioButtons(this, "",object,rowType);
layout->addWidget(radioButtons);
}
#else // USE_JSON_SETTINGS
RMGroupRadioButtons::RMGroupRadioButtons(QWidget *parent,QString title, QStringList titles,unsigned char* value,QList<int> indexMap) : QGroupBox(parent)
{
setObjectName("settings");
setTitle(title);
layout = new QVBoxLayout(this);
layout->setAlignment(Qt::AlignVCenter | Qt::AlignLeading);
layout->setContentsMargins(8,2,8,2);
layout->setSpacing(3);
radioButtons = new RMRadioButtons(this, "",titles,value,indexMap);
layout->addWidget(radioButtons);
}
RMGroupRadioButtons::RMGroupRadioButtons(QWidget *parent,QString title, QStringList titles,unsigned char* value,bool rowType, QList<int> indexMap) : QGroupBox(parent)
{
setObjectName("settings");
setTitle(title);
layout = new QVBoxLayout(this);
layout->setAlignment(Qt::AlignVCenter | Qt::AlignLeading);
layout->setContentsMargins(8,2,8,2);
layout->setSpacing(3);
radioButtons = new RMRadioButtons(this, "",titles,value,rowType,indexMap);
layout->addWidget(radioButtons);
}
#endif // #if (USE_JSON_SETTINGS)

View File

@@ -0,0 +1,28 @@
#ifndef RM_GROUP_RADIO_BUTTONS_H
#define RM_GROUP_RADIO_BUTTONS_H
#include <QWidget>
#include <QGroupBox>
#include <QList>
#include <QVBoxLayout>
class RMRadioButtons;
class RMGroupRadioButtons : public QGroupBox
{
Q_OBJECT
public:
#if (USE_JSON_SETTINGS)
explicit RMGroupRadioButtons(QWidget *parent,int object);
explicit RMGroupRadioButtons(QWidget *parent,int object,bool rowType);
#else // #if (USE_JSON_SETTINGS)
explicit RMGroupRadioButtons(QWidget *parent,QString title, QStringList items,unsigned char* value,QList<int> indexMap = QList<int>());
explicit RMGroupRadioButtons(QWidget *parent,QString title, QStringList items,unsigned char* value,bool rowType = false,QList<int> indexMap = QList<int>());
#endif // #if (USE_JSON_SETTINGS)
QVBoxLayout* layout;
RMRadioButtons* radioButtons;
};
#endif // RM_GROUP_RADIO_BUTTONS_H

View File

@@ -0,0 +1,243 @@
#include "rm_radio_buttons.h"
#include <QHBoxLayout>
#include <QDebug>
#include <QStyleOption>
#include <QPainter>
#include <QLabel>
#if (USE_JSON_SETTINGS)
#include <QJsonArray>
#include <QJsonObject>
#include "rm_settings_cfg.h"
RMRadioButtons::RMRadioButtons(QWidget *parent,
QString title, int object, bool rowType) : QWidget(parent), RMValueSelector(object)
{
setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
//setObjectName("test_widget");
if(rowType == true)
{
layout = new QVBoxLayout(this);
}
else
{
setFixedHeight(25);
layout = new QHBoxLayout(this);
}
layout->setContentsMargins(8,2,8,2);
layout->setSpacing(2);
layout->setAlignment(Qt::AlignJustify);
if(title.isEmpty() == false) {
titleLabel = new QLabel(this);
titleLabel->setObjectName("text_normal_label");
titleLabel->setText(title);
layout->addWidget(titleLabel);
}
QJsonObject obj = CFG::items.at(object).toObject();
//foreach (QString eachTitle, titles) {
QJsonArray value_strings = obj.value("value_strings").toArray();
for(int i=0;i<value_strings.size();i++) {
QRadioButton* btn = new QRadioButton(this);
btn->setObjectName("settings");
btn->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
btn->setText(value_strings.at(i).toString());
if(rowType == true)
{
btn->setFixedHeight(25);
}
connect(btn,SIGNAL(clicked()),SLOT(onSelected()));
layout->addWidget(btn);
buttons.append(btn);
}
if(buttons.count() <= realIndex(obj.value("current").toInt()))
{
obj.insert("current",QJsonValue(0));
}
buttons[realIndex(obj.value("current").toInt())]->setChecked(true);
connect(RMValueUpdater::instance(),SIGNAL(updateByValues()),SLOT(onUpdateByValue()));
}
RMRadioButtons::RMRadioButtons(QWidget *parent,
QString title,
int object) : QWidget(parent),
RMValueSelector(object)
{
setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
//setObjectName("test_widget");
setFixedHeight(25);
layout = new QHBoxLayout(this);
layout->setContentsMargins(8,2,8,2);
layout->setSpacing(2);
layout->setAlignment(Qt::AlignJustify);
if(title.isEmpty() == false) {
titleLabel = new QLabel(this);
titleLabel->setObjectName("text_normal_label");
titleLabel->setText(title);
layout->addWidget(titleLabel);
}
QJsonObject obj = CFG::items.at(object).toObject();
//foreach (QString eachTitle, titles) {
QJsonArray value_strings = obj.value("value_strings").toArray();
for(int i=0;i<value_strings.size();i++) {
QRadioButton* btn = new QRadioButton(this);
btn->setObjectName("settings");
btn->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
btn->setText(value_strings.at(i).toString());
connect(btn,SIGNAL(clicked()),SLOT(onSelected()));
layout->addWidget(btn);
buttons.append(btn);
}
if(buttons.count() > realIndex(obj.value("current").toInt()))
{
buttons[realIndex(obj.value("current").toInt())]->setChecked(true);
}
connect(RMValueUpdater::instance(),SIGNAL(updateByValues()),SLOT(onUpdateByValue()));
}
void RMRadioButtons::onSelected()
{
QJsonObject obj = CFG::items.at(_object).toObject();
QRadioButton* btn = qobject_cast<QRadioButton*>(sender());
int index = buttons.indexOf(btn);
obj.insert("current",QJsonValue(realValue(index)));
CFG::items.replace(_object,obj);
emit selected(index);
}
void RMRadioButtons::onUpdateByValue()
{
QJsonObject obj = CFG::items.at(_object).toObject();
buttons[realIndex(obj.value("current").toInt())]->setChecked(true);
}
#else // #if (USE_JSON_SETTINGS)
RMRadioButtons::RMRadioButtons(QWidget *parent,
QString title,
QStringList titles,
unsigned char* value,
bool rowType,
QList<int> indexMap
) : QWidget(parent), RMValueSelector(value,indexMap)
{
setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
//setObjectName("test_widget");
if(rowType == true)
{
layout = new QVBoxLayout(this);
}
else
{
setFixedHeight(25);
layout = new QHBoxLayout(this);
}
layout->setContentsMargins(8,2,8,2);
layout->setSpacing(2);
layout->setAlignment(Qt::AlignJustify);
if(title.isEmpty() == false) {
titleLabel = new QLabel(this);
titleLabel->setObjectName("text_normal_label");
titleLabel->setText(title);
layout->addWidget(titleLabel);
}
foreach (QString eachTitle, titles) {
QRadioButton* btn = new QRadioButton(this);
btn->setObjectName("settings");
btn->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
btn->setText(eachTitle);
if(rowType == true)
{
btn->setFixedHeight(25);
}
connect(btn,SIGNAL(clicked()),SLOT(onSelected()));
layout->addWidget(btn);
buttons.append(btn);
}
if(buttons.count() <= realIndex(*_value))
{
*_value = 0;
}
buttons[realIndex(*_value)]->setChecked(true);
connect(RMValueUpdater::instance(),SIGNAL(updateByValues()),SLOT(onUpdateByValue()));
}
RMRadioButtons::RMRadioButtons(QWidget *parent,
QString title,
QStringList titles,
unsigned char* value,
QList<int> indexMap) : QWidget(parent),
RMValueSelector(value,indexMap)
{
setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
//setObjectName("test_widget");
setFixedHeight(25);
layout = new QHBoxLayout(this);
layout->setContentsMargins(8,2,8,2);
layout->setSpacing(2);
layout->setAlignment(Qt::AlignJustify);
if(title.isEmpty() == false) {
titleLabel = new QLabel(this);
titleLabel->setObjectName("text_normal_label");
titleLabel->setText(title);
layout->addWidget(titleLabel);
}
foreach (QString eachTitle, titles) {
QRadioButton* btn = new QRadioButton(this);
btn->setObjectName("settings");
btn->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
btn->setText(eachTitle);
connect(btn,SIGNAL(clicked()),SLOT(onSelected()));
layout->addWidget(btn);
buttons.append(btn);
}
if(buttons.count() > realIndex(*_value))
{
buttons[realIndex(*_value)]->setChecked(true);
}
connect(RMValueUpdater::instance(),SIGNAL(updateByValues()),SLOT(onUpdateByValue()));
}
int RMRadioButtons::currentIndex()
{
for(int i=0;i<buttons.size();i++) {
if(buttons[i]->isChecked()) {
return i;
}
}
return -1;
}
void RMRadioButtons::setCurrentIndex(int index)
{
for(int i=0;i<buttons.size();i++) {
buttons[i]->setChecked(i == index);
}
}
void RMRadioButtons::onSelected()
{
QRadioButton* btn = qobject_cast<QRadioButton*>(sender());
int index = buttons.indexOf(btn);
*_value = realValue(index);
emit selected(index);
}
void RMRadioButtons::onUpdateByValue()
{
//qInfo() << buttons[0]->text() << "value:" << *_value << "_indexMap:" << _indexMap.size() << realIndex(*_value) << __FUNCTION__;
buttons[realIndex(*_value)]->setChecked(true);
}
#endif // #else // #if (USE_JSON_SETTINGS)
void RMRadioButtons::paintEvent(QPaintEvent *pe)
{
Q_UNUSED(pe);
QStyleOption o;
o.initFrom(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &o, &p, this);
}

View File

@@ -0,0 +1,39 @@
#ifndef RM_RADIO_BUTTONS_H
#define RM_RADIO_BUTTONS_H
#include <QWidget>
#include <QRadioButton>
#include <QHBoxLayout>
#include <QPaintEvent>
#include <QLabel>
#include "rm_value_selector.h"
class RMRadioButtons : public QWidget, public RMValueSelector
{
Q_OBJECT
public:
#if (USE_JSON_SETTINGS)
explicit RMRadioButtons(QWidget *parent,QString title, int object);
explicit RMRadioButtons(QWidget *parent,QString title, int object, bool rowType);
#else // USE_JSON_SETTINGS
explicit RMRadioButtons(QWidget *parent,QString title, QStringList titles,unsigned char* value, QList<int> indexMap = QList<int>());
explicit RMRadioButtons(QWidget *parent,QString title, QStringList titles,unsigned char* value, bool rowType = false,QList<int> indexMap = QList<int>());
#endif // USE_JSON_SETTINGS
QBoxLayout* layout;
//QHBoxLayout* layout;
QLabel* titleLabel;
void setCurrentIndex(int index);
int currentIndex();
private:
QList<QRadioButton*> buttons;
void paintEvent(QPaintEvent *pe);
signals:
void selected(int index);
private slots:
void onSelected();
void onUpdateByValue();
};
#endif // RM_RADIO_BUTTONS_H

View File

@@ -0,0 +1,246 @@
#include "rm_setting_time.h"
#if (USE_DEVICE_SETTINGS && !USE_DEVICE_SETTINGS_JSON)
#include "rm_include.h"
#include <QStyleOption>
#include <QPainter>
#include <QLabel>
#include <QDebug>
#include <QCheckBox>
#include <QDateTime>
#include <QDir>
#include <QFile>
#include "../ui/rm_button.h"
#include "rm_settings_window_base.h"
#include "../ui/rm_widget_checkbox.h"
RMSettingTime::RMSettingTime(QWidget *parent) : QGroupBox(parent)
{
setFixedHeight(100);
setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
setObjectName("settings");
// 日時
//#if (LIVE_LANGUAGE_CHANGE)
// if(RMLanguage::isJP() == false)
// {
// setTitle("Time");
// }
// else {
//#else
setTitle(MKU8("\xe6\x97\xa5\xe6\x99\x82"));
//#endif
//#if (LIVE_LANGUAGE_CHANGE)
// }
//#endif
layout = new QVBoxLayout(this);
layout->setAlignment(Qt::AlignVCenter | Qt::AlignLeading);
layout->setContentsMargins(4,2,4,2);
layout->setSpacing(3);
QSpacerItem* space = new QSpacerItem(0,10);
layout->addSpacerItem(space);
// パソコンの時刻と同期
check = new RMWidgetCheckBox(this);
//#if (LIVE_LANGUAGE_CHANGE)
// if(RMLanguage::isJP() == false)
// {
// check->setText("PC Time Sync.");
// }
// else {
//#else
check->setText(MKU8("\xe3\x83\x91\xe3\x82\xbd\xe3\x82\xb3\xe3\x83\xb3\xe3\x81\xae\xe6\x99\x82\xe5\x88\xbb\xe3\x81\xa8\xe5\x90\x8c\xe6\x9c\x9f"));
//#endif // LIVE_LANGUAGE_CHANGE
//#if (LIVE_LANGUAGE_CHANGE)
// }
//#endif
layout->addWidget(check);
// 시계 기능 없음
#if !(SETTINGS_TIME_TYPE2)
connect(check,SIGNAL(clicked()),SLOT(onCheckBoxTimeSync()));
#endif
check->setStyleSheet("font-size: 13px;color : white;");
QWidget* timeWidget = new QWidget(this);
layout->addWidget(timeWidget);
QHBoxLayout* timeLayout = new QHBoxLayout(timeWidget);
date = new QDateEdit(timeWidget);
date->setCalendarPopup(true);
date->setStyleSheet("font-family: Arial;font-size: 14px;");
timeLayout->addWidget(date);
time = new QTimeEdit(timeWidget);
timeLayout->addWidget(time);
time->setStyleSheet("font-family: Arial;font-size: 14px;");
time->setDisplayFormat("AP hh:mm:ss");
QString applyString = MKU8("\xe9\x81\xa9\xe7\x94\xa8");
//#if (LIVE_LANGUAGE_CHANGE)
// if(RMLanguage::isJP() == false)
// {
// applyString = "Apply";
// }
//#endif
#if !(SETTINGS_TIME_TYPE2)
#if (LIVE_LANGUAGE2)
RMButton* applyButton = RMButton::create2(timeWidget,timeLayout,"button","apply",QSize(60,30));
#else // LIVE_LANGUAGE2
RMButton* applyButton = RMButton::create(timeWidget,timeLayout,"button",applyString,QSize(60,30));
#endif // LIVE_LANGUAGE2
applyButton->setText(applyString);
connect(applyButton,SIGNAL(clicked()),this,SLOT(onSave()));
#endif // SETTINGS_TIME_TYPE2
_syncTimer = NULL;
// 읽어 오기는 한다 (나머지 공간에 저장값 그대로 유지하기 위해)
memset(_buffer,0,TIME_SET_FILE_SIZE);
QString timePath = QDir::cleanPath(RMSettingsWindowBase::lastSettingDisk + "//timeset.txt");
if(QFile::exists(timePath))
{
FILE* f = fopen(timePath.toLocal8Bit(),"r+b");
if(f != NULL)
{
fread(_buffer,1,TIME_SET_FILE_SIZE,f);
fclose(f);
//_readFromBuffer();
}
}
onSyncTime();
}
//void RMSettingTime::_readFromBuffer()
//{
//// 0x10 2BYTE 2019 // 년
//// 0x12 2BYTE 01 // 월
//// 0x14 2BYTE 01 // 일
//// 0x16 2BYTE 01 // 시
//// 0x18 2BYTE 00 // 분
//// 0x1A 2BYTE 00 // 초
// int y = _buffer[0x11] << 8 | _buffer[0x10];
// int m = _buffer[0x13] << 8 | _buffer[0x12];
// int d = _buffer[0x15] << 8 | _buffer[0x14];
// int h = _buffer[0x17] << 8 | _buffer[0x16];
// int mm = _buffer[0x19] << 8 | _buffer[0x18];
// int s = _buffer[0x1B] << 8 | _buffer[0x1A];
// //qInfo() << year << month << day << h << m << s;
// QDateTime dt;
// dt.setDate(QDate(y,m,d));
// dt.setTime(QTime(h,mm,s));
// date->setDate(dt.date());
// time->setTime(dt.time());
// qInfo() << dt;
//}
void RMSettingTime::onSave()
{
#if (RM_MODEL == RM_MODEL_TYPE_XLDR_88)
QString timePath = QDir::cleanPath(RMSettingsWindowBase::lastSettingDisk + "//data_time.cfg");
if(QFile::exists(timePath))
{
QFile::remove(timePath);
}
QDateTime dt = date->dateTime();
dt.setTime(time->time());
// 2020 10 21 17 18 28
QString ts = dt.toString("yyyy MM dd HH mm ss");
QFile f(timePath);
if(f.open(QIODevice::WriteOnly))
{
f.write(ts.toUtf8());
f.close();
}
#else
int y = date->date().year();
int m = date->date().month();
int d = date->date().day();
int h = time->time().hour();
int mm = time->time().minute();
int s = time->time().second();
_buffer[0x10] = (y) & 0xFF;
_buffer[0x11] = (y >> 8) & 0xFF;
_buffer[0x12] = (m) & 0xFF;
_buffer[0x13] = (m >> 8) & 0xFF;
_buffer[0x14] = (d) & 0xFF;
_buffer[0x15] = (d >> 8) & 0xFF;
_buffer[0x16] = (h) & 0xFF;
_buffer[0x17] = (h >> 8) & 0xFF;
_buffer[0x18] = (mm) & 0xFF;
_buffer[0x19] = (mm >> 8) & 0xFF;
_buffer[0x1A] = (s) & 0xFF;
_buffer[0x1B] = (s >> 8) & 0xFF;
QString timePath = QDir::cleanPath(RMSettingsWindowBase::lastSettingDisk + "//timeset.txt");
if(QFile::exists(timePath))
{
QFile::remove(timePath);
}
FILE* f = fopen(timePath.toLocal8Bit(),"w+b");
if(f != NULL)
{
fwrite(_buffer,1,TIME_SET_FILE_SIZE,f);
fclose(f);
}
#endif
}
void RMSettingTime::onCheckBoxTimeSync()
{
QCheckBox* checkbox = qobject_cast<QCheckBox *>(QObject::sender());
if(checkbox != NULL)
{
bool turnOn = (checkbox->checkState() != Qt::Unchecked );
_startStopTimer(turnOn);
}
}
void RMSettingTime::_startStopTimer(bool bStart)
{
if(_syncTimer != NULL) {
_syncTimer->stop();
delete _syncTimer;
_syncTimer = NULL;
}
if(bStart) {
_syncTimer = new QTimer(this);
_syncTimer->setSingleShot(false);
_syncTimer->setInterval(1000);
connect(_syncTimer,SIGNAL(timeout()),SLOT(onSyncTime()));
_syncTimer->start();
}
}
void RMSettingTime::onSyncTime()
{
QDateTime d = QDateTime::currentDateTime();
date->setDate(d.date());
time->setTime(d.time());
}
#endif // #if (USE_DEVICE_SETTINGS)

View File

@@ -0,0 +1,41 @@
#ifndef RM_SETTING_TIME_H
#define RM_SETTING_TIME_H
#if (USE_DEVICE_SETTINGS && !USE_DEVICE_SETTINGS_JSON)
#include <QWidget>
#include <QGroupBox>
#include <QComboBox>
#include <QList>
#include <QVBoxLayout>
#include <QDateEdit>
#include <QTimeEdit>
#include <QTimer>
#define TIME_SET_FILE_SIZE 28
class RMWidgetCheckBox;
class RMSettingTime : public QGroupBox
{
Q_OBJECT
public:
explicit RMSettingTime(QWidget *parent = nullptr);
QBoxLayout* layout;
RMWidgetCheckBox* check;
private:
QTimer* _syncTimer;
void _startStopTimer(bool bStart);
QDateEdit* date;
QTimeEdit* time;
unsigned char _buffer[TIME_SET_FILE_SIZE];
//void _readFromBuffer();
public slots:
void onSave();
void onCheckBoxTimeSync();
void onSyncTime();
};
#endif // #if (USE_DEVICE_SETTINGS)
#endif // RM_SETTING_TIME_H

View File

@@ -0,0 +1,15 @@
#ifndef RM_SETTINGS_CFG_H
#define RM_SETTINGS_CFG_H
#if (USE_DEVICE_SETTINGS)
#include "../rm_include.h"
#if (RM_MODEL == RM_MODEL_TYPE_XLDR_88)
#include "rm_settings_cfg_xdr6688.h"
#elif (RM_MODEL_EMT_KR)
#include "rm_settings_cfg_emt_kr.h"
#else
#include "rm_settings_cfg_standard.h"
#endif
#endif // #if (USE_DEVICE_SETTINGS)
#endif // RM_SETTINGS_CFG_H

View File

@@ -0,0 +1,268 @@
#include "rm_settings_cfg_emt_kr.h"
#include <QFile>
#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
#include <Windows.h>
#if (USE_DEVICE_SETTINGS && RM_MODEL_EMT_KR)
unsigned char CFG::data[SETTINGS_CFG_SIZE] = {0,};
unsigned char CFG::stored[SETTINGS_CFG_SIZE] = {0,};
EMTINFO CFG::info = {0,};
QJsonArray CFG::items;
QString CFG::cfgPath = "";
QStringList CFG::model_names = QStringList() << "NM5000" << "NP5000" << "MIRROR5" << "PRO5" << "360X";
uint32_t CFG::model_codes[5] = { NM5000, NP5000, MIRROR5, PRO5, A360X};
bool CFG::_loadJSon()
{
// CLEAR
while(CFG::items.count()) {
CFG::items.pop_back();
}
//QString jsonPath = ":/raw/cfg_5102.json";
QString jsonPath = "";
switch(info.model) {
case NM5000:
jsonPath = ":/raw/cfg_nm5000.json";
break;
case NP5000:
jsonPath = ":/raw/cfg_np5000.json";
break;
case MIRROR5:
jsonPath = ":/raw/cfg_mirror5.json";
break;
case PRO5:
jsonPath = ":/raw/cfg_pro5.json";
break;
case A360X:
jsonPath = ":/raw/cfg_360x.json";
break;
default:
return false;
}
QFile file(jsonPath);
if(file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QByteArray data = file.readAll();
file.close();
CFG::items = QJsonDocument::fromJson(data).array();
}
return true;
}
void CFG::_getItems(QJsonArray& in, QJsonArray& ret)
{
for(int i=0;i<in.size();i++) {
QJsonObject obj = in.at(i).toObject();
if(obj.contains("group") && obj.contains("items")) {
_getItems(obj.value("items").toArray(),ret);
}
else {
ret.append(obj);
}
}
}
void CFG::serializeItems(QJsonArray& citems) {
_getItems(CFG::items,citems);
return;
}
void CFG::setDefault()
{
QJsonArray citems;
serializeItems(citems);
for(int i=0;i<citems.size();i++) {
QJsonObject obj = citems.at(i).toObject();
//qInfo() << obj << __FUNCTION__;
if(obj.contains("default") && obj.contains("offset")) {
QString type = obj.value("type").toString();
if(type == "int") {
int offset = obj.value("offset").toInt();
CFG::data[offset] = obj.value("default").toInt();
//qInfo() << obj << __FUNCTION__;
}
}
}
//qInfo() << items << __FUNCTION__;
}
void CFG::backup()
{
memcpy(CFG::stored,CFG::data,SETTINGS_CFG_SIZE);
}
void CFG::restore()
{
memcpy(CFG::data,CFG::stored,SETTINGS_CFG_SIZE);
}
bool CFG::isEdited()
{
return (0 != memcmp(CFG::stored,CFG::data,SETTINGS_CFG_SIZE-sizeof(_EMTINFO)));
}
CFG_ERROR_CODE CFG::load(QString path)
{
CFG_ERROR_CODE r = CFG_UNKNOWN_ERROR;
if(!QFile::exists(path)) {
return CFG_IO_ERROR;
}
FILE* f = fopen(path.toLocal8Bit(),"r+b");
if(f == NULL) {
return CFG_IO_ERROR;
}
unsigned char d[SETTINGS_CFG_SIZE] = {0,};
size_t read = fread(d,1,SETTINGS_CFG_SIZE,f);
fclose(f);
if(read > 0)
{
cfgPath = path;
memcpy(CFG::data,d,read);
// 나머지 영역 초기화???
if(read == SETTINGS_CFG_SIZE) {
memcpy(&CFG::info,&d[SETTINGS_CFG_SIZE-sizeof(_EMTINFO)],sizeof(_EMTINFO));
//setDefault();
r = verifyInfo();
if(r != CFG_SUCCESS) {
return r;
}
_loadJSon();
}
}
else
{
setDefault();
}
CFG::backup(); // 현재 데이터 보존
// CHECKSUM 확인
if(!appUserSettingCheck()) {
return CFG_CHECKSUM_ERROR;
}
return r;
}
CFG_ERROR_CODE CFG::verifyInfo()
{
if(info.magic != 0xA5A5CC33) {
return CFG_WRONG_FILE;
}
// Magnus : 5101
// Trinity : 5102
// Mirror5 : 5103
// Pro5 : 5104
// 360X : 5105
// qInfo() << info.file1 << info.file2 << __FUNCTION__; // ?? 값이 정확하지 않음
// qInfo() << "MODEL CODE:" << info.model << "FILE SIZE:" << info.file1 << info.file2 << __FUNCTION__;
if(info.model < 5101 || info.model > 5105)
{
return CFG_WRONG_FILE;
}
return CFG_SUCCESS;
}
QString CFG::modelName()
{
for(int i=0;i<model_names.size();i++) {
if(model_codes[i] == info.model) {
return model_names.at(i);
}
}
return "UNKNOWN";
}
uint32_t CFG::version()
{
return info.version;
}
void CFG::clear()
{
memset(CFG::stored,0,SETTINGS_CFG_SIZE);
memset(CFG::data,0,SETTINGS_CFG_SIZE);
}
bool CFG::save(QString path)
{
bool bSuccess = false;
DWORD dwAttrib = ::GetFileAttributes((LPCTSTR)path.utf16());
if((dwAttrib & FILE_ATTRIBUTE_HIDDEN)) {
::SetFileAttributes((LPCTSTR)path.utf16(),FILE_ATTRIBUTE_NORMAL);
}
FILE* f = fopen(path.toLocal8Bit(),"w+b");
if(f != NULL)
{
if(isEdited())
{
CFG::data[SD_VIEWSETTING_OFFSET] = 1;
_updateCheckSum(); // 다시 계산
}
size_t write = 0;
write = fwrite(CFG::data,1,SETTINGS_CFG_SIZE,f);
bSuccess = write > 0;
fclose(f);
::SetFileAttributes((LPCTSTR)path.utf16(),FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN);
}
CFG::backup();
return bSuccess;
}
#define COMBINE_16(h, l) (uint16_t)((((uint16_t)h)<<8) | (uint16_t)l)
uint16_t CFG::checkSum() // appGetSum16_Emt(uint16_t *Buf, uint32_t Size)
{
// 마지막 1은?
uint32_t Size = ((SETTINGS_CFG_SIZE - sizeof(_EMTINFO)) / 2) - 1;
//uint8_t bSettingFileOK = TRUE;
uint16_t* pbuf = (uint16_t*)&data[0];
uint16_t Sum = 0;
uint32_t SumTemp = 0;
while( Size )
{
SumTemp += (uint32_t)pbuf[--Size];
}
Sum = (uint16_t)((uint16_t)((SumTemp >> 16) & 0x0000ffff) | (uint16_t)(SumTemp & 0x0000ffff));
return Sum;
}
void CFG::_updateCheckSum()
{
uint16_t c = checkSum();
data[SETTINGS_CFG_SIZE-sizeof(_EMTINFO)-2] = ((c >> 8) & 0xFF);
data[SETTINGS_CFG_SIZE-sizeof(_EMTINFO)-1] = (c & 0xFF);
}
bool CFG::appUserSettingCheck()
{
/*
uint16_t* pbuf = (uint16_t*)&data[0];
uint16_t checkSum, CalCheckSum;
// 설정 영역의 마지막 2BYTE
uint8_t checkData1 = data[SETTINGS_CFG_SIZE-sizeof(_EMTINFO)-2];
uint8_t checkData2 = data[SETTINGS_CFG_SIZE-sizeof(_EMTINFO)-1];
const uint32_t dataSize = SETTINGS_CFG_SIZE - sizeof(_EMTINFO);
checkSum = COMBINE_16(checkData1, checkData2);
CalCheckSum = appGetSum16_Emt(pbuf, (dataSize / 2)); // sizeof(uiParamSetting_t)
return (checkSum != CalCheckSum);
*/
// 설정 영역의 마지막 2BYTE
uint8_t checkData1 = data[SETTINGS_CFG_SIZE-sizeof(_EMTINFO)-2];
uint8_t checkData2 = data[SETTINGS_CFG_SIZE-sizeof(_EMTINFO)-1];
//qInfo() << "CHECKED:" << COMBINE_16(checkData1,checkData2) << "CALCED:" << checkSum() <<__FUNCTION__;
return (COMBINE_16(checkData1,checkData2) == checkSum());
}
//uint16_t CFG::checkSum()
//{
// uint16_t* pbuf = (uint16_t*)&data[0];
// const uint32_t dataSize = SETTINGS_CFG_SIZE - sizeof(_EMTINFO);
// return appGetSum16_Emt(pbuf, (dataSize / 2) - 1);
//}
#endif // #if (USE_DEVICE_SETTINGS && RM_MODEL_EMT_KR)

View File

@@ -0,0 +1,101 @@
#ifndef RM_SETTINGS_CFG_EMT_KR_H
#define RM_SETTINGS_CFG_EMT_KR_H
#if (USE_DEVICE_SETTINGS && RM_MODEL_EMT_KR)
#include <QObject>
#include <QJsonArray>
#include <stdint.h>
#define SETTINGS_CFG_SIZE 248 // 248 BYTE
typedef enum
{
CFG_SUCCESS = 0, // 성공
CFG_IO_ERROR = 1, // 파일 열기, 읽기 실패
CFG_WRONG_FILE = 2, // 잘못된 파일 에러
CFG_CHECKSUM_ERROR = 3, // 체크섬 확인실패
// CFG_MODEL_CODE_ERROR = 3, // 모델코드 에러 UI 에서 확인
CFG_UNKNOWN_ERROR = 99, // ??
} CFG_ERROR_CODE;
typedef struct _EMTINFO
{
uint32_t magic;
uint32_t model; //micom ver : 31 ~ 16, model : 15 ~ 0
uint32_t version;//F/W version
uint32_t file1; //1 bit + 31 bit ( 1 : file exist, 31 : file size )
uint32_t file2; //1 bit + 31 bit ( 1 : file exist, 31 : file size )
} EMTINFO;
// 제품 코드에 따라 설정항목이 달라짐
// NM5000(5101),MIRROR5(5103) = MAGNUS
// NP5000(5102),PRO5(5104) = TRINITY
typedef enum
{
NM5000 = 5101, // NM5000 OFFLINE
NP5000 = 5102, // TRINITY OFFLINE
MIRROR5 = 5103, // Mirror5(MAGNUS ONLINE)
PRO5 = 5104, // Pro5(TRINITY ONLINE)
A360X = 5105, // Cyclops = 360X
} MODEL_CODE;
// 설정 편집시 1로 지정해야 하는 OFFSET
#define SD_VIEWSETTING_OFFSET 143
class CFG : public QObject
{
Q_OBJECT
public:
static QString cfgPath;
static QStringList model_names;// = QStringList() << "NM5000" << "NP5000";
static uint32_t model_codes[];// = { 5101, 5102};
static QJsonArray items;
static QJsonObject _findType(QString key,int* index);
static unsigned char data[SETTINGS_CFG_SIZE];
static unsigned char stored[SETTINGS_CFG_SIZE];
static EMTINFO info;
//! \brief 모델에 따른 JSON 파일 확인
//! \return
static bool _loadJSon();
static CFG_ERROR_CODE load(QString path);
static bool save(QString path);
static void backup();
static void restore();
static void setDefault();
//! \brief 데이터 수정여부 확인
//! \return
static bool isEdited();
static void clear();
static QString modelName();
static uint32_t version();
private:
//! \brief 모델정보등 검증
//! \return
static CFG_ERROR_CODE verifyInfo();
//static uint16_t appGetSum16_Emt(); // uint16_t *Buf, uint32_t Size
static bool appUserSettingCheck();
// 현재 데이터 CHECKSUM 계산하여 반영
static void _updateCheckSum();
// 설정항목을 그룹 제외하고 직렬화
static void _getItems(QJsonArray& in, QJsonArray& ret);
static void serializeItems(QJsonArray& citems);
// checkData1 << 8 | checkData2
//! \brief EMTINFO 를 제외한 데이터를 확인하여
//! \return checkData1 << 8 | checkData2
static uint16_t checkSum();
signals:
public slots:
};
#endif // #if (USE_DEVICE_SETTINGS && RM_MODEL_EMT_KR)
#endif // RM_SETTINGS_CFG_EMT_KR_H

View File

@@ -0,0 +1,301 @@
#include "rm_settings_cfg_standard.h"
#if (USE_DEVICE_SETTINGS && !(RM_MODEL_EMT_KR))
#include <QDebug>
#include <stdio.h>
#include <QFile>
#if !(RM_MODEL == RM_MODEL_TYPE_AN6000)
unsigned char CFG::data[SETTINGS_CFG_SIZE] = {0,};
unsigned char CFG::stored[SETTINGS_CFG_SIZE] = {0,};
#endif // #if (RM_MODEL == RM_MODEL_TYPE_AN6000)
#if (RM_MODEL == RM_MODEL_TYPE_AN6000)
#include <QJsonObject>
#include <QJsonDocument>
#include <QJsonArray>
#if (ENCODE_CFG_BASE64)
#include "fm_base64.h"
#endif // ENCODE_CFG_BASE64
QJsonArray CFG::items;
QJsonObject CFG::_findType(QString key, int* index)
{
for(int i=0;i<items.size();i++) {
QJsonObject obj = items.at(i).toObject();
if(obj.value("key").toString() == key) {
*index = i;
return obj;
}
}
*index = -1;
return QJsonObject();
}
#endif // AN6000
#if (ENCODE_CFG_BASE64)
bool CFG::isEncoded = false;
#endif // #if (ENCODE_CFG_BASE64)
void CFG::setDefault()
{
#if (RM_MODEL == RM_MODEL_TYPE_AN6000)
#if (RM_MODEL == RM_MODEL_TYPE_AN6000)
// CLEAR
while(CFG::items.count()) {
CFG::items.pop_back();
}
#if (AN6000_YEC)
const char* fname = ":/html/an6000_cfg_yec.json";
#else
const char* fname = ":/html/an6000_cfg.json";
#endif
QFile file(fname); // ":/html/an6000_cfg.json"
if(file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QByteArray data = file.readAll();
file.close();
CFG::items = QJsonDocument::fromJson(data).array();
}
#endif // #if (RM_MODEL == RM_MODEL_TYPE_AN6000)
for(int i=0;i<items.size();i++) {
QJsonObject obj = items.at(i).toObject();
if(obj.contains("default")) {
QString type = obj.value("type").toString();
if(type == "int") {
obj.insert("current",QJsonValue(obj.value("default").toInt()));
}
else if(type == "text") {
obj.insert("current",QJsonValue(obj.value("default").toString()));
}
items.replace(i,obj); // 반영
}
}
//qInfo() << items << __FUNCTION__;
#else // !RM_MODEL_TYPE_AN6000
// CS-32FH
unsigned char def[SETTINGS_CFG_SIZE] = {0x00,// 0
0x00,// 1
0x00,// 2
0x00,// 3
0x00,// 4
0x00,// 5
0x00,// 6
0x00,// 7
0x00,// 8
0x00,// 9
0x00,// 10
0x00,// 11
0x00,// 12
0x00,// 13
0x00,// 14
0x00,// 15
0x00,// 16
0x00,// 17
0x00,// 18
0x00,// 19
0x00,// 20
0x02,// 21 // SD_SENSOR = SENSOR_MIDDLE
0x00,// 22 // SD_SIZE = SIZE_800x600
0x00,// 23 // SD_FRAME = FRAME_20
0x04,// 24 // SD_VOLUME = VOLUME_4
0x01,// 25 // SD_VOICE = VOICE_ENABLE
0x00 // RESERVED
};
memcpy(CFG::data,def,SETTINGS_CFG_SIZE);
#endif // RM_MODEL_TYPE_AN6000
}
void CFG::backup()
{
#if !(RM_MODEL == RM_MODEL_TYPE_AN6000)
memcpy(CFG::stored,CFG::data,SETTINGS_CFG_SIZE);
#endif // #if (RM_MODEL == RM_MODEL_TYPE_AN6000)
}
void CFG::restore()
{
#if !(RM_MODEL == RM_MODEL_TYPE_AN6000)
memcpy(CFG::data,CFG::stored,SETTINGS_CFG_SIZE);
#endif // #if !(RM_MODEL == RM_MODEL_TYPE_AN6000)
}
bool CFG::load(QString path)
{
#if (RM_MODEL == RM_MODEL_TYPE_AN6000)
// CLEAR
while(CFG::items.count()) {
CFG::items.pop_back();
}
#if (AN6000_YEC)
const char* fname = ":/html/an6000_cfg_yec.json";
#else
const char* fname = ":/html/an6000_cfg.json";
#endif
QFile file(fname); // ":/html/an6000_cfg.json"
if(file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QByteArray data = file.readAll();
file.close();
CFG::items = QJsonDocument::fromJson(data).array();
setDefault();
}
#endif // #if (RM_MODEL == RM_MODEL_TYPE_AN6000)
bool bSuccess = false;
if(QFile::exists(path)) {
#if (RM_MODEL == RM_MODEL_TYPE_AN6000)
#if (ENCODE_CFG_BASE64)
QStringList lines = FMBase64::load(path,&isEncoded);
qInfo() << lines << __FUNCTION__;
for(int i=0;i<lines.size();i++) {
QString line = lines.at(i);
QStringList items = line.split('=');
if(items.size() != 2) {
qInfo() << "ERROR:" << line << __FUNCTION__;
continue;
}
QString key = items.at(0);
key = key.trimmed();
QString value = items.at(1);
value = value.trimmed();
int kidx = -1;
QJsonObject obj = _findType(key,&kidx);
if(obj.isEmpty() || kidx < 0) {
qInfo() << "KEY ERROR:" << line << __FUNCTION__;
continue;
}
QString otype = obj.value("type").toString();
if(otype == "text") {
obj.insert("current",QJsonValue(value));
CFG::items.replace(kidx,obj);
} else if (otype == "int") {
obj.insert("current",QJsonValue(value.toInt()));
CFG::items.replace(kidx,obj);
}
}
#else // #if (ENCODE_CFG_BASE64)
QFile file(path);
if(file.exists() && file.open(QIODevice::ReadOnly))
{
QTextStream in(&file);
while(!in.atEnd()) {
QString line = in.readLine();
QStringList items = line.split('=');
if(items.size() != 2) {
qInfo() << "ERROR:" << line << __FUNCTION__;
continue;
}
QString key = items.at(0);
key = key.trimmed();
QString value = items.at(1);
value = value.trimmed();
int kidx = -1;
QJsonObject obj = _findType(key,&kidx);
if(obj.isEmpty() || kidx < 0) {
qInfo() << "KEY ERROR:" << line << __FUNCTION__;
continue;
}
QString otype = obj.value("type").toString();
if(otype == "text") {
obj.insert("current",QJsonValue(value));
CFG::items.replace(kidx,obj);
} else if (otype == "int") {
obj.insert("current",QJsonValue(value.toInt()));
CFG::items.replace(kidx,obj);
}
}
file.close();
}
#endif // #else // #if (ENCODE_CFG_BASE64)
#else // !AN6000
FILE* f = fopen(path.toLocal8Bit(),"r+b");
if(f != NULL)
{
unsigned char d[SETTINGS_CFG_SIZE] = {0,};
size_t read = fread(d,1,SETTINGS_CFG_SIZE,f);
fclose(f);
#if (CFG_SEARCH_DISK)
bSuccess = read > 0 && strncmp(SETTINGS_TAG,(const char*)d,strlen(SETTINGS_TAG)) == 0;
#else
bSuccess = read > 0;
#endif
if(bSuccess == true)
{
memcpy(CFG::data,d,SETTINGS_CFG_SIZE);
}
else
{
setDefault();
}
}
#endif // #else // !AN6000
}
CFG::backup(); // 현재 데이터 보존
return bSuccess;
}
bool CFG::save(QString path)
{
bool bSuccess = false;
#if (RM_MODEL == RM_MODEL_TYPE_AN6000)
#if (ENCODE_CFG_BASE64)
QStringList lines = QStringList();
for(int i=0;i<items.size();i++) {
QJsonObject obj = items.at(i).toObject();
QString type = obj.value("type").toString();
QString line;
line += obj.value("key").toString();
line += "=";
if(type == "int") {
line += QString::number(obj.value("current").toInt());
} else if (type == "text") {
line += obj.value("current").toString();
}
lines.append(line);
}
FMBase64::save(path,lines,true); // isEncoded // 항상 인코딩된 상태로 저장
#else // ENCODE_CFG_BASE64
QFile file(path);
if (file.open(QIODevice::WriteOnly)) {
QTextStream stream(&file);
//stream << "something" << endl;
for(int i=0;i<items.size();i++) {
QJsonObject obj = items.at(i).toObject();
QString type = obj.value("type").toString();
QString line;
line += obj.value("key").toString();
line += "=";
if(type == "int") {
line += QString::number(obj.value("current").toInt());
} else if (type == "text") {
line += obj.value("current").toString();
}
stream << line << endl;
qInfo() << line << __FUNCTION__;
}
file.close();
}
#endif // ENCODE_CFG_BASE64
#else // !AN6000
FILE* f = fopen(path.toLocal8Bit(),"w+b");
if(f != NULL)
{
size_t write = fwrite(CFG::data,1,SETTINGS_CFG_SIZE,f);
bSuccess = write > 0;
fclose(f);
}
#endif // #if (RM_MODEL = RM_MODEL_TYPE_AN6000)
CFG::backup();
return bSuccess;
}
#endif // #if (USE_DEVICE_SETTINGS && !(RM_MODEL_EMT_KR))

View File

@@ -0,0 +1,54 @@
#ifndef RM_SETTINGS_CFG_STANDARD_H
#define RM_SETTINGS_CFG_STANDARD_H
#if (USE_DEVICE_SETTINGS && !(RM_MODEL_EMT_KR))
#include "../rm_include.h"
#include <QObject>
#if (RM_MODEL == RM_MODEL_TYPE_AN6000)
#include <QJsonArray>
#endif
#if !(RM_MODEL == RM_MODEL_TYPE_AN6000)
#define SETTINGS_CFG_SIZE 0x2D // 45 BYTE
typedef enum
{
SD_SENSOR = 21, // SENSOR_LOW = 0, SENSOR_LOW_MID, SENSOR_MIDDLE, SENSOR_MID_HI,SENSOR_HIGH
SD_SIZE = 22, // SIZE_800x600, SIZE_640x320
SD_FRAME = 23, // FRAME_20, FRAME_25, FRAME_30
SD_VOLUME = 24, // VOLUME_0~5
SD_VOICE = 25, // VOICE_DISABLE, VOICE_ENABLE
} SD_TYPE;
#endif // !RM_MODEL_TYPE_AN6000
class CFG : public QObject
{
Q_OBJECT
public:
#if (RM_MODEL == RM_MODEL_TYPE_AN6000)
#if (ENCODE_CFG_BASE64)
static bool isEncoded; // 암호화 되지 않은 CFG 파일의 경우 저장시에도 암호화 하지 않음
#endif // ENCODE_CFG_BASE64
static QJsonArray items;
static QJsonObject _findType(QString key,int* index);
#else
static unsigned char data[SETTINGS_CFG_SIZE];
static unsigned char stored[SETTINGS_CFG_SIZE];
#endif // #if (RM_MODEL == RM_MODEL_TYPE_AN6000)
static bool load(QString path);
static bool save(QString path);
static void backup();
static void restore();
static void setDefault();
signals:
public slots:
};
//#endif // #if (RM_MODEL == RM_SETTINGS_CFG_STANDARD_H)
#endif // (USE_DEVICE_SETTINGS && !(RM_MODEL_EMT_KR))
#endif // RM_SETTINGS_CFG_STANDARD_H

View File

@@ -0,0 +1,199 @@
#include "rm_settings_cfg_xdr6688.h"
#if (USE_DEVICE_SETTINGS)
#include <QDebug>
#include <stdio.h>
#include <QFile>
#if (RM_MODEL == RM_MODEL_TYPE_XLDR_88)
unsigned char CFG::data[SETTINGS_CFG_SIZE] = {0,};
unsigned char CFG::stored[SETTINGS_CFG_SIZE] = {0,};
MODEL_XDR CFG::model = MODEL_XDR_NOT_DEFINED;
void CFG::setDefault()
{
switch (CFG::model) {
case MODEL_XDR_66:
CFG66::setDefault();
break;
case MODEL_XDR_88:
CFG88::setDefault();
break;
default:
break;
}
}
void CFG::backup()
{
memcpy(CFG::stored,CFG::data,SETTINGS_CFG_SIZE);
}
void CFG::restore()
{
memcpy(CFG::data,CFG::stored,SETTINGS_CFG_SIZE);
}
QString CFG::modelName()
{
switch (CFG::model)
{
case MODEL_XDR_66:
return "XDR-66";
case MODEL_XDR_88:
return "XLDR-88";
default:
qInfo() << "MODEL NOT DEFINED";
}
return "";
}
bool CFG::setModel(QString modelString)
{
bool bSuccess = true;
if(modelString == "XDR-66") {
CFG::model = MODEL_XDR_66;
}
else if(modelString == "XLDR-88") {
CFG::model = MODEL_XDR_88;
}
else
{
bSuccess = false;
}
return bSuccess;
}
bool CFG::load(QString path)
{
bool bSuccess = false;
if(QFile::exists(path)) {
FILE* f = fopen(path.toLocal8Bit(),"r+b");
if(f != NULL)
{
size_t readSize = (model == MODEL_XDR_66) ? SETTINGS_CFG_SIZE : SETTINGS_CFG_SIZE_88;
unsigned char d[SETTINGS_CFG_SIZE] = {0,};
size_t read = fread(d,1,SETTINGS_CFG_SIZE,f);
fclose(f);
#if (CFG_SEARCH_DISK)
bSuccess = read > 0 && strncmp(SETTINGS_TAG,(const char*)d,strlen(SETTINGS_TAG)) == 0;
#else
bSuccess = read > 0;
#endif
if(bSuccess == true)
{
memcpy(CFG::data,d,read);
// EMS 데이터로 나머지는 초기화 해야함
if(read != readSize) {
setDefault();
}
}
else
{
setDefault();
}
}
}
CFG::backup(); // 현재 데이터 보존
return bSuccess;
}
bool CFG::save(QString path)
{
bool bSuccess = false;
FILE* f = fopen(path.toLocal8Bit(),"w+b");
if(f != NULL)
{
size_t write = 0;
if(model == MODEL_XDR_88) {
write = fwrite(CFG::data,1,SETTINGS_CFG_SIZE_88,f);
}
else {
write = fwrite(CFG::data,1,SETTINGS_CFG_SIZE,f);
}
bSuccess = write > 0;
fclose(f);
}
CFG::backup();
return bSuccess;
}
void CFG66::setDefault() {
// XDR-66
// SD_SENSOR = 21, // SENSOR_LOW = 0, SENSOR_LOW_MID, SENSOR_MIDDLE, SENSOR_MID_HI,SENSOR_HIGH
// SD_SIZE = 22, // SIZE_800x600, SIZE_640x320
// SD_FRAME = 23, // FRAME_20, FRAME_25, FRAME_30
// SD_VOLUME = 24, // VOLUME_0~5
// SD_VOICE = 25, // VOICE_DISABLE, VOICE_ENABLE
unsigned char def[SETTINGS_CFG_SIZE] = { 0x00,// 0
0x00,// 1
0x00,// 2
0x00,// 3
0x00,// 4
0x00,// 5
0x00,// 6
0x00,// 7
0x00,// 8
0x00,// 9
0x00,// 10
0x00,// 11
0x00,// 12
0x00,// 13
0x00,// 14
0x00,// 15
0x00,// 16
0x00,// 17
0x00,// 18
0x00,// 19
0x00,// 20
0x02,// 21 // SD_SENSOR = SENSOR_MIDDLE
0x00,// 22 // SD_SIZE = SIZE_800x600
0x00,// 23 // SD_FRAME = FRAME_20
0x04,// 24 // SD_VOLUME = VOLUME_4
0x01,// 25 // SD_VOICE = VOICE_ENABLE
0x00 // RESERVED
};
// 부분만 초기화함
memcpy(&CFG::data[21],&def[21],5);
}
void CFG88::setDefault() {
// XLDR-88
unsigned char def[SETTINGS_CFG_SIZE_88] = { 0x00,// 0
0x00,// 1
0x00,// 2
0x00,// 3
0x00,// 4
0x00,// 5
0x00,// 6
0x00,// 7
0x00,// 8
0x00,// 9
0x00,// 10
0x00,// 11
0x00,// 12
0x00,// 13
0x00,// 14
0x00,// 15
0x00,// 16
0x01,// 17 // SD88_SCREEN_SAVEER
0x02,// 18 // SD88_SCREEN_PIP
0x03,// 19 // SD88_SPEAKER
0x01,// 20 // SD88_MIC
0x00,// 21 // SD88_VOLUME
0x02,// 22 // SD88_GSENSOR_NORMAL
0x02,// 23 // SD88_GSENSOR_PARKING
0x00,// 24 // SD88_PARKING_ON (2021/04/01 OFF)
0x00,// 25 // SD88_PARKING_VOLTAGE
0x00,// 26 // SD88_VIDEO_RESOLUTION
0x01,// 27 // SD88_ADMIN_PW (SD88_VIDEO_QUALITY) -> 비밀번호 ON:/OFF1
0x02,// 28 // SD88_VIDEO_FRAME
0x01,// 29 // SD88_VIDEO_SUB_CAMERA
0x01,// 30 // SD88_VIDEO_HDR
0x01,// 31 // SD88_VIDEO_NIGHT_VISION (2021/04/01 OFF)
};
// 부분만 초기화 함
//memcpy(&CFG::data[11],&def[11],21);
memcpy(&CFG::data[17],&def[17],15);
//memcpy(CFG::data,def,SETTINGS_CFG_SIZE_88);
}
#endif // #if (RM_MODEL == RM_MODEL_TYPE_XLDR_88)
#endif // #if (USE_DEVICE_SETTINGS)

View File

@@ -0,0 +1,106 @@
#ifndef RM_SETTINGS_CFG_XDR6688_H
#define RM_SETTINGS_CFG_XDR6688_H
#if (USE_DEVICE_SETTINGS)
#include "../rm_include.h"
#if (RM_MODEL == RM_MODEL_TYPE_XLDR_88)
#include <QObject>
#define SETTINGS_CFG_SIZE 45 // 45 BYTE
#define SETTINGS_CFG_SIZE_88 0x20 // 32 BYTE
typedef enum
{
MODEL_XDR_NOT_DEFINED = 0,
MODEL_XDR_66 = 1,
MODEL_XDR_88 = 2,
} MODEL_XDR;
typedef enum
{
SD_SENSOR = 21, // SENSOR_LOW = 0, SENSOR_LOW_MID, SENSOR_MIDDLE, SENSOR_MID_HI,SENSOR_HIGH
SD_SIZE = 22, // SIZE_800x600, SIZE_640x320
SD_FRAME = 23, // FRAME_20, FRAME_25, FRAME_30
SD_VOLUME = 24, // VOLUME_0~5
SD_VOICE = 25, // VOICE_DISABLE, VOICE_ENABLE
} SD_TYPE_66;
typedef enum
{
SD88_ADMIN_PW = 27, // 관리자 암호 지정 0:ON 1:OFF
// SYSTEM システム
SD88_SCREEN_SAVEER = 17, // 画面表示, 1分後画面OFF, 常時ON, 1分後時計画面
SD88_SCREEN_PIP = 18, // 録画画面, フロント, リア, フロント/リア,リア/フロント
SD88_SPEAKER = 19, // Off,1 Level,2 Level,3 Level,4 Level,5 Level
SD88_MIC = 20, // Off, On
// STORAGE, メモリ割当
SD88_VOLUME = 21, // メモリ割当: 常時録画重視, 駐車録画重視, イベント録画重視
// G-SENSOR, センサー感度
SD88_GSENSOR_NORMAL = 22, // 常時センサー感度: Off, Low, Middle-Low, Middle, Middle-High, High
SD88_GSENSOR_PARKING = 23, // 駐車センサー感度: Off, Low, Middle-Low, Middle, Middle-High, High
// PARKING MODE, 駐車録画
SD88_PARKING_ON = 24, // 駐車録画機能: Off, On
SD88_PARKING_VOLTAGE = 25, // 放電遮断電圧: 표시만
// VIDEO, 録画設定
SD88_VIDEO_RESOLUTION = 26, // 解像度: "1920*1080", "1280*720"
//SD88_VIDEO_QUALITY = 27, // 画質: 低,中, 高
SD88_VIDEO_FRAME = 28, // 録画フレーム数: 4.9,19.1,29.1
SD88_VIDEO_SUB_CAMERA = 29, // サブカメラ録画: Off, On
SD88_VIDEO_HDR = 30, // HDR: Off, On
SD88_VIDEO_NIGHT_VISION = 31, // ナイトビジョン: Off, On
} SD_TYPE_88;
class CFG : public QObject
{
Q_OBJECT
public:
static MODEL_XDR model;
static QString modelName();
static bool setModel(QString modelString);
static unsigned char data[SETTINGS_CFG_SIZE];
static unsigned char stored[SETTINGS_CFG_SIZE];
static bool load(QString path);
static bool save(QString path);
static void backup();
static void restore();
static void setDefault();
signals:
public slots:
};
// XDR-66
class CFG66 : public CFG
{
Q_OBJECT
public:
static void setDefault();
signals:
public slots:
};
// XDR-88
class CFG88 : public CFG
{
Q_OBJECT
public:
static void setDefault();
signals:
public slots:
};
#endif // #if (RM_MODEL)
#endif
#endif // RM_SETTINGS_CFG_XDR6688_H

View File

@@ -0,0 +1,442 @@
#include "rm_settings_window_base.h"
#if (USE_DEVICE_SETTINGS && !USE_DEVICE_SETTINGS_JSON)
#include "../fm_dimensions.h"
#include "../ui/title_widget.h"
#include "rm_combo_box.h"
#include "rm_radio_buttons.h"
#include "rm_group_combo_box.h"
#include "rm_group_radio_buttons.h"
#include "../core/fm_strings.h"
#include "rm_settings_cfg.h"
#include <QComboBox>
#include <QGridLayout>
#include <QDir>
#if (SETTINGS_WINDOW_SCROLL)
#include <QScrollArea>
#include <QScrollBar>
#include "../ui/fm_colors.h"
#endif
#if (DETECT_SETTING_USB_EJECT && !DETECT_USB_CHANGE)
#include "../core/rm_usb.h"
#include "../rm_application.h"
#endif
// 문자열 처리 UTF
// https://www.branah.com/unicode-converter
//bool RMSettingsWindowBase::loaded = false;
QString RMSettingsWindowBase::lastSettingDisk = "";
//const int gComboBoxWidth = 200;
//const int gTitleWidth = 50;
#if (RM_MODEL_EMT_KR)
static const QStringList gOnOff = QStringList() << "ON" << "OFF";
#else // RM_MODEL_EMT_KR
static const QStringList gOnOff = QStringList() << MKU8("\xe3\x82\xaa\xe3\x83\xb3") << MKU8("\xe3\x82\xaa\xe3\x83\x95");
#endif // RM_MODEL_EMT_KR
static const QStringList gEOnOff = QStringList() << "ON" << "OFF";
#if (RM_MODEL_EMT_KR)
RMSettingsWindowBase::RMSettingsWindowBase(QWidget *parent) : RMPopup(parent,FMS::txt("settings"),"")
#else
RMSettingsWindowBase::RMSettingsWindowBase(QWidget *parent) : RMPopup(parent,"","title_settings.png")
#endif
{
// 이미 로딩됨
#if !(RM_MODEL_EMT_KR)
QString path = QDir::cleanPath(lastSettingDisk + SETTINGS_FILE_NAME);
// 모델 디스크가 아닐 경우
#if (MULTI_MODEL_VIEWER)
if(RMApp::isModelDisk(lastSettingDisk,NULL) == false)
#else // MULTI_MODEL_VIEWER
if(RMApp::isModelDisk(lastSettingDisk) == false)
#endif // MULTI_MODEL_VIEWER
{
CFG::setDefault();
#if !(DO_NOT_MAKE_CFG)
CFG::save(path);
#endif
}
else {
CFG::load(path);
}
#endif // #if !(RM_MODEL_EMT_KR)
#if (SETTINGS_WINDOW_SCROLL)
this->setFixedSize(SETTINGS_WINDOW_WIDTH,SETTINGS_WiNDOW_SCROLL_HEIGHT);
#else
this->setFixedSize(SETTINGS_WINDOW_WIDTH,SETTINGS_WiNDOW_HEIGHT);
#endif
//_contentWidget->setObjectName("bg_dark_widget");
layout = new QHBoxLayout(_contentWidget);
layout->setMargin(8);
layout->setSpacing(8);
QWidget* bwLeft = new QWidget(_buttonWidget);
_buttonLayout->addWidget(bwLeft);
QWidget* bwRight = new QWidget(_buttonWidget);
_buttonLayout->addWidget(bwRight);
QHBoxLayout* blLeft = new QHBoxLayout(bwLeft);
blLeft->setAlignment(Qt::AlignLeft|Qt::AlignVCenter);
QHBoxLayout* blRight = new QHBoxLayout(bwRight);
blRight->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
#if (LIVE_LANGUAGE2)
RMButton* defaultButton = RMButton::create2(bwRight,blRight,"button","default",QSize(120,30));
defaultButton->setText(FMS::txt("default"));
connect(defaultButton,SIGNAL(clicked()),SLOT(onDefault()));
RMButton* okButton = RMButton::create2(bwRight,blRight,"button","ok",QSize(120,30));
okButton->setText(FMS::txt("ok"));
connect(okButton,SIGNAL(clicked()),this,SLOT(onSaveAnAccept()));
RMButton* cancelButton = RMButton::create2(bwRight,blRight,"button","cancel",QSize(120,30));
cancelButton->setText(FMS::txt("cancel"));
connect(cancelButton,SIGNAL(clicked()),this,SLOT(onCancel()));
#else // LIVE_LANGUAGE2
// 初期値
QString title = MKU8("\xe5\x88\x9d\xe6\x9c\x9f\xe5\x80\xa4");
#if (LIVE_LANGUAGE_CHANGE)
if(RMLanguage::isJP() == false) { title = "Reset"; }
#endif
RMButton* defaultButton = RMButton::create(bwRight,blRight,"button",FMS::txt("default"),QSize(120,30));
//RMButton* defaultButton = RMButton::create(bwLeft,blLeft,"popup_button",title,QSize(120,30));
defaultButton->setText(FMS::txt("default"));
connect(defaultButton,SIGNAL(clicked()),SLOT(onDefault()));
// 適用
title = MKU8("\xe9\x81\xa9\xe7\x94\xa8");
#if (LIVE_LANGUAGE_CHANGE)
if(RMLanguage::isJP() == false) { title = "Save";}
#endif
//#if !(MODEL_WATEX)
// RMButton* applyButton = RMButton::create(bwRight,blRight,"popup_button",title,QSize(120,30));
// applyButton->setText(title);
// connect(applyButton,SIGNAL(clicked()),this,SLOT(onSave()));
//#endif
RMButton* okButton = RMButton::create(bwRight,blRight,"button",FMS::txt("ok"),QSize(120,30));
//RMButton* okButton = RMButton::create(bwRight,blRight,"popup_button",QString("OK"),QSize(120,30));
okButton->setText(FMS::txt("ok"));
connect(okButton,SIGNAL(clicked()),this,SLOT(onSaveAnAccept()));
#if (MODEL_WATEX)
title = MKU8("\xe3\x82\xad\xe3\x83\xa3\xe3\x83\xb3\xe3\x82\xbb\xe3\x83\xab"); // キャンセル
#else
title = MKU8("\xe9\x96\x89\xe3\x81\x98\xe3\x82\x8b"); // 閉じる
#endif
#if (LIVE_LANGUAGE_CHANGE)
if(RMLanguage::isJP() == false) { title = "Cancel";}
#endif
RMButton* cancelButton = RMButton::create(bwRight,blRight,"button",FMS::txt("cancel"),QSize(120,30));
cancelButton->setText(FMS::txt("cancel"));
// connect(okButton,SIGNAL(clicked()),this,SLOT(onOK()));
// RMButton* cancelButton = RMButton::create(bwRight,blRight,"popup_button",title,QSize(120,30));
// cancelButton->setText(title);
connect(cancelButton,SIGNAL(clicked()),this,SLOT(onCancel()));
/* 모델명 사용하지 않음
QLabel* modelName = new QLabel(_title->_toolBar);
modelName->setText(QString((const char*)CFG::data) + " ");
modelName->setObjectName("title_label");
_title->_toolBarLayout->insertWidget(0,modelName);
*/
#endif // #else // LIVE_LANGUAGE2
#if (DETECT_SETTING_USB_EJECT && !DETECT_USB_CHANGE)
_usb = new rm_usb(this);
_usb->registerEvent(this);
RMApplication::instance()->installNativeEventFilter(_usb);
connect(_usb,SIGNAL(usbChanged(bool,QString&)),SLOT(onUSBChange(bool,QString&)));
#endif // @DETECT_SETTING_USB_EJECT
#if (SETTINGS_WINDOW_SCROLL)
layout->setMargin(0);
layout->setSpacing(0);
// 스크롤 영역 생성
_contentScrollArea = new QScrollArea(_contentWidget);
_contentScrollArea->setStyleSheet("QScrollArea { background: transparent; }");
_contentScrollArea->setWidgetResizable( true );
layout->addWidget(_contentScrollArea);
_contentScrollWidget = new QWidget(); // _contentScrollArea
//_contentScrollWidget->setStyleSheet("QWidget { background: #FF0000;}");
_contentScrollLayout = new QHBoxLayout(_contentScrollWidget);
//ZERO_LAYOUT(_contentScrollLayout);
//_contentScrollLayout->setMargin(8);
_contentScrollLayout->setSpacing(8);
_contentScrollLayout->setContentsMargins(8,10,8,10); // 상하단 마진
_contentScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
_contentScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
// 자동 설정
//_contentScrollWidget->setFixedHeight(SETTINGS_WiNDOW_HEIGHT);
_contentScrollWidget->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
_contentScrollArea->setFrameShape(QFrame::NoFrame);
_contentScrollWidget->setObjectName("scroll2");
_contentScrollWidget->setStyleSheet("QWidget#scroll2 { background: transparent; }");
//_contentScrollWidget->setFixedHeight(SETTINGS_WiNDOW_HEIGHT);
_contentScrollArea->setWidget(_contentScrollWidget);
QString scrollStyle = "QScrollBar:vertical\
{\
border-width: 0px;\
border-style: solid;\
background: #3B3B3B;\
width: 10px;\
margin: 0px 0 0px 0;\
}\
QScrollBar::handle:vertical:disabled{background: #3B3B3B;}\
QScrollBar::handle:vertical\
{\
background: $HANDLE_COLOR$;\
min-height: 25px;\
margin: 2px 1px 2px 1px;\
}\
QScrollBar::add-line:vertical\
{\
border: none;\
background: none;\
height: 0px;\
width: 0px;\
subcontrol-position: bottom;\
subcontrol-origin: margin;\
}\
QScrollBar::sub-line:vertical\
{\
border: none;\
background: none;\
height: 0px;\
width: 0px;\
subcontrol-position: top;\
subcontrol-origin: margin;\
}\
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical\
{\
background: #3A3A3A;\
}\
QScrollBar::up-arrow:vertical\
{\
border: none;\
background: #3A3A3A;\
}\
QScrollBar::down-arrow:vertical\
{\
border: none;\
background: #5A5A5A;\
}";
_contentScrollArea->verticalScrollBar()->setStyleSheet(scrollStyle.replace("$HANDLE_COLOR$",QString().sprintf("#%06X",FM_SILDER_COLOR)));
#endif // SCROLL
}
#if (DETECT_SETTING_USB_EJECT && !DETECT_USB_CHANGE)
void RMSettingsWindowBase::onUSBChange(bool inserted, QString& drive)
{
// qInfo() << __FUNCTION__;
if(inserted == false && RMSettingsWindowBase::lastSettingDisk.contains(drive,Qt::CaseInsensitive)) {
reject();
}
}
#elif (DETECT_USB_CHANGE)
void RMSettingsWindowBase::onUsbRemoved()
{
reject();
}
#endif // @DETECT_SETTING_USB_EJECT
#if (USE_JSON_SETTINGS)
#else // #if (USE_JSON_SETTINGS)
RMGroupRadioButtons* RMSettingsWindowBase::_MGR_ONOFF(QWidget* parent,QString title,QLayout* layout,int type)
{
#if (LIVE_LANGUAGE_CHANGE && !RM_MODEL_EMT_KR)
if(RMLanguage::isJP() == false) { return _MGR(parent,title,layout,gEOnOff,type); }
#endif
return _MGR(parent,title,layout,gOnOff,type);
}
RMRadioButtons* RMSettingsWindowBase::_MR_ONOFF(QWidget* parent,QString title,QLayout* layout,int type,QLabel** titleLabel)
{
#if (LIVE_LANGUAGE_CHANGE && !RM_MODEL_EMT_KR)
if(RMLanguage::isJP() == false) { return _MR(parent,title,layout,gEOnOff,type,QList<int>(),titleLabel); }
#endif
return _MR(parent,title,layout,gOnOff,type,QList<int>(),titleLabel);
}
RMGroupRadioButtons* RMSettingsWindowBase::_MGR(QWidget *parent,QString title,QLayout* layout,QStringList items,int type,QList<int> indexMap)
{
RMGroupRadioButtons* gr = new RMGroupRadioButtons(parent,title,items,&CFG::data[type],indexMap);
layout->addWidget(gr);
return gr;
}
RMGroupRadioButtons* RMSettingsWindowBase::_MGRR(QWidget *parent,QString title,QLayout* layout,QStringList items,int type,QList<int> indexMap)
{
RMGroupRadioButtons* gr = new RMGroupRadioButtons(parent,title,items,&CFG::data[type],true,indexMap);
layout->addWidget(gr);
return gr;
}
RMRadioButtons* RMSettingsWindowBase::_MR(QWidget *parent,QString title,QLayout* layout,QStringList items,int type,QList<int> indexMap,QLabel** titleLabel)
{
RMRadioButtons* gr = NULL;
if(strcmp(layout->metaObject()->className(),"QGridLayout") == 0)
{
// (qobject_cast<QGridLayout*>)
QGridLayout* gl = (QGridLayout*)layout;
int row = ceil((double)layout->count() / 2.0);
QLabel* tl = new QLabel(this);
tl->setObjectName("text_normal_label");
tl->setText(title);
gl->addWidget(tl,row,0,Qt::AlignLeft);
// qInfo() << title << "MR row:" << row;
gr = new RMRadioButtons(parent,"",items,&CFG::data[type],indexMap);
gl->addWidget(gr,row,1,Qt::AlignLeft);
if(titleLabel != NULL) {
*titleLabel = tl;
}
}
else
{
gr = new RMRadioButtons(parent,title,items,&CFG::data[type],indexMap);
layout->addWidget(gr);
}
return gr;
}
void RMSettingsWindowBase::_MGT(QWidget *parent,QString title,QLayout* layout, QString text,QLabel** titleLabel)
{
if(strcmp(layout->metaObject()->className(),"QGridLayout") == 0)
{
QGridLayout* gl = (QGridLayout*)layout;
int row = ceil(((double)layout->count()) / 2.0);
int column = 0;
int columnSpan = 2;
if(title.length() > 0) {
QLabel* tl = new QLabel(this);
tl->setObjectName("text_normal_label");
tl->setText(title);
gl->addWidget(tl,row,0,Qt::AlignLeft);
column = 1;
columnSpan = 1;
if(titleLabel != NULL) {
*titleLabel = tl;
}
}
if(text.length() > 0)
{
QLabel* textLabel = new QLabel(parent);
textLabel->setObjectName("text_normal_label");
textLabel->setText(text);
gl->addWidget(textLabel,row,column,1,columnSpan,Qt::AlignLeft);
}
}
else
{
QLabel* textLabel = new QLabel(parent);
textLabel->setObjectName("text_normal_label");
textLabel->setText(text);
layout->addWidget(textLabel);
};
}
RMComboBox* RMSettingsWindowBase::_MC(QWidget *parent,QString title,QLayout* layout, QStringList items,int type,QList<int> indexMap,QLabel** titleLabel)
{
RMComboBox* c = NULL;
if(strcmp(layout->metaObject()->className(),"QGridLayout") == 0)
{
QGridLayout* gl = (QGridLayout*)layout;
int row = ceil(((double)layout->count()) / 2.0);
int column = 0;
int columnSpan = 2;
if(title.length() > 0) {
QLabel* tl = new QLabel(this);
tl->setObjectName("text_normal_label");
tl->setText(title);
gl->addWidget(tl,row,0,Qt::AlignLeft);
column = 1;
columnSpan = 1;
if(titleLabel != NULL) {
*titleLabel = tl;
}
}
//qInfo() << "row" << row << " column:" << column;
c = new RMComboBox(parent,"",items,&CFG::data[type],indexMap);
gl->addWidget(c,row,column,1,columnSpan,Qt::AlignLeft);
}
else
{
c = new RMComboBox(parent,title,items,&CFG::data[type],indexMap);
layout->addWidget(c);
}
return c;
}
RMGroupComboBox* RMSettingsWindowBase::_MGC(QWidget *parent,QString title,QLayout* layout,QStringList items,int type,QList<int> indexMap)
{
RMGroupComboBox* c = new RMGroupComboBox(parent,title,items,&CFG::data[type],indexMap);
layout->addWidget(c);
return c;
}
#endif // #else // #if (USE_JSON_SETTINGS)
void RMSettingsWindowBase::onSaveAnAccept()
{
#if (RM_MODEL_EMT_KR)
QString path = CFG::cfgPath;
#else // RM_MODEL_EMT_KR
QString path = QDir::cleanPath(lastSettingDisk + SETTINGS_FILE_NAME);
#endif // RM_MODEL_EMT_KR
if(validateData()) {
CFG::save(path);
afterSave();
accept();
}
}
RMSettingsWindowBase::~RMSettingsWindowBase()
{
}
void RMSettingsWindowBase::onSave()
{
QString path = QDir::cleanPath(lastSettingDisk + SETTINGS_FILE_NAME);
if(validateData()) {
CFG::save(path);
afterSave();
}
}
void RMSettingsWindowBase::onCancel()
{
CFG::restore();
reject();
}
void RMSettingsWindowBase::onDefault()
{
CFG::setDefault();
emit RMValueUpdater::instance()->updateByValues();
// 레코드 세팅 문제로 (Combo box event 발생하면 default 값이 save 됨)
CFG::setDefault();
}
#endif // #if (USE_DEVICE_SETTINGS)

View File

@@ -0,0 +1,131 @@
#ifndef RM_SETTINGS_WINDOW_BASE_H
#define RM_SETTINGS_WINDOW_BASE_H
#if (USE_DEVICE_SETTINGS && !USE_DEVICE_SETTINGS_JSON)
#include "../ui/rm_popup.h"
#include <QGroupBox>
#include <QRadioButton>
class RMRadioButtons;
class RMGroupComboBox;
class RMGroupRadioButtons;
class RMComboBox;
class QJsonObject;
class QScrollArea;
#if (DETECT_SETTING_USB_EJECT)
class rm_usb;
#endif
extern const QStringList gOnOff;
class RMSettingsWindowBase : public RMPopup
{
Q_OBJECT
public:
//static bool loaded;
static QString lastSettingDisk;
explicit RMSettingsWindowBase(QWidget *parent = 0);
~RMSettingsWindowBase();
virtual void afterSave() = NULL;
virtual bool validateData() = NULL;
protected:
#if (DETECT_SETTING_USB_EJECT)
rm_usb* _usb;
#endif // @DETECT_SETTING_USB_EJECT
#if (SETTINGS_WINDOW_SCROLL)
QScrollArea* _contentScrollArea;
QWidget* _contentScrollWidget;
QHBoxLayout* _contentScrollLayout;
#endif // SCROLL
QHBoxLayout* layout;
// ON,OFF 기능만 하는 라디오 버튼 생성
#if (USE_JSON_SETTINGS)
RMGroupRadioButtons* _MGR_ONOFF(QWidget* parent,
QJsonObject* object,
QLayout* layout);
RMGroupRadioButtons* _MGR(QWidget *parent,
QLayout* layout,
QJsonObject* object);
RMGroupComboBox* _MGC(QWidget *parent,
QLayout* layout,
QJsonObject* object);
#else // USE_JSON_SETTINGS
RMGroupRadioButtons* _MGR_ONOFF(QWidget* parent,
QString title,
QLayout* layout,
int type
);
RMRadioButtons* _MR_ONOFF(QWidget* parent,
QString title,
QLayout* layout,
int type,
QLabel** titleLabel = NULL);
RMGroupRadioButtons* _MGR(QWidget *parent,
QString title,
QLayout* layout,
QStringList items,
int type,
QList<int> indexMap = QList<int>());
RMGroupRadioButtons* _MGRR(QWidget *parent,
QString title,
QLayout* layout,
QStringList items,
int type,
QList<int> indexMap = QList<int>());
RMRadioButtons* _MR(QWidget *parent,
QString title,
QLayout* layout,
QStringList items,
int type,
QList<int> indexMap = QList<int>(),
QLabel** titleLabel = NULL);
void _MGT(QWidget *parent,QString title,QLayout* layout, QString text,QLabel** titleLabel = NULL);
RMComboBox* _MC(QWidget *parent,
QString title,
QLayout* layout,
QStringList items,
int type,
QList<int> indexMap = QList<int>(),
QLabel** titleLabel = NULL);
RMGroupComboBox* _MGC(QWidget *parent,
QString title,
QLayout* layout,
QStringList items,
int type,
QList<int> indexMap = QList<int>());
#endif // USE_JSON_SETTINGS
protected slots:
void onDefault();
void onSave();
void onCancel();
void onSaveAnAccept();
// DETECT_USB_CHANGE 사용시에는 직접 처리하여 필요없음???
#if (DETECT_SETTING_USB_EJECT && !DETECT_USB_CHANGE)
void onUSBChange(bool inserted, QString& drive);
#elif (DETECT_USB_CHANGE)
void onUsbRemoved();
#endif
};
#endif // #if (USE_DEVICE_SETTINGS)
#endif // RM_SETTINGS_WINDOW_BASE_H

View File

@@ -0,0 +1,86 @@
#include "rm_text_edit.h"
#if (USE_JSON_SETTINGS)
#include <QJsonObject>
#include <QStyleOption>
#include <QPainter>
#include "rm_settings_cfg.h"
RMTextEdit::RMTextEdit(QWidget *parent,QString title, int index) : QWidget(parent), RMValueSelector(index)
{
setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
layout = new QHBoxLayout(this);
layout->setAlignment(Qt::AlignLeading | Qt::AlignVCenter);
layout->setContentsMargins(0,2,8,2);
layout->setSpacing(3);
if(!title.isEmpty())
{
titleLabel = new QLabel(this);
titleLabel->setObjectName("text_normal_label");
titleLabel->setText(title);
layout->addWidget(titleLabel);
}
else
{
titleLabel = NULL;
}
QJsonObject obj = CFG::items.at(index).toObject();
edit = new QLineEdit(this);
edit->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
edit->setObjectName("file");
edit->setText(obj.value("current").toString());
/*
if(obj.contains("regex")) {
QRegExp re(obj.value("regex").toString());
QRegExpValidator *validator = new QRegExpValidator(re, this);
edit->setValidator(validator);
}
if(obj.contains("input_mask")) {
edit->setInputMask(obj.value("input_mask").toString());
}
*/
connect(edit,SIGNAL(textChanged(const QString &)),SLOT(onTextChanged(const QString &)));
layout->addWidget(edit);
connect(RMValueUpdater::instance(),SIGNAL(updateByValues()),SLOT(onUpdateByValue()));
}
void RMTextEdit::onTextChanged(const QString &text) {
QJsonObject obj = CFG::items.at(_object).toObject();
obj.insert("current",QJsonValue(text));
CFG::items.replace(_object,obj);
//qInfo() << text << __FUNCTION__;
}
void RMTextEdit::paintEvent(QPaintEvent *pe)
{
Q_UNUSED(pe);
QStyleOption o;
o.initFrom(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &o, &p, this);
}
void RMTextEdit::onUpdateByValue()
{
QJsonObject obj = CFG::items.at(_object).toObject();
edit->setText(obj.value("current").toString());
}
RMGroupTextEdit::RMGroupTextEdit(QWidget *parent,int index) : QGroupBox(parent)
{
setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
setObjectName("settings");
QJsonObject obj = CFG::items.at(index).toObject();
if(obj.contains("title")) {
setTitle(obj.value("title").toString());
}
layout = new QVBoxLayout(this);
layout->setAlignment(Qt::AlignVCenter | Qt::AlignLeading);
layout->setContentsMargins(8,2,8,2);
layout->setSpacing(3);
text = new RMTextEdit(this,"",index);
layout->addWidget(text);
}
#endif // #if (USE_JSON_SETTINGS)

View File

@@ -0,0 +1,44 @@
#ifndef RM_TEXT_EDIT_H
#define RM_TEXT_EDIT_H
#include <QObject>
#include <QWidget>
#include <QLineEdit>
#include <QList>
#include <QHBoxLayout>
#include <QLabel>
#include <QGroupBox>
#include "rm_value_selector.h"
#if (USE_JSON_SETTINGS)
class RMTextEdit : public QWidget, public RMValueSelector
{
Q_OBJECT
public:
explicit RMTextEdit(QWidget *parent,QString title, int index);
QHBoxLayout* layout;
QLineEdit* edit;
QLabel* titleLabel;
private:
void paintEvent(QPaintEvent *pe);
signals:
void selected(int index);
private slots:
void onTextChanged(const QString &text);
void onUpdateByValue();
};
class RMGroupTextEdit : public QGroupBox
{
Q_OBJECT
public:
explicit RMGroupTextEdit(QWidget *parent,int index);
QVBoxLayout* layout;
RMTextEdit* text;
};
#endif // USE_JSON_SETTINGS
#endif // RM_TEXT_EDIT_H

View File

@@ -0,0 +1,39 @@
#include "rm_value_selector.h"
#if (USE_JSON_SETTINGS)
#include <QJsonArray>
#include <QJsonObject>
#include "rm_settings_cfg.h"
RMValueSelector::RMValueSelector(int object)
{
_object = object;
}
unsigned char RMValueSelector::realValue(int index)
{
QJsonObject obj = CFG::items.at(_object).toObject();
if(obj.contains("index_map")) {
QJsonArray a = obj.value("index_map").toArray();
return a.at(index).toInt();
}
return index;
}
int RMValueSelector::realIndex(unsigned char value)
{
QJsonObject obj = CFG::items.at(_object).toObject();
if(obj.contains("index_map")) {
QJsonArray a = obj.value("index_map").toArray();
for(int i=0;i<a.size();i++) {
if(a.at(i).toInt() == value) {
return i;
}
}
}
return value;
}
#else // USE_JSON_SETTINGS
RMValueSelector::RMValueSelector(unsigned char* value,QList<int> indexMap)
{
_value = value;
_indexMap = indexMap;
}
#endif // USE_JSON_SETTINGS

View File

@@ -0,0 +1,60 @@
#ifndef RM_VALUE_SELECTOR_H
#define RM_VALUE_SELECTOR_H
#include <QObject>
#include <QList>
class QJsonObject;
class RMValueSelector
{
public:
#if (USE_JSON_SETTINGS)
explicit RMValueSelector(int object);
protected:
int _object;
unsigned char realValue(int index);
int realIndex(unsigned char value);
#else // USE_JSON_SETTINGS
public:
RMValueSelector(unsigned char* value,QList<int> indexMap);
unsigned char realValue(int index)
{
return _indexMap.isEmpty() ? (unsigned char)index : (unsigned char)_indexMap[index];
}
int realIndex(unsigned char value)
{
return _indexMap.isEmpty() ? (int)value : _indexMap.indexOf(value);
}
protected:
QList<int> _indexMap;
unsigned char* _value;
#endif // USE_JSON_SETTINGS
};
class RMValueUpdater : public QObject
{
Q_OBJECT
public:
explicit RMValueUpdater(QObject* parent = nullptr) : QObject(parent){
}
// SIGNAL 연동을 위해 사용
static RMValueUpdater* instance()
{
static RMValueUpdater * _instance = 0;
if ( _instance == 0 ) {
_instance = new RMValueUpdater();
}
return _instance;
}
signals:
void updateByValues();
};
#endif // RM_VALUE_SELECTOR_H

View File

@@ -0,0 +1,19 @@
#ifndef WINDOW_SETTINGS_COMMON_H
#define WINDOW_SETTINGS_COMMON_H
#if (USE_DEVICE_SETTINGS)
#include "../rm_include.h"
#if (USE_DEVICE_SETTINGS_JSON)
#include "window_settings_json.h"
#else // USE_DEVICE_SETTINGS_JSON
#if (RM_MODEL == RM_MODEL_TYPE_XLDR_88)
#include "window_settings_xdr6688.h"
#elif (RM_MODEL_EMT_KR)
#include "window_settings_emt_kr.h"
#else
#include "window_settings_standard.h"
#endif
#endif // USE_DEVICE_SETTINGS_JSON
#endif // USE_DEVICE_SETTINGS
#endif // WINDOW_SETTINGS_COMMON_H

View File

@@ -0,0 +1,420 @@
#include "window_settings_emt_kr.h"
#if (USE_DEVICE_SETTINGS && !USE_DEVICE_SETTINGS_JSON && RM_MODEL_EMT_KR)
#include "../fm_dimensions.h"
#include "../ui/title_widget.h"
#include "rm_combo_box.h"
#include "rm_radio_buttons.h"
#include "rm_group_combo_box.h"
#include "rm_group_radio_buttons.h"
#include "rm_text_edit.h"
//#include "rm_setting_time.h"
#include <QComboBox>
#include <QGridLayout>
#include <QDir>
#include <QMessageBox>
#include <QJsonObject>
#include "rm_settings_cfg_emt_kr.h"
#if (DETECT_SETTING_USB_EJECT)
#include "../core/rm_usb.h"
#include "../rm_application.h"
#endif
// 문자열 처리 UTF
// https://www.branah.com/unicode-converter
const int gComboBoxWidth = 200;
const int gTitleWidth = 50;
const int defaultHeight = 55;
const int subHeight = 30;
WindowSettings::WindowSettings(QWidget *parent) : RMSettingsWindowBase(parent)
{
QWidget* row = NULL;
QVBoxLayout* rowLayout = _createRow(&row);
_createRowS(row,rowLayout,CFG::items,0,200);
_refresh(CFG::items,0);
}
void WindowSettings::afterSave()
{
}
bool WindowSettings::validateData()
{
return true;
}
QStringList WindowSettings::_valueStrings(QJsonObject& object,const char* key)
{
QStringList ret = QStringList();
if(object.contains(key)) {
QJsonArray array = object.value(key).toArray();
for(int i=0;i<array.size();i++) {
ret.append(array.at(i).toString());
}
}
return ret;
}
QList<int> WindowSettings::_valueIndices(QJsonObject& object,const char* key)
{
QList<int> ret = QList<int>();
if(object.contains(key)) {
QJsonArray array = object.value(key).toArray();
for(int i=0;i<array.size();i++) {
ret.append(array.at(i).toInt());
}
}
return ret;
}
int WindowSettings::_getObjectIndex(QString& id)
{
QObject* obj = _controlByNames.value(id);
RMComboBox* combo = qobject_cast<RMComboBox*>(obj);
if(combo != NULL) {
return combo->comboBox->currentIndex();
}
else {
RMRadioButtons* radios = qobject_cast<RMRadioButtons*>(obj);
if(radios != NULL) {
return radios->currentIndex();
}
}
return -1;
}
void WindowSettings::_refresh(QJsonArray items,int depth)
{
for(int i=0;i<items.size();i++) {
QJsonObject obj = items.at(i).toObject();
// sub group
if(obj.contains("group")) {
_refresh(obj.value("items").toArray(),depth+1);
}
if(obj.contains("events")) {
//qInfo() << obj << __FUNCTION__;
QStringList eventList = _valueStrings(obj,"events");
QString keyString = obj.value("key").toString();
int index = _getObjectIndex(keyString);
//qInfo() << index << __FUNCTION__;
_processEvent(keyString,eventList,index);
}
}
}
void WindowSettings::_createRowS(QWidget* parent, QLayout* playout, QJsonArray items, int depth, int value_column_width)
{
for(int i=0;i<items.size();i++) {
QJsonObject obj = items.at(i).toObject();
if(obj.contains("hidden")) {
continue;
}
// 열 변환
if(obj.contains("column_break")) {
QWidget* row = NULL;
QVBoxLayout* rowLayout = _createRow(&row);
parent = row;
playout = rowLayout;
}
if(!obj.contains("key")) {
qInfo() << "ERROR:" << obj << __FUNCTION__;
continue;
}
QString key = obj.value("key").toString();
QString title = "";
if(obj.contains("title")) {
title = obj.value("title").toString();
}
QObject* keyObject = NULL;
// 그룹 타입의 경우
if(obj.contains("group")) {
QGroupBox* g = new QGroupBox(parent);
keyObject = g;
//g->setFixedHeight(160);
g->setObjectName("settings");
playout->addWidget(g);
g->setTitle(title);
QLayout *gg = NULL;
if(!obj.contains("no_grid")) {
QGridLayout* gl = new QGridLayout(g);
gl->setMargin(8);
gl->setSpacing(4);
gg = gl;
} else {
QVBoxLayout* gl = new QVBoxLayout(g);
gl->setMargin(8);
gl->setSpacing(4);
gg = gl;
}
int vcw = value_column_width;
if(obj.contains("value_column_width")) {
vcw = obj.value("value_column_width").toInt(value_column_width);
}
_createRowS(g,gg,obj.value("items").toArray(),depth+1,vcw);
}
else if(obj.contains("control")) {
QString ctype = obj.value("control").toString();
if(ctype == "radio" || ctype == "combo") {
if (!obj.contains("value_strings") || !obj.contains("offset")) {
qInfo() << "ERROR ITEM:" << obj << __FUNCTION__;
continue;
}
QStringList items = _valueStrings(obj,"value_strings"); // .value("value_strings").toArray()
int offset = obj.value("offset").toInt();
QList<int> indexMap = QList<int>();
if(obj.contains("values")) {
indexMap = _valueIndices(obj,"values");
}
QLabel* titleLabel = NULL;
if(ctype == "radio") {
RMRadioButtons* rrb = NULL;
if(depth == 0 || obj.contains("group_box")) {
RMGroupRadioButtons* rb = _MGR(parent,title,playout,items,offset,indexMap);
rb->setFixedHeight(defaultHeight);
rrb = rb->radioButtons;
} else {
rrb = _MR(parent,title,playout,items,offset,indexMap,&titleLabel);
rrb->setFixedHeight(subHeight);
rrb->setFixedWidth(value_column_width);
//titleLabel->setFixedWidth(100);
}
keyObject = rrb;
if(obj.contains("events")) {
connect(rrb,SIGNAL(selected(int)),SLOT(onSelected(int)));
}
if(obj.contains("disabled")) {
rrb->setEnabled(false);
}
} else {
RMComboBox* ccb = NULL;
if(depth == 0 || obj.contains("group_box")) {
RMGroupComboBox* cb = _MGC(parent,title,playout,items,offset,indexMap);
cb->comboBox->setFixedWidth(gComboBoxWidth);
cb->setFixedHeight(defaultHeight);
ccb = cb->comboBox;
} else {
ccb = _MC(parent,title,playout,items,offset,indexMap,&titleLabel);
ccb->setFixedHeight(subHeight);
ccb->setFixedWidth(value_column_width);
//titleLabel->setFixedWidth(100);
}
keyObject = ccb;
if(obj.contains("events")) {
connect(ccb,SIGNAL(selected(int)),SLOT(onSelected(int)));
}
if(obj.contains("disabled")) {
//qInfo() << "disabled" << title << __FUNCTION__;
ccb->setEnabled(false);
}
}
}
}
if(keyObject != NULL) {
_controlByNames.insert(key,keyObject);
_controlByObjects.insert(keyObject,key);
// 이벤트 추가
if(obj.contains("events")) {
QStringList eventList = _valueStrings(obj,"events");
// QJsonArray events = obj.value("events").toArray();
// QStringList eventList = QStringList();
// for(int e=0;e<events.size();e++) {
// eventList.append(events.at(e).toString());
// }
_controlEvents.insert(keyObject,eventList);
}
}
}
}
void WindowSettings::_processEvent(QString& key, QStringList& eventStrings, int index)
{
for(int e=0;e<eventStrings.size();e++) {
QString event = eventStrings.at(e);
if(event.startsWith("UPDATEVALUES")) {
_processUpdateEvent(key,event,index);
}
else if(event.startsWith("ENABLED")) {
_processEnableEvent(key,event,index);
}
else if (event.startsWith("UPDATE_OTHER_BY_EVENTS")) {
_processUpdateOtherEvent(key,event,index);
}
}
}
void WindowSettings::_setObjectIndex(QObject* sender, int index)
{
RMComboBox* combo = qobject_cast<RMComboBox*>(sender);
if(combo != NULL) {
combo->comboBox->setCurrentIndex(index);
}
else {
RMRadioButtons* radios = qobject_cast<RMRadioButtons*>(sender);
if(radios != NULL) {
radios->setCurrentIndex(index);
}
}
}
void WindowSettings::_setObjectEnable(QObject* sender, bool enabled)
{
RMComboBox* combo = qobject_cast<RMComboBox*>(sender);
if(combo != NULL) {
combo->setEnabled(enabled);
}
else {
RMRadioButtons* radios = qobject_cast<RMRadioButtons*>(sender);
if(radios != NULL) {
radios->setEnabled(enabled);
}
}
}
void WindowSettings::_processUpdateOtherEvent(QString& key, QString& event, int index)
{
Q_UNUSED(index)
Q_UNUSED(key)
QStringList args = event.split(",");
if(args.size() < 2) {
return;
}
//qInfo() << "key:" << key << __FUNCTION__;
// SD_OMAKASE의 항목을 확인하여
QString target = args.at(1); // = SD_OMAKASE
QObject* tt = _controlByNames.value(target); // 프리셋 컨트롤
// "UPDATEVALUES,0,SD_DRIVERECMODE=0,SD_PARKINGRECMODE=4,SD_VIDEOQUALITY=1,SD_VIDEOFRAME=1",
// "UPDATEVALUES,1,SD_DRIVERECMODE=0,SD_PARKINGRECMODE=1,SD_VIDEOQUALITY=2,SD_VIDEOFRAME=1",
// "UPDATEVALUES,2,SD_DRIVERECMODE=0,SD_PARKINGRECMODE=3,SD_VIDEOQUALITY=0,SD_VIDEOFRAME=0"
QStringList targetEvents = _controlEvents.value(tt);
for(int e=0;e<targetEvents.size();e++) {
// "UPDATEVALUES,0,SD_DRIVERECMODE=0,SD_PARKINGRECMODE=4,SD_VIDEOQUALITY=1,SD_VIDEOFRAME=1",
QStringList eargs = targetEvents.at(e).split(",");
// UPDATEVALUES 항목이 아닌 경우 무시
if(eargs.at(0) != "UPDATEVALUES") {
//qInfo() << eargs.at(0) << __FUNCTION__;
continue;
}
// 각 KEY=VALUE 확인 모든 조합이 일치하면
bool bAllEqual = true;
for(int a=2;a<eargs.size();a++) {
QString etarget = eargs[a]; // SD_DRIVERECMODE=0
QStringList etargetList = etarget.split("=");
QString etargetKey = etargetList.at(0); // SD_DRIVERECMODE
int etargetValue = etargetList.at(1).toInt(); // 0
int eidx = _getObjectIndex(etargetKey); // 현재 컨트롤 값
//QObject* ct = _controlByNames.value(etargetKey); // 컨트롤 값 확인
// 조합이 틀린경우 다음 UPDATEVALUES 로 이동
if(eidx != etargetValue) {
bAllEqual = false;
break;
}
}
// 모든 항목이 일치하는 조합이 발생하면 해당 컨트롤 업데이트 후 종료
if(bAllEqual) {
int foundIndex = eargs.at(1).toInt(); // UPDATEVALUES,0 -> 0
_setObjectIndex(tt,foundIndex);
return;
}
}
// 발견된 조합이 없을 경우 NOT_FOUND 확인 (VERIFYVALUES_BY,SD_OMAKASE,NOT_FOUND=4)
if(args.size() >= 3) {
QString nf = args.at(2);
if(nf.startsWith("NOT_FOUND")) {
int idx = nf.split("=").at(1).toInt();
//QObject* tt = _controlByNames.value(target);
_setObjectIndex(tt,idx);
}
}
//qInfo() << args << __FUNCTION__;
}
void WindowSettings::_processUpdateEvent(QString& key, QString& event, int index)
{
Q_UNUSED(key)
QStringList args = event.split(",");
// 업데이트 명령일 경우
if(args[1].toInt() != index) {
return;
}
// 각 업데이트 확인
for(int a=2;a<args.size();a++) {
QString target = args[a];
QStringList taget = target.split("=");
QString targetKey = taget.at(0);
int targetValue = taget.at(1).toInt();
QObject* tt = _controlByNames.value(targetKey);
_setObjectIndex(tt,targetValue);
}
}
void WindowSettings::_processEnableEvent(QString& key, QString& event, int index)
{
Q_UNUSED(key)
// SHOWHIDE,1,SD_PARKING_CUTOFF_NORMAL=1,SD_PARKING_CUTOFF_HYBRID=0,SD_PARKING_CUTOFF_ELECTRIC=0
QStringList args = event.split(",");
// 선택된 인덱스의 항목이 아닐 경우
if(args[1].toInt() != index) {
return;
}
// 각 업데이트 확인
for(int a=2;a<args.size();a++) {
QString target = args[a];
QStringList taget = target.split("=");
QString targetKey = taget.at(0);
int targetValue = taget.at(1).toInt();
QObject* tt = _controlByNames.value(targetKey);
//qInfo() << tt << taget << targetValue << __FUNCTION__;
_setObjectEnable(tt,targetValue == 1);
}
}
void WindowSettings::onSelected(int index)
{
QObject* s = sender();
// 정보 존재
if(_controlEvents.contains(s)) {
QStringList eventStrings = _controlEvents.value(s);
QString key = _controlByObjects[s];
_processEvent(key,eventStrings,index);
}
}
QVBoxLayout* WindowSettings::_createRow(QWidget** rw)
{
const int width = SETTINGS_WINDOW_WIDTH - _contentScrollLayout->contentsMargins().left() - _contentScrollLayout->contentsMargins().right() - _contentScrollLayout->spacing() - 20;
*rw = new QWidget(_contentScrollWidget);
(*rw)->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
(*rw)->setFixedWidth(width / 2);
_contentScrollLayout->addWidget(*rw);
_contentScrollLayout->setAlignment(Qt::AlignTop);
QVBoxLayout* l = new QVBoxLayout(*rw);
l->setMargin(0);
l->setSpacing(10);
l->setAlignment(Qt::AlignTop);
return l;
}
WindowSettings::~WindowSettings()
{
}
#endif // #if (USE_DEVICE_SETTINGS)
//#endif // #if (RM_MODEL == RM_MODEL_TYPE_CS_32FH)

View File

@@ -0,0 +1,68 @@
#ifndef WINDOW_SETTINGS_H
#define WINDOW_SETTINGS_H
#if (USE_DEVICE_SETTINGS && !USE_DEVICE_SETTINGS_JSON && RM_MODEL_EMT_KR)
#include "../rm_include.h"
#include "rm_settings_window_base.h"
#include "../ui/rm_popup.h"
#include <QGroupBox>
#include <QRadioButton>
#define SETTING_NUM_ROW 2
class RMTextEdit;
class WindowSettings : public RMSettingsWindowBase
{
Q_OBJECT
public:
explicit WindowSettings(QWidget *parent = 0);
~WindowSettings();
virtual void afterSave() override;
virtual bool validateData() override;
private:
QHash<QString,QObject*> _controlByNames; ///! 명칭으로 Contro(Combo,Button) 확인
QHash<QObject*,QString> _controlByObjects; ///! 컨트롤로 명칭 확인
QHash<QObject*,QStringList> _controlEvents; ///! 컨트롤로 이벤트 확인
// Utility
QVBoxLayout* _createRow(QWidget** rw);
void _createRowS(QWidget* parent, QLayout* playout, QJsonArray items, int depth, int value_column_width = -1);
// 이벤트가 존재하는 항목 확인하여 ENABLE/DISABLED 등 처리함
// 항목 사용에 따라 값을 변경
void _refresh(QJsonArray items,int depth);
// 설정 항목별 리스트를 확인
static QStringList _valueStrings(QJsonObject& object,const char* key);
// 설정 항목별 저장값을 확인
static QList<int> _valueIndices(QJsonObject& object,const char* key);
void _processEvent(QString& key, QStringList& events, int index);
//! \brief 항목이 변경되면 다른 항목을 업데이트함
//! \param event
//! \param index
void _processUpdateEvent(QString& key, QString& event, int index);
void _processEnableEvent(QString& key, QString& event, int index);
//! \brief 항목이 변경되면 다른 항목을 (UpdateEvent) 를 참조하여 업데이트함
//! eg. 화질설정이 변경되면 프리셋을 프리셋내의 UPDATES.. 를 확인하여 업데이트
//! \param event
//! \param index
void _processUpdateOtherEvent(QString& key, QString& event, int index);
int _getObjectIndex(QString& id);
void _setObjectIndex(QObject* sender, int index);
void _setObjectEnable(QObject* sender, bool enabled);
private slots:
void onSelected(int index);
};
#endif // #if (USE_DEVICE_SETTINGS)
#endif // WINDOW_SETTINGS_H

View File

@@ -0,0 +1,69 @@
#if (USE_DEVICE_SETTINGS_JSON)
#include "window_settings_json.h"
#include "../ui/title_widget.h"
#include "../core/fm_strings.h"
#include "rm_combo_box.h"
#include "rm_radio_buttons.h"
#include "rm_group_combo_box.h"
#include "rm_group_radio_buttons.h"
#include "rm_text_edit.h"
//#include "rm_settings_cfg_standard.h"
//#include "rm_setting_time.h"
#include <QComboBox>
#include <QGridLayout>
#include <QDir>
#include <QMessageBox>
#if (DETECT_SETTING_USB_EJECT)
#include "../core/rm_usb.h"
#include "../rm_application.h"
#endif
QString WindowSettings::lastSettingDisk = "";
WindowSettings::WindowSettings(QWidget *parent) : RMPopup(parent,"","title_settings.png")
{
if(RMApp::isModelDisk(lastSettingDisk) == false)
{
// CFG::setDefault();
// CFG::save(path);
}
else {
// CFG::load(path);
}
this->setFixedSize(640,480);
_contentWidget->setObjectName("bg_dark_widget");
layout = new QHBoxLayout(_contentWidget);
layout->setMargin(8);
layout->setSpacing(8);
QWidget* bwLeft = new QWidget(_buttonWidget);
_buttonLayout->addWidget(bwLeft);
QWidget* bwRight = new QWidget(_buttonWidget);
_buttonLayout->addWidget(bwRight);
QHBoxLayout* blLeft = new QHBoxLayout(bwLeft);
blLeft->setAlignment(Qt::AlignLeft|Qt::AlignVCenter);
QHBoxLayout* blRight = new QHBoxLayout(bwRight);
blRight->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
RMButton* defaultButton = RMButton::create2(bwRight,blRight,"button","default",QSize(120,30));
defaultButton->setText(FMS::txt("default"));
connect(defaultButton,SIGNAL(clicked()),SLOT(onDefault()));
RMButton* okButton = RMButton::create2(bwRight,blRight,"button","ok",QSize(120,30));
okButton->setText(FMS::txt("ok"));
connect(okButton,SIGNAL(clicked()),this,SLOT(onSaveAnAccept()));
RMButton* cancelButton = RMButton::create2(bwRight,blRight,"button","cancel",QSize(120,30));
cancelButton->setText(FMS::txt("cancel"));
connect(cancelButton,SIGNAL(clicked()),this,SLOT(onCancel()));
}
WindowSettings::~WindowSettings()
{
}
#endif // #if (USE_DEVICE_SETTINGS_JSON)

View File

@@ -0,0 +1,20 @@
#ifndef WINDOW_SETTINGS_JSON_H
#define WINDOW_SETTINGS_JSON_H
#if (USE_DEVICE_SETTINGS_JSON)
#include "../ui/rm_popup.h"
class WindowSettings : public RMPopup
{
Q_OBJECT
public:
explicit WindowSettings(QWidget *parent = 0);
~WindowSettings();
static QString lastSettingDisk;
private:
QHBoxLayout* layout;
QVBoxLayout* _createRow(QWidget** rw);
};
#endif // #if (USE_DEVICE_SETTINGS_JSON)
#endif // WINDOW_SETTINGS_JSON_H

View File

@@ -0,0 +1,184 @@
#include "window_settings_standard.h"
#if (USE_DEVICE_SETTINGS && !USE_DEVICE_SETTINGS_JSON && !RM_MODEL_EMT_KR)
#include "../fm_dimensions.h"
//#if (RM_MODEL == RM_MODEL_TYPE_STANDARD || RM_MODEL == RM_MODEL_TYPE_XLDR_88)
#include "../ui/title_widget.h"
#include "rm_combo_box.h"
#include "rm_radio_buttons.h"
#include "rm_group_combo_box.h"
#include "rm_group_radio_buttons.h"
#include "rm_text_edit.h"
#include "rm_settings_cfg_standard.h"
#include "rm_setting_time.h"
#include <QComboBox>
#include <QGridLayout>
#include <QDir>
#include <QMessageBox>
#if (DETECT_SETTING_USB_EJECT)
#include "../core/rm_usb.h"
#include "../rm_application.h"
#endif
#if (RM_MODEL == RM_MODEL_TYPE_AN6000)
#include <QJsonObject>
#endif // RM_MODEL_TYPE_AN6000
// 문자열 처리 UTF
// https://www.branah.com/unicode-converter
const int gComboBoxWidth = 200;
const int gTitleWidth = 50;
static const QString glevel = MKU8("\xe3\x83\xac\xe3\x83\x99\xe3\x83\xab");
WindowSettings::WindowSettings(QWidget *parent) : RMSettingsWindowBase(parent)
{
_createRowA();
}
void WindowSettings::afterSave()
{
}
#if (RM_MODEL == RM_MODEL_TYPE_AN6000)
bool WindowSettings::validateData()
{
const QList<QString> keys = validateList.keys();
for(int i=0;i<keys.size();i++){
QPair<QString,RMTextEdit*> item = validateList[keys.at(i)];
QString str = item.second->edit->text();
QString reg = item.first;
QRegExp regexp(reg);
if(!regexp.exactMatch(str)) {
QMessageBox msgBox(QMessageBox::Warning,
"AN6000",
FM_WSTR(L"「入力内容にエラーがあります。\n修正して再度ご登録をお願いいたします。」"),
QMessageBox::Ok,
this);
msgBox.exec();
QTimer::singleShot(0, item.second->edit, SLOT(setFocus()));
return false;
}
}
return true;
}
#endif // #if (RM_MODEL == RM_MODEL_TYPE_AN6000)
void WindowSettings::_createRowA()
{
#if (RM_MODEL == RM_MODEL_TYPE_AN6000)
QWidget* row = NULL;
QVBoxLayout* rowLayout = NULL;
rowLayout = _createRow(&row);
int fh = 55;
for(int i=0;i<CFG::items.size();i++) {
QJsonObject obj = CFG::items.at(i).toObject();
if(obj.contains("hidden")) {
continue;
}
if(obj.contains("control")) {
QString ctype = obj.value("control").toString();
if(ctype == "radio") {
RMGroupRadioButtons* rb = new RMGroupRadioButtons(row,i);
rowLayout->addWidget(rb);
rb->setFixedHeight(fh);
} else if(ctype == "combo") {
RMGroupComboBox* cb = new RMGroupComboBox(row,i);
rowLayout->addWidget(cb);
cb->comboBox->setFixedWidth(gComboBoxWidth);
cb->setFixedHeight(fh);
} else if(ctype == "text") {
RMGroupTextEdit* tb = new RMGroupTextEdit(row,i);
// REGEX 포함 데이터일 경우 추가
if(obj.contains("regex")) {
validateList.insert(obj.value("key").toString(),QPair<QString,RMTextEdit*>(obj.value("regex").toString(),tb->text));
}
rowLayout->addWidget(tb);
tb->text->setFixedWidth(gComboBoxWidth);
tb->setFixedHeight(fh);
}
}
}
#else // AN6000
QWidget* row = NULL;
QVBoxLayout* rowLayout = NULL;
rowLayout = _createRow(&row);
int fh = 65;
RMGroupComboBox* gs = new RMGroupComboBox(row,
MKU8("\x33\x47\xe3\x82\xbb\xe3\x83\xb3\xe3\x82\xb5\xe3\x83\xbc\xe6\x84\x9f\xe5\xba\xa6"),
QStringList() << glevel + "1" << glevel + "2" << glevel + "3" \
<< glevel + "4" << glevel + "5",
&CFG::data[SD_SENSOR]);
rowLayout->addWidget(gs);
gs->comboBox->setFixedWidth(gComboBoxWidth);
gs->setFixedHeight(fh);
// 画像 SIZE_800x600, SIZE_640x320
RMGroupRadioButtons* gr = _MGR(row,MKU8("\xe7\x94\xbb\xe5\x83\x8f"),rowLayout,QStringList() << "800x600" << "640x320",SD_SIZE);
gr->setFixedHeight(fh);
// 録画フレームレート
gr = _MGR(row,
MKU8("\xe9\x8c\xb2\xe7\x94\xbb\xe3\x83\x95\xe3\x83\xac\xe3\x83\xbc\xe3\x83\xa0\xe3\x83\xac\xe3\x83\xbc\xe3\x83\x88"),
rowLayout,
QStringList()
<< "20 fps"
<< "25 fps"
<< "30 fps",SD_FRAME);
gr->setFixedHeight(fh);
// 音量調整
gr = _MGR(row,
MKU8("\xe9\x9f\xb3\xe9\x87\x8f\xe8\xaa\xbf\xe6\x95\xb4") + " ",
rowLayout,
QStringList() << "0" << "1" << "2" << "3" << "4" << "5",
SD_VOLUME);
gr->setFixedHeight(fh);
// ボイス
gr = _MGR(row,
MKU8("\xe3\x83\x9c\xe3\x82\xa4\xe3\x82\xb9") + " ",
rowLayout,
QStringList() << MKU8("\xe3\x82\xaa\xe3\x83\x95") << MKU8("\xe3\x82\xaa\xe3\x83\xb3"),
SD_VOICE);
gr->setFixedHeight(fh);
RMSettingTime* time = new RMSettingTime(row);
rowLayout->addWidget(time);
#endif // #else // AN6000
}
QVBoxLayout* WindowSettings::_createRow(QWidget** rw)
{
*rw = new QWidget(_contentWidget);
(*rw)->setFixedWidth(SETTINGS_WINDOW_WIDTH-(8*2));
layout->addWidget(*rw);
QVBoxLayout* l = new QVBoxLayout(*rw);
l->setMargin(0);
l->setSpacing(4);
l->setAlignment(Qt::AlignTop);
return l;
}
WindowSettings::~WindowSettings()
{
}
#endif // #if (USE_DEVICE_SETTINGS)
//#endif // #if (RM_MODEL == RM_MODEL_TYPE_CS_32FH)

View File

@@ -0,0 +1,53 @@
#ifndef WINDOW_SETTINGS_H
#define WINDOW_SETTINGS_H
#if (USE_DEVICE_SETTINGS && !USE_DEVICE_SETTINGS_JSON && !RM_MODEL_EMT_KR)
#include "../rm_include.h"
#include "rm_settings_window_base.h"
//#if (RM_MODEL == RM_MODEL_TYPE_STANDARD || RM_MODEL == RM_MODEL_TYPE_XLDR_88)
#include "../ui/rm_popup.h"
#include <QGroupBox>
#include <QRadioButton>
#define SETTING_NUM_ROW 3
class RMTextEdit;
class WindowSettings : public RMSettingsWindowBase
{
Q_OBJECT
public:
explicit WindowSettings(QWidget *parent = 0);
~WindowSettings();
virtual void afterSave() override;
#if (RM_MODEL == RM_MODEL_TYPE_AN6000)
virtual bool validateData() override;
#endif // #if (RM_MODEL == RM_MODEL_TYPE_AN6000)
private:
// Utility
QVBoxLayout* _createRow(QWidget** rw);
RMRadioButtons* videoResolution; // 1
RMRadioButtons* videoQuality; // 2
RMRadioButtons* videoBrightness; // 3
RMRadioButtons* videoContrast; // 4
#if (RM_MODEL == RM_MODEL_TYPE_AN6000)
// 검증용 데이터 KEY 명칭, (REGEX,CONTROL)
QMap<QString,QPair<QString,RMTextEdit*>> validateList;
#endif // #if (RM_MODEL == RM_MODEL_TYPE_AN6000)
void _createRowA();
private slots:
// void onRecordSetting(int index);
// void onGSensorSensitivity(int index);
};
//#endif // #if (RM_MODEL == RM_MODEL_TYPE_CS_91FH)
#endif // #if (USE_DEVICE_SETTINGS)
#endif // WINDOW_SETTINGS_H

View File

@@ -0,0 +1,402 @@
#include "window_settings_xdr6688.h"
#if (USE_DEVICE_SETTINGS)
#include "../fm_dimensions.h"
#if (RM_MODEL == RM_MODEL_TYPE_XLDR_88)
#include "../ui/title_widget.h"
#include "rm_combo_box.h"
#include "rm_radio_buttons.h"
#include "rm_group_combo_box.h"
#include "rm_group_radio_buttons.h"
#include "rm_settings_cfg_xdr6688.h"
#include "rm_setting_time.h"
#include "rm_admin_passwd.h"
#include <QComboBox>
#include <QGridLayout>
#include <QDir>
#include "../ui/rm_widget_checkbox.h"
#if (DETECT_SETTING_USB_EJECT)
#include "../core/rm_usb.h"
#include "../rm_application.h"
#endif
// 문자열 처리 UTF
// https://www.branah.com/unicode-converter
const int gComboBoxWidth = 200;
const int gTitleWidth = 50;
static const QString glevel = MKU8("\xe3\x83\xac\xe3\x83\x99\xe3\x83\xab");
static const QStringList gEOffOn = QStringList() << "OFF" << "ON";
WindowSettings::WindowSettings(QWidget *parent) : RMSettingsWindowBase(parent)
{
//_createRowA();
}
WindowSettings::~WindowSettings()
{
}
//-------------------------------------------------------------
// XDR-66
WindowSettings66::WindowSettings66(QWidget *parent) : WindowSettings(parent)
{
_wwidth = SETTINGS_WINDOW_WIDTH;
_wheight = 600;
setFixedSize(_wwidth,_wheight);
_createRowA();
}
QVBoxLayout* WindowSettings66::_createRow(QWidget** rw)
{
*rw = new QWidget(_contentWidget);
(*rw)->setFixedWidth(_wwidth-(8*2));
layout->addWidget(*rw);
QVBoxLayout* l = new QVBoxLayout(*rw);
l->setMargin(0);
l->setSpacing(4);
l->setAlignment(Qt::AlignTop);
return l;
}
void WindowSettings66::_createRowA()
{
QWidget* row = NULL;
QVBoxLayout* rowLayout = NULL;
rowLayout = _createRow(&row);
int fh = 65;
RMGroupComboBox* gs = new RMGroupComboBox(row,
MKU8("\x33\x47\xe3\x82\xbb\xe3\x83\xb3\xe3\x82\xb5\xe3\x83\xbc\xe6\x84\x9f\xe5\xba\xa6"),
QStringList() << glevel + "1" << glevel + "2" << glevel + "3" \
<< glevel + "4" << glevel + "5",
&CFG::data[SD_SENSOR]);
rowLayout->addWidget(gs);
gs->comboBox->setFixedWidth(gComboBoxWidth);
gs->setFixedHeight(fh);
// 画像 SIZE_800x600, SIZE_640x320
RMGroupRadioButtons* gr = _MGR(row,MKU8("\xe7\x94\xbb\xe5\x83\x8f"),rowLayout,QStringList() << "800x600" << "640x320",SD_SIZE);
gr->setFixedHeight(fh);
// 録画フレームレート
gr = _MGR(row,
MKU8("\xe9\x8c\xb2\xe7\x94\xbb\xe3\x83\x95\xe3\x83\xac\xe3\x83\xbc\xe3\x83\xa0\xe3\x83\xac\xe3\x83\xbc\xe3\x83\x88"),
rowLayout,
QStringList()
<< "20 fps"
<< "25 fps"
<< "30 fps",SD_FRAME);
gr->setFixedHeight(fh);
// 音量調整
gr = _MGR(row,
MKU8("\xe9\x9f\xb3\xe9\x87\x8f\xe8\xaa\xbf\xe6\x95\xb4") + " ",
rowLayout,
QStringList() << "0" << "1" << "2" << "3" << "4" << "5",
SD_VOLUME);
gr->setFixedHeight(fh);
// ボイス
gr = _MGR(row,
MKU8("\xe3\x83\x9c\xe3\x82\xa4\xe3\x82\xb9") + " ",
rowLayout,
QStringList() << MKU8("\xe3\x82\xaa\xe3\x83\x95") << MKU8("\xe3\x82\xaa\xe3\x83\xb3"),
SD_VOICE);
gr->setFixedHeight(fh);
time = new RMSettingTime(row);
rowLayout->addWidget(time);
}
void WindowSettings66::afterSave()
{
if(time->check->isChecked()) {
time->onSave();
}
}
//-------------------------------------------------------------
// XDR-88
WindowSettings88::WindowSettings88(QWidget *parent) : WindowSettings(parent)
{
_wwidth = 800;
_wheight = 550;
setFixedSize(_wwidth,_wheight);
_createRowA();
_createRowB();
}
QVBoxLayout* WindowSettings88::_createRow(QWidget** rw)
{
*rw = new QWidget(_contentWidget);
(*rw)->setFixedWidth((_wwidth-(8*4))/2);
layout->addWidget(*rw);
QVBoxLayout* l = new QVBoxLayout(*rw);
l->setMargin(0);
l->setSpacing(4);
l->setAlignment(Qt::AlignTop);
return l;
}
void WindowSettings88::_createRowA()
{
QWidget* row = NULL;
QVBoxLayout* rowLayout = NULL;
QLabel* tl;
RMRadioButtons* rb;
rowLayout = _createRow(&row);
// システム
QGroupBox* g = new QGroupBox(row);
g->setObjectName("settings");
rowLayout->addWidget(g);
const int lbWidth = 130;
const int cWidth = 150;
const int rWidth = 220;
g->setTitle(MKU8("\xe3\x82\xb7\xe3\x82\xb9\xe3\x83\x86\xe3\x83\xa0"));
QGridLayout* gl = new QGridLayout(g);
gl->setMargin(8);
gl->setSpacing(4);
g->setFixedHeight(160);
// 画面表示
// 1分後画面OFF
// 常時ON
// 1分後時計画面
// RMComboBox* RMSettingsWindowBase::_MC(QWidget *parent,QString title,QLayout* layout, QStringList items,int type,QList<int> indexMap,QLabel** titleLabel)
RMComboBox* tc;
tc = _MC(g,
MKU8("\xe7\x94\xbb\xe9\x9d\xa2\xe8\xa1\xa8\xe7\xa4\xba"),
gl,
QStringList() << MKU8("\x31\xe5\x88\x86\xe5\xbe\x8c\xe7\x94\xbb\xe9\x9d\xa2\x4f\x46\x46") \
<< MKU8("\xe5\xb8\xb8\xe6\x99\x82\x4f\x4e") \
<< MKU8("\x31\xe5\x88\x86\xe5\xbe\x8c\xe6\x99\x82\xe8\xa8\x88\xe7\x94\xbb\xe9\x9d\xa2"),
SD88_SCREEN_SAVEER,
QList<int>(),&tl);
tl->setFixedWidth(lbWidth);
tc->comboBox->setFixedWidth(cWidth);
// 録画画面
// フロント
// リア
// フロント/リア
// リア/フロント
tc = _MC(g,
MKU8("\xe9\x8c\xb2\xe7\x94\xbb\xe7\x94\xbb\xe9\x9d\xa2"),
gl,
QStringList() << FM_WSTR(L"フロント") << FM_WSTR(L"サブ") << FM_WSTR(L"フロント/サブ") << FM_WSTR(L"サブ/フロント"),
SD88_SCREEN_PIP,
QList<int>(),&tl);
tl->setFixedWidth(lbWidth);
tc->comboBox->setFixedWidth(cWidth);
// スピーカー音量
tc = _MC(g,
MKU8("\xe3\x82\xb9\xe3\x83\x94\xe3\x83\xbc\xe3\x82\xab\xe3\x83\xbc\xe9\x9f\xb3\xe9\x87\x8f"),
gl,
QStringList() << "Off" << "1" << "2" << "3" << "4" << "5",
SD88_SPEAKER,
QList<int>(),&tl);
tl->setFixedWidth(lbWidth);
tc->comboBox->setFixedWidth(cWidth);
// MIC
rb = _MR(g,FM_WSTR(L"録音設定"),gl,gEOffOn,SD88_MIC,QList<int>(),&tl);;
tl->setFixedWidth(lbWidth);
rb->setFixedWidth(rWidth);
// GROUP 2 STORAGE, メモリ割当
g = new QGroupBox(row);
g->setObjectName("settings");
rowLayout->addWidget(g);
g->setTitle(MKU8("\xe3\x83\xa1\xe3\x83\xa2\xe3\x83\xaa\xe5\x89\xb2\xe5\xbd\x93"));
g->setFixedHeight(89);
gl = new QGridLayout(g);
gl->setMargin(8);
gl->setSpacing(4);
// メモリ割当情報
// 常時録画重視
// 駐車録画重視
// イベント録画重視
tc = _MC(g,
MKU8("\xe3\x83\xa1\xe3\x83\xa2\xe3\x83\xaa\xe5\x89\xb2\xe5\xbd\x93\xe6\x83\x85\xe5\xa0\xb1"),
gl,
QStringList() << MKU8("\xe5\xb8\xb8\xe6\x99\x82\xe9\x8c\xb2\xe7\x94\xbb\xe9\x87\x8d\xe8\xa6\x96") \
<< MKU8("\xe9\xa7\x90\xe8\xbb\x8a\xe9\x8c\xb2\xe7\x94\xbb\xe9\x87\x8d\xe8\xa6\x96") \
<< MKU8("\xe3\x82\xa4\xe3\x83\x99\xe3\x83\xb3\xe3\x83\x88\xe9\x8c\xb2\xe7\x94\xbb\xe9\x87\x8d\xe8\xa6\x96"),
SD88_VOLUME,
QList<int>(),&tl);
tl->setFixedWidth(lbWidth);
tc->comboBox->setFixedWidth(cWidth);
// GROUP 3 G-SENSOR, センサー感度
g = new QGroupBox(row);
g->setObjectName("settings");
rowLayout->addWidget(g);
g->setTitle(MKU8("\xe3\x82\xbb\xe3\x83\xb3\xe3\x82\xb5\xe3\x83\xbc\xe6\x84\x9f\xe5\xba\xa6"));
g->setFixedHeight(120);
gl = new QGridLayout(g);
gl->setMargin(8);
gl->setSpacing(4);
// 常時センサー感度
//QStringList sensors = QStringList() << "Low" << "Middle-Low" << "Middle" << "Middle-High" << "High";
QStringList sensors = QStringList() << "1" << "2" << "3" << "4" << "5";
tc = _MC(g,
MKU8("\xe5\xb8\xb8\xe6\x99\x82\xe3\x82\xbb\xe3\x83\xb3\xe3\x82\xb5\xe3\x83\xbc\xe6\x84\x9f\xe5\xba\xa6"),
gl,
sensors,
SD88_GSENSOR_NORMAL,
QList<int>(),&tl);
tl->setFixedWidth(lbWidth);
tc->comboBox->setFixedWidth(cWidth);
// 駐車センサー感度
tc = _MC(g,
MKU8("\xe9\xa7\x90\xe8\xbb\x8a\xe3\x82\xbb\xe3\x83\xb3\xe3\x82\xb5\xe3\x83\xbc\xe6\x84\x9f\xe5\xba\xa6"),
gl,
sensors,
SD88_GSENSOR_PARKING,
QList<int>(),&tl);
tl->setFixedWidth(lbWidth);
tc->comboBox->setFixedWidth(cWidth);
// GROUP 4 PARKING MODE, 駐車録画
g = new QGroupBox(row);
g->setObjectName("settings");
rowLayout->addWidget(g);
g->setTitle(MKU8("\xe9\xa7\x90\xe8\xbb\x8a\xe9\x8c\xb2\xe7\x94\xbb"));
gl = new QGridLayout(g);
gl->setMargin(8);
gl->setSpacing(4);
//駐車録画機能
rb = _MR(g,MKU8("\xe9\xa7\x90\xe8\xbb\x8a\xe9\x8c\xb2\xe7\x94\xbb\xe6\xa9\x9f\xe8\x83\xbd"),gl,gEOffOn,SD88_PARKING_ON,QList<int>(),&tl);;
tl->setFixedWidth(lbWidth);
rb->setFixedWidth(rWidth);
rb->setFixedHeight(45);
// 放電遮断電圧
/*
tl = new QLabel(this);
tl->setObjectName("text_normal_label");
tl->setText(MKU8("\xe6\x94\xbe\xe9\x9b\xbb\xe9\x81\xae\xe6\x96\xad\xe9\x9b\xbb\xe5\x9c\xa7"));
gl->addWidget(tl,1,0,Qt::AlignLeft);
*/
}
void WindowSettings88::_createRowB()
{
QWidget* row = NULL;
QVBoxLayout* rowLayout = NULL;
QLabel* tl;
RMRadioButtons* rb;
const int lbWidth = 130;
//const int cWidth = 150;
const int rWidth = 220;
rowLayout = _createRow(&row);
//int fh = 55;
QGroupBox* g;
QGridLayout* gl;
//暗証番号暗証番号設定OFF 0 ON暗証番号変更暗証番号設定をONにしているとき暗証番号変更4桁の入力が可能。
// GROUP 5 VIDEO, 録画設定
g = new QGroupBox(row);
g->setObjectName("settings");
rowLayout->addWidget(g);
g->setTitle(MKU8("\xe9\x8c\xb2\xe7\x94\xbb\xe8\xa8\xad\xe5\xae\x9a"));
g->setFixedHeight(220);
gl = new QGridLayout(g);
gl->setMargin(8);
gl->setSpacing(4);
// 解像度
rb = _MR(g,MKU8("\xe8\xa7\xa3\xe5\x83\x8f\xe5\xba\xa6"),gl,QStringList() << "FHD" << "HD",SD88_VIDEO_RESOLUTION,QList<int>(),&tl);;
tl->setFixedWidth(lbWidth);
rb->setFixedWidth(rWidth);
// 画質
// 低
// 中
// 高
/* 2021/11/22 수정제거
rb = _MR(g,MKU8("\xe7\x94\xbb\xe8\xb3\xaa"),gl,QStringList() << MKU8("\xe4\xbd\x8e") << MKU8("\xe4\xb8\xad") << MKU8("\xe9\xab\x98"),SD88_VIDEO_QUALITY,QList<int>(),&tl);;
tl->setFixedWidth(lbWidth);
rb->setFixedWidth(rWidth);
*/
// 録画フレーム数
rb = _MR(g,MKU8("\xe9\x8c\xb2\xe7\x94\xbb\xe3\x83\x95\xe3\x83\xac\xe3\x83\xbc\xe3\x83\xa0\xe6\x95\xb0"),gl,QStringList() << "4.9 fps" << "19.1 fps" << "29.1 fps",SD88_VIDEO_FRAME,QList<int>(),&tl);;
tl->setFixedWidth(lbWidth);
rb->setFixedWidth(rWidth);
// サブカメラ録画
rb = _MR(g,MKU8("\xe3\x82\xb5\xe3\x83\x96\xe3\x82\xab\xe3\x83\xa1\xe3\x83\xa9\xe9\x8c\xb2\xe7\x94\xbb"),gl,gEOffOn,SD88_VIDEO_SUB_CAMERA,QList<int>(),&tl);;
tl->setFixedWidth(lbWidth);
rb->setFixedWidth(rWidth);
// HDR
rb = _MR(g,"HDR",gl,gEOffOn,SD88_VIDEO_HDR,QList<int>(),&tl);;
tl->setFixedWidth(lbWidth);
rb->setFixedWidth(rWidth);
// ナイトビジョン
rb = _MR(g,MKU8("\xe3\x83\x8a\xe3\x82\xa4\xe3\x83\x88\xe3\x83\x93\xe3\x82\xb8\xe3\x83\xa7\xe3\x83\xb3"),gl,gEOffOn,SD88_VIDEO_NIGHT_VISION,QList<int>(),&tl);;
tl->setFixedWidth(lbWidth);
rb->setFixedWidth(rWidth);
// 관리자 암호 설정도 저장
pw = new RMSettingAdminPW(&CFG::data[SD88_ADMIN_PW],(QList<int>() << 1 << 0), row);
rowLayout->addWidget(pw);
//time = new RMSettingTime(row);
//time->setFixedHeight(100);
//rowLayout->addWidget(time);
}
void WindowSettings88::afterSave()
{
// if(time->check->isChecked()) {
// time->onSave();
// }
#if (REMOVE_ADMIN_PW_CHECKBOX)
// 저장
if(CFG::data[SD88_ADMIN_PW] == 0) { // pw->buttons.at(1)->isChecked()) {
#else
if(pw->saveCheckbox->isChecked()) {
#endif
pw->onSave();
}
//qInfo() << __FUNCTION__;
}
#endif // #if (USE_DEVICE_SETTINGS)
#endif // #if (RM_MODEL)

View File

@@ -0,0 +1,71 @@
#ifndef WINDOW_SETTINGS_H
#define WINDOW_SETTINGS_H
#if (USE_DEVICE_SETTINGS)
#include "../rm_include.h"
#include "rm_settings_window_base.h"
#if (RM_MODEL == RM_MODEL_TYPE_XLDR_88)
#include "../ui/rm_popup.h"
#include <QGroupBox>
#include <QRadioButton>
#define SETTING_NUM_ROW 3
class RMSettingTime;
class WindowSettings : public RMSettingsWindowBase
{
Q_OBJECT
public:
int _wwidth;
int _wheight;
explicit WindowSettings(QWidget *parent = 0);
~WindowSettings();
protected:
private slots:
};
// XDR-66
class WindowSettings66 : public WindowSettings
{
Q_OBJECT
public:
explicit WindowSettings66(QWidget *parent = 0);
QVBoxLayout* _createRow(QWidget** rw);
void afterSave() override;
bool validateData(){return true;}
private:
RMSettingTime* time;
void _createRowA();
private slots:
};
// XDR-88
class RMSettingAdminPW;
class RMSettingTime;
class WindowSettings88 : public WindowSettings
{
Q_OBJECT
public:
explicit WindowSettings88(QWidget *parent = 0);
QVBoxLayout* _createRow(QWidget** rw);
void afterSave() override;
bool validateData(){return true;}
private:
RMSettingAdminPW* pw;
//RMSettingTime* time;
void _createRowA();
void _createRowB();
private slots:
};
#endif // #if (RM_MODEL)
#endif // #if (USE_DEVICE_SETTINGS)
#endif // WINDOW_SETTINGS_H

View File

@@ -0,0 +1,61 @@
#ifndef WINDOW_SETTINGS_H
#define WINDOW_SETTINGS_H
#include "../rm_include.h"
#include "rm_settings_window_base.h"
#if (RM_MODEL == RM_MODEL_TYPE_XLDR_F_ADAS)
#include "../ui/rm_popup.h"
#include <QGroupBox>
#include <QRadioButton>
#define SETTING_NUM_ROW 3
class WindowSettings : public RMSettingsWindowBase
{
Q_OBJECT
public:
explicit WindowSettings(QWidget *parent = 0);
~WindowSettings();
private:
// custom value save
//int _lastRecordingSetting;
//void _saveCustomRecordingSetting();
//void _loadCustomRecordingSetting();
// Utility
QVBoxLayout* _createRow(QWidget** rw);
//void _createRecordSetting(QWidget* row,QVBoxLayout* rowLayout);
// RMRadioButtons* videoResolution; // 1
// RMRadioButtons* videoQuality; // 2
// RMRadioButtons* videoBrightness; // 3
// RMRadioButtons* videoContrast; // 4
// void _createGSensorSensitivity(QWidget* row,QVBoxLayout* rowLayout);
// RMGroupComboBox* gSensorSensitivity;
// RMComboBox* gSensorSensitivityFB;
// RMComboBox* gSensorSensitivityLR;
// RMComboBox* gSensorSensitivityUD;
//void _createOrbis(QWidget* row,QVBoxLayout* rowLayout);
//void _createDisasterAlarm(QWidget* row,QVBoxLayout* rowLayout);
void _createFCWS(QWidget* row,QVBoxLayout* rowLayout);
//void _createBCWS(QWidget* row,QVBoxLayout* rowLayout);
//void _createFLIP(QWidget* row,QVBoxLayout* rowLayout);
void _createRowA();
void _createRowB();
//void _createRowC();
private slots:
//void onRecordSetting(int index);
//void onGSensorSensitivity(int index);
};
#endif // #if (RM_MODEL == RM_MODEL_TYPE_CS_91FH)
#endif // WINDOW_SETTINGS_H

View File

@@ -0,0 +1,805 @@
#include "fm_strings.h"
#define DEBUG_FM_STRINGS 0
#include <QFile>
#if (FM_STR_TYPE2)
QMap<QString,QStringList> FMS::strings = QMap<QString,QStringList>();
#else // FM_STR_TYPE2
#if !(MODEL_KOREAN_ONLY)
QMap<QString,QString> FMS::jps = QMap<QString,QString>();
#endif // MODEL_KOREAN_ONLY
#if(RM_MODEL == RM_MODEL_TYPE_ADT_CAPS || RM_MODEL == RM_MODEL_TYPE_TB4000 || RM_MODEL == RM_MODEL_TYPE_BV2000 || SUB_MODEL_KEIYO_KR || RM_MODEL == RM_MODEL_TYPE_MH9000 || RM_MODEL_TYPE_EMT_KR)
QMap<QString,QString> FMS::kos = QMap<QString,QString>();
#endif
#if(SUB_MODEL_BV5000 || RM_MODEL == RM_MODEL_TYPE_MH9000 || RM_MODEL_TYPE_EMT_KR)
QMap<QString,QString> FMS::ens = QMap<QString,QString>();
#endif
#endif // FM_STR_TYPE2
bool FMS::_loaded = false;
#if (FM_STR_TYPE2)
void FMS::load()
{
if(QFile::exists(":/raw/strings.txt"))
{
QFile f(":/raw/strings.txt");
if (f.open(QIODevice::ReadOnly))
{
QTextStream in(&f);
in.setCodec("UTF-8");
while (!in.atEnd())
{
QString line = in.readLine();
QStringList lst = line.split("|");
QString key = lst.first();
lst.removeFirst();
FMS::strings.insert(key,lst);
}
f.close();
}
}
}
QString FMS::txtLine(const char* key)
{
return txt(key).replace("\\n","\n");
}
QString FMS::txt(const char* key)
{
if(!_loaded) {
load();
_loaded = true;
}
return txt(key,RMLanguage::instance()->language());
}
QString FMS::txt(const char* key,RMLanguage::LANGUAGE_TYPE type)
{
if(!strings.contains(key)) {
qInfo() << "KEY NOT FOUND:" << key << __FUNCTION__;
return "";
}
int idx = 0;
switch (type) {
case RMLanguage::LANGUAGE_JP:
idx = 1;
break;
case RMLanguage::LANGUAGE_KR:
idx = 0;
break;
case RMLanguage::LANGUAGE_EN:
idx = 2;
break;
}
QStringList l = strings[key];
if(l.size() > idx) {
return l.at(idx);
}
qInfo() << "LANGUAGE NOT FOUND:" << key << "type:" << type << __FUNCTION__;
return "";
}
#else // FM_STR_TYPE2
void FMS::load()
{
#if (RM_MODEL == RM_MODEL_TYPE_TBD360) // ENG
jps.insert("report","Report");
jps.insert("map","Map");
jps.insert("language","Language");
jps.insert("file_type","Type");
jps.insert("file_name","File Name");
jps.insert("file_duration","Duration");
jps.insert("restart_msg","The application must restart in order to enable/disable H/W acceleration");
// ビューア情報
jps.insert("viewer_info","Viewer Info.");
jps.insert("capture","Capture");
jps.insert("close","Close");
jps.insert("minimize","Minimize");
jps.insert("low_speed","Low Speed");
jps.insert("high_speed","High Speed");
jps.insert("flip_h","Flip Horizontal");
jps.insert("flip_v","Flip Vertical");
// フロント・リア切替え
jps.insert("swap","Swap Screen");
jps.insert("mode_360","Camera Mode");
jps.insert("zoom_360","Zoom Mode");
jps.insert("reset_360","Reset Camera");
jps.insert("fullscreen","Full Screen");
jps.insert("graph_zoom_in","Zoom In");
jps.insert("graph_zoom_out","Zoom Out");
jps.insert("brightness","Brightness");
jps.insert("contrast","Contrast");
jps.insert("play_speed","Speed");
jps.insert("volume_mute","Mute");
jps.insert("volume_un_mute","Unmute");
jps.insert("play_backward","Seek backward");
jps.insert("play_forward","Seek forward");
jps.insert("play_play","Play");
jps.insert("play_stop","Stop");
jps.insert("play_file_previous","Previous File");
jps.insert("play_file_next","Next File");
jps.insert("open","Open");
jps.insert("play_pause","Pause");
jps.insert("backup","Backup");
jps.insert("viewer_sw_version","S/W Version");
jps.insert("hw_accel","Graphic Accelation");
jps.insert("ok","OK");
jps.insert("cancel","Cancel");
jps.insert("change","Change");
// 入力,確認
jps.insert("input","Input");
jps.insert("confirm","Confirm");
jps.insert("old","Previous");
// 間違ったパスワード
jps.insert("password_wrong","Wrong Password");
// 注意
jps.insert("warning","Warning");
jps.insert("save","Save");
// 初期値
jps.insert("default","Default");
// 緯度
jps.insert("lat","LAT:");
//経度
jps.insert("lon","LON:");
// 録画ファイルがあるフォルダを選択
jps.insert("open_message","Select the folder");
#else
#if (SUB_MODEL_BV5000 || RM_MODEL == RM_MODEL_TYPE_MH9000 || RM_MODEL_TYPE_EMT_KR)
ens.insert("report","Report");
ens.insert("map","Map");
ens.insert("language","Language");
ens.insert("auto_select","Auto");
ens.insert("file_type","Type");
ens.insert("file_name","File Name");
ens.insert("file_duration","Duration");
ens.insert("file_date","Date");
ens.insert("file_size","Size");
ens.insert("restart_msg","The application must restart in order to enable/disable H/W acceleration");
// ビューア情報
ens.insert("viewer_info","Viewer Info.");
ens.insert("capture","Capture");
ens.insert("close","Close");
ens.insert("minimize","Minimize");
ens.insert("maximize","Maximize");
ens.insert("restore_window","Restore Window");
ens.insert("low_speed","Low Speed");
ens.insert("high_speed","High Speed");
ens.insert("flip_h","Flip Horizontal");
ens.insert("flip_v","Flip Vertical");
// フロント・リア切替え
ens.insert("swap","Swap Screen");
ens.insert("mode_360","Camera Mode");
ens.insert("zoom_360","Zoom Mode");
ens.insert("reset_360","Reset Camera");
ens.insert("fullscreen","Full Screen");
ens.insert("graph_zoom_in","Zoom In");
ens.insert("graph_zoom_out","Zoom Out");
ens.insert("brightness","Brightness");
ens.insert("contrast","Contrast");
ens.insert("play_speed","Speed");
ens.insert("volume_mute","Mute");
ens.insert("volume_un_mute","Unmute");
ens.insert("play_backward","Seek backward");
ens.insert("play_forward","Seek forward");
ens.insert("play_play","Play");
ens.insert("play_stop","Stop");
ens.insert("play_file_previous","Previous File");
ens.insert("play_file_next","Next File");
ens.insert("open","Open");
ens.insert("play_pause","Pause");
ens.insert("backup","Backup");
ens.insert("viewer_sw_version","S/W Version");
ens.insert("hw_accel","Graphic Accelation");
ens.insert("ok","OK");
ens.insert("cancel","Cancel");
ens.insert("change","Change");
// 入力,確認
ens.insert("input","Input");
ens.insert("confirm","Confirm");
ens.insert("old","Previous");
// 間違ったパスワード
ens.insert("password_wrong","Wrong Password");
// 注意
ens.insert("warning","Warning");
ens.insert("save","Save");
// 初期値
ens.insert("default","Default");
// 緯度
ens.insert("lat","LAT:");
//経度
ens.insert("lon","LON:");
// 録画ファイルがあるフォルダを選択
ens.insert("open_message","Select the folder");
#endif // BV5000 ENG
#if !(MODEL_KOREAN_ONLY)
// 파일 저장
// ファイルの保存
jps.insert("backup_title",FM_WSTR(L"バックアップ中"));
// 파일 종류
jps.insert("type_event",FM_WSTR(L"衝撃検知"));
jps.insert("type_manual",FM_WSTR(L"手動"));
jps.insert("type_parking_normal",FM_WSTR(L"駐車監視"));
jps.insert("type_parking",FM_WSTR(L"パーキング"));
jps.insert("type_parking_event",FM_WSTR(L"駐車衝撃"));
jps.insert("type_normal",FM_WSTR(L"常時"));
#endif // #if !(MODEL_KOREAN_ONLY)
#if(SUB_MODEL_BV5000 || RM_MODEL == RM_MODEL_TYPE_MH9000 || RM_MODEL_TYPE_EMT_KR)
ens.insert("type_event","EVENT");
ens.insert("type_manual","MANUAL");
ens.insert("type_parking_normal","PARKING");
ens.insert("type_parking","PARKING");
ens.insert("type_parking_event","PAKING.E");
ens.insert("type_normal","NORMAL");
#endif // #if(SUPPORT_LANGUAGE_INSERT)
#if (RC_LANGUAGE==0x0412 || MODEL_KOREAN_ONLY)
kos.insert("type_event",FM_WSTR(L"이벤트"));
kos.insert("type_manual",FM_WSTR(L"수동"));
kos.insert("type_parking_normal",FM_WSTR(L"주차감시"));
kos.insert("type_parking",FM_WSTR(L"주차"));
kos.insert("type_parking_event",FM_WSTR(L"주차E"));
kos.insert("type_normal",FM_WSTR(L"상시"));
kos.insert("restart_msg",FM_WSTR(L"설정 적용을 위해 뷰어를 다시 시작 합니다."));
kos.insert("report",FM_WSTR(L"보고서"));
kos.insert("map",FM_WSTR(L"지도"));
kos.insert("language",FM_WSTR(L"언어"));
kos.insert("file_type",FM_WSTR(L"분류"));
kos.insert("file_name",FM_WSTR(L"파일명"));
kos.insert("file_date",FM_WSTR(L"날짜"));
kos.insert("file_duration",FM_WSTR(L"재생시간"));
kos.insert("file_size",FM_WSTR(L"크기")); // タイプ
kos.insert("viewer_info",FM_WSTR(L"S/W 정보"));
kos.insert("speed_warning",FM_WSTR(L"GPS 속도는 실제 속도와 다를 수 있습니다."));
kos.insert("capture",FM_WSTR(L"JPG 저장"));
kos.insert("capture_desc",FM_WSTR(L"현재 화면을 JPEG 파일로 저장 합니다"));
kos.insert("close",FM_WSTR(L"닫기"));
kos.insert("minimize",FM_WSTR(L"최소화"));
kos.insert("maximize",FM_WSTR(L"최대화"));
kos.insert("restore_window",FM_WSTR(L"이전 크기로 복원"));
kos.insert("low_speed",FM_WSTR(L"저속"));
kos.insert("high_speed",FM_WSTR(L"고속"));
kos.insert("flip_h",FM_WSTR(L"좌우반전"));
kos.insert("flip_v",FM_WSTR(L"상하반전"));
kos.insert("swap",FM_WSTR(L"전후방 화면 전환"));
kos.insert("indoor",FM_WSTR(L"실내외 화면 전환"));
kos.insert("fullscreen",FM_WSTR(L"전체화면"));
kos.insert("graph_zoom_in",FM_WSTR(L"확대"));
kos.insert("graph_zoom_out",FM_WSTR(L"축소"));
kos.insert("brightness",FM_WSTR(L"밝기조정"));
kos.insert("contrast",FM_WSTR(L"대비조정"));
kos.insert("play_speed",FM_WSTR(L"재생속도"));
kos.insert("volume_mute",FM_WSTR(L"음량"));
kos.insert("volume_un_mute",FM_WSTR(L"음량"));
kos.insert("play_backward",FM_WSTR(L"1초 이전으로 이동"));
kos.insert("play_forward",FM_WSTR(L"1초 이후로 이동"));
kos.insert("play_play",FM_WSTR(L"재생"));
kos.insert("play_stop",FM_WSTR(L"정지"));
kos.insert("play_file_previous",FM_WSTR(L"이전 파일"));
kos.insert("play_file_next",FM_WSTR(L"다음 파일"));
kos.insert("open",FM_WSTR(L"파일 열기"));
kos.insert("open_title",FM_WSTR(L"파일 열기"));
kos.insert("play_pause",FM_WSTR(L"일시 정지"));
kos.insert("backup_title",FM_WSTR(L"파일 저장"));
kos.insert("file_exist",FM_WSTR(L"이 위치에 같은 이름의 파일이 있습니다."));
kos.insert("apply_rule",FM_WSTR(L"동일한 작업을 다음의"));
kos.insert("after_files",FM_WSTR(L"파일에도 적용"));
kos.insert("skip",FM_WSTR(L"건너뛰기"));
kos.insert("overwrite",FM_WSTR(L"덮어쓰기"));
kos.insert("auto_select",FM_WSTR(L"자동선택"));
kos.insert("select_language",FM_WSTR(L"언어 선택"));
kos.insert("enter_file_name",FM_WSTR(L"파일명을 입력해 주십시오"));
kos.insert("front_suffix",FM_WSTR(L"전방"));
kos.insert("rear_suffix",FM_WSTR(L"후방"));
kos.insert("save_jpg_fail",FM_WSTR(L"JPEG 파일 생성 실패"));
kos.insert("thumbnail",FM_WSTR(L"썸네일보기"));
kos.insert("register",FM_WSTR(L"적발원부"));
kos.insert("save_video",FM_WSTR(L"영상저장"));
kos.insert("reset_360",FM_WSTR(L"초기화"));
kos.insert("save_360",FM_WSTR(L"파일로 저장"));
//
kos.insert("backup_title",FM_WSTR(L"파일 저장중..."));
kos.insert("change_folder",FM_WSTR(L"폴더 선택"));
kos.insert("backup",FM_WSTR(L"파일 저장"));
kos.insert("viewer_sw_version",FM_WSTR(L"Viewer S/W 버전 정보"));
kos.insert("hw_accel",FM_WSTR(L"H/W 가속 설정"));
kos.insert("pw_message",FM_WSTR(L"암호 (4 자 이상 20 자 이하) 생성."));
kos.insert("password",FM_WSTR(L"암호"));
kos.insert("password_new",FM_WSTR(L"신규 암호"));
kos.insert("password_input",FM_WSTR(L"암호 입력"));
kos.insert("password_change",FM_WSTR(L"암호 변경"));
kos.insert("ok",FM_WSTR(L"확인"));
kos.insert("cancel",FM_WSTR(L"취소"));
kos.insert("change",FM_WSTR(L"변경"));
kos.insert("input",FM_WSTR(L"입력"));
kos.insert("confirm",FM_WSTR(L"확인"));
kos.insert("old",FM_WSTR(L"이전"));
kos.insert("password_wrong",FM_WSTR(L"잘못된 암호"));
kos.insert("invalid_media",FM_WSTR(L"재생할 수 없는 파일입니다"));
kos.insert("warning",FM_WSTR(L"주의"));
kos.insert("file_not_exist",FM_WSTR(L"파일이 존재하지 않습니다"));
kos.insert("save",FM_WSTR(L"저장"));
kos.insert("default",FM_WSTR(L"초기화"));
kos.insert("lat",FM_WSTR(L"위도"));
kos.insert("lon",FM_WSTR(L"경도"));
kos.insert("open_message",FM_WSTR(L"녹화 파일이 있는 폴더를 선택"));
kos.insert("password",FM_WSTR(L"비밀번호설정"));
#endif // SUPPORT_LANGUAGE_INSERT || MODEL_KOREAN_ONLY
#if !(MODEL_KOREAN_ONLY)
jps.insert("settings",FM_WSTR(L"設定"));
#endif // MODEL_KOREAN_ONLY
kos.insert("settings",FM_WSTR(L"설정"));
ens.insert("settings",FM_WSTR(L"Settings"));
#if(SUPPORT_LANGUAGE_INSERT || RM_MODEL == RM_MODEL_TYPE_MH9000)
ens.insert("open_title","Open");
ens.insert("select_language","Select language");
#endif // #if(SUPPORT_LANGUAGE_INSERT)
#if !(MODEL_KOREAN_ONLY)
jps.insert("info",FM_WSTR(L"ビューアー情報"));
#endif // #if !(MODEL_KOREAN_ONLY)
#if(SUPPORT_LANGUAGE_INSERT)
kos.insert("info",FM_WSTR(L"뷰어정보"));
ens.insert("info",FM_WSTR(L"Viewer Info."));
#endif // #if(SUPPORT_LANGUAGE_INSERT)
#if !(MODEL_KOREAN_ONLY)
jps.insert("report","\xe3\x83\xac\xe3\x83\x9d\xe3\x83\xbc\xe3\x83\x88");
jps.insert("map","\xe5\x9c\xb0\xe5\x9b\xb3");
jps.insert("language","\xe8\xa8\x80\xe8\xaa\x9e\xe9\x81\xb8\xe6\x8a\x9e");
jps.insert("file_type","\xe3\x82\xbf\xe3\x82\xa4\xe3\x83\x97"); // タイプ
jps.insert("file_name","\xe3\x83\x95\xe3\x82\xa1\xe3\x82\xa4\xe3\x83\xab\xe5\x90\x8d"); // ファイル名
jps.insert("file_date",FM_WSTR(L"ファイル日付")); // ファイル名
jps.insert("file_duration","\xe9\x8c\xb2\xe7\x94\xbb\xe6\x99\x82\xe9\x96\x93"); // 録画時間
jps.insert("file_size",FM_WSTR(L"サイズ")); // タイプ
// 재기동 경고 再起動します
jps.insert("restart_msg","\xe5\x86\x8d\xe8\xb5\xb7\xe5\x8b\x95\xe3\x81\x97\xe3\x81\xbe\xe3\x81\x99");
// ビューア情報
jps.insert("viewer_info","\xe3\x83\x93\xe3\x83\xa5\xe3\x83\xbc\xe3\x82\xa2\xe6\x83\x85\xe5\xa0\xb1");
//速度はGPS算出値となります。実際の速度とは異なります。
jps.insert("speed_warning","\xe9\x80\x9f\xe5\xba\xa6\xe3\x81\xaf\x47\x50\x53\xe7\xae\x97\xe5\x87\xba\xe5\x80\xa4\xe3\x81\xa8\xe3\x81\xaa\xe3\x82\x8a\xe3\x81\xbe\xe3\x81\x99\xe3\x80\x82\xe5\xae\x9f\xe9\x9a\x9b\xe3\x81\xae\xe9\x80\x9f\xe5\xba\xa6\xe3\x81\xa8\xe3\x81\xaf\xe7\x95\xb0\xe3\x81\xaa\xe3\x82\x8a\xe3\x81\xbe\xe3\x81\x99\xe3\x80\x82");
jps.insert("capture","\x4a\x50\x47 \xe4\xbf\x9d\xe5\xad\x98");
jps.insert("capture_desc",FM_WSTR(L"キャプチャーをJPEGで保存します"));
//
jps.insert("close","\xe9\x96\x89\xe3\x81\x98\xe3\x82\x8b"); // 閉じる
jps.insert("minimize","\xe6\x9c\x80\xe5\xb0\x8f\xe5\x8c\x96"); // 最小化
jps.insert("maximize","\xe6\x9c\x80\xe5\xa4\xa7\xe5\x8c\x96"); // 最大化
jps.insert("restore_window","\xe5\x85\x83\xe3\x81\xab\xe6\x88\xbb\xe3\x81\x99\xef\xbc\x88\xe7\xb8\xae\xe5\xb0\x8f\xef\xbc\x89"); // 元に戻す(縮小)
jps.insert("low_speed","\xe4\xbd\x8e\xe9\x80\x9f");
jps.insert("high_speed","\xe9\xab\x98\xe9\x80\x9f \x28 \x31\x33\x30 \x4b\x6d\x2f\x68 \xe4\xbb\xa5\xe4\xb8\x8a \x29");
jps.insert("flip_h","\xe5\xb7\xa6\xe5\x8f\xb3\xe5\x8f\x8d\xe8\xbb\xa2"); // 左右反転
jps.insert("flip_v","\xe4\xb8\x8a\xe4\xb8\x8b\xe5\x8f\x8d\xe8\xbb\xa2"); // 上下反転
// フロント・リア切替え
// フロント・サブ切替え
#if (RM_MODEL == RM_MODEL_TYPE_XLDR_88)
jps.insert("swap",FM_WSTR(L"フロント・サブ切替え"));
#else
// フロント・リア切替え
#if (SUB_MODEL_KEIYO_360)
jps.insert("swap",FM_WSTR(L"360度・ワイド切り替え"));
#else // SUB_MODEL_KEIYO_360
jps.insert("swap",FM_WSTR(L"フロント・リア切替え"));
#endif // SUB_MODEL_KEIYO_360
#endif
jps.insert("indoor",FM_WSTR(L"室内・外切替え"));
#if (RM_MODEL == RM_MODEL_TYPE_KEIYO1 || \
RM_MODEL == RM_MODEL_TYPE_MBJ5010 || \
RM_MODEL == RM_MODEL_TYPE_BV2000 || \
RM_MODEL == RM_MODEL_TYPE_FC_DR232W) // 最大化,フルスクリーン
// フルスクリーン
jps.insert("fullscreen","\xe3\x83\x95\xe3\x83\xab\xe3\x82\xb9\xe3\x82\xaf\xe3\x83\xaa\xe3\x83\xbc\xe3\x83\xb3");
#else
jps.insert("fullscreen","\xe6\x9c\x80\xe5\xa4\xa7\xe5\x8c\x96");
#endif
jps.insert("graph_zoom_in","\xe6\x8b\xa1\xe5\xa4\xa7"); // 拡大
jps.insert("graph_zoom_out","\xe7\xb8\xae\xe5\xb0\x8f"); // 縮小
jps.insert("brightness","\xe6\x98\x8e\xe3\x82\x8b\xe3\x81\x95"); // 明るさ
jps.insert("contrast","\xe3\x82\xb3\xe3\x83\xb3\xe3\x83\x88\xe3\x83\xa9\xe3\x82\xb9\xe3\x83\x88"); // コントラスト
jps.insert("play_speed","\xe5\x86\x8d\xe7\x94\x9f\xe9\x80\x9f\xe5\xba\xa6"); // 再生速度
#if (RM_MODEL == RM_MODEL_TYPE_KEIYO1 || \
RM_MODEL == RM_MODEL_TYPE_BV2000 || \
RM_MODEL == RM_MODEL_TYPE_MBJ5010 || \
RM_MODEL == RM_MODEL_TYPE_FC_DR232W) // 音量
jps.insert("volume_mute","\xe9\x9f\xb3\xe9\x87\x8f"); // 音量
jps.insert("volume_un_mute","\xe9\x9f\xb3\xe9\x87\x8f");
#else
jps.insert("volume_mute","\xe6\xb6\x88\xe9\x9f\xb3\xe3\x83\xa2\xe3\x83\xbc\xe3\x83\x89");
jps.insert("volume_un_mute","\xe3\x83\x9f\xe3\x83\xa5\xe3\x83\xbc\xe3\x83\x88\xe3\x81\xae\xe8\xa7\xa3\xe9\x99\xa4");
#endif
#if (SEEK_STEP_SIZE == 10)
jps.insert("play_backward","\x31\x30\xe7\xa7\x92\xe5\x89\x8d\xe3\x81\xb8"); // 10秒前へ
jps.insert("play_forward","\x31\x30\xe7\xa7\x92\xe9\x80\xb2\xe3\x82\x80"); // 10秒進む
#else
jps.insert("play_backward","\x31\xe7\xa7\x92\xe5\x89\x8d\xe3\x81\xb8");
jps.insert("play_forward","\x31\xe7\xa7\x92\xe9\x80\xb2\xe3\x82\x80");
#endif
jps.insert("play_play","\xe5\x86\x8d\xe7\x94\x9f"); // 再生
jps.insert("play_stop","\xe5\x81\x9c\xe6\xad\xa2"); // 停止
jps.insert("play_file_previous","\xe5\x89\x8d\xe3\x81\xae\xe3\x83\x95\xe3\x82\xa1\xe3\x82\xa4\xe3\x83\xab"); // 前のファイル <- \xe4\xbb\xa5\xe5\x89\x8d\xe3\x83\x95\xe3\x82\xa1\xe3\x82\xa4\xe3\x83\xab
jps.insert("play_file_next","\xe6\xac\xa1\xe3\x81\xae\xe3\x83\x95\xe3\x82\xa1\xe3\x82\xa4\xe3\x83\xab");
jps.insert("open","\xe3\x83\x95\xe3\x82\xa1\xe3\x82\xa4\xe3\x83\xab\xe3\x82\x92\xe9\x96\x8b\xe3\x81\x8f");
jps.insert("play_pause","\xe4\xb8\x80\xe6\x99\x82\xe5\x81\x9c\xe6\xad\xa2");
jps.insert("open_title",FM_WSTR(L"ファイルOPEN"));
//
jps.insert("backup_title",FM_WSTR(L"ファイルの保存"));
jps.insert("file_exist",FM_WSTR(L"この場所に同じ名前のファイルがあります。"));
jps.insert("apply_rule",FM_WSTR(L"同じ処理を次の"));
jps.insert("after_files",FM_WSTR(L"個の競合に適用"));
jps.insert("skip",FM_WSTR(L"スキップ"));
jps.insert("overwrite",FM_WSTR(L"置き換える"));
jps.insert("auto_select",FM_WSTR(L"自動"));
jps.insert("select_language",FM_WSTR(L"言語を選択してください"));
jps.insert("enter_file_name",FM_WSTR(L"ファイル名を入れてください。"));
jps.insert("front_suffix",FM_WSTR(L"前方"));
jps.insert("rear_suffix",FM_WSTR(L"後方"));
jps.insert("save_jpg_fail",FM_WSTR(L"JPEGファイル生成に失敗しました。"));
jps.insert("change_folder",FM_WSTR(L"フォルダー変更"));
//
//
//
//
#if (RM_MODEL == RM_MODEL_TYPE_KEIYO1 || \
RM_MODEL == RM_MODEL_TYPE_MBJ5010 || \
RM_MODEL == RM_MODEL_TYPE_BV2000 || \
RM_MODEL == RM_MODEL_TYPE_FC_DR232W)
jps.insert("backup","\xe3\x82\xb3\xe3\x83\x94\xe3\x83\xbc"); // コピー
#else
jps.insert("backup","\xe4\xbf\x9d\xe5\xad\x98");
#endif
// Viewer S/W バージョン情報
jps.insert("viewer_sw_version","\x56\x69\x65\x77\x65\x72 \x53\x2f\x57 \xe3\x83\x90\xe3\x83\xbc\xe3\x82\xb8\xe3\x83\xa7\xe3\x83\xb3\xe6\x83\x85\xe5\xa0\xb1");
// ハードウェアアクセラレーション
jps.insert("hw_accel","\xe3\x83\x8f\xe3\x83\xbc\xe3\x83\x89\xe3\x82\xa6\xe3\x82\xa7\xe3\x82\xa2\xe3\x82\xa2\xe3\x82\xaf\xe3\x82\xbb\xe3\x83\xa9\xe3\x83\xac\xe3\x83\xbc\xe3\x82\xb7\xe3\x83\xa7\xe3\x83\xb3");
jps.insert("directx_audio",FM_WSTR(L"DirectX Audio でサウンド再生"));
// パスワード作成4文字以上20文字以内生成。
jps.insert("pw_message","\xe3\x83\x91\xe3\x82\xb9\xe3\x83\xaf\xe3\x83\xbc\xe3\x83\x89\xe4\xbd\x9c\xe6\x88\x90\xef\xbc\x88\x34\xe6\x96\x87\xe5\xad\x97\xe4\xbb\xa5\xe4\xb8\x8a\x32\x30\xe6\x96\x87\xe5\xad\x97\xe4\xbb\xa5\xe5\x86\x85\xef\xbc\x89\xe7\x94\x9f\xe6\x88\x90\xe3\x80\x82");
// パスワード
jps.insert("password","\xe3\x83\x91\xe3\x82\xb9\xe3\x83\xaf\xe3\x83\xbc\xe3\x83\x89");
// パスワード作成
jps.insert("password_new","\xe3\x83\x91\xe3\x82\xb9\xe3\x83\xaf\xe3\x83\xbc\xe3\x83\x89\xe4\xbd\x9c\xe6\x88\x90");
// パスワード入力
jps.insert("password_input","\xe3\x83\x91\xe3\x82\xb9\xe3\x83\xaf\xe3\x83\xbc\xe3\x83\x89\xe5\x85\xa5\xe5\x8a\x9b");
// パスワード変更
jps.insert("password_change","\xe3\x83\x91\xe3\x82\xb9\xe3\x83\xaf\xe3\x83\xbc\xe3\x83\x89\xe5\xa4\x89\xe6\x9b\xb4");
jps.insert("ok","\x4f\x4b");
jps.insert("cancel","\xe3\x82\xad\xe3\x83\xa3\xe3\x83\xb3\xe3\x82\xbb\xe3\x83\xab"); // キャンセル
jps.insert("change","\xe5\xa4\x89\xe6\x9b\xb4"); // 変更
// 入力,確認
jps.insert("input","\xe5\x85\xa5\xe5\x8a\x9b"); // 入力
jps.insert("confirm","\xe7\xa2\xba\xe8\xaa\x8d"); // 確認
jps.insert("old","\xe4\xbb\xa5\xe5\x89\x8d"); // 以前
//jps.insert("file_open_title","\xe3\x83\x95\xe3\x82\xa1\xe3\x82\xa4\xe3\x83\xab\x4f\x50\x45\x4e")
// 間違ったパスワード
jps.insert("password_wrong","\xe9\x96\x93\xe9\x81\x95\xe3\x81\xa3\xe3\x81\x9f\xe3\x83\x91\xe3\x82\xb9\xe3\x83\xaf\xe3\x83\xbc\xe3\x83\x89");
// 注意
jps.insert("warning","\xe6\xb3\xa8\xe6\x84\x8f");
jps.insert("file_not_exist",FM_WSTR(L"ファイルがありません"));
// \x4a\x50\x47 \xe4\xbf\x9d\xe5\xad\x98
// 保存 \xe4\xbf\x9d\xe5\xad\x98
jps.insert("save","\xe4\xbf\x9d\xe5\xad\x98");
// 初期値
jps.insert("default","\xe5\x88\x9d\xe6\x9c\x9f\xe5\x80\xa4");
// 緯度
jps.insert("lat","\xe7\xb7\xaf\xe5\xba\xa6");
//経度
jps.insert("lon","\xe7\xb5\x8c\xe5\xba\xa6");
// 録画ファイルがあるフォルダを選択
jps.insert("open_message","\xe9\x8c\xb2\xe7\x94\xbb\xe3\x83\x95\xe3\x82\xa1\xe3\x82\xa4\xe3\x83\xab\xe3\x81\x8c\xe3\x81\x82\xe3\x82\x8b\xe3\x83\x95\xe3\x82\xa9\xe3\x83\xab\xe3\x83\x80\xe3\x82\x92\xe9\x81\xb8\xe6\x8a\x9e");
// ファイル分割
jps.insert("split_files",FM_WSTR(L"ファイル分割"));
#endif // #if !(MODEL_KOREAN_ONLY)
#if(RM_MODEL == RM_MODEL_TYPE_ADT_CAPS)
kos.insert("report","\xeb\xb3\xb4\xea\xb3\xa0\xec\x84\x9c");
kos.insert("map","\xec\xa7\x80\xeb\x8f\x84");
kos.insert("language","\xec\x96\xb8\xec\x96\xb4");
kos.insert("viewer_info","\xeb\xb7\xb0\xec\x96\xb4\xec\xa0\x95\xeb\xb3\xb4");
kos.insert("capture","\xed\x99\x94\xeb\xa9\xb4\xec\xba\xa1\xec\xb3\x90");
kos.insert("setup","");
kos.insert("close","\xeb\x8b\xab\xea\xb8\xb0"); // 닫기
kos.insert("minimize","\xec\xb5\x9c\xec\x86\x8c\xed\x99\x94"); // 최소화
kos.insert("low_speed","\xec\xa0\x80\xec\x86\x8d");
kos.insert("high_speed","\xea\xb3\xa0\xec\x86\x8d \x28 \x31\x33\x30 \x4b\x6d\x2f\x68 \xec\x9d\xb4\xec\x83\x81 \x29");
kos.insert("flip_h","\xed\x99\x94\xeb\xa9\xb4\xec\xa2\x8c\xec\x9a\xb0\xeb\xb0\x98\xec\xa0\x84");
kos.insert("flip_v","\xed\x99\x94\xeb\xa9\xb4\xec\x83\x81\xed\x95\x98\xeb\xb0\x98\xec\xa0\x84");
kos.insert("swap","\xec\xb1\x84\xeb\x84\x90\xec\xa0\x84\xed\x99\x98");
kos.insert("fullscreen","\xec\xa0\x84\xec\xb2\xb4\xed\x99\x94\xeb\xa9\xb4");
kos.insert("graph_zoom_in","\xed\x99\x95\xeb\x8c\x80");
kos.insert("graph_zoom_out","\xec\xb6\x95\xec\x86\x8c");
kos.insert("brightness","\xeb\xb0\x9d\xea\xb8\xb0\xec\xa1\xb0\xec\xa0\x95");
kos.insert("contrast","\xeb\x8c\x80\xeb\xb9\x84\xec\xa1\xb0\xec\xa0\x95");
kos.insert("play_speed","\xec\x9e\xac\xec\x83\x9d\xec\x86\x8d\xeb\x8f\x84");
kos.insert("volume_mute","\xec\x9d\x8c\xec\x86\x8c\xea\xb1\xb0");
kos.insert("volume_un_mute","\xec\x9d\x8c\xec\x86\x8c\xea\xb1\xb0 \xed\x95\xb4\xec\xa0\x9c");
kos.insert("play_backward","\x31\xec\xb4\x88\xed\x9b\x84\xeb\xb0\xa9\xec\x9d\xb4\xeb\x8f\x99");
kos.insert("play_play","\xec\x9e\xac\xec\x83\x9d\xec\x8b\x9c\xec\x9e\x91");
kos.insert("play_stop","\xec\xa0\x95\xec\xa7\x80");
kos.insert("play_forward","\x31\xec\xb4\x88\xec\xa0\x84\xeb\xb0\xa9\xec\x9d\xb4\xeb\x8f\x99");
kos.insert("play_file_previous","\xec\x9d\xb4\xec\xa0\x84\xed\x8c\x8c\xec\x9d\xbc\xec\x9e\xac\xec\x83\x9d");
kos.insert("play_file_next","\xeb\x8b\xa4\xec\x9d\x8c\xed\x8c\x8c\xec\x9d\xbc\xec\x9e\xac\xec\x83\x9d");
kos.insert("open","\xed\x8c\x8c\xec\x9d\xbc\xec\x97\xb4\xea\xb8\xb0");
kos.insert("play_pause","\xec\x9d\xbc\xec\x8b\x9c\xec\xa0\x95\xec\xa7\x80");
kos.insert("backup","\xed\x8c\x8c\xec\x9d\xbc\xeb\xb3\xb4\xec\xa1\xb4");
kos.insert("viewer_sw_version","\xeb\xb7\xb0\xec\x96\xb4 \x53\x2f\x57 \xeb\xb2\x84\xec\xa0\x84 \xec\xa0\x95\xeb\xb3\xb4");
kos.insert("hw_accel","\x48\x2f\x57 \xea\xb7\xb8\xeb\x9e\x98\xed\x94\xbd \xea\xb0\x80\xec\x86\x8d \xec\x82\xac\xec\x9a\xa9");
#endif
#endif // ENG
#if (RM_MODEL == RM_MODEL_TYPE_MH9000)
jps.insert("select_fr",FM_WSTR(L"フロント l リア"));
jps.insert("select_lr",FM_WSTR(L"左サイド l 右サイド"));
jps.insert("select_front",FM_WSTR(L"フロント"));
jps.insert("select_rear",FM_WSTR(L"リア"));
jps.insert("select_left",FM_WSTR(L"左サイド"));
jps.insert("select_right",FM_WSTR(L"右サイド"));
jps.insert("select_asst",FM_WSTR(L"サブカメラ"));
jps.insert("select_indoor",FM_WSTR(L"車内カメラ"));
kos.insert("select_fr",FM_WSTR(L"전후방"));
kos.insert("select_lr",FM_WSTR(L"좌우측"));
kos.insert("select_front",FM_WSTR(L"전방"));
kos.insert("select_rear",FM_WSTR(L"후방"));
kos.insert("select_left",FM_WSTR(L"좌측"));
kos.insert("select_right",FM_WSTR(L"우측"));
kos.insert("select_asst",FM_WSTR(L"보조캠"));
kos.insert("select_indoor",FM_WSTR(L"실내캠"));
ens.insert("select_fr","Front|Rear");
ens.insert("select_lr",FM_WSTR(L"Left|Right"));
ens.insert("select_front",FM_WSTR(L"Front"));
ens.insert("select_rear",FM_WSTR(L"Rear"));
ens.insert("select_left",FM_WSTR(L"Left"));
ens.insert("select_right",FM_WSTR(L"Right"));
ens.insert("select_asst",FM_WSTR(L"Assistant"));
ens.insert("select_indoor",FM_WSTR(L"Indoor"));
#endif //
#if (DEBUG_FM_STRINGS)
QFile logFile("C:\\home\\temp2\\fmviewer.txt");
if (logFile.open(QFile::WriteOnly | QFile::Text))
{
QTextStream ts(&logFile);
ts.setCodec("UTF-8");
foreach (QString key, kos.keys())
{
ts << "\"" << key << "\",\"" << kos[key] << "\",\"" << jps[key] << "\",\"" << ens[key] << "\"\n";
}
}
#endif
}
#if (MODEL_KOREAN_ONLY)
QString FMS::txt(const char* key,RMLanguage::LANGUAGE_TYPE type) {
Q_UNUSED(type)
return FMS::txt(key);
}
#else // #if !(MODEL_KOREAN_ONLY)
QString FMS::txt(const char* key,RMLanguage::LANGUAGE_TYPE type) {
if(!_loaded) {
load();
_loaded = true;
}
QMap<QString,QString>* l = NULL;
switch (type) {
case RMLanguage::LANGUAGE_JP:
l = &FMS::jps;
break;
#if (RC_LANGUAGE == 0x0412)
case RMLanguage::LANGUAGE_KR:
l = &FMS::kos;
break;
#endif // #if (RC_LANGUAGE == 0x0412)
#if(SUB_MODEL_BV5000 || RM_MODEL == RM_MODEL_TYPE_MH9000 || RM_MODEL == RM_MODEL_TYPE_EMT_KR)
case RMLanguage::LANGUAGE_EN:
l = &FMS::ens;
break;
#endif // #if(SUB_MODEL_BV5000)
}
if(l->contains(key)) {
return l->value(key);
} else {
qInfo() << "STN:!!!" << key << RMLanguage::instance()->language() <<__FUNCTION__;
return "";
}
}
#endif // #if !(MODEL_KOREAN_ONLY)
QString FMS::txt(const char* key)
{
if(!_loaded) {
load();
_loaded = true;
}
#if (RM_MODEL == RM_MODEL_TYPE_NX_DRW22 || \
RM_MODEL == RM_MODEL_TYPE_KEIYO1 || \
RM_MODEL == RM_MODEL_TYPE_MBJ5010 || \
RM_MODEL == RM_MODEL_TYPE_TBD360 || \
RM_MODEL == RM_MODEL_TYPE_FC_DR232W || \
RM_MODEL == RM_MODEL_TYPE_AN6000 || \
RM_MODEL == RM_MODEL_TYPE_XLDR_88)
//qInfo() << jps.contains(key) << key << ":" << jps[key] << __FUNCTION__;
//qInfo() << key << QString::fromUtf8(jps[key]) << __FUNCTION__;
#if (MODEL_KOREAN_ONLY)
return kos[key];
#else // MODEL_KOREAN_ONLY
return jps[key];
#endif // MODEL_KOREAN_ONLY
#elif(RM_MODEL == RM_MODEL_TYPE_ADT_CAPS)
return kos[key];//QString::fromUtf8(kos[key]);
#elif (SUB_MODEL_BV5000 || RM_MODEL == RM_MODEL_TYPE_MH9000)
QMap<QString,QString>* l = NULL;
switch (RMLanguage::instance()->language()) {
case RMLanguage::LANGUAGE_JP:
l = &FMS::jps;
break;
case RMLanguage::LANGUAGE_KR:
l = &FMS::kos;
break;
case RMLanguage::LANGUAGE_EN:
l = &FMS::ens;
break;
}
if(l->contains(key)) {
return l->value(key);
} else {
qInfo() << "STN:!!!" << key << RMLanguage::instance()->language() <<__FUNCTION__;
return "";
}
#elif (RC_LANGUAGE == 0x0412)
if(!kos.contains(key) || kos.contains(key) == NULL) {
qInfo() << "LANGUAGE ERROR:" << key << __FUNCTION__ ;
return "";
}
return kos[key];
#endif
}
#if(SUPPORT_LANGUAGE_INSERT)
// 1:KR 2:JP, 3:EN
void FMS::insert_if_not_exist(int type,QString key, QString text)
{
QMap<QString,QString>* l = NULL;
switch (type) {
case 2:
l = &FMS::jps;
break;
case 3:
l = &FMS::ens;
break;
case 1:
l = &FMS::kos;
break;
}
//qInfo() << type << key << text << __FUNCTION__;
if(l != NULL && !l->contains(key)) {
l->insert(key,text);
}
}
#endif // #SUPPORT_LANGUAGE_INSERT
#endif // FM_STR_TYPE2

View File

@@ -0,0 +1,47 @@
#ifndef FM_STRINGS_H
#define FM_STRINGS_H
#include <QObject>
#include <QMap>
#include "rm_include.h"
class FMS : public QObject
{
Q_OBJECT
private:
static bool _loaded;
public:
explicit FMS(QObject *parent = nullptr) : QObject(parent){}
#if (FM_STR_TYPE2)
static QMap<QString,QStringList> strings;
#else // FM_STR_TYPE2
#if !(MODEL_KOREAN_ONLY)
static QMap<QString,QString> jps;
#endif
#if(RM_MODEL == RM_MODEL_TYPE_ADT_CAPS || RM_MODEL == RM_MODEL_TYPE_TB4000 || RM_MODEL == RM_MODEL_TYPE_BV2000 || SUB_MODEL_KEIYO_KR || RM_MODEL == RM_MODEL_TYPE_MH9000 || RM_MODEL_TYPE_EMT_KR)
static QMap<QString,QString> kos;
#endif
#if(SUB_MODEL_BV5000 || RM_MODEL == RM_MODEL_TYPE_MH9000 || RM_MODEL_TYPE_EMT_KR)
static QMap<QString,QString> ens;
#endif
#endif // FM_STR_TYPE2
static void load();
static QString txt(const char* key);
static QString txt(const char* key,RMLanguage::LANGUAGE_TYPE type);
static QString txtLine(const char* key);
#if(SUPPORT_LANGUAGE_INSERT)
// 1:JP, 2:EN, 3:KR
static void insert_if_not_exist(int type,QString key, QString text);
#endif // SUB_MODEL_BV5000
signals:
public slots:
};
#endif // FM_STRINGS_H

View File

@@ -0,0 +1,678 @@
#include "fm_video_split.h"
#if (SUPPORT_AVI_SPLIT)
#include <QStyleOption>
#include <QPainter>
#include "../ui/QProgressIndicator.h"
#if !(USE_FFMPEG_TO_SPLIT)
//#include "../data/rm_format_avi.h"
//#include "../data/fileio.h"
#else
extern "C" {
//#define __STDC_FORMAT_MACROS
//#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
}
#endif // #if (USE_FFMPEG_TO_SPLIT)
FMProgressDialog* FMVideoSplit::progress = NULL;
FMVideoSplit::FMVideoSplit(QString path)
{
_path = path;
idx = NULL;
}
#if !(USE_FFMPEG_TO_SPLIT)
#define A4_CHAR_TO_NUM(cs) ((((int32_t)cs[3])<<24)|(((int32_t)cs[2])<<16)|(((int32_t)cs[1])<<8)|(((int32_t)cs[0])))
void FMVideoSplit::run()
{
QString document = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
bFirstVideo = true;
QString dest = QDir::cleanPath(document + QDir::separator() + QFileInfo(_path).baseName() + "_F" + ".AVI");
if(QFile(dest).exists()) {
QFile(dest).remove();
}
convert(_path,dest);
bFirstVideo = false;
dest = QDir::cleanPath(document + QDir::separator() + QFileInfo(_path).baseName() + "_R" + ".AVI");
if(QFile(dest).exists()) {
QFile(dest).remove();
}
convert(_path,dest);
emit done();
}
void FMVideoSplit::convert(QString path,QString dest)
{
FILE *file = _wfopen(path.toStdWString().c_str(),L"rb");
memset(&root,0,sizeof(_RIFF_TREE));
fread(&root.tag,4,1,file);
fread(&root.size,4,1,file);
fread(&root.type,4,1,file);
root.offset = ftell(file);
//debugPrint(&root);
bFirstVideoStreamFound = false;
readTree(file,&root,0);
root.offset_w = root.offset;
//debugTree(&root);
makeOffset(&root);
makeIndex(&root);
root.size_w += 4;
//debugTree(&root);
//QString dest = "C:\\home\\temp\\copy2.avi";
// QByteArray ba = str1.toLocal8Bit();
//const char *c_str2 = ba.data();
//FILE *wf = fopen(dest.toLocal8Bit().data(),"wb");
FILE *wf = _wfopen(dest.toStdWString().c_str(),L"wb");
saveTree(&root,file,wf);
fclose(wf);
fclose(file);
//qInfo() << "---------------------------------------------------";
//
free(idx);
idx = NULL;
freeTree(&root);
QThread::msleep(100);
}
void FMVideoSplit::debugPrint(RIFF_TREE* item)
{
QString tab = "";
for(int i=0;i<item->depth;i++) {
tab += " ";
}
// RIFF_TREE* p = item->depth;
// while(p != NULL) {
// p = p->parent;
// }
if(item->tag == A4_CHAR_TO_NUM("LIST") || item->tag == A4_CHAR_TO_NUM("RIFF")) {
qInfo() << tab + QString().sprintf("OFFSET: %d",item->offset) +
QString().sprintf(" W_OFFSET: %d COUNT:%d",item->offset_w,item->count) +
QString().sprintf(" TAG:[%c%c%c%c]",(char)((item->tag >> 0) & 0xFF),(char)((item->tag >> 8) & 0xFF),(char)((item->tag >> 16) & 0xFF),(char)((item->tag >> 24) & 0xFF)) +
QString().sprintf(" TYPE:[%c%c%c%c]",(char)(item->type >> 0 & 0xFF),(char)(item->type >> 8 & 0xFF),(char)(item->type >> 16 & 0xFF),(char)((item->type >> 24) & 0xFF)) +
QString().sprintf(" SIZE: %d",item->size) + QString().sprintf(" SIZE_W: %d REMOVE:%d",item->size_w, item->remove);
}
else {
qInfo() << tab + QString().sprintf("OFFSET: %d",item->offset) +
QString().sprintf(" W_OFFSET: %d",item->offset_w) +
QString().sprintf(" TAG:[%c%c%c%c]",(char)((item->tag >> 0) & 0xFF),(char)((item->tag >> 8) & 0xFF),(char)((item->tag >> 16) & 0xFF),(char)((item->tag >> 24) & 0xFF)) +
QString().sprintf(" SIZE: %d",item->size) + QString().sprintf(" SIZE_W: %d REMOVE:%d",item->size_w, item->remove);
}
}
void FMVideoSplit::debugTree(RIFF_TREE* p)
{
//p->parent = NULL;
debugPrint(p);
int32_t sz = 0;
for(int i=0;i<p->count;i++) {
if(p->type == A4_CHAR_TO_NUM("movi")) {
// sz += 8;
// sz = sz + p->childs[i].size + (p->childs[i].size % 2);
// qInfo() << "+" << sz;
}
else {
//p->childs[i].parent = NULL;
debugTree(&p->childs[i]);
}
}
if(p->type == A4_CHAR_TO_NUM("movi")) {
qInfo() << "MOVI SIZE:" << sz;
}
}
void FMVideoSplit::freeTree(RIFF_TREE* p)
{
if(p->childs != NULL) {
// CHILD 가 존재할 경우 처리
for(int i=0;i<p->count;i++) {
if(p->childs[i].childs != NULL) {
freeTree(&p->childs[i]);
}
}
// SUB 가 없을 경우
//debugPrint(parent);
free(p->childs);
}
}
void FMVideoSplit::saveTree(RIFF_TREE* p,FILE* read,FILE* write)
{
if(p->remove != 0) {
return;
}
//debugPrint(p);
int roffset_w = p->offset_w - 8;
if(p->count > 0) {
roffset_w -= 4; // TYPE
}
fseek(write,roffset_w,SEEK_SET);
fwrite(&p->tag,4,1,write);
fwrite(&p->size_w,4,1,write);
if(p->count > 0) {
if(p->count > 0) {
fwrite(&p->type,4,1,write);
}
for(int i=0;i<p->count;i++) {
saveTree(&p->childs[i],read,write);
}
}
else {
if(p->tag == A4_CHAR_TO_NUM("idx1")) {
fwrite(idx,p->size_w,1,write);
}
else {
// 그냥 이동하면 데이터 영역임
fseek(read,p->offset,SEEK_SET);
void* buffer = malloc(p->size);
fread(buffer,p->size_w,1,read);
if(p->tag == A4_CHAR_TO_NUM("avih")) {
AVIHeader* h = (AVIHeader*)buffer;
h->NumberOfStreams = 3;
//qInfo() << "NumberOfStreams:" << h->NumberOfStreams << "h->DataLength:" << h->DataLength ;
}
fwrite(buffer,p->size_w,1,write);
free(buffer);
}
}
}
// OFFSET 우선 처리하고 index 처리
void FMVideoSplit::makeIndex(RIFF_TREE* p)
{
RIFF_TREE* movi = NULL;
for(int i=0;i<p->count;i++) {
if(p->childs[i].type == A4_CHAR_TO_NUM("movi")) {
movi = &p->childs[i];
break;
}
}
//qInfo() << "MOV:" << movi->count << movi->count * 16;
idx = (int32_t*)malloc(movi->count * 16);
int realCount = 0;
for(int i=0;i<movi->count;i++) {
RIFF_TREE* t = &movi->childs[i];
if(t->tag == A4_CHAR_TO_NUM("01dc")) {
t->tag = A4_CHAR_TO_NUM("00dc");
}
else if (t->tag == A4_CHAR_TO_NUM("02wb")) {
t->tag = A4_CHAR_TO_NUM("01wb");
}
else if (t->tag == A4_CHAR_TO_NUM("03tx")) {
t->tag = A4_CHAR_TO_NUM("02tx");
}
int ri = realCount * 4;
idx[ri + 0] = t->tag;
idx[ri + 1] = 16;
idx[ri + 2] = t->offset_w - 8;
idx[ri + 3] = t->size;
if(t->remove != 1) {
realCount++;
}
}
RIFF_TREE* idx1 = NULL;
for(int i=0;i<p->count;i++) {
if(p->childs[i].tag == A4_CHAR_TO_NUM("idx1")) {
idx1 = &p->childs[i];
break;
}
}
idx1->size_w = realCount * 16;
int diff = (idx1->size - (realCount * 16));
p->size_w -= diff;
//qInfo() << "IDX:" << idx1->size << idx1->size_w << "DIFF:" << diff;
}
// TREE 탐색하며 OFFSET 생성, SIZE 는 유지
void FMVideoSplit::makeOffset(RIFF_TREE* p)
{
// if(p->remove != 0) {
// return;
// }
int offset = p->offset_w; // SIZE + TAG 는 항상 존재
for(int i=0;i<p->count;i++) {
if(p->childs[i].remove == 1) {
continue;
}
int add = 8;
if(p->childs[i].tag == A4_CHAR_TO_NUM("LIST")) {
add += 4;
}
// 쓰기 옵셋
p->childs[i].offset_w = offset + add;
// if(p->childs[i].offset_w != p->childs[i].offset) {
// qInfo() << offset << "--------------------------------------------";
// debugPrint(&p->childs[i]);
// qInfo() << add << "--------------------------------------------";
// }
if(p->childs[i].count > 0) {
p->childs[i].size_w = 4; //???
makeOffset(&p->childs[i]);
}
else if (p->childs[i].remove == 0){
p->childs[i].size_w = p->childs[i].size;
}
if(p->childs[i].remove == 0) {
p->size_w = p->size_w + p->childs[i].size_w + (p->childs[i].size_w % 2);
p->size_w += 8;
// if(p->type == A4_CHAR_TO_NUM("movi")) {
// debugPrint(&p->childs[i]);
// qInfo() << i << p->size_w;
// }
// if(p->childs[i].tag == A4_CHAR_TO_NUM("LIST")) {
// p->size_w += 4;
// }
offset += 8; // TAG+SIZE
offset += p->childs[i].size_w;
// PAD 2
if(offset % 2 == 1) {
offset += 1;
}
}
//parent->childs[i].offset = offset;
}
}
void FMVideoSplit::resizeTree(RIFF_TREE* item,int count)
{
// qInfo() << "REALLOC-----------" << count;
// debugPrint(item);
// qInfo() << "------------------";
RIFF_TREE* temp = (RIFF_TREE*)malloc(sizeof(_RIFF_TREE) * count);
memset(temp,0,sizeof(_RIFF_TREE) * count);
memcpy(temp,item->childs,sizeof(_RIFF_TREE) * item->count);
free(item->childs);
item->childs = temp;
}
void FMVideoSplit::readTree(FILE* file, RIFF_TREE* p, int32_t depth)
{
const int buffer_count = 100;
int current_buffer_count = 100;
// if(p->type != A4_CHAR_TO_NUM("movi")) {
// qInfo() << "CHECK:" << p->offset + p->size << "TELL:" << ftell(file);
// }
while (ftell(file)<(p->offset + p->size - 4))
{
if(p->childs == NULL) {
p->childs = (RIFF_TREE*)malloc(sizeof(_RIFF_TREE) * buffer_count);
memset(p->childs,0,sizeof(_RIFF_TREE) * buffer_count);
current_buffer_count = buffer_count;
}
if(p->count + 1 >= current_buffer_count) {
current_buffer_count += buffer_count;
// 기존 레코드 복사
resizeTree(p,current_buffer_count);
}
RIFF_TREE* item = &p->childs[p->count];
fread(&item->tag,4,1,file);
fread(&item->size,4,1,file);
item->offset = ftell(file); // SIZE 읽은 후 OFFSET 지정
item->depth = depth + 1;
//item->parent = p;
// 리스트만 처리
if(item->tag == A4_CHAR_TO_NUM("LIST")) {
item->offset += 4; // LIST 는 TYPE 이 존재하여 추가
fread(&item->type,4,1,file);
//debugPrint(item);
readTree(file,item,item->depth + 1);
// if(item->type == A4_CHAR_TO_NUM("movi")) {
// item->remove = 1;
// }
}
else {
//if(p->type != A4_CHAR_TO_NUM("movi")) {
//debugPrint(item);
//}
if(item->tag == A4_CHAR_TO_NUM("strh")) {
void* v = malloc(item->size);
fread(v,item->size,1,file);
_STRHeader* h = (_STRHeader*)v;
if(h->DataType[0] == 'v' && h->DataType[1] == 'i' && h->DataType[2] == 'd' && h->DataType[3] == 's')
{
if(bFirstVideoStreamFound) {
if(bFirstVideo) {
p->remove = 1;
}
}
else {
if(!bFirstVideo) {
p->remove = 1;
}
}
bFirstVideoStreamFound = true;
}
//qInfo() << "STRH" << h->DataType;
}
// if(item->tag == A4_CHAR_TO_NUM("idx1")) {
// item->remove = 1;
// }
if(item->tag == A4_CHAR_TO_NUM("01dc") && bFirstVideo) {
item->remove = 1;
}
else if(item->tag == A4_CHAR_TO_NUM("00dc") && !bFirstVideo) {
item->remove = 1;
}
// 2 BIT PAD
int32_t seek = item->offset + item->size;
if(seek % 2 == 1) {
seek += 1;
}
fseek(file,seek,SEEK_SET);
}
p->count += 1;
}
resizeTree(p,p->count);
}
#else // (USE_FFMPEG_TO_SPLIT)
// MP4 의 경우 PCM 오디오 인코딩이 안됨
// MOV 의 경우 PCM 이 되나 미디어 플레이어에서 재생안됨
// https://cpp.hotexamples.com/examples/-/-/avformat_write_header/cpp-avformat_write_header-function-examples.html
bool FMVideoSplit::start(QString path)
{
//av_register_all();
QString outFile = "C:\\home\\temp\\test.mov";
char buffer[1024] = {0,};
qInfo() << path << __FUNCTION__;
const AVOutputFormat *ofmt = NULL;
AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
AVPacket *pkt = NULL;
//const char *in_filename, *out_filename;
int ret, i;
int stream_index = 0;
int *stream_mapping = NULL;
int stream_mapping_size = 0;
pkt = av_packet_alloc();
if (!pkt) {
qInfo() << "Could not allocate AVPacket";
// fprintf(stderr, "Could not allocate AVPacket\n");
return 1;
}
// d->format_ctx->probesize = 8000000;
// d->format_ctx->max_analyze_duration = 8000000;
// ret = avformat_find_stream_info(d->format_ctx, NULL);
if ((ret = avformat_open_input(&ifmt_ctx, path.toUtf8().constData(), 0, 0)) < 0) {
qInfo() << "Could not open input file " << path;
// fprintf(stderr, "Could not open input file '%s'", .toUtf8().constData());
goto end;
}
// ifmt_ctx->probesize = 8000000;
// ifmt_ctx->max_analyze_duration = 8000000;
if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
qInfo() << "Failed to retrieve input stream information";
//fprintf(stderr, "Failed to retrieve input stream information");
goto end;
}
av_dump_format(ifmt_ctx, 0, path.toUtf8().constData(), 0);
avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, outFile.toUtf8().constData());
if (!ofmt_ctx) {
qInfo() << "Could not create output context";
//fprintf(stderr, "Could not create output context\n");
ret = AVERROR_UNKNOWN;
goto end;
}
stream_mapping_size = ifmt_ctx->nb_streams;
stream_mapping = (int*)av_calloc(stream_mapping_size, sizeof(*stream_mapping));
if (!stream_mapping) {
ret = AVERROR(ENOMEM);
goto end;
}
ofmt = ofmt_ctx->oformat;
bool bVideo = false;
int subtitle_stream = -1;
for (i = 0; i < ifmt_ctx->nb_streams; i++) {
AVStream *out_stream;
AVStream *in_stream = ifmt_ctx->streams[i];
AVCodecParameters *in_codecpar = in_stream->codecpar;
if (in_codecpar->codec_type != AVMEDIA_TYPE_AUDIO &&
in_codecpar->codec_type != AVMEDIA_TYPE_VIDEO &&
in_codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) {
stream_mapping[i] = -1;
continue;
}
// bVideo
if(in_codecpar->codec_type == AVMEDIA_TYPE_VIDEO && !bVideo) {
bVideo = true;
//qInfo() << "TEST" << __FUNCTION__;
stream_mapping[i] = -1;
continue;
}
if(in_codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
//qInfo() << "TEST" << __FUNCTION__;
//stream_mapping[i] = -1;
//continue;
}
if(in_codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) {
subtitle_stream = i;
in_codecpar->codec_id = AV_CODEC_ID_MOV_TEXT;//AV_CODEC_ID_TEXT;
//in_codecpar->codec_id = AV_CODEC_ID_TEXT;
//qInfo() << "TEST" << __FUNCTION__;
//stream_mapping[i] = -1;
//continue;
}
stream_mapping[i] = stream_index++;
out_stream = avformat_new_stream(ofmt_ctx, NULL);
if (!out_stream) {
qInfo() << "Failed allocating output stream";
//fprintf(stderr, "Failed allocating output stream\n");
ret = AVERROR_UNKNOWN;
goto end;
}
ret = avcodec_parameters_copy(out_stream->codecpar, in_codecpar);
if (ret < 0) {
qInfo() << "Failed to copy codec parameters";
//fprintf(stderr, "Failed to copy codec parameters\n");
goto end;
}
out_stream->codecpar->codec_tag = 0;
}
// AUDIO STREAM
//open_audio(ofmt_ctx, audio_codec, &audio_st, opt);
av_dump_format(ofmt_ctx, 0, outFile.toUtf8().constData(), 1);
if (!(ofmt->flags & AVFMT_NOFILE)) {
ret = avio_open(&ofmt_ctx->pb, outFile.toUtf8().constData(), AVIO_FLAG_WRITE);
if (ret < 0) {
qInfo() << "Could not open output file " << outFile;
//fprintf(stderr, "Could not open output file '%s'", outFile.toUtf8().constData());
goto end;
}
}
// https://stackoverflow.com/questions/31846650/avformat-write-header-return-error-code-when-trying-to-write-pcmu-encoded-frame
ret = avformat_write_header(ofmt_ctx, NULL);
if (ret < 0) {
memset(buffer,0,1024);
av_strerror(ret,buffer,1024);
qInfo() << "Error occurred when opening output file" << buffer;
// fprintf(stderr, "Error occurred when opening output file\n");
goto end;
}
while (1) {
AVStream *in_stream, *out_stream;
ret = av_read_frame(ifmt_ctx, pkt);
if (ret < 0)
break;
in_stream = ifmt_ctx->streams[pkt->stream_index];
if (pkt->stream_index >= stream_mapping_size ||
stream_mapping[pkt->stream_index] < 0) {
av_packet_unref(pkt);
continue;
}
// SUB TITLE 일 경우
if(in_stream->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) {
qInfo() << (const char*)pkt->data << __FUNCTION__;
}
pkt->stream_index = stream_mapping[pkt->stream_index];
out_stream = ofmt_ctx->streams[pkt->stream_index];
//log_packet(ifmt_ctx, pkt, "in");
/* copy packet */
av_packet_rescale_ts(pkt, in_stream->time_base, out_stream->time_base);
pkt->pos = -1;
//log_packet(ofmt_ctx, pkt, "out");
ret = av_interleaved_write_frame(ofmt_ctx, pkt);
/* pkt is now blank (av_interleaved_write_frame() takes ownership of
* its contents and resets pkt), so that no unreferencing is necessary.
* This would be different if one used av_write_frame(). */
if (ret < 0) {
qInfo() << "Error muxing packet";
//fprintf(stderr, "Error muxing packet\n");
break;
}
}
av_write_trailer(ofmt_ctx);
end:
av_packet_free(&pkt);
avformat_close_input(&ifmt_ctx);
/* close output */
if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
avio_closep(&ofmt_ctx->pb);
avformat_free_context(ofmt_ctx);
av_freep(&stream_mapping);
if (ret < 0 && ret != AVERROR_EOF) {
//fprintf(stderr, "Error occurred: %s\n", av_err2str(ret));
return 1;
}
return true;
}
#endif // USE_FFMPEG_TO_SPLIT
FMProgressDialog::FMProgressDialog(QWidget *parent, QString title) : QDialog(parent)
{
setWindowFlags(Qt::FramelessWindowHint | Qt::Dialog);
setModal(true);
setStyleSheet("FMProgressDialog{ background-color: #3a3a3a; border: 3px solid #666666;}"); //
setFixedSize(120,120);
QVBoxLayout* layout = new QVBoxLayout(this);
layout->setMargin(0);
layout->setSpacing(20);
layout->setAlignment(Qt::AlignCenter);
QLabel* top = new QLabel(this);
top->setAlignment(Qt::AlignCenter);
top->setText(title); // QString::fromWCharArray(L"ファイル分割中")
top->setStyleSheet("font-family: MS PGothic; font-style: bold;font-size:14px; color : #6699FF");
layout->addWidget(top);
indicator = new QProgressIndicator(this);
indicator->setFixedSize(120,50);
indicator->setColor(QColor(0x6699FF));
indicator->show();
indicator->startAnimation();
layout->addWidget(indicator);
// QLabel* bottom = new QLabel(this);
// bottom->setAlignment(Qt::AlignCenter);
// bottom->setText(QString::fromWCharArray(L"8〜30秒かかります"));
// bottom->setStyleSheet("font-family: MS PGothic; font-style: black;font-size:18px; color : #FFFFFF");
// layout->addWidget(bottom);
}
void FMProgressDialog::paintEvent(QPaintEvent *pe)
{
Q_UNUSED(pe);
QStyleOption o;
o.initFrom(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &o, &p, this);
}
#endif // #if (SUPPORT_AVI_SPLIT)

View File

@@ -0,0 +1,86 @@
#ifndef FM_VIDEO_SPLIT_H
#define FM_VIDEO_SPLIT_H
#if (SUPPORT_AVI_SPLIT)
#include <QObject>
#include <QtCore>
#include <QDialog>
#include <stdint.h>
// MP4 의 경우 PCM 오디오 인코딩이 안됨
// MOV 의 경우 PCM 이 되나 미디어 플레이어에서 재생안됨
// AVI RIFF 처리로 분리 해야함..
// https://cpp.hotexamples.com/examples/-/-/avformat_write_header/cpp-avformat_write_header-function-examples.html
#define USE_FFMPEG_TO_SPLIT 0
#if !(USE_FFMPEG_TO_SPLIT)
#include "../data/rm_format_avi.h"
#endif // USE_FFMPEG_TO_SPLIT
class QProgressIndicator;
class FMProgressDialog : public QDialog
{
Q_OBJECT
public:
FMProgressDialog(QWidget *parent, QString title);
QProgressIndicator* indicator;
private:
void paintEvent(QPaintEvent *pe) override;
};
typedef struct _RIFF_TREE
{
int32_t tag;
int32_t type;
int32_t offset;
int32_t offset_w;
int32_t size;
int32_t size_w;
int32_t depth;
int32_t remove; // 제거
//_RIFF_TREE* parent; // REALLOCATE 로 링크가 깨짐 readTree 내부에서만 사용
int32_t count;
_RIFF_TREE* childs;
} RIFF_TREE;
class FMVideoSplit : public QObject, public QRunnable
{
Q_OBJECT
public:
FMVideoSplit(QString path);
//bool start(QString path);
virtual void run();
static FMProgressDialog* progress;
private:
#if !(USE_FFMPEG_TO_SPLIT)
QString _path;
int32_t *idx;
bool bFirstVideo;
bool bFirstVideoStreamFound;
RIFF_TREE root;
void debugPrint(RIFF_TREE* item);
void debugTree(RIFF_TREE* p);
void freeTree(RIFF_TREE* p);
void makeOffset(RIFF_TREE* p);
void makeIndex(RIFF_TREE* p);
void saveTree(RIFF_TREE* p,FILE* read, FILE* write);
void resizeTree(RIFF_TREE* item,int count);
void readTree(FILE* file, RIFF_TREE* p,int32_t depth);
void convert(QString path,QString dest);
#endif
signals:
void done();
};
#endif // (SUPPORT_AVI_SPLIT)
#endif // FM_VIDEO_SPLIT_H

View File

@@ -0,0 +1,260 @@
#include "rm_excel_report.h"
#include "../rm_app.h"
#include "../rm_include.h"
#include "../fm_dimensions.h"
#include "../ui/rm_dialog_map.h"
#include "../ui/rm_widget_map.h"
#include "../core/rm_math.h"
#include <QDir>
#include <QPainter>
// Screen Capture
#include <QPixmap>
#include <QScreen>
#include <QGuiApplication>
#include <QMainWindow>
#include <QDialog>
#include <QDate>
#include <QDateTime>
#if (USE_JP_ADDRESS)
#include "../data/fm_address.h"
#endif
/*
float QAspectScaleFit(QSize sourceSize, QRect destRect)
{
QSize destSize = destRect.size();
float scaleW = (float)destSize.width() / (float)sourceSize.width();
float scaleH = (float)destSize.height() / (float)sourceSize.height();
return MIN(scaleW, scaleH);
}
// Fit
QRect QRectAspectFitRect(QSize sourceSize, QRect destRect)
{
QSize destSize = destRect.size();
float destScale = QAspectScaleFit(sourceSize, destRect);
float newWidth = (float)sourceSize.width() * destScale;
float newHeight = (float)sourceSize.height() * destScale;
float dWidth = (((float)destSize.width() - newWidth) / 2.0f);
float dHeight = (((float)destSize.height() - newHeight) / 2.0f);
QRect rect = QRect(dWidth + destRect.left(), dHeight + destRect.top(), newWidth, newHeight);
return rect;
}
*/
RMExcelReport::RMExcelReport(QObject *parent) : QAxObject("Excel.Application",parent)
{
_workbooks = NULL;
_workbook = NULL;
_sheets = NULL;
_sheet = NULL;
_shapes = NULL;
_picture = NULL;
}
void RMExcelReport::clearAll()
{
// 순서대로 제거해야함!!
if(_picture != NULL) {
delete _picture;
_picture = NULL;
}
if(_shapes != NULL) {
delete _shapes;
_shapes = NULL;
}
if(_sheet != NULL) {
delete _sheet;
_sheet = NULL;
}
if(_sheets != NULL) {
delete _sheets;
_sheets = NULL;
}
if(_workbook != NULL) {
delete _workbook;
_workbook = NULL;
}
if(_workbooks != NULL) {
delete _workbooks;
_workbooks = NULL;
}
}
#if (USE_JP_ADDRESS)
bool RMExcelReport::prepare(QString& path,QDateTime* pDateTime, double lon, double lat)
#else
bool RMExcelReport::prepare(QString& path,QDateTime* pDateTime)
#endif
{
#if (RM_MODEL != RM_MODEL_TYPE_XLDR_88)
Q_UNUSED(pDateTime)
#endif // !RM_MODEL_TYPE_XLDR_88
QDateTime date = QDateTime::currentDateTime();
_workbooks = querySubObject("Workbooks");
if(_workbooks == NULL)
{
clearAll();
return false;
}
// 저장 완료
QString tempReportPath = QDir::cleanPath(RMApp::appPath(RMApp::CAPTURE) + "/_report" + date.toString("yyyy_MMdd_HHmmss") + ".xls");
QFile::copy(":/report/report.xls",tempReportPath);
QFile::setPermissions(tempReportPath,QFile::permissions(tempReportPath) | QFileDevice::WriteOther);
// qInfo() << tempReportPath;
// QDir::toNativeSeparators(last);
QString srcPath = QDir::toNativeSeparators(tempReportPath);
_workbook = _workbooks->querySubObject( "Open(const QString&)", srcPath );
if(_workbook == NULL) {
clearAll();
return false;
}
// 경고 표시 방지
_workbook->setProperty("DisplayAlerts", false);
_sheets = _workbook->querySubObject( "Worksheets" );
if(_sheets == NULL)
{
clearAll();
return false;
}
// sheet 가져옴
_sheet = _sheets->querySubObject( "Item( int )", 1 );
if(_sheet == NULL)
{
clearAll();
return false;
}
#if (RM_MODEL == RM_MODEL_TYPE_XLDR_88)
//qInfo() << time << __FUNCTION__;
QAxObject * range;
// 보고서 작성일을 기준으로 생성
QDateTime current = QDateTime::currentDateTime();
range = _sheet->querySubObject("Range(AQ2)");
range->setProperty("Value",current.toString("yyyy"));
range = _sheet->querySubObject("Range(AU2)");
range->setProperty("Value",current.toString("MM"));
range = _sheet->querySubObject("Range(AX2)");
range->setProperty("Value",current.toString("dd"));
// 사고 발생일은 데이터 시간
range = _sheet->querySubObject("Range(H12)");
range->setProperty("Value",pDateTime->toString("yyyy-MM-dd"));
QString pam = pDateTime->time().hour() > 12 ? "PM" : "AM";
range = _sheet->querySubObject("Range(W12)");
range->setProperty("Value",pam + " " + pDateTime->toString("HH") + QString::fromUtf8("\xe6\x99\x82") + pDateTime->toString("mm"));
#if (USE_JP_ADDRESS)
if(IS_VALID_LOCATION(lon,lat))
{
// 발생장소
if(FMAddress::instance()->open()) {
QStringList res;
if(FMAddress::instance()->search(lon,lat,res)) {
range = _sheet->querySubObject("Range(H14)");
QString address = (res.at(0) + " " + res.at(1) + " " + res.at(2) + " " + res.at(3) + "-" + res.at(4));
range->setProperty("Value",address);
}
}
}
#endif // USE_JP_ADDRESS
#endif
QScreen *screen = QGuiApplication::primaryScreen();
QPixmap originalPixmap = screen->grabWindow(RMApp::instance()->pMainWindow->winId());
QPainter painter(&originalPixmap);
#if !(DO_NOT_USE_MAP)
// 지도 크기로 생성
#if (USE_WEBVIEW2)
QPixmap mapPixmap;
#else // #if (USE_WEBVIEW2)
QPixmap mapPixmap(RMDialogMap::instance()->_map->size());
// 그린다
// qInfo() << RMDialogMap::instance()->isGPSExist();
if(RMDialogMap::instance()->isGPSExist()) {
RMDialogMap::instance()->_map->render(&mapPixmap);
}
#endif // #if (USE_WEBVIEW2)
// 지도 그릴 영역
#if (RM_MODEL == RM_MODEL_TYPE_XLDR_88)
QRect targetRect = QRect(699,285,352,391);
#else
QRect targetRect = QRect(681,331,339,358);
#endif
QRect cropSrc = QRect();
cropSrc.setSize(targetRect.size());
QRect cropRect = QRectAspectFitRect(targetRect.size(),cropSrc);
#if (DUAL_VIEWER)
QPixmap cropped = mapPixmap.copy(0,(358-225)/2,cropRect.width(),cropRect.height());
#else
// 크롭
QPixmap cropped = mapPixmap.copy(cropRect);
#endif
// 지도 그림
painter.drawPixmap(targetRect.left(),targetRect.top(),targetRect.width(),targetRect.height(),cropped);
#endif // #if !(DO_NOT_USE_MAP)
// 저장 (엑셀에서 불러오기 위해)
QString tempImagePath = QDir::cleanPath(RMApp::appPath(RMApp::CAPTURE) + "/_report" + date.toString("yyyy_MMdd_HHmmss") + ".jpg");
QFile imageFile(tempImagePath);
if(imageFile.exists())
{
QFile::remove(tempImagePath);
}
imageFile.open(QIODevice::WriteOnly);
originalPixmap.save(&imageFile, "JPG");
imageFile.close();
// 엑셀파일 이미지 영역
#if (RM_MODEL == RM_MODEL_TYPE_XLDR_88)
QRect imageRect(33,633,507,507*MAIN_WINDOW_HEIGHT/MAIN_WINDOW_WIDTH);
#else
QRect imageRect(781,409,480,480*MAIN_WINDOW_HEIGHT/MAIN_WINDOW_WIDTH);
#endif
QRect destRect = QRectAspectFitRect(originalPixmap.size(),imageRect);
_shapes = _sheet->querySubObject("Shapes");
_picture = _shapes->querySubObject("AddPicture( QString&, bool, bool, double, double, double, double)",QDir::toNativeSeparators(tempImagePath),true,true,destRect.left(),destRect.top(),destRect.width(),destRect.height());
_workbook->dynamicCall("SaveAs (const QString&)", QDir::toNativeSeparators(path));
_workbook->dynamicCall("Close()");
clearAll();
dynamicCall("Quit()");
QFile::remove(tempReportPath);
QFile::remove(tempImagePath);
return true;
}

View File

@@ -0,0 +1,30 @@
#ifndef RM_EXCEL_REPORT_H
#define RM_EXCEL_REPORT_H
#include <QAxObject>
class RMExcelReport : public QAxObject
{
Q_OBJECT
public:
explicit RMExcelReport(QObject *parent = nullptr);
#if (USE_JP_ADDRESS)
bool prepare(QString& path,QDateTime* pDateTime, double lon, double lat);
#else
bool prepare(QString& path,QDateTime* pDateTime);
#endif
private:
void clearAll();
QAxObject* _workbooks;
QAxObject* _workbook;
QAxObject* _sheets;
QAxObject* _sheet;
QAxObject* _shapes;
QAxObject* _picture;
signals:
public slots:
};
#endif // RM_EXCEL_REPORT_H

View File

@@ -0,0 +1,79 @@
#include "rm_key_event.h"
#include "ui/fm_button.h"
#if (USE_RM_KEYBOARD_EVENT)
RMKeyEvent::RMKeyEvent(QObject *parent) : QObject(parent)
{
_lastKey = -1;
_enabled = true;
}
void RMKeyEvent::pressed(int key)
{
// KEY PRESS + MOUSE PRESS 동시 처리할 경우
if(_enabled && exist(key) && _lastKey != key)
{
// 이전 pressed 된 이후 released 안된 버튼 존재할 경우
if(_lastKey != -1)
{
released(_lastKey);
}
_lastKey = key;
switch (key) {
case Qt::Key_Right:
// qInfo() << "F1 start";
emit pressedF1();
break;
case Qt::Key_Left:
//qInfo() << "B1 start";
emit pressedB1();
break;
case Qt::Key_F:
//qInfo() << "FF start";
emit pressedFF();
break;
case Qt::Key_D:
//qInfo() << "FF start";
emit pressedBF();
break;
default:
break;
}
}
}
void RMKeyEvent::released(int key)
{
FMButton* btn = qobject_cast<FMButton*>(sender());
if(btn != NULL) {
btn->blockFor(100);
}
if(_enabled && exist(key) && key == _lastKey)
{
_lastKey = -1;
switch (key) {
case Qt::Key_Right:
// qInfo() << "F1 end";
emit releasedF1();
break;
case Qt::Key_Left:
//qInfo() << "B1 end";
emit releasedB1();
break;
case Qt::Key_F:
//qInfo() << "FF end";
emit releasedFF();
break;
case Qt::Key_D:
//qInfo() << "FF end";
emit releasedBF();
break;
default:
break;
}
}
}
#endif

View File

@@ -0,0 +1,74 @@
#ifndef RM_KEY_EVENT_H
#define RM_KEY_EVENT_H
#include "../rm_include.h"
#if (USE_RM_KEYBOARD_EVENT)
#include <QObject>
#include <QKeyEvent>
class RMKeyEvent : public QObject
{
Q_OBJECT
public:
explicit RMKeyEvent(QObject *parent = nullptr);
static RMKeyEvent* instance()
{
static RMKeyEvent * _instance = 0;
if ( _instance == 0 ) {
_instance = new RMKeyEvent();
}
return _instance;
}
void pressed(int key);
void released(int key);
inline static bool exist(int key)
{
return (key == Qt::Key_Left || key == Qt::Key_Right || key == Qt::Key_F || key == Qt::Key_D);
}
inline bool exist()
{
return (_lastKey != -1);
}
void setEnabled(bool enabled)
{
_enabled = enabled;
_lastKey = -1;
}
private:
int _lastKey;
bool _enabled;
signals:
void pressedF1();
void releasedF1();
void pressedB1();
void releasedB1();
void pressedFF();
void releasedFF();
void pressedBF();
void releasedBF();
public slots:
void onPressedF1()
{
pressed(Qt::Key_Right);
}
void onReleasedF1()
{
released(Qt::Key_Right);
}
void onPressedB1()
{
pressed(Qt::Key_Left);
}
void onReleasedB1()
{
released(Qt::Key_Left);
}
};
#endif // #if (USE_RM_KEYBOARD_EVENT)
#endif // RM_KEY_EVENT_H

View File

@@ -0,0 +1,136 @@
#include "rm_language.h"
#if(LIVE_LANGUAGE_CHANGE)
#include <QDebug>
#include <QPushButton>
#include <QLabel>
#include "../rm_settings.h"
#include "../rm_include.h"
#include "fm_strings.h"
#include <QLocale>
#if (INITIAL_LANGUAGE_AUTO)
bool RMLanguage::isAuto = true;
#else
bool RMLanguage::isAuto = false;
#endif
RMLanguage::RMLanguage(QObject *parent) : QObject(parent)
{
#if (FIXED_ENGLISH)
RMLanguage::isAuto = false;
_currentLanguage = LANGUAGE_EN;
#else
RMLanguage::isAuto = RMSettings::instance()->autoLanguage();
_currentLanguage = (LANGUAGE_TYPE)RMSettings::instance()->language();
#endif
_list = new QHash<qint64,QHash<int,QString>*>();
}
RMLanguage::LANGUAGE_TYPE RMLanguage::systemLanguage()
{
QString lan = QLocale::system().name();
#if (LIVE_LANGUAGE2)
if(lan.contains("JP",Qt::CaseInsensitive))
{
return RMLanguage::LANGUAGE_JP;
}
else if (lan.contains("KR",Qt::CaseInsensitive)) {
return RMLanguage::LANGUAGE_KR;
}
return RMLanguage::LANGUAGE_EN;
#else // #if (LIVE_LANGUAGE2)
#if (LANGUAGE_REMOVE_ENG) // WATEX
return RMLanguage::LANGUAGE_JP;
#else
if(lan.contains("JP",Qt::CaseInsensitive))
{
return RMLanguage::LANGUAGE_JP;
}
return RMLanguage::LANGUAGE_EN;
#endif
#endif // #if (LIVE_LANGUAGE2)
}
#if (LIVE_LANGUAGE2)
void RMLanguage::append(const char* key,RMLanguage::TEXT_TYPE type,QObject* control)
{
qint64 object_key = (qint64)control;
//qInfo() << control << "key:" << key;
QHash<int,QString>* info = _list->value(object_key);
if(info == NULL)
{
info = new QHash<int,QString>();
_list->insert(object_key,info);
}
info->insert((LANGUAGE_JP | type),FMS::txt(key,LANGUAGE_JP));
info->insert((LANGUAGE_EN | type),FMS::txt(key,LANGUAGE_EN));
info->insert((LANGUAGE_KR | type),FMS::txt(key,LANGUAGE_KR));
}
#else // LIVE_LANGUAGE2
void RMLanguage::append(RMLanguage::LANGUAGE_TYPE language,RMLanguage::TEXT_TYPE type, QObject* control,QString str)
{
//QMutableHashIterator itr = _list->iterator;
//_controls->append(control);
qint64 key = (qint64)control;
//qInfo() << control << "key:" << key;
QHash<int,QString>* info = _list->value(key);
if(info == NULL)
{
info = new QHash<int,QString>();
_list->insert(key,info);
}
info->insert((language | type),str);
//info->insert((LANGUAGE_EN | type),"TEST");
//qInfo()<< "insert:" << str;
}
#endif // LIVE_LANGUAGE2
void RMLanguage::setLanguage(RMLanguage::LANGUAGE_TYPE language)
{
#if (FIXED_ENGLISH)
Q_UNUSED(language);
#else
_currentLanguage = language;
refresh();
RMSettings::instance()->setLanguage(_currentLanguage);
emit languageChange(_currentLanguage);
#endif
}
void RMLanguage::remove(QObject* control)
{
qint64 key = (qint64)control;
_list->remove(key);
}
void RMLanguage::refresh()
{
RMLanguage::LANGUAGE_TYPE language = _currentLanguage;
foreach (qint64 key, _list->keys()) {
QHash<int,QString>* info = _list->value(key);
// qInfo() << control;
QObject* control = (QObject*)key;
if(control->inherits("QPushButton"))
{
QString str = info->value(language | TOOLTIP_TEXT);
QPushButton* btn = (QPushButton*)control;
if(str.length() > 0)
{
btn->setToolTip(str);
}
str = info->value(language | TITLE_TEXT);
if(str.length() > 0)
{
btn->setText(str);
}
}
else if (control->inherits("QLabel"))
{
QLabel* lb = (QLabel*)control;
QString str = info->value(language | TITLE_TEXT);
//qInfo() << info->values();
lb->setText(str);
}
}
}
#endif

View File

@@ -0,0 +1,134 @@
#ifndef RM_LANGUAGE_H
#define RM_LANGUAGE_H
#if(LIVE_LANGUAGE_CHANGE)
#include <QObject>
#include <QList>
#include <QHash>
// 등록(append)하여 자동으로 변경되는 타입(Button,Label)과
// signal/slot 방식으로 처리 (TableWidget Header.. etc)
class RMLanguage : public QObject
{
Q_OBJECT
public:
typedef enum
{
LANGUAGE_JP = 1 << 0, // 1
LANGUAGE_EN = 1 << 1, // 2
#if (RC_LANGUAGE == 0x0412)
LANGUAGE_KR = 1 << 2, // 4
#endif
} LANGUAGE_TYPE;
typedef enum
{
TITLE_TEXT = 1 << 8,
TOOLTIP_TEXT = 1 << 9,
} TEXT_TYPE;
explicit RMLanguage(QObject *parent = NULL);
static RMLanguage* instance()
{
static RMLanguage * _instance = NULL;
if ( _instance == 0 ) {
_instance = new RMLanguage();
}
return _instance;
}
static RMLanguage::LANGUAGE_TYPE systemLanguage();
static bool isAuto;
#if !(REMOVE_OLD_C)
static bool isJP() // 제거예정
{
#if (FIXED_ENGLISH)
return false;
#else
return (RMLanguage::instance()->language() == RMLanguage::LANGUAGE_JP);
#endif
}
#endif // #if !(REMOVE_OLD_C)
static QString languageTag()
{
#if (LANGUAGE_REMOVE_ENG)
return "";
#else // LANGUAGE_REMOVE_ENG
#if (FIXED_ENGLISH)
return "_en";
#else // FIXED_ENGLISH
#if (SUB_MODEL_BV5000 == 1 || RM_MODEL == 16 || SUPPORT_MULTI_LANGUAGE)
switch(RMLanguage::instance()->language()) {
case RMLanguage::LANGUAGE_EN:
return "_en";
case RMLanguage::LANGUAGE_JP:
return "_jp";
case RMLanguage::LANGUAGE_KR:
return "";
}
#else
return (RMLanguage::instance()->language() == RMLanguage::LANGUAGE_EN) ? "_en" : "";
#endif //SUB_MODEL_BV5000
#endif // FIXED_ENGLISH
#endif // LANGUAGE_REMOVE_ENG
return "";
}
#if (LIVE_LANGUAGE2)
void append(const char* key,RMLanguage::TEXT_TYPE type,QObject* control);
#else // LIVE_LANGUAGE2
void append(RMLanguage::LANGUAGE_TYPE language,RMLanguage::TEXT_TYPE type, QObject* control,QString str);
void appendENGToolTip(QObject* control,QString str)
{
#if (LANGUAGE_REMOVE_ENG)
Q_UNUSED(control)
Q_UNUSED(str)
#else
append(RMLanguage::LANGUAGE_EN,RMLanguage::TOOLTIP_TEXT,control,str);
#endif
}
#endif // LIVE_LANGUAGE2
void remove(QObject* control);
void setLanguage(RMLanguage::LANGUAGE_TYPE language);
RMLanguage::LANGUAGE_TYPE language()
{
#if (FIXED_ENGLISH)
return RMLanguage::LANGUAGE_EN;
#else
return _currentLanguage;
#endif
}
// 0:자동, 1:일본어, 2:영어, 3:한국어
int languageIndex() {
if(RMLanguage::isAuto) {
return 0;
}
switch (_currentLanguage) {
case LANGUAGE_JP:
return 2;
case LANGUAGE_EN:
return 3;
#if (RC_LANGUAGE == 0x0412)
case LANGUAGE_KR:
return 1;
#endif
}
return 0;
}
void refresh();
private:
RMLanguage::LANGUAGE_TYPE _currentLanguage;
QHash<qint64,QHash<int,QString>*>* _list;
signals:
void languageChange(RMLanguage::LANGUAGE_TYPE language);
};
#endif // LIVE_LANGUAGE_CHANGE
#endif // RM_LANGUAGE_H

View File

@@ -0,0 +1,64 @@
#include "rm_math.h"
#include <qmath.h>
float QAspectScaleFit(QSize sourceSize, QRect destRect)
{
QSize destSize = destRect.size();
float scaleW = (float)destSize.width() / (float)sourceSize.width();
float scaleH = (float)destSize.height() / (float)sourceSize.height();
return qMin(scaleW, scaleH);
}
QRect QRectFRound(QRectF r)
{
double xmin = floor(r.left());
double ymin = floor(r.top());
double width = ceil(r.width() + (r.left()-xmin));
double height = ceil(r.height() + (r.top()-ymin));
return QRect(xmin,ymin,width,height);
}
// Fit
QRect QRectAspectFitRect(QSize sourceSize, QRect destRect)
{
QSize destSize = destRect.size();
float destScale = QAspectScaleFit(sourceSize, destRect);
float newWidth = (float)sourceSize.width() * destScale;
float newHeight = (float)sourceSize.height() * destScale;
float dWidth = (((float)destSize.width() - newWidth) / 2.0f);
float dHeight = (((float)destSize.height() - newHeight) / 2.0f);
QRect rect = QRect(dWidth + destRect.left(), dHeight + destRect.top(), newWidth, newHeight);
return rect;
}
QRect QRectCenter(QSize sourceSize, QRect destRect)
{
int xo = (destRect.width() - sourceSize.width()) / 2;
int yo = (destRect.height() - sourceSize.height()) / 2;
return QRect(destRect.left()+xo,destRect.top()+yo,sourceSize.width(),sourceSize.height());
}
QRect QRectAspectFillRect(QSize sourceSize, QRect destRect)
{
QSize destSize = destRect.size();
float destScale = QAspectScaleFill(sourceSize, destRect);
float newWidth = (float)sourceSize.width() * destScale;
float newHeight = (float)sourceSize.height() * destScale;
float dWidth = (((float)destSize.width() - newWidth) / 2.0f);
float dHeight = (((float)destSize.height() - newHeight) / 2.0f);
// l,t,r,b
QRect rect = QRect(dWidth + destRect.left(), dHeight + destRect.top(), newWidth, newHeight);
return rect;
}
float QAspectScaleFill(QSize sourceSize, QRect destRect)
{
QSize destSize = destRect.size();
float scaleW = (float)destSize.width() / (float)sourceSize.width();
float scaleH = (float)destSize.height() / (float)sourceSize.height();
return qMax(scaleW, scaleH);
}

View File

@@ -0,0 +1,13 @@
#ifndef RM_MATH_H
#define RM_MATH_H
#include <QRect>
#include <QRectF>
QRect QRectAspectFitRect(QSize sourceSize, QRect destRect);
float QAspectScaleFit(QSize sourceSize, QRect destRect);
QRect QRectAspectFillRect(QSize sourceSize, QRect destRect);
float QAspectScaleFill(QSize sourceSize, QRect destRect);
QRect QRectFRound(QRectF r);
QRect QRectCenter(QSize sourceSize, QRect destRect);
#endif // RM_MATH_H

View File

@@ -0,0 +1,161 @@
#include "rm_play_process.h"
#include "rm_player.h"
#include "../ui/rm_widget_video_list.h"
#include "../data/rm_video_item_loader.h"
#include <QThreadPool>
#include <QTimer>
bool RMPlayProcess::bConnected = false;
bool RMPlayProcess::bProcessing = false;
#if (PLAYER_ONLY_LIBRARY_MODE)
void RMPlayProcess::connectEvents()
{
if(RMPlayProcess::bConnected == false) {
RMPlayProcess::bConnected = true;
// 리스트에서 파일 플레이 요청되면 로딩 시작
//connect(listWidget,SIGNAL(listSelected(RMVideoItem*)),this,SLOT(onVideoLoadingStart(RMVideoItem*)));
RMPlayer* player = RMPlayer::instance();
// 플레이 중 버튼 상태 업데이트
connect(player,SIGNAL(playEvent(PLAY_EVENT, RMVideoItem*)),SLOT(onPlayEvent(PLAY_EVENT, RMVideoItem*)));
}
}
#else // PLAYER_ONLY_LIBRARY_MODE
void RMPlayProcess::connectEvents(RMWidgetVideoList* listWidget)
{
_loadingItem = NULL;
if(RMPlayProcess::bConnected == false) {
RMPlayProcess::bConnected = true;
// 리스트에서 파일 플레이 요청되면 로딩 시작
connect(listWidget,SIGNAL(listSelected(RMVideoItem*)),this,SLOT(onVideoLoadingStart(RMVideoItem*)));
RMPlayer* player = RMPlayer::instance();
// 비디오 로딩 종료시
//connect(player,SIGNAL(mediaLoadEnd(RMVideoItem*)),SLOT(onVideoLoadingEnd(RMVideoItem*)));
// 플레이 중 버튼 상태 업데이트
connect(player,SIGNAL(playEvent(PLAY_EVENT, RMVideoItem*)),SLOT(onPlayEvent(PLAY_EVENT, RMVideoItem*)));
}
}
#endif // #else // PLAYER_ONLY_LIBRARY_MODE
void RMPlayProcess::onVideoLoadingStart(RMVideoItem* item)
{
//qInfo() << item << __FUNCTION__;
RMPlayProcess::bProcessing = true;
// QApplication::setOverrideCursor(Qt::WaitCursor);
// 기존 item 및 설정 초기화
RMPlayer* player = RMPlayer::instance();
#if !(PLAY_CONTINUE_EVENT)
connect(player,SIGNAL(playerClearedDone()),SLOT(onPlayerClearDone()));
#endif
// cpu limit https://technet.microsoft.com/en-us/library/ff384148(v=ws.10).aspx
// sid wmic useraccount get name,sid
_loadingItem = item; // 먼저 설정하고..
player->stop(); // 플레이어 종료
//#if (USE_LIBRARY_MODE)
// if(RMPlayer::libraryInstance != NULL) {
// RMPlayer::libraryInstance->stop();
// }
//#endif // USE_LIBRARY_MODE
}
#if !(PLAY_CONTINUE_EVENT)
void RMPlayProcess::onPlayerClearDone()
{
RMPlayer* player = RMPlayer::instance();
disconnect(player,SIGNAL(playerClearedDone()),this,SLOT(onPlayerClearDone()));
if (_loadingItem != NULL)
{
#if (!PRE_LOAD_SENSOR_DATA)
// 센서 정보 로딩
connect(_loadingItem,SIGNAL(loadAVIInfoEnd()),SLOT(onInfoLoadingEnd()),Qt::UniqueConnection); // -> 로딩완료시
// qInfo() << "2. start sensor data loading ----------------- ";
// Loader delete???
// GPS, 및 기타 센서 정보 가져오기
// QRunnable 은 자동으로 delete 됨
RMVideoItemLoader* loader = new RMVideoItemLoader(_loadingItem);
QThreadPool::globalInstance()->start(loader,LOADER_THREAD_PRIORITY);
_loadingItem = NULL;
#else
RMPlayer::instance()->onLoad(_loadingItem);
#endif
}
}
#endif
#if (!PRE_LOAD_SENSOR_DATA)
void RMPlayProcess::onInfoLoadingEnd() // 현재 사용되지 않는다.????
{
RMVideoItem* item = qobject_cast <RMVideoItem*>(QObject::sender());
// 연결해제
disconnect(item,SIGNAL(loadAVIInfoEnd()),this,SLOT(onInfoLoadingEnd()));
//#if (USE_LIBRARY_MODE)
// if(RMApp::isTB5000 && RMPlayer::libraryInstance != NULL) {
// RMPlayer::libraryInstance->onLoad(item);
// return;
// }
//#endif // USE_LIBRARY_MODE
// 동영상 로딩시작 및 플레이
RMPlayer::instance()->onLoad(item);
}
#endif
void RMPlayProcess::onPlayEvent(PLAY_EVENT event, RMVideoItem* item)
{
Q_UNUSED(item)
if(event == PLAY_DID_LOADED)
{
//QApplication::restoreOverrideCursor();
RMPlayProcess::bProcessing = false;
if(item != NULL)
{
// _plotWidget->onUpdateGraph(item);
// no sensor 의 경우 update map 처리하면 onUpdateMap 처리됨
// emit _mapWidget->updateMap(item);
}
}
#if (PLAY_CONTINUE_EVENT)
else if (event == PLAY_DID_CLEARED && _loadingItem != NULL)
{
// DELAY 처리해도 멈추는 증상 발생함.
// RMPlayer::instance()->onLoad(_loadingItem);
// qInfo() << "START LOAD NEXT FILE" << __FUNCTION__ << __LINE__;
// _loadingItem = NULL;
//#if (SUPPORT_LIBRARY_MODE)
// onStartNext();
// const int mWait = 100;
//#else // SUPPORT_LIBRARY_MODE
const int mWait = 1;
//#endif // SUPPORT_LIBRARY_MODE
QTimer::singleShot(mWait,Qt::PreciseTimer,this,SLOT(onStartNext()));
}
#endif
}
void RMPlayProcess::onStartNext()
{
//#if !(PLAYER_ONLY_LIBRARY_MODE)
// FIXED_SLEEP; //qInfo() << "START LOAD NEXT FILE" << __FUNCTION__ << __LINE__;
//#endif//
//qInfo() << "::onStartNext" << _loadingItem->title();
RMPlayer::instance()->onLoad(_loadingItem);
_loadingItem = NULL;
}

View File

@@ -0,0 +1,60 @@
#ifndef RM_PLAY_PROCESS_H
#define RM_PLAY_PROCESS_H
#include "../rm_include.h"
#include "rm_player_base.h"
#include <QObject>
class RMVideoItem;
class RMWidgetVideoList;
class RMPlayProcess : public QObject
{
Q_OBJECT
public:
static RMPlayProcess* instance()
{
static RMPlayProcess * _instance = 0;
if ( _instance == 0 ) {
_instance = new RMPlayProcess();
}
return _instance;
}
// STOP (PLAY_DID_CLEARED) 이벤트 발생시 다음 파일 로딩 중인지 확인해서
// 화면 깜빡거리지 않도록 처리
bool isLoadingNextFile() {
return (_loadingItem != NULL);
}
// 모든 윈도우 및 instance 생성 후 연결해야함
#if (PLAYER_ONLY_LIBRARY_MODE)
void connectEvents();
#else // PLAYER_ONLY_LIBRARY_MODE
void connectEvents(RMWidgetVideoList* listWidget);
#endif // PLAYER_ONLY_LIBRARY_MODE
// 다른 프로세스 중지
static bool bProcessing;
private:
static bool bConnected;
RMVideoItem* _loadingItem;
private slots:
void onPlayEvent(PLAY_EVENT event, RMVideoItem* item);
// Play Control (from mainWindow)
#if (PLAYER_ONLY_LIBRARY_MODE)
public slots:
#endif
void onVideoLoadingStart(RMVideoItem* item); // 1. 플레이 시작
#if !(PLAY_CONTINUE_EVENT)
void onPlayerClearDone(); // 2. 기존 파일 unload 종료
#endif
#if (!PRE_LOAD_SENSOR_DATA)
void onInfoLoadingEnd(); // 3. 파일 센서 정보 읽어 오기
#endif
void onStartNext();
//void onVideoLoadingEnd(RMVideoItem* item); // 4. Player 에서 Video Loading 완료
};
#endif // RM_PLAY_PROCESS_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,383 @@
#ifndef RM_PLAYER_H
#define RM_PLAYER_H
#include <QObject>
#include <qslider.h>
#include <QElapsedTimer>
#include "rm_constants.h"
#include "rm_include.h"
#include "rm_player_base.h"
#include "rm_player_zoom.h"
#define DEBUG_RM_PLAYER 0
#define USE_RM_D2_PLAYER 0
#define CAPTURE_DEBUG 0
class RMVideoItem;
class RMFrameSlider;
#if (MODEL_BBVIEWER)
class RMEQWidget;
#endif
#define DEBUG_HIGH_SPEED_STOP 0
#define REMOVE_CLOCK_UPDATE_WHEN_SEEKING 1
#define STEP_SEEK_DURATION 34
//extern const int g_PlaySpeed1XIndex;
#if (MODEL_BBVIEWER)
#define PLAY_SPEED_COUNT 5
#elif (RM_MODEL == RM_MODEL_TYPE_TB4000 || RM_MODEL_EMT_KR)
#define PLAY_SPEED_COUNT 5
#else
#define PLAY_SPEED_COUNT 4
#endif
extern float g_PlaySpeedList[PLAY_SPEED_COUNT];
extern int DEFAULT_SPEED_INDEX;
class RMPlayer : public RMPlayerZoom
{
Q_OBJECT
public:
static RMPlayer* instance()
{
static RMPlayer * _instance = 0;
if ( _instance == NULL ) {
_instance = new RMPlayer();
}
return _instance;
}
//#if (USE_LIBRARY_MODE) // 360 DLL
// static RMPlayer* libraryInstance;
//#endif //
#if !(SINGLE_CH_VIEWER)
// 는 _isSwapped() 와는 별도로 사용자 swap 상태만 확인하기 위해 사용
bool isSwapped() { return _swaped; }
#endif
void stop() override;
QString currentTimeString(double* lat, double* lon);
#if (PENTA_CHANNEL)
void changeCHMode(RMApp::ChannelMode mode) override;
#endif
void updateSpeedForce(qreal speed)
{
_speedValue = speed;
updateSpeed(_speedValue);
}
#if !(RM_MODEL == RM_MODEL_TYPE_TB4000)
void setMuteButton(FMButton* btn) {
_muteButton = btn;
}
#endif // #if !(RM_MODEL == RM_MODEL_TYPE_TB4000)
void setSlider(QSlider* slider) {
_slider = slider;
// PRESSED -> MOVED -> RELEASED 순서대로 호출 되도록 이벤트 처리해야함
// 노브가 아닌 지점 CLICK 시에는 Slider는 (Slider)PRESSED 이벤트 만 호출됨 (RMSlider 내부에서 구현)
// PRESSED 이벤트 발생시 KNOB 가 false 일 경우 released 이벤트 발생하지 않으니 별도의 처리 하지 말아야함
connect(_slider, SIGNAL(sliderPressedWithKnob(bool)),SLOT(onSliderPress(bool))); // 이벤트 두번씩 발생함
connect(_slider, SIGNAL(sliderMoved(int)),SLOT(onSliderMove(int))); // 슬라이더 컨트롤
connect(_slider, SIGNAL(mouseReleased()),SLOT(onSliderRelease()));
}
#if !(RM_MODEL == RM_MODEL_TYPE_TB4000)
void setVolumeSlider(QSlider* slider) {
_volumeSlider = slider;
connect(_volumeSlider, SIGNAL(sliderPressed()), SLOT(onSetVolume()));
connect(_volumeSlider, SIGNAL(valueChanged(int)), SLOT(onSetVolume()));
onSetVolume();
}
#endif // RM_MODEL_TYPE_TB4000
void setSpeedSlider(QSlider* slider, QLabel* label) {
_speedLabel = label;
connect(slider, SIGNAL(mouseReleased()),SLOT(onSpeedSliderReleased()));
connect(slider, SIGNAL(sliderMoved(int)),SLOT(onSpeedSliderMoved(int)));
}
private:
// SLIDER 이동중에는 강제 PAUSE
bool _pauseWhileSeek;
QTimer* _captureTimer;
QTimer* _captureWatcher;
//#if (SEEK_BY_SLIDER)
// QTimer* _seekPositionTimer;
// void startSeekTimer();
//#endif
#if (USE_RM_KEYBOARD_EVENT)
int _seekAcceleration; // 가속
QTimer* _seekPressedTimer; // Slider 이동 타이머
QTimer* _seekReleasedTimer; // Seek Update 타이머
QTimer* _seekUpdateTimer; // Pressed 중간 Seek Update 타이머
void startSeekUpdateTimer();
void stopSeekUpdateTimer() override;
bool isSeekProcessing() override
{
return (_seekPressedTimer != NULL || _seekReleasedTimer != NULL || _seekUpdateTimer != NULL);
}
//#else
// QElapsedTimer _stepTimer;
#endif
protected:
virtual void timerEvent(QTimerEvent *e);
public:
explicit RMPlayer(QObject *parent = 0);
RMVideoItem* getCurrentItem()
{
return _currentItem;
}
QDateTime currentTime(double* lon = NULL,double* lat = NULL);
VIDEO_MODE videoMode()
{
return _videoMode;
}
FAV::AVPlayer* playerF()
{
return _playerF;
}
QWidget* playWidgetF()
{
return _videoOutputF->widget();
}
#if ((!SINGLE_CH_VIEWER && !TOGGLE_PLAYER) || DUAL_VIEWER || DUAL_VIDEO_WIDGET)
QWidget* playWidgetR()
{
return _videoOutputR->widget();
}
#endif
#if !(SINGLE_CH_VIEWER)
FAV::AVPlayer* playerR()
{
return _playerR;
}
#endif // #if !(SINGLE_CH_VIEWER)
#if (RM_MODEL_360)
// SWAP 되더라도 메인 Renderer 는 항상 _videoOutputF 임
FAV::VideoRenderer* mainRenderer() {
return _videoOutputF;
}
FAV::VideoRenderer* renderer360() {
#if (SINGLE_CH_VIEWER)
return _videoOutputF;
#else // SINGLE_CH_VIEWER
return _swaped ? _videoOutputR : _videoOutputF;
#endif // SINGLE_CH_VIEWER
}
#endif // RM_MODEL_360
//void setSliderFrame(RMFrameSlider* frame); // 슬라이더 설정
#if (MODEL_BBVIEWER)
void setEQFrame(RMEQWidget* eq);
#endif
// 비디오 캡쳐 요청
bool requestVideoCapture();
qreal speedValue()
{
return _speedValue;
}
#if (SUPPORT_WIDE_MODE)
/**
* @brief 현재 재생중인 영상이 WIDE 모드 지원 영상일 경우
* @return
*/
bool itemIsWideMode();
/**
* @brief 현재 재생중인 모드가 WIDE 모드일 경우
* @return
*/
bool isWideMode();
#endif // SUPPORT_WIDE_MODE
#if (RM_TESTING)
FAV::LibAVFilterVideo* _testFilterF;
void applyTestFilter(QString filter);
#endif
qint64 titleSeconds; // 슬라이더에 표시된 시간 (Capture 등에서 동기화 처리위해 사용)
public slots:
void requestVideoCaptureProcess();
// Slider Control
void onSliderPress(bool bKnob);
void onSliderMove(int value);
void onSliderRelease();
//void onPlotMove(qreal ratio);
// 사용자 정지
void onUserStop();
#if (USE_RM_KEYBOARD_EVENT)
void onSeekForwardStart();
void onSeekBackwardStart();
void onSeekFrameForwardStart();
#if (PLAY_SYNC_FIX2)
void onSeekFrameBackwardStart();
#endif // PLAY_SYNC_FIX2
void onSeekStepEnd();
#endif
#if (PLAYER_ONLY_LIBRARY_MODE)
void clear();
void create();
#endif // PLAYER_ONLY_LIBRARY_MODE
bool prepareForCapture();
void onCaptureSavedFront(const QString& path);
#if !(SINGLE_CH_VIEWER)
void onCaptureSavedRear(const QString& path);
void toggleSwap(bool forceEmit = false);
#endif
#if (TRI_CHANNEL || TRI_CHANNEL2)
void toggleIndoor(bool forceEmit = false); // 후방 <-> 실내
#endif
#if (SUPPORT_WIDE_MODE)
void toggleWide(); // NORMAL <-> WIDE
#endif // SUPPORT_WIDE_MODE
void onSpeedSliderMoved(int position);
void onSpeedSliderReleased();
private:
// 재생시간이 AVI:60260 VIDEO(Audio):59281 일 경우
// 현재 POSITION + step 을
// 실제 재생시간을 N 분할 하여 사용자가 보기 적합한 간격으로 표시함
// eg 988.0166667 msec 씩 이동할 경우
#if (USE_RM_KEYBOARD_EVENT)
quint64 _stepPosition();
//#else
// quint64 _stepPosition(int step=1); // +1,-1
#endif
bool _isLastStep();
// 이벤트 블럭처리함
void blockEvents(int msec,bool b_show_indicator = false);
// pause 된 상태에서도 positionChanged 가 계속호출되어 방지용
// int timeToSliderValue(qint64 time);
qint64 ratioToSliderValue(qreal ratio);
QString _captureDir;
QList<QString>* _captureFileList;
// 통합관리 + 화면갱신을 위한 seek 구분
void seek(qint64 pos,bool refresh) override;
// 정지된 상태에서 SEEK 를 통해 화면 갱신
void updatePausedScreen();
void seekFrontOnly(qint64 pos,bool refresh);
#if !(SINGLE_CH_VIEWER)
void seekRearOnly(qint64 pos,bool refresh);
#endif
// 후방만 갱신
#if (!(SINGLE_CH_VIEWER || TOGGLE_PLAYER) || PENTA_CHANNEL)
void updatePausedScreenRearOnly();
#endif
void updatePausedScreenFrontOnly();
void clearCaptureTimer();
void clearCaptureWatcher();
//void clearSliderReleaseTimer();
// Rear 는 V+H Flip 모두 지원
void updateRearFlip();
#if (RM_MODEL == RM_MODEL_TYPE_TELEBIT)
void updateFrontFlip();
#endif
// 메인 clock 과 각 player 의 clock 동기화
void clear_seek_timer();
void sync_clock();
private slots:
#if (USE_RM_KEYBOARD_EVENT)
void onSeekUpdate(); // 업데이트 후 타이머 제거하지 않음
void onSeekLastUpdate(); // 업데이트 후 타이머 제거
void onSeekPressed();
#endif
#if (CAPTURE_DEBUG)
void onSeekCapture();
void onImageCaptured(const QImage& image);
void onCaptureFailed();
#endif
//#if !(FORCE_SINGLE_PLAYER)
// 이벤트 블럭 해제 (전후방 swap 중에 별도 이벤트 받지 못하도록 처리)
void onReleaseBlockEvent();
//#endif
// 캡쳐 대기 완료
void onReadyForCapture();
// 캡쳐 실패
void onFailToCapture();
#if (PENTA_CHANNEL)
void onUpdateCHMode() override;
#endif // PENTA_CHANNEL
// 동기화 시작 타이머
void on_sync_timer_restarted();
// 처음으로 이동
void onPlayRestart();
//----------------------------------------------------------------------------------------------
signals:
void videoCaptureDone(QList<QString>* list);
void swapChanged(bool swap);
#if (H265_SUPPORT)
void show_indicator(bool show);
#endif
void cancelFullScreen();
void userStop();
};
#endif // RM_PLAYER_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,481 @@
#ifndef RM_PLAY_CONTROL_H
#define RM_PLAY_CONTROL_H
#include <QObject>
#include <QTimerEvent>
#include <QSlider>
#include <QWaitCondition>
#include "../fav/fav.h"
#include "../rm_include.h"
#include "../fm_event_types.h"
// 단말기 별로 설정 가능
typedef enum
{
VIDEO_MODE_UNDEFINED = 0,
VIDEO_MODE_OPENGL_ES = 1,
VIDEO_MODE_WINDOW = 2,
} VIDEO_MODE;
#if (USE_RM_KEYBOARD_EVENT)
typedef enum
{
STEP_MODE_NONE = 0, // 일반 재생
STEP_MODE_F1 = 1, // 1초 전방 이동
STEP_MODE_B1 = 2, // 2초 후방 이동
STEP_MODE_FF = 3, // 1 Frame 전방 이동
#if (PLAY_SYNC_FIX2)
STEP_MODE_BF = 4, // 1 Frame 후방 이동
#endif
} STEP_MODE;
#endif
// Video Seek 방식 (KeyFrameSeek 처리하면 안됨)
#if (RM_MODEL == RM_MODEL_TYPE_KEIYO1 || RM_MODEL == RM_MODEL_TYPE_MBJ5010 || RM_MODEL == RM_MODEL_TYPE_FC_DR232W)
#define VIDEO_SEEK_TYPE FAV::KeyFrameSeek //AccurateSeek
#else
#define VIDEO_SEEK_TYPE FAV::AccurateSeek // FAV::AccurateSeek, KeyFrameSeek
#endif
#define INITIAL_VIDEO_POSITION 1 // msec
#define PLAYER_MUTE_INTERVAL 10 // //const int kMuteInterval = 10;//100;
#if (RM_MODEL == RM_MODEL_TYPE_KEIYO1 || RM_MODEL == RM_MODEL_TYPE_MBJ5010 || RM_MODEL == RM_MODEL_TYPE_FC_DR232W)
#define PLAYER_SYNC_INTERVAL 300 //const int kSyncInterval = 500;
#else
#define PLAYER_SYNC_INTERVAL 500 //const int kSyncInterval = 500;
#endif
#define PLAYER_VOLUME_INTERVAL 0.1 // const qreal kVolumeInterval = 0.1;
#define PLAYER_END_LIMIT 0 // 영상 마지막에 깨진 패킷 등이 있거나, 실제 영상 길이가 짧은 경우가 있어
// 종료 시간은 duration 보다 n 초 빠르게 처리함
#define PLAYER_START_LIMIT 50 // 실제 0 POS 은 이동하기 어렵고 33 정도
class FMButton;
class RMVideoItem;
class QOpenGLContext;
#if (USE_DRAG_ZOOM)
class FMDragZoomWidget;
#endif // #if (USE_DRAG_ZOOM)
class RMPlayerBase : public QObject
{
friend class RMFrameVideoBase;
Q_OBJECT
public:
explicit RMPlayerBase(QObject *parent = 0);
#if (PLAY_CONTINUE_EVENT)
virtual void stop(); // 종료 bUserStop == 사용자가 강제 종료
#else
void stop();
#endif
// 전/후방 스텝모드 모두 종료
virtual bool isSeekProcessing()
{
return false;
}
void endStepMode() {
_stepMode = STEP_MODE_NONE;
}
FAV::VideoRenderer* _rendererFBackup;
FAV::VideoRenderer* _rendererRBackup;
// 화면에 업데이트 하지 않도록 처리
void pauseRenderer()
{
_rendererFBackup = _playerF->renderer();
_playerF->setRenderer(NULL);
#if !(SINGLE_CH_VIEWER)
_rendererRBackup = _playerR->renderer();
_playerR->setRenderer(NULL);
#endif
#if (TRI_CHANNEL)
_rendererRBackup = _playerI->renderer();
_playerI->setRenderer(NULL);
#endif
}
#if (PENTA_CHANNEL)
//! \brief 채널 모드 변경
//! \param mode : 변경할 모드
virtual void changeCHMode(RMApp::ChannelMode mode){Q_UNUSED(mode)}
#endif // PENTA_CHANNEL
#if (TOGGLE_PLAYER)
bool _tempSwapInResetPlayer; // PLAYER 초기화 중 임시로 SWAP 복구 처리되고 있는 경우
#endif
// 전체화면 전환시 비율 조정
// void updateMainPlayerAspect(FAV::VideoRenderer::OutAspectRatioMode mode)
// {
// // TOGGLE 처리할 수 있어 전부 변경
// _videoOutputF->setOutAspectRatioMode(mode);
// _videoOutputR->setOutAspectRatioMode(mode);
// }
public:
QString lastError;
FAV::VideoRenderer* _videoOutputF;
#if ((!SINGLE_CH_VIEWER && !TOGGLE_PLAYER) || DUAL_VIEWER || PENTA_CHANNEL)
FAV::VideoRenderer* _videoOutputR;
#endif
#if (KEEP_ROI_ON_CAPTURE)
int _roiX, _roiY, _roiW, _roiH; ///< 화면 CROP 영역 pixel
int _roiX2, _roiY2, _roiW2, _roiH2; ///< 후방 화면 CROP 영역 pixel
#endif // KEEP_ROI_ON_CAPTURE
#if (USE_DRAG_ZOOM)
bool isROI()
{
return (_hROIFilter != NULL);
}
bool isROIRear()
{
return (_hROIFilterRear != NULL);
}
#endif // #if (USE_DRAG_ZOOM)
QSize frameSize();
#if ((!SINGLE_CH_VIEWER && !TOGGLE_PLAYER) || DUAL_VIEWER || PENTA_CHANNEL)
/**
* @brief 2CH 프레임 크기
* @return 없을 경우 0,0
*/
QSize rearFrameSize();
#endif // SINGLE_CH_VIEWER
#if (SUPPORT_LIBRARY_MODE)
/**
* @brief 360(1:1) 영상 존재여부
* @return 1,2CH 모두 확인하여 1:1 영상 존재할 경우 true
*/
bool isContain360Frame();
#endif
#if (TOPDOWN_360_QUAD_MODE)
FAV::VideoRenderer* _videoOutput2; // 우상단
FAV::VideoRenderer* _videoOutput3; // 좌하단
FAV::VideoRenderer* _videoOutput4; // 우하단
void setQuadMode(bool quad);
#endif // TOPDOWN_360_QUAD_MODE
#if (PAUSED_FRAME_REFRESH)
// 정지 상태에서 프레임 필터 업데이트
// 필터, MODE: 0=생성,1=해제,2=UPDATE
void applyPausedFilter(FAV::VideoRenderer* out,FAV::LibAVFilterVideo* filter,int mode);
#else // PAUSED_FRAME_UPDATE
#if (USE_DRAG_ZOOM)
void updatePausedScreen();
#endif // USE_DRAG_ZOOM
#endif // PAUSED_FRAME_UPDATE
#if (USE_DRAG_ZOOM)
FMDragZoomWidget* _roiWidget;
FMDragZoomWidget* _roiWidgetRear;
FAV::LibAVFilterVideo* _hROIFilter;
FAV::LibAVFilterVideo* _hROIFilterRear;
void resetROIRear();
void resetROI();
void setROIWidget(FMDragZoomWidget* widget);
void setROIWidgetRear(FMDragZoomWidget* widget);
void EnableROIWidget(bool enabled);
#endif // #if (USE_DRAG_ZOOM)
protected:
FAV::VideoRendererId _renderID; // 비디오 랜더링 ID
FAV::AVPlayer* _playerF; // 전방 (MULTI PLAYER 에서는 포인터만 할당)
#if !(SINGLE_CH_VIEWER)
FAV::AVPlayer* _playerR; // 후방 (MULTI PLAYER 에서는 포인터만 할당)
#endif
#if (TRI_CHANNEL)
FAV::AVPlayer* _playerI; // 실내
#endif
#if (PENTA_CHANNEL)
FAV::AVPlayer* _players[5]; // 전후방 포함 (FRONT,REAR,LEFT,RIGHT,SUB)
//! \brief 이벤트 제거
void _clearPlayerConnection();
//! \brief 이벤트 연결, 정리
void _updatePlayerConnection();
#endif // PENTA_CHANNEL
#if !(DO_NOT_USE_ZOOM)
FAV::VideoRenderer* _videoOutputZOOMMain;
#if !((SINGLE_CH_VIEWER || TOGGLE_PLAYER) && (!DUAL_VIEWER)) // DUAL 은 사용함
FAV::VideoRenderer* _videoOutputZOOMSub;
#endif
#endif
// 메인 클럭 (전/후방 동기화를 관리)
int _timer_id;
bool _sync_time;
FAV::AVClock* _clock;
QTimer* _sync_restart_timer;
qint64 _lastPosition;
#if (TOGGLE_PLAYER)
void resetPlayer(); // 전후방 변경되어 있을 경우 복구
void restorePlayer();
#endif
#if (START_FROM_FIRST_FRAME)
qreal _startPTS;
#endif
// Player
VIDEO_MODE _videoMode;
#if (TRI_CHANNEL || TRI_CHANNEL2)
bool _ch3mode; // 후방화면에 실내 영상 처리
#endif
// SEEK
// step 처리중 (다시 플레이시)
#if (PLAY_SEEK_FILE_MOVE)
#if (USE_RM_KEYBOARD_EVENT)
STEP_MODE _stepMode;
bool _firstStep; // 파일 오픈 후 처음 step
#endif
#endif
// 슬라이드 이동중에는 강제 PAUSE 하는 것이 좋겠는데...
//bool _sliderMoving; // 슬라이드 이동중에는 다음파일로 이동하지 않도록
bool _stepping; // ?? 사용하지 않는것 같은데 제거
bool _refreshSeeking; // 화면갱신을 위해 SEEK
qint64 _pausePosition; // 처리하지 않으면 전방으로 이동함
// CONTROL + EVENT
QSlider* _slider; // "
#if !(RM_MODEL == RM_MODEL_TYPE_TB4000)
FMButton* _muteButton; // 참조용
QSlider* _volumeSlider; // "
#endif // #if !(RM_MODEL == RM_MODEL_TYPE_TB4000)
bool _forceStop; // control -> 사용자 중지
bool _pausedState; // 현재 플레이어는 정지된 상태 (임시 저장)
// media change 또는 position 종료시 naturla end 2회 발생할 수 있음
bool _onNaturalEnd;
bool _sliderMoving; // 슬라이더로 포지션 변경 (중)
qreal _speedValue;
QLabel* _speedLabel;
// 플레이 중 버퍼 일시 중지
bool _muteIfRequired;
// Video Data
RMVideoItem* _currentItem;
// 후방 파일 존재 (전방 파일 처리후 사용해야함)
// 전방에 후방 파일을 설정했을 경우 (1개 파일만 존재하는 경우) -> 로딩하지 않는다.
QString _realRearFile();
// 이벤트 처리하지 않는다
QTimer* _blockTimer;
bool _eventBlocking;
#if !(SINGLE_CH_VIEWER)
FAV::AVPlayer* mainScreenPlayer() {
return _isSwaped() ? _playerR : _playerF;
}
FAV::AVPlayer* subScreenPlayer() {
#if (TRI_CHANNEL)
if(_isSwaped()) {
return _playerF;
}
return _ch3mode ? _playerI : _playerR;
#else // TRI_CHANNEL
return _isSwaped() ? _playerF : _playerR;
#endif // TRI_CHANNEL
}
FAV::VideoRenderer* _currentVideoRenderer(FAV::AVPlayer* player)
{
#if (TOGGLE_PLAYER)
Q_UNUSED(player)
return _videoOutputF;
#else // TOGGLE_PLAYER
if(player == _playerF) {
return _isSwaped() ? _videoOutputR : _videoOutputF;
}
else if (player == _playerR) {
return _isSwaped() ? _videoOutputF : _videoOutputR;
}
return NULL;
#endif // TOGGLE_PLAYER
}
bool _swaped;
//bool _rear_swaped;
// 전방, 후방 파일만 존재하는 경우 _playerF 에 rear 파일을 로딩해서 처리
// SWAP 확인하려면 _swaped 및 _rear_swaped 모두 확인해야함
bool _isSwaped()
{
// 둘다 swaped 인 경우 false
// player swap 상태일 경우 _rear_swaped 인 경우는 swap 이 아님, player swap 상태가 아닌 경우 _rear_swap 상태일 경우 swap 상태임
return _swaped;// ? !_rear_swaped : _rear_swaped;
}
#endif // SINGLE_CH_VIEWER
// media change 상태에서 로딩하면 loaded 가 true 되어 있지 않음
void playOrPause(bool forcePlay);
void updateSpeed(qreal speed);
// 슬라이드 이동 등에서 mute 처리함 (timer > 0 자동 릴리즈)
void muteIfRequired(int timer);
void unMuteIfMuted();
bool _isMuted();
// 화면 CLEAR (BLACK)
virtual void clearScreen(bool front);
// 통합관리 + 화면갱신을 위한 seek 구분
virtual void seek(qint64 pos,bool refresh);
// 강제 pause / unpause
void _pause(bool bPause, bool waitUntil = false, qint64 pos = -1);
// 전후방 seek 가 모두 종료 되었을 때 동기화 타이머를 다시 시작한다
void restart_sync_timer();
public:
#if (TRI_CHANNEL || TRI_CHANNEL2)
bool isIndoor() {
return _ch3mode;
}
#endif // TRI_CHANNEL
protected:
int _playerStopCount; // 전후방 동시 종료시 모두 종료된 상황을 확인하기 위해 count 처리
void _createVideoPlayers(); // 초기 플레이어 생성
void _configureVideoRenderers(); // 파일 set 상태에 따라 renderer 설정
void _setItemToPlayers(RMVideoItem* item); // 파일 set
virtual void stopSeekUpdateTimer();
//virtual void _updateEQFilter(bool bForce = false){Q_UNUSED(bForce)}
signals:
void playEvent(PLAY_EVENT event, RMVideoItem* item);
void positionChanged(qint64 current,qint64 total); // 다른 컨트롤에 상태 변경 신호
#if !(PLAY_CONTINUE_EVENT)
void playerClearedDone(); // STOP/UNLOAD 완료시 처리
#endif
protected slots:
// 소리 정지 해제 (slide move)
void onUnMuteIfRequired();
void onSetVolume();
void onMute();
void updateSlider(qint64 value); //
void updateSlider(); //
void changeClockType();
#if (PENTA_CHANNEL)
void onMediaChangedM(FAV::MediaStatus mediaStatus);
#else // PENTA_CHANNEL
void onMediaChangedF(FAV::MediaStatus mediaStatus);
#if !(FORCE_SINGLE_PLAYER)
void onMediaChangedR(FAV::MediaStatus mediaStatus);
#endif // #if (FORCE_SINGLE_PLAYER)
#if (TRI_CHANNEL)
void onMediaChangedI(FAV::MediaStatus mediaStatus);
#endif
#endif // PENTA_CHANNEL
void onPositionChanged(qint64 value);
#if (PENTA_CHANNEL)
void onStoppedM();
void onSeekFinishedM(qint64 pos);
#else // PENTA_CHANNEL
void onSeekFinishedF(qint64 pos);
void onStoppedF();
#if !(FORCE_SINGLE_PLAYER)
void onStoppedR();
void onSeekFinishedR(qint64 pos);
#endif // FORCE_SINGLE_PLAYER
#endif // PENTA_CHANNEL
void onFirstFrame(qreal pts);
#if(PROFILE_BUILD)
void onFirstFrameR(qreal pts);
#endif //
#if (FIXED_FPS_DURATION && (!FORCE_BREAK_EOF || PREVENT_OVER_DURATION_RENDER))
void onMediaEnded();
#endif
void onRestoreRenderer();
void onTest();
// void onTest2();
// void onTest3();
public slots:
void onPlayOrPause();
#if (PENTA_CHANNEL)
virtual void onUpdateCHMode() {}
#endif // PENTA_CHANNEL
// 파일리스트 클릭시 발생
void onLoad(RMVideoItem* item);
void onStartPause();
#if (PENTA_CHANNEL)
void onPlayerM(const FAV::AVError&);
#else // PENTA_CHANNEL
void onPlayerF(const FAV::AVError&);
#if !(FORCE_SINGLE_PLAYER || SINGLE_CH_VIEWER || TOGGLE_PLAYER)
void onPlayerR(const FAV::AVError&);
#endif
#if (TRI_CHANNEL)
void onPlayerI(const FAV::AVError&);
#endif
#endif // PENTA_CHANNEL
#if (PAUSED_FRAME_REFRESH)
#if (USE_DRAG_ZOOM)
void zoomPausedScreen(bool bFront, bool clear, bool onStartStop);
#endif // #if (USE_DRAG_ZOOM)
#endif // PAUSED_CROP_FRAME
#if (USE_DRAG_ZOOM)
void onClearROI(bool bStartStop = false); // 지난 영역 제거
void onClearROIRear(bool bStartStop = false); // 지난 영역 제거
void onROISelected(double fx, double fy, double width, double height);
#if !(SINGLE_CH_VIEWER)
void onROISelectedRear(double fx, double fy, double width, double height);
#endif // #if !(SINGLE_CH_VIEWER)
#endif // #if (USE_DRAG_ZOOM)
};
#endif // RM_PLAY_CONTROL_H

View File

@@ -0,0 +1,700 @@
#include "rm_player_zoom.h"
#if (USE_EQ_FILTER)
#include "../ui/rm_slider.h"
#endif
#include "rm_utility.h"
#if (MODEL_360 || RM_MODEL_360 || ZOOM_SHADER || PAUSED_FRAME_REFRESH)
#include "fav/OpenGLVideo.h"
#include "fav/OpenGLRendererBase.h"
#endif
#if (ZOOM_SHADER)
RMPlayerZoom::RMPlayerZoom(QObject *parent) : RMPlayerBase(parent)
{
}
// ZOOM 모드 시작
void RMPlayerZoom::startZoom(bool mainVideo)
{
}
// ZOOM 종료
void RMPlayerZoom::stopZoom(bool mainVideo)
{
}
// ZOOM 위치 변경 + 프레임 크기가 확정되기 전에는 return false
bool RMPlayerZoom::updateZoom(float x, float y, bool mainVideo)
{
return true;
}
#else //ZOOM_SHADER
RMPlayerZoom::RMPlayerZoom(QObject *parent) : RMPlayerBase(parent)
{
#if !(DO_NOT_USE_ZOOM)
zoomTargetMain = NULL;
#if !((SINGLE_CH_VIEWER || TOGGLE_PLAYER) && (!DUAL_VIEWER))
zoomTargetSub = NULL;
#endif
#endif
#if !(DO_NOT_USE_FLIP)
#if !(USE_SHADER_FLIP)
_hMainFilter = NULL;
_hRearFilter = NULL;
#endif //
_bHFlipMain = false;
_bVFlipMain = false;
_bHFlipSub = false;
_bVFlipSub = false;
#endif // ?USE_SHADER_FLIP
#if (USE_EQ_FILTER)
_brightness = -1000; // -1 ~ 1
_contrast = -1000; // 0.0 ~ 2.0
_hEQFilterF = NULL;
#if (!SINGLE_CH_VIEWER)
_hEQFilterR = NULL;
#endif // #if (!SINGLE_CH_VIEWER)
#endif // USE_EQ_FILTER
#if (RESIZE_FILTER)
_hResizeFilterF = NULL;
_hResizeFilterR = NULL;
#endif
}
#if !(DO_NOT_USE_ZOOM)
QWidget* RMPlayerZoom::startZoom(bool mainVideo)
{
//qInfo() << "START ZOOM";
#if !(SINGLE_CH_VIEWER || TOGGLE_PLAYER)
FAV::AVPlayer* player = mainVideo ? mainScreenPlayer() : subScreenPlayer();
#else
FAV::AVPlayer* player = _playerF;
#endif
if(player->videoOutputs().length() == 0)
{
qDebug() << "NO OUTPUTS" << __FUNCTION__;
return NULL;
}
#if !(SINGLE_CH_VIEWER || TOGGLE_PLAYER)
FAV::VideoRenderer* zt = NULL;
if (mainVideo) {
zt = zoomTargetMain = player->videoOutputs().first();
}
else {
zt = zoomTargetSub = player->videoOutputs().first();
}
#else
#if (DUAL_VIEWER && MODEL_360)
FAV::VideoRenderer* zt = NULL;
if (mainVideo) {
zt = zoomTargetMain = player->videoOutputs().first();
}
else {
zt = zoomTargetSub = player->videoOutputs().at(1); // 1,2,3
}
#else
FAV::VideoRenderer*zt = zoomTargetMain = player->videoOutputs().first();
#endif
#endif
zt->setRegionOfInterest(QRectF(0.25,0.25,0.5,0.5));
// QSize vSize = zt->videoFrameSize() / 2;
// zt->setRegionOfInterest(QRect(vSize.width()/2,vSize.height()/2,vSize.width(),vSize.height()));
FAV::VideoRenderer* vr = NULL;
if (mainVideo) {
if(_videoOutputZOOMMain == NULL)
{
// ZOOM Rendered
_videoOutputZOOMMain = FAV::VideoRenderer::create(_renderID);
// 비율을 고정할 경우 setOutAspectRatioMode(FAV::VideoRenderer::CustomAspectRation) +
// setOutAspectRatio(16.0 / 9.0); 사용
_videoOutputZOOMMain->setOutAspectRatioMode(FAV::VideoRenderer::CustomAspectRation);
_videoOutputZOOMMain->setOutAspectRatio(16.0 / 9.0);
vr = _videoOutputZOOMMain;
}
}
#if !((SINGLE_CH_VIEWER || TOGGLE_PLAYER) && (!DUAL_VIEWER))
else {
if(_videoOutputZOOMSub == NULL)
{
// ZOOM Rendered
_videoOutputZOOMSub = FAV::VideoRenderer::create(_renderID);
_videoOutputZOOMSub->setOutAspectRatioMode(FAV::VideoRenderer::CustomAspectRation);
_videoOutputZOOMSub->setOutAspectRatio(16.0 / 9.0);
vr = _videoOutputZOOMSub;
}
}
#endif // #if !(SINGLE_CH_VIEWER)
player->addVideoRenderer(vr);
if(player->isPaused() ) {
player->seek(MIN(player->position(),player->duration()));
}
#if (DUAL_VIEWER && MODEL_360) // 360 ONLY
vr->opengl()->setDualMode(1); // 2D
vr->opengl()->setDualFront(mainVideo); // 전후방
#if (USE_ZOOM_WH_RATIO)
// 해상도 높이가 1/2 로 다시 처리 해야함
QSize r = _playerF->videoResolution();
float wr = ((double)r.width()) / ((double)r.height() / 2.0) ;
vr->setOutAspectRatioMode(FAV::VideoRenderer::CustomAspectRation);
vr->setOutAspectRatio(wr); // 16.0/9.0
#endif // USE_ZOOM_WH_RATIO
#endif
#if !((SINGLE_CH_VIEWER || TOGGLE_PLAYER) && (!DUAL_VIEWER))
return mainVideo ? _videoOutputZOOMMain->widget() : _videoOutputZOOMSub->widget();
#else
return _videoOutputZOOMMain->widget();
#endif
}
void RMPlayerZoom::stopZoom(bool mainVideo)
{
//qInfo() << "STOP ZOOM";
#if !((SINGLE_CH_VIEWER || TOGGLE_PLAYER) && (!DUAL_VIEWER))
FAV::VideoRenderer* zt = mainVideo ? zoomTargetMain : zoomTargetSub;
#else
FAV::VideoRenderer* zt = zoomTargetMain;
#endif
if(zt == NULL) {
return;
}
// 복구 처리 해야함 + 다음파일 재생시 비디오 크기에 따라 다시 조절 해야함
zt->setRegionOfInterest(QRectF(0,0,0,0));
if(mainVideo) {
#if !(SINGLE_CH_VIEWER || TOGGLE_PLAYER)
mainScreenPlayer()->removeVideoRenderer(_videoOutputZOOMMain);
#else
_playerF->removeVideoRenderer(_videoOutputZOOMMain);
#endif
delete _videoOutputZOOMMain;
_videoOutputZOOMMain = NULL;
zoomTargetMain = NULL;
}
#if !((SINGLE_CH_VIEWER || TOGGLE_PLAYER) && (!DUAL_VIEWER))
else
{
#if (DUAL_VIEWER)
_playerF->removeVideoRenderer(_videoOutputZOOMSub);
#else
subScreenPlayer()->removeVideoRenderer(_videoOutputZOOMSub);
#endif
delete _videoOutputZOOMSub;
_videoOutputZOOMSub = NULL;
zoomTargetSub = NULL;
}
#endif // #if !(SINGLE_CH_VIEWER)
}
#if (USE_ZOOM_WH_RATIO)
void RMPlayerZoom::updateZoomAspectRatio()
{
// 해상도 높이가 1/2 로 다시 처리 해야함
QSize r = _playerF->videoResolution();
float wr = ((double)r.width()) / ((double)r.height() / 2.0) ;
if(_videoOutputZOOMMain != NULL) {
_videoOutputZOOMMain->setOutAspectRatioMode(FAV::VideoRenderer::CustomAspectRation);
_videoOutputZOOMMain->setOutAspectRatio(wr); // 16.0/9.0
}
if(_videoOutputZOOMSub != NULL) {
_videoOutputZOOMSub->setOutAspectRatioMode(FAV::VideoRenderer::CustomAspectRation);
_videoOutputZOOMSub->setOutAspectRatio(wr); // 16.0/9.0
}
}
#endif
bool RMPlayerZoom::updateZoom(float x, float y, bool mainVideo)
{
#if !((SINGLE_CH_VIEWER || TOGGLE_PLAYER) && (!DUAL_VIEWER))
FAV::VideoRenderer* zt = mainVideo ? zoomTargetMain : zoomTargetSub;
#else
FAV::VideoRenderer* zt = zoomTargetMain;
#endif
if(zt != NULL)
{
// y 를 0.001 로 처리하면 깨지는 증상이 발생함
x = ((int)(x * 100.0)) / 100.0;
y = ((int)(y * 100.0)) / 100.0;
//x = 0.152256;
// Ratio(0~1.0) 로 변경하면 해상도 변경시 문제 없음
zt->setRegionOfInterest(QRectF(x,y,0.5,0.5));
return true;
}
return false;
}
#endif
void RMPlayerZoom::clearScreen(bool front)
{
#if !((SINGLE_CH_VIEWER || TOGGLE_PLAYER) && (!DUAL_VIEWER))
#if (DUAL_VIEWER)
FAV::VideoRenderer* r = front ? _videoOutputF : _videoOutputR;
#else
FAV::AVPlayer* p = front ? _playerF : _playerR;
FAV::VideoRenderer* r = _currentVideoRenderer(p);
#endif
if(r != NULL) {
r->receive(FAV::VideoFrame());
}
#if !(DO_NOT_USE_ZOOM)
r = front ? _videoOutputZOOMMain : _videoOutputZOOMSub;
if(r != NULL) {
r->receive(FAV::VideoFrame());
}
#endif
#else
if(_videoOutputF != NULL) {
_videoOutputF->receive(FAV::VideoFrame());
}
#if !(DO_NOT_USE_ZOOM)
if(_videoOutputZOOMMain != NULL) {
_videoOutputZOOMMain->receive(FAV::VideoFrame());
}
#endif
#endif // #if !(SINGLE_CH_VIEWER)
//#if (MODEL_BBVIEWER)
// emit playEvent(front ? PLAY_DID_CLEAR_SCREEN_F : PLAY_DID_CLEAR_SCREEN_R, NULL);
//#endif
}
#if (RESIZE_FILTER && !DO_NOT_USE_ZOOM)
void RMPlayerZoom::resizeFilter(QSize size, bool bMain)
{
qInfo() << __FUNCTION__ << size;
if(size.width() < 0) {
if(_hResizeFilterF != NULL) {
_hResizeFilterF->uninstall();
delete _hResizeFilterF;
_hResizeFilterF = NULL;
}
if(_hResizeFilterR != NULL) {
_hResizeFilterR->uninstall();
delete _hResizeFilterR;
_hResizeFilterR = NULL;
}
updatePausedScreenFrontOnly();
updatePausedScreenRearOnly();
return;
}
FAV::LibAVFilterVideo* f = bMain ? _hResizeFilterF : _hResizeFilterR;
if(f != NULL)
{
f->uninstall();
delete f;
f = NULL;
}
//size *= 2.0;
//bilinear,, format=pix_fmts=yuv420p,
QString filterString = QString().sprintf("scale=%d:%d:force_original_aspect_ratio=decrease:flags=lanczos",size.width(),size.height());
qInfo() << filterString;
// QString filterString = QString().sprintf("scale=%d:%d -sws_flags bilinear",size.width(),size.height());
//QString filterString = QString().sprintf("format=pix_fmts=rgb24,scale=%d:%d",size.width(),size.height());
if(bMain == true)
{
f = new FAV::LibAVFilterVideo(_playerF);
f->installTo(_playerF);
f->setOptions(filterString);
_hResizeFilterF = f;
}
else
{
f = new FAV::LibAVFilterVideo(_playerR);
f->installTo(_playerR);
f->setOptions(filterString);
_hResizeFilterR = f;
}
updatePausedScreenFrontOnly();
updatePausedScreenRearOnly();
}
#endif //RESIZE_FILTER
#if !(DO_NOT_USE_FLIP)
#if (USE_SHADER_FLIP)
void RMPlayerZoom::updateFlip(bool bMain)
{
if(bMain) {
FAV::VideoRenderer* vr = _currentVideoRenderer(_playerF);
vr->opengl()->setVFlip(_bVFlipMain);
vr->opengl()->setHFlip(_bHFlipMain);
vr->widget()->update();
} else {
FAV::VideoRenderer* vr = _currentVideoRenderer(_playerR);
vr->opengl()->setVFlip(_bVFlipSub);
vr->opengl()->setHFlip(_bHFlipSub);
vr->widget()->update();
}
}
#else // USE_SHADER_FLIP
void RMPlayerZoom::updateFlip(bool bMain)
{
#if (PENTA_CHANNEL)
if(_hMainFilter != NULL)
{
_hMainFilter->uninstall();
delete _hMainFilter;
_hMainFilter = NULL;
}
if(_hRearFilter != NULL)
{
_hRearFilter->uninstall();
delete _hRearFilter;
_hRearFilter = NULL;
}
QString filterString = "format=pix_fmts=rgb24";
if(_bHFlipMain || _bVFlipMain )
{
_hMainFilter = new FAV::LibAVFilterVideo(_playerF);
_hMainFilter->installTo(_playerF);
if(_bHFlipMain) {
filterString += ",hflip";
}
if(_bVFlipMain) {
filterString += ",vflip";
}
_hMainFilter->setOptions(filterString);
}
updatePausedScreenFrontOnly();
if(_playerR != NULL) {
if(_bHFlipMain || _bVFlipMain ) { // PENTA 는 SUB가 없음
_hRearFilter = new FAV::LibAVFilterVideo(_playerR);
_hRearFilter->installTo(_playerR);
if(_bHFlipSub) {
filterString += ",hflip";
}
if(_bVFlipSub) {
filterString += ",vflip";
}
_hRearFilter->setOptions(filterString);
}
updatePausedScreenRearOnly();
}
#else // PENTA_CHANNEL
FAV::LibAVFilterVideo* f = bMain ? _hMainFilter : _hRearFilter;
if(f != NULL)
{
f->uninstall();
delete f;
f = NULL;
}
QString filterString = "format=pix_fmts=rgb24";
if(bMain == true)
{
if(_bHFlipMain == false && _bVFlipMain == false)
{
_hMainFilter = NULL;
updatePausedScreenFrontOnly();
return;
}
f = new FAV::LibAVFilterVideo(_playerF);
f->installTo(_playerF);
if(_bHFlipMain) {
filterString += ",hflip";
}
if(_bVFlipMain) {
filterString += ",vflip";
}
f->setOptions(filterString);
_hMainFilter = f;
updatePausedScreenFrontOnly();
}
else
{
if(_bHFlipSub == false && _bVFlipSub == false)
{
_hRearFilter = NULL;
#if (TOGGLE_PLAYER)
updatePausedScreenFrontOnly();
#else // TOGGLE_PLAYER
#if !(SINGLE_CH_VIEWER)
updatePausedScreenRearOnly();
#endif // #if !(SINGLE_CH_VIEWER)
#endif // TOGGLE_PLAYER
return;
}
#if (TOGGLE_PLAYER)
f = new FAV::LibAVFilterVideo(_playerF);
f->installTo(_playerF);
#else
#if (TRI_CHANNEL)
f = new FAV::LibAVFilterVideo(isIndoor() ? _playerI : _playerR);
f->installTo(isIndoor() ? _playerI : _playerR);
#else // TRI_CHANNEL
#if !(SINGLE_CH_VIEWER)
f = new FAV::LibAVFilterVideo(_playerR);
f->installTo(_playerR);
#endif // #if !(SINGLE_CH_VIEWER)
#endif // TRI_CHANNEL
#endif
if(_bHFlipSub) {
filterString += ",hflip";
}
if(_bVFlipSub) {
filterString += ",vflip";
}
f->setOptions(filterString);
_hRearFilter = f;
#if (TOGGLE_PLAYER)
updatePausedScreenFrontOnly();
#else
#if !(SINGLE_CH_VIEWER)
updatePausedScreenRearOnly();
#endif // #if !(SINGLE_CH_VIEWER)
#endif
}
#endif // PENTA_CHANNEL
}
#endif // #if !(USE_SHADER_FLIP)
void RMPlayerZoom::onToggleHFlipMain()
{
bool main = true;
#if !(SINGLE_CH_VIEWER)
if(_swaped)
{
_bHFlipSub =!_bHFlipSub;
main = false;
}
else
#endif // SINGLE_CH_VIEWER
{
_bHFlipMain =!_bHFlipMain;
main = true;
}
updateFlip(main);
}
#if !(SINGLE_CH_VIEWER)
void RMPlayerZoom::onToggleHFlipSub()
{
bool main;
if(_swaped)
{
_bHFlipMain =!_bHFlipMain;
main = true;
}
else
{
_bHFlipSub =!_bHFlipSub;
main = false;
}
updateFlip(main);
}
void RMPlayerZoom::onToggleVFlipSub()
{
bool main;
if(_swaped)
{
main = true;
_bVFlipMain =!_bVFlipMain;
}
else
{
main = false;
_bVFlipSub =!_bVFlipSub;
}
updateFlip(main);
}
#endif // #if !(SINGLE_CH_VIEWER)
void RMPlayerZoom::onToggleVFlipMain()
{
bool main;
#if !(SINGLE_CH_VIEWER)
if(_swaped)
{
main = false;
_bVFlipSub =!_bVFlipSub;
}
else
#endif // #if !(SINGLE_CH_VIEWER)
{
main = true;
_bVFlipMain =!_bVFlipMain;
}
updateFlip(main);
}
#endif // #if !(DO_NOT_USE_FLIP)
#if (USE_EQ_FILTER)
// 0~100 = -0.5 ~ 0.5
void RMPlayerZoom::onSetBrightness()
{
RMSlider* slider = qobject_cast<RMSlider*>(sender());
// 0~100 = -0.9 ~ 0.9
_brightness = RMUtility::range(0.0f,100.0f,-0.3f,0.3f,(float)slider->value());
//_brightness = RMUtility::range(0.0f,100.0f,-0.5f,0.5f,(float)slider->value());
//qInfo() << "B:" << slider->value() << _brightness;
_updateEQFilter();
}
// 0~100 = 0.1 ~ 1.9
void RMPlayerZoom::onSetContrast()
{
RMSlider* slider = qobject_cast<RMSlider*>(sender());
_contrast = RMUtility::range(0.0f,100.0f,0.6f,1.4f,(float)slider->value());
//_contrast = RMUtility::range(0.0f,100.0f,0.1f,1.9f,(float)slider->value());
//qInfo() << "C:" << slider->value() << _contrast;
_updateEQFilter();
}
void RMPlayerZoom::_updateEQFilter(bool bForce)
{
if(_brightness < 0.05f && _brightness > -0.05f)
{
_brightness = -1000.0f;
}
if(_contrast > 0.95f && _contrast < 1.05f)
{
_contrast = -1000.0f;
}
bool bExist = _brightness > -100.0f || _contrast > -100.0f;
if(bExist == false)
{
if(_hEQFilterF != NULL)
{
_hEQFilterF->uninstall();
delete _hEQFilterF;
_hEQFilterF = NULL;
}
#if (!SINGLE_CH_VIEWER)
if(_hEQFilterR != NULL)
{
_hEQFilterR->uninstall();
delete _hEQFilterR;
_hEQFilterR = NULL;
}
#endif // #if (!SINGLE_CH_VIEWER)
}
else
{
// 강제 삭제
if(bForce) {
if(_hEQFilterF != NULL)
{
_hEQFilterF->uninstall();
delete _hEQFilterF;
_hEQFilterF = NULL;
}
#if (!SINGLE_CH_VIEWER)
if(_hEQFilterR != NULL)
{
_hEQFilterR->uninstall();
delete _hEQFilterR;
_hEQFilterR = NULL;
}
#endif // #if (!SINGLE_CH_VIEWER)
}
QString filterString = "format=pix_fmts=rgb24,eq=";
if(_brightness > -100)
{
filterString += "brightness=";
filterString += QString().sprintf("%.3f",_brightness);
if(_contrast > -100)
{
filterString += ":";
}
}
if(_contrast > -100)
{
filterString += "contrast=";
filterString += QString().sprintf("%.3f",_contrast);
}
//qInfo() << "filter:" << filterString;
if(_hEQFilterF == NULL)
{
_hEQFilterF = new FAV::LibAVFilterVideo(this);
_hEQFilterF->installTo(_playerF);
}
#if (!SINGLE_CH_VIEWER)
if(_hEQFilterR == NULL && _playerR != NULL)
{
_hEQFilterR = new FAV::LibAVFilterVideo(this);
_hEQFilterR->installTo(_playerR);
}
#endif // #if (!SINGLE_CH_VIEWER)
_hEQFilterF->setOptions(filterString);
#if (!SINGLE_CH_VIEWER)
if(_hEQFilterR != NULL) {
_hEQFilterR->setOptions(filterString);
}
#endif // #if (!SINGLE_CH_VIEWER)
}
updatePausedScreenFrontOnly();
#if (!SINGLE_CH_VIEWER && !(TOGGLE_PLAYER))
updatePausedScreenRearOnly();
#endif
}
#endif
#if (!(SINGLE_CH_VIEWER || TOGGLE_PLAYER) || PENTA_CHANNEL)
void RMPlayerZoom::updatePausedScreenRearOnly()
{
}
#endif
void RMPlayerZoom::updatePausedScreenFrontOnly()
{
}
#if (RM_MODEL_360)
void RMPlayerZoom::onClearROI()
{
#if (USE_DRAG_ZOOM)
RMPlayerBase::onClearROI();
#endif // USE_DRAG_ZOOM
//FAV::VideoRenderer* vr = playerF()->renderer();
_videoOutputF->opengl()->clearROI();
_videoOutputF->widget()->update();
if(_videoOutputF->opengl()->mode() == 0) {
_videoOutputF->setOutAspectRatioMode(FAV::VideoRenderer::VideoAspectRatio);
}
#if (REAR_VIEW_ZOOM)
_videoOutputR->opengl()->clearROI();
_videoOutputR->widget()->update();
if(_videoOutputR->opengl()->mode() == 0) {
_videoOutputR->setOutAspectRatioMode(FAV::VideoRenderer::VideoAspectRatio);
}
#endif // REAR_VIEW_ZOOM
}
#endif // RM_MODEL_360
#endif // //ZOOM_SHADER

View File

@@ -0,0 +1,126 @@
#ifndef RM_PLAYER_ZOOM_H
#define RM_PLAYER_ZOOM_H
#include "rm_player_base.h"
#if (ZOOM_SHADER)
class RMPlayerZoom : public RMPlayerBase
{
Q_OBJECT
public:
explicit RMPlayerZoom(QObject *parent = nullptr);
// ZOOM 모드 시작
void startZoom(bool mainVideo);
// ZOOM 종료
void stopZoom(bool mainVideo);
// ZOOM 위치 변경 + 프레임 크기가 확정되기 전에는 return false
bool updateZoom(float x, float y, bool mainVideo);
};
#else // ZOOM_SHADER
class RMPlayerZoom : public RMPlayerBase
{
Q_OBJECT
private:
#if !(DO_NOT_USE_FLIP)
bool _bHFlipMain;
bool _bVFlipMain;
bool _bHFlipSub;
bool _bVFlipSub;
#endif
#if (USE_EQ_FILTER)
float _brightness;
float _contrast;
FAV::LibAVFilterVideo* _hEQFilterF;
#if (!SINGLE_CH_VIEWER)
FAV::LibAVFilterVideo* _hEQFilterR;
#endif // SINGLE_CH_VIEWER
protected:
void _updateEQFilter(bool bForce = false);
private:
#endif
#if (RESIZE_FILTER)
FAV::LibAVFilterVideo* _hResizeFilterF;
FAV::LibAVFilterVideo* _hResizeFilterR;
#endif
public:
explicit RMPlayerZoom(QObject *parent = nullptr);
#if !(DO_NOT_USE_ZOOM)
void resizeFilter(QSize size, bool mainVideo);
// ZOOM 모드 시작
QWidget* startZoom(bool mainVideo);
// ZOOM 종료
void stopZoom(bool mainVideo);
// ZOOM 위치 변경 + 프레임 크기가 확정되기 전에는 return false
bool updateZoom(float x, float y, bool mainVideo);
#if (USE_ZOOM_WH_RATIO)
void updateZoomAspectRatio();
#endif
// ZOOM TARGET 저장
FAV::VideoRenderer* zoomTargetMain;
#if !((SINGLE_CH_VIEWER || TOGGLE_PLAYER) && (!DUAL_VIEWER))
FAV::VideoRenderer* zoomTargetSub;
#endif // #if !(SINGLE_CH_VIEWER)
#endif // #if !(DO_NOT_USE_ZOOM)
#if !(DO_NOT_USE_FLIP)
#if !(USE_SHADER_FLIP)
FAV::LibAVFilterVideo* _hMainFilter;
FAV::LibAVFilterVideo* _hRearFilter;
#endif // !USE_SHADER_FLIP
void updateFlip(bool bMain);
#endif // USE_SHADER_FLIP
protected:
void clearScreen(bool front) override;
//void _updateEQFilter() override;
private:
#if (!(SINGLE_CH_VIEWER || TOGGLE_PLAYER) || PENTA_CHANNEL)
virtual void updatePausedScreenRearOnly();
#endif
virtual void updatePausedScreenFrontOnly();
signals:
public slots:
#if !(DO_NOT_USE_FLIP)
void onToggleHFlipMain();
void onToggleVFlipMain();
#if !(SINGLE_CH_VIEWER)
void onToggleHFlipSub();
void onToggleVFlipSub();
#endif // #if !(SINGLE_CH_VIEWER)
#endif
#if (RM_MODEL_360)
void onClearROI();
#endif
#if (USE_EQ_FILTER)
// 0~100 = -0.9 ~ 0.9
void onSetBrightness();
// 0~100 = 0.1 ~ 1.9
void onSetContrast();
#endif
};
#endif // ZOOM_SHADER
#endif // RM_PLAYER_ZOOM_H

View File

@@ -0,0 +1,340 @@
#include "rm_usb.h"
#include <Windows.h>
#include <dbt.h>
#include <QDebug>
#include <QDir>
#include <QTimer>
#if (RM_MODEL_EMT_KR)
#include "../cfg/rm_settings_cfg_emt_kr.h"
#endif
#if (DETECT_SETTING_USB_EJECT || DETECT_USB_CHANGE)
rm_usb::rm_usb(QObject *parent) :
QObject(parent)
{
_hDevNotify = NULL;
_insertringDrive = 0;
_removingDrive = 0;
_openDrive = "";
_irTimer = NULL;
}
rm_usb::~rm_usb()
{
if(_hDevNotify != NULL) {
::UnregisterDeviceNotification(_hDevNotify);
}
_cancelIRTimer();
}
// 항상 대문자로 리턴됨
char rm_usb::_firstDriveFromMask( uint unitmask )
{
char i;
for (i = 0; i < 26; ++i)
{
if (unitmask & 0x1)
break;
unitmask = unitmask >> 1;
}
return( i + 'A' );
}
void rm_usb::_startIRTimer()
{
_cancelIRTimer();
_irTimer = new QTimer(this);
_irTimer->setSingleShot(true);
_irTimer->setInterval(1000);
connect(_irTimer,SIGNAL(timeout()),SLOT(onClearIR()));
_irTimer->start();
}
void rm_usb::_cancelIRTimer()
{
if(_irTimer != NULL) {
_irTimer->stop();
delete _irTimer;
_irTimer = NULL;
}
}
void rm_usb::onClearIR()
{
_insertringDrive = 0;
_removingDrive = 0;
}
bool rm_usb::nativeEventFilter(const QByteArray &eventType, void *message, long *result)
{
Q_UNUSED(eventType);
Q_UNUSED(result);
#ifdef Q_OS_WIN32
MSG *msg = (MSG *)message;
if( WM_DEVICECHANGE == msg->message && DBT_DEVICEARRIVAL == msg->wParam )
{
DEV_BROADCAST_HDR* dev = (DEV_BROADCAST_HDR*)msg->lParam;
if (dev->dbch_devicetype == DBT_DEVTYP_VOLUME)
{
PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)dev;
char driver = _firstDriveFromMask(lpdbv->dbcv_unitmask);
// 다중 호출 방지 -> 처리 끝나면 초기화 해야 함
if (_insertringDrive != driver)
{
_insertringDrive = driver;
QString driver_letter(driver);
driver_letter += ":";
// qInfo() << "USB arrival detected:" << driver;
*result = 1;
emit usbChanged(true,driver_letter);
_startIRTimer();
}
}
}
else if( WM_DEVICECHANGE == msg->message && DBT_DEVICEREMOVECOMPLETE == msg->wParam )
{
DEV_BROADCAST_HDR* dev = (DEV_BROADCAST_HDR*)msg->lParam;
if (dev->dbch_devicetype == DBT_DEVTYP_VOLUME)
{
PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)dev;
char driver = _firstDriveFromMask(lpdbv->dbcv_unitmask);
if (_removingDrive != driver)
{
_removingDrive = driver;
QString driver_letter(driver);
driver_letter += ":";
// qInfo() << "USB departure detected:" << " driver:" << driver;
*result = 1;
emit usbChanged(false,driver_letter);
_startIRTimer();
}
}
}
#endif // Q_OS_WIN32
// Return false so that the event is propagated
return false;
}
void rm_usb::registerEvent(QWidget *window)
{
if( window != nullptr )
{
#ifdef Q_OS_WIN32
GUID WceusbshGUID = { 0x25dbce51, 0x6c8f, 0x4a72, 0x8a,0x6d,0xb5,0x4c,0x2b,0x4f,0xc8,0x35 };
//GUID WusbrawGUID = {0xa5dcbf10, 0x6530, 0x11d2, 0x90, 0x1f, 0x00, 0xc0, 0x4f, 0xb9, 0x51, 0xed };
//GUID WusbGUID = {0x88BAE032, 0x5A81, 0x49f0, 0xBC, 0x3D, 0xA4, 0xFF, 0x13, 0x82, 0x16, 0xD6 };
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
::ZeroMemory( &NotificationFilter, sizeof(NotificationFilter) );
NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
NotificationFilter.dbcc_classguid = WceusbshGUID;
_hDevNotify = ::RegisterDeviceNotification((HANDLE)window->winId(), &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
if( NULL == _hDevNotify )
{
// Print error
}
#endif
}
}
bool rm_usb::isRemovablePath(QString path)
{
if(path.contains(":")) {
QString driver = path.split(":").first();
WCHAR diskName[4] = {0,};
driver = driver.left(1) + ":\\";
driver.toWCharArray(diskName);
if(::GetDriveType(diskName) == DRIVE_REMOVABLE) {
return true;
}
}
return false;
}
void rm_usb::setOpenDriveWithPath(QString path)
{
if(rm_usb::isRemovablePath(path)) {
_openDrive = path.left(1).toUpper() + ":";
}
}
int rm_usb::isDeviceDriver(QString folder, bool loadCFG)
{
#if (RM_MODEL_TB) // RM_MODEL == RM_MODEL_TYPE_TB4000
return QDir(folder).exists() && QDir(folder + QDir::separator() + QString("Video")).exists() && QDir(folder + QDir::separator() + QString("Photo")).exists();
#elif (RM_MODEL_EMT_KR)
// 각 모델별로 확인
for(int i=0;i<CFG::model_names.size();i++) {
QString path = folder + "Setting" + QDir::separator() + CFG::model_names.at(i) + "_setting.cfg";
if(QFile::exists(path)) {
// 설정파일 읽을 필요 없음
if(!loadCFG) {
return 0;
}
// checksum + 크기 + 모델코드 확인
CFG_ERROR_CODE code = CFG::load(path);
if(code != CFG_SUCCESS) {
if(code == CFG_CHECKSUM_ERROR) {
return code;
}
//qInfo() << path << " MODEL CODE:" << CFG::info.model << "ERROR:" << code << __FUNCTION__;
}
if(code == CFG_SUCCESS && CFG::info.model == CFG::model_codes[i])
{
return 0;
}
}
}
// E:\Setting , NM5000_setting.cfg
// Magnus : 5101
// Trinity : 5102
// Mirror5 : 5103
// Pro5 : 5104
// 360X : 5105
return 999;
#else //
return false;
#endif;
}
// Disk 의 볼륨명이 모델명과 동일함
#if (RM_MODEL_EMT_KR)
QString rm_usb::getRemovableDisk()
{
#if defined(WIN32)
WCHAR szLogicalDrives[MAX_PATH * sizeof(TCHAR)];
DWORD dwByte = ::GetLogicalDriveStrings(MAX_PATH, szLogicalDrives);
for(DWORD i=0; i<dwByte / 4; i++)
{
WCHAR* diskName = &szLogicalDrives[i * 4];
UINT type = ::GetDriveType(diskName);
if(DRIVE_REMOVABLE == type)
{
#if (!NO_SD_CHECK)
QString diskLetter = QString::fromUtf16((const ushort*)diskName);
diskLetter.replace("\\","");
diskLetter = "\\\\.\\" + diskLetter;
// qInfo() << "check disk name:" << diskLetter;
// empty media check
HANDLE hDisk = ::CreateFile(reinterpret_cast<LPCWSTR>(diskLetter.utf16()),
FILE_READ_DATA,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL);
if(hDisk == INVALID_HANDLE_VALUE || hDisk == NULL)
{
//qInfo() << "INVALID_HANDLE_VALUE" << diskLetter << "type" << type;
continue;
}
DWORD dwBytesReturned;
ULONG MediaChangeCount;
BOOL res = ::DeviceIoControl( hDisk,
IOCTL_STORAGE_CHECK_VERIFY,
NULL,
0,
&MediaChangeCount,
sizeof(ULONG),
&dwBytesReturned,
NULL);
::CloseHandle(hDisk);
if(res == FALSE)
{
//qInfo() << "empty" << QString::fromUtf16((const ushort*)diskName) << "type" << type;
continue;
}
#endif
QString diskPath = QString::fromUtf16((const ushort*)diskName);
// 설정파일 존재 확인
if(isDeviceDriver(diskPath,false) == 0) {
return diskPath;
}
}
}
#endif
return "";
}
#else // TB
QString rm_usb::getRemovableDisk(QString volume)
{
#if defined(WIN32)
WCHAR szLogicalDrives[MAX_PATH * sizeof(TCHAR)];
DWORD dwByte = ::GetLogicalDriveStrings(MAX_PATH, szLogicalDrives);
for(DWORD i=0; i<dwByte / 4; i++)
{
WCHAR* diskName = &szLogicalDrives[i * 4];
UINT type = ::GetDriveType(diskName);
if(DRIVE_REMOVABLE == type)
{
#if (!NO_SD_CHECK)
QString diskLetter = QString::fromUtf16((const ushort*)diskName);
diskLetter.replace("\\","");
diskLetter = "\\\\.\\" + diskLetter;
// qInfo() << "check disk name:" << diskLetter;
// empty media check
HANDLE hDisk = ::CreateFile(reinterpret_cast<LPCWSTR>(diskLetter.utf16()),
FILE_READ_DATA,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL);
if(hDisk == INVALID_HANDLE_VALUE || hDisk == NULL)
{
//qInfo() << "INVALID_HANDLE_VALUE" << diskLetter << "type" << type;
continue;
}
DWORD dwBytesReturned;
ULONG MediaChangeCount;
BOOL res = ::DeviceIoControl( hDisk,
IOCTL_STORAGE_CHECK_VERIFY,
NULL,
0,
&MediaChangeCount,
sizeof(ULONG),
&dwBytesReturned,
NULL);
::CloseHandle(hDisk);
if(res == FALSE)
{
//qInfo() << "empty" << QString::fromUtf16((const ushort*)diskName) << "type" << type;
continue;
}
#endif
WCHAR volumeNameW[MAX_PATH * sizeof(TCHAR)] = {0,};
::GetVolumeInformation(diskName,volumeNameW, 1024, NULL, NULL, NULL, NULL, MAX_PATH);
QString volumeName = QString::fromUtf16((const ushort*)volumeNameW);
QString diskPath = QString::fromUtf16((const ushort*)diskName);
//qInfo() << volumeName << __FUNCTION__;
if(volume.isEmpty() || volume.compare(volumeName,Qt::CaseInsensitive) == 0)
{
diskPath = diskPath.replace("\\","");
//rm_usb::current_driver = diskPath.at(0).toLatin1();
return diskPath;
}
}
}
#endif
return "";
}
#endif // #if (RM_MODEL_EMT_KR)
#endif // #if (USE_SD_CARD_DETECT)

View File

@@ -0,0 +1,74 @@
#ifndef RM_USB_H
#define RM_USB_H
#include <QObject>
#include <QAbstractNativeEventFilter>
#include <QMainWindow>
#include "../rm_include.h"
#if (DETECT_SETTING_USB_EJECT || DETECT_USB_CHANGE)
class rm_usb : public QObject, public QAbstractNativeEventFilter
{
friend class RMWindowsDisk;
Q_OBJECT
public:
explicit rm_usb(QObject *parent = 0);
~rm_usb();
virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *result);
//! \brief 지정된 볼륨의 USB 장치 탐색
//! \param volume: 탐색할 볼륨명
//! \return 탐색된 USB 폴더명
#if (RM_MODEL_EMT_KR)
static QString getRemovableDisk();
#else // RM_MODEL_EMT_KR
static QString getRemovableDisk(QString volume);
#endif // RM_MODEL_EMT_KR
QString _openDrive; ///!< 현재 사용된 드라이버
//! \brief 지정된 경로가 USB 디스크인지 확인
//! \param volume
//! \return true:YES
static bool isRemovablePath(QString path);
//! \brief setopenDriveWithFolder
//! 경로의 드라이버가 removeable 일 경우 _openDrive 지정
//! \param path: 경로
void setOpenDriveWithPath(QString path);
//! \brief 장치 드라이버인지 확인
//! 내부의 폴더, 볼륨명 등으로 확인
//! 설정파일도 읽고 검증
//! \param path: 경로
//! \return 0:success other:CFG_ERROR_CODE
static int isDeviceDriver(QString path,bool loadCFG = false);
private:
char _firstDriveFromMask(uint unitmask );
char _insertringDrive; // 다중호출 방지, 현재 추가되고 있는 드라이브
char _removingDrive; // " 제거되고 있는 드라이브
QTimer* _irTimer; ///! 동시 다중호출 방지를 위해 사용하는 _insertringDrive, _removingDrive 초기화
//! \brief 초기화(_insertringDrive,_removingDrive) 타이머 시작
void _startIRTimer();
//! \brief 초기화(_insertringDrive,_removingDrive) 타이머 취소
void _cancelIRTimer();
void* _hDevNotify;
signals:
void usbChanged(bool inserted, QString& drive); // or removed
//! \brief 잘못된 SD 카드
//! \param code: CFG_ERROR_CODE
void wrongSDCard(int code);
public slots:
void registerEvent(QWidget *window);
//! \brief _insertringDrive, _removingDrive 초기화
void onClearIR();
};
#endif // USE_SD_CARD_DETECT
#endif // RM_USB_H

View File

@@ -0,0 +1,12 @@
#include "rm_utility.h"
RMUtility::RMUtility()
{
}
float RMUtility::range(float fmin, float fmax,float tmin, float tmax, float value)
{
const float newRange = tmax - tmin;
const float oldRange = fmax - fmin;
return ((((value - (fmin)) * newRange) / oldRange) + tmin);
}

View File

@@ -0,0 +1,14 @@
#ifndef RM_UTILITY_H
#define RM_UTILITY_H
class RMUtility
{
public:
RMUtility();
static float range(float fmin, float fmax,float tmin, float tmax, float value);
};
#endif // RM_UTILITY_H

View File

@@ -0,0 +1,373 @@
#include "an6000_decode.h"
#if (RM_MODEL == RM_MODEL_TYPE_AN6000)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
volatile unsigned long size;
volatile char type[4];
} BoxHeader_t;
typedef struct _BoxAddress_t {
fpos_t addr;
unsigned long size;
} BoxAddress_t;
/**
* @brief Convert the Byte-order(32bit) <for little-endian environment>
*
* @param[in] val target value
*
* @return convert value
*/
unsigned long convertEndian_32(unsigned long val)
{
unsigned long result = 0;
unsigned char *p1 = (unsigned char *)&val;
unsigned char *p2 = (unsigned char *)&result;
p2[0] = p1[3];
p2[1] = p1[2];
p2[2] = p1[1];
p2[3] = p1[0];
return result;
}
/**
* @brief [DEBUG] Box data analysis
*
* @param[in] fp target file pointer
* @param[in] level analysis depth
* @param[in] parentSize parent box size
*/
void BoxAnalysis(FILE *fp, int level, size_t parentSize)
{
BoxHeader_t boxHead, childBoxHead;
char typeDesc[8] = {0};
size_t readSize;
int i;
int isNotAscii;
char *pbuf = NULL;
fpos_t pos;
while (1) {
// read box header
readSize = fread(&boxHead, 1, sizeof(BoxHeader_t), fp);
if ((feof(fp) != 0) || (readSize == 0)) {
break;
}
boxHead.size = convertEndian_32(boxHead.size);
strncpy(typeDesc, (const char *)&boxHead.type[0], 4);
if (boxHead.size == 0) {
break;
}
fgetpos(fp, &pos);
printf("[%08X] ", (unsigned long)(pos - sizeof(BoxHeader_t)));
for (i = 0; i < level; i++) {
printf(" ");
}
printf("%s[%d]\n", typeDesc, boxHead.size);
if (parentSize != (size_t)-1) {
parentSize -= boxHead.size;
}
// check child box exist
readSize = fread(&childBoxHead, 1, sizeof(BoxHeader_t), fp);
childBoxHead.size = convertEndian_32(childBoxHead.size);
isNotAscii = 0;
for (i = 0; i < 4; i++) {
if ((childBoxHead.type[i] < 'a') || (childBoxHead.type[i] > 'z')) {
++isNotAscii;
break;
}
}
fseek(fp, -(long)sizeof(BoxHeader_t), SEEK_CUR);
if ((boxHead.size > childBoxHead.size) && (isNotAscii == 0)) {
// child box exist
BoxAnalysis(fp, level + 1, boxHead.size - sizeof(BoxHeader_t));
}
else {
// child box not exist
pbuf = (char *)malloc(boxHead.size - sizeof(BoxHeader_t));
fread(pbuf, 1, boxHead.size - sizeof(BoxHeader_t), fp);
free(pbuf);
}
// check of continue the child box loop
if (parentSize == 0) {
break;
}
// terminal box
if (strncmp((const char *)boxHead.type, "FWVR", 4) == 0) {
break;
}
}
if (parentSize == (size_t)-1) {
fseek(fp, 0, SEEK_SET);
}
}
/**
* @brief Get the crypt target box information
*
* @param[in] fp target file pointer
* @param[out] buf target box information
* @param[in] bufSize number of \a buf buffer
* @param[in] level analysis depth
* @param[in] parentSize parent box size
*
* @return number of available target box information
*/
static bool gFound_stsz = false;
static bool gFount_udat = false;
size_t GetCryptTarget(FILE *fp, BoxAddress_t *buf, size_t bufSize, int level, size_t parentSize)
{
size_t buf_num = 0;
BoxHeader_t boxHead = {0,};
BoxHeader_t childBoxHead = {0,};
size_t readSize = 0;
int isNotAscii = 0;
int i = 0;
//char *pbuf = NULL;
fpos_t pos = 0;
if (bufSize == 0) {
return 0;
}
while (1) {
// read box header
readSize = fread(&boxHead, 1, sizeof(BoxHeader_t), fp);
if (readSize == 0) {
break;
}
boxHead.size = convertEndian_32(boxHead.size);
if (boxHead.size == 0) {
break;
}
if (parentSize != (size_t)-1) {
parentSize -= boxHead.size;
}
// check crypt target box
if (((strncmp((const char *)boxHead.type, "stsz", 4) == 0) && (gFound_stsz == false) )
|| (strncmp((const char *)boxHead.type, "udat", 4) == 0) && (gFount_udat == false)){
fgetpos(fp, &pos);
buf[buf_num].addr = pos - sizeof(BoxHeader_t);
buf[buf_num].size = boxHead.size;
fseek(fp, boxHead.size - sizeof(BoxHeader_t), SEEK_CUR);
if(strncmp((const char *)boxHead.type, "stsz", 4) == 0)
{
gFound_stsz = true;
}
else if(strncmp((const char *)boxHead.type, "udat", 4) == 0)
{
gFount_udat = true;
}
if (++buf_num == bufSize) {
return buf_num;
}
}
else {
// Existence check of child box
readSize = fread(&childBoxHead, 1, sizeof(BoxHeader_t), fp);
fseek(fp, -(long)sizeof(BoxHeader_t), SEEK_CUR);
childBoxHead.size = convertEndian_32(childBoxHead.size);
isNotAscii = 0;
for (i = 0; i < 4; i++) {
if ((childBoxHead.type[i] < 'a') || (childBoxHead.type[i] > 'z')) {
++isNotAscii;
break;
}
}
if ((boxHead.size > childBoxHead.size) && (isNotAscii == 0)) {
// child box is exist
buf_num += GetCryptTarget(fp, &buf[buf_num], bufSize - buf_num, level + 1, boxHead.size - sizeof(BoxHeader_t));
if (buf_num == bufSize) {
return buf_num;
}
}
else {
// child box is not exist
fseek(fp, boxHead.size - sizeof(BoxHeader_t), SEEK_CUR);
}
}
// check of continue the child box loop
if (parentSize == 0) {
break;
}
// terminal box
if (strncmp((const char *)boxHead.type, "FWVR", 4) == 0) {
break;
}
}
return buf_num;
}
/**
* @brief Encrypt MP4 File
*
* @param[in] filename target file name
*
* @retval 0 successful
* @retval non-zero failed
*/
int encrypt_an6000(const wchar_t *filename)
{
FILE *fp = NULL;
BoxAddress_t tgtInfo[8];
size_t infoSize, i, x;
char *pbuf;
char key = 0x19;
char preData, enc;
memset(tgtInfo, 0, sizeof(tgtInfo));
infoSize = sizeof(tgtInfo) / sizeof(BoxAddress_t);
fp = _wfopen(filename,L"r+b");
//fp = fopen(filename, "r+b");
if (fp == NULL) {
return -1;
}
fseek(fp, 0, SEEK_SET);
// Analysis box information
//BoxAnalysis(fp, 0, (size_t)-1);
infoSize = GetCryptTarget(fp, tgtInfo, infoSize, 0, -1);
for (i = 0; i < infoSize; i++) {
pbuf = (char*)malloc(tgtInfo[i].size);
// read the target data
fsetpos(fp, &tgtInfo[i].addr);
fread(pbuf, 1, tgtInfo[i].size, fp);
// data encrypt
preData = 0;
for (x = 8; x < tgtInfo[i].size; x++) {
enc = ((~pbuf[x]) ^ preData) ^ key;
preData = pbuf[x];
pbuf[x] = enc;
}
// over-write the target data
fsetpos(fp, &tgtInfo[i].addr);
fwrite(pbuf, 1, tgtInfo[i].size, fp);
free(pbuf);
}
fclose(fp);
return 0;
}
/**
* @brief Decrypt MP4 File
*
* @param[in] filename target file name
*
* @retval 0 successful
* @retval non-zero failed
*/
int decrypt_an6000(const wchar_t *filename)
{
gFound_stsz = false;
gFount_udat = false;
FILE *fp = NULL;
BoxAddress_t tgtInfo[8] = {0,};
size_t infoSize, i, x;
char *pbuf = NULL;
char key = 0x19;
char preData = 0;
memset(tgtInfo, 0, sizeof(_BoxAddress_t) * 8);
infoSize = sizeof(tgtInfo) / sizeof(_BoxAddress_t);
// filePathCH2.toStdWString().c_str()
fp = _wfopen(filename,L"r+b");
//fp = fopen(filename, "r+b");
if (fp == NULL) {
return -1;
}
fseek(fp, 0, SEEK_SET);
// Analysis box information
infoSize = GetCryptTarget(fp, tgtInfo, infoSize, 0, -1);
for (i = 0; i < infoSize; i++) {
pbuf = (char*)malloc(tgtInfo[i].size);
// read the target data
fsetpos(fp, &tgtInfo[i].addr);
fread(pbuf, 1, tgtInfo[i].size, fp);
// data decrypt
preData = 0;
for (x = 8; x < tgtInfo[i].size; x++) {
pbuf[x] = ~((pbuf[x] ^ key) ^ preData);
preData = pbuf[x];
}
// over-write the target data
fsetpos(fp, &tgtInfo[i].addr);
fwrite(pbuf, 1, tgtInfo[i].size, fp);
free(pbuf);
}
fclose(fp);
return 0;
}
bool is_encrypted_an6000(const wchar_t *filename)
{
gFound_stsz = false;
gFount_udat = false;
FILE *fp = NULL;
BoxAddress_t tgtInfo[8] = {0,};
size_t infoSize, i, x;
memset(tgtInfo, 0, sizeof(_BoxAddress_t) * 8);
infoSize = sizeof(tgtInfo) / sizeof(_BoxAddress_t);
fp = _wfopen(filename,L"r+b");
if (fp == NULL) {
return false;
}
fseek(fp, 0, SEEK_SET);
infoSize = GetCryptTarget(fp, tgtInfo, infoSize, 0, -1);
char buffer[16] = {0,};
for (i = 0; i < infoSize; i++) {
// read the target data
tgtInfo[i].addr += 4;
fsetpos(fp, &tgtInfo[i].addr);
fread(buffer, 1, 16, fp);
if(buffer[0] == 's' && buffer[1] =='t' && buffer[2] =='s' && buffer[3] =='z') {
bool enc = (buffer[4] != 0 || buffer[5] != 0 || buffer[6] != 0 || buffer[7] != 0);
fclose(fp);
return enc;//(buffer[4] != 0 || buffer[5] != 0 || buffer[6] != 0 || buffer[7] != 0);
}
//printf("%c:%c:%c:%c\n",buffer[0],buffer[1],buffer[2],buffer[3]);
//printf("%02X:%02X:%02X:%02X %02X:%02X:%02X:%02X\n",buffer[0],buffer[1],buffer[2],buffer[3],buffer[4],buffer[5],buffer[6],buffer[7]);
}
fclose(fp);
return true;
}
#endif // #if (RM_MODEL == RM_MODEL_TYPE_AN6000)

View File

@@ -0,0 +1,12 @@
#ifndef AN6000_DECODE_H
#define AN6000_DECODE_H
#include "../rm_include.h"
#if (RM_MODEL == RM_MODEL_TYPE_AN6000)
int decrypt_an6000(const wchar_t *filename);
int encrypt_an6000(const wchar_t *filename);
bool is_encrypted_an6000(const wchar_t *filename);
#endif // #if (RM_MODEL == RM_MODEL_TYPE_AN6000)
#endif // AN6000_DECODE_H

View File

@@ -0,0 +1,37 @@
#include "fileio.h"
int read_long(FILE *in)
{
int c;
c=getc(in);
c=c+(getc(in)<<8);
c=c+(getc(in)<<16);
c=c+(getc(in)<<24);
return c;
}
int read_word(FILE *in)
{
int c;
c=getc(in);
c=c+(getc(in)<<8);
return c;
}
int read_chars(FILE *in, char *s, int count)
{
int t;
for (t=0; t<count; t++)
{
s[t]=getc(in);
}
s[t]=0;
return 0;
}

View File

@@ -0,0 +1,22 @@
#ifndef FILEIO_H
#define FILEIO_H
// #include "rm_constants.h"
#include <stdio.h>
typedef FILE* RMfile;
#define RMfread(__BUFFER,__SIZE,__COUNT,__FILE) fread(__BUFFER,__SIZE,__COUNT,__FILE)
#define RMftell(__FILE) ftell(__FILE)
#define RMfseek(__FILE,__OFSET,__SEEK_TYPE) fseek(__FILE,__OFSET,__SEEK_TYPE)
#define RMgetc(__FILE) getc(__FILE)
#define RMfeof(__FILE) feof(__FILE)
int read_long(RMfile in);
int read_word(RMfile in);
int read_chars(RMfile in, char *s, int count);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,196 @@
#ifndef FM_ADDRESS_H
#define FM_ADDRESS_H
#if (USE_JP_ADDRESS)
#include <QString>
#include <inttypes.h>
#include <QMap>
#define USE_ADDR_DB_TYPE 1
// // _maxLenPref: 4 _maxLenCity: 8 _maxLenChome: 12
#define MAX_JA_AREA_NAME_LEN 30
#define MAX_PREF_COUNT 50 // 도도부현 개수
#define MAX_CITY_COUNT 200 // 도도부현당 최대 시/구 개수, "北海道" 52, "大阪府" 64
#if (USE_ADDR_DB_TYPE == 0)
#define MAX_JIBUN_COUNT 4000 // 3381
#define MAX_CHOME_COUNT 1500 // 시/구당 최대 마을 개수 旭川市 1475
#elif (USE_ADDR_DB_TYPE == 1)
#define MAX_JIBUN_COUNT 20000 // 20000
#define MAX_CHOME_COUNT 5000 // 福島県, 시/구당 최대 마을 개수 旭川市 1475
#endif
typedef struct _JA_HEADER {
char header[128]; // 0
char date[24]; // 128
char version[24]; // 152
uint32_t stringSize; // 176 문자열 테이블 크기
//uint32_t prefOffset; // 180 도도부현 시작 옵셋 (=sizeof header + stringSize) + ....
uint32_t pref_count; // 184 pref 개수
uint32_t city_count; // 188 전체 city 개수
uint32_t town_count; // 192 전체 town 개수
uint32_t jibun_count; // 196 전체 지번 개수
int32_t xmin; // 영역..
int32_t ymin;
int32_t xmax;
int32_t ymax;
} JA_HEADER;
// 저장용 구조체
typedef struct _JA_AREA_PACKET
{
uint32_t nameOffset;
uint32_t sub_count;
uint32_t sub_offset;
int32_t xmin;
int32_t ymin;
int32_t xmax;
int32_t ymax;
uint8_t bits[4]; // CHOME 만 사용 (BITS X,Y,A0,A1)
} JA_AREA_PACKET;
#if (USE_JP_ADDRESS_TOOL)
// 주소(지번) -> 처리용 구조체
typedef struct _JA_JIBUN {
uint16_t a0; // 150 : 8bit
uint16_t a1; // 732 : 10bit
int32_t x;
int32_t y;
} JA_JIBUN; // 12BYTE
// 도도부현,시구,마을(丁目) -> 처리용 구조체
typedef struct _JA_AREA {
ushort name[MAX_JA_AREA_NAME_LEN]; // 16(12 * 2)
int32_t count; // sub area(or a) count
_JA_AREA* subAreas;
_JA_JIBUN* subJibuns;
uint32_t offset; // sub area offset // or Pointer
int32_t xmin;
int32_t ymin;
int32_t xmax;
int32_t ymax;
uint32_t nameOffset;
uint16_t a0min; // BYTE PACK 계산하기 위해 사용
uint16_t a0max;
uint16_t a1min;
uint16_t a1max;
uint8_t bits[4]; // CHOME 의 경우 포함된 지번 데이터의 BIT PACK
} JA_AREA;
#endif // #if (USE_JP_ADDRESS_TOOL)
// 北海道 深川市 一条 (3066,1474)
// 神奈川県 横浜市鶴見区 大黒町 (879,2625)
// XMAX: 12bit, YMAX:12bit,
// a0: // 150 : 8bit
// a1: // 732 : 10bit
class FMAddress
{
private:
// 탐색 결과
int _found_a0;
int _found_a1;
int _found_dist_sq; // 거리^2
int _found_x;
int _found_y;
JA_HEADER _header; // 헤더
JA_AREA_PACKET* _areas; // 도도부현, 시구군, 읍면동..
uint8_t* _strings; // 문자열 "
FILE* _file;
// TOWN내에서 가장 가까운 지번 탐색 town, x,y (return dist, a0:대번지, a1:소번지)
bool _searchTown(JA_AREA_PACKET* town,int x, int y,int tolerance);
public:
FMAddress();
~FMAddress();
static FMAddress* instance()
{
static FMAddress * _instance = 0;
if ( _instance == 0 ) {
_instance = new FMAddress();
}
return _instance;
}
bool isOpened() {
return (_areas != NULL && _strings != NULL && _file != NULL);
}
bool open(); // 파일열기 (실행파일경로 or 테스트 경로)
bool open(QString path); // 파일열기
// 탐색 (경위도, 도도부현, 시구군, 읍면동, 탐색거리, 허용 오차m)
bool search(double lon,double lat,wchar_t** pref,wchar_t** city,wchar_t** town,int* a0, int*a1, int* pdist, int tolerance = 200);
// 탐색 결과 도도부현,시군구,읍면동,번지,호,거리
bool search(double lon, double lat,QStringList& result, int tolerance = 200);
#if (USE_JP_ADDRESS_TOOL)
void debugList(); // 디버그
void _debugTown(JA_AREA_PACKET* town); //
public:
bool convert(QString src, QString target);
bool verify(QString src); // TXT 데이터와 비교
// CODE,PREF,CITY,CHOME,A0,A1,LON,LAT,CDATE
static QMap<QString,uint32_t> _stringTable;
private:
static void _updateBound(_JA_AREA* dest, _JA_AREA* src);
static void _updateBoundP(_JA_AREA* dest, _JA_JIBUN* src);
static void _initBound(_JA_AREA* dest);
static void _calculateBitPack(_JA_AREA * src); // , uint8_t* byte
static void _copyPacket(JA_AREA_PACKET* dest, JA_AREA* src);
void _freeConvertData();
void _resizeChome(JA_AREA* chome); // MAX 크기로 할당된 지번을 원래 개수대로 변경
void _createStatCSV(); // 통계 생성
void _saveData();
void _saveStringTable();
void _savePref(); // 도도부현 저장
void _sortJIBUN(JA_AREA* chome); // 지번 XY 순으로 정렬
bool _packJibun(JA_AREA* town,uint8_t* buffer,uint32_t* offset); // 지번 BIT PACK
uint32_t _totalJibunBytes; // 전체 지번 할당 데이터
uint32_t _writeOffset; // 현재 파일 포인터
FILE* _outFile; // 파일
uint32_t _totalStringBufferLength; // 전체 문자열 버퍼 크기(중복제거)
uint32_t _totalStringLength; // 전체 문자열 크기
uint32_t _currentStringOffset; // 현재 문자열 버퍼 옵셋
void _setStringOffset(JA_AREA* area,QString name); // 문자열 추가 + 옵셋처리
// 현재 처리중인 도도부현,...
QString _currentPrefName;
QString _currentCityName;
QString _currentChomeName;
JA_AREA* _prefs; // 도도부현 저장
JA_AREA* _currentPref; // 현재 도도부현
JA_AREA* _currentCity; // " 시군구
JA_AREA* _currentChome; // " 마을
JA_JIBUN* _currentJibun; // " 지번
int _maxLenPref; // 통계 확인용
int _maxLenCity; // "
int _maxLenChome; // "
int _maxA0; // " 지번 대번지 최대값
int _maxA1; // " " 소번지
#endif // USE_JP_ADDRESS_TOOL
};
#endif // USE_JP_ADDRESS
#endif // FM_ADDRESS_H

View File

@@ -0,0 +1,151 @@
#include "fm_parse_gps.h"
bool FMParseGPS::ParseRMC(char szSentence[], NMEA_INFO* data, int nPacketSize)
{
Q_UNUSED(nPacketSize);
// qDebug() << szSentence;
char szItem[NMEA_TOKEN_SIZE]={0,};
long nTemp;
// 의미없으니 하지 말자
// DWORD dwCheckSum = 0;
//for (i = 1; i < strlen(szSentence) && szSentence[i] != '*'; i++)
// dwCheckSum ^= szSentence[i];
//NMEA_INFO* data = &_gpsData[index];
//0th
szSentence = _GetNextToken(szSentence, szItem);
// 1th
char szUtc[11] = {0,};
szSentence = _GetNextToken(szSentence, szItem);
strncpy(szUtc, szItem, sizeof(szUtc));
// 2th
szSentence = _GetNextToken(szSentence, szItem);
char cStatus = szItem[0];
// 3th
szSentence = _GetNextToken(szSentence, szItem);
data->Latitude = atof(szItem);
// 4th
szSentence = _GetNextToken(szSentence, szItem);
unsigned char cLatitudeDir = szItem[0];
// 5th
szSentence = _GetNextToken(szSentence, szItem);
data->Longitude = atof(szItem);
// 6th
szSentence = _GetNextToken(szSentence, szItem);
unsigned char cLongitudeDir = szItem[0];
// 7th
szSentence = _GetNextToken(szSentence, szItem);
double speed = atof(szItem);
// 8th
szSentence = _GetNextToken(szSentence, szItem);
double track = atof(szItem);
// 9th
char szDate[7] = {0,};
szSentence = _GetNextToken(szSentence, szItem);
strncpy(szDate, szItem, sizeof(szDate));
// 10th
szSentence = _GetNextToken(szSentence, szItem);
// double Mag = atof(szItem); // Magnetic variation
// 11th
szSentence = _GetNextToken(szSentence, szItem);
// unsigned char cMagDir = szItem[0];
// Set GPS Info
if (strlen(szUtc) >= 6)
{
data->nHour = (szUtc[0] - 48) * 10 + (szUtc[1] - 48);
data->nMin = (szUtc[2] - 48) * 10 + (szUtc[3] - 48);
data->nSec = (szUtc[4] - 48) * 10 + (szUtc[5] - 48);
}
if (strlen(szDate) >= 6)
{
data->nDay = (szDate[0] - 48) * 10 + (szDate[1] - 48);
data->nMonth = (szDate[2] - 48) * 10 + (szDate[3] - 48);
data->nYear = (szDate[4] - 48) * 10 + (szDate[5] - 48);
if (data->nYear > 80)
{
data->nYear += 1900;
}
else
{
data->nYear += 2000;
}
}
// printf("date:%d-%d-%d %d:%d:%d\n",data->nYear,data->nMonth,data->nDay,data->nHour,data->nMin,data->nSec);
data->nStatus = (cStatus == 'A') ? 1 : 0;
data->Latitude = data->Latitude / 100.0;
nTemp = (long)data->Latitude;
data->Latitude = (double)nTemp + (double)(data->Latitude - nTemp) * 100.0 / 60.0;
if (cLatitudeDir == 'S')
{
data->Latitude *= -1;
}
data->Longitude = data->Longitude / 100.0;
nTemp = (long)data->Longitude;
data->Longitude = (double)nTemp + (double)(data->Longitude - nTemp) * 100.0 / 60.0;
if (cLongitudeDir == 'W')
{
data->Longitude *= -1;
}
data->Speed = speed * 1.852; // 1 knots는 1해리를 1시간에 가는 속력으로 1해리는 1852m
data->nAngle = (uint8_t) track;
// // data->nAngle = (long) track;
// if((data->nAngle-track>5 || data->nAngle-track<-5) ||
// (_nLastAngle[1]==_nLastAngle[0] && _nLastAngle[0] == track ))
// {
// data->nAngle = (uint8_t) track;
// }
// else
// {
// _nLastAngle[1] = _nLastAngle[0];
// _nLastAngle[0] = (long)track;
// }
return true;
}
char* FMParseGPS::_GetNextToken(char* lpSentence, char* lpToken, int iTokenSize)
{
lpToken[0] = '\0';
if (lpSentence == NULL || lpSentence[0] == '\0')
{
return NULL;
}
if (lpSentence[0] == ',')
{
return lpSentence + 1;
}
iTokenSize--;
while( *lpSentence != ',' &&
*lpSentence != '\0' &&
*lpSentence != '*'&& iTokenSize > 0)
{
*lpToken = *lpSentence;
lpToken++;
lpSentence++;
iTokenSize--;
}
lpSentence++;
*lpToken = '\0';
return lpSentence;
}

View File

@@ -0,0 +1,15 @@
#ifndef FM_PARSE_GPS_H
#define FM_PARSE_GPS_H
#include "rm_sensordata.h"
#define NMEA_TOKEN_SIZE 51
class FMParseGPS
{
public:
static bool ParseRMC(char szSentence[], NMEA_INFO* data, int nPacketSize);
private:
static char* _GetNextToken(char* lpSentence, char* lpToken, int iTokenSize = NMEA_TOKEN_SIZE);
};
#endif // FM_PARSE_GPS_H

View File

@@ -0,0 +1,461 @@
#include "fm_video_edit.h"
#if (FM_VIDEO_EDIT)
#if (USE_LIB_MP4V2)
#include "mp4v2.h"
#else
#ifdef __cplusplus
extern "C"
{
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
}
#endif // extern C
#endif //USE_LIB_MP4V2
#define MAX_VIDEO_STREAM_COUNT 10
FMVideoEdit::FMVideoEdit(QStringList files, QStringList dests, EditMode mode, bool deleteSrcDone)
{
_files = files;
_dests = dests;
_mode = mode;
_deleteSrcAfter = deleteSrcDone;
}
void FMVideoEdit::run()
{
_splitMP4VideoTracks();
}
int _find_video_stream_index(int stream_id, int *streams) {
for(int i=0;i<MAX_VIDEO_STREAM_COUNT;i++) {
if(stream_id == streams[i]) {
return i;
}
}
return -1;
}
void _clean_output_contexts(AVFormatContext** ctxs) {
for(int i=0;i<MAX_VIDEO_STREAM_COUNT;i++) {
if (ctxs[i] != NULL) {
avformat_free_context(ctxs[i]);
}
}
}
void FMVideoEdit::_splitMP4VideoTracks()
{
QString src = _files.first();
AVFormatContext *input_ctx = NULL;
int video_stream_index[MAX_VIDEO_STREAM_COUNT] = {-1,};
bool video_stream_validation[MAX_VIDEO_STREAM_COUNT] = {true,}; // 정상 Video Stream 확인
int video_stream_count = 0;
int real_video_stream_count = 0; // 실제 비디오 스트림 개수
int audio_stream_index = -1; // Audio 가 존재하지 않을 경우 stream index 를 2가 아닌 1로 작성하기 위해 사용
int video_frame_count = -1; // Progress 처리하기 위해 사용
// 입력 파일 열기
if (avformat_open_input(&input_ctx, src.toUtf8().constData(), NULL, NULL) < 0) {
emit done(ErrorFileOpen);
return;
}
// 메타 정보 확인
// major_brand : isom
// minor_version : 512
// compatible_brands : isomiso2avc1mp41
/*
AVDictionaryEntry *tag = NULL;
while ((tag = av_dict_get(input_ctx->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
qInfo() << tag->key << ":" << tag->value << __FUNCTION__;
}
*/
// 스트림 정보 읽기
if (avformat_find_stream_info(input_ctx, NULL) < 0) {
emit done(ErrorOpenStream);
avformat_close_input(&input_ctx);
return;
}
// 각 비디오 스트림 + 오디오/자막 스트림 인덱스 확인
for (unsigned int i = 0; i < input_ctx->nb_streams; i++) {
AVMediaType type = input_ctx->streams[i]->codecpar->codec_type;
if (type == AVMEDIA_TYPE_VIDEO) {
// 프레임이 없는 N 번째 영상 스트림 제거
video_stream_validation[i] = (input_ctx->streams[i]->nb_frames > 0);
// if(input_ctx->streams[i]->nb_frames == 0) {
// video_stream_validation[i] = false;
// }
if(video_stream_validation[i])
{
real_video_stream_count += 1;
}
// Progress 처리하기 위해
if(video_frame_count < 0) {
video_frame_count = input_ctx->streams[i]->nb_frames;
}
// AVStream* ps = input_ctx->streams[i];
// qInfo() << "VSTREAM:" << i << ps->discard << __FUNCTION__;
video_stream_index[video_stream_count] = i;
video_stream_count++;
} else if (type == AVMEDIA_TYPE_AUDIO) {
audio_stream_index = i;
} //else if (type == AVMEDIA_TYPE_SUBTITLE) {
// subtitle_stream_index = i;
//}
}
// 실제 영상 스트림이 출력 개수보다 많을 경우에만 처리
//video_stream_count = 2;
if(real_video_stream_count > _dests.size()) {
emit done(ErrorWrongTrackCount);
avformat_close_input(&input_ctx);
return;
}
// 한번에 모든 패킷을 여러 파일에 저장하기 위해 동시 생성
AVFormatContext *output_ctx[MAX_VIDEO_STREAM_COUNT] = {NULL,};
AVStream *out_video_stream[MAX_VIDEO_STREAM_COUNT] = {NULL,};
AVStream *out_audio_stream[MAX_VIDEO_STREAM_COUNT] = {NULL,};
AVStream *out_subtitle_stream[MAX_VIDEO_STREAM_COUNT] = {NULL,};
for(int s=0;s<video_stream_count;s++) {
// 프레임 없는 영상 스트림 SKIP
if(!video_stream_validation[s]) {
continue;
}
// 복사할 비디오 스트림 인덱스
// int current_video_stream = video_stream_index[s];
// 출력 파일 생성
//AVFormatContext *output_ctx = NULL;
QString destString = _dests.at(s);
char dest[4096] = {0,};
memcpy(dest,destString.toUtf8().constData(),destString.toUtf8().length());
// MP4는 PCM 데이터를 저장하지 못하므로 확장자는 MP4 이나 강제로 MOV 포멧으로 지정
avformat_alloc_output_context2(&output_ctx[s], NULL, "mov", dest);
if (!output_ctx[s]) {
emit done(ErrorFileCreate);
avformat_close_input(&input_ctx);
_clean_output_contexts(output_ctx);
return;
}
// 비디오 스트림 생성
out_video_stream[s] = avformat_new_stream(output_ctx[s], NULL);
if (!out_video_stream[s]) {
emit done(ErrorCreateStream);
_clean_output_contexts(output_ctx);
avformat_close_input(&input_ctx);
return;
}
// 스트림 파라미터 복사
int ret = avcodec_parameters_copy(out_video_stream[s]->codecpar, input_ctx->streams[video_stream_index[s]]->codecpar);
if (ret < 0) {
emit done(ErrorCopyStreamParameters);
_clean_output_contexts(output_ctx);
avformat_close_input(&input_ctx);
return;
}
out_video_stream[s]->codecpar->codec_tag = 0;
// 나머지 스트림들 복사
for (unsigned int i = 0; i < input_ctx->nb_streams; i++) {
AVMediaType type = input_ctx->streams[i]->codecpar->codec_type;
if (type == AVMEDIA_TYPE_AUDIO) {
out_audio_stream[s] = avformat_new_stream(output_ctx[s], NULL);
if (!out_audio_stream[s]) {
emit done(ErrorCreateStream);
_clean_output_contexts(output_ctx);
avformat_close_input(&input_ctx);
return;
}
ret = avcodec_parameters_copy(out_audio_stream[s]->codecpar, input_ctx->streams[i]->codecpar);
if (ret < 0) {
emit done(ErrorCopyStreamParameters);
_clean_output_contexts(output_ctx);
avformat_close_input(&input_ctx);
return;
}
out_audio_stream[s]->codecpar->codec_tag = 0;
}
// 자막 스트림 복사
if (type == AVMEDIA_TYPE_SUBTITLE) {
out_subtitle_stream[s] = avformat_new_stream(output_ctx[s], NULL);
if (!out_subtitle_stream[s]) {
emit done(ErrorCreateStream);
_clean_output_contexts(output_ctx);
avformat_close_input(&input_ctx);
return;
}
ret = avcodec_parameters_copy(out_subtitle_stream[s]->codecpar, input_ctx->streams[i]->codecpar);
if (ret < 0) {
emit done(ErrorCopyStreamParameters);
_clean_output_contexts(output_ctx);
avformat_close_input(&input_ctx);
return;
}
out_subtitle_stream[s]->codecpar->codec_tag = 0;
}
} // 나머지 스트림 복사
// 출력 파일 헤더 쓰기
av_dump_format(output_ctx[s], 0, dest, 1);
if (!(output_ctx[s]->oformat->flags & AVFMT_NOFILE)) {
if (avio_open(&output_ctx[s]->pb, dest, AVIO_FLAG_WRITE) < 0) {
emit done(ErrorFileWrite);
_clean_output_contexts(output_ctx);
avformat_close_input(&input_ctx);
return;
}
}
// major_brand를 'isom'으로 설정
// AVDictionary *options = NULL;
// av_dict_set(&options, "major_brand", "isom", 0);
// av_dict_set(&options, "compatible_brands", "isomiso2avc1mp41", 0);
//&options
int res = avformat_write_header(output_ctx[s], NULL); // &options
if (res < 0)
{
//char ebuffer[1024] = {0,};
//av_strerror(res,ebuffer,1024);
qInfo() << "VIDEO STREAM:" << s << " HEADER WRITE ERROR" << __FUNCTION__;
emit done(ErrorFileWrite);
_clean_output_contexts(output_ctx);
avformat_close_input(&input_ctx);
// av_dict_free(&options);
return;
}
} // 파일 생성 및 스트림 정보 복사
// 패킷 복사
AVPacket packet;
// for 문으로 처리시 처음으로 다시 돌아가는 기능 필요...
// av_seek_frame(input_ctx, -1, 0, AVSEEK_FLAG_BACKWARD);
int frame_processed = 0; // Progress 처리
while (av_read_frame(input_ctx, &packet) >= 0) {
AVMediaType type = input_ctx->streams[packet.stream_index]->codecpar->codec_type;
// 입력 스트림과 출력 스트림의 타임베이스 정보를 가져옵니다.
const AVRational& in_time_base = input_ctx->streams[packet.stream_index]->time_base;
if(type == AVMEDIA_TYPE_VIDEO) {
// video 순서 index 확인
int vindex = _find_video_stream_index(packet.stream_index,video_stream_index);
if(!video_stream_validation[vindex]) {
continue;
}
// 시간계산 비디오 스트림 인덱스는 무조건 0
const AVRational& out_time_base = output_ctx[vindex]->streams[0]->time_base;
// 모든 패킷 RESCALE 하지 않으면 재생시간 짧아짐
packet.pts = av_rescale_q(packet.pts, in_time_base, out_time_base);
packet.dts = av_rescale_q(packet.dts, in_time_base, out_time_base);
packet.duration = av_rescale_q(packet.duration, in_time_base, out_time_base);
packet.stream_index = 0; // 출력 비디오 스트림 인덱스
av_interleaved_write_frame(output_ctx[vindex], &packet);
if(vindex == 0 && video_frame_count > 0) {
frame_processed++;
int percent = int (((double)frame_processed) / ((double)video_frame_count) * 100.0);
emit progress(percent);
//qInfo() << progress << __FUNCTION__;
}
} else if (type == AVMEDIA_TYPE_AUDIO) {
// 패킷을 여러번 쓰기 위해서는 av_packet_clone 해서 처리해야함..
for(int s=0;s<video_stream_count;s++) { // 모든 파일에 오디오 패킷 쓰기
if(!video_stream_validation[s]) { // invalid video 스트림 SKIP
continue;
}
// 패킷 시간 RESCALE 처리해아함 // AUDIO STREAM INDEX 는 무조건 1
const AVRational& out_time_base = output_ctx[s]->streams[1]->time_base;
AVPacket* cp = av_packet_clone(&packet);
// 모든 패킷 RESCALE 하지 않으면 재생시간 짧아짐
cp->pts = av_rescale_q(cp->pts, in_time_base, out_time_base);
cp->dts = av_rescale_q(cp->dts, in_time_base, out_time_base);
cp->duration = av_rescale_q(cp->duration, in_time_base, out_time_base);
cp->stream_index = 1; //출력 오디오 스트림 인덱스
av_interleaved_write_frame(output_ctx[s], cp);
av_packet_free(&cp);
}
} else if (type == AVMEDIA_TYPE_SUBTITLE) {
// AUDIO 가 존재하지 않을경우 1 존재할 경우 2
const int subtitle_stream_index = audio_stream_index >= 0 ? 2 : 1;
for(int s=0;s<video_stream_count;s++) { // 모든 파일에 자막 패킷 쓰기
if(!video_stream_validation[s]) { // invalid video 스트림 SKIP
continue;
}
// 패킷 시간 RESCALE 처리해아함
const AVRational& out_time_base = output_ctx[s]->streams[subtitle_stream_index]->time_base;
AVPacket* cp = av_packet_clone(&packet);
// 모든 패킷 RESCALE 하지 않으면 재생시간 짧아짐 for 에서 누적되니 신규 packet 에만 처리
cp->pts = av_rescale_q(cp->pts, in_time_base, out_time_base);
cp->dts = av_rescale_q(cp->dts, in_time_base, out_time_base);
cp->duration = av_rescale_q(cp->duration, in_time_base, out_time_base);
cp->stream_index = subtitle_stream_index;
av_interleaved_write_frame(output_ctx[s], cp);
av_packet_free(&cp);
}
}
av_packet_unref(&packet);
}
for(int s=0;s<video_stream_count;s++) {
if(!video_stream_validation[s]) { // invalid video 스트림 파일 SKIP
continue;
}
// 트레일러 쓰기 및 파일 닫기
av_write_trailer(output_ctx[s]);
if (!(output_ctx[s]->oformat->flags & AVFMT_NOFILE)) {
avio_closep(&output_ctx[s]->pb);
}
//av_dict_free(&options);
avformat_free_context(output_ctx[s]);
}
avformat_close_input(&input_ctx);
_afterProcess();
emit done(ErrorNone);
}
void FMVideoEdit::_afterProcess()
{
if(_deleteSrcAfter) {
for(int i=0;i<_files.size();i++) {
QFile(_files.at(i)).remove();
qInfo() << "DELETE:" << _files.at(i) << __FUNCTION__;
}
}
}
#if (USE_LIB_MP4V2)
void FMVideoEdit::_splitVideoTracks()
{
QString src = _files.first();
/* 최적화 하여 마지막 이상한 atom 제거해도 수정이 안됨
QFileInfo fi = QFileInfo(src2);
QString src = QDir::cleanPath(fi.dir().path() + QDir::separator() + fi.baseName() + "_2.mp4");
if(!MP4Optimize(src2.toLocal8Bit().data(),src.toLocal8Bit().data())) {
//qInfo() << "OPTIMIZE FAILED.." << __FUNCTION__;
return;
} */
MP4FileHandle inputFile = MP4Read(src.toLocal8Bit().data());
if(MP4_INVALID_FILE_HANDLE == inputFile) {
emit done(ErrorFileOpen);
return;
}
uint32_t numTrack = MP4GetNumberOfTracks(inputFile,NULL,0);
//qInfo() << QString(MP4Info(mp4File)) << __FUNCTION__;
qInfo() << "numTrack" << numTrack << __FUNCTION__;
QList<MP4TrackId> videoTracks = QList<MP4TrackId>();
QList<MP4TrackId> otherTracks = QList<MP4TrackId>(); // 영상을 제외한 트랙들
for(uint32_t i=0;i<numTrack;i++) {
MP4TrackId tid = MP4FindTrackId (inputFile, i, NULL,0);
const char* type = MP4GetTrackType(inputFile,tid);
if(strcmp(type,MP4_VIDEO_TRACK_TYPE) == 0) {
videoTracks.append(tid);
qInfo() << "TRACK:" << i << " ID:" << tid << " TYPE:" << type << __FUNCTION__;
} else if (strcmp(type,MP4_AUDIO_TRACK_TYPE) != 0) { // else { //
// 오디오 트랙만 처리하면 atom size 에러가 발생함...
otherTracks.append(tid);
qInfo() << "OTHER TRACK:" << i << " ID:" << tid << " TYPE:" << type << __FUNCTION__;
}
}
if(videoTracks.size() != _dests.size()) {
emit done(ErrorWrongTrackCount);
MP4Close(inputFile);
}
for(int i=0;i<_dests.size();i++) {
QString dest = _dests.at(i);
MP4FileHandle outputFile = MP4Create(dest.toLocal8Bit().data());
if (outputFile == MP4_INVALID_FILE_HANDLE) {
emit done(ErrorFileCreate);
MP4Close(inputFile);
return;
}
// 입력 파일의 일반적인 정보를 출력 파일에 복사
MP4SetTimeScale(outputFile, MP4GetTimeScale(inputFile));
QList<MP4TrackId> cloneTracks = QList<MP4TrackId>();
cloneTracks.append(videoTracks.at(i)); // 영상 트랙
cloneTracks.append(otherTracks); // 나머지 트랙(자막,메타,음성)
qInfo() << "cloneTracks:" << cloneTracks << __FUNCTION__;
// 트랙 복제
for (int j=0;j<cloneTracks.size();j++) {
MP4TrackId srcID = cloneTracks.at(j);
MP4TrackId outputVideoTrackId = MP4CloneTrack(inputFile, srcID, outputFile);
qInfo() << "CLONE TRACK SRC:" << srcID << " OUT:" << outputVideoTrackId << __FUNCTION__;
if (outputVideoTrackId == MP4_INVALID_TRACK_ID) {
MP4Close(inputFile);
MP4Close(outputFile);
emit done(ErrorTrackClone);
return;
}
// 샘플 복사
uint32_t sampleId = 1;
MP4SampleId sampleCount = MP4GetTrackNumberOfSamples(inputFile, srcID);
while (sampleId <= sampleCount) {
uint8_t* sampleData = nullptr;
uint32_t sampleSize = 0;
MP4Timestamp startTime = 0;
MP4Duration sampleDuration = 0;
if (MP4ReadSample(inputFile, srcID, sampleId, &sampleData, &sampleSize,&startTime, &sampleDuration)) {
if (!MP4WriteSample(outputFile, outputVideoTrackId, sampleData, sampleSize, sampleDuration)) {
emit done(ErrorWriteSample);
MP4Free(sampleData);
break;
}
MP4Free(sampleData);
} else {
emit done(ErrorReadSample);
break;
}
sampleId++;
}
} // 각 트랙 복사
MP4Close(outputFile);
}
MP4Close(inputFile);
}
#endif // #if (USE_LIB_MP4V2)
#endif // #if (FM_VIDEO_EDIT)

View File

@@ -0,0 +1,75 @@
#ifndef FM_VIDEO_EDIT_H
#define FM_VIDEO_EDIT_H
#if (FM_VIDEO_EDIT)
#include <QtCore>
/**
* @brief 영상(MP4,AVI) 편집(FILE JOIN,TRACK SPLIT) 기능
* @example
* FMVideoEdit* ed = new FMVideoEdit(...,...);
connect(sp, SIGNAL(done()), SLOT(onSplitVideoDone()));
QThreadPool::globalInstance()->start(sp,LOADER_THREAD_PRIORITY);
*/
class FMVideoEdit : public QObject, public QRunnable
{
Q_OBJECT
public:
enum EditMode {
FileJoin,
VideoTrackSplit,
};
enum ErrorCode {
ErrorNone = 0,
ErrorWrongTrackCount,
ErrorFileOpen,
ErrorOpenStream,
ErrorFileCreate,
ErrorCreateStream,
ErrorCopyStreamParameters,
ErrorFileWrite,
};
/**
* @brief 생성
* @param files: 처리할 파일경로(들) - 동일 포맷(+코덱,etc)만 가능
* @param dests: 처리완료된 파일경로(들)
* @param mode: 처리 모드
*/
FMVideoEdit(QStringList files, QStringList dests, EditMode mode, bool deleteSrcDone = true);
virtual void run();
private:
QStringList _files; //! 처리할 파일(들)
QStringList _dests; //! 처리 결과물 파일 경로(들)
EditMode _mode; //! 처리 모드
bool _deleteSrcAfter; //! 처리 완료 후 소스 영상 삭제
/**
* @brief 멀티트랙 영상을 N개의 단일트랙 영상+오디오 파일로 저장
*/
void _splitMP4VideoTracks();
/**
* @brief 처리 완료 후 진행
* eg. 소스 파일 삭제 등
*/
void _afterProcess();
signals:
/**
* @brief 처리완료 (또는 ERROR)
* @param error: 에러코드, 0=성공
*/
void done(int error); //! 처리 완료 결과
/**
* @brief 처리 경과
* @param progress , 0~100%
*/
void progress(int progress); //! 처리 프로그래스
};
#endif // FM_VIDEO_EDIT
#endif // FM_VIDEO_EDIT_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,32 @@
#ifndef RM_AVIREPAIR_H
#define RM_AVIREPAIR_H
#include <QObject>
//#include "rm_constants.h"
class RMAVIRepair : public QObject
{
Q_OBJECT
public:
explicit RMAVIRepair(QObject *parent = 0);
static bool repair(QString& filePath, QString& destPath);
#if (SUPPORT_AVI_FIX_DURL)
static bool repairV50(QString& filePath, QString& destPath);
#endif
static bool clipToPair(QString& filePath, QString& pairfilePath);
#if (REPAIR_CHECK_SUBTITLE)
// 파일명과 비교하여 30분이상 차이나면 처리하지 않음
static bool compare_subtitle_time(QString& filePath, char* subtitle);
#endif
// 명칭뒤에 _f 추가
#if (REPAIR_FAILED_TAG)
static void rename_failed(QString& filePath);
#endif
signals:
public slots:
};
#endif // RM_AVIREPAIR_H

View File

@@ -0,0 +1,29 @@
#ifndef RM_FORMAT_H
#define RM_FORMAT_H
#if !defined(BBEXTRACT)
#include "../rm_include.h"
#endif
typedef struct _VideoPreInfo {
bool bDuration;
unsigned int duration;
#if (CHECK_VIDEO_BITRATE)
bool bMOVSize;
unsigned int movSize;
#endif
unsigned int width;
unsigned int height;
} VideoPreInfo;
typedef enum {
VideoReadSensor = 0, // 센서 데이터 읽어 오기
VideoReadDuration = 1, // 재생시간만 확인
#if (CHECK_VIDEO_BITRATE)
VideoReadMOVSize = 2, // duration + 'mov' 크기 확인 (후방카메라 연결되지 않은경우 확인)
#endif
} VideoReadMode;
#endif // RM_FORMAT_H

View File

@@ -0,0 +1,883 @@
#if (FILE_FORMAT_AVI)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if !defined(BBEXTRACT)
#include <QtCore>
#include "rm_constants.h"
#if (APPLICATION_PROFILE)
#include <QElapsedTimer>
#include <QDebug>
#endif
#endif //#if !defined(BBEXTRACT)
#include "rm_format_avi.h"
extern "C" {
#include "fileio.h"
}
// 참조: https://github.com/masayukig/jpegtoavi/blob/master/aviformat.txt
// http://telnet.or.kr/directx/htm/avirifffilereference.htm
// http://blog.naver.com/PostView.nhn?blogId=shlee7708&logNo=120121689464 <- 제일 편함!!
//static int skip_chunk(FILE *in)
//{
//char chunk_id[5];
//int chunk_size;
//int end_of_chunk;
// read_chars(_file,chunk_id,4);
// chunk_size=read_long(_file);
// printf("Uknown Chunk at %d\n",(int)ftell(_file));
// printf("-------------------------------\n");q
// printf(" chunk_id: %s\n",chunk_id);
// printf(" chunk_size: %d\n",chunk_size);
// printf("\n");
// end_of_chunk=ftell(_file)+chunk_size;
// if ((end_of_chunk%4)!=0)
// {
// end_of_chunk=end_of_chunk+(4-(end_of_chunk%4));
// }
// RMfseek(_file,end_of_chunk,SEEK_SET);
// return 0;
//}
//AVIRiff(RMfile in,bool durationOnly = false);
AVIRiff::AVIRiff(RMfile in,VideoReadMode mode, VideoPreInfo* info)
{
_readMode = mode;
_preInfo = info;
#if (SENSOR_AVI_SUBTITLE)
subtitles = NULL;
#endif
if(_preInfo != NULL) {
_preInfo->bDuration = false;
_preInfo->duration = 0;
#if (CHECK_VIDEO_BITRATE)
_preInfo->movSize = 0;
_preInfo->bMOVSize = false;
#endif
_preInfo->width = 0;
_preInfo->height = 0;
}
_isValid = false;
_file = in;
_videoStreamCount = 0;
#if (AVI_CHUNHO_SENSOR_FORMAT_1)
_gps_buffer = NULL;
_sensor_buffer = NULL;
_gps_buffer_size = 0;
_sensor_buffer_size = 0;
#endif
parse_riff();
}
AVIRiff::~AVIRiff()
{
for(int i=0;i<_videoStreamCount;i++)
{
if(_videoStreamFormat[i].palette != NULL)
{
free(_videoStreamFormat[i].palette);
}
}
#if (AVI_CHUNHO_SENSOR_FORMAT_1)
if(_gps_buffer != NULL)
{
free(_gps_buffer);
}
if(_sensor_buffer != NULL)
{
free(_sensor_buffer);
}
#endif
#if (SENSOR_AVI_SUBTITLE)
if(subtitles != NULL) {
for(int i=0;i<subtitles->size();i++) {
free(subtitles->at(i));
}
delete subtitles;
subtitles = NULL;
}
#endif
}
int AVIRiff::hex_dump_chunk(int chunk_len)
{
#if (USE_AVI_DUMP)
char chars[17];
int ch,n;
chars[16]=0;
int line = 0;
for (n=0; n<chunk_len; n++)
{
if ((n%16)==0)
{
if (n!=0)
{
printf("%s\n", chars);
}
printf("%04d ",line++);
memset(chars, ' ', 16);
}
ch=getc(_file);
if (ch==EOF)
{
break;
}
printf("%02x ", ch);
if (ch>=' ' && ch<=126)
{
chars[n%16]=ch;
}
else
{
chars[n%16]='.';
}
}
if ((n%16)!=0)
{
for (ch=n%16; ch<16; ch++)
{
printf(" ");
}
}
printf("%s\n", chars);
#else
#if !defined(BBEXTRACT)
Q_UNUSED(chunk_len);
#endif // #if !defined(BBEXTRACT)
#endif
return 0;
}
#if (DEBUG_AVI_FORMAT)
int AVIRiff::parse_idx1(int chunk_len)
{
index_entry_t index_entry;
int t;
printf(" IDX1\n");
printf(" -------------------------------\n");
printf(" ckid dwFlags dwChunkOffset dwChunkLength\n");
for (t=0; t<chunk_len/16; t++)
{
read_chars(_file,index_entry.ckid,4);
index_entry.dwFlags=read_long(_file);
index_entry.dwChunkOffset=read_long(_file);
index_entry.dwChunkLength=read_long(_file);
printf(" %s 0x%08x 0x%08x 0x%08x\n",
index_entry.ckid,
index_entry.dwFlags,
index_entry.dwChunkOffset,
index_entry.dwChunkLength);
}
printf("\n");
return 0;
}
#endif
int AVIRiff::read_avi_header() // ,struct avi_header_t *avi_header
{
// 연결되지 않은 영상 Bit rate : 121 kb/s
// 연결된 영상 Bit rate : 4 493 kb/s
//long offset=ftell(_file);
AVIHeader* avi_header = &_avi_header;
avi_header->TimeBetweenFrames=read_long(_file); // AVI TYPE 1 = 0
avi_header->MaximumDataRate=read_long(_file);
avi_header->PaddingGranularity=read_long(_file);
avi_header->Flags=read_long(_file);
avi_header->TotalNumberOfFrames=read_long(_file);
avi_header->NumberOfInitialFrames=read_long(_file);
avi_header->NumberOfStreams=read_long(_file);
avi_header->SuggestedBufferSize=read_long(_file);
avi_header->Width=read_long(_file);
avi_header->Height=read_long(_file);
avi_header->TimeScale=read_long(_file);
avi_header->DataRate=read_long(_file);
avi_header->StartTime=read_long(_file);
avi_header->DataLength=read_long(_file);
if(_preInfo != NULL)
{
_preInfo->width = avi_header->Width;
_preInfo->height = avi_header->Height;
}
#if (DEBUG_AVI_HEADER)
printf(" offset=0x%lx\n",offset);
printf(" TimeBetweenFrames: %d\n",avi_header->TimeBetweenFrames);
printf(" MaximumDataRate: %d\n",avi_header->MaximumDataRate);
printf(" PaddingGranularity: %d\n",avi_header->PaddingGranularity);
printf(" Flags: %d\n",avi_header->Flags);
printf(" TotalNumberOfFrames: %d\n",avi_header->TotalNumberOfFrames);
printf(" NumberOfInitialFrames: %d\n",avi_header->NumberOfInitialFrames);
printf(" NumberOfStreams: %d\n",avi_header->NumberOfStreams);
printf(" SuggestedBufferSize: %d\n",avi_header->SuggestedBufferSize);
printf(" Width: %d\n",avi_header->Width);
printf(" Height: %d\n",avi_header->Height);
printf(" TimeScale: %d\n",avi_header->TimeScale);
printf(" DataRate: %d\n",avi_header->DataRate);
printf(" StartTime: %d\n",avi_header->StartTime);
printf(" DataLength: %d\n",avi_header->DataLength);
fflush(stdout);
#endif
return 0;
}
void AVIRiff::print_data_handler(unsigned char *handler)
{
int t;
for (t=0; t<4; t++)
{
if ((handler[t]>='a' && handler[t]<='z') ||
(handler[t]>='A' && handler[t]<='Z') ||
(handler[t]>='0' && handler[t]<='9'))
{
printf("%c",handler[t]);
}
else
{
printf("[0x%02x]",handler[t]);
}
}
}
int AVIRiff::read_stream_header(stream_header_t *stream_header)
{
#if (DEBUG_AVI_STREAM_HEADER)
long offset=ftell(_file);
#endif
read_chars(_file,stream_header->DataType,4);
read_chars(_file,stream_header->DataHandler,4);
stream_header->Flags=read_long(_file);
stream_header->Priority=read_long(_file);
stream_header->InitialFrames=read_long(_file);
stream_header->TimeScale=read_long(_file);
stream_header->DataRate=read_long(_file);
stream_header->StartTime=read_long(_file);
stream_header->DataLength=read_long(_file);
stream_header->SuggestedBufferSize=read_long(_file);
stream_header->Quality=read_long(_file);
stream_header->SampleSize=read_long(_file);
if(_preInfo != NULL) {
if(_preInfo->duration == 0 && stream_header->TimeScale != 0)
{
_preInfo->bDuration = true;
_preInfo->duration = (unsigned int)(((double)(stream_header->TimeScale * 1000) / (double)stream_header->DataRate) * (double)stream_header->DataLength);
}
}
#if (DEBUG_AVI_STREAM_HEADER)
printf("------------------------------------------------------------\n");
printf(" offset=0x%lx\n",offset);
printf(" DataType: %s\n",stream_header->DataType);
printf(" DataHandler: ");
print_data_handler((unsigned char *)stream_header->DataHandler);
printf("\n");
printf(" Flags: %d\n",stream_header->Flags);
printf(" Priority: %d\n",stream_header->Priority);
printf(" InitialFrames: %d\n",stream_header->InitialFrames);
printf(" TimeScale: %d\n",stream_header->TimeScale);
printf(" DataRate: %d\n",stream_header->DataRate);
printf(" StartTime: %d\n",stream_header->StartTime);
printf(" DataLength: %d\n",stream_header->DataLength);
printf(" SuggestedBufferSize: %d\n",stream_header->SuggestedBufferSize);
printf(" Quality: %d\n",stream_header->Quality);
printf(" SampleSize: %d\n",stream_header->SampleSize);
fflush(stdout);
#endif
return 0;
}
int AVIRiff::read_stream_format(stream_format_t *stream_format)
{
int t,r,g,b;
#if (DEBUG_AVI_FORMAT)
long offset=ftell(_file);
#endif
stream_format->header_size=read_long(_file);
stream_format->image_width=read_long(_file);
stream_format->image_height=read_long(_file);
stream_format->number_of_planes=read_word(_file);
stream_format->bits_per_pixel=read_word(_file);
stream_format->compression_type=read_long(_file);
stream_format->image_size_in_bytes=read_long(_file);
stream_format->x_pels_per_meter=read_long(_file);
stream_format->y_pels_per_meter=read_long(_file);
stream_format->colors_used=read_long(_file);
stream_format->colors_important=read_long(_file);
stream_format->palette=0;
if (stream_format->colors_important!=0)
{
stream_format->palette= (int*) malloc(stream_format->colors_important*sizeof(int));
for (t=0; t<stream_format->colors_important; t++)
{
b=RMgetc(_file);
g=RMgetc(_file);
r=RMgetc(_file);
stream_format->palette[t]=(r<<16)+(g<<8)+b;
}
}
#if (DEBUG_AVI_FORMAT)
printf(" offset=0x%lx\n",offset);
printf(" header_size: %d\n",stream_format->header_size);
printf(" image_width: %d\n",stream_format->image_width);
printf(" image_height: %d\n",stream_format->image_height);
printf(" number_of_planes: %d\n",stream_format->number_of_planes);
printf(" bits_per_pixel: %d\n",stream_format->bits_per_pixel);
printf(" compression_type: %04x (%c%c%c%c)\n",stream_format->compression_type,
((stream_format->compression_type)&255),
((stream_format->compression_type>>8)&255),
((stream_format->compression_type>>16)&255),
((stream_format->compression_type>>24)&255));
printf(" image_size_in_bytes: %d\n",stream_format->image_size_in_bytes);
printf(" x_pels_per_meter: %d\n",stream_format->x_pels_per_meter);
printf(" y_pels_per_meter: %d\n",stream_format->y_pels_per_meter);
printf(" colors_used: %d\n",stream_format->colors_used);
printf(" colors_important: %d\n",stream_format->colors_important);
fflush(stdout);
#endif
return 0;
}
int AVIRiff::read_stream_format_auds(stream_format_auds_t *stream_format)
{
#if (DEBUG_AVI_FORMAT)
long offset=RMftell(_file);
#endif
stream_format->format=read_word(_file);
stream_format->channels=read_word(_file);
stream_format->samples_per_second=read_long(_file);
stream_format->bytes_per_second=read_long(_file);
#if (DEBUG_AVI_FORMAT)
int block_align=read_word(_file);
#endif
stream_format->block_size_of_data=read_word(_file);
stream_format->bits_per_sample=read_word(_file);
//stream_format->extended_size=read_word(_file);
#if (DEBUG_AVI_FORMAT)
printf(" offset=0x%lx\n",offset);
printf(" format: %d\n",stream_format->format);
printf(" channels: %d\n",stream_format->channels);
printf(" samples_per_second: %d\n",stream_format->samples_per_second);
printf(" bytes_per_second: %d\n",stream_format->bytes_per_second);
printf(" block_align: %d\n",block_align);
printf(" block_size_of_data: %d\n",stream_format->block_size_of_data);
printf(" bits_per_sample: %d\n",stream_format->bits_per_sample);
#endif
return 0;
}
int AVIRiff::parse_hdrl_list()
{
// stream_format_auds_t stream_format_auds;
// stream_header_t stream_header_auds;
char chunk_id[5];
int chunk_size;
char chunk_type[5];
int end_of_chunk;
int next_chunk;
//long offset=RMftell(_file);
AVI_STREAM_TYPE stream_type = UNDEFINED_STREAM;
read_chars(_file,chunk_id,4);
chunk_size=read_long(_file);
read_chars(_file,chunk_type,4);
#if (DEBUG_AVI_FORMAT)
printf(" AVI Header LIST (id=%s size=%d type=%s offset=0x%lx)\n",chunk_id,chunk_size,chunk_type,offset);
printf(" {\n");
#endif
end_of_chunk=RMftell(_file)+chunk_size-4;
if ((end_of_chunk%4)!=0)
{
//printf("Adjusting end of chunk %d\n", end_of_chunk);
//end_of_chunk=end_of_chunk+(4-(end_of_chunk%4));
//printf("Adjusting end of chunk %d\n", end_of_chunk);
}
if (strcmp(chunk_id,"JUNK")==0)
{
RMfseek(_file,end_of_chunk,SEEK_SET);
#if (DEBUG_AVI_FORMAT)
printf(" }\n");
#endif
return 0;
}
while (RMftell(_file)<end_of_chunk)
{
//long offset=RMftell(_file);
read_chars(_file,chunk_type,4);
chunk_size=read_long(_file);
next_chunk=RMftell(_file)+chunk_size;
if ((chunk_size%4)!=0)
{
//printf("Chunk size not a multiple of 4?\n");
//chunk_size=chunk_size+(4-(chunk_size%4));
}
#if (DEBUG_AVI_FORMAT)
printf(" %.4s (size=%d offset=0x%lx)\n",chunk_type,chunk_size,offset);
printf(" {\n");
#endif
if (strcasecmp("strh",chunk_type)==0)
{
long marker=RMftell(_file);
char buffer[5];
read_chars(_file,buffer,4);
RMfseek(_file,marker,SEEK_SET);
if (strcmp(buffer, "vids")==0)
{
stream_type = VIDEO_STREAM;
read_stream_header(&_videoStreamHeaders[_videoStreamCount]); // format 읽은후에 ++
}
else if (strcmp(buffer, "auds")==0)
{
stream_type = AUDIO_STREAM;
read_stream_header(&_audioStreamHeader);
}
else if (strcmp(buffer, "txts")==0)
{
stream_type = TXT_STREAM;
read_stream_header(&_txtStreamHeader);
}
else
{
#if (DEBUG_AVI_FORMAT)
printf("Unknown stream type %s\n", buffer);
#endif
return -1;
}
}
else if (strcasecmp("strf",chunk_type)==0)
{
if (stream_type == VIDEO_STREAM)
{
read_stream_format(&_videoStreamFormat[_videoStreamCount]);
_videoStreamCount++;
}
else if (stream_type==1)
{
read_stream_format_auds(&_audioStreamFormat);
}
else if (stream_type==2) // subtitle
{
// STRF 에는 아무 정보도 없음
//hex_dump_chunk(in,371);
//read_stream_format_auds(in,&stream_format_auds);
}
}
else if (strcasecmp("strd",chunk_type)==0)
{
//RMfseek(_file,offset,SEEK_SET);
//read_strd_format(chunk_size);
//_stopProcessing = true;
}
else
{
#if (DEBUG_AVI_FORMAT)
printf(" Unknown chunk type: %s\n",chunk_type);
#endif
// skip_chunk(_file);
}
#if (DEBUG_AVI_FORMAT)
printf(" }\n");
#endif
RMfseek(_file,next_chunk,SEEK_SET);
}
//printf("@@@@ %ld %d\n", RMftell(_file), end_of_chunk);
#if (DEBUG_AVI_FORMAT)
printf(" }\n");
#endif
RMfseek(_file,end_of_chunk,SEEK_SET);
return 0;
}
#if (SENSOR_AVI_SUBTITLE)
// 자막 데이터 AVI TYPE2
int AVIRiff::add_subtitle(long chunkSize)
{
if(subtitles == NULL) {
subtitles = new QList<char*>();
}
char* buffer = (char*)malloc(chunkSize + 1);
memset(buffer,0,chunkSize + 1);
RMfread(buffer,chunkSize,1,_file);
//qInfo() << buffer;
//QString str = QString(buffer);
subtitles->append(buffer);
//free(buffer);
return 0;
}
#endif
int AVIRiff::parse_movi_list(unsigned int size)
{
#if (APPLICATION_PROFILE)
QElapsedTimer timer;
timer.start();
#endif
char chunk_id[5] = {0,};
long chunk_size;
long end_of_chunk;
long offset=RMftell(_file);
#if (DEBUG_AVI_MOVI)
printf(" AVI MOVI Chunk (id=%s size=%d offset=0x%lx)\n",chunk_id,chunk_size,offset);
printf(" {\n");
fflush(stdout);
#endif
//int txtCount = 0;
while(RMftell(_file)< offset+(long)size-4)
{
read_chars(_file,chunk_id,4); // 4 byte
chunk_size=read_long(_file); // 4 byte
// pack 하지 않는다.. 다음 chunk 의 chunk_id + size 제외하고 이동
end_of_chunk= RMftell(_file) + chunk_size;
// H265 AVI 는 2 BYTE pack 되어 있음..
if ((end_of_chunk % 2) == 1)
{
end_of_chunk += 1;
}
if(chunk_id[2] == 't' && chunk_id[3] == 'x')
{
#if (SENSOR_AVI_SUBTITLE)
add_subtitle(chunk_size);
#endif
#if (DEBUG_AVI_MOVI)
printf(" MOVI TXT (id(%04d)=%s size=%d offset=%ld)\n",txtCount++,chunk_id,chunk_size,ftell(_file));
// hex_dump_chunk(chunk_size);
fflush(stdout);
#endif
}
else
{
// 이후 NAL SEI 처리 기능 추가
}
RMfseek(_file,end_of_chunk,SEEK_SET);
}
#if (DEBUG_AVI_MOVI)
printf(" }\n");
fflush(stdout);
#endif
#if (APPLICATION_PROFILE)
qInfo () << "Elapsed Time(parse_movi_list):" << timer.elapsed() << " msec" << " count:" << txtCount;
#endif
return 0;
}
//int AVIRiff::parse_hdrl(stream_format_t *stream_format, unsigned int size)
int AVIRiff::parse_hdrl(unsigned int size)
{
char chunk_id[5];
long chunk_size;
long end_of_chunk;
long offset=RMftell(_file);
read_chars(_file,chunk_id,4);
chunk_size=read_long(_file);
#if (DEBUG_AVI_FORMAT)
printf(" AVI Header Chunk (id=%s size=%d offset=0x%lx)\n",chunk_id,chunk_size,offset);
printf(" {\n");
#endif
end_of_chunk=RMftell(_file)+chunk_size;
if ((end_of_chunk%4)!=0) // 4BYTE PACK
{
end_of_chunk=end_of_chunk+(4-(end_of_chunk%4));
}
read_avi_header(); // 메인 헤더 읽기
#if (DEBUG_AVI_FORMAT)
printf(" }\n");
#endif
while(RMftell(_file)<offset+(long)size-4)
{
//printf("Should end at 0x%lx 0x%lx\n",offset+size,RMftell(_file));
parse_hdrl_list();
//parse_hdrl_list(stream_header,stream_format);
}
return 0;
}
int AVIRiff::parse_riff()
{
_isValid = false; // TAG 문제시만..
// 개별 chunk 처리
char chunk_id[5] = {0,};
int chunk_size;
char chunk_type[5] = {0,};
int end_of_chunk, end_of_subchunk;
//struct avi_header_t avi_header;
// stream_header_t stream_header;
// stream_format_t stream_format={0};
read_chars(_file,chunk_id,4);
chunk_size=read_long(_file);
read_chars(_file,chunk_type,4);
#if (DEBUG_AVI_FORMAT)
long offset=RMftell(_file);
printf("RIFF Chunk (id=%s size=%d type=%s offset=0x%lx)\n",chunk_id,chunk_size,chunk_type, offset);
printf("{\n");
#endif
if (strcasecmp("RIFF",chunk_id)!=0)
{
//printf("Not a RIFF file.\n");
return 1;
}
else if (strcasecmp("AVI ",chunk_type)!=0)
{
//printf("Not an AVI file.\n");
return 1;
}
end_of_chunk=RMftell(_file)+chunk_size-4;
#if (AVI_CHUNHO_SENSOR_FORMAT_1)
// Sensor 는 STRD 와 유사하게 end_of_chunk 뒤에 위치함
if(_preInfo == NULL)
{
RMfseek(_file,end_of_chunk,SEEK_SET);
bool b_gps_read = false;
bool b_sensor_read = false;
// 파일 끝까지 확인
int tryCount = 0;
while (RMfeof(_file) == 0 && tryCount++ < 100)
{
int offset = RMftell(_file);
#if !defined(BBEXTRACT)
QString offsetStr;
offsetStr.sprintf("%08X",offset);
#endif
read_chars(_file,chunk_id,4); // ID
chunk_size=read_long(_file); // 크기
// 센서 데이터 깨진 파일이 존재함 (chunck offset 이 짧거나..)
// qInfo() << "chunk_id:" << chunk_id << " chunk_size:" << chunk_size << " offset:" << offsetStr;
//end_of_subchunk=RMftell(_file)+chunk_size; // 범위 지정
//IDIT : 9355386 = 20
//gpsa : 9355398 = 4
//gps0 : 9357326 = 1920
//gsea : 9357354 = 20
//gsen : 9361576 = 4214
// 14 BYTE
if (strcasecmp("gps0",chunk_id)==0)
{
_gps_buffer_size = chunk_size;
_gps_buffer = (uint8_t*)malloc(chunk_size);
RMfread(_gps_buffer,_gps_buffer_size,1,_file);
b_gps_read = true;
}
else if (strcasecmp("gsen",chunk_id)==0)
{
if(chunk_size > 0) {
//qInfo() << "gsen offset:" << offsetStr;
_sensor_buffer_size = chunk_size;
_sensor_buffer = (uint8_t*)malloc(chunk_size);
RMfread(_sensor_buffer,_sensor_buffer_size,1,_file);
b_sensor_read = true;
}
}
// 깨진 데이터는 1 BYTE 씩 탐색
else if(chunk_size < 0 ||
(strcasecmp("IDIT",chunk_id) != 0 &&
strcasecmp("gpsa",chunk_id) != 0 &&
strcasecmp("gsea",chunk_id) != 0)) {
chunk_size = -7;
}
end_of_subchunk = offset + 8 + chunk_size;
if(b_sensor_read && b_gps_read)
{
break;
}
//qInfo() << chunk_id << ":" << end_of_subchunk << " =" << chunk_size;
RMfseek(_file,end_of_subchunk,SEEK_SET);
}
_isValid = true;
return 0;
}
#endif
while (RMftell(_file)<end_of_chunk)
{
read_chars(_file,chunk_id,4); // ID
chunk_size=read_long(_file); // 크기
end_of_subchunk=RMftell(_file)+chunk_size; // 범위 지정
if (strcasecmp("JUNK",chunk_id)==0 || strcasecmp("PAD ",chunk_id)==0) // ID 가 JUNK 또는 PAD 일 경우 ...
{
chunk_type[0]=0;
}
else
{
read_chars(_file,chunk_type,4); // 타입이 존재할 경우
}
#if (DEBUG_AVI_FORMAT)
long offset=RMftell(_file); // 현재 옵셋
printf(" New Chunk (id=%s size=%d type=%s offset=0x%lx)\n",chunk_id,chunk_size,chunk_type,offset);
printf(" {\n");
fflush(stdout);
#endif
if (strcasecmp("JUNK",chunk_id)==0 || strcasecmp("PAD ",chunk_id)==0) // 사용되지 않음
{
if ((chunk_size%4)!=0)
{
chunk_size=chunk_size+(4-(chunk_size%4));
}
#if (DEBUG_AVI_JUNK)
hex_dump_chunk(chunk_size);
#endif
}
else if (strcasecmp("INFO",chunk_type)==0) // 사용되지 않음..
{
if ((chunk_size%4)!=0)
{
chunk_size=chunk_size+(4-(chunk_size%4));
}
#if (DEBUG_AVI_INFO)
hex_dump_chunk(chunk_size);
#endif
}
else if (strcasecmp("hdrl",chunk_type)==0) // 헤더 리스트 처리
{
parse_hdrl(chunk_size);
// 기본 모드가 아닐 경우 종료 (subtitle 모델 체크는 subtitle 확인해야함)
// skip_chunk(_file);
}
else if (strcasecmp("movi",chunk_type)==0) // 'movi' 리스트 처리
{
#if (CHECK_VIDEO_BITRATE)
if(_readMode == AVIReadMOVSize && _preInfo != NULL) {
_preInfo->bMOVSize = true;
_preInfo->movSize = chunk_size;
}
#endif
// 처리할 필요 없음
#if (SENSOR_AVI_SUBTITLE)
if(_readMode == VideoReadSensor) {
parse_movi_list(chunk_size);
}
#endif
}
else if (strcasecmp("idx1",chunk_id)==0) // 인덱스 처리
{
RMfseek(_file,RMftell(_file)-4,SEEK_SET);
#if (DEBUG_AVI_FORMAT)
parse_idx1(chunk_size);
#endif
}
else
{
#if (DEBUG_AVI_FORMAT)
printf(" Unknown chunk at %d (%4s)\n",(int)RMftell(_file)-8,chunk_type);
#endif
if (chunk_size==0)
{
break;
}
}
// 필요한 정보 모두 수집되었는지 확인
if(_readMode == VideoReadDuration && _preInfo != NULL && _preInfo->bDuration == true) {
_isValid = true;
return 0;
}
#if (CHECK_VIDEO_BITRATE)
else if(_readMode == VideoReadMOVSize && _preInfo != NULL && _preInfo->bDuration == true && _preInfo->bMOVSize)
{
_isValid = true;
return 0;
}
#endif
RMfseek(_file,end_of_subchunk,SEEK_SET);
#if (DEBUG_AVI_FORMAT)
printf(" }\n");
#endif
}
#if (DEBUG_AVI_FORMAT)
printf("}\n");
#endif
_isValid = true;
return 0;
}
bool AVIRiff::duration(RMfile in, VideoPreInfo* info)
{
AVIRiff avi = AVIRiff(in,VideoReadDuration,info);
return avi.isValid();
}
#if (CHECK_VIDEO_BITRATE)
bool AVIRiff::movSize(RMfile in, VideoPreInfo* info)
{
AVIRiff avi = AVIRiff(in,VideoReadMOVSize,info);
return avi.isValid();
}
#endif
#endif // #if (FILE_FORMAT_AVI)

View File

@@ -0,0 +1,263 @@
#ifndef RM_FORMAT_AVI_H
#define RM_FORMAT_AVI_H
#if (FILE_FORMAT_AVI)
#if !defined(BBEXTRACT)
#include "../rm_include.h"
#include <QString>
#endif
#include "rm_format.h"
#pragma once
#include <stdio.h>
#include <vector>
#include <stdint.h>
#ifdef _MSC_VER
#define strncasecmp _strnicmp
#define strcasecmp _stricmp
#endif
extern "C" {
#include "fileio.h"
}
// DEBUG 용
#define DEBUG_AVI_FORMAT 0
#define USE_AVI_DUMP 0
#define DEBUG_AVI_JUNK 0
#define DEBUG_AVI_INFO 0
#define DEBUG_AVI_HEADER 0 // 메인헤더
#define DEBUG_AVI_STREAM_HEADER 0 // 스트림 헤더
#define DEBUG_AVI_MOVI 0 // 스트림 데이터 리스트
// http://www.econote.co.kr/main/view_post.asp?post_seq_no=49407 정리 잘 되어 있음
// AVI RIFF 구조는
// RIFF
// - hdrl(LIST)
// - avih : AVI 파일 전체 기본 정보
// - strl (LIST)
// - strh (vids) : 영상 스트림 헤더
// - strf " 포멧
// - strl (LIST)
// - strh (auds) : 음성 스트림 헤더
// - strf " 포멧
// - strl (LIST)
// - strh (txts) : 자막 스트림 헤더
// - strf " 포멧
// - IDIT
// - INFO(LIST) : 정보
// -ISFT : 업체정보
// - movi(LIST) : 실제 데이터
// - 00dc/b : 음성패킷 (c:압축,b:비압축)
// - 00wb/c : 영상패킷 (")
// - 00tx : 자막
// - idx1: 프레임의 위치 (binary 로 01wb,flag,offset,size ..... 00dc,flat,offset,size..
#define MAX_VIDEO_STREAM_COUNT 4
// TimeScale 등이 0 라 재생시간 처리에 문제가 생김
// avi 정보 (AVI Header,'avih')
typedef struct _AVIHeader
{
int TimeBetweenFrames;
int MaximumDataRate;
int PaddingGranularity;
int Flags;
int TotalNumberOfFrames;
int NumberOfInitialFrames;
int NumberOfStreams;
int SuggestedBufferSize;
int Width;
int Height;
int TimeScale;
int DataRate;
int StartTime;
int DataLength;
} AVIHeader;
// str(eam) 정보 (Stream Header,'strh') / Video('vids')
typedef struct _STRHeader
{
char DataType[5];
char DataHandler[5];
int Flags;
int Priority;
int InitialFrames;
int TimeScale;
int DataRate;
int StartTime;
int DataLength;
int SuggestedBufferSize;
int Quality;
int SampleSize;
} stream_header_t;
// str(eam) 포멧 (Stream Format,'strf') / Video('vids')
typedef struct _STRFormat
{
int header_size;
int image_width;
int image_height;
int number_of_planes;
int bits_per_pixel;
int compression_type;
int image_size_in_bytes;
int x_pels_per_meter;
int y_pels_per_meter;
int colors_used;
int colors_important;
int *palette;
} stream_format_t;
// str(eam) 정보 (Stream Header,'strh') / Audio('auds')
typedef struct _STRHeaderAudio
{
int format_type;
int number_of_channels;
int sample_rate;
int bytes_per_second;
int block_size_of_data;
int bits_per_sample;
int byte_count_extended;
}stream_header_auds_t;
// str(eam) 포멧 (Stream Format,'strf') / Video('auds')
typedef struct _STRFormatAudio
{
int header_size;
int format;
int channels;
int samples_per_second;
int bytes_per_second;
int block_size_of_data;
int bits_per_sample;
int extended_size;
} stream_format_auds_t;
// 인덱스 구조 ('idx1')
typedef struct _AVIDX
{
char ckid[5];
int dwFlags;
int dwChunkOffset;
int dwChunkLength;
} index_entry_t;
class AVIRiff
{
private:
VideoReadMode _readMode; // 현재 모드
VideoPreInfo* _preInfo; // 기본 정보 (존재할 경우 ReadMode == PreInfo)
bool _isValid; // 센서 관련?
public:
#if (SENSOR_AVI_SUBTITLE)
QList<char*>* subtitles; // 자막 센서 포멧
#endif
AVIRiff(RMfile in,VideoReadMode mode = VideoReadSensor, VideoPreInfo* info = NULL);
static bool duration(RMfile in,VideoPreInfo* info);
#if (CHECK_VIDEO_BITRATE)
static bool movSize(RMfile in,VideoPreInfo* info);
#endif
~AVIRiff();
bool isValid()
{
return _isValid;
}
// MOV 크기와 재생시간 그리고 오디오 bitrate (kb/sec) 를 입력받아 mov bitrate 를 계산한다
#if (CHECK_VIDEO_BITRATE)
static int videoBitrate(VideoPreInfo* info, double audioBitrate = 256) {
const double kb = 1000.0; // 1024?
const double durationInSec = (double)(info->duration) / 1000.0;
double audioSize = durationInSec * audioBitrate * kb / 8.0; // kb SEC
double videoSize = (double)(info->movSize) - audioSize;
return videoSize * 8.0 / durationInSec / kb;
}
#endif
#if (AVI_CHUNHO_SENSOR_FORMAT_1)
uint8_t* _gps_buffer;
size_t _gps_buffer_size;
uint8_t* _sensor_buffer;
size_t _sensor_buffer_size;
void getChunckData(uint8_t** gps,size_t* gps_size,uint8_t** sensor,size_t* sensor_size)
{
*gps = _gps_buffer;
*sensor = _sensor_buffer;
*gps_size = _gps_buffer_size;
*sensor_size = _sensor_buffer_size;
}
#endif
typedef enum
{
UNDEFINED_STREAM = 0,
VIDEO_STREAM,
AUDIO_STREAM,
TXT_STREAM,
} AVI_STREAM_TYPE;
RMfile _file;
AVIHeader _avi_header; // 파일헤더
stream_header_t _audioStreamHeader;
stream_format_auds_t _audioStreamFormat;
int _videoStreamCount; // 현재까지 로딩된 입력된 스트림 인덱스
stream_header_t _videoStreamHeaders[MAX_VIDEO_STREAM_COUNT]; // N개
stream_format_t _videoStreamFormat[MAX_VIDEO_STREAM_COUNT];
stream_header_t _txtStreamHeader; // TXT 스트림 헤더
int parse_riff();
int hex_dump_chunk(int chunk_len);
#if (DEBUG_AVI_FORMAT)
int parse_idx1(int chunk_len);
#endif
int read_avi_header(); // 메인 헤더
#if (SENSOR_AVI_SUBTITLE)
int add_subtitle(long chunkSize); // 자막 처리
#endif
void print_data_handler(unsigned char *handler); // Data Handler 출력 (DEBUG)
int read_stream_header(stream_header_t *stream_header);
int read_stream_format(stream_format_t *stream_format);
int read_stream_format_auds(stream_format_auds_t *stream_format);
//int read_strd_format(long strdSize);
//int add_subtitle(long chunkSize);
int parse_hdrl(unsigned int size);
int parse_hdrl_list();
int parse_movi_list(unsigned int size);
};
#endif // #if (FILE_FORMAT_AVI)
#endif // RM_FORMAT_AVI_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,308 @@
#ifndef RM_FORMAT_MOV_H
#define RM_FORMAT_MOV_H
#if (FILE_FORMAT_MOV)
// 기본정보(duration + bitrate) 읽기 + 센서 파서
// 기존 mov_reader.h/cpp + rm_mov_format.h/cpp 통합 버전
#if !defined(BBEXTRACT)
#include "../rm_include.h"
#include <QString>
#endif
#include "rm_format.h"
extern "C" {
#include "fileio.h"
}
#include <stdint.h>
// 최대 tag 탐색 깊이
#define RM_MOV_MAX_DEPTH 10
// 마지막 탐색된 tag 의 최대 크기 (eg. 3개 trak 에서 mdia->stbl->stsd stsd 가 text 인 tag 탐색시
#if(RM_MODEL == RM_MODEL_TYPE_TB4000 || RM_MODEL == RM_MODEL_TYPE_MH9000)
#define RM_MOV_MAX_TAG_VALUE_SIZE 100
#else // 4000
#define RM_MOV_MAX_TAG_VALUE_SIZE 10
#endif // 4000
#if (RM_MODEL == RM_MODEL_TYPE_NX_DRW22)
typedef struct _GPS0
{
double lat; // 0
double lon; // 8
uint32_t alt; // 16
uint16_t speed; // 20
uint8_t year; // 22
uint8_t month; // 23
uint8_t day; // 24
uint8_t hour; // 25
uint8_t min; // 26
uint8_t sec; // 27
uint8_t degree; // 28
uint8_t status; // 29
uint8_t version; // 30
uint8_t reserved;
} GPS0;
#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)
// #define SENSOR_INTERVAL 1 // 2초 간격으로 저장
// #define SENSOR_FPS 40 // " 40개씩 저장됨
#define MAX_SENSOR_FPS 40
#endif // RM_MODEL_TYPE_NX_DRW22
#if (RM_MODEL == RM_MODEL_TYPE_TB4000)
//typedef struct GPSINFOCHUCKTIME_s
//{
// unsigned char byYear; // < Years since 1900
// unsigned char byMon; // < Months since January - [0,11]
// unsigned char byDay; // < Day of the month - [1,31]
// unsigned char byHour; // < Hours since midnight - [0,23]
// unsigned char byMin; // < Minutes after the hour - [0,59]
// unsigned char bySec; // < Seconds after the minute - [0,59]
//} GPSINFOCHUCKTIME;
typedef struct _GPSINFOCHUCK_TELEBIT
{
double dwLat; //< Latitude in NDEG - +/-[degree][min].[sec/60]
double dwLon; //< Longitude in NDEG - +/-[degree][min].[sec/60]
long lAlt; //< Altitude in meter +-:under/below sea level
unsigned short usSpeed; //< Speed unit: km/h
unsigned char datetime[6]; // byYear(from 1900), byMon, byDay, byHour, byMin, bySec
//GPSINFOCHUCKTIME sUTC; //< UTC of position
unsigned char ubDirection; //< Clockwise degree from the North.
unsigned char ubFlag; //< Check if the GPS data is valid;
unsigned char ubVersion; //< Stuture Version
unsigned char ubReserved;
} GPSINFOCHUCK_TELEBIT;
#endif // 4000
#if (RM_MODEL == RM_MODEL_TYPE_MH9000)
#pragma pack(push, 1)
typedef struct _gps_chunk_t
{
char header[4]; // id = "DVAL"
unsigned short year;
unsigned short mon;
unsigned short mday;
unsigned short hour;
unsigned short min;
unsigned short sec;
char vehicle_id[16];
char driver_id[16];
char rec_type; // 0:normal, 1:event
char event_gsensor;
char event_smoke;
char event_dsm;
char flag_seat_b;
char flag_side_b;
char flag_wink_l;
char flag_wink_r;
char flag_foot_b;
char flag_gron_b;
char blank_a[2];
char flag_smoke;
char flag_dsm;
char blank_b[2];
unsigned short speed;
unsigned short rpm;
unsigned short gps_speed;
char blank_c[2];
unsigned int latitude; // lat * 100000
unsigned int longitude; // long * 100000
unsigned short gx[10]; // (g x 100) 100ms x 10 = 1sec
unsigned short gy[10]; // (g x 100) 100ms x 10 = 1sec
unsigned short gz[10]; // (g x 100) 100ms x 10 = 1sec
char fw_version[16];
long lAlt; /**< Altitude in meter +-:under/below sea level*/
unsigned char ubDirection; /**< Clockwise degree from the North.*/
} gps_chunk_t;
#pragma pack(pop)
#endif // MH9000
class MOVFormat
{
// 재생시간 및 bitrate parser
private:
VideoReadMode _readMode; // 현재 모드
VideoPreInfo* _preInfo; // 기본 정보 (존재할 경우 ReadMode == PreInfo)
bool _isValid; // 센서 관련?
// Python..
unsigned int _offset;
RMfile _file;
private:
// parse 중지
bool _stop_parse;
// 탐색할 tag 리스트 (최대 depth = 10)
int _tag_count;
long _tag_list[RM_MOV_MAX_DEPTH];
// 탐색 tag (depth) offset
unsigned int _tag_offset_list[RM_MOV_MAX_DEPTH];
// 탐색 tag (depth) size
long _tag_size_list[RM_MOV_MAX_DEPTH];
// 탐색 중 확인할 value (자막의 경우 track 중 value 가 txt 인지 확인해야함)
char _tag_search_value[RM_MOV_MAX_TAG_VALUE_SIZE];
// 초기화
void init_parser();
void set_tags(const char* tag,...);
// 현재 tag value 가 _tag_search_value 와 동일한지 확인
bool check_tag_value(long atom_type, unsigned int offset, long size);
// 처음부터 끝까지 depth 0 에서 parse 시작
void parse_all();
// 센서 데이터만 parse
//void parse_sensor();
// bitrate 만 parse
#if !defined(BBEXTRACT)
#if (CHECK_VIDEO_BITRATE)
bool parse_bitrate();
#endif // CHECK_VIDEO_BITRATE
void parse_avc1(long offset, long size); // H264 Video
#endif // #if !defined(BBEXTRACT)
bool parse_duration();
bool parse_sensor();
#if (RM_MODEL == RM_MODEL_TYPE_XLDR_88 || SUB_MODEL_CARROT_EMT)
bool parse_buffer(uint8_t* buffer,long size);
#endif
// _tag 로 지정된 tag 의 옵셋 가져오기
void parse(unsigned int offset, unsigned int length, int depth = 0);
unsigned int get_tag_list_offset();
#if (RM_MODEL == RM_MODEL_TYPE_NX_DRW22)
GPS0* _gps0;
int16_t *_zyx; // 20201005 XYZ->ZYX 로 수정요청
#elif (RM_MODEL == RM_MODEL_TYPE_ADT_CAPS || \
RM_MODEL == RM_MODEL_TYPE_BV2000 || \
RM_MODEL == RM_MODEL_TYPE_XLDR_88 || \
RM_MODEL == RM_MODEL_TYPE_KEIYO1 || \
RM_MODEL == RM_MODEL_TYPE_MBJ5010 || \
RM_MODEL == RM_MODEL_TYPE_FC_DR232W)
void* _nmea; // NMEA INFO
void* _sens; // _SEN
#elif (RM_MODEL == RM_MODEL_TYPE_MH9000)
gps_chunk_t* _sens;
#elif (RM_MODEL_EMT_KR)
void* _nmea; // NMEA INFO
#endif
#if (RM_MODEL != RM_MODEL_TYPE_MH9000 && !RM_MODEL_EMT_KR)
int _gps0Count;
#endif // _sens 에 통합
int _gsenCount;
#if (RM_MODEL == RM_MODEL_TYPE_BV2000 ||\
RM_MODEL == RM_MODEL_TYPE_KEIYO1 ||\
RM_MODEL == RM_MODEL_TYPE_MBJ5010 ||\
RM_MODEL == RM_MODEL_TYPE_MH9000 || \
RM_MODEL == RM_MODEL_TYPE_FC_DR232W)
int _sensorFPS;
void process_subtitle(const char* subtitle);
#elif (RM_MODEL_EMT_KR)
void process_subtitle(const char* subtitle);
#endif
public:
MOVFormat(RMfile in,VideoReadMode mode = VideoReadSensor, VideoPreInfo* info = NULL);
static bool duration(RMfile in,VideoPreInfo* info);
#if !defined(BBEXTRACT)
#if (CHECK_VIDEO_BITRATE)
static bool movSize(RMfile in,VideoPreInfo* info);
#endif
#endif // #if !defined(BBEXTRACT)
~MOVFormat();
#if (RM_MODEL_EMT_KR)
QString modelName;
#endif
bool isValid()
{
return _isValid;
}
// MOV 크기와 재생시간 그리고 오디오 bitrate (kb/sec) 를 입력받아 mov bitrate 를 계산한다
#if (CHECK_VIDEO_BITRATE)
static int videoBitrate(VideoPreInfo* info) {
const double kb = 1000.0; // 1024?
const double durationInSec = (double)(info->duration) / 1000.0;
double videoSize = (double)(info->movSize);
return videoSize * 8.0 / durationInSec / kb;
}
#endif
#if (RM_MODEL == RM_MODEL_TYPE_NX_DRW22)
int getGPS(GPS0** gps)
{
*gps = _gps0;
return _gps0Count;
}
int getSensor(int16_t** sensor)
{
*sensor = _zyx;
return _gsenCount;
}
#elif (RM_MODEL_EMT_KR)
int getNMEA(void** nmea)
{
*nmea = _nmea;
return _gsenCount;
}
#elif (RM_MODEL == RM_MODEL_TYPE_ADT_CAPS || \
RM_MODEL == RM_MODEL_TYPE_XLDR_88 || \
RM_MODEL == RM_MODEL_TYPE_KEIYO1 || \
RM_MODEL == RM_MODEL_TYPE_MBJ5010 || \
RM_MODEL == RM_MODEL_TYPE_BV2000 || \
RM_MODEL == RM_MODEL_TYPE_MH9000 | \
RM_MODEL == RM_MODEL_TYPE_FC_DR232W)
// MH9000은 sensor 에 통합되어 있음
#if (RM_MODEL != RM_MODEL_TYPE_MH9000)
int getGPS(void** nmea)
{
*nmea = _nmea;
return _gps0Count;
}
#endif //
int getSensor(void** sensor)
{
*sensor = _sens;
return _gsenCount;
}
#if (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 || \
RM_MODEL == RM_MODEL_TYPE_MH9000)
int getSensorFPS() {
return _sensorFPS;
}
#endif
#elif (RM_MODEL == RM_MODEL_TYPE_TB4000)
GPSINFOCHUCK_TELEBIT* _nmea; // NMEA INFO
int getGPS(GPSINFOCHUCK_TELEBIT** nmea)
{
*nmea = _nmea;
return _gps0Count;
}
#endif
};
#endif // #if (FILE_FORMAT_MOV)
#endif // RM_FORMAT_MOV_H

View File

@@ -0,0 +1,179 @@
#include "rm_format_mov.h"
#include "fm_parse_gps.h"
#if (RM_MODEL == RM_MODEL_TYPE_XLDR_88)
#define GPSR_FLAG ('G' | 'P' << 8 | 'S' << 16 | 'R' << 24)
#define SENS_FLAG ('S' | 'E' << 8 | 'N' << 16 | 'S' << 24)
#define BYTE_SWAP_32(__num) ( ((__num>>24)&0xff) | ((__num<<8)&0xff0000) | ((__num>>8)&0xff00) | ((__num<<24)&0xff000000))
//#define GPRS_SENTENCE_SIZE 128
bool MOVFormat::parse_buffer(uint8_t* strd,long strdSize)
{
size_t check_offset = 0;
// GPSR 및 데이터 시작 위치가 다름 (이벤트에 따라)
for(long i=0;i<strdSize;i++)
{
if(*((int32_t*)&strd[i]) == GPSR_FLAG || *((int32_t*)&strd[i]) == SENS_FLAG)
{
check_offset = i;
break;
}
}
if (check_offset == 0)
{
return false;
}
long offset = 0;
int32_t flag;// = *((int32_t*)&strd[offset]);
offset += (check_offset - 4); // GRSP 앞쪽 4BYTE 에 크기가 시작됨
bool gpsDone = false;
bool sensorDone = false;
while(offset < strdSize)
{
// 데이터 크기는 GPS 및 센서 데이터 앞쪽에 있음
int32_t dataSize = *((int32_t*)&strd[offset]); // data to offset 이 가장 먼저 있음
offset += 4;
dataSize = BYTE_SWAP_32(dataSize); // eg. 4088 byte 부터 sensor data 시작, 센서는 9032
int32_t nextOffset = dataSize + (check_offset - 4); // 4088 + 24(front) = 4120 (file offset: )
flag = *((int32_t*)&strd[offset]); // flag (type)
offset += 4;
int index = 0;
if(flag == GPSR_FLAG) // GPS Data
{
//_gps0Count = dataSize / (GPRS_SENTENCE_SIZE + 4 + 4); // 136 = 0x88
//qInfo() << "GPSR_FLAG found:" << _gpsCount;
if(_nmea != NULL)
{
free(_nmea);
}
//_bGPSExist = true;
NMEA_INFO* nmea = (NMEA_INFO*)malloc(sizeof(_NMEA_INFO) * 1000);
_nmea = nmea;
//#if (DEBUG_SENSOR_DATA)
// printf("gps data size:%d count:%d test:%f\n",dataSize,_gpsCount,(float)dataSize / (float)(GPRS_SENTENCE_SIZE + 4 + 4));
//#endif
// bool validGPSFound = false;
while(offset < dataSize)
{
// int32_t frameNumber = *((int32_t*)&strd[offset]);
//offset += 4;
int32_t packetSize = strd[offset];
offset += 1;
char* buffer = (char*)&strd[offset];
if(strncmp(buffer,"$GPRMC",6) != 0) {
break;
}
FMParseGPS::ParseRMC(buffer, &nmea[index],packetSize);
char deb[1024] = {0,};
strncpy(deb,buffer,packetSize);
// qInfo() << index << ":" << deb << " =" << QString().sprintf("%X",offset);
//_ParseRMC(buffer,index,NMEA_TOKEN_SIZE);
//#if (GPRMC_VALID_CHECK)
// if( validGPSFound == false &&
// _gpsData[index].nStatus == 1 &&
// IS_VALID_LOCATION(_gpsData[index].Longitude,_gpsData[index].Latitude) == true)
//#else
// if( validGPSFound == false && IS_VALID_LOCATION(_gpsData[index].Longitude,_gpsData[index].Latitude) == true)
//#endif
// {
// validGPSFound = true;
// }
offset +=packetSize;
// _bGPSExist = validGPSFound;
index++;
}
gpsDone = true;
_gps0Count = index;
}
else if(flag == SENS_FLAG) // Sensor Data
{
dataSize -= 8; // size + flag = 8 byte
_gsenCount = dataSize / SENS_PACKET_SIZE;
if(_sens != NULL)
{
free(_sens);
}
float* sens = (float*)malloc(SENS_PACKET_SIZE * _gsenCount);
_sens = sens;
//printf("----------------------------------------------------------\n");
//printf("sensor data:%d\n",_sensorCount);
//printf("----------------------------------------------------------\n");
#if (DEBUG_SENSOR_DATA)
printf("sensor data size:%d count:%d test:%f\n",dataSize,_sensorCount,(float)dataSize / (float)(SENS_PACKET_SIZE));
#endif
while(index < _gsenCount)
{
memcpy(&sens[index * NUM_SENSOR_DATA],&strd[offset],SENS_PACKET_SIZE);
offset += SENS_PACKET_SIZE;
//qInfo() << QString().sprintf("%.2f,%.2f,%.2f", sens[(index * NUM_SENSOR_DATA)+0],sens[(index * NUM_SENSOR_DATA)+1],sens[(index * NUM_SENSOR_DATA)+2]);
//printf("%d,%.5f,%.5f,%.5f\n",index, _sensorData[(index * NUM_SENSOR_DATA)+0],_sensorData[(index * NUM_SENSOR_DATA)+1],_sensorData[(index * NUM_SENSOR_DATA)+2]);
#if (DEBUG_SENSOR_DATA)
printf("idx:%d x:%.5f y:%.5f z:%.5f\n",index, _sensorData[(index * NUM_SENSOR_DATA)+0],_sensorData[(index * NUM_SENSOR_DATA)+1],_sensorData[(index * NUM_SENSOR_DATA)+2]);
#endif
index++;
}
sensorDone = true;
}
else
{
break; // flag 가 아닐경우
}
if(gpsDone == true && sensorDone == true)
{
break;
}
offset = nextOffset;// dataSize; // 어차피 같아야함..
}
return true;
}
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)
{
//qInfo() << QString().sprintf("%X,%d",udat_offset+8,udat_size-8);
RMfseek(_file,udat_offset+8,SEEK_SET);
uint8_t *buffer = (uint8_t*)malloc(udat_size-8);
RMfread(buffer,udat_size-8,1,_file);
parse_buffer(buffer,udat_size-8);
return true;
}
return false;
}
#endif

View File

@@ -0,0 +1,10 @@
#include "rm_overwrite.h"
QMutex RMOverwrite::_lock;
QWaitCondition RMOverwrite::_wait;
int RMOverwrite::gCurrent = OVERWRITE_OPTION_ASK | OVERWRITE_OPTION_ALL;
QString RMOverwrite::currentFileName;
int RMOverwrite::currentCount = 0;

View File

@@ -0,0 +1,56 @@
#ifndef RM_OVERWRITE_H
#define RM_OVERWRITE_H
#include <QWaitCondition>
#include <QObject>
#include <QMutex>
// 백업시 파일 존재할 경우 처리 방법
typedef enum
{
OVERWRITE_OPTION_ASK = 1 << 0, // 확인
OVERWRITE_OPTION_SKIP = 1 << 1, // 스킵
OVERWRITE_OPTION_WRITE = 1 << 2, // 덮어쓰기
OVERWRITE_OPTION_CANCEL = 1 << 3, // 취소
OVERWRITE_OPTION_ALL = 1 << 4, // 전체
} OVERWRITE_OPTION;
class RMOverwrite
{
private:
static QMutex _lock; // 프로세스 잠금/WAIT (백업 중 동일 파일 존재 ETC)
static QWaitCondition _wait; // 대기
public:
static void lock () {
RMOverwrite::_lock.lock();
}
static void unlock () {
RMOverwrite::_lock.unlock();
}
static void wait() {
RMOverwrite::_wait.wait(&RMOverwrite::_lock);
}
static void unwait() {
RMOverwrite::_wait.wakeAll();
}
static void reset() {
RMOverwrite::gCurrent = OVERWRITE_OPTION_ASK;
RMOverwrite::currentFileName = "";
RMOverwrite::currentCount = 0;
}
static QString currentFileName; // 중복된 파일명
static int currentCount; // 남은 파일 개수
//static OVERWRITE_OPTION gGlobal; // 전체 옵션 (eg. 모두 skip, etc)
static int gCurrent; // 현재 옵션 (다이얼로그 선택)
static bool check(OVERWRITE_OPTION with) {
return ((RMOverwrite::gCurrent & with) == with);
}
};
#endif // RM_OVERWRITE_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,328 @@
#ifndef RM_SENSORDATA_H
#define RM_SENSORDATA_H
#include <stdio.h>
#include <stdint.h>
#include <vector>
#if !defined(BBEXTRACT)
#include "../rm_include.h"
#endif // #if !defined(BBEXTRACT)
#if !defined(MIN)
#define MIN(a,b) (((a)<(b))?(a):(b))
#endif
#if !defined(MAX)
#define MAX(a,b) (((a)>(b))?(a):(b))
#endif
#include "rm_format_avi.h"
#define DEBUG_SENSOR_DATA 0
#define GPRMC_VALID_CHECK 1
#define NUM_SENSOR_DATA 3 // XYZ
#define SENS_PACKET_SIZE (NUM_SENSOR_DATA * sizeof(float))
#if (RM_MODEL_EMT_KR)
// 모든 자막에 GPS+SENSOR 같은 숫자로 존재
typedef struct _NMEA_INFO
{
uint16_t nYear; // (UTC)년(+9시간)
uint8_t nMonth; // (UTC)월
uint8_t nDay; // (UTC)일
uint8_t nHour; // (UTC)시
uint8_t nMin; // (UTC)분
uint8_t nSec; // (UTC)초
uint8_t reserved; // ... (64 bit pack)
double Latitude; // 위도
double Longitude; // 경도
float Speed; // 속도 (Km/h)
float x;
float y;
float z;
float Voltage; // 전압
float Temperature; // 온도
uint8_t nStatus; // 상태(0,1)
uint8_t nAngle; // 방위각
#if (USE_TRIGGER)
uint8_t eTrigger; // 트리거 E (255 가 아닌 위치index 가 발생위치임)
uint8_t mTrigger; // " M (255 가 아닌 위치index 가 발생위치임)
uint8_t reserved2[4]; // 64 bit pack
#else // USE_TRIGGER
uint8_t reserved2[6]; // 64 bit pack
#endif // USE_TRIGGER
} NMEA_INFO;
#else // #if (RM_MODEL_EMT_KR)
typedef struct _NMEA_INFO
{
uint16_t nYear; // (UTC)년(+9시간)
uint8_t nMonth; // (UTC)월
uint8_t nDay; // (UTC)일
uint8_t nHour; // (UTC)시
uint8_t nMin; // (UTC)분
uint8_t nSec; // (UTC)초
uint8_t reserved; // ... (64 bit pack)
double Latitude; // 위도
double Longitude; // 경도
double Speed; // 속도 (Km/h)
uint8_t nStatus; // 상태(0,1)
uint8_t nAngle; // 방위각
uint8_t reserved2[6]; // 64 bit pack
} NMEA_INFO;
#endif //
#if (RM_MODEL == RM_MODEL_TYPE_ADT_CAPS && !SUB_MODEL_CARROT_EMT)
typedef struct _SEN {
float x;
float y;
float z;
float gcal;
uint32_t drive;
uint32_t parking;
} SEN;
#endif // RM_MODEL_TYPE_ADT_CAPS
class RMSensorData
{
public:
typedef enum
{
FILE_TYPE_AVI = 0,
FILE_TYPE_MOV = 1, // MP4
} FILE_TYPE;
RMSensorData(FILE* f,FILE_TYPE type);
~RMSensorData();
QString modelName;
uint32_t getSensorCount()
{
return _sensorCount;
}
#if (RM_MODEL_EMT_KR)
const NMEA_INFO* getSensor()
{
return _sensorData;
}
uint32_t getGPSCount()
{
return _bGPSExist ? _sensorCount : 0;
}
const NMEA_INFO* getGPS()
{
return _bGPSExist ? _sensorData : NULL;
}
const bool getGPSCoord(double ratio,double* lat, double* lon)
{
if(_bGPSExist)
{
unsigned int index = (int)((double)_sensorCount * ratio);
if(index <= _sensorCount) // 마지막 1초는 오차 범위
{
index = MIN(index,_sensorCount-1);
*lat = _sensorData[index].Latitude;
*lon = _sensorData[index].Longitude;
return true;
}
}
return false;
}
const double getGPSSpeed(double ratio)
{
if(_bGPSExist)
{
unsigned int index = (int)((double)_sensorCount * ratio);
if(index <= _sensorCount) // 마지막 1초는 오차 범위
{
index = MIN(index,_sensorCount-1);
//qInfo() << "SPD:" << index << _gpsData[index].nStatus << _gpsData[index].Speed;
return _sensorData[index].nStatus == 1 ? _sensorData[index].Speed : -1;
}
}
return -1;
}
bool getGPSPosition(double ratio,double* lonX, double* latY, double* speed)
{
if(_bGPSExist)
{
unsigned int index = (int)((double)_sensorCount * ratio);
if(index < _sensorCount && _sensorData[index].nStatus == 1)
{
*lonX = _sensorData[index].Longitude;
*latY = _sensorData[index].Latitude;
*speed = _sensorData[index].Speed;
return true;
}
}
return false;
}
#else // RM_MODEL_EMT_KR
const float* getSensor()
{
return _sensorData;
}
uint32_t getGPSCount()
{
return _bGPSExist ? _gpsCount : 0;
}
const NMEA_INFO* getGPS()
{
return _bGPSExist ? _gpsData : NULL;
}
const bool getGPSCoord(double ratio,double* lat, double* lon)
{
if(_bGPSExist)
{
unsigned int index = (int)((double)_gpsCount * ratio);
if(index <= _gpsCount) // 마지막 1초는 오차 범위
{
index = MIN(index,_gpsCount-1);
*lat = _gpsData[index].Latitude;
*lon = _gpsData[index].Longitude;
return true;
}
}
return false;
}
const double getGPSSpeed(double ratio)
{
#if !(SPEED_ALWAYS_EXITS)
if(_bGPSExist)
#endif
{
unsigned int index = (int)((double)_gpsCount * ratio);
if(index <= _gpsCount) // 마지막 1초는 오차 범위
{
index = MIN(index,_gpsCount-1);
#if (SPEED_ALWAYS_EXITS)
//qInfo() << "SPD:" << index << _gpsData[index].nStatus << _gpsData[index].Speed;
return _gpsData[index].Speed;
#else
//qInfo() << "SPD:" << index << _gpsData[index].nStatus << _gpsData[index].Speed;
return _gpsData[index].nStatus == 1 ? _gpsData[index].Speed : -1;
#endif
}
}
return -1;
}
bool getGPSPosition(double ratio,double* lonX, double* latY, double* speed)
{
if(_bGPSExist)
{
unsigned int index = (int)((double)_gpsCount * ratio);
if(index < _gpsCount && _gpsData[index].nStatus == 1)
{
*lonX = _gpsData[index].Longitude;
*latY = _gpsData[index].Latitude;
*speed = _gpsData[index].Speed;
return true;
}
}
return false;
}
#if (CLIP_SENSOR_DATA)
void clipWithDuration(qint64 duration);
void clipSensorCount(int number);
void clipGPSCount(int number);
void fixGPS();
#endif
#endif // RM_MODEL_EMT_KR
#if (USE_TRIGGER)
float triggerE; // 트리거 위치 (ratio 0~1.0)
float triggerM;
#endif // USE_TRIGGER
#if defined(MODEL_BBVIEWER) && (!MODEL_WATEX)
const double getOBDSpeed(double ratio)
{
if(_sensorData != NULL && _sensorCount > 0)
{
unsigned int index = (int)(((double)_sensorCount) * ratio);
//qInfo() << "SENSOR INDEX:" << index;
if(index < _sensorCount)
{
return _OBDSpeed[index];
}
}
return true;
}
#endif // BB
// 재생시간과 센서 데이터 동기화 (짧으면 추가, 길면 자르기)
void processWithDuration(qint64 ms);
private:
#if (CLIP_SENSOR_DATA)
int _skipSensorCount;
#endif
bool _bGPSExist;
long _nLastAngle[2]; // RMC 데이터 처리용
#if(RM_MODEL_EMT_KR)
uint32_t _sensorCount;
NMEA_INFO* _sensorData;
#else // RM_MODEL_EMT_KR
#if (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 || \
RM_MODEL == RM_MODEL_TYPE_MH9000 )
int _sensorFPS;
#endif
uint32_t _gpsCount;
uint32_t _sensorCount;
#if defined(BBEXTRACT)
GSENSOR* _sensorData;
#else
float* _sensorData;
#endif
NMEA_INFO* _gpsData;
#if (MODEL_BBVIEWER && !MODEL_WATEX)
float* _OBDSpeed; // GPS 속도인지 ODB 속도인지 확인
#endif
#endif // !RM_MODEL_EMT_KR
#if (FILE_FORMAT_MOV)
bool loadMOV(FILE* f);
#if !(RM_MODEL_EMT_KR)
#if(RM_MODEL == RM_MODEL_TYPE_MH9000)
bool loadMOVGPSNR(void* p,uint32_t count);
#else // RM_MODEL_TYPE_MH9000
bool loadMOVGPSNR(void* p,uint32_t count, void* ps, uint32_t sensorCount);
//bool loadMOVGPSNR(void* p,uint32_t count);
#endif // RM_MODEL_TYPE_MH9000
#endif // #if !(RM_MODEL_EMT_KR)
#endif // FILE_FORMAT_MOV
#if (FILE_FORMAT_AVI)
bool loadAVIRiff(FILE* f); // TYPE 1
#if (AVI_CHUNHO_SENSOR_FORMAT_1)
bool loadAVIChunck(uint8_t* gps, size_t gps_size,uint8_t* sensor, size_t sensor_size);
#endif
#if (SENSOR_AVI_SUBTITLE)
bool loadAVISubTitle(QList<char*>* subTitles); // AVI TYPE 2 불러오기
#endif
#endif // FILE_FORMAT_AVI
};
#endif // RM_SENSORDATA_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,236 @@
#ifndef RM_VIDEOITEM_2CH_H
#define RM_VIDEOITEM_2CH_H
// 각 채널별 파일이 구분되어 있는 단말기의 경우 RMVideoItem 을 별도로 사용함
#include "../rm_include.h"
#include "rm_video_list.h"
#include <QObject>
#include <QString>
#include <QRunnable>
#include <QDateTime>
#include <QThread>
// 분리 채널 파일 포멧만 사용
#if (RM_USE_SEPARATED_CH_FILE)
class RMSensorData;
class RMVideoItem : public QObject
{
Q_OBJECT
friend class RMVideoListLoader;
friend class RMVideoItemLoader;
friend class RMPlayerBase;
public:
#ifdef _DEBUG
void print();
#endif
#if (USE_DEBUG_FUNCTIONS)
static void make_many_files(QString path);
#endif
RMVideoFileList::GROUP_TYPE type;
bool checked; ///! 백업용 FLAG
bool dropItem; // dropitem = single path
bool added; // 신규로 추가된 아이템을 확인하기위해 추가시 초기화 한다.
// bool forceSwap; // 후방만 존재하고 전방이 없을 경우=> Player 의 RearSwap.. 을 사용
int width; // 해상도 (1CH, 현재 AVI 만 구현)
int height; // "
QString filePath;
QString fileName; // 경로 제외 명칭
#if (RM_MODEL == RM_MODEL_TYPE_AN6000)
QString decodedPath;
void removeTemp();
#endif // RM_MODEL_TYPE_AN6000
#if (DUAL_CH_FILE && DUAL_CH_1CH_EXIST)
bool only1CH;
#endif
#if (RM_MODEL_EMT_KR)
QString modelName; //! WIDE 지원 모델 영상
#endif
#if (TRI_CHANNEL)
RMVideoItem(QString path,int CH,QDateTime* pDateTime); // CH 1,2,3
#else
RMVideoItem(QString path,bool CH2,QDateTime* pDateTime);
#endif
#if (RM_MODEL_EMT_KR)
bool isWideMode() {
return (modelName == "360X" && realCHCount() > 2);
}
#endif
// MP4 CH2 파일
QString filePathCH2;
#if (TRI_CHANNEL)
QString filePathCH3;
bool isCH3Exist() {
return !filePathCH3.isEmpty();
}
#elif (PENTA_CHANNEL)
QString filePathCH3;
QString filePathCH4;
QString filePathCH5;
#endif // PENTA_CHANNEL
QString& anyFilePath()
{
return filePath.length() > 0 ? filePath : filePathCH2;
}
bool isRearOnly() {
return (filePath.isEmpty() && (filePathCH2.isEmpty() == false));
}
bool isFrontOnly() {
return (filePathCH2.isEmpty() && (filePath.isEmpty() == false));
}
bool isSingleChannel()
{
#if (DUAL_CH_FILE && DUAL_CH_1CH_EXIST) // 2CH 파일이나 후방없는 1CH 파일도 존재
return only1CH;
#elif (DUAL_CH_FILE || SINGLE_CH_VIEWER)
return false;
#else
return (filePath.length() == 0 || filePathCH2.length() == 0);
#endif
}
#if (TRI_CHANNEL2)
int _realCHCount;
//! \brief 프레임이 존재하지 않는 채널 제외한 채널 개수 확인
//! AVDemux 에서 확인됨, 초기값은 0
//! \return
int realCHCount() {
return _realCHCount;
}
#endif // TRI_CHANNEL2
#if (CHECK_VIDEO_BITRATE)
// 후방 채널 비디오 비트레이트 확인해서 일정 기준 미만이면 제거
void testBitrate2CH();
#endif
#if !(SINGLE_CH_VIEWER)
#if (!FORCE_2CH || (RM_MODEL == RM_MODEL_TYPE_XLDR_88 || SUB_MODEL_CARROT_EMT))
// 채널정보를 제외한 파일명을 리턴한다
static QString fileNameWithoutChannel(QString& path);
#endif
#endif // SINGLE_CH_VIEWER
~RMVideoItem();
static bool FileExist(QString path);
static int IsFeasible(QString path, QDateTime* pDateTime = NULL);
int fileFormat();
QString title();
QString typeString();
QString titleDate();
QString titleTime();
QString titleDateTime();
QString titleDuration();
#if !(DUAL_CH_FILE || SINGLE_CH_VIEWER)
QString titleFrontRear();
#endif
QString titlePrefix();
QString titleSize();
QString titleCapture(qint64 secs);
RMSensorData* getSensorData()
{
return sensorData;
}
//static QString recoveredPath(QString filePath);
static QString durationString(unsigned int sec,bool hms);
unsigned int durationInMSecs()
{
return _durationInMSecs;
}
#if (PLAY_SYNC_FIX2)
unsigned int packetDurationInMSecs; // 재생시작해야 발생함???
#endif // PLAY_SYNC_FIX2
// ?? 사용되지 않는다???
QDateTime dateTimeInPosition(qreal ratio,double* lat, double* lon); // 전체 플레이 ratio 를 통해 시간 가져오기
bool isValid()
{
#if (SKIP_VIDEO_PREINFO)
return true;
#else
return (_durationInMSecs > 300); // 0.3?
#endif
}
QDateTime& startTime()
{
return _dateTime;
}
#if (CHECK_REAR_DURATION)
bool isRearDuration();
#endif
// 인덱스 순으로 정렬
//int fileIndex();
protected:
#if (!PRE_LOAD_SENSOR_DATA)
void load()
{
emit loadSensorInfoStarted();
if(getSensorData() == NULL)
{
loadSensorInfo();
}
emit loadSensorInfoEnd();
}
#endif
void loadSensorInfo();
private:
signals:
void loadSensorInfoStarted();
void loadSensorInfoEnd();
void loadSensorInfoFail();
private:
#if (TRI_CHANNEL)
QString filePathWithCH(int ch);
#endif
QDateTime _dateTime;
#if (TRI_CHANNEL)
bool _loadDuration(int ch); // 1,2,3
#else
bool _loadDuration(bool rear = false);
#endif
qint64 _fileSize;
// 1초 미만 파일이 있음
unsigned int _durationInMSecs;
RMSensorData* sensorData;
static QDateTime _fileNameToDateTime(QString baseName);
public slots:
void onChecked();
};
#endif // #if (RM_USE_SEPARATED_CH_FILE)
#endif // RM_VIDEOITEM_2CH_H

View File

@@ -0,0 +1,2 @@
#include "rm_video_item_loader.h"

View File

@@ -0,0 +1,26 @@
#ifndef RM_VIDEO_ITEM_LOADER_H
#define RM_VIDEO_ITEM_LOADER_H
#include <QObject>
#include <QRunnable>
#include "rm_video_item_2ch.h"
#if (!PRE_LOAD_SENSOR_DATA)
class RMVideoItemLoader : public QRunnable
{
private:
RMVideoItem* _item;
public:
RMVideoItemLoader(RMVideoItem* item)
{
_item = item;
}
virtual void run()
{
_item->load();
}
};
#endif // PRE_LOAD_SENSOR_DATA
#endif // RM_VIDEO_ITEM_LOADER_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,435 @@
#ifndef RM_VIDEOFILELIST_H
#define RM_VIDEOFILELIST_H
#include <QObject>
#include <QUrl>
#include <qstring.h>
#include <QMainWindow>
#include <QDebug>
#include "../rm_include.h"
class RMVideoItem;
// #include "rm_video_group.h"
class RMVideoFileList : public QObject
{
Q_OBJECT
public:
friend class RMWidgetVideoListDelegate;
friend class RMWidgetVideoList;
friend class RMVideoFileListLoader;
friend class RMVideoFileListBackup;
static int n_lastPercent;
static QList<QString> fileFilters;
#if (SUPPORT_LOADING_CANCEL)
// 로딩 취소
bool bCancelLoading;
#endif
// item 으로 이동
typedef enum
{
// 주행 / 주행 이벤트 / 주차 / 주차 충격 / 수동 녹화( / 보관함???)
#if (RM_MODEL_EMT_KR)
FILTER_NONE = 0,
FILTER_NORMAL = 1 << 0, // 주행
FILTER_EVENT = 1 << 1, // 주행 이벤트
FILTER_PARK = 1 << 2, // 주차
FILTER_PARK_EVENT = 1 << 3, // 주차 충격
FILTER_MANUAL = 1 << 4, // 수동 녹화
FILTER_MYBOX = 1 << 5, // 보관함 (포함된 폴더가 MYBOX 일 경우 분류 관계없이)
#else // RM_MODEL_EMT_KR
FILTER_NONE = 0,
FILTER_NORMAL = 1 << 0,
FILTER_EVENT = 1 << 1,
#if ((RM_MODEL == RM_MODEL_TYPE_XLDR_88 || SUB_MODEL_CARROT_EMT) || \
RM_MODEL == RM_MODEL_TYPE_BV2000 || \
RM_MODEL == RM_MODEL_TYPE_KEIYO1 || \
RM_MODEL == RM_MODEL_TYPE_TBD360 || \
RM_MODEL == RM_MODEL_TYPE_MBJ5010 || \
RM_MODEL == RM_MODEL_TYPE_MH9000 || \
RM_MODEL == RM_MODEL_TYPE_FC_DR232W)
FILTER_PARK = 1 << 2,
#endif // 주차 사용
#if (RM_MODEL == RM_MODEL_TYPE_KEIYO1 ||\
RM_MODEL == RM_MODEL_TYPE_BV2000 || \
RM_MODEL == RM_MODEL_TYPE_MH9000 || \
RM_MODEL == RM_MODEL_TYPE_MBJ5010 ||\
RM_MODEL == RM_MODEL_TYPE_FC_DR232W)
FILTER_MANUAL = 1 << 3,
FILTER_ALL = FILTER_NORMAL | FILTER_EVENT | FILTER_PARK | FILTER_MANUAL,
#elif (RM_MODEL == RM_MODEL_TYPE_XLDR_88 || SUB_MODEL_CARROT_EMT || RM_MODEL == RM_MODEL_TYPE_MH9000 )
FILTER_ALL = FILTER_NORMAL | FILTER_EVENT | FILTER_PARK,
#elif !(RM_MODEL == RM_MODEL_TYPE_TBD360)
FILTER_ALL = FILTER_NORMAL | FILTER_EVENT,
#endif
#endif // RM_MODEL_EMT_KR
} FILTER;
// 단말기 별로 설정 가능
typedef enum
{
TYPE_NORMAL = 0,
TYPE_EVENT = 1,
TYPE_MANUAL = 2,
TYPE_PARKING = 3,
TYPE_PARKING_EVENT = 4,
TYPE_PARKING_MOTION = 5,
#if (RM_MODEL_EMT_KR)
TYPE_MY_BOX = 6,
#endif // #if (RM_MODEL_EMT_KR)
TYPE_UNDEFINED = 9999,
} GROUP_TYPE;
#if (RM_MODEL == RM_MODEL_TYPE_AN6000)
void removeTemps();
#endif //
#if (RM_MODEL_EMT_KR)
static RMVideoFileList::GROUP_TYPE parseName(QString baseName, QDateTime* dateTime, int* tag);
#endif
#if (USE_1HOUR_FILTER)
//! \brief 1시간 단위 필터 설정
//! \param b1Hour true: 1시간 단위 표시, false: 전체 표시
//! \param bEmit: listUpdateEnd 발생여부
void set1HourList(bool b1Hour, bool bEmit, RMVideoItem* selected);
bool get1HourList()
{
return b1HourList;
}
//! \brief 시간순, 최신순으로 정렬
//! \param bAsc: true: 시간순
void setSortList(bool bAsc);
bool getSortList()
{
return bSortDsc;
}
bool bSortDsc; // 시간순으로 정렬, 최신순으로 정렬
//! \brief 전체 항목에서 1시간 항목 추출
//! \param res : 결과
void load1HourList(QList<RMVideoItem*>& res);
//! \brief 전달된 item 에 속하는 모든 RMVideoItem 추출
//! - Thumbnail 에서 사용
//! \param dt : 탐색할 아이템 시간
//! \param res : 결과
void load1HourInList(QDateTime dt, QList<RMVideoItem*>& res);
//! \brief RMVideoItem 리스트 에서 썸네일 경로 추출
//! \param src : 추출할 VideoItem 리스트
//! \param res : Thumbnail 경로 <CH1,CH2>
void loadThumbnails(QList<RMVideoItem*>& src, QList<QPair<QString,QString>>& res);
#endif // USE_1HOUR_FILTER
#if (USE_DATE_TIME_LIST)
//! \brief 파일리스트 중 지정된 년/월 에 영상인 포함된 날짜만 리턴
//! \param year: 지정된 년
//! \param month: 지정된 월
//! \param ret<out>: 영상이 존재하는 일 리스트
//! \param dayItems<out>: 속도개선을 위해 해당일자의 파일리스트 리턴
void getMonthList(int year, int month, QSet<int>& ret, QList<RMVideoItem*>& dayItems);
//! \brief 로딩된 파일 중 가장 먼저 녹화된 날짜 확인
//! \return
QDate getFirstDate();
#endif// USE_DATE_TIME_LIST
#ifdef _DEBUG
void print();
#endif
#if !(USE_1HOUR_FILTER)
//! \brief 백업용으로 선택된 항목 초기화
void clearChecked();
#endif // #if !(USE_1HOUR_FILTER)
private:
#if (USE_1HOUR_FILTER)
bool b1HourList; // 1시간 단위 리스트
public:
int count1Hour; // 텍스트 표시용
//! \brief 데이터 로딩 후 All 및 1Hour 영상 개수 확인
void count1HourItems();
QDateTime simpleFromDateTime(QDateTime& startTime);
//! \brief 리스트 텍스트 색사용
//! \param startTime
//! \return
QDateTime simpleFromDateTime2(QDateTime& startTime);
private:
//! \brief 현재 선택된 아이템이 전체<->1HOUR 리스트 전환시 유지
//! \param searchItem: 현재 선택된 아이템
//! \return 이후 선택될 아이템
RMVideoItem* findFirstItemIn1Hour(RMVideoItem* searchItem);
#endif // #if (USE_1HOUR_FILTER)
QList<RMVideoItem*> _items; // 전체 아이템
QList<RMVideoItem*> _filteredItems; // 필터 적용된 아이템
#if !(USE_1HOUR_FILTER)
void _updateItemsByFilter(); // 필터 적용 후
#endif // #if (USE_1HOUR_FILTER)
#if !(RECURSIVE_APPEND_FILE)
static void _appendFrontRear(QString& folder,QList<QUrl>& list);
#endif
#if (!(FORCE_2CH) && (!SINGLE_CH_VIEWER))
#if (TRI_CHANNEL || PENTA_CHANNEL)
QString _checkChannelInfo(QString filePath,int *ch);
#else // TRI_CHANNEL
QString _checkChannelInfo(QString filePath,bool *isCH2, bool *is2CH = NULL);
#endif // TRI_CHANNEL
bool _addOtherChannelFile(RMVideoItem* item);
#endif
void _backupOverwrite(QString target,QString src,int countLeft);
public:
void loadFromList(QList<QUrl> list,bool bPlayFirstAdded = false);
//! \brief 리스트 삭제하고 이벤트 전달
void clearList();
//! \brief item 존재할 경우 파일 재생
//! \param item: 재생할 item
void playItem(RMVideoItem* item);
//! \brief 파일 경로로 item 탐색
//! \param path : 탐색할 경로
//! \return
RMVideoItem* itemWithPath(QString path);
//!
//! \brief 파일 로딩 후 선택된 녹화타입 확인용
//! 현재 선택된 필터의 INDEX 를 리턴
//!
int currentFilterIndex();
protected:
int _filter;
RMVideoItem* _playItem; // 현재 플레이 아이템
void backup(QString dest); // 보존
bool addItem(QString filePath, QDateTime* pDateTime,GROUP_TYPE type);
bool itemExist(QString filePath);
// List Widget 에서만 호출 할 수 있도록 변경
void setPlayItem(RMVideoItem* item)
{
_playItem = item;
}
public:
explicit RMVideoFileList(QObject* parent = 0);
//! \brief 파일 시작명으로 재생 아이템 탐색
//! \param prefix: eg. 20231007-040556_PSR0_0017
//! \return 탐색된 재생 리스트 아이템 , 없으면 NULL
RMVideoItem* searchPlayItem(QString prefix);
static FILTER filterTypeFromGroupType(GROUP_TYPE type)
{
FILTER t = FILTER_NONE;
switch (type) {
#if (RM_MODEL == RM_MODEL_TYPE_XLDR_88)
case TYPE_NORMAL:
case TYPE_MANUAL:
t = FILTER_NORMAL;
break;
case TYPE_EVENT:
case TYPE_PARKING_EVENT:
case TYPE_PARKING_MOTION:
t = FILTER_EVENT;
break;
case TYPE_PARKING:
t = FILTER_PARK;
break;
}
#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 || RM_MODEL == RM_MODEL_TYPE_MH9000)
case TYPE_NORMAL:
t = FILTER_NORMAL;
break;
//#if !(RM_MODEL == RM_MODEL_TYPE_MH9000)
case TYPE_MANUAL:
t = FILTER_MANUAL;
break;
//#endif // RM_MODEL == RM_MODEL_TYPE_MH9000
case TYPE_PARKING:
case TYPE_PARKING_EVENT:
t = FILTER_PARK;
break;
case TYPE_EVENT:
case TYPE_PARKING_MOTION:
t = FILTER_EVENT;
break;
}
#elif (RM_MODEL_EMT_KR)
case TYPE_NORMAL:
t = FILTER_NORMAL;
break;
case TYPE_EVENT:
t = FILTER_EVENT;
break;
case TYPE_PARKING:
t = FILTER_PARK;
break;
case TYPE_PARKING_EVENT:
t = FILTER_PARK_EVENT;
break;
case TYPE_MANUAL:
t = FILTER_MANUAL;
break;
case TYPE_MY_BOX:
t = FILTER_MYBOX;
break;
}
#else
case TYPE_NORMAL:
t = FILTER_NORMAL;
break;
case TYPE_EVENT:
case TYPE_PARKING_EVENT:
case TYPE_PARKING_MOTION:
t = FILTER_EVENT;
break;
}
#endif
return t;
}
QList<RMVideoItem*>& allItems() { return _items; }
QList<RMVideoItem*>& filteredItems()
{
return _filteredItems;
}
void checkedItems(QList<RMVideoItem*>& items);
#if (PLAYER_ONLY_LIBRARY_MODE)
static RMVideoFileList * _instance;
static void updateInstance(RMVideoFileList* instance)
{
_instance = instance;
}
static RMVideoFileList* instance()
{
return _instance;
}
#else // PLAYER_ONLY_LIBRARY_MODE
// singletone
static RMVideoFileList* instance()
{
static RMVideoFileList * _instance = 0;
if ( _instance == 0 ) {
_instance = new RMVideoFileList();
}
return _instance;
}
#endif // PLAYER_ONLY_LIBRARY_MODE
#if (RM_MODEL_EMT_KR)
/**
* @brief 로딩된 파일 중 가장 많은 파일이 존재하는 녹화타입 선택
*/
void selectMaxCountFilter();
#endif // RM_MODEL_EMT_KR
#if !(USE_1HOUR_FILTER)
bool checkFilter(RMVideoFileList::FILTER filter)
{
return ((_filter & filter) == filter);
}
void setFilter(RMVideoFileList::FILTER filter, bool on);
void setFilterSingle(RMVideoFileList::FILTER filter);
#endif // #if !(USE_1HOUR_FILTER)
RMVideoItem* getPlayItem() // 현재 플레이 중이면 다시 플레이 하지 않는다.
{
return _playItem;
}
int getPlayIndex() // 현재 그룹에서 플레이 중인 인덱스 리턴
{
if(_playItem == NULL)
{
return -1;
}
return filteredItems().indexOf(_playItem);
}
//! \brief 영상 경로를 썸네일 경로로 변경
//! \param item: 추출할 아이템
//! \return
static QPair<QString,QString> getThumbnailPath(RMVideoItem* item);
static bool isRootPath(QString folderPath);
int removeItem(RMVideoItem* item); // 플레이중 삭제 등으로 사라진 아이템 제거
// List.appendToList
#if (RECURSIVE_APPEND_FILE)
static void appendFolderToList(QString rootFolderPath,QList<QUrl>& list,int depth=0);
#else
static void appendFolderToList(QString rootFolderPath,QList<QUrl>& list);
// Group.appendToList
static void appendToList(QString folderPath,QList<QUrl>& list, bool baseFolderOnly);
#endif
static RMVideoFileList::GROUP_TYPE checkGroupTypeFromFolderPath(QString folderPath);
#if (RM_MODEL == RM_MODEL_TYPE_AN6000)
static int parseSerial(QString baseName);
static QString groupTypeFromFilePath(QString filePath,RMVideoFileList::GROUP_TYPE* type, int* serial);
#else // RM_MODEL_TYPE_AN6000
static QString groupTypeFromFilePath(QString filePath,RMVideoFileList::GROUP_TYPE* type);
#endif // RM_MODEL_TYPE_AN6000
bool isNextPlayItemExist(bool bNext);
RMVideoItem* nexItem(bool bNext);
private:
bool _searchItem(bool next, RMVideoItem** item,int fromIndex); // 탐색 next = +1, previous = -1
public slots:
void onPlayNextVideo(int fromIndex);
void onPlayPreviousVideo(int fromIndex);
signals:
// 리스트 업데이트 시작(파일 로딩 또는 필터 변경시 호출)
void listUpdateStarted(bool bLoading);
void listUpdateEnd(bool bLoading,RMVideoItem* selected);
// 리스트의 경우 타입이 변경되어도 이벤트가 발생하니 로딩 종료시에만 처리
void loadListEnd();
void backupStarted();
void backupEnd();
void backupPaused(bool bPaused);
// 다음 플레이 아이템 확인, 더이상 없음
void playItemFound(RMVideoItem* item,int old);
void playNoMoreItem();
void updateProgress(int value);
};
#endif // RM_VIDEOFILELIST_H

View File

@@ -0,0 +1,6 @@
#include "rm_video_list_loader.h"
//RMVideoListLoader::RMVideoListLoader()
//{
//}

View File

@@ -0,0 +1,42 @@
#ifndef RM_VIDEO_LIST_LOADER_H
#define RM_VIDEO_LIST_LOADER_H
#include <QObject>
#include <QRunnable>
#include <QThreadPool>
#include "../rm_include.h"
#include "rm_video_list.h"
class RMVideoFileListLoader : public QRunnable
{
private:
QList<QUrl> _list;
bool _bPlayFirstAdded; // 추가된 1st 파일 즉시 플레이
public:
RMVideoFileListLoader(QList<QUrl>& list, bool bPlayFirstAdded = false)
{
_bPlayFirstAdded = bPlayFirstAdded;
_list = list;
}
virtual void run()
{
RMVideoFileList::instance()->loadFromList(_list,_bPlayFirstAdded);
}
};
class RMVideoFileListBackup : public QRunnable
{
private:
QString _dest;
public:
RMVideoFileListBackup(QString dest)
{
_dest = dest;
}
virtual void run()
{
RMVideoFileList::instance()->backup(_dest);
}
};
#endif // RM_VIDEO_LIST_LOADER_H

View File

@@ -0,0 +1,275 @@
/******************************************************************************
QtAV: Multimedia framework based on Qt and FFmpeg
Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
* This file is part of QtAV
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
******************************************************************************/
#include "AVClock.h"
#include <QtCore/QTimer>
#include <QtCore/QTimerEvent>
#include <QtCore/QDateTime>
#include "Logger.h"
namespace FAV {
enum {
kRunning,
kPaused,
kStopped
};
AVClock::AVClock(AVClock::ClockType c, QObject *parent):
QObject(parent)
, auto_clock(true)
, m_state(kStopped)
, clock_type(c)
, mSpeed(1.0)
, value0(0)
, avg_err(0)
, nb_restarted(0)
, nb_sync(0)
, sync_id(0)
{
last_pts = pts_ = pts_v = delay_ = 0;
}
AVClock::AVClock(QObject *parent):
QObject(parent)
, auto_clock(true)
, m_state(kStopped)
, clock_type(AudioClock)
, mSpeed(1.0)
, value0(0)
, avg_err(0)
, nb_restarted(0)
, nb_sync(0)
, sync_id(0)
{
last_pts = pts_ = pts_v = delay_ = 0;
}
void AVClock::setClockType(ClockType ct)
{
if (clock_type == ct)
return;
clock_type = ct;
QTimer::singleShot(0, this, SLOT(restartCorrectionTimer()));
}
AVClock::ClockType AVClock::clockType() const
{
return clock_type;
}
bool AVClock::isActive() const
{
return clock_type == AudioClock || timer.isValid();
}
void AVClock::setInitialValue(double v)
{
value0 = v;
qDebug("Clock initial value: %f", v);
}
double AVClock::initialValue() const
{
return value0;
}
void AVClock::setClockAuto(bool a)
{
auto_clock = a;
}
bool AVClock::isClockAuto() const
{
return auto_clock;
}
void AVClock::updateExternalClock(qint64 msecs)
{
if (clock_type == AudioClock)
{
return;
}
#if !(OFF_OTHER_DEBUG)
qDebug("External clock change: %f ==> %f", value(), double(msecs) * kThousandth);
#endif
pts_ = double(msecs) * kThousandth; //can not use msec/1000.
if (!isPaused())
{
timer.restart();
}
last_pts = pts_;
t = QDateTime::currentMSecsSinceEpoch();
if (clockType() == VideoClock)
{
pts_v = pts_;
}
}
void AVClock::updateExternalClock(const AVClock &clock)
{
if (clock_type != ExternalClock)
{
return;
}
#if !(OFF_OTHER_DEBUG)
qDebug("External clock change: %f ==> %f", value(), clock.value());
#endif
pts_ = clock.value();
// qInfo() << "C:" << pts_;
if (!isPaused())
timer.restart();
last_pts = pts_;
t = QDateTime::currentMSecsSinceEpoch();
}
void AVClock::setSpeed(qreal speed)
{
mSpeed = speed;
}
bool AVClock::isPaused() const
{
return m_state == kPaused;
}
// SYNC START 처리하면
//
int AVClock::syncStart(int count)
{
static int sId = 0;
nb_sync = count;
if (sId == -1) {
sId = 0;
}
sync_id = ++sId;
return sId;
}
bool AVClock::syncEndOnce(int id)
{
if (id != sync_id)
{
qWarning("bad sync id: %d, current: %d", id, sync_id);
return true;
}
if (!nb_sync.deref()) {
sync_id = 0;
}
return sync_id;
}
void AVClock::start()
{
m_state = kRunning;
qDebug("AVClock started!!!!!!!!");
timer.start();
QTimer::singleShot(0, this, SLOT(restartCorrectionTimer()));
Q_EMIT started();
}
//remember last value because we don't reset pts_, pts_v, delay_
void AVClock::pause(bool p)
{
if (isPaused() == p)
return;
if (clock_type == AudioClock)
return;
m_state = p ? kPaused : kRunning;
if (p) {
QTimer::singleShot(0, this, SLOT(stopCorrectionTimer()));
#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
timer.invalidate();
#else
timer.stop();
#endif //QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
Q_EMIT paused();
} else {
timer.start();
QTimer::singleShot(0, this, SLOT(restartCorrectionTimer()));
Q_EMIT resumed();
}
t = QDateTime::currentMSecsSinceEpoch();
Q_EMIT paused(p);
}
void AVClock::reset()
{
nb_sync = 0;
sync_id = 0;
// keep mSpeed
m_state = kStopped;
value0 = 0;
pts_ = pts_v = delay_ = 0;
QTimer::singleShot(0, this, SLOT(stopCorrectionTimer()));
#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
timer.invalidate();
#else
timer.stop();
#endif //QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
t = QDateTime::currentMSecsSinceEpoch();
Q_EMIT resetted();
}
void AVClock::timerEvent(QTimerEvent *event)
{
Q_ASSERT_X(clockType() != AudioClock, "AVClock::timerEvent", "Internal error. AudioClock can not call this");
if (event->timerId() != correction_schedule_timer.timerId())
return;
if (isPaused())
return;
const double delta_pts = (value() - last_pts)/speed();
//const double err = double(correction_timer.restart()) * kThousandth - delta_pts;
const qint64 now = QDateTime::currentMSecsSinceEpoch();
const double err = double(now - t) * kThousandth - delta_pts;
t = now;
// FIXME: avfoundation camera error is large (about -0.6s)
if (qAbs(err*10.0) < kCorrectionInterval || clock_type == VideoClock) {
avg_err += err/(nb_restarted+1);
}
//qDebug("correction timer event. error = %f, avg_err=%f, nb_restarted=%d", err, avg_err, nb_restarted);
last_pts = value();
nb_restarted = 0;
}
void AVClock::restartCorrectionTimer()
{
nb_restarted = 0;
avg_err = 0;
correction_schedule_timer.stop();
if (clockType() == AudioClock) // TODO: for all clock type
return;
// parameters are reset. do not start correction timer if not running
if (m_state != kRunning)
return;
// timer is always started in AVClock::start()
if (!timer.isValid())
return;
t = QDateTime::currentMSecsSinceEpoch();
correction_schedule_timer.start(kCorrectionInterval*1000, this);
}
void AVClock::stopCorrectionTimer()
{
nb_restarted = 0;
avg_err = 0;
correction_schedule_timer.stop();
}
} //namespace FAV

View File

@@ -0,0 +1,221 @@
/******************************************************************************
QtAV: Multimedia framework based on Qt and FFmpeg
Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
* This file is part of QtAV
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
******************************************************************************/
#ifndef QTAV_AVCLOCK_H
#define QTAV_AVCLOCK_H
#include "_fav_constants.h"
#include <QtCore/QAtomicInt>
#include <QtCore/QBasicTimer>
#include <QtCore/QObject>
#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
#include <QtCore/QElapsedTimer>
#else
#include <QtCore/QTime>
typedef QTime QElapsedTimer;
#endif
/*
* AVClock is created by AVPlayer. The only way to access AVClock is through AVPlayer::masterClock()
* The default clock type is Audio's clock, i.e. vedio synchronizes to audio. If audio stream is not
* detected, then the clock will set to External clock automatically.
* I name it ExternalClock because the clock can be corrected outside, though it is a clock inside AVClock
*/
namespace FAV {
static const double kThousandth = 0.001;
class Q_AV_EXPORT AVClock : public QObject
{
Q_OBJECT
public:
typedef enum {
AudioClock = 0,
ExternalClock = 1,
VideoClock = 2, //sync to video timestamp
} ClockType;
AVClock(ClockType c, QObject* parent = 0);
AVClock(QObject* parent = 0);
void setClockType(ClockType ct);
ClockType clockType() const;
bool isActive() const;
/*!
* \brief setInitialValue
* Usually for ExternalClock. For example, media start time is not 0, clock have to set initial value as media start time
*/
void setInitialValue(double v);
double initialValue() const;
/*
* auto clock: use audio clock if audio stream found, otherwise use external clock
*/
void setClockAuto(bool a);
bool isClockAuto() const;
/*in seconds*/
inline double pts() const;
/*!
* \brief value
* the real timestamp in seconds: pts + delay
* \return
*/
inline double value() const;
inline void updateValue(double pts); //update the pts
/*used when seeking and correcting from external*/
void updateExternalClock(qint64 msecs);
/*external clock outside still running, so it's more accurate for syncing multiple clocks serially*/
void updateExternalClock(const AVClock& clock);
inline void updateVideoTime(double pts);
inline double videoTime() const;
inline double delay() const; //playing audio spends some time
inline void updateDelay(double delay);
inline qreal diff() const;
void setSpeed(qreal speed);
inline qreal speed() const;
bool isPaused() const;
/*!
* \brief syncStart
* For internal use now
* Start to sync "count" objects. Call syncEndOnce(id) "count" times to end sync.
* \param count Number of objects to sync. Each one should call syncEndOnce(int id)
* \return an id
*/
int syncStart(int count);
int syncId() const {return sync_id;}
/*!
* \brief syncEndOnce
* Decrease sync objects count if id is current sync id.
* \return true if sync is end for id or id is not current sync id
*/
bool syncEndOnce(int id);
Q_SIGNALS:
void paused(bool);
void paused(); //equals to paused(true)
void resumed();//equals to paused(false)
void started();
void resetted();
public Q_SLOTS:
//these slots are not frequently used. so not inline
/*start the external clock*/
void start();
/*pause external clock*/
void pause(bool p);
/*reset clock intial value and external clock parameters (and stop timer). keep speed() and isClockAuto()*/
void reset();
protected:
virtual void timerEvent(QTimerEvent *event);
private Q_SLOTS:
/// make sure QBasic timer start/stop in a right thread
void restartCorrectionTimer();
void stopCorrectionTimer();
private:
bool auto_clock;
int m_state;
ClockType clock_type;
mutable double pts_;
mutable double pts_v;
double delay_;
mutable QElapsedTimer timer;
qreal mSpeed;
double value0;
/*!
* \brief correction_schedule_timer
* accumulative error is too large using QElapsedTimer.restart() frequently.
* we periodically correct value() to keep the error always less
* than the error of calling QElapsedTimer.restart() once
* see github issue 46, 307 etc
*/
QBasicTimer correction_schedule_timer;
qint64 t; // absolute time for elapsed timer correction
static const int kCorrectionInterval = 1; // 1000ms
double last_pts;
double avg_err; // average error of restart()
mutable int nb_restarted;
QAtomicInt nb_sync;
int sync_id;
};
double AVClock::value() const
{
if (clock_type == AudioClock) {
// TODO: audio clock need a timer too
// timestamp from media stream is >= value0
return pts_ == 0 ? value0 : pts_ + delay_;
} else if (clock_type == ExternalClock) {
if (timer.isValid()) {
++nb_restarted;
pts_ += (double(timer.restart()) * kThousandth + avg_err)* speed();
} else {//timer is paused
//qDebug("clock is paused. return the last value %f", pts_);
}
return pts_ + value0;
} else {
return pts_v; // value0 is 1st video pts_v already
}
}
void AVClock::updateValue(double pts)
{
if (clock_type == AudioClock)
{
pts_ = pts;
}
}
void AVClock::updateVideoTime(double pts)
{
pts_v = pts;
if (clock_type == VideoClock)
timer.restart();
}
double AVClock::videoTime() const
{
return pts_v;
}
double AVClock::delay() const
{
return delay_;
}
void AVClock::updateDelay(double delay)
{
delay_ = delay;
}
qreal AVClock::diff() const
{
return value() - videoTime();
}
qreal AVClock::speed() const
{
return mSpeed;
}
} //namespace FAV
#endif // QTAV_AVCLOCK_H

View File

@@ -0,0 +1,415 @@
/******************************************************************************
QtAV: Multimedia framework based on Qt and FFmpeg
Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
* This file is part of QtAV
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
******************************************************************************/
#include "AVCompat.h"
#include "version.h"
#if !FFMPEG_MODULE_CHECK(LIBAVFORMAT, 56, 4, 101)
int avio_feof(AVIOContext *s)
{
#if QTAV_USE_FFMPEG(LIBAVFORMAT)
return url_feof(s);
#else
return s && s->eof_reached;
#endif
}
#endif
#if QTAV_USE_LIBAV(LIBAVFORMAT)
int avformat_alloc_output_context2(AVFormatContext **avctx, AVOutputFormat *oformat, const char *format, const char *filename)
{
AVFormatContext *s = avformat_alloc_context();
int ret = 0;
*avctx = NULL;
if (!s)
goto nomem;
if (!oformat) {
if (format) {
oformat = av_guess_format(format, NULL, NULL);
if (!oformat) {
av_log(s, AV_LOG_ERROR, "Requested output format '%s' is not a suitable output format\n", format);
ret = AVERROR(EINVAL);
goto error;
}
} else {
oformat = av_guess_format(NULL, filename, NULL);
if (!oformat) {
ret = AVERROR(EINVAL);
av_log(s, AV_LOG_ERROR, "Unable to find a suitable output format for '%s'\n",
filename);
goto error;
}
}
}
s->oformat = oformat;
if (s->oformat->priv_data_size > 0) {
s->priv_data = av_mallocz(s->oformat->priv_data_size);
if (!s->priv_data)
goto nomem;
if (s->oformat->priv_class) {
*(const AVClass**)s->priv_data= s->oformat->priv_class;
av_opt_set_defaults(s->priv_data);
}
} else
s->priv_data = NULL;
if (filename)
av_strlcpy(s->filename, filename, sizeof(s->filename));
*avctx = s;
return 0;
nomem:
av_log(s, AV_LOG_ERROR, "Out of memory\n");
ret = AVERROR(ENOMEM);
error:
avformat_free_context(s);
return ret;
}
#endif //QTAV_USE_LIBAV(LIBAVFORMAT)
#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 32, 0)
static const struct {
const char *name;
int nb_channels;
uint64_t layout;
} channel_layout_map[] = {
{ "mono", 1, AV_CH_LAYOUT_MONO },
{ "stereo", 2, AV_CH_LAYOUT_STEREO },
{ "4.0", 4, AV_CH_LAYOUT_4POINT0 },
{ "quad", 4, AV_CH_LAYOUT_QUAD },
{ "5.0", 5, AV_CH_LAYOUT_5POINT0 },
{ "5.0", 5, AV_CH_LAYOUT_5POINT0_BACK },
{ "5.1", 6, AV_CH_LAYOUT_5POINT1 },
{ "5.1", 6, AV_CH_LAYOUT_5POINT1_BACK },
{ "5.1+downmix", 8, AV_CH_LAYOUT_5POINT1|AV_CH_LAYOUT_STEREO_DOWNMIX, },
{ "7.1", 8, AV_CH_LAYOUT_7POINT1 },
{ "7.1(wide)", 8, AV_CH_LAYOUT_7POINT1_WIDE },
{ "7.1+downmix", 10, AV_CH_LAYOUT_7POINT1|AV_CH_LAYOUT_STEREO_DOWNMIX, },
{ 0 }
};
int64_t av_get_default_channel_layout(int nb_channels) {
int i;
for (i = 0; i < FF_ARRAY_ELEMS(channel_layout_map); i++)
if (nb_channels == channel_layout_map[i].nb_channels)
return channel_layout_map[i].layout;
return 0;
}
#endif
/*
* always need this function if avresample available
* use AVAudioResampleContext to avoid func type confliction when swr is also available
*/
#if QTAV_HAVE(AVRESAMPLE)
AVAudioResampleContext *swr_alloc_set_opts(AVAudioResampleContext *s
, int64_t out_ch_layout
, enum AVSampleFormat out_sample_fmt
, int out_sample_rate
, int64_t in_ch_layout
, enum AVSampleFormat in_sample_fmt
, int in_sample_rate
, int log_offset, void *log_ctx)
{
//DO NOT use swr_alloc() because it's not defined as a macro in QtAV_Compat.h
if (!s)
s = avresample_alloc_context();
if (!s)
return 0;
Q_UNUSED(log_offset);
Q_UNUSED(log_ctx);
av_opt_set_int(s, "out_channel_layout", out_ch_layout , 0);
av_opt_set_int(s, "out_sample_fmt" , out_sample_fmt , 0);
av_opt_set_int(s, "out_sample_rate" , out_sample_rate, 0);
av_opt_set_int(s, "in_channel_layout" , in_ch_layout , 0);
av_opt_set_int(s, "in_sample_fmt" , in_sample_fmt , 0);
av_opt_set_int(s, "in_sample_rate" , in_sample_rate , 0);
return s;
}
#endif
#if !AV_MODULE_CHECK(LIBAVUTIL, 52, 3, 0, 13, 100)
extern const AVPixFmtDescriptor av_pix_fmt_descriptors[];
const AVPixFmtDescriptor *av_pix_fmt_desc_get(AVPixelFormat pix_fmt)
{
if (pix_fmt < 0 || pix_fmt >= QTAV_PIX_FMT_C(NB))
return NULL;
return &av_pix_fmt_descriptors[pix_fmt];
}
const AVPixFmtDescriptor *av_pix_fmt_desc_next(const AVPixFmtDescriptor *prev)
{
if (!prev)
return &av_pix_fmt_descriptors[0];
// can not use sizeof(av_pix_fmt_descriptors)
while (prev - av_pix_fmt_descriptors < QTAV_PIX_FMT_C(NB) - 1) {
prev++;
if (prev->name)
return prev;
}
return NULL;
}
AVPixelFormat av_pix_fmt_desc_get_id(const AVPixFmtDescriptor *desc)
{
if (desc < av_pix_fmt_descriptors ||
desc >= av_pix_fmt_descriptors + QTAV_PIX_FMT_C(NB))
return QTAV_PIX_FMT_C(NONE);
return AVPixelFormat(desc - av_pix_fmt_descriptors);
}
#endif // !AV_MODULE_CHECK(LIBAVUTIL, 52, 3, 0, 13, 100)
#if !FFMPEG_MODULE_CHECK(LIBAVUTIL, 52, 48, 101)
enum AVColorSpace av_frame_get_colorspace(const AVFrame *frame)
{
if (!frame)
return AVCOL_SPC_NB;
#if LIBAV_MODULE_CHECK(LIBAVUTIL, 53, 16, 0) //8c02adc
return frame->colorspace;
#endif
return AVCOL_SPC_NB;
}
enum AVColorRange av_frame_get_color_range(const AVFrame *frame)
{
if (!frame)
return AVCOL_RANGE_UNSPECIFIED;
#if LIBAV_MODULE_CHECK(LIBAVUTIL, 53, 16, 0) //8c02adc
return frame->color_range;
#endif
return AVCOL_RANGE_UNSPECIFIED;
}
#endif //!FFMPEG_MODULE_CHECK(LIBAVUTIL, 52, 28, 101)
#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 38, 100)
int av_pix_fmt_count_planes(AVPixelFormat pix_fmt)
{
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
int i, planes[4] = { 0 }, ret = 0;
if (!desc)
return AVERROR(EINVAL);
for (i = 0; i < desc->nb_components; i++)
planes[desc->comp[i].plane] = 1;
for (i = 0; i < (int)FF_ARRAY_ELEMS(planes); i++)
ret += planes[i];
return ret;
}
#endif //AV_VERSION_INT(52, 38, 100)
#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 73, 101)
int av_samples_copy(uint8_t **dst, uint8_t * const *src, int dst_offset,
int src_offset, int nb_samples, int nb_channels,
enum AVSampleFormat sample_fmt)
{
int planar = av_sample_fmt_is_planar(sample_fmt);
int planes = planar ? nb_channels : 1;
int block_align = av_get_bytes_per_sample(sample_fmt) * (planar ? 1 : nb_channels);
int data_size = nb_samples * block_align;
int i;
dst_offset *= block_align;
src_offset *= block_align;
if((dst[0] < src[0] ? src[0] - dst[0] : dst[0] - src[0]) >= data_size) {
for (i = 0; i < planes; i++)
memcpy(dst[i] + dst_offset, src[i] + src_offset, data_size);
} else {
for (i = 0; i < planes; i++)
memmove(dst[i] + dst_offset, src[i] + src_offset, data_size);
}
return 0;
}
#endif //AV_VERSION_INT(51, 73, 101)
#if QTAV_USE_LIBAV(LIBAVCODEC)
const char *avcodec_get_name(enum AVCodecID id)
{
const AVCodecDescriptor *cd;
AVCodec *codec;
if (id == AV_CODEC_ID_NONE)
return "none";
cd = avcodec_descriptor_get(id);
if (cd)
return cd->name;
av_log(NULL, AV_LOG_WARNING, "Codec 0x%x is not in the full list.\n", id);
codec = avcodec_find_decoder(id);
if (codec)
return codec->name;
codec = avcodec_find_encoder(id);
if (codec)
return codec->name;
return "unknown_codec";
}
#endif
#if !AV_MODULE_CHECK(LIBAVCODEC, 55, 55, 0, 68, 100)
void av_packet_rescale_ts(AVPacket *pkt, AVRational src_tb, AVRational dst_tb)
{
if (pkt->pts != (int64_t)AV_NOPTS_VALUE)
pkt->pts = av_rescale_q(pkt->pts, src_tb, dst_tb);
if (pkt->dts != (int64_t)AV_NOPTS_VALUE)
pkt->dts = av_rescale_q(pkt->dts, src_tb, dst_tb);
if (pkt->duration > 0)
pkt->duration = av_rescale_q(pkt->duration, src_tb, dst_tb);
if (pkt->convergence_duration > 0)
pkt->convergence_duration = av_rescale_q(pkt->convergence_duration, src_tb, dst_tb);
}
#endif
// since libav-11, ffmpeg-2.1
#if !LIBAV_MODULE_CHECK(LIBAVCODEC, 56, 1, 0) && !FFMPEG_MODULE_CHECK(LIBAVCODEC, 55, 39, 100)
int av_packet_copy_props(AVPacket *dst, const AVPacket *src)
{
dst->pts = src->pts;
dst->dts = src->dts;
dst->pos = src->pos;
dst->duration = src->duration;
dst->convergence_duration = src->convergence_duration;
dst->flags = src->flags;
dst->stream_index = src->stream_index;
for (int i = 0; i < src->side_data_elems; i++) {
enum AVPacketSideDataType type = src->side_data[i].type;
int size = src->side_data[i].size;
uint8_t *src_data = src->side_data[i].data;
uint8_t *dst_data = av_packet_new_side_data(dst, type, size);
if (!dst_data) {
av_packet_free_side_data(dst);
return AVERROR(ENOMEM);
}
memcpy(dst_data, src_data, size);
}
return 0;
}
#endif
// since libav-10, ffmpeg-2.1
#if !LIBAV_MODULE_CHECK(LIBAVCODEC, 55, 34, 1) && !FFMPEG_MODULE_CHECK(LIBAVCODEC, 55, 39, 100)
void av_packet_free_side_data(AVPacket *pkt)
{
for (int i = 0; i < pkt->side_data_elems; ++i)
av_freep(&pkt->side_data[i].data);
av_freep(&pkt->side_data);
pkt->side_data_elems = 0;
}
#endif
#if !AV_MODULE_CHECK(LIBAVCODEC, 55, 34, 1, 39, 101)
int av_packet_ref(AVPacket *dst, const AVPacket *src)
{
#if QTAV_USE_FFMPEG(LIBAVCODEC)
return av_copy_packet(dst, const_cast<AVPacket*>(src)); // not const in these versions
#else // libav <=11 has no av_copy_packet
#define DUP_DATA(dst, src, size, padding) \
do { \
void *data; \
if (padding) { \
if ((unsigned)(size) > \
(unsigned)(size) + FF_INPUT_BUFFER_PADDING_SIZE) \
goto failed_alloc; \
data = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE); \
} else { \
data = av_malloc(size); \
} \
if (!data) \
goto failed_alloc; \
memcpy(data, src, size); \
if (padding) \
memset((uint8_t*)data + size, 0, \
FF_INPUT_BUFFER_PADDING_SIZE); \
*((void**)&dst) = data; \
} while (0)
*dst = *src;
dst->data = NULL;
dst->side_data = NULL;
DUP_DATA(dst->data, src->data, dst->size, 1);
dst->destruct = av_destruct_packet;
if (dst->side_data_elems) {
int i;
DUP_DATA(dst->side_data, src->side_data,
dst->side_data_elems * sizeof(*dst->side_data), 0);
memset(dst->side_data, 0,
dst->side_data_elems * sizeof(*dst->side_data));
for (i = 0; i < dst->side_data_elems; i++) {
DUP_DATA(dst->side_data[i].data, src->side_data[i].data, src->side_data[i].size, 1);
dst->side_data[i].size = src->side_data[i].size;
dst->side_data[i].type = src->side_data[i].type;
}
}
return 0;
failed_alloc:
av_destruct_packet(dst);
return AVERROR(ENOMEM);
#endif
}
#endif
#if !AV_MODULE_CHECK(LIBAVCODEC, 55, 52, 0, 63, 100)
void avcodec_free_context(AVCodecContext **pavctx)
{
AVCodecContext *avctx = *pavctx;
if (!avctx)
return;
avcodec_close(avctx);
av_freep(&avctx->extradata);
av_freep(&avctx->subtitle_header);
av_freep(&avctx->intra_matrix);
av_freep(&avctx->inter_matrix);
av_freep(&avctx->rc_override);
av_freep(pavctx);
}
#endif
const char *get_codec_long_name(enum AVCodecID id)
{
if (id == AV_CODEC_ID_NONE)
return "none";
const AVCodecDescriptor *cd = avcodec_descriptor_get(id);
if (cd)
return cd->long_name;
av_log(NULL, AV_LOG_WARNING, "Codec 0x%x is not in the full list.\n", id);
AVCodec *codec = avcodec_find_decoder(id);
if (codec)
return codec->long_name;
codec = avcodec_find_encoder(id);
if (codec)
return codec->long_name;
return "unknown_codec";
}
#if QTAV_HAVE(AVFILTER)
#if !AV_MODULE_CHECK(LIBAVFILTER, 2, 22, 0, 79, 100) //FF_API_AVFILTERPAD_PUBLIC
const char *avfilter_pad_get_name(const AVFilterPad *pads, int pad_idx)
{
return pads[pad_idx].name;
}
enum AVMediaType avfilter_pad_get_type(const AVFilterPad *pads, int pad_idx)
{
return pads[pad_idx].type;
}
#endif
#endif //QTAV_HAVE(AVFILTER)

View File

@@ -0,0 +1,460 @@
/******************************************************************************
QtAV: Multimedia framework based on Qt and FFmpeg
solve the version problem and diffirent api in FFmpeg and libav
Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
* This file is part of QtAV
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
******************************************************************************/
#ifndef QTAV_COMPAT_H
#define QTAV_COMPAT_H
/*!
NOTE: include this at last
*/
#define QTAV_USE_FFMPEG(MODULE) (MODULE##_VERSION_MICRO >= 100)
#define QTAV_USE_LIBAV(MODULE) !QTAV_USE_FFMPEG(MODULE)
#define FFMPEG_MODULE_CHECK(MODULE, MAJOR, MINOR, MICRO) \
(QTAV_USE_FFMPEG(MODULE) && MODULE##_VERSION_INT >= AV_VERSION_INT(MAJOR, MINOR, MICRO))
#define LIBAV_MODULE_CHECK(MODULE, MAJOR, MINOR, MICRO) \
(QTAV_USE_LIBAV(MODULE) && MODULE##_VERSION_INT >= AV_VERSION_INT(MAJOR, MINOR, MICRO))
#define AV_MODULE_CHECK(MODULE, MAJOR, MINOR, MICRO, MINOR2, MICRO2) \
(LIBAV_MODULE_CHECK(MODULE, MAJOR, MINOR, MICRO) || FFMPEG_MODULE_CHECK(MODULE, MAJOR, MINOR2, MICRO2))
/// example: AV_ENSURE(avcodec_close(avctx), false) will print error and return false if failed. AV_WARN just prints error.
#define AV_ENSURE_OK(FUNC, ...) AV_RUN_CHECK(FUNC, return, __VA_ARGS__)
#define AV_ENSURE(FUNC, ...) AV_RUN_CHECK(FUNC, return, __VA_ARGS__)
#define AV_WARN(FUNC) AV_RUN_CHECK(FUNC, void)
#include "_fav_constants.h"
#ifdef __cplusplus
extern "C"
{
/*UINT64_C: C99 math features, need -D__STDC_CONSTANT_MACROS in CXXFLAGS*/
#endif /*__cplusplus*/
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
#include <libavutil/avstring.h>
#include <libavutil/dict.h>
#include <libavutil/imgutils.h>
#include <libavutil/log.h>
#include <libavutil/mathematics.h> //AV_ROUND_UP, av_rescale_rnd for libav
#include <libavutil/cpu.h>
#include <libavutil/error.h>
#include <libavutil/opt.h>
#include <libavutil/parseutils.h>
#include <libavutil/pixdesc.h>
#include <libavutil/avstring.h>
#if !FFMPEG_MODULE_CHECK(LIBAVUTIL, 51, 73, 101)
#include <libavutil/channel_layout.h>
#endif
/* TODO: how to check whether we have swresample or not? how to check avresample?*/
#include <libavutil/samplefmt.h>
#if QTAV_HAVE(SWRESAMPLE)
#include <libswresample/swresample.h>
#ifndef LIBSWRESAMPLE_VERSION_INT //ffmpeg 0.9, swr 0.5
#define LIBSWRESAMPLE_VERSION_INT AV_VERSION_INT(LIBSWRESAMPLE_VERSION_MAJOR, LIBSWRESAMPLE_VERSION_MINOR, LIBSWRESAMPLE_VERSION_MICRO)
#endif //LIBSWRESAMPLE_VERSION_INT
//ffmpeg >= 0.11.x. swr0.6.100: ffmpeg-0.10.x
#define HAVE_SWR_GET_DELAY (LIBSWRESAMPLE_VERSION_INT > AV_VERSION_INT(0, 6, 100))
#endif //QTAV_HAVE(SWRESAMPLE)
#if QTAV_HAVE(AVRESAMPLE)
#include <libavresample/avresample.h>
#endif //QTAV_HAVE(AVRESAMPLE)
#if QTAV_HAVE(AVFILTER)
#if !(USE_FFMPEG4)
#include <libavfilter/avfiltergraph.h> //code is here for old version
#endif // USE_FFMPEG4
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#endif //QTAV_HAVE(AVFILTER)
#if QTAV_HAVE(AVDEVICE)
#include <libavdevice/avdevice.h>
#endif
#ifdef __cplusplus
}
#endif /*__cplusplus*/
/*!
* Guide to uniform the api for different FFmpeg version(or other libraries)
* We use the existing old api to simulater .
* 1. The old version does not have this api: Just add it.
* 2. The old version has similar api: Try using macro.
* e.g. the old is bool my_play(char* data, size_t size)
* the new is bool my_play2(const ByteArray& data)
* change:
* #define my_play2(data) my_play(data.data(), data.size());
*
* 3. The old version api is conflicted with the latest's. We can redefine the api
* e.g. the old is bool my_play(char* data, size_t size)
* the new is bool my_play(const ByteArray& data)
* change:
* typedef bool (*my_play_t)(const ByteArray&);
* static my_play_t my_play_ptr = my_play; //using the existing my_play(char*, size_t)
* #define my_play my_play_compat
* inline bool my_play_compat(const ByteArray& data)
* {
* return my_play_ptr(data.data(), data.size());
* }
* 4. conflict macros
* see av_err2str
*/
#ifndef AV_VERSION_INT
#define AV_VERSION_INT(a, b, c) (a<<16 | b<<8 | c)
#endif /*AV_VERSION_INT*/
void ffmpeg_version_print();
#if !FFMPEG_MODULE_CHECK(LIBAVFORMAT, 56, 4, 101)
int avio_feof(AVIOContext *s);
#endif
#if QTAV_USE_LIBAV(LIBAVFORMAT)
int avformat_alloc_output_context2(AVFormatContext **avctx, AVOutputFormat *oformat, const char *format, const char *filename);
#endif
//TODO: always inline
/* --gnu option of the RVCT compiler also defines __GNUC__ */
#if defined(__GNUC__) && !(defined(__ARMCC__) || defined(__CC_ARM))
#define GCC_VERSION_AT_LEAST(major, minor, patch) \
(__GNUC__ > major || (__GNUC__ == major && (__GNUC_MINOR__ > minor \
|| (__GNUC_MINOR__ == minor && __GNUC_PATCHLEVEL__ >= patch))))
#else
/* Define this for !GCC compilers, just so we can write things like GCC_VERSION_AT_LEAST(4, 1, 0). */
#define GCC_VERSION_AT_LEAST(major, minor, patch) 0
#endif
//FFmpeg2.0, Libav10 2013-03-08 - Reference counted buffers - lavu 52.19.100/52.8.0, lavc 55.0.100 / 55.0.0, lavf 55.0.100 / 55.0.0, lavd 54.4.100 / 54.0.0, lavfi 3.5.0
#define QTAV_HAVE_AVBUFREF AV_MODULE_CHECK(LIBAVUTIL, 52, 8, 0, 19, 100)
#if defined(_MSC_VER) || !defined(av_err2str) || (GCC_VERSION_AT_LEAST(4, 7, 0) && __cplusplus)
#ifdef av_err2str
#undef av_err2str
/*#define av_make_error_string qtav_make_error_string*/
#else
/**
* Fill the provided buffer with a string containing an error string
* corresponding to the AVERROR code errnum.
*
* @param errbuf a buffer
* @param errbuf_size size in bytes of errbuf
* @param errnum error code to describe
* @return the buffer in input, filled with the error description
* @see av_strerror()
*/
static av_always_inline char *av_make_error_string(char *errbuf, size_t errbuf_size, int errnum)
{
av_strerror(errnum, errbuf, errbuf_size);
return errbuf;
}
#endif /*av_err2str*/
#define AV_ERROR_MAX_STRING_SIZE 64
#ifdef QT_CORE_LIB
#include <QtCore/QSharedPointer>
#define av_err2str(e) av_err2str_qsp(e).data()
av_always_inline QSharedPointer<char> av_err2str_qsp(int errnum)
{
QSharedPointer<char> str((char*)calloc(AV_ERROR_MAX_STRING_SIZE, 1), ::free);
av_strerror(errnum, str.data(), AV_ERROR_MAX_STRING_SIZE);
return str;
}
#else
av_always_inline char* av_err2str(int errnum)
{
static char str[AV_ERROR_MAX_STRING_SIZE];
memset(str, 0, sizeof(str));
return av_make_error_string(str, AV_ERROR_MAX_STRING_SIZE, errnum);
}
#endif /* QT_CORE_LIB */
#endif /*!defined(av_err2str) || GCC_VERSION_AT_LEAST(4, 7, 2)*/
#if (LIBAVCODEC_VERSION_INT <= AV_VERSION_INT(52,23,0))
#define avcodec_decode_audio3(avctx, samples, frame_size_ptr, avpkt) \
avcodec_decode_audio2(avctx, samples, frame_size_ptr, (*avpkt).data, (*avpkt).size);
#endif /*AV_VERSION_INT(52,23,0)*/
#if (LIBAVCODEC_VERSION_INT <= AV_VERSION_INT(52,101,0))
#define av_dump_format(...) dump_format(__VA_ARGS__)
#endif /*AV_VERSION_INT(52,101,0)*/
#if QTAV_HAVE(SWRESAMPLE) && (LIBSWRESAMPLE_VERSION_INT <= AV_VERSION_INT(0, 5, 0))
#define swresample_version() LIBSWRESAMPLE_VERSION_INT //we can not know the runtime version, so just use build time version
#define swresample_configuration() "Not available."
#define swresample_license() "Not available."
#endif
#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 32, 0)
int64_t av_get_default_channel_layout(int nb_channels);
#endif
/*
* mapping avresample to swresample
* https://github.com/xbmc/xbmc/commit/274679d
*/
#if (QTAV_HAVE(SWR_AVR_MAP) || !QTAV_HAVE(SWRESAMPLE)) && QTAV_HAVE(AVRESAMPLE)
#ifndef SWR_CH_MAX
#ifdef AVRESAMPLE_MAX_CHANNELS
#define SWR_CH_MAX AVRESAMPLE_MAX_CHANNELS
#else
#define SWR_CH_MAX 64
#endif //AVRESAMPLE_MAX_CHANNELS
#endif //SWR_CH_MAX
#define SwrContext AVAudioResampleContext
#define swr_init(ctx) avresample_open(ctx)
//free context and set pointer to null. see swresample
#define swr_free(ctx) \
if (ctx && *ctx) { \
avresample_close(*ctx); \
*ctx = 0; \
}
#define swr_get_class() avresample_get_class()
#define swr_alloc() avresample_alloc_context()
//#define swr_next_pts()
#define swr_set_compensation() avresample_set_compensation()
#define swr_set_channel_mapping(ctx, map) avresample_set_channel_mapping(ctx, map)
#define swr_set_matrix(ctx, matrix, stride) avresample_set_matrix(ctx, matrix, stride)
//#define swr_drop_output(ctx, count)
//#define swr_inject_silence(ctx, count)
#define swr_get_delay(ctx, ...) avresample_get_delay(ctx)
#if LIBAVRESAMPLE_VERSION_INT >= AV_VERSION_INT(1, 0, 0) //ffmpeg >= 1.1
#define swr_convert(ctx, out, out_count, in, in_count) \
avresample_convert(ctx, out, 0, out_count, const_cast<uint8_t**>(in), 0, in_count)
#else
#define swr_convert(ctx, out, out_count, in, in_count) \
avresample_convert(ctx, (void**)out, 0, out_count, (void**)in, 0, in_count)
#define HAVE_SWR_GET_DELAY 1
#define swr_get_delay(ctx, ...) avresample_get_delay(ctx)
#endif
struct SwrContext *swr_alloc_set_opts(struct SwrContext *s, int64_t out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate, int64_t in_ch_layout, enum AVSampleFormat in_sample_fmt, int in_sample_rate, int log_offset, void *log_ctx);
#define swresample_version() avresample_version()
#define swresample_configuration() avresample_configuration()
#define swresample_license() avresample_license()
#endif //MAP_SWR_AVR
/* For FFmpeg < 2.0
* FF_API_PIX_FMT macro?
* 51.42.0: PIX_FMT_* -> AV_PIX_FMT_*, PixelFormat -> AVPixelFormat
* so I introduce QTAV_PIX_FMT_C(X) for internal use
* FFmpeg n1.1 AVPixelFormat
*/
#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 13, 100) //(51, 42, 0)
typedef enum PixelFormat AVPixelFormat; // so we must avoid using enum AVPixelFormat
#define QTAV_PIX_FMT_C(X) PIX_FMT_##X
#else //FFmpeg >= 2.0
typedef enum AVPixelFormat AVPixelFormat;
#define QTAV_PIX_FMT_C(X) AV_PIX_FMT_##X
#endif //AV_VERSION_INT(51, 42, 0)
// FF_API_PIX_FMT
#ifdef PixelFormat
#undef PixelFormat
#endif
// AV_PIX_FMT_FLAG_XXX was PIX_FMT_XXX before FFmpeg 2.0
// AV_PIX_FMT_FLAG_ALPHA was added at 52.2.0. but version.h not changed
#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 2, 1) //git cbe5a60c9d495df0fb4775b064f06719b70b9952
#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 22, 1) //git 38d553322891c8e47182f05199d19888422167dc
#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 19, 0) //git 6b0768e2021b90215a2ab55ed427bce91d148148
#define PIX_FMT_PLANAR 16 ///< At least one pixel component is not in the first data plane
#define PIX_FMT_RGB 32 ///< The pixel format contains RGB-like data (as opposed to YUV/grayscale)
#endif //AV_VERSION_INT(51, 19, 0)
#define PIX_FMT_PSEUDOPAL 64 //why not defined in FFmpeg 0.9 lavu51.32.0 but git log says 51.22.1 defined it?
#endif //AV_VERSION_INT(51, 22, 1)
#define PIX_FMT_ALPHA 128 ///< The pixel format has an alpha channel
#endif //AV_VERSION_INT(52, 2, 1)
#ifndef PIX_FMT_PLANAR
#define PIX_FMT_PLANAR 16
#endif //PIX_FMT_PLANAR
#ifndef PIX_FMT_RGB
#define PIX_FMT_RGB 32
#endif //PIX_FMT_RGB
#ifndef PIX_FMT_PSEUDOPAL
#define PIX_FMT_PSEUDOPAL 64
#endif //PIX_FMT_PSEUDOPAL
#ifndef PIX_FMT_ALPHA
#define PIX_FMT_ALPHA 128
#endif //PIX_FMT_ALPHA
/*
* rename PIX_FMT_* flags to AV_PIX_FMT_FLAG_*. git e6c4ac7b5f038be56dfbb0171f5dd0cb850d9b28
*/
//#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 11, 0)
#ifndef AV_PIX_FMT_FLAG_BE
#define AV_PIX_FMT_FLAG_BE PIX_FMT_BE
#define AV_PIX_FMT_FLAG_PAL PIX_FMT_PAL
#define AV_PIX_FMT_FLAG_BITSTREAM PIX_FMT_BITSTREAM
#define AV_PIX_FMT_FLAG_HWACCEL PIX_FMT_HWACCEL
// FFmpeg >= 0.9, libav >= 0.8.8(51,22,1)
#define AV_PIX_FMT_FLAG_PLANAR PIX_FMT_PLANAR
#define AV_PIX_FMT_FLAG_RGB PIX_FMT_RGB
// FFmpeg >= 1.0, libav >= 9.7
#define AV_PIX_FMT_FLAG_PSEUDOPAL PIX_FMT_PSEUDOPAL
// FFmpeg >= 1.1, libav >= 9.7
#define AV_PIX_FMT_FLAG_ALPHA PIX_FMT_ALPHA
#endif //AV_PIX_FMT_FLAG_BE
//#endif //AV_VERSION_INT(52, 11, 0)
// FFmpeg >= 1.1, but use internal av_pix_fmt_descriptors. FFmpeg < 1.1 has extern av_pix_fmt_descriptors
// used by av_pix_fmt_count_planes
#if !AV_MODULE_CHECK(LIBAVUTIL, 52, 3, 0, 13, 100)
const AVPixFmtDescriptor *av_pix_fmt_desc_get(AVPixelFormat pix_fmt);
const AVPixFmtDescriptor *av_pix_fmt_desc_next(const AVPixFmtDescriptor *prev);
AVPixelFormat av_pix_fmt_desc_get_id(const AVPixFmtDescriptor *desc);
#endif // !AV_MODULE_CHECK(LIBAVUTIL, 52, 3, 0, 13, 100)
#if !FFMPEG_MODULE_CHECK(LIBAVUTIL, 52, 48, 101) // since ffmpeg2.1, libavutil53.16.0 (FF_API_AVFRAME_COLORSPACE), git 8c02adc
enum AVColorSpace av_frame_get_colorspace(const AVFrame *frame);
enum AVColorRange av_frame_get_color_range(const AVFrame *frame);
#endif
/*
* lavu 52.9.0 git 2c328a907978b61949fd20f7c991803174337855
* FFmpeg >= 2.0.
*/
#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 38, 100)
int av_pix_fmt_count_planes(AVPixelFormat pix_fmt);
#endif //AV_VERSION_INT(52, 38, 100)
// FFmpeg < 1.0 has no av_samples_copy
#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 73, 101)
/**
* Copy samples from src to dst.
*
* @param dst destination array of pointers to data planes
* @param src source array of pointers to data planes
* @param dst_offset offset in samples at which the data will be written to dst
* @param src_offset offset in samples at which the data will be read from src
* @param nb_samples number of samples to be copied
* @param nb_channels number of audio channels
* @param sample_fmt audio sample format
*/
int av_samples_copy(uint8_t **dst, uint8_t * const *src, int dst_offset,
int src_offset, int nb_samples, int nb_channels,
enum AVSampleFormat sample_fmt);
#endif //AV_VERSION_INT(51, 73, 101)
// < ffmpeg 1.0
//#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 59, 100)
#if AV_MODULE_CHECK(LIBAVCODEC, 54, 25, 0, 51, 100)
#define QTAV_CODEC_ID(X) AV_CODEC_ID_##X
#else
typedef enum CodecID AVCodecID;
#define QTAV_CODEC_ID(X) CODEC_ID_##X
#endif
/* av_frame_alloc
* since FFmpeg2.0: 2.0.4 avcodec-55.18.102, avutil-52.38.100 (1.2.7 avcodec-54.92.100,avutil-52.18.100)
* since libav10.0: 10.2 avcodec55.34.1, avutil-53.3.0
* the same as avcodec_alloc_frame() (deprecated since 2.2). AVFrame was in avcodec.h, now in avutil/frame.h
*/
#if !AV_MODULE_CHECK(LIBAVCODEC, 55, 34, 0, 18, 100)
#define av_frame_alloc() avcodec_alloc_frame()
#if QTAV_USE_LIBAV(LIBAVCODEC) || FFMPEG_MODULE_CHECK(LIBAVCODEC, 54, 59, 100)
#define av_frame_free(f) avcodec_free_frame(f)
#else
#define av_frame_free(f) av_free(f)
#endif
#endif
#if QTAV_USE_LIBAV(LIBAVCODEC)
const char *avcodec_get_name(enum AVCodecID id);
#endif
#if !AV_MODULE_CHECK(LIBAVCODEC, 55, 55, 0, 68, 100)
void av_packet_rescale_ts(AVPacket *pkt, AVRational src_tb, AVRational dst_tb);
#endif
// since libav-11, ffmpeg-2.1
#if !LIBAV_MODULE_CHECK(LIBAVCODEC, 56, 1, 0) && !FFMPEG_MODULE_CHECK(LIBAVCODEC, 55, 39, 100)
int av_packet_copy_props(AVPacket *dst, const AVPacket *src);
#endif
// since libav-10, ffmpeg-2.1
#if !LIBAV_MODULE_CHECK(LIBAVCODEC, 55, 34, 1) && !FFMPEG_MODULE_CHECK(LIBAVCODEC, 55, 39, 100)
void av_packet_free_side_data(AVPacket *pkt);
#endif
//ffmpeg2.1 libav10
#if !AV_MODULE_CHECK(LIBAVCODEC, 55, 34, 1, 39, 101)
int av_packet_ref(AVPacket *dst, const AVPacket *src);
#define av_packet_unref(pkt) av_free_packet(pkt)
#endif
#if !AV_MODULE_CHECK(LIBAVCODEC, 55, 52, 0, 63, 100)
void avcodec_free_context(AVCodecContext **pavctx);
#endif
#if QTAV_HAVE(AVFILTER)
// ffmpeg2.0 2013-07-03 - 838bd73 - lavfi 3.78.100 - avfilter.h
#if QTAV_USE_LIBAV(LIBAVFILTER)
#define avfilter_graph_parse_ptr(pGraph, pFilters, ppInputs, ppOutputs, pLog) avfilter_graph_parse(pGraph, pFilters, *ppInputs, *ppOutputs, pLog)
#elif !FFMPEG_MODULE_CHECK(LIBAVFILTER, 3, 78, 100)
#define avfilter_graph_parse_ptr(pGraph, pFilters, ppInputs, ppOutputs, pLog) avfilter_graph_parse(pGraph, pFilters, ppInputs, ppOutputs, pLog)
#endif //QTAV_USE_LIBAV(LIBAVFILTER)
//ffmpeg1.0 2012-06-12 - c7b9eab / 84b9fbe - lavfi 2.79.100 / 2.22.0 - avfilter.h
#if !AV_MODULE_CHECK(LIBAVFILTER, 2, 22, 0, 79, 100) //FF_API_AVFILTERPAD_PUBLIC
const char *avfilter_pad_get_name(const AVFilterPad *pads, int pad_idx);
enum AVMediaType avfilter_pad_get_type(const AVFilterPad *pads, int pad_idx);
#endif
///ffmpeg1.0 lavfi 2.74.100 / 2.17.0. was in ffmpeg <libavfilter/avcodec.h> in old ffmpeg and now are in avfilter.h and deprecated. declare here to avoid version check
#if QTAV_USE_FFMPEG(LIBAVFILTER)
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
struct AVFilterBufferRef;
int avfilter_copy_buf_props(AVFrame *dst, const AVFilterBufferRef *src);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
#endif //QTAV_HAVE(AVFILTER)
/* helper functions */
const char *get_codec_long_name(AVCodecID id);
// AV_CODEC_ID_H265 is a macro defined as AV_CODEC_ID_HEVC in ffmpeg but not in libav. so we can use FF_PROFILE_HEVC_MAIN to avoid libavcodec version check. (from ffmpeg 2.1)
#ifndef FF_PROFILE_HEVC_MAIN //libav does not define it
#define AV_CODEC_ID_HEVC ((AVCodecID)0) //QTAV_CODEC_ID(NONE)
#define CODEC_ID_HEVC ((AVCodecID)0) //QTAV_CODEC_ID(NONE)
#define FF_PROFILE_HEVC_MAIN -1
#define FF_PROFILE_HEVC_MAIN_10 -1
#endif
#if !FFMPEG_MODULE_CHECK(LIBAVCODEC, 54, 92, 100) && !LIBAV_MODULE_CHECK(LIBAVCODEC, 55, 34, 1) //ffmpeg1.2 libav10
#define AV_CODEC_ID_VP9 ((AVCodecID)0) //QTAV_CODEC_ID(NONE)
#define CODEC_ID_VP9 ((AVCodecID)0) //QTAV_CODEC_ID(NONE)
#endif
#ifndef FF_PROFILE_VP9_0
#define FF_PROFILE_VP9_0 0
#define FF_PROFILE_VP9_1 1
#define FF_PROFILE_VP9_2 2
#define FF_PROFILE_VP9_3 3
#endif
#define AV_RUN_CHECK(FUNC, RETURN, ...) do { \
int ret = FUNC; \
if (ret < 0) { \
char str[AV_ERROR_MAX_STRING_SIZE]; \
memset(str, 0, sizeof(str)); \
av_strerror(ret, str, sizeof(str)); \
av_log(NULL, AV_LOG_WARNING, "Error " #FUNC " @%d " __FILE__ ": (%#x) %s\n", __LINE__, ret, str); \
RETURN __VA_ARGS__; \
} } while(0)
#endif //QTAV_COMPAT_H

View File

@@ -0,0 +1,287 @@
/******************************************************************************
QtAV: Multimedia framework based on Qt and FFmpeg
Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
* This file is part of QtAV
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
******************************************************************************/
#include "AVDecoder.h"
#include "AVDecoder_p.h"
#include "version.h"
#include "internal.h"
#include "Logger.h"
namespace FAV {
static AVCodec* get_codec(const QString &name, const QString& hwa, AVCodecID cid)
{
QString fullname(name);
if (name.isEmpty()) {
if (hwa.isEmpty())
return avcodec_find_decoder(cid);
fullname = QString("%1_%2").arg(avcodec_get_name(cid)).arg(hwa);
}
AVCodec *codec = avcodec_find_decoder_by_name(fullname.toUtf8().constData());
if (codec)
return codec;
const AVCodecDescriptor* cd = avcodec_descriptor_get_by_name(fullname.toUtf8().constData());
if (cd)
return avcodec_find_decoder(cd->id);
return NULL;
}
AVDecoder::AVDecoder(AVDecoderPrivate &d)
:DPTR_INIT(&d)
{
avcodec_register_all(); // avcodec_find_decoder will always be used
}
AVDecoder::~AVDecoder()
{
setCodecContext(0); // FIXME: will call virtual
}
QString AVDecoder::name() const
{
return QString();
}
QString AVDecoder::description() const
{
return QString();
}
bool AVDecoder::open()
{
DPTR_D(AVDecoder);
// codec_ctx can't be null for none-ffmpeg based decoders because we may use it's properties in those decoders
if (!d.codec_ctx) {
qWarning("FFmpeg codec context not ready");
return false;
}
const QString hwa = property("hwaccel").toString();
AVCodec* codec = get_codec(codecName(), hwa, d.codec_ctx->codec_id);
if (!codec) { // TODO: can be null for none-ffmpeg based decoders
QString es("No codec could be found for '%1'");
if (d.codec_name.isEmpty()) {
es = es.arg(QLatin1String(avcodec_get_name(d.codec_ctx->codec_id)));
if (!hwa.isEmpty())
es.append('_').append(hwa);
} else {
es = es.arg(d.codec_name);
}
qWarning() << es;
AVError::ErrorCode ec(AVError::CodecError);
switch (d.codec_ctx->codec_type) {
case AVMEDIA_TYPE_VIDEO:
ec = AVError::VideoCodecNotFound;
break;
case AVMEDIA_TYPE_AUDIO:
ec = AVError::AudioCodecNotFound;
break;
case AVMEDIA_TYPE_SUBTITLE:
ec = AVError::SubtitleCodecNotFound;
default:
break;
}
Q_EMIT error(AVError(ec, es));
return false;
}
// hwa extra init can be here
if (!d.open()) {
d.close();
return false;
}
// CODEC_FLAG_OUTPUT_CORRUPT, CODEC_FLAG2_SHOW_ALL?
// TODO: skip for none-ffmpeg based decoders
d.applyOptionsForDict();
av_opt_set_int(d.codec_ctx, "refcounted_frames", d.enableFrameRef(), 0); // why dict may have no effect?
d.codec_ctx->thread_count = 1;
d.codec_ctx->flags |= AV_CODEC_FLAG_LOW_DELAY;
d.codec_ctx->flags2 |= AV_CODEC_FLAG2_CHUNKS;
// TODO: only open for ff decoders
//av_dict_set(&d.dict, "lowres", "1", 0);
// dict is used for a specified AVCodec options (priv_class), av_opt_set_xxx(avctx) is only for avctx
AV_ENSURE_OK(avcodec_open2(d.codec_ctx, codec, d.options.isEmpty() ? NULL : &d.dict), false);
d.is_open = true;
static const char* thread_name[] = { "Single", "Frame", "Slice"};
qDebug("%s thread type: %s, count: %d", metaObject()->className(), thread_name[d.codec_ctx->active_thread_type], d.codec_ctx->thread_count);
return true;
}
bool AVDecoder::close()
{
if (!isOpen()) {
return true;
}
DPTR_D(AVDecoder);
d.is_open = false;
// hwa extra finalize can be here
flush();
d.close();
// TODO: reset config?
if (d.codec_ctx) {
AV_ENSURE_OK(avcodec_close(d.codec_ctx), false);
}
return true;
}
bool AVDecoder::isOpen() const
{
return d_func().is_open;
}
void AVDecoder::flush()
{
if (!isAvailable())
return;
if (!isOpen())
return;
avcodec_flush_buffers(d_func().codec_ctx);
}
/*
* do nothing if equal
* close the old one. the codec context can not be shared in more than 1 decoder.
*/
void AVDecoder::setCodecContext(void *codecCtx)
{
DPTR_D(AVDecoder);
AVCodecContext *ctx = (AVCodecContext*)codecCtx;
if (ctx == NULL || d.codec_ctx == ctx)
{
return;
}
if (isOpen())
{
#if !(OFF_OTHER_DEBUG)
qWarning("Can not copy codec properties when it's open");
#endif
close(); //
}
d.is_open = false;
if (!ctx) {
avcodec_free_context(&d.codec_ctx);
d.codec_ctx = 0;
return;
}
if (!d.codec_ctx)
d.codec_ctx = avcodec_alloc_context3(NULL);
// avcodec_alloc_context3(codec) equals to avcodec_alloc_context3(NULL) + avcodec_get_context_defaults3(codec), codec specified private data is initialized
if (!d.codec_ctx) {
qWarning("avcodec_alloc_context3 failed");
return;
}
AV_ENSURE_OK(avcodec_copy_context(d.codec_ctx, ctx));
}
//TODO: reset other parameters?
void* AVDecoder::codecContext() const
{
return d_func().codec_ctx;
}
void AVDecoder::setCodecName(const QString &name)
{
DPTR_D(AVDecoder);
if (d.codec_name == name)
return;
d.codec_name = name;
Q_EMIT codecNameChanged();
}
QString AVDecoder::codecName() const
{
DPTR_D(const AVDecoder);
return d.codec_name;
}
bool AVDecoder::isAvailable() const
{
return d_func().codec_ctx != 0;
}
int AVDecoder::undecodedSize() const
{
return d_func().undecoded_size;
}
void AVDecoder::setOptions(const QVariantHash &dict)
{
DPTR_D(AVDecoder);
d.options = dict;
// if dict is empty, can not return here, default options will be set for AVCodecContext
// apply to AVCodecContext
d.applyOptionsForContext();
/* set AVDecoder meta properties.
* we do not check whether the property exists thus we can set dynamic properties.
*/
if (dict.isEmpty())
return;
if (name() == QLatin1String("avcodec"))
return;
QVariant opt(dict);
if (dict.contains(name()))
opt = dict.value(name());
else if (dict.contains(name().toLower()))
opt = dict.value(name().toLower());
Internal::setOptionsForQObject(opt, this);
}
QVariantHash AVDecoder::options() const
{
return d_func().options;
}
void AVDecoderPrivate::applyOptionsForDict()
{
if (dict) {
av_dict_free(&dict);
dict = 0; //aready 0 in av_free
}
// enable ref if possible
av_dict_set(&dict, "refcounted_frames", enableFrameRef() ? "1" : "0", 0);
if (options.isEmpty())
return;
// TODO: use QVariantMap only
if (!options.contains(QStringLiteral("avcodec")))
return;
qDebug("set AVCodecContext dict:");
// workaround for VideoDecoderFFmpeg. now it does not call av_opt_set_xxx, so set here in dict
// TODO: wrong if opt is empty
Internal::setOptionsToDict(options.value(QStringLiteral("avcodec")), &dict);
}
void AVDecoderPrivate::applyOptionsForContext()
{
if (!codec_ctx)
return;
if (options.isEmpty()) {
// av_opt_set_defaults(codec_ctx); //can't set default values! result maybe unexpected
return;
}
if (!options.contains(QStringLiteral("avcodec")))
return;
// workaround for VideoDecoderFFmpeg. now it does not call av_opt_set_xxx, so set here in dict
// TODO: wrong if opt is empty
Internal::setOptionsToFFmpegObj(options.value(QStringLiteral("avcodec")), codec_ctx);
}
} //namespace FAV

View File

@@ -0,0 +1,97 @@
/******************************************************************************
QtAV: Multimedia framework based on Qt and FFmpeg
Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
* This file is part of QtAV
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
******************************************************************************/
#ifndef QAV_DECODER_H
#define QAV_DECODER_H
#include "AVError.h"
#include <QtCore/QVariant>
#include <QtCore/QObject>
namespace FAV {
class Packet;
class AVDecoderPrivate;
class Q_AV_EXPORT AVDecoder : public QObject
{
Q_OBJECT
DPTR_DECLARE_PRIVATE(AVDecoder)
//Q_PROPERTY(QString description READ description NOTIFY descriptionChanged)
public:
virtual ~AVDecoder();
virtual QString name() const;
virtual QString description() const;
/*!
* default is open FFmpeg codec context
* codec config must be done before open
* NOTE: open() and close() are not thread safe. You'd better call them in the same thread.
*/
virtual bool open();
virtual bool close();
bool isOpen() const;
virtual void flush();
void setCodecContext(void* codecCtx); //protected
void* codecContext() const;
/*not available if AVCodecContext == 0*/
bool isAvailable() const;
#if (PLAY_SYNC_FIX2)
virtual bool send(const Packet* packet) = 0;
virtual bool receive() = 0;
#endif // PLAY_SYNC_FIX2
virtual bool decode(const Packet& packet) = 0;
int undecodedSize() const; //TODO: remove. always decode whole input data completely
// avcodec_open2
/*!
* \brief setOptions
* 1. If has key "avcodec", it's value (suboption, a hash or map) will be used to set AVCodecContext use av_opt_set and av_dict_set. A value of hash type is ignored.
* we can ignore the flags used in av_dict_xxx because we can use hash api.
* empty value does nothing to current context if it is open, but will clear AVDictionary in the next open.
* AVDictionary is used in avcodec_open2() and will not change unless user call setOptions().
* 2. Set QObject properties for AVDecoder. Use AVDecoder::name() or lower case as a key to set properties. If key not found, assume key is "avcodec"
* 3. If no ket AVDecoder::name() found in the option, set key-value pairs as QObject property-value pairs.
* \param dict
* example:
* "avcodec": {"vismv":"pf"}, "vaapi":{"display":"DRM"}, "copyMode": "ZeroCopy"
* means set avcodec context option vismv=>pf, VA-API display (qt property) to DRM when using VA-API, set copyMode (GPU decoders) property to ZeroCopy
*/
void setOptions(const QVariantHash &dict);
QVariantHash options() const;
Q_SIGNALS:
void error(const FAV::AVError& e); //explictly use FAV::AVError in connection for Qt4 syntax
void descriptionChanged();
protected:
AVDecoder(AVDecoderPrivate& d);
DPTR_DECLARE(AVDecoder)
// force a codec. only used by avcodec sw decoders. TODO: move to public? profile set?
void setCodecName(const QString& name);
QString codecName() const;
virtual void codecNameChanged() {}//signals can not be decared virtual (winrt)
private:
Q_DISABLE_COPY(AVDecoder)
AVDecoder(); // base class, not direct create. only final class has is enough
};
} //namespace FAV
#endif // QAV_DECODER_H

View File

@@ -0,0 +1,147 @@
/******************************************************************************
QtAV: Multimedia framework based on Qt and FFmpeg
Copyright (C) 2012-2015 Wang Bin <wbsecg1@gmail.com>
* This file is part of QtAV
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
******************************************************************************/
#ifndef QTAV_AVDECODER_P_H
#define QTAV_AVDECODER_P_H
#include <QtCore/QHash>
#include <QtCore/QSharedPointer>
#include <QtCore/QVector>
#include "_fav_constants.h"
#include "AVCompat.h"
namespace FAV {
// always define the class to avoid macro check when using it
class AVFrameBuffers {
#if QTAV_HAVE(AVBUFREF)
QVector<AVBufferRef*> buf;
#endif
public:
AVFrameBuffers(AVFrame* frame) {
Q_UNUSED(frame);
#if QTAV_HAVE(AVBUFREF)
if (!frame->buf[0]) { //not ref counted. duplicate data?
return;
}
buf.reserve(frame->nb_extended_buf + FF_ARRAY_ELEMS(frame->buf));
buf.resize(frame->nb_extended_buf + FF_ARRAY_ELEMS(frame->buf));
for (int i = 0; i < (int)FF_ARRAY_ELEMS(frame->buf); ++i) {
if (!frame->buf[i]) //so not use planes + nb_extended_buf!
continue;
buf[i] = av_buffer_ref(frame->buf[i]);
if (!buf[i]) {
qWarning("av_buffer_ref(frame->buf[%d]) error", i);
}
}
if (!frame->extended_buf)
return;
for (int i = 0; i < frame->nb_extended_buf; ++i) {
const int k = buf.size() + i - frame->nb_extended_buf;
buf[k] = av_buffer_ref(frame->extended_buf[i]);
if (!buf[k]) {
qWarning("av_buffer_ref(frame->extended_buf[%d]) error", i);
}
}
#endif //QTAV_HAVE(AVBUFREF)
}
~AVFrameBuffers() {
#if QTAV_HAVE(AVBUFREF)
foreach (AVBufferRef* b, buf) {
av_buffer_unref(&b);
}
#endif //QTAV_HAVE(AVBUFREF)
}
};
typedef QSharedPointer<AVFrameBuffers> AVFrameBuffersRef;
class Q_AV_PRIVATE_EXPORT AVDecoderPrivate : public DPtrPrivate<AVDecoder>
{
public:
static const char* getProfileName(AVCodecID id, int profile) {
AVCodec *c = avcodec_find_decoder(id);
if (!c)
return "Unknow";
return av_get_profile_name(c, profile);
}
static const char* getProfileName(const AVCodecContext* ctx) {
if (ctx->codec)
return av_get_profile_name(ctx->codec, ctx->profile);
return getProfileName(ctx->codec_id, ctx->profile);
}
AVDecoderPrivate():
codec_ctx(0)
, available(true)
, is_open(false)
, undecoded_size(0)
, dict(0)
{
codec_ctx = avcodec_alloc_context3(NULL);
}
virtual ~AVDecoderPrivate() {
if (dict) {
av_dict_free(&dict);
}
if (codec_ctx) {
avcodec_free_context(&codec_ctx);
}
}
virtual bool open() {return true;}
virtual void close() {}
virtual bool enableFrameRef() const { return true;}
void applyOptionsForDict();
void applyOptionsForContext();
AVCodecContext *codec_ctx; //set once and not change
bool available; //TODO: true only when context(and hw ctx) is ready
bool is_open;
int undecoded_size;
QString codec_name;
QVariantHash options;
AVDictionary *dict;
};
class AudioResampler;
class AudioDecoderPrivate : public AVDecoderPrivate
{
public:
AudioDecoderPrivate();
virtual ~AudioDecoderPrivate();
AudioResampler *resampler;
QByteArray decoded;
};
class Q_AV_PRIVATE_EXPORT VideoDecoderPrivate : public AVDecoderPrivate
{
public:
VideoDecoderPrivate():
AVDecoderPrivate()
{}
virtual ~VideoDecoderPrivate() {}
};
} //namespace FAV
Q_DECLARE_METATYPE(FAV::AVFrameBuffersRef)
#endif // QTAV_AVDECODER_P_H

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More