-/**\r
- * @file Controller.cpp\r
- *\r
- */\r
+//\r
+//\r
+//\r
+\r
+//#include "stdafx.h"\r
+\r
+#define DBG_LEVEL 0\r
+#include <Raym/Log.h>\r
\r
#include <time.h>\r
+#include <direct.h>\r
+#include <sys/types.h>\r
+#include <sys/stat.h>\r
+#include <stdio.h>\r
+#include <fcntl.h>\r
+#include <io.h>\r
+#include <share.h>\r
+#include <winsock2.h>\r
+#include <ws2tcpip.h>\r
+#include <Iphlpapi.h>\r
\r
-#define DBG_LEVEL 1\r
-#include "Raym/Log.h"\r
+#include "ry0/iPTd/Controller.h"\r
+#include "ry0/iPTd/HTTPLiveStreaming.h"\r
+#include "mpeg2/ts/Analyzer.h"\r
\r
#include "b25/arib_std_b25.h"\r
#include "b25/b_cas_card.h"\r
-#include "mpeg2/ts/Analyzer.h"\r
-\r
-#include "ry0/iPTd/Controller.h"\r
\r
using namespace Raym;\r
+using namespace NET;\r
using namespace ry0::device;\r
\r
namespace ry0\r
namespace iPTd\r
{\r
\r
+static const char *PLIST_PREFIX = "com.gmail.tim.and.pom";\r
+\r
// プロパティデフォルト値\r
static const char * DEF_NAME = "iPTd_R2";\r
static const char * DEF_HOSTNAME = "localhost";\r
static const TimeInterval DEF_COLLECT_EPG_LIMIT_S = 10.5;\r
static const TimeInterval DEF_COLLECT_EPG_LIMIT_T = 20.5;\r
\r
-const char *Controller::_plist_prefix = "com.gmail.tim.and.pom";\r
+static const time_t OFFSET_OF_START_TIME = -2; // 録画開始時刻の補正(秒単位)\r
+static const time_t OFFSET_OF_END_TIME = -3; // 録画停止時刻の補正(秒単位)\r
+static const time_t OFFSET_OF_WAKEUP = -240; // 起動スケジュールの補正(秒単位) 注:休止するまでの時間(DEF_SUSPEND_TIME)よりも短くすること\r
+static const time_t OFFSET_OF_SUPPRESSION_TIME = -600; // 録画開始前に休止の抑制を開始する時間(秒単位)\r
\r
-#ifndef _WIN32\r
-#pragma mark '\r
-#pragma mark ------- コンストラクタ/デストラクタ -------\r
-#endif\r
\r
-Controller::Controller()\r
-{\r
- DebugLog3("%s", __FUNCTION__);\r
-}\r
+#if 0\r
\r
-Controller::~Controller()\r
+void Controller::delaySuspend()\r
{\r
- DebugLog3("%s", __FUNCTION__);\r
+ // \r
+ Timer *timer = Timer::scheduledTimerWithTimeInterval(1.0, this, (void *)CMD_SUSPEND, false);\r
+ if (timer == NULL)\r
+ {\r
+ DebugLog0("Can't start timer.");\r
+ }\r
}\r
+#endif\r
\r
-Controller *Controller::alloc()\r
+std::string Controller::createVideoPath(int tuner)\r
{\r
- DebugLog3("%s", __FUNCTION__);\r
- return new Controller();\r
+ DebugLog2("Controller::createVideoPath()");\r
+\r
+ std::string result = "";\r
+\r
+ while (true)\r
+ {\r
+ time_t now;\r
+ time(&now);\r
+ TM tm;\r
+ if (localtime_s(&tm, &now) != 0)\r
+ {\r
+ break;\r
+ }\r
+\r
+ result = _store_path->cString();\r
+ DebugLog2("result: %s\n", result.c_str());\r
+\r
+ char tmp[128];\r
+ if (sprintf_s(tmp, sizeof(tmp), "\\%04d", tm.tm_year + 1900) < 0)\r
+ {\r
+ DebugLog0("sprintf_s() error: year\n");\r
+ result = "";\r
+ break;\r
+ }\r
+ result += tmp;\r
+ DebugLog2("result: %s\n", result.c_str());\r
+\r
+ STAT stat;\r
+ if (_stat(result.c_str(), &stat) != 0)\r
+ {\r
+ if (_mkdir(result.c_str()) != 0)\r
+ {\r
+ DebugLog0("_mkdir() error: year\n");\r
+ result = "";\r
+ break;\r
+ }\r
+ _stat(result.c_str(), &stat);\r
+ }\r
+ if ((stat.st_mode & _S_IFDIR) != _S_IFDIR)\r
+ {\r
+ DebugLog0("%s is not directory.\n", result.c_str());\r
+ result = "";\r
+ break;\r
+ }\r
+\r
+ if (sprintf_s(tmp, sizeof(tmp), "\\%02d", tm.tm_mon + 1) < 0)\r
+ {\r
+ DebugLog0("sprintf_s() error: month\n");\r
+ result = "";\r
+ break;\r
+ }\r
+ result += tmp;\r
+ DebugLog2("result: %s\n", result.c_str());\r
+\r
+ if (_stat(result.c_str(), &stat) != 0)\r
+ {\r
+ if (_mkdir(result.c_str()) != 0)\r
+ {\r
+ DebugLog0("_mkdir() error: month\n");\r
+ result = "";\r
+ break;\r
+ }\r
+ _stat(result.c_str(), &stat);\r
+ }\r
+ if ((stat.st_mode & _S_IFDIR) != _S_IFDIR)\r
+ {\r
+ DebugLog0("%s is not directory.", result.c_str());\r
+ result = "";\r
+ break;\r
+ }\r
+\r
+ if (sprintf_s(tmp, sizeof(tmp),\r
+ "\\%04d%02d%02d_%02d%02d%02d_%03d_%s.ts",\r
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,\r
+ _tuners[tuner]->channel(), _tuners[tuner]->name()) < 0)\r
+ {\r
+ DebugLog0("sprintf_s() error: filename");\r
+ result = "";\r
+ break;\r
+ }\r
+ result += tmp;\r
+ DebugLog2("result: %s\n", result.c_str());\r
+\r
+ break;\r
+ }\r
+\r
+ return result;\r
}\r
\r
#ifndef _WIN32\r
#pragma mark '\r
-#pragma mark ------- 起動/停止関連 -------\r
+#pragma mark ------- EPG関連 -------\r
#endif\r
\r
-// システム状態をチェックしてサスペンド可能かを返す\r
-bool Controller::canSuspend()\r
+void Controller::removePastEPGs()\r
{\r
- bool result = false;\r
+ DebugLog2("Controller::removePastEPGs()");\r
+\r
+ // 現在時刻\r
+ time_t now = time(NULL);\r
+\r
+ // lock\r
RaymLock(this);\r
- if (_initialized)\r
+\r
+ // サービスIDでループ\r
+ Array *keys1 = _epgs->allKeys();\r
+ for (uint i = 0; i < keys1->count(); ++i)\r
{\r
- result = _reservation->canSuspend();\r
+ // イベント取得\r
+ Dictionary *events = _epgs->dictionaryForKey((String *)keys1->objectAtIndex(i));\r
+ if (events != NULL)\r
+ {\r
+ // イベントでループ\r
+ Array *keys2 = events->allKeys();\r
+ for (uint j = 0; j < keys2->count(); ++j)\r
+ {\r
+ // キーとEPG取得\r
+ String *key = (String *)keys2->objectAtIndex(j);\r
+ Dictionary *epg = events->dictionaryForKey(key);\r
+ if (epg != NULL)\r
+ {\r
+ // EPG取得できた場合\r
+ time_t start = 0;\r
+ time_t end = 0;\r
+ getTimeWithEPG(epg, &start, &end);\r
+\r
+ // 終了時刻が過ぎていたら\r
+ if (now > end)\r
+ {\r
+ // 削除する\r
+ events->removeObjectForKey(key);\r
+ }\r
+ }\r
+ }\r
+ }\r
}\r
+\r
+ // unlock\r
RaymUnlock(this);\r
- return result;\r
}\r
\r
-bool Controller::canTerminate()\r
+void Controller::collectEPGsForTuner(int tuner, TimeInterval limit)\r
{\r
- bool result = false;\r
+ DebugLog2("Controller::collectEPGsForTuner(%d) start.", tuner);\r
+\r
+ // 既にロックされた状態でコールされる前提\r
+ bool locked = false;\r
RaymLock(this);\r
- if (_initialized)\r
+ if ((0 <= tuner) && (tuner < _tunerCount))\r
{\r
- result = _reservation->canTerminate();\r
+ locked = _tuners[tuner]->isLocked();\r
}\r
RaymUnlock(this);\r
- return result;\r
-}\r
-\r
-/*\r
- * Application::WndProc() からコールされる\r
- */\r
-void Controller::systemWillSuspend()\r
-{\r
- DebugLog2("Controller::systemWillSuspend() start");\r
-\r
- if ((_timer_restart != NULL) && _timer_restart->valid())\r
+ if (!locked)\r
{\r
- _timer_restart->invalidate();\r
+ DebugLog2("Controller::collectEPGsForTuner(%d) end(no locked).", tuner);\r
+ return;\r
}\r
- _reservation->systemWillSuspend();\r
- _streaming->systemWillSuspend();\r
\r
- RaymLock(this);\r
+ // \r
+ Array *collected = Array::arrayWithCapacity(0);\r
\r
- RELEASE(_timer_restart);\r
+ // 開始時刻取得\r
+ Date *start = Date::date();\r
\r
- _reservation->updateSchedule();\r
+ // 取得開始\r
+ bool done = false;\r
+ while (!done)\r
+ {\r
+ AutoreleasePool *pool = AutoreleasePool::alloc()->init();\r
\r
- _initialized = false;\r
+ // EPG取得\r
+ MPEG2::TS::Analyzer an;\r
+ an.setFlag(MPEG2::TS::Analyzer::FLAG_EIT);\r
+ _tuners[tuner]->setListener(&an);\r
+ Array *epgs = an.epgInfos();\r
+ _tuners[tuner]->setListener(NULL);\r
\r
- for (int i = 0; i < _tuner_count; ++i)\r
- {\r
- if (_tuners[i] != NULL)\r
+ if (epgs != NULL)\r
{\r
- delete _tuners[i];\r
- _tuners[i] = NULL;\r
+ // 取得成功\r
+ collected->addObjectsFromArray(epgs);\r
}\r
- }\r
\r
- DebugLog0("system will suspend...");\r
-\r
- RaymUnlock(this);\r
+ // リミットチェック\r
+ if (Date::date()->timeIntervalSinceDate(start) >= limit)\r
+ {\r
+ done = true;\r
+ }\r
\r
-#ifdef RAYM_MEMORY_CHECK\r
- DebugLog0("global_raym_count_ = %d", Raym::global_raym_count_);\r
-#endif\r
- DebugLog2("Controller::systemWillSuspend() end");\r
-}\r
+ // キャンセル確認\r
+ RaymLock(this);\r
+ if (!done)\r
+ {\r
+ done = _cancel_epg_collect;\r
+ }\r
+ RaymUnlock(this);\r
\r
-/*\r
- * Application::WndProc() からコールされる\r
- */\r
-void Controller::systemResumed()\r
-{\r
- DebugLog2("Controller::systemResumed() start");\r
+ pool->release();\r
+ }\r
\r
- DebugLog0("system resumed.");\r
+ // lock\r
+ RaymLock(this);\r
\r
- if (_timer_restart == NULL)\r
+ for (uint j = 0; j < collected->count(); ++j)\r
{\r
- _timer_restart = Timer::alloc()->initWithTimeInterval(1.0, this, (void *)CMD_RESTART, false);\r
- _timer_restart->fire();\r
+ Dictionary *epg1 = (Dictionary *)collected->objectAtIndex(j);\r
+\r
+ if (epg1->stringForKey(KEY_EPG_TITLE) == NULL)\r
+ {\r
+ // タイトルが無い場合は不要\r
+ continue;\r
+ }\r
+\r
+ // Service ID を Primary Key\r
+ String *key = epg1->stringForKey(KEY_EPG_SERVICE_ID);\r
+ if (key != NULL)\r
+ {\r
+ Dictionary *epgs_of_service = _epgs->dictionaryForKey(key);\r
+ if (epgs_of_service == NULL)\r
+ {\r
+ // 無い場合は作成\r
+ epgs_of_service = Dictionary::dictionaryWithCapacity(0);\r
+ _epgs->setObject(epgs_of_service, key);\r
+ }\r
+ // Event ID を Secondary Key\r
+ key = epg1->stringForKey(KEY_EPG_EVENT_ID);\r
+ if (key != NULL)\r
+ {\r
+ epgs_of_service->setObject(epg1, key);\r
+ }\r
+ }\r
}\r
\r
- DebugLog2("Controller::systemResumed()");\r
+ // ファイルへ書き出し\r
+ _epgs->writeToFile(_epgs_path, true);\r
+\r
+ // unlock\r
+ RaymUnlock(this);\r
+\r
+ DebugLog2("Controller::collectEPGsForTuner(%d) end.", tuner);\r
}\r
\r
-/*\r
- * Application::WndProc() からコールされる\r
- */\r
-void Controller::detectIdle()\r
+bool Controller::collectEPGs(Tuner::Type type)\r
{\r
- DebugLog2("Controller::detectIdle()");\r
-\r
- // ARP生成\r
- AutoreleasePool *pool = AutoreleasePool::alloc()->init();\r
+ DebugLog0("Controller::collectEPGs(%s) start.", type == Tuner::ISDB_S ? "ISDB-S" : "ISDB-T");\r
\r
+ // 使用するチューナを決定\r
+ int tuner = 0;\r
+ bool locked = false;\r
RaymLock(this);\r
-\r
- // サスペンド可能か確認\r
- if (canSuspend())\r
+ for (int i = _tunerCount - 1; i >= 0; --i)\r
{\r
- if (_idle_count == 0)\r
+ if (isTunerEnabled(i))\r
{\r
- DebugLog0("detect idle...");\r
+ if ((!_tuners[i]->isLocked()) && (_tuners[i]->type() == type))\r
+ {\r
+ if (_tuners[i]->lock())\r
+ {\r
+ tuner = i;\r
+ locked = true;\r
+ break;\r
+ }\r
+ }\r
}\r
+ }\r
+ RaymUnlock(this);\r
\r
- // アイドルカウンタを更新\r
- ++_idle_count;\r
+ // ロック確認\r
+ if (!locked)\r
+ {\r
+ DebugLog0("Controller::collectEPGs(%s) end(Can't locled).", type == Tuner::ISDB_S ? "ISDB-S" : "ISDB-T");\r
+ // ロックできない場合は収集しない\r
+ return false;\r
+ }\r
\r
- // 起動中アプリと休止状態抑止アプリのチェック\r
- bool found = false;\r
+ bool canceled = false;\r
+ DebugLog0("start collect EPG of \"%s(#%d)\".", _tuners[tuner]->name(), tuner);\r
\r
- Array *dont_in_suspend = _props->arrayForKey(KEY_DO_NOT_IN_SUSPEND);\r
- Array *running_apps = Workspace::sharedWorkspace()->runningApplications();\r
- for (uint i = 0; (i < running_apps->count()) && !found; ++i)\r
- {\r
- RunningApplication *ra = (RunningApplication *)running_apps->objectAtIndex(i);\r
+ // 現在のチャンネルを保存\r
+ int channel = _tuners[tuner]->channel();\r
\r
- // 実行中でなければ次へ\r
- if (!ra->isRunning())\r
+ // チャンネルを変更しつつEPGを取得\r
+ int max_channel = (type == Tuner::ISDB_S ? Tuner::MAX_CHANNELS_ISDB_S : Tuner::MAX_CHANNELS_ISDB_T);\r
+ for (int ch = 0; ch <= max_channel; ++ch)\r
+ {\r
+ // チャンネルが有効かチェック\r
+ if (isChannelEnabled(tuner, ch))\r
+ {\r
+ // チャンネル設定\r
+ if (_tuners[tuner]->setChannel(ch))\r
{\r
- continue;\r
- }\r
+ // EPG取集\r
+ collectEPGsForTuner(tuner, ((type == Tuner::ISDB_S) ? DEF_COLLECT_EPG_LIMIT_S : DEF_COLLECT_EPG_LIMIT_T));\r
\r
- // 実行ファイルのチェック\r
- String *path = ra->executePath();\r
- if ((path == NULL) || (path->length() == 0))\r
- {\r
- continue;\r
- }\r
- DebugLog3("exec path: %s", path->cString());\r
+ // キャンセル確認\r
+ RaymLock(this);\r
+ if (_cancel_epg_collect)\r
+ {\r
+ ch = Tuner::MAX_CHANNELS_ISDB_T + 1;\r
+ canceled = true;\r
+ }\r
+ RaymUnlock(this);\r
\r
- // 休止状態抑止アプリリストのチェック\r
- for (uint j = 0; (j < dont_in_suspend->count()) && !found; ++j)\r
- {\r
- found = path->isMatch((String *)dont_in_suspend->objectAtIndex(j));\r
+ // キャンセル確認\r
+ // 終了不可 -> 録画待機中/録画中 なので、収集は諦める\r
+ if (!canTerminate())\r
+ {\r
+ ch = Tuner::MAX_CHANNELS_ISDB_T + 1;\r
+ canceled = true;\r
+ }\r
}\r
}\r
+ }\r
\r
- // 抑止有効なら KEY_FORCED_SUSPEND_TIME、無効なら KEY_SUSPEND_TIME で取得\r
- int limit = _props->integerForKey(found ? KEY_FORCED_SUSPEND_TIME : KEY_SUSPEND_TIME);\r
- DebugLog3("found: %d, _idle_count: %d, limit: %d", found, _idle_count, limit);\r
- if (_idle_count >= limit)\r
- {\r
- RaymUnlock(this);\r
-\r
- // サスペンド前にARPを解放しておく\r
- pool->release();\r
-\r
- // 下記の TrayApp::suspend() を使用する場合、自アプリへsuspendメッセージが\r
- // ブロードキャストされないため、自分でコールしておく\r
- systemWillSuspend();\r
+ // 元のチャンネルに戻す\r
+ _tuners[tuner]->setChannel(channel);\r
\r
- // サスペンド\r
- suspend();\r
+ // lock\r
+ RaymLock(this);\r
\r
- // 再度 ARPを用意\r
- pool = AutoreleasePool::alloc()->init();\r
+ // チューナをアンロック\r
+ _tuners[tuner]->unlock();\r
\r
- RaymLock(this);\r
+ // unlock\r
+ RaymUnlock(this);\r
\r
- // アイドルカウンタをクリア\r
- _idle_count = 0;\r
- }\r
+ if (canceled)\r
+ {\r
+ DebugLog0("collect EPG of \"%s\" was canceled.", _tuners[tuner]->name());\r
}\r
else\r
{\r
- // アイドルカウンタをクリア\r
- _idle_count = 0;\r
+ DebugLog0("collect EPG of \"%s\" was finished.", _tuners[tuner]->name());\r
+\r
+ // 過去のEPGを削除\r
+ removePastEPGs();\r
}\r
\r
- RaymUnlock(this);\r
+ DebugLog2("Controller::collectEPGs(%s) end.", type == Tuner::ISDB_S ? "ISDB-S" : "ISDB-T");\r
\r
- // ARP解放\r
- pool->release();\r
+ return true;\r
}\r
\r
-void Controller::detectNonIdle()\r
+#ifndef _WIN32\r
+#pragma mark '\r
+#pragma mark ------- 予約録画関連 -------\r
+#endif\r
+\r
+//\r
+// 録画予約:サービスID/イベントID指定\r
+// EPGデータから指定のサービスID/イベントIDのEPGを取り出し、EPG指定の録画予約をコール\r
+//\r
+bool Controller::reserve(int service_id, int event_id)\r
{\r
- DebugLog2("Controller::detectNonIdle()");\r
+ DebugLog2("Controller::reserve(service_id, event_id)");\r
+\r
+ bool result = false;\r
\r
+ // lock\r
RaymLock(this);\r
\r
- if (_idle_count > 0)\r
+ if (_epgs != NULL)\r
{\r
- DebugLog0("detect non idle...");\r
+ Dictionary *events = _epgs->dictionaryForKey(String::stringWithFormat("%d", service_id));\r
+ if (events != NULL)\r
+ {\r
+ Dictionary *epg = events->dictionaryForKey(String::stringWithFormat("%d", event_id));\r
+ if (epg != NULL)\r
+ {\r
+ result = reserve(epg);\r
+ }\r
+ }\r
}\r
- _idle_count = 0;\r
\r
+ // unlock\r
RaymUnlock(this);\r
-}\r
\r
-#ifndef _WIN32\r
-#pragma mark '\r
-#pragma mark ------- プロパティ取得 -------\r
-#endif\r
+ return result;\r
+}\r
\r
-/*\r
- *\r
- */\r
-bool Controller::isTunerInitialized(int tuner)\r
+//\r
+// 録画予約:EPG指定\r
+// EPGからサービスIDを取り出し、チューナ情報を検索して一致するサービスIDがあったらチューナ/EPG指定の録画予約を試行\r
+//\r
+bool Controller::reserve(Dictionary *in_epg)\r
{\r
- DebugLog2("Controller::isTunerInitialized()");\r
+ DebugLog2("Controller::reserve(epg)");\r
\r
bool result = false;\r
\r
+ // lock\r
RaymLock(this);\r
\r
- if ((0 <= tuner) && (tuner < _tuner_count))\r
+ while (in_epg != NULL)\r
{\r
- Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
- if (tunersInfo != NULL)\r
+ // EPGを複製\r
+ Dictionary *epg = Dictionary::dictionaryWithDictionary(in_epg);\r
+ if (epg == NULL)\r
{\r
- Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
- if (tunerInfo != NULL)\r
+ DebugLog3("Dictionary::dictionaryWithDictionary() ng.");\r
+ break;\r
+ }\r
+\r
+ // サービスID取得\r
+ String *service_id = epg->stringForKey(KEY_EPG_SERVICE_ID);\r
+ if (service_id == NULL)\r
+ {\r
+ DebugLog3("epg->stringForKey(KEY_EPG_SERVICE_ID) ng.");\r
+ break;\r
+ }\r
+ DebugLog3("service_id: %s\n", service_id->cString());\r
+\r
+ // 全チューナ情報取得\r
+ Dictionary *tunerInfos = _props->dictionaryForKey(KEY_TUNERS);\r
+ if (tunerInfos == NULL)\r
+ {\r
+ DebugLog3("_props->dictionaryForKey(KEY_TUNERS) ng.");\r
+ break;\r
+ }\r
+\r
+ // チューナを検索\r
+ for (int i = 0; (!result) && (i < _tunerCount); ++i)\r
+ {\r
+ // チューナ情報取得\r
+ Dictionary *tunerInfo = tunerInfos->dictionaryForKey(_tuners[i]->name());\r
+ if (tunerInfo == NULL)\r
{\r
- result = tunerInfo->boolForKey(KEY_INITIALIZED);\r
+ DebugLog3("tunerInfos->dictionaryForKey(_tuners[%d]->name()) ng.", i);\r
+ continue;\r
+ }\r
+\r
+ // 全チャンネル情報取得\r
+ Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
+ if (channels == NULL)\r
+ {\r
+ DebugLog3("tunerInfo->dictionaryForKey(KEY_CHANNELS) ng.");\r
+ continue;\r
+ }\r
+\r
+ // キー取得\r
+ Array *chkeys = channels->allKeys();\r
+ for (uint ch = 0; ch < chkeys->count(); ++ch)\r
+ {\r
+ // チャンネル情報取得\r
+ Dictionary *channel = channels->dictionaryForKey((String *)chkeys->objectAtIndex(ch));\r
+ if (channel == NULL)\r
+ {\r
+ DebugLog3("channels->dictionaryForKey() ng.");\r
+ continue;\r
+ }\r
+\r
+ // 全サービス情報取得\r
+ Array *services = (Array *)channel->objectForKey(KEY_SERVICES);\r
+ if (services == NULL)\r
+ {\r
+ DebugLog3("channel->objectForKey() ng.");\r
+ continue;\r
+ }\r
+\r
+ for (uint s = 0; s < services->count(); ++s)\r
+ {\r
+ // サービス情報取得\r
+ Dictionary *service = (Dictionary *)services->objectAtIndex(s);\r
+ if (service == NULL)\r
+ {\r
+ DebugLog3("service->objectAtIndex() ng.");\r
+ continue;\r
+ }\r
+\r
+ // サービスIDを比較\r
+ String *sid = service->stringForKey(KEY_SERVICE_ID);\r
+ if ((sid != NULL) && sid->isEqualToString(service_id))\r
+ {\r
+ // チャンネルを設定\r
+ epg->setString((String *)chkeys->objectAtIndex(ch), KEY_EPG_CHANNEL);\r
+\r
+ // 録画予約\r
+ result = reserve(i, epg);\r
+\r
+ // チャンネルループのカウンタを更新(=ループを終了させる)\r
+ ch = chkeys->count();\r
+ break;\r
+ }\r
+ }\r
}\r
}\r
+\r
+ break;\r
}\r
\r
+ // unlock\r
RaymUnlock(this);\r
\r
return result;\r
}\r
\r
-/*\r
- *\r
- */\r
-bool Controller::isTunerEnabled(int tuner)\r
+//\r
+// 録画予約:チューナ/EPG指定\r
+//\r
+bool Controller::reserve(int tuner, Dictionary *in_epg)\r
{\r
- DebugLog2("Controller::isTunerEnabled()");\r
+ DebugLog2("Controller::reserve(tuner, epg)");\r
\r
bool result = false;\r
\r
+ // lock\r
RaymLock(this);\r
\r
- if ((0 <= tuner) && (tuner < _tuner_count))\r
+ while ((0 <= tuner) && (tuner < _tunerCount) && (in_epg != NULL))\r
{\r
- Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
- if (tunersInfo != NULL)\r
+ Dictionary *epg = Dictionary::dictionaryWithDictionary(in_epg);\r
+ if (epg == NULL)\r
{\r
- Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
- if (tunerInfo != NULL)\r
+ DebugLog3("Dictionary::dictionaryWithDictionary() ng.");\r
+ break;\r
+ }\r
+\r
+ Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
+ if (array == NULL)\r
+ {\r
+ array = Array::arrayWithCapacity(0);\r
+ _reservations->setObject(array, _tuners[tuner]->name());\r
+ }\r
+\r
+ time_t epg_start;\r
+ time_t epg_end;\r
+ getTimeWithEPG(epg, &epg_start, &epg_end);\r
+ DebugLog2("epg start: %ld, end: %ld\n", epg_start, epg_end);\r
+\r
+ time_t pre_start = 0;\r
+ time_t pre_end = 0;\r
+ pre_end = time(NULL);\r
+ DebugLog2("pre_end: %ld", pre_end);\r
+\r
+ for (uint i = 0; i < array->count(); ++i)\r
+ {\r
+ Dictionary *cur = (Dictionary *)array->objectAtIndex(i);\r
+ time_t cur_start;\r
+ time_t cur_end;\r
+ getTimeWithEPG(cur, &cur_start, &cur_end);\r
+ DebugLog2("cur start: %ld, end: %ld\n", cur_start, cur_end);\r
+ if ((pre_end <= epg_start) && (epg_end <= cur_start))\r
{\r
- result = tunerInfo->boolForKey(KEY_ENABLED);\r
+ DebugLog2("insert: %d\n", i);\r
+ array->insertObject(epg, i);\r
+ result = true;\r
+ break;\r
+ }\r
+ pre_start = cur_start;\r
+ pre_end = cur_end;\r
+ }\r
+\r
+ if (!result)\r
+ {\r
+ if (pre_end <= epg_start)\r
+ {\r
+ DebugLog2("add\n");\r
+ array->addObject(epg);\r
+ result = true;\r
}\r
+ else\r
+ {\r
+ DebugLog2("no add\n");\r
+ }\r
+ }\r
+ if (result)\r
+ {\r
+ epg->setInteger(_reservation_seq_id, KEY_EPG_RESV_ID);\r
+ _reservation_seq_id = (_reservation_seq_id + 1) % 1000000;\r
+ _reservations->setInteger(_reservation_seq_id, KEY_EPG_LAST_RESV_ID);\r
+\r
+ //\r
+ _reservations->writeToFile(_reservations_path, true);\r
}\r
+\r
+ break;\r
}\r
\r
+ // unlock\r
RaymUnlock(this);\r
\r
return result;\r
}\r
\r
-/*\r
- *\r
- */\r
-bool Controller::isChannelEnabled(int tuner, int channel)\r
+//\r
+// tuner: 0 - (_tunerCount - 1) cancel current\r
+// tuner: -1 cancel reserve_id\r
+//\r
+bool Controller::cancel(int tuner, int reserve_id)\r
{\r
- DebugLog2("Controller::isChannelEnabled()");\r
-\r
bool result = false;\r
\r
+ // lock\r
RaymLock(this);\r
\r
- if ((0 <= tuner) && (tuner < _tuner_count) && (0 <= channel) && (channel <= Tuner::MAX_CHANNELS_ISDB_T))\r
+ //\r
+ if ((0 <= tuner) && (tuner < _tunerCount))\r
{\r
- Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
- if (tunersInfo != NULL)\r
+ Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
+ if (array != NULL)\r
{\r
- Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
- if (tunerInfo != NULL)\r
+ if (array->count() > 0)\r
{\r
- Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
- if (channels != NULL)\r
+ Dictionary *epg = (Dictionary *)array->objectAtIndex(0);\r
+ String *status = epg->stringForKey(KEY_EPG_STATUS);\r
+ if (status != NULL)\r
{\r
- char key[4];\r
- sprintf_s(key, "%03d", channel);\r
- Dictionary *chInfo = channels->dictionaryForKey(key);\r
- if (chInfo != NULL)\r
+ if (status->isEqualToString("running"))\r
{\r
- result = chInfo->boolForKey(KEY_ENABLED);\r
+ epg->setString("stop", KEY_EPG_STATUS);\r
+ result = true;\r
}\r
}\r
}\r
}\r
}\r
\r
- RaymUnlock(this);\r
-\r
- return result;\r
-}\r
-\r
-#ifndef _WIN32\r
-#pragma mark '\r
-#pragma mark ------- チューナ制御 -------\r
-#endif\r
-\r
-void Controller::scanChannel(int tuner)\r
-{\r
- DebugLog2("Controller::scanChannel(%d)", tuner);\r
-\r
- RaymLock(this);\r
- if ((tuner < 0) || (_tuner_count <= tuner))\r
- {\r
- RaymUnlock(this);\r
- DebugLog3("Invalid tuner: %d", tuner);\r
- return;\r
- }\r
-\r
- DebugLog0("");\r
- DebugLog0("start channel scan of \"%s\".", _tuners[tuner]->name());\r
-\r
- // 設定ファイルから全チューナ情報取得\r
- Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
- if (tunersInfo == NULL)\r
+ //\r
+ else if ((tuner < 0) && (0 <= reserve_id) && (reserve_id < 1000000))\r
{\r
- // 無ければ作成\r
- tunersInfo = Dictionary::dictionaryWithCapacity(0);\r
- _props->setObject(tunersInfo, KEY_TUNERS);\r
+ for (int i = 0; i < _tunerCount; ++i)\r
+ {\r
+ Array *array = _reservations->arrayForKey(_tuners[i]->name());\r
+ if (array != NULL)\r
+ {\r
+ for (uint j = 0; j < array->count(); ++j)\r
+ {\r
+ Dictionary *epg = (Dictionary *)array->objectAtIndex(j);\r
+ if (reserve_id == epg->integerForKey(KEY_EPG_RESV_ID))\r
+ {\r
+ String *status = epg->stringForKey(KEY_EPG_STATUS);\r
+ if ((status != NULL) && status->isEqualToString("running"))\r
+ {\r
+ epg->setString("stop", KEY_EPG_STATUS);\r
+ }\r
+ else\r
+ {\r
+ array->removeObjectAtIndex(j);\r
+ }\r
+ result = true;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ if (result)\r
+ {\r
+ break;\r
+ }\r
+ }\r
}\r
\r
- // 全チューナ情報から指定のチューナ情報を取得\r
- Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
- if (tunerInfo == NULL)\r
+ if (result)\r
{\r
- // 無ければ作成\r
- tunerInfo = Dictionary::dictionaryWithCapacity(0);\r
- tunersInfo->setObject(tunerInfo, _tuners[tuner]->name());\r
+ _reservations->writeToFile(_reservations_path, true);\r
}\r
\r
- // チューナを未初期化に設定\r
- tunerInfo->setBool(false, KEY_INITIALIZED);\r
-\r
// unlock\r
RaymUnlock(this);\r
\r
- // 全チャンネル情報を作成\r
- Dictionary *channels = Dictionary::dictionaryWithCapacity(0);\r
- tunerInfo->setObject(channels, KEY_CHANNELS);\r
+ return result;\r
+}\r
\r
- // チューナタイプにより最大チャンネル数を設定\r
- int max_channel;\r
- Tuner::Type type = _tuners[tuner]->type();\r
- if (type == Tuner::ISDB_S)\r
- {\r
- max_channel = Tuner::MAX_CHANNELS_ISDB_S;\r
- }\r
- else\r
- {\r
- max_channel = Tuner::MAX_CHANNELS_ISDB_T;\r
- }\r
+//\r
+// 録画制御\r
+//\r
+void Controller::periodic(void)\r
+{\r
+ bool need_update = false;\r
\r
- // 最終設定成功チャンネル保持用変数\r
- int lastChannel = -1;\r
+#ifdef OBJC_MEMORY_CHECK\r
+ DebugLog0("global_objc_count_ = %d", Raym::global_objc_count_);\r
+#endif\r
\r
- // チャンネルサーチ\r
- for (int ch = 0; ch <= max_channel; ++ch)\r
- {\r
- // チャンネル情報\r
- Dictionary *channelInfo = Dictionary::dictionaryWithCapacity(0);\r
+ // lock\r
+ RaymLock(this);\r
\r
- // チャンネルキー(10進数:PTに対するチャンネル番号)\r
- char chkey[4];\r
- sprintf_s(chkey, sizeof(chkey), "%03d", ch);\r
- channels->setObject(channelInfo, chkey);\r
+ // 現在時刻取得\r
+ time_t now = time(NULL);\r
\r
- // チャンネルID(論理チャンネル番号)\r
- char channelID[8];\r
- if (type == Tuner::ISDB_S)\r
+ DebugLog2("periodic: %d", now);\r
+\r
+ //\r
+ for (int tuner = 0; tuner < _tunerCount; ++tuner)\r
+ {\r
+ Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
+ if ((array == NULL) || (array->count() == 0))\r
{\r
- if (ch < 12)\r
+ // next tuner\r
+ continue;\r
+ }\r
+\r
+ //\r
+ Dictionary *epg = (Dictionary *)array->objectAtIndex(0);\r
+ time_t start;\r
+ time_t end;\r
+ getTimeWithEPG(epg, &start, &end);\r
+ \r
+ //\r
+ // 録画停止要否チェック\r
+ //\r
+ bool stop_need = false;\r
+ while (true)\r
+ {\r
+ String *status = epg->stringForKey(KEY_EPG_STATUS);\r
+ if (status != NULL)\r
{\r
- sprintf_s(channelID, sizeof(channelID), "BS%02d", 1 + 2 * ch);\r
+ if (status->isEqualToString("stop"))\r
+ {\r
+ stop_need = true;\r
+ break;\r
+ }\r
+ if (!status->isEqualToString("running"))\r
+ {\r
+ break;\r
+ }\r
}\r
- else if (ch < 24)\r
+ if (end + OFFSET_OF_END_TIME <= now)\r
{\r
- sprintf_s(channelID, sizeof(channelID), "ND%02d", 2 + 2 * (ch - 12));\r
+ stop_need = true;\r
+ }\r
+ break;\r
+ }\r
+ if (stop_need)\r
+ {\r
+ DebugLog2("I try stop\n");\r
+ int fd =_tuners[tuner]->stopRecording();\r
+ if (fd < 0)\r
+ {\r
+ DebugLog1("stopRecording() error.\n");\r
}\r
else\r
{\r
- sprintf_s(channelID, sizeof(channelID), "ND%02d", 1 + 2 * (ch -24));\r
+ DebugLog2("stopRecording() ok\n");\r
+ DebugLog0("stop recording of \"%s\"", _tuners[tuner]->name());\r
+ _close(fd);\r
}\r
+ array->removeObject(epg);\r
+ \r
+ if (array->count() > 0)\r
+ {\r
+ epg = (Dictionary *)array->objectAtIndex(0);\r
+ }\r
+ else\r
+ {\r
+ epg = NULL;\r
+ }\r
+ need_update = true;\r
}\r
- else\r
+\r
+ if (epg == NULL)\r
{\r
- static int TABLE[][3] =\r
- {\r
- { 2, 0, 3 },\r
- { 12, 1, 22 },\r
- { 21, 0, 12 },\r
- { 62, 1, 63 },\r
- { 112, 0, 62 }\r
- };\r
+ // next tuner\r
+ continue;\r
+ }\r
\r
- uint i;\r
- for (i = 0; i < sizeof(TABLE)/sizeof(*TABLE); ++i)\r
+ //\r
+ // 録画開始要否チェック\r
+ //\r
+ bool start_need = false;\r
+ start = end = 0;\r
+ getTimeWithEPG(epg, &start, &end);\r
+ if ((start != 0) && (end != 0))\r
+ {\r
+ String *status = epg->stringForKey(KEY_EPG_STATUS);\r
+ if ((status == NULL) || !(status->isEqualToString("running")))\r
{\r
- if (ch <= TABLE[i][0])\r
+ if (end + OFFSET_OF_END_TIME <= now)\r
{\r
- sprintf_s(channelID, sizeof(channelID), "%s%d", TABLE[i][1] ? "C" : "", ch + TABLE[i][2] - TABLE[i][0]);\r
- break;\r
+ // 既に終了時間が経過しているので削除する\r
+ array->removeObject(epg);\r
+ }\r
+ else if (start + OFFSET_OF_START_TIME <= now)\r
+ {\r
+ start_need = true;\r
}\r
}\r
}\r
\r
- // チャンネルIDを設定\r
- channelInfo->setString(channelID, KEY_CHANNEL_ID);\r
-\r
- RaymLock(this);\r
-\r
- // チャンネル設定\r
+ if (start_need)\r
+ {\r
+ DebugLog2("I need start.\n");\r
+ String *ch = epg->stringForKey(KEY_EPG_CHANNEL);\r
+ if (ch != NULL)\r
+ {\r
+ int channel = atoi(ch->cString());\r
+ DebugLog2("channel: %d\n", channel);\r
+ std::string videopath = createVideoPath(tuner);\r
+ if (videopath != "")\r
+ {\r
+ DebugLog2("videopath: %s\n", videopath.c_str());\r
+ int fd = -1;\r
+ if (_sopen_s(&fd, videopath.c_str(),\r
+ (_O_CREAT | _O_EXCL | _O_WRONLY | _O_BINARY | _O_TRUNC), _SH_DENYRW, (_S_IREAD | _S_IWRITE)) == 0)\r
+ {\r
+ DebugLog2("open ok.\n");\r
+ bool startResult = true;\r
+ if (_tuners[tuner]->channel() != channel)\r
+ {\r
+ if (!setChannel(tuner, channel))\r
+ {\r
+ DebugLog3("setChannel() ng.");\r
+ startResult = false;\r
+ }\r
+ }\r
+\r
+ if (startResult)\r
+ {\r
+ if (_tuners[tuner]->startRecording(fd))\r
+ {\r
+ DebugLog2("startRecording() ok.");\r
+ DebugLog0("start recording of \"%s\" to %s.", _tuners[tuner]->name(), videopath.c_str());\r
+ }\r
+ else\r
+ {\r
+ DebugLog3("Tuner::startRecording() failed.");\r
+ startResult = false;\r
+ }\r
+ }\r
+\r
+ if (startResult)\r
+ {\r
+ epg->setString("running", KEY_EPG_STATUS);\r
+ }\r
+ else\r
+ {\r
+ _close(fd);\r
+ }\r
+ }\r
+ else\r
+ {\r
+ DebugLog0("open ng. 0x%08x\n", errno);\r
+ }\r
+ }\r
+ else\r
+ {\r
+ DebugLog0("Can't create videopath.\n");\r
+ }\r
+ }\r
+ else\r
+ {\r
+ DebugLog0("error.\n");\r
+ }\r
+ }\r
+ }\r
+\r
+ if (need_update)\r
+ {\r
+ //\r
+ _reservations->writeToFile(_reservations_path, true);\r
+ }\r
+\r
+#if 0\r
+ // EPG収集時刻を取得\r
+ String *collect_str = _props->stringForKey(KEY_COLLECT_EPG_TIME);\r
+ if (collect_str != NULL)\r
+ {\r
+ // 秒に変換\r
+ time_t collect_time = 0;\r
+ getTimeWithString(collect_str, &collect_time);\r
+\r
+ // 現在時刻と比較\r
+ if ((collect_time <= now) && (now < collect_time + 1))\r
+ {\r
+ // タイマが起動中か確認\r
+ if ((_timer_epg_s == NULL) || !_timer_epg_s->valid())\r
+ {\r
+ // EPG収集用タイマ起動(ISDB-S)\r
+ RELEASE(_timer_epg_s);\r
+ _timer_epg_s = Timer::alloc()->initWithTimeInterval(DEF_COLLECT_EPG_DELAY, this, (void *)CMD_COLLECT_EPG_ISDB_S, true);\r
+ if (_timer_epg_s != NULL)\r
+ {\r
+ _timer_epg_s->fire();\r
+ }\r
+ }\r
+\r
+ // タイマが起動中か確認\r
+ if ((_timer_epg_t == NULL) || !_timer_epg_t->valid())\r
+ {\r
+ // EPG収集用タイマ起動(ISDB-T)\r
+ RELEASE(_timer_epg_t);\r
+ _timer_epg_t = Timer::alloc()->initWithTimeInterval(DEF_COLLECT_EPG_DELAY, this, (void *)CMD_COLLECT_EPG_ISDB_T, true);\r
+ if (_timer_epg_t != NULL)\r
+ {\r
+ _timer_epg_t->fire();\r
+ }\r
+ }\r
+ }\r
+ }\r
+#endif\r
+\r
+ // unlock\r
+ RaymUnlock(this);\r
+\r
+ //\r
+ // 1/100秒単位が 0 に近くなるように次回T.O.を微調整\r
+ // ただし、windowsは精度が低いので期待しないことw\r
+ //\r
+\r
+#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)\r
+ static const __time64_t DELTA_EPOCH_IN_MICROSECS = 11644473600000000Ui64;\r
+#else\r
+ static const __time64_t DELTA_EPOCH_IN_MICROSECS = 11644473600000000ULL;\r
+#endif\r
+ // 現在時刻を取得\r
+ FILETIME ft;\r
+ GetSystemTimeAsFileTime(&ft);\r
+\r
+ // EPOCH秒への変換\r
+ __time64_t now_sec;\r
+ __time64_t now_usec;\r
+ now_sec = ft.dwHighDateTime;\r
+ now_sec <<= 32;\r
+ now_sec |= ft.dwLowDateTime;\r
+ now_sec /= 10; /*convert into microseconds*/\r
+ now_sec -= DELTA_EPOCH_IN_MICROSECS;\r
+ now_usec = (now_sec % 1000000UL);\r
+ now_sec = now_sec / 1000000UL;\r
+\r
+ TimeInterval interval = (TimeInterval)now_usec;\r
+ interval = interval / 1000000;\r
+ _timer_periodic->setTimeInterval(1.005 - interval);\r
+\r
+#ifdef OBJC_MEMORY_CHECK\r
+ DebugLog0("global_objc_count_ = %d", Raym::global_objc_count_);\r
+#endif\r
+}\r
+\r
+void Controller::updateKeywordsReservation()\r
+{\r
+ DebugLog2("Controller::updateKeywordsReservation()");\r
+\r
+ // lock\r
+ RaymLock(this);\r
+\r
+ // 予約情報からキーワード予約フラグが設定されているものを削除\r
+ for (int tuner = 0; tuner < _tunerCount; ++tuner)\r
+ {\r
+ Array *epgs = _reservations->arrayForKey(_tuners[tuner]->name());\r
+ if (epgs != NULL)\r
+ {\r
+ for (uint epgs_idx_offset = epgs->count(); epgs_idx_offset > 0; --epgs_idx_offset)\r
+ {\r
+ Dictionary *epg = (Dictionary *)epgs->objectAtIndex(epgs_idx_offset - 1);\r
+ if (epg->boolForKey(KEY_EPG_RESERVED_BY_KEYWORDS))\r
+ {\r
+ epgs->removeObject(epg);\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ // キーワードで検索\r
+ Dictionary *keywords_info = _reservations->dictionaryForKey(KEY_EPG_KEYWORDS);\r
+ if (keywords_info != NULL)\r
+ {\r
+ // キーワード有り\r
+\r
+ // _epgs からキー(サービスID)を取得\r
+ Array *service_id_keys = _epgs->allKeys();\r
+\r
+ // サービスIDでループ\r
+ for (uint service_id_keys_idx = 0; service_id_keys_idx < service_id_keys->count(); ++service_id_keys_idx)\r
+ {\r
+ // サービスID\r
+ String *service_id = (String *)service_id_keys->objectAtIndex(service_id_keys_idx);\r
+\r
+ // 局名\r
+ String *stationName = stationNameForServiceID(service_id);\r
+ if (stationName == NULL)\r
+ {\r
+ // 局名が取得できなかったら次のサービスIDへ\r
+ continue;\r
+ }\r
+\r
+ // 指定サービスIDのEPGを取得\r
+ Dictionary *events = _epgs->dictionaryForKey((String *)service_id_keys->objectAtIndex(service_id_keys_idx));\r
+\r
+ // キー(イベントID)を取得\r
+ Array *event_id_keys = events->allKeys();\r
+\r
+ // イベントIDでループ\r
+ for (uint event_id_keys_idx = 0; event_id_keys_idx < event_id_keys->count(); ++event_id_keys_idx)\r
+ {\r
+ // 指定イベントIDのEPGを取得\r
+ Dictionary *epg = events->dictionaryForKey((String *)event_id_keys->objectAtIndex(event_id_keys_idx));\r
+\r
+ // 取得したEPGがキーワードの条件にマッチするか\r
+\r
+ time_t start;\r
+ getTimeWithString(epg->stringForKey(KEY_EPG_START), &start);\r
+\r
+ // キーワードの配列を取得\r
+ Array *keywords = keywords_info->allKeys();\r
+ for (uint keywords_idx = 0; keywords_idx < keywords->count(); ++keywords_idx)\r
+ {\r
+ // キーワード\r
+ String *kwd = (String *)keywords->objectAtIndex(keywords_idx);\r
+\r
+ // キーワード(分割)\r
+ Array *kwds = kwd->componentsSeparatedByString(",");\r
+ if (kwds == NULL)\r
+ {\r
+ continue;\r
+ }\r
+\r
+ // キーワード情報\r
+ Dictionary *kwdinf = keywords_info->dictionaryForKey(kwd);\r
+\r
+ // 局名フィルタ\r
+ String *kwdinf_service_id = kwdinf->stringForKey(KEY_EPG_SERVICE_ID);\r
+ if (kwdinf_service_id != NULL)\r
+ {\r
+ // 局名フィルタ有り\r
+ String *sname = stationNameForServiceID(kwdinf_service_id);\r
+ if (sname != NULL)\r
+ {\r
+ if (!stationName->isEqualToString(sname))\r
+ {\r
+ // 局名が異なる場合は次のキーワードへ\r
+ continue;\r
+ }\r
+ }\r
+ }\r
+\r
+ // 開始時刻フィルタ\r
+ String *kwdinf_start = kwdinf->stringForKey(KEY_EPG_START);\r
+ if (kwdinf_start != NULL)\r
+ {\r
+ // 開始時刻フィルタ有り\r
+ String *st = kwdinf_start->stringByAppendingString(":00");\r
+ time_t kwd_st, kwd_ed;\r
+ getTimeWithString(st, &kwd_st);\r
+ kwd_ed = kwd_st + 3600;\r
+\r
+ // 開始時刻フィルタの時刻 <= EPGの開始時刻 <= 開始時刻フィルタの時刻+60min ならOK\r
+ if ((kwd_st > start) || (start > kwd_ed))\r
+ {\r
+ // 範囲外なので次のキーワードへ\r
+ continue;\r
+ }\r
+ }\r
+\r
+ // タイトル\r
+ String *title = epg->stringForKey(KEY_EPG_TITLE);\r
+ bool title_flag = (title != NULL);\r
+\r
+ // 概要\r
+ String *desc = epg->stringForKey(KEY_EPG_DESCRIPTION);\r
+ bool desc_flag = (desc != NULL);\r
+\r
+ // キーワード(分割)がタイトル/概要のどちらかに、全て(AND)含まれていたら予約対象とする\r
+ for (uint kwds_idx = 0; kwds_idx < kwds->count(); ++kwds_idx)\r
+ {\r
+ String *kw = ((String *)kwds->objectAtIndex(kwds_idx))->stringByTrimming();\r
+\r
+ // タイトル\r
+ if (title != NULL)\r
+ {\r
+ Range r = title->rangeOfString(kw);\r
+ if (r.location == NotFound)\r
+ {\r
+ title_flag = false;\r
+ }\r
+ }\r
+\r
+ // 概要\r
+ if (desc != NULL)\r
+ {\r
+ Range r = desc->rangeOfString(kw);\r
+ if (r.location == NotFound)\r
+ {\r
+ desc_flag = false;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (title_flag || desc_flag)\r
+ {\r
+ // タイトル/概要のどちらかに、キーワード(分割)が全て含まれていたので\r
+ // この EPG を予約対象とする\r
+ Dictionary *epg2 = Dictionary::dictionaryWithDictionary(epg);\r
+ if (epg2 != NULL)\r
+ {\r
+ DebugLog0("kwd: %s", kwd->cString());\r
+ if (title != NULL)\r
+ {\r
+ DebugLog0("title: %s", title->cString());\r
+ }\r
+ if (desc != NULL)\r
+ {\r
+ DebugLog0("desc: %s", desc->cString());\r
+ }\r
+ epg2->setBool(true, KEY_EPG_RESERVED_BY_KEYWORDS);\r
+ reserve(epg2);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ // unlock\r
+ RaymUnlock(this);\r
+}\r
+\r
+void Controller::updateSchedule()\r
+{\r
+ DebugLog2("Controller::updateSchedule()");\r
+\r
+ // lock\r
+ RaymLock(this);\r
+\r
+ // レジューム時刻\r
+ time_t resume_time = 0;\r
+\r
+ // EPG収集時刻を取得\r
+ String *collect_epg_time = _props->stringForKey(KEY_COLLECT_EPG_TIME);\r
+ if (collect_epg_time != NULL)\r
+ {\r
+ getTimeWithString(collect_epg_time, &resume_time);\r
+ }\r
+\r
+ // 録画予約をチェック\r
+ for (int tuner = 0; tuner < _tunerCount; ++tuner)\r
+ {\r
+ Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
+ if ((array == NULL) || (array->count() == 0))\r
+ {\r
+ // next tuner\r
+ continue;\r
+ }\r
+\r
+ Dictionary *epg = (Dictionary *)array->objectAtIndex(0);\r
+ time_t start = 0;\r
+ time_t end = 0;\r
+ getTimeWithEPG(epg, &start, &end);\r
+ if ((start != 0) && (end != 0))\r
+ {\r
+ if (start < resume_time)\r
+ {\r
+ resume_time = start;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ TM resume_tm;\r
+ resume_time += OFFSET_OF_WAKEUP; // 起動時刻を 5分前に設定(OFFSET_OF_WAKEUPで調整)\r
+ if (localtime_s(&resume_tm, &resume_time) == 0)\r
+ {\r
+ resetWakeSchedule();\r
+ if (setWakeSchedule(resume_tm.tm_year + 1900, resume_tm.tm_mon + 1, resume_tm.tm_mday, resume_tm.tm_hour, resume_tm.tm_min))\r
+ {\r
+ DebugLog0("set wake schedule to \"%04d/%02d/%02d %02d:%02d:00\".",\r
+ resume_tm.tm_year + 1900, resume_tm.tm_mon + 1, resume_tm.tm_mday, resume_tm.tm_hour, resume_tm.tm_min);\r
+ }\r
+ }\r
+\r
+ // unlock\r
+ RaymUnlock(this);\r
+}\r
+\r
+#ifndef _WIN32\r
+#pragma mark '\r
+#pragma mark ------- チューナ制御 -------\r
+#endif\r
+\r
+void Controller::scanChannel(int tuner)\r
+{\r
+ DebugLog2("Controller::scanChannel(%d)", tuner);\r
+\r
+ if ((tuner < 0) || (_tunerCount <= tuner))\r
+ {\r
+ DebugLog3("Invalid tuner: %d", tuner);\r
+ return;\r
+ }\r
+\r
+ DebugLog0("start channel scan of \"%s\".", _tuners[tuner]->name());\r
+\r
+ // lock\r
+ RaymLock(this);\r
+\r
+ // 設定ファイルから全チューナ情報取得\r
+ Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
+ if (tunersInfo == NULL)\r
+ {\r
+ // 無ければ作成\r
+ tunersInfo = Dictionary::dictionaryWithCapacity(0);\r
+ _props->setObject(tunersInfo, KEY_TUNERS);\r
+ }\r
+\r
+ // 全チューナ情報から指定のチューナ情報を取得\r
+ Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
+ if (tunerInfo == NULL)\r
+ {\r
+ // 無ければ作成\r
+ tunerInfo = Dictionary::dictionaryWithCapacity(0);\r
+ tunersInfo->setObject(tunerInfo, _tuners[tuner]->name());\r
+ }\r
+\r
+ // チューナを未初期化に設定\r
+ tunerInfo->setBool(false, KEY_INITIALIZED);\r
+\r
+ // unlock\r
+ RaymUnlock(this);\r
+\r
+ // 全チャンネル情報を作成\r
+ Dictionary *channels = Dictionary::dictionaryWithCapacity(0);\r
+ tunerInfo->setObject(channels, KEY_CHANNELS);\r
+\r
+ // チューナタイプにより最大チャンネル数を設定\r
+ int max_channel;\r
+ Tuner::Type type = _tuners[tuner]->type();\r
+ if (type == Tuner::ISDB_S)\r
+ {\r
+ max_channel = Tuner::MAX_CHANNELS_ISDB_S;\r
+ }\r
+ else\r
+ {\r
+ max_channel = Tuner::MAX_CHANNELS_ISDB_T;\r
+ }\r
+\r
+ // 最終設定成功チャンネル保持用変数\r
+ int lastChannel = -1;\r
+\r
+ // チャンネルサーチ\r
+ for (int ch = 0; ch <= max_channel; ++ch)\r
+ {\r
+ // チャンネル情報\r
+ Dictionary *channelInfo = Dictionary::dictionaryWithCapacity(0);\r
+\r
+ // チャンネルキー(10進数:PTに対するチャンネル番号)\r
+ char chkey[4];\r
+ sprintf_s(chkey, sizeof(chkey), "%03d", ch);\r
+ channels->setObject(channelInfo, chkey);\r
+\r
+ // チャンネルID(論理チャンネル番号)\r
+ char channelID[8];\r
+ if (type == Tuner::ISDB_S)\r
+ {\r
+ if (ch < 12)\r
+ {\r
+ sprintf_s(channelID, sizeof(channelID), "BS%02d", 1 + 2 * ch);\r
+ }\r
+ else if (ch < 24)\r
+ {\r
+ sprintf_s(channelID, sizeof(channelID), "ND%02d", 2 + 2 * (ch - 12));\r
+ }\r
+ else\r
+ {\r
+ sprintf_s(channelID, sizeof(channelID), "ND%02d", 1 + 2 * (ch -24));\r
+ }\r
+ }\r
+ else\r
+ {\r
+ static int TABLE[][3] =\r
+ {\r
+ { 2, 0, 3 },\r
+ { 12, 1, 22 },\r
+ { 21, 0, 12 },\r
+ { 62, 1, 63 },\r
+ { 112, 0, 62 }\r
+ };\r
+\r
+ uint i;\r
+ for (i = 0; i < sizeof(TABLE)/sizeof(*TABLE); ++i)\r
+ {\r
+ if (ch <= TABLE[i][0])\r
+ {\r
+ sprintf_s(channelID, sizeof(channelID), "%s%d", TABLE[i][1] ? "C" : "", ch + TABLE[i][2] - TABLE[i][0]);\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ // チャンネルIDを設定\r
+ channelInfo->setString(channelID, KEY_CHANNEL_ID);\r
+\r
+ // lock\r
+ RaymLock(this);\r
+\r
+ // チャンネル設定\r
if (_tuners[tuner]->setChannel(ch))\r
{\r
// 設定成功\r
DebugLog0(" CH %s: NG", chkey);\r
}\r
\r
+ // unlock\r
RaymUnlock(this);\r
}\r
\r
+ // lock\r
RaymLock(this);\r
\r
// チューナを初期化済みに更新\r
// 設定ファイルを更新\r
_props->writeToFile(_props_path, true);\r
\r
+ // unlock\r
RaymUnlock(this);\r
}\r
\r
-/*\r
- *\r
- */\r
int Controller::getChannel(int tuner)\r
{\r
DebugLog2("Controller::getChannel()");\r
\r
int channel = -1;\r
\r
- RaymLock(this);\r
-\r
- if ((0 <= tuner) && (tuner < _tuner_count))\r
+ if ((0 <= tuner) && (tuner < _tunerCount))\r
{\r
- Dictionary *tunersInfo = _status->dictionaryForKey(KEY_TUNERS);\r
+ // lock\r
+ RaymLock(this);\r
+\r
+ Dictionary *tunersInfo = _status->dictionaryForKey(KEY_TUNERS);\r
if (tunersInfo != NULL)\r
{\r
Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
channel = tunerInfo->integerForKey(KEY_CHANNEL);\r
}\r
}\r
- }\r
\r
- RaymUnlock(this);\r
+ // unlock\r
+ RaymUnlock(this);\r
+ }\r
\r
return channel;\r
}\r
\r
-/*\r
- *\r
- */\r
bool Controller::setChannel(int tuner, int channel)\r
{\r
DebugLog2("Controller::setChannel()");\r
\r
bool result = false;\r
\r
- RaymLock(this);\r
-\r
- if ((0 <= tuner) && (tuner < _tuner_count))\r
+ if ((0 <= tuner) && (tuner < _tunerCount))\r
{\r
+ // lock\r
+ RaymLock(this);\r
+\r
if (!_tuners[tuner]->isLocked())\r
{\r
Dictionary *tunersInfo = _status->dictionaryForKey(KEY_TUNERS);\r
\r
result = true;\r
}\r
+ else\r
+ {\r
+ DebugLog0("set channel failed. %d, %d", tuner, channel);\r
+ }\r
}\r
}\r
- }\r
\r
- RaymUnlock(this);\r
+ // unlock\r
+ RaymUnlock(this);\r
+ }\r
\r
return result;\r
}\r
\r
+//\r
+// チューナ(streaming)制御/システム関連 周期処理\r
+//\r
+void Controller::periodic_2(void)\r
+{\r
+ //\r
+ // UDPポート監視\r
+ //\r
+\r
+ // マッピング(UDPPort:tuner,ch)情報取得\r
+ Dictionary *mapping = NULL;\r
+ if ((_streaming_ctrls != NULL) && ((mapping = _streaming_ctrls->dictionaryForKey(KEY_MAPPING_UDP_TO_TUNER_CHANNEL)) != NULL))\r
+ {\r
+ // マッピング情報取得OK\r
+\r
+ // 使用中のUDPの情報を取得\r
+ // どれだけ使用中なのか不明なので、まずは必要サイズを調べる\r
+ DWORD size = 0;\r
+ if (GetExtendedUdpTable(NULL, &size, true, AF_INET, UDP_TABLE_OWNER_PID, 0) == ERROR_INSUFFICIENT_BUFFER)\r
+ {\r
+ // ERROR_INSUFFICIENT_BUFFER の場合、必要なバッファサイズが size に格納される\r
+\r
+ // バッファ確保\r
+ PMIB_UDPTABLE_OWNER_PID udptable = (PMIB_UDPTABLE_OWNER_PID)malloc(size);\r
+ if (udptable != NULL)\r
+ {\r
+ // バッファ確保OK\r
+\r
+ // UDP情報取得\r
+ if (GetExtendedUdpTable(udptable, &size, true, AF_INET, UDP_TABLE_OWNER_PID, 0) == NO_ERROR)\r
+ {\r
+ // 取得OK\r
+ DebugLog3("udptable->dwNumEntries: %d", udptable->dwNumEntries);\r
+\r
+ // 停止要否確認\r
+ Dictionary *using_port = _streaming_ctrls->dictionaryForKey(KEY_UDP_IN_USE);\r
+ if (using_port != NULL)\r
+ {\r
+ // key = 使用中ポート\r
+ Array *using_ports = using_port->allKeys();\r
+ if (using_ports != NULL)\r
+ {\r
+ // 使用中ポートでループ\r
+ for (uint i = 0; i < using_ports->count(); ++i)\r
+ {\r
+ // 停止要否フラグ\r
+ bool stop_need = true;\r
+\r
+ // 使用中のUDP情報でループ\r
+ for (uint j = 0; j < udptable->dwNumEntries; ++j)\r
+ {\r
+ if (((String *)using_ports->objectAtIndex(i))->intValue() == ntohs((WORD)udptable->table[j].dwLocalPort))\r
+ {\r
+ // 使用中なので停止不要\r
+ stop_need = false;\r
+ break;\r
+ }\r
+ }\r
+\r
+ // 停止要否\r
+ if (stop_need)\r
+ {\r
+ // マッピング情報を取得\r
+ String *tuner_and_channel = mapping->stringForKey((String *)using_ports->objectAtIndex(i));\r
+ if (tuner_and_channel != NULL)\r
+ {\r
+ // チューナとチャンネルに分割\r
+ Range r = tuner_and_channel->rangeOfString(",");\r
+ if (r.location != NotFound)\r
+ {\r
+ int tuner = tuner_and_channel->substringToIndex(r.location)->intValue();\r
+ int channel = tuner_and_channel->substringFromIndex(r.location + 1)->intValue();\r
+ DebugLog3("tuner: %d, channel: %d", tuner, channel);\r
+\r
+ DebugLog0("auto streaming stop: %s", ((String *)using_ports->objectAtIndex(i))->cString());\r
+\r
+ _tuners[tuner]->stopStreaming();\r
+ using_port->removeObjectForKey((String *)using_ports->objectAtIndex(i));\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+\r
+ // 起動要否確認\r
+ for (uint i = 0; i < udptable->dwNumEntries; ++i)\r
+ {\r
+ // ポート番号を文字列に変換して\r
+ char port[10];\r
+ sprintf_s(port, "%d", ntohs((WORD)udptable->table[i].dwLocalPort));\r
+ DebugLog3("port = %s", port);\r
+\r
+ // マッピング情報を取得\r
+ String *tuner_and_channel = mapping->stringForKey(port);\r
+ if (tuner_and_channel != NULL)\r
+ {\r
+ // 取得OK: 監視対象ポートが使用されている\r
+\r
+ // 使用アプリを調べる\r
+ bool auto_streaming = false;\r
+ char exec_path[MAX_PATH];\r
+ memset(exec_path, 0, sizeof(exec_path));\r
+\r
+ // プロセスハンドル取得\r
+ size_t returnValue;\r
+ HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, udptable->table[i].dwOwningPid);\r
+ if (hProcess != NULL)\r
+ {\r
+ TCHAR exec[MAX_PATH];\r
+ memset(exec, 0, sizeof(exec));\r
+ DWORD len = sizeof(exec) - 1;\r
+\r
+ // イメージ取得\r
+ if (QueryFullProcessImageName(hProcess, 0, exec, &len))\r
+ {\r
+ // ワイド -> マルチ 変換\r
+ if (wcstombs_s(&returnValue, exec_path, sizeof(exec_path), exec, _TRUNCATE) == 0)\r
+ {\r
+ // 成功\r
+\r
+ // とりあえず、、、現状は "ffmpeg.exe" / "vlc.exe" / "Kodi.exe" があったら auto_streaming を true にする\r
+ if ((strstr(exec_path, "ffmpeg.exe") != NULL) ||\r
+ (strstr(exec_path, "vlc.exe") != NULL) ||\r
+ (strstr(exec_path, "Kodi.exe") != NULL))\r
+ {\r
+ auto_streaming = true;\r
+ }\r
+ }\r
+ }\r
+\r
+ // プロセスハンドル解放\r
+ CloseHandle(hProcess);\r
+ }\r
+\r
+ if (auto_streaming)\r
+ {\r
+ // チューナとチャンネルに分割\r
+ Range r = tuner_and_channel->rangeOfString(",");\r
+ if (r.location != NotFound)\r
+ {\r
+ int tuner = tuner_and_channel->substringToIndex(r.location)->intValue();\r
+ int channel = tuner_and_channel->substringFromIndex(r.location + 1)->intValue();\r
+ DebugLog3("tuner: %d, channel: %d", tuner, channel);\r
+\r
+ // lock\r
+ RaymLock(this);\r
+\r
+ // 非ストリーミング中 かつ 非レコーディング中 または チャンネルが同じ 場合\r
+ if (!_tuners[tuner]->isStreaming() && (!_tuners[tuner]->isRecording() || _tuners[tuner]->channel() == channel))\r
+ {\r
+ // ストリーミング開始可能\r
+\r
+ if (_tuners[tuner]->channel() != channel)\r
+ {\r
+ setChannel(tuner, channel);\r
+ }\r
+\r
+ SOCKADDR_IN dst_addr;\r
+ dst_addr.sin_family = AF_INET;\r
+ dst_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);\r
+ dst_addr.sin_port = (WORD)udptable->table[i].dwLocalPort;\r
+\r
+ if (_tuners[tuner]->startStreaming(&dst_addr))\r
+ {\r
+ // 成功\r
+ DebugLog0("auto streaming start: %d", ntohs((WORD)udptable->table[i].dwLocalPort));\r
+\r
+ // 使用中ポートに登録\r
+ using_port = _streaming_ctrls->dictionaryForKey(KEY_UDP_IN_USE);\r
+ if (using_port == NULL)\r
+ {\r
+ using_port = Dictionary::dictionaryWithCapacity(0);\r
+ _streaming_ctrls->setObject(using_port, KEY_UDP_IN_USE);\r
+ }\r
+ using_port->setBool(true, port);\r
+ }\r
+ }\r
+\r
+ // unlock\r
+ RaymUnlock(this);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ // バッファ解放\r
+ free(udptable);\r
+ }\r
+ }\r
+ }\r
+\r
+\r
+\r
+ //\r
+ // 1/100秒単位が 0 に近くなるように次回T.O.を微調整\r
+ //\r
+\r
+#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)\r
+ static const __time64_t DELTA_EPOCH_IN_MICROSECS = 11644473600000000Ui64;\r
+#else\r
+ static const __time64_t DELTA_EPOCH_IN_MICROSECS = 11644473600000000ULL;\r
+#endif\r
+ // 現在時刻を取得\r
+ FILETIME ft;\r
+ GetSystemTimeAsFileTime(&ft);\r
+\r
+ // EPOCH秒への変換\r
+ __time64_t now_sec;\r
+ __time64_t now_usec;\r
+ now_sec = ft.dwHighDateTime;\r
+ now_sec <<= 32;\r
+ now_sec |= ft.dwLowDateTime;\r
+ now_sec /= 10; /*convert into microseconds*/\r
+ now_sec -= DELTA_EPOCH_IN_MICROSECS;\r
+ now_usec = (now_sec % 1000000UL);\r
+ now_sec = now_sec / 1000000UL;\r
+\r
+ TimeInterval interval = (TimeInterval)now_usec;\r
+ interval = interval / 1000000;\r
+ _timer_periodic_2->setTimeInterval(1.005 - interval);\r
+}\r
+\r
#ifndef _WIN32\r
#pragma mark '\r
-#pragma mark ------- タイマディスパッチャ -------\r
+#pragma mark ------- HTTP制御 -------\r
#endif\r
\r
-void Controller::timerExpired(Timer *timer, void *userInfo)\r
+static std::string epg_regist_form(Dictionary *epg)\r
{\r
- DebugLog2("Controller::timerExpired()");\r
+ DebugLog3("epg_regist_form() start.");\r
\r
- //\r
+ std::string epgs;\r
\r
- switch ((long long)userInfo)\r
+ if ((epg != NULL) &&\r
+ (epg->stringForKey(KEY_EPG_SERVICE_ID) != NULL) &&\r
+ (epg->stringForKey(KEY_EPG_EVENT_ID) != NULL) &&\r
+ (epg->stringForKey(KEY_EPG_START) != NULL) &&\r
+ (epg->stringForKey(KEY_EPG_END) != NULL) &&\r
+ (epg->stringForKey(KEY_EPG_DATE) != NULL) &&\r
+ (epg->stringForKey(KEY_EPG_TITLE) != NULL))\r
{\r
- case CMD_RESTART:\r
- if (restart() > 0)\r
+ epgs += "<form id=\"";\r
+ epgs += "epg_";\r
+ epgs += epg->stringForKey(KEY_EPG_SERVICE_ID)->cString();\r
+ epgs += "_";\r
+ epgs += epg->stringForKey(KEY_EPG_EVENT_ID)->cString();\r
+ epgs += "\" title=\"";\r
+ epgs += epg->stringForKey(KEY_EPG_START)->substringToIndex(5)->cString();\r
+ epgs += "-";\r
+ epgs += epg->stringForKey(KEY_EPG_END)->substringToIndex(5)->cString();\r
+ epgs += "\" class=\"panel\" action=\"regist.cgi\" method=\"GET\" target=\"_self\" onclick='return confirm(\"Is it ok?\");'>";\r
+\r
+ // 放送時間\r
+ epgs += "<h2>";\r
+ epgs += LocalizedString(KEY_I18N_Broadcasting_Time, NULL)->cString();\r
+ epgs += "</h2>";\r
+ epgs += "<fieldset>";\r
+ epgs += "<p class=\"normalText\"> ";\r
+ epgs += epg->stringForKey(KEY_EPG_DATE)->substringFromIndex(5)->cString();\r
+ epgs += " ";\r
+ epgs += epg->stringForKey(KEY_EPG_START)->substringToIndex(5)->cString();\r
+ epgs += "-";\r
+ epgs += epg->stringForKey(KEY_EPG_END)->substringToIndex(5)->cString();\r
+ epgs += "</p>";\r
+ epgs += "</fieldset>";\r
+\r
+ // 番組名\r
+ epgs += "<h2>";\r
+ epgs += LocalizedString(KEY_I18N_Program_Title, NULL)->cString();\r
+ epgs += "</h2>";\r
+ epgs += "<fieldset>";\r
+ epgs += "<p class=\"normalText\"> ";\r
+ epgs += epg->stringForKey(KEY_EPG_TITLE)->cString();\r
+ epgs += "</p>";\r
+ epgs += "</fieldset>";\r
+\r
+ // 概要\r
+ if (epg->stringForKey(KEY_EPG_DESCRIPTION) != NULL)\r
{\r
- // 初期化成功\r
- DebugLog2("tuner initialize success.");\r
+ epgs += "<h2>";\r
+ epgs += LocalizedString(KEY_I18N_Description, NULL)->cString();\r
+ epgs += "</h2>";\r
+ epgs += "<fieldset>";\r
+ epgs += "<p class=\"normalText\"> ";\r
+ epgs += epg->stringForKey(KEY_EPG_DESCRIPTION)->cString();\r
+ epgs += "</p>";\r
+ epgs += "</fieldset>";\r
+ }\r
+\r
+ //\r
+ epgs += "<input type=\"hidden\" name=\"service_id\" value=\"";\r
+ epgs += epg->stringForKey(KEY_EPG_SERVICE_ID)->cString();\r
+ epgs += "\"/>";\r
+ epgs += "<input type=\"hidden\" name=\"event_id\" value=\"";\r
+ epgs += epg->stringForKey(KEY_EPG_EVENT_ID)->cString();\r
+ epgs += "\"/>";\r
+ epgs += "<input class=\"redButton\" type=\"submit\" value=\"";\r
+ epgs += LocalizedString(KEY_I18N_New_Reservation, NULL)->cString();\r
+ epgs += "\"/>";\r
+ // epgs += "<a class=\"redButton\" type=\"submit\">";\r
+ // epgs += LocalizedString(KEY_I18N_New_Reservation, NULL)->cString();\r
+ // epgs += "</a>";\r
+ epgs += "</form>";\r
+ }\r
+\r
+ return epgs;\r
+}\r
+\r
+HTTPResponse *responseWithDictionary(HTTPRequest *request, Dictionary *dictionary)\r
+{\r
+ HTTPResponse *result = NULL;\r
+ if ((request != NULL) && (dictionary != NULL))\r
+ {\r
+ std::string xml = dictionary->toString();\r
+\r
+ // header\r
+ InternetTextMessageHeader *header = InternetTextMessageHeader::alloc()->init();\r
+ // Date\r
+ // Server\r
+ // Content-Encoding\r
+ // Last-Modified\r
+ // Content-Type\r
+ header->setFieldBodyWithName("application/xml", "Content-Type");\r
+ // Connection\r
+ // Tranfer-Encoding\r
+ // Content-Length\r
+ header->setFieldBodyWithName(String::stringWithFormat("%I64u", xml.length()), "Content-Length");\r
+\r
+ // body\r
+ InternetTextMessageBody *body = InternetTextMessageBody::alloc()->initWithString(String::stringWithUTF8String(xml.c_str()));\r
+\r
+ // message\r
+ InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, body);\r
+ RELEASE(header);\r
+ RELEASE(body);\r
+ if (message != NULL)\r
+ {\r
+ result = HTTPResponse::alloc()->init();\r
+ result->autorelease();\r
+ result->setVersion(request->version());\r
+ result->setReason(NET::HTTPDaemon::reasonForStatus(200));\r
+ result->setStatus(200);\r
+ result->setMessage(message);\r
+ RELEASE(message);\r
+ }\r
+ }\r
+ return result;\r
+}\r
+\r
+// positive response by XML\r
+HTTPResponse *responseForSuccess(HTTPRequest *request)\r
+{\r
+ Dictionary *dict = Dictionary::dictionaryWithCapacity(0);\r
+ dict->setString("Success", KEY_RESULT);\r
+ return responseWithDictionary(request, dict);\r
+}\r
+\r
+// negative response by XML\r
+HTTPResponse *responseForFailed(HTTPRequest *request)\r
+{\r
+ Dictionary *dict = Dictionary::dictionaryWithCapacity(0);\r
+ dict->setString("Failed", KEY_RESULT);\r
+ return responseWithDictionary(request, dict);\r
+}\r
+\r
+HTTPResponse *Controller::responseWithHTML(HTTPRequest *request, String *html)\r
+{\r
+ HTTPResponse *result = NULL;\r
+ if ((html != NULL) && (request != NULL))\r
+ {\r
+ // header\r
+ InternetTextMessageHeader *header = InternetTextMessageHeader::alloc()->init();\r
+ // Date\r
+ // Server\r
+ // Content-Encoding\r
+ // Last-Modified\r
+ // Content-Type\r
+ header->setFieldBodyWithName("text/html", "Content-Type");\r
+ // Connection\r
+ // Tranfer-Encoding\r
+ // Content-Length\r
+ header->setFieldBodyWithName(String::stringWithFormat("%I64u", html->length()), "Content-Length");\r
+\r
+ // body\r
+ InternetTextMessageBody *body = InternetTextMessageBody::alloc()->initWithString(html);\r
+\r
+ // message\r
+ InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, body);\r
+ RELEASE(header);\r
+ RELEASE(body);\r
+ if (message != NULL)\r
+ {\r
+// result = HTTPResponse::response();\r
+ result = HTTPResponse::alloc()->init();\r
+ result->setVersion(request->version());\r
+ result->setReason(NET::HTTPDaemon::reasonForStatus(200));\r
+ result->setStatus(200);\r
+ result->setMessage(message);\r
+ result->autorelease();\r
+ RELEASE(message);\r
+ }\r
+ }\r
+ return result;\r
+}\r
+\r
+HTTPResponse *Controller::responseWithUTF8Text(HTTPRequest *request, String *text)\r
+{\r
+ HTTPResponse *result = NULL;\r
+ if ((text != NULL) && (request != NULL))\r
+ {\r
+ // header\r
+ InternetTextMessageHeader *header = InternetTextMessageHeader::alloc()->init();\r
+ // Date\r
+ // Server\r
+ // Content-Encoding\r
+ // Last-Modified\r
+ // Content-Type\r
+ header->setFieldBodyWithName("text/plane; charset=UTF-8", "Content-Type");\r
+ // Connection\r
+ // Tranfer-Encoding\r
+ // Content-Length\r
+ header->setFieldBodyWithName(String::stringWithFormat("%I64u", text->length()), "Content-Length");\r
+\r
+ // body\r
+ InternetTextMessageBody *body = InternetTextMessageBody::alloc()->initWithString(text);\r
+\r
+ // message\r
+ InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, body);\r
+ RELEASE(header);\r
+ RELEASE(body);\r
+ if (message != NULL)\r
+ {\r
+// result = HTTPResponse::response();\r
+ result = HTTPResponse::alloc()->init();\r
+ result->setVersion(request->version());\r
+ result->setReason(NET::HTTPDaemon::reasonForStatus(200));\r
+ result->setStatus(200);\r
+ result->setMessage(message);\r
+ result->autorelease();\r
+ RELEASE(message);\r
+ }\r
+ }\r
+ return result;\r
+}\r
+\r
+HTTPResponse *Controller::responseByResultAndReferer(HTTPRequest *request, bool result, const char *referer)\r
+{\r
+ HTTPResponse *retval = NULL;\r
+\r
+ bool iui = false;\r
+\r
+ String *ref = NULL;\r
+ InternetTextMessage *msg = request->message();\r
+ if (msg != NULL)\r
+ {\r
+ InternetTextMessageHeader *header = msg->header();\r
+ if (header != NULL)\r
+ {\r
+ String *field_referer = header->fieldBodyForName("Referer");\r
+ if (field_referer != NULL)\r
+ {\r
+ String *field_host = header->fieldBodyForName("Host");\r
+ if (field_host != NULL)\r
+ {\r
+ std::string tmp = "http://";\r
+ tmp += field_host->cString();\r
+ tmp += "/";\r
+ tmp += referer;\r
+ iui = field_referer->isEqualToString(tmp.c_str());\r
+ DebugLog2("tmp: %s", tmp.c_str());\r
+ DebugLog2("ref: %s", field_referer->cString());\r
+ }\r
+ }\r
+ }\r
+ }\r
+// if ((ref != NULL) && match(ref->cString(), referer))\r
+// if ((ref != NULL) && ref->isEqualToString(referer))\r
+ if (iui)\r
+ {\r
+ String *path = _httpd->rootPath()->stringByAppendingPathComponent("template2.html");\r
+ String *html = String::stringWithContentsOfFile(path->cString(), UTF8StringEncoding);\r
+ if (html != NULL)\r
+ {\r
+ html = html->stringByReplacingOccurrencesOfString("%%TITLE%%", "Result");\r
+ std::string contents;\r
+ std::string reservations;\r
+ contents += "<div id=\"home\" class=\"panel\" title=\"Result\" selected=\"true\">";\r
+ if (result)\r
+ {\r
+ contents += "<h2>Success</h2>";\r
+ }\r
+ else\r
+ {\r
+ contents += "<h2>Failed</h2>";\r
+ }\r
+ contents += "</div>";\r
+\r
+ html = html->stringByReplacingOccurrencesOfString("%%CONTENTS%%", contents.c_str());\r
+ retval = responseWithHTML(request, html);\r
+ }\r
+ }\r
+ else\r
+ {\r
+ if (result)\r
+ {\r
+ retval = responseForSuccess(request);\r
}\r
else\r
{\r
- // 失敗\r
- DebugLog0("tuner initialize failed.");\r
+ retval = responseForFailed(request);\r
}\r
- break;\r
+ }\r
\r
+ return retval;\r
+}\r
+\r
+HTTPResponse *Controller::responseForMain(HTTPRequest *request, SOCKADDR_IN *client)\r
+{\r
+ DebugLog2("Controller::responseForMain()");\r
+\r
+ HTTPResponse *result = NULL;\r
+ while ((request != NULL) && (client != NULL))\r
+ {\r
+ String *path = _httpd->rootPath();\r
+ if (path == NULL)\r
+ {\r
+ DebugLog3("_httpd->rootPath() ng.");\r
+ break;\r
+ }\r
+\r
+ path = path->stringByAppendingPathComponent("template1.html");\r
+ if (path == NULL)\r
+ {\r
+ DebugLog3("path->stringByAppendingPathComponent() ng.");\r
+ break;\r
+ }\r
+\r
+ String *html = String::stringWithContentsOfFile(path->cString(), UTF8StringEncoding);\r
+ if (html == NULL)\r
+ {\r
+ DebugLog3("String::stringWithContentsOfFile() ng.");\r
+ break;\r
+ }\r
+\r
+ String *server_name = _props->stringForKey(KEY_NAME);\r
+ if (server_name == NULL)\r
+ {\r
+ DebugLog3("_props->stringForKey(KEY_NAME) ng.");\r
+ break;\r
+ }\r
+\r
+ html = html->stringByReplacingOccurrencesOfString("%%TITLE%%", server_name);\r
+ if (html == NULL)\r
+ {\r
+ DebugLog3("html->stringByReplacingOccurrencesOfString() ng.");\r
+ break;\r
+ }\r
+\r
+ html = html->stringByReplacingOccurrencesOfString("%%PAGE_TITLE%%", LocalizedString(KEY_I18N_Main_Menu, NULL));\r
+ if (html == NULL)\r
+ {\r
+ DebugLog3("html->stringByReplacingOccurrencesOfString() ng.");\r
+ }\r
+\r
+ std::string contents;\r
+ contents += "<div id=\"home\" class=\"panel\" selected=\"true\">";\r
+ contents += "<ul>";\r
+ contents += "<li><a target=\"_self\" href=\"/programs.html\">";\r
+ contents += LocalizedString(KEY_I18N_Programs, NULL)->cString();\r
+ contents += "</a></li>";\r
+ contents += "<li><a target=\"_self\" href=\"/tv.html\">";\r
+ contents += LocalizedString(KEY_I18N_TV, NULL)->cString();\r
+ contents += "</a></li>";\r
+ contents += "<li><a target=\"_self\" href=\"/video.html\">";\r
+ contents += LocalizedString(KEY_I18N_Video, NULL)->cString();\r
+ contents += "</a></li>";\r
+ contents += "<li><a target=\"_self\" href=\"/reservation.html\">";\r
+ contents += LocalizedString(KEY_I18N_Reservation, NULL)->cString();\r
+ contents += "</a></li>";\r
+ contents += "</ul>";\r
#if 0\r
- case CMD_SUSPEND:\r
- if (canSuspend())\r
+ contents += "<ul>";\r
+ contents += "<li><a target=\"_self\" href=\"/exec_vlc.html\">VLC media player";\r
+ contents += "</ul>";\r
+#endif\r
+ contents += "<ul>";\r
+ contents += "<li><a target=\"_self\" href=\"/status.html\">";\r
+ contents += LocalizedString(KEY_I18N_Tuner_Status, NULL)->cString();\r
+ contents += "</a></li>";\r
+ contents += "</ul>";\r
+ contents += "<center><a target=\"_self\" href=\"/iptd.log\">Ver. ";\r
+ contents += VERSION;\r
+ contents += "</a></center>";\r
+ contents += "</div>";\r
+\r
+ html = html->stringByReplacingOccurrencesOfString("%%CONTENTS%%", contents.c_str());\r
+ if (html != NULL)\r
{\r
- suspend();\r
+ result = responseWithHTML(request, html);\r
}\r
+\r
break;\r
-#endif\r
}\r
-}\r
\r
-#ifndef _WIN32\r
-#pragma mark '\r
-#pragma mark ------- その他 -------\r
-#endif\r
+ return result;\r
+}\r
\r
-//\r
-// HH:MM:SS 形式の文字列から time_t に変換\r
-// 現在時刻が指定文字列の時刻以前の場合、当日の時刻。指定文字列の時刻よりも過ぎている場合、翌日の時刻を返す。\r
-//\r
-void Controller::getTimeWithString(String *str, time_t *time_var)\r
+HTTPResponse *Controller::responseForPrograms(HTTPRequest *request, SOCKADDR_IN *client)\r
{\r
- if ((str != NULL) && str->isMatch(String::stringWithUTF8String("^\\d\\d:\\d\\d:\\d\\d$")) && (time_var != NULL))\r
+ DebugLog2("Controller::responseForPrograms() start.");\r
+\r
+ HTTPResponse *result = NULL;\r
+ while ((request != NULL) && (client != NULL))\r
{\r
- // 時:分:秒 を int型に分解\r
- std::string time_str = str->cString();\r
- int hour = atoi(time_str.substr(0, 2).c_str());\r
- int min = atoi(time_str.substr(3, 2).c_str());\r
- int sec = atoi(time_str.substr(6, 2).c_str());\r
- DebugLog2("%02d:%02d:%02d", hour, min, sec);\r
+ String *path = _httpd->rootPath();\r
+ if (path == NULL)\r
+ {\r
+ DebugLog3("_httpd->rootPath() ng.");\r
+ break;\r
+ }\r
\r
- // 現在時刻取得\r
- time_t now = time(NULL);\r
- TM now_tm;\r
- if (localtime_s(&now_tm, &now) == 0)\r
+ path = path->stringByAppendingPathComponent("template2.html");\r
+ if (path == NULL)\r
{\r
- int now_sec = now_tm.tm_hour * 3600 + now_tm.tm_min * 60 + now_tm.tm_sec;\r
- int col_sec = hour * 3600 + min * 3600 + sec;\r
- if (now_sec > col_sec)\r
+ DebugLog3("path->stringByAppendingPathComponent() ng.");\r
+ break;\r
+ }\r
+\r
+ String *html = String::stringWithContentsOfFile(path->cString(), UTF8StringEncoding);\r
+ if (html == NULL)\r
+ {\r
+ DebugLog3("String::stringWithContentsOfFile() ng.");\r
+ break;\r
+ }\r
+\r
+ html = html->stringByReplacingOccurrencesOfString("%%TITLE%%", LocalizedString(KEY_I18N_Programs, NULL));\r
+ if (html == NULL)\r
+ {\r
+ DebugLog3("html->stringByReplacingOccurrencesOfString() ng.");\r
+ break;\r
+ }\r
+\r
+ std::string contents;\r
+ std::string epgs;\r
+\r
+ contents += "<ul id=\"home\" title=\"";\r
+ contents += LocalizedString(KEY_I18N_Programs, NULL)->cString();\r
+ contents += "\" selected=\"true\">";\r
+ // 地デジ\r
+ contents += "<li>";\r
+ contents += "<a href=\"#isdb_t\">";\r
+ contents += LocalizedString(KEY_I18N_Digital_Terrestrial_Television_Broadcasting, NULL)->cString();\r
+ contents += "</a></li>";\r
+ // BS/CS\r
+ contents += "<li>";\r
+ contents += "<a href=\"#isdb_s\">";\r
+ contents += "BS/CS";\r
+ contents += "</a></li>";\r
+\r
+ contents += "</ul>";\r
+\r
+ // 地デジ(局名)\r
+ contents += "<ul id=\"isdb_t\" title=\"";\r
+ contents += LocalizedString(KEY_I18N_Digital_Terrestrial_Television_Broadcasting, NULL)->cString();\r
+ contents += "\">";\r
+ Array *stations = stationInfos(Tuner::ISDB_T);\r
+ for (uint i = 0; i < stations->count(); ++i)\r
+ {\r
+ Dictionary *station = (Dictionary *)stations->objectAtIndex(i);\r
+ if ((station != NULL) &&\r
+ (station->stringForKey(KEY_CHANNEL_ID) != NULL) &&\r
+ (station->stringForKey(KEY_NAME) != NULL))\r
{\r
- ++now_tm.tm_mday;\r
+ contents += "<li>";\r
+ contents += "<a href=\"#isdb_t_";\r
+ contents += station->stringForKey(KEY_CHANNEL_ID)->cString();\r
+ contents += "\">";\r
+ contents += station->stringForKey(KEY_NAME)->cString();\r
+ contents += "</a></li>";\r
}\r
- now_tm.tm_hour = hour;\r
- now_tm.tm_min = min;\r
- now_tm.tm_sec = sec;\r
+ }\r
+ contents += "</ul>";\r
+\r
+ // 地デジ(番組データ)\r
+ for (uint i = 0; i < stations->count(); ++i)\r
+ {\r
+ Dictionary *station = (Dictionary *)stations->objectAtIndex(i);\r
+ if ((station != NULL) &&\r
+ (station->stringForKey(KEY_CHANNEL_ID) != NULL) &&\r
+ (station->stringForKey(KEY_NAME) != NULL))\r
+ {\r
+\r
+ contents += "<ul id=\"isdb_t_";\r
+ contents += station->stringForKey(KEY_CHANNEL_ID)->cString();\r
+ contents += "\" title=\"";\r
+ contents += station->stringForKey(KEY_NAME)->cString();\r
+ contents += "\">";\r
+\r
+ Array *programs = programsForServices(station->arrayForKey(KEY_SERVICES));\r
+ for (uint i = 0; i < programs->count(); ++i)\r
+ {\r
+ Dictionary *epg = (Dictionary *)programs->objectAtIndex(i);\r
+ if ((epg != NULL) &&\r
+ (epg->stringForKey(KEY_EPG_SERVICE_ID) != NULL) &&\r
+ (epg->stringForKey(KEY_EPG_EVENT_ID) != NULL) &&\r
+ (epg->stringForKey(KEY_EPG_DATE) != NULL) &&\r
+ (epg->stringForKey(KEY_EPG_START) != NULL) &&\r
+ (epg->stringForKey(KEY_EPG_END) != NULL))\r
+ {\r
+ contents += "<li>";\r
+ contents += "<a href=\"#epg_";\r
+ contents += epg->stringForKey(KEY_EPG_SERVICE_ID)->cString();\r
+ contents += "_";\r
+ contents += epg->stringForKey(KEY_EPG_EVENT_ID)->cString();\r
+ contents += "\">";\r
+ contents += epg->stringForKey(KEY_EPG_DATE)->substringFromIndex(5)->cString();\r
+ contents += " ";\r
+ contents += epg->stringForKey(KEY_EPG_START)->substringToIndex(5)->cString();\r
+ contents += "-";\r
+ contents += epg->stringForKey(KEY_EPG_END)->substringToIndex(5)->cString();\r
+ contents += "</a></li>";\r
+\r
+ epgs += epg_regist_form(epg);\r
+ }\r
+ }\r
+ contents += "</ul>";\r
+ }\r
+ }\r
+\r
+ // BS/CS(局名)\r
+ contents += "<ul id=\"isdb_s\" title=\"";\r
+ contents += LocalizedString(KEY_I18N_Digital_Terrestrial_Television_Broadcasting, NULL)->cString();\r
+ contents += "\">";\r
+ stations = stationInfos(Tuner::ISDB_S);\r
+ for (uint i = 0; i < stations->count(); ++i)\r
+ {\r
+ Dictionary *station = (Dictionary *)stations->objectAtIndex(i);\r
+ if ((station != NULL) &&\r
+ (station->stringForKey(KEY_CHANNEL_ID) != NULL) &&\r
+ (station->stringForKey(KEY_NAME) != NULL))\r
+ {\r
+ contents += "<li>";\r
+ contents += "<a href=\"#isdb_s_";\r
+ contents += station->stringForKey(KEY_CHANNEL_ID)->cString();\r
+ contents += "\">";\r
+ contents += station->stringForKey(KEY_NAME)->cString();\r
+ contents += "</a></li>";\r
+ }\r
+ }\r
+ contents += "</ul>";\r
+\r
+ // BS/CS(番組データ)\r
+ for (uint i = 0; i < stations->count(); ++i)\r
+ {\r
+ Dictionary *station = (Dictionary *)stations->objectAtIndex(i);\r
+ if ((station != NULL) &&\r
+ (station->stringForKey(KEY_CHANNEL_ID) != NULL) &&\r
+ (station->stringForKey(KEY_NAME) != NULL))\r
+ {\r
+ contents += "<ul id=\"isdb_s_";\r
+ contents += station->stringForKey(KEY_CHANNEL_ID)->cString();\r
+ contents += "\" title=\"";\r
+ contents += station->stringForKey(KEY_NAME)->cString();\r
+ contents += "\">";\r
+\r
+ Array *programs = programsForServices(station->arrayForKey(KEY_SERVICES));\r
+ for (uint i = 0; i < programs->count(); ++i)\r
+ {\r
+ Dictionary *epg = (Dictionary *)programs->objectAtIndex(i);\r
+ if ((epg != NULL) &&\r
+ (epg->stringForKey(KEY_EPG_SERVICE_ID) != NULL) &&\r
+ (epg->stringForKey(KEY_EPG_EVENT_ID) != NULL) &&\r
+ (epg->stringForKey(KEY_EPG_DATE) != NULL) &&\r
+ (epg->stringForKey(KEY_EPG_START) != NULL) &&\r
+ (epg->stringForKey(KEY_EPG_END) != NULL))\r
+ {\r
+ contents += "<li>";\r
+ contents += "<a href=\"#epg_";\r
+ contents += epg->stringForKey(KEY_EPG_SERVICE_ID)->cString();\r
+ contents += "_";\r
+ contents += epg->stringForKey(KEY_EPG_EVENT_ID)->cString();\r
+ contents += "\">";\r
+ contents += epg->stringForKey(KEY_EPG_DATE)->substringFromIndex(5)->cString();\r
+ contents += " ";\r
+ contents += epg->stringForKey(KEY_EPG_START)->substringToIndex(5)->cString();\r
+ contents += "-";\r
+ contents += epg->stringForKey(KEY_EPG_END)->substringToIndex(5)->cString();\r
+ contents += "</a></li>";\r
+\r
+ epgs += epg_regist_form(epg);\r
+ }\r
+ }\r
+ contents += "</ul>";\r
+ }\r
+ }\r
+\r
+ contents += epgs;\r
+ html = html->stringByReplacingOccurrencesOfString("%%CONTENTS%%", contents.c_str());\r
+ if (html != NULL)\r
+ {\r
+ result = responseWithHTML(request, html);\r
+ }\r
+\r
+ break;\r
+ } \r
+ return result;\r
+}\r
+\r
+HTTPResponse *Controller::responseForReservation(HTTPRequest *request, SOCKADDR_IN *client)\r
+{\r
+ DebugLog2("Controller::responseForReservation() start.");\r
+\r
+ // lock\r
+ RaymLock(this);\r
+\r
+ HTTPResponse *result = NULL;\r
+ while ((request != NULL) && (client != NULL))\r
+ {\r
+ // create array of reservations sorted by seq_id.\r
+ Array *array = Array::arrayWithCapacity(0);\r
+ for (int i = 0; i < _tunerCount; ++i)\r
+ {\r
+ Array *tmp = _reservations->arrayForKey(_tuners[i]->name());\r
+ if (tmp == NULL)\r
+ {\r
+ continue;\r
+ }\r
+ for (uint idx = 0; idx < tmp->count(); ++idx)\r
+ {\r
+ Dictionary *epg = (Dictionary *)tmp->objectAtIndex(idx);\r
+ if (epg == NULL)\r
+ {\r
+ continue;\r
+ }\r
+\r
+ time_t epg_start;\r
+ time_t tmp_end;\r
+ getTimeWithEPG(epg, &epg_start, &tmp_end);\r
+\r
+ bool inserted = false;\r
+ for (uint j = 0; j < array->count(); ++j)\r
+ {\r
+ Dictionary *epg2 = (Dictionary *)array->objectAtIndex(j);\r
+ if (epg2 == NULL)\r
+ {\r
+ continue;\r
+ }\r
+\r
+ time_t epg2_start;\r
+ getTimeWithEPG(epg2, &epg2_start, &tmp_end);\r
+\r
+ if (epg_start <= epg2_start)\r
+ {\r
+ array->insertObject(epg, j);\r
+ inserted = true;\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (!inserted)\r
+ {\r
+ array->addObject(epg);\r
+ }\r
+ }\r
+ }\r
+\r
+ String *path = _httpd->rootPath();\r
+ if (path == NULL)\r
+ {\r
+ break;\r
+ }\r
+\r
+ path = path->stringByAppendingPathComponent("template2.html");\r
+ if (path == NULL)\r
+ {\r
+ break;\r
+ }\r
+\r
+ String *html = String::stringWithContentsOfFile(path->cString(), UTF8StringEncoding);\r
+ if (html == NULL)\r
+ {\r
+ break;\r
+ }\r
+\r
+ html = html->stringByReplacingOccurrencesOfString("%%TITLE%%", LocalizedString(KEY_I18N_Reservation, NULL));\r
+ if (html == NULL)\r
+ {\r
+ break;\r
+ }\r
+\r
+ std::string contents;\r
+ std::string reservations;\r
+ contents += "<ul id=\"home\" title=\"";\r
+ contents += LocalizedString(KEY_I18N_Reservation, NULL)->cString();\r
+ contents += "\" selected=\"true\">";\r
+\r
+#if 0\r
+ contents += "<li><a href=\"#tuners\">";\r
+ contents += "<font color=\"blue\">[";\r
+ contents += LocalizedString(KEY_I18N_Tuner, NULL)->cString();\r
+ contents += "]</font>";\r
+ contents += "</a></li>";\r
+#endif\r
+\r
+ //\r
+ // "[新規予約]"\r
+ //\r
+ contents += "<li><a href=\"#new_reservation\">";\r
+ contents += "<font color=\"red\">[";\r
+ contents += LocalizedString(KEY_I18N_New_Reservation, NULL)->cString();\r
+ contents += "]</font>";\r
+ contents += "</a></li>";\r
+\r
+ //\r
+ // "[キーワード]"\r
+ //\r
+ contents += "<li><a href=\"#keywords\">";\r
+ contents += "<font color=\"blue\">[";\r
+ contents += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
+ contents += "]</font>";\r
+ contents += "</a></li>";\r
+\r
+ //\r
+ // 登録済み予約情報\r
+ //\r
+ for (uint i = 0; i < array->count(); ++i)\r
+ {\r
+ Dictionary *epg = (Dictionary *)array->objectAtIndex(i);\r
+ char seq_id[7];\r
+ sprintf_s(seq_id, "%06d", epg->integerForKey(KEY_EPG_RESV_ID) % 1000000);\r
+ contents += "<li>";\r
+ contents += "<a href=\"#seqid";\r
+ contents += seq_id;\r
+ contents += "\">";\r
+ String *title = epg->stringForKey(KEY_EPG_TITLE);\r
+ if (title != NULL)\r
+ {\r
+ contents += title->cString();\r
+ }\r
+ else\r
+ {\r
+ time_t start;\r
+ time_t end;\r
+ Controller::getTimeWithEPG(epg, &start, &end);\r
+ TM tm;\r
+ if (localtime_s(&tm, &start) == 0)\r
+ {\r
+ char date_time[24];\r
+ sprintf_s(date_time, "%04d/%02d/%02d %02d:%02d:%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);\r
+ contents += date_time;\r
+ }\r
+ else\r
+ {\r
+ contents += seq_id;\r
+ }\r
+ }\r
+ contents += "</a></li>";\r
+\r
+ // reservations\r
+ reservations += "<div id=\"";\r
+ reservations += "seqid";\r
+ reservations += seq_id;\r
+ reservations += "\" title=\"";\r
+ reservations += "xxxxxx";\r
+ reservations += "\" class=\"panel\">";\r
+\r
+ //reservations += "<h2>Status</h2>";\r
+ reservations += "<fieldset>";\r
+\r
+ //\r
+ reservations += "<div class=\"row\">";\r
+ reservations += "<label>Date</label>";\r
+ reservations += "<span>";\r
+ String *tmp = epg->stringForKey(KEY_EPG_DATE);\r
+ reservations += tmp->cString();\r
+ reservations += "</span>";\r
+ reservations += "</div>";\r
+\r
+ reservations += "<div class=\"row\">";\r
+ reservations += "<label>Start</label>";\r
+ reservations += "<span>";\r
+ tmp = epg->stringForKey(KEY_EPG_START);\r
+ reservations += tmp->cString();\r
+ reservations += "</span>";\r
+ reservations += "</div>";\r
+\r
+ reservations += "<div class=\"row\">";\r
+ reservations += "<label>End</label>";\r
+ reservations += "<span>";\r
+ tmp = epg->stringForKey(KEY_EPG_END);\r
+ reservations += tmp->cString();\r
+ reservations += "</span>";\r
+ reservations += "</div>";\r
+\r
+ reservations += "</fieldset>";\r
+\r
+ reservations += "<a class=\"whiteButton\" type=\"submit\" href=\"/cancel.cgi?";\r
+ reservations += "resv_id=";\r
+ reservations += seq_id;\r
+ reservations += "\" onclick='return confirm(\"To cancel. Is it ok?\");'>";\r
+ reservations += LocalizedString(KEY_I18N_Cancel, NULL)->cString();\r
+ reservations += "</a>";\r
+\r
+ reservations += "</div>";\r
+ }\r
+\r
+ contents += "</ul>";\r
+ contents += reservations;\r
+\r
+ //\r
+ // 新規予約:ここから\r
+ //\r
+\r
+ // 予約時間の初期値作成\r
+ TM tm1, tm2;\r
+ time_t time1 = time(NULL);\r
+ time1 = (time1 + 15 * 60) / (15 * 60);\r
+ time1 = time1 * 15 * 60;\r
+ time_t time2 = time1 + 15 * 60;\r
+ localtime_s(&tm1, &time1);\r
+ localtime_s(&tm2, &time2);\r
+ \r
+ contents += "<form name=\"reservation\" id=\"new_reservation\" class=\"panel\" method=\"GET\" action=\"regist.cgi\" target=\"_self\">";\r
+ contents += "<fieldset>";\r
+ contents += "<div class=\"row\">";\r
+ contents += "<label>";\r
+ contents += LocalizedString(KEY_I18N_Station_Name, NULL)->cString();\r
+ contents += " : </label>";\r
+ contents += "<span>";\r
+ contents += "<select name=\"service_id\" size=\"1\">";\r
+\r
+ // 局情報の収集\r
+ Array *stationInfos = Controller::stationInfos(Tuner::ISDB_T);\r
+ if (stationInfos != NULL)\r
+ {\r
+ stationInfos->addObjectsFromArray(Controller::stationInfos(Tuner::ISDB_S));\r
+ }\r
+ else\r
+ {\r
+ stationInfos = Controller::stationInfos(Tuner::ISDB_S);\r
+ }\r
+ for (uint i = 0; (stationInfos != NULL) && (i < stationInfos->count()); ++i)\r
+ {\r
+ Array *services = ((Dictionary *)stationInfos->objectAtIndex(i))->arrayForKey(KEY_SERVICES);\r
+\r
+// とりえあず、現状は最初のサービスのみ表示\r
+// for (uint j = 0; (services != NULL) && (j < services->count()); ++j)\r
+ for (uint j = 0; (services != NULL) && (j < 1); ++j)\r
+ {\r
+ Dictionary *service = (Dictionary *)services->objectAtIndex(j);\r
+\r
+ // テレビ放送かチェック\r
+ if ((service->stringForKey(KEY_SERVICE_TYPE) != NULL) && service->stringForKey(KEY_SERVICE_TYPE)->isEqualToString("1"))\r
+ {\r
+ String *name = service->stringForKey(KEY_NAME);\r
+ String *sid = service->stringForKey(KEY_SERVICE_ID);\r
+ if ((name != NULL) && (sid != NULL))\r
+ {\r
+ String *tmp = String::stringWithFormat("<option value=\"%s\">%s[%s]</option>", sid->cString(), name->cString(), sid->cString());\r
+ contents += tmp->cString();\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ contents += "</select>";\r
+ contents += "</span>";\r
+ contents += "</div>";\r
+ contents += "<div class=\"row\">";\r
+ contents += "<label>";\r
+ contents += LocalizedString(KEY_I18N_Date, NULL)->cString();\r
+ contents += " : </label>";\r
+ contents += "<span>";\r
+ contents += "<select name=\"year\" size=\"1\">";\r
+ for (int year = tm1.tm_year + 1900; year < tm1.tm_year + 1900 + 3; ++year)\r
+ {\r
+ char year_buf[64];\r
+ sprintf_s(year_buf, "<option value=\"%d\">%d</option>", year, year);\r
+ contents += year_buf;\r
+ }\r
+ contents += "</select>";\r
+ contents += "<select name=\"month\" size=\"1\">";\r
+ for (int month = 1; month < 13; ++month)\r
+ {\r
+ char month_buf[64];\r
+ if (month == tm1.tm_mon + 1)\r
+ {\r
+ sprintf_s(month_buf, "<option value=\"%d\" selected>%d</option>", month, month);\r
+ }\r
+ else\r
+ {\r
+ sprintf_s(month_buf, "<option value=\"%d\">%d</option>", month, month);\r
+ }\r
+ contents += month_buf;\r
+ }\r
+ contents += "</select>";\r
+ contents += "<select name=\"day\" size=\"1\">";\r
+ for (int day = 1; day < 32; ++day)\r
+ {\r
+ char day_buf[64];\r
+ if (day == tm1.tm_mday)\r
+ {\r
+ sprintf_s(day_buf, "<option value=\"%d\" selected>%d</option>", day, day);\r
+ }\r
+ else\r
+ {\r
+ sprintf_s(day_buf, "<option value=\"%d\">%d</option>", day, day);\r
+ }\r
+ contents += day_buf;\r
+ }\r
+ contents += "</select>";\r
+ contents += "</span>";\r
+ contents += "</div>";\r
+ contents += "<div class=\"row\">";\r
+ contents += "<label>";\r
+ contents += LocalizedString(KEY_I18N_Start_Time, NULL)->cString();\r
+ contents += " : </label>";\r
+ contents += "<span>";\r
+ contents += "<select name=\"start_hour\" size=\"1\">";\r
+ for (int sh = 0; sh < 24; ++sh)\r
+ {\r
+ char sh_buf[64];\r
+ if (sh == tm1.tm_hour)\r
+ {\r
+ sprintf_s(sh_buf, "<option value=\"%02d\" selected>%02d</option>", sh, sh);\r
+ }\r
+ else\r
+ {\r
+ sprintf_s(sh_buf, "<option value=\"%02d\">%02d</option>", sh, sh);\r
+ }\r
+ contents += sh_buf;\r
+ }\r
+ contents += "</select>";\r
+ contents += "<select name=\"start_min\" size=\"1\">";\r
+ for (int sm = 0; sm < 60; ++sm)\r
+ {\r
+ char sm_buf[64];\r
+ if (sm == tm1.tm_min)\r
+ {\r
+ sprintf_s(sm_buf, "<option value=\"%02d\" selected>%02d</option>", sm, sm);\r
+ }\r
+ else\r
+ {\r
+ sprintf_s(sm_buf, "<option value=\"%02d\">%02d</option>", sm, sm);\r
+ }\r
+ contents += sm_buf;\r
+ }\r
+ contents += "</select>";\r
+ contents += "</span>";\r
+ contents += "</div>";\r
+ contents += "<div class=\"row\">";\r
+ contents += "<label>";\r
+ contents += LocalizedString(KEY_I18N_End_Time, NULL)->cString();\r
+ contents += " : </label>";\r
+ contents += "<span>";\r
+ contents += "<select name=\"end_hour\" size=\"1\">";\r
+ for (int eh = 0; eh < 24; ++eh)\r
+ {\r
+ char eh_buf[64];\r
+ if (eh == tm2.tm_hour)\r
+ {\r
+ sprintf_s(eh_buf, "<option value=\"%02d\" selected>%02d</option>", eh, eh);\r
+ }\r
+ else\r
+ {\r
+ sprintf_s(eh_buf, "<option value=\"%02d\">%02d</option>", eh, eh);\r
+ }\r
+ contents += eh_buf;\r
+ }\r
+ contents += "</select>";\r
+ contents += "<select name=\"end_min\" size=\"1\">";\r
+ for (int em = 0; em < 60; ++em)\r
+ {\r
+ char em_buf[64];\r
+ if (em == tm2.tm_min)\r
+ {\r
+ sprintf_s(em_buf, "<option value=\"%02d\" selected>%02d</option>", em, em);\r
+ }\r
+ else\r
+ {\r
+ sprintf_s(em_buf, "<option value=\"%02d\">%02d</option>", em, em);\r
+ }\r
+ contents += em_buf;\r
+ }\r
+ contents += "</select>";\r
+ contents += "</span>";\r
+ contents += "</div>";\r
+ contents += "<div class=\"row\">";\r
+ contents += "<label>";\r
+ contents += LocalizedString(KEY_I18N_Repeat, NULL)->cString();\r
+ contents += " : </label>";\r
+ contents += "<span>";\r
+ contents += "<select name=\"repeat\" size=\"1\">";\r
+ contents += "<option value=\"off\" selected>";\r
+ contents += LocalizedString(KEY_I18N_Repeat_off, NULL)->cString();\r
+ contents += "</option>";\r
+ contents += "<option value=\"everyday\">";\r
+ contents += LocalizedString(KEY_I18N_Repeat_everyday, NULL)->cString();\r
+ contents += "</option>";\r
+ contents += "<option value=\"weekly\">";\r
+ contents += LocalizedString(KEY_I18N_Repeat_weekly, NULL)->cString();\r
+ contents += "</option>";\r
+ contents += "<option value=\"weekday\">";\r
+ contents += LocalizedString(KEY_I18N_Repeat_weekday, NULL)->cString();\r
+ contents += "</option>";\r
+ contents += "</select>";\r
+ contents += "</span>";\r
+ contents += "</div>";\r
+ contents += "</fieldset>";\r
+\r
+ contents += "<input type=\"submit\" class=\"whiteButton\" value=\"";\r
+ contents += LocalizedString(KEY_I18N_Registration, NULL)->cString();\r
+ contents += "\">";\r
+ contents += "</form>";\r
+\r
+ //\r
+ // 新規予約:ここまで\r
+ //\r
+\r
+\r
+ //\r
+ // チューナ:ここから\r
+ //\r
+ contents += "<ul id=\"tuners\" title=\"";\r
+ contents += LocalizedString(KEY_I18N_Tuner, NULL)->cString();\r
+ contents += "\">";\r
+\r
+ std::string controls;\r
+ Dictionary *tunerInfos = _props->dictionaryForKey(KEY_TUNERS);\r
+ if (tunerInfos != NULL)\r
+ {\r
+ for (int i = 0; i < _tunerCount; ++i)\r
+ {\r
+ Dictionary *tunerInfo = tunerInfos->dictionaryForKey(_tuners[i]->name());\r
+ if (tunerInfo != NULL)\r
+ {\r
+ if (tunerInfo->boolForKey(KEY_INITIALIZED) && tunerInfo->boolForKey(KEY_ENABLED))\r
+ {\r
+ char key[4];\r
+ sprintf_s(key, "%03d", i);\r
+\r
+ // tuner list\r
+ contents += "<li>";\r
+ contents += "<a href=\"#tuner";\r
+ contents += key;\r
+ contents += "\">";\r
+ contents += key;\r
+ contents += ": ";\r
+ Dictionary *dict = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
+ if (dict != NULL)\r
+ {\r
+ char chkey[4];\r
+ sprintf_s(chkey, "%03d", tunerInfo->integerForKey(KEY_CHANNEL));\r
+ dict = dict->dictionaryForKey(chkey);\r
+ String *name = dict->stringForKey(KEY_NAME);\r
+ if (name != NULL)\r
+ {\r
+ contents += name->cString();\r
+ }\r
+ else\r
+ {\r
+ dict = NULL;\r
+ }\r
+ }\r
+ if (dict == NULL)\r
+ {\r
+ contents += _tuners[i]->name();\r
+ }\r
+ contents += "</a></li>";\r
+\r
+ // controls\r
+ controls += "<form name=\"control\" id=\"tuner";\r
+ controls += key;\r
+ controls += "\" class=\"panel\" method=\"GET\" action=\"/";\r
+ controls += key;\r
+ controls += "/recording=on\" target=\"_self\">";\r
+\r
+ // 白い枠:ここから\r
+ controls += "<fieldset>";\r
+\r
+ // 局指定:ここから\r
+ controls += "<div class=\"row\">";\r
+ controls += "<label>";\r
+ controls += LocalizedString(KEY_I18N_Station_Name, NULL)->cString();\r
+ controls += " : </label>";\r
+ controls += "<span>";\r
+ controls += "<select name=\"channel\" size=\"1\">";\r
+ Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
+ if (channels != NULL)\r
+ {\r
+ for (int ch = 0; ch <= Tuner::MAX_CHANNELS_ISDB_T; ++ch)\r
+ {\r
+ char chkey[4];\r
+ sprintf_s(chkey, "%03d", ch);\r
+ dict = channels->dictionaryForKey(chkey);\r
+ if (dict == NULL)\r
+ {\r
+ break;\r
+ }\r
+ Array *services = (Array *)dict->objectForKey(KEY_SERVICES);\r
+ if (services != NULL)\r
+ {\r
+ for (uint s = 0; s < services->count(); ++s)\r
+ {\r
+ Dictionary *service = (Dictionary *)services->objectAtIndex(s);\r
+ if (service != NULL)\r
+ {\r
+ char sid[8];\r
+ sprintf_s(sid, "%d", service->integerForKey(KEY_SERVICE_ID));\r
+ controls += "<option value=\"";\r
+ controls += chkey;\r
+ controls += "\"";\r
+ // selected\r
+ controls += ">";\r
+ String *name = service->stringForKey(KEY_NAME);\r
+ if (name != NULL)\r
+ {\r
+ controls += name->cString();\r
+ }\r
+ controls += "[";\r
+ controls += sid;\r
+ controls += "]</option>";\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ controls += "</select>";\r
+ controls += "</span>";\r
+ controls += "</div>";\r
+ // 局指定:ここまで\r
+\r
+ // 時間指定:ここから\r
+ controls += "<div class=\"row\">";\r
+ controls += "<label>";\r
+ controls += LocalizedString(KEY_I18N_Time, NULL)->cString();\r
+ controls += " : </label>";\r
+ controls += "<span>";\r
+ controls += "<select name=\"hour\" size=\"1\">";\r
+ for (int sh = 0; sh < 24; ++sh)\r
+ {\r
+ char sh_buf[64];\r
+ if (sh == 1)\r
+ {\r
+ sprintf_s(sh_buf, "<option value=\"%02d\" selected>%02d</option>", sh, sh);\r
+ }\r
+ else\r
+ {\r
+ sprintf_s(sh_buf, "<option value=\"%02d\">%02d</option>", sh, sh);\r
+ }\r
+ controls += sh_buf;\r
+ }\r
+ controls += "</select>";\r
+ controls += "<select name=\"min\" size=\"1\">";\r
+ for (int sm = 0; sm < 60; ++sm)\r
+ {\r
+ char sm_buf[64];\r
+ if (sm == 0)\r
+ {\r
+ sprintf_s(sm_buf, "<option value=\"%02d\" selected>%02d</option>", sm, sm);\r
+ }\r
+ else\r
+ {\r
+ sprintf_s(sm_buf, "<option value=\"%02d\">%02d</option>", sm, sm);\r
+ }\r
+ controls += sm_buf;\r
+ }\r
+ controls += "</select>";\r
+ controls += "</span>";\r
+ controls += "</div>";\r
+ // 時間指定:ここまで\r
+\r
+ controls += "</fieldset>";\r
+ // 白い枠:ここまで\r
+\r
+ controls += "<input type=\"submit\" class=\"whiteButton\" value=\"";\r
+ controls += LocalizedString(KEY_I18N_Registration, NULL)->cString();\r
+ controls += "\">";\r
+ controls += "</form>";\r
+ }\r
+ }\r
+ }\r
+ }\r
+ contents += "</ul>";\r
+ contents += controls;\r
+\r
+ //\r
+ // チューナ:ここまで\r
+ //\r
+\r
+ //\r
+ // キーワード:ここから\r
+ //\r
+ contents += "<ul id=\"keywords\" title=\"";\r
+ contents += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
+ contents += "\">";\r
+\r
+ contents += "<li><a href=\"#dialogForm\">";\r
+ contents += "<font color=\"red\">[";\r
+// contents += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
+ contents += LocalizedString(KEY_I18N_Add, NULL)->cString();\r
+ contents += "]</font>";\r
+ contents += "</a></li>";\r
+\r
+ // 登録済みキーワード\r
+ Dictionary *keywords_info = _reservations->dictionaryForKey(KEY_EPG_KEYWORDS);\r
+ if (keywords_info != NULL)\r
+ {\r
+ controls = "";\r
+ Array *keys = keywords_info->allKeys();\r
+ if (keys != NULL)\r
+ {\r
+ for (uint i = 0; i < keys->count(); ++i)\r
+ {\r
+ Dictionary *kwd_inf = keywords_info->dictionaryForKey((String *)keys->objectAtIndex(i));\r
+ if (kwd_inf == NULL)\r
+ {\r
+ continue;\r
+ }\r
+\r
+ char tmp[32];\r
+ sprintf_s(tmp, "%d", i);\r
+ contents += "<li><a href=\"#keywords_";\r
+ contents += tmp;\r
+ contents += "\">";\r
+ contents += ((String *)keys->objectAtIndex(i))->cString();\r
+ contents += "</a></li>";\r
+\r
+ controls += "<form name=\"filter\" id=\"keywords_";\r
+ controls += tmp;\r
+ controls += "\" class=\"panel\" method=\"GET\" action=\"mod_keywords.cgi\" target=\"_self\">";\r
+ controls += "<input type=\"hidden\" name=\"keywords\" value=\"";\r
+ controls += ((String *)keys->objectAtIndex(i))->cString();\r
+ controls += "\">";\r
+ controls += "<fieldset>";\r
+\r
+ controls += "<div class=\"row\">";\r
+ controls += "<label>";\r
+ controls += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
+ controls += " : </label>";\r
+ controls += "<span>";\r
+ controls += ((String *)keys->objectAtIndex(i))->cString();\r
+ controls += "</span>";\r
+ controls += "</div>";\r
+\r
+ controls += "<div class=\"row\">";\r
+ controls += "<label>";\r
+ controls += LocalizedString(KEY_I18N_Station_Name, NULL)->cString();\r
+ controls += " : </label>";\r
+ controls += "<span>";\r
+ controls += "<select name=\"service_id\" size=\"1\">";\r
+ controls += "<option value=\"-\">----</option>";\r
+\r
+ String *service_id = kwd_inf->stringForKey(KEY_EPG_SERVICE_ID);\r
+\r
+ // 局情報の収集\r
+ Array *stationInfos = Controller::stationInfos(Tuner::ISDB_T);\r
+ if (stationInfos != NULL)\r
+ {\r
+ stationInfos->addObjectsFromArray(Controller::stationInfos(Tuner::ISDB_S));\r
+ }\r
+ else\r
+ {\r
+ stationInfos = Controller::stationInfos(Tuner::ISDB_S);\r
+ }\r
+ for (uint i = 0; (stationInfos != NULL) && (i < stationInfos->count()); ++i)\r
+ {\r
+ Array *services = ((Dictionary *)stationInfos->objectAtIndex(i))->arrayForKey(KEY_SERVICES);\r
+\r
+ for (uint j = 0; (services != NULL) && (j < 1); ++j)\r
+ {\r
+ Dictionary *service = (Dictionary *)services->objectAtIndex(j);\r
+\r
+ // テレビ放送かチェック\r
+ if ((service->stringForKey(KEY_SERVICE_TYPE) != NULL) && service->stringForKey(KEY_SERVICE_TYPE)->isEqualToString("1"))\r
+ {\r
+ String *name = service->stringForKey(KEY_NAME);\r
+ String *sid = service->stringForKey(KEY_SERVICE_ID);\r
+ if ((name != NULL) && (sid != NULL))\r
+ {\r
+ if ((service_id != NULL) && (service_id->isEqualToString(sid)))\r
+ {\r
+ String *tmp = String::stringWithFormat("<option value=\"%s\" selected>%s[%s]</option>", sid->cString(), name->cString(), sid->cString());\r
+ controls += tmp->cString();\r
+ }\r
+ else\r
+ {\r
+ String *tmp = String::stringWithFormat("<option value=\"%s\">%s[%s]</option>", sid->cString(), name->cString(), sid->cString());\r
+ controls += tmp->cString();\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ controls += "</select>";\r
+ controls += "</span>";\r
+ controls += "</div>";\r
+\r
+ controls += "<div class=\"row\">";\r
+ controls += "<label>";\r
+ controls += LocalizedString(KEY_I18N_Start_Time, NULL)->cString();\r
+ controls += " : </label>";\r
+ controls += "<span>";\r
+ controls += "<select name=\"start_hour\" size=\"1\">";\r
+ controls += "<option value=\"-\">--</option>";\r
+ String *sh_str = kwd_inf->stringForKey(KEY_EPG_START);\r
+ for (int sh = 0; sh < 24; ++sh)\r
+ {\r
+ char sh_buf[64];\r
+ if ((sh_str != NULL) && (sh_str->length() == 5) && (sh_str->substringToIndex(2)->intValue() == sh))\r
+ {\r
+ sprintf_s(sh_buf, "<option value=\"%02d\" selected>%02d</option>", sh, sh);\r
+ }\r
+ else\r
+ {\r
+ sprintf_s(sh_buf, "<option value=\"%02d\">%02d</option>", sh, sh);\r
+ }\r
+ controls += sh_buf;\r
+ }\r
+ controls += "</select>";\r
+ controls += "<select name=\"start_min\" size=\"1\">";\r
+ controls += "<option value=\"-\">--</option>";\r
+ for (int sm = 0; sm < 60; ++sm)\r
+ {\r
+ char sm_buf[64];\r
+ if ((sh_str != NULL) && (sh_str->length() == 5) && (sh_str->substringFromIndex(3)->intValue() == sm))\r
+ {\r
+ sprintf_s(sm_buf, "<option value=\"%02d\" selected>%02d</option>", sm, sm);\r
+ }\r
+ else\r
+ {\r
+ sprintf_s(sm_buf, "<option value=\"%02d\">%02d</option>", sm, sm);\r
+ }\r
+ controls += sm_buf;\r
+ }\r
+ controls += "</select>";\r
+ controls += "</span>";\r
+ controls += "</div>";\r
+\r
+ controls += "</fieldset>";\r
+\r
+ controls += "<input type=\"submit\" class=\"whiteButton\" name=\"req_mod\" value=\"";\r
+ controls += LocalizedString(KEY_I18N_Registration, NULL)->cString();\r
+ controls += "\">";\r
+\r
+ controls += "<input type=\"submit\" class=\"redButton\" name=\"req_del\" value=\"";\r
+ controls += LocalizedString(KEY_I18N_Delete, NULL)->cString();\r
+// controls += "\">";\r
+ controls += "\" onclick='return confirm(\"To delete. Is it ok?\");'>";\r
+\r
+ controls += "</form>";\r
+ }\r
+ }\r
+ }\r
+\r
+ contents += "</ul>";\r
+ contents += controls;\r
+\r
+ controls = "<form id=\"dialogForm\" title=\"";\r
+ controls += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
+ controls += LocalizedString(KEY_I18N_Add, NULL)->cString();\r
+ controls += "\" class=\"dialog\" target=\"_self\" action=\"add_keywords.cgi\" method=\"GET\">";\r
+ controls += "<fieldset>";\r
+ controls += "<h1>";\r
+ controls += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
+ controls += LocalizedString(KEY_I18N_Add, NULL)->cString();\r
+ controls += "</h1>";\r
+ controls += "<a class=\"button leftButton\" type=\"cancel\">Cancel</a>";\r
+ controls += "<a class=\"button blueButton\" type=\"submit\">Submit</a>";\r
+ controls += "<label>Parm1:</label>";\r
+ controls += "<input type=\"text\" name=\"keywords\" value=\"";\r
+// controls += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
+ controls += "\"/>";\r
+ controls += "</fieldset>";\r
+ controls += "<div class=\"spinner\"></div>";\r
+ controls += "</form>";\r
+\r
+ contents += controls;\r
+ //\r
+ // キーワード:ここまで\r
+ //\r
+\r
+ html = html->stringByReplacingOccurrencesOfString("%%CONTENTS%%", contents.c_str());\r
+ if (html != NULL)\r
+ {\r
+ result = responseWithHTML(request, html);\r
+ }\r
+\r
+ break;\r
+ }\r
+\r
+ // unlock\r
+ RaymUnlock(this);\r
+\r
+ return result;\r
+}\r
+\r
+HTTPResponse *Controller::responseForStatus(HTTPRequest *request, SOCKADDR_IN *client)\r
+{\r
+ HTTPResponse *result = NULL;\r
+ if ((request != NULL) && (client != NULL))\r
+ {\r
+ String *path = _httpd->rootPath()->stringByAppendingPathComponent("template2.html");\r
+ String *html = String::stringWithContentsOfFile(path->cString(), UTF8StringEncoding);\r
+ if (html != NULL)\r
+ {\r
+ html = html->stringByReplacingOccurrencesOfString("%%TITLE%%", LocalizedString(KEY_I18N_Tuner_Status, NULL));\r
+ std::string contents;\r
+ std::string status;\r
+ contents += "<ul id=\"home\" title=\"";\r
+ contents += LocalizedString(KEY_I18N_Tuner_Status, NULL)->cString();\r
+ contents += "\" selected=\"true\">";\r
+ DebugLog2("_tunerCount = %d", _tunerCount);\r
+ for (int i = 0; i < _tunerCount; ++i)\r
+ {\r
+ char key[4];\r
+ sprintf_s(key, sizeof(key), "%03d", i);\r
+\r
+ if (isTunerInitialized(i))\r
+ {\r
+ contents += "<li>";\r
+ contents += "<a href=\"#tuner";\r
+ contents += key;\r
+ contents += "\">";\r
+ contents += key;\r
+ contents += ": ";\r
+ contents += _tuners[i]->name();\r
+ contents += "</a></li>";\r
+\r
+ //\r
+ status += "<div id=\"";\r
+ status += "tuner";\r
+ status += key;\r
+ status += "\" title=\"";\r
+ status += _tuners[i]->name();\r
+ status += "\" class=\"panel\">";\r
+\r
+ status += "<h2>Status</h2>";\r
+ status += "<fieldset>";\r
+\r
+ // Tuner Type\r
+ status += "<div class=\"row\">";\r
+ status += "<label>Type</label>";\r
+ status += "<span>";\r
+ switch (_tuners[i]->type())\r
+ {\r
+ case Tuner::ISDB_S:\r
+ status += "ISDB-S";\r
+ break;\r
+ case Tuner::ISDB_T:\r
+ status += "ISDB-T";\r
+ break;\r
+ case Tuner::TYPE_NA:\r
+ default:\r
+ status += "N/A";\r
+ break;\r
+ }\r
+ status += "</span>";\r
+ status += "</div>";\r
+\r
+ // LnbPower\r
+ status += "<div class=\"row\">";\r
+ status += "<label>LnbPower</label>";\r
+ status += "<span>";\r
+ switch (_tuners[i]->lnbPower())\r
+ {\r
+ case Tuner::LNB_POWER_11V:\r
+ status += "11V";\r
+ break;\r
+ case Tuner::LNB_POWER_15V:\r
+ status += "15V";\r
+ break;\r
+ case Tuner::LNB_POWER_OFF:\r
+ default:\r
+ status += "OFF";\r
+ break;\r
+ }\r
+ status += "</span>";\r
+ status += "</div>";\r
+\r
+ // Channel\r
+ char tmpstr[32];\r
+ status += "<div class=\"row\">";\r
+ status += "<label>Channel</label>";\r
+ status += "<span>";\r
+ sprintf_s(tmpstr, sizeof(tmpstr), "%03d", _tuners[i]->channel());\r
+ status += tmpstr;\r
+ status += "</span>";\r
+ status += "</div>";\r
+\r
+ // C/N[dB] AGC xxx/255\r
+ uint32_t cn100 = 0;\r
+ uint32_t agc = 0;\r
+ uint32_t maxAgc = 0;\r
+ _tuners[i]->getCnAgc(&cn100, &agc, &maxAgc);\r
+\r
+ sprintf_s(tmpstr, sizeof(tmpstr), "%d.%02d[dB]", cn100 / 100, cn100 % 100);\r
+\r
+ status += "<div class=\"row\">";\r
+ status += "<label>C/N</label>";\r
+ status += "<span>";\r
+ status += tmpstr;\r
+ status += "</span>";\r
+ status += "</div>";\r
+\r
+ sprintf_s(tmpstr, sizeof(tmpstr), "%03d/%03d", agc, maxAgc);\r
+\r
+ status += "<div class=\"row\">";\r
+ status += "<label>AGC</label>";\r
+ status += "<span>";\r
+ status += tmpstr;\r
+ status += "</span>";\r
+ status += "</div>";\r
+\r
+ status += "</fieldset>";\r
+ status += "</div>";\r
+\r
+ }\r
+ else\r
+ {\r
+ contents += "<li>";\r
+ contents += _tuners[i]->name();\r
+ contents += "[uninitialized]";\r
+ contents += "</li>";\r
+ }\r
+ }\r
+ contents += "</ul>";\r
+ contents += status;\r
+ html = html->stringByReplacingOccurrencesOfString("%%CONTENTS%%", contents.c_str());\r
+ result = responseWithHTML(request, html);\r
+ }\r
+ else\r
+ {\r
+ DebugLog2("responseForStatus() html is null\n");\r
+ }\r
+ }\r
+ return result;\r
+}\r
+\r
+HTTPResponse *Controller::responseForRegistCGI(HTTPRequest *request, SOCKADDR_IN *client)\r
+{\r
+ DebugLog2("Controller::responseForRegistCGI()");\r
+\r
+ HTTPResponse *result = NULL;\r
+\r
+ // CGIリクエストとして解析\r
+ Dictionary *cgi = request->parseAsCGI();\r
+ if (cgi != NULL)\r
+ {\r
+ // CGIパスが一致しているか\r
+ if ((cgi->stringForKey(HTTPRequest::KEY_CGI) != NULL) && (cgi->stringForKey(HTTPRequest::KEY_CGI)->isEqualToString("/regist.cgi")))\r
+ {\r
+ // パラメータがあるか\r
+ Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
+ if (params != NULL)\r
+ {\r
+ // パラメータ数が2か\r
+ if (params->count() == 2)\r
+ {\r
+ // パラメータのチェック\r
+ String *service_id = NULL;\r
+ String *event_id = NULL;\r
+\r
+ for (uint i = 0; i < params->count(); ++i)\r
+ {\r
+ Dictionary *param = (Dictionary *)params->objectAtIndex(i);\r
+ String *value = param->stringForKey("service_id");\r
+ if ((value != NULL) && value->isMatch("^\\d+$"))\r
+ {\r
+ service_id = value;\r
+ }\r
+ value = param->stringForKey("event_id");\r
+ if ((value != NULL) && value->isMatch("^\\d+$"))\r
+ {\r
+ event_id = value;\r
+ }\r
+ }\r
+\r
+ // 有効なパラメータか\r
+ if ((service_id != NULL) && (event_id != NULL))\r
+ {\r
+ DebugLog2("valid request");\r
+\r
+ result = responseByResultAndReferer(request, reserve(service_id->intValue(), event_id->intValue()), URI_PROGRAMS_HTML);\r
+ }\r
+ }\r
+\r
+ // パラメータ数が9か\r
+ else if (params->count() == 9)\r
+ {\r
+ // パラメータのチェック\r
+ String *service_id = NULL;\r
+ String *year = NULL;\r
+ String *month = NULL;\r
+ String *day = NULL;\r
+ String *start_hour = NULL;\r
+ String *start_min = NULL;\r
+ String *end_hour = NULL;\r
+ String *end_min = NULL;\r
+ String *repeat = NULL;\r
+\r
+ struct {\r
+ const char *name;\r
+ String **variable;\r
+ const char *regex;\r
+ }\r
+ cgi[] =\r
+ {\r
+ {"service_id", &service_id, "^\\d+$"},\r
+ {"year", &year, "^\\d{4}$"},\r
+ {"month", &month, "^([1-9]|1[0-2])$"},\r
+ {"day", &day, "^([1-9]|[12][0-9]|3[01])$"},\r
+ {"start_hour", &start_hour, "^\\d{2}$"},\r
+ {"start_min", &start_min, "^\\d{2}$"},\r
+ {"end_hour", &end_hour, "^\\d{2}$"},\r
+ {"end_min", &end_min, "^\\d{2}$"},\r
+ {"repeat", &repeat, "^(off|everyday|weekly|weekday)$"},\r
+ {NULL, NULL, NULL}\r
+ };\r
+\r
+ for (uint i = 0; cgi[i].name != NULL; ++i)\r
+ {\r
+ for (uint j = 0; j < params->count(); ++j)\r
+ {\r
+ Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
+ String *value = param->stringForKey(cgi[i].name);\r
+ if ((value != NULL) && value->isMatch(cgi[i].regex))\r
+ {\r
+ *(cgi[i].variable) = value;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ // 有効なパラメータか\r
+ if ((service_id != NULL) && (year != NULL) && (month != NULL) && (day != NULL) &&\r
+ (start_hour != NULL) && (start_min != NULL) && (end_hour != NULL) && (end_min != NULL) && (repeat != NULL))\r
+ {\r
+ //\r
+ DebugLog1("valid param");\r
+\r
+ Dictionary *epg = Dictionary::dictionaryWithCapacity(0);\r
+\r
+ // 日付\r
+ epg->setString(String::stringWithFormat("%s/%02d/%02d", year->cString(), month->intValue(), day->intValue()), KEY_EPG_DATE);\r
+\r
+ // 開始時刻\r
+ epg->setString(String::stringWithFormat("%s:%s:00", start_hour->cString(), start_min->cString()), KEY_EPG_START);\r
+\r
+ // 終了時刻\r
+ epg->setString(String::stringWithFormat("%s:%s:00", end_hour->cString(), end_min->cString()), KEY_EPG_END);\r
+\r
+ // 繰り返し\r
+ epg->setString(repeat, KEY_EPG_REPEAT);\r
+\r
+ // Service ID\r
+ epg->setString(service_id, KEY_EPG_SERVICE_ID);\r
+\r
+ // Status\r
+ epg->setString("ready", KEY_EPG_STATUS);\r
+\r
+ result = responseByResultAndReferer(request, reserve(epg), URI_RESERVATION_HTML);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ return result;\r
+}\r
+\r
+HTTPResponse *Controller::responseForCancelCGI(HTTPRequest *request, SOCKADDR_IN *client)\r
+{\r
+ DebugLog2("Controller::responseForCancelCGI()");\r
+\r
+ HTTPResponse *result = NULL;\r
+\r
+ // CGIリクエストとして解析\r
+ Dictionary *cgi = request->parseAsCGI();\r
+ if (cgi != NULL)\r
+ {\r
+ // CGIパスが一致しているか\r
+ if ((cgi->stringForKey(HTTPRequest::KEY_CGI) != NULL) && (cgi->stringForKey(HTTPRequest::KEY_CGI)->isEqualToString("/cancel.cgi")))\r
+ {\r
+ // パラメータがあるか\r
+ Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
+ if (params != NULL)\r
+ {\r
+ // パラメータ数が1か\r
+ if (params->count() == 1)\r
+ {\r
+ Dictionary *param = (Dictionary *)params->objectAtIndex(0);\r
+ String *value = param->stringForKey("resv_id");\r
+ if ((value != NULL) && value->isMatch("^\\d{6}$"))\r
+ {\r
+ result = responseByResultAndReferer(request, cancel(-1, value->intValue()), URI_RESERVATION_HTML);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ return result;\r
+}\r
+\r
+HTTPResponse *Controller::responseForAddKeywordsCGI(HTTPRequest *request, SOCKADDR_IN *client)\r
+{\r
+ DebugLog2("Controller::responseForAddKeywordsCGI()");\r
+\r
+ HTTPResponse *result = NULL;\r
+\r
+ // CGIリクエストとして解析\r
+ Dictionary *cgi = request->parseAsCGI();\r
+ if (cgi != NULL)\r
+ {\r
+ // CGIパスが一致しているか\r
+ if ((cgi->stringForKey(HTTPRequest::KEY_CGI) != NULL) && (cgi->stringForKey(HTTPRequest::KEY_CGI)->isEqualToString("/add_keywords.cgi")))\r
+ {\r
+ // パラメータがあるか\r
+ Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
+ if (params != NULL)\r
+ {\r
+ // パラメータ数が1か\r
+ if (params->count() == 1)\r
+ {\r
+ Dictionary *param = (Dictionary *)params->objectAtIndex(0);\r
+ String *value = param->stringForKey("keywords");\r
+ if (value != NULL)\r
+ {\r
+ value = value->stringByReplacingOccurrencesOfString("+", " ");\r
+ if (value != NULL)\r
+ {\r
+ value = value->stringByRemovingPercentEncoding();\r
+ }\r
+ }\r
+ if (value != NULL)\r
+ {\r
+ // lock\r
+ RaymLock(this);\r
+\r
+ Dictionary *keywords_info = _reservations->dictionaryForKey(KEY_EPG_KEYWORDS);\r
+ if (keywords_info == NULL)\r
+ {\r
+ keywords_info = Dictionary::dictionaryWithCapacity(0);\r
+ _reservations->setObject(keywords_info, KEY_EPG_KEYWORDS);\r
+ }\r
+\r
+ if (keywords_info->dictionaryForKey(value) == NULL)\r
+ {\r
+ // 新規キーワードの場合\r
+ keywords_info->setObject(Dictionary::dictionaryWithCapacity(0), value);\r
+\r
+ // キーワード予約更新\r
+ updateKeywordsReservation();\r
+\r
+ // スケジュール更新\r
+ updateSchedule();\r
+\r
+ // 保存\r
+ _reservations->writeToFile(_reservations_path, true);\r
+ }\r
+\r
+ RaymUnlock(this);\r
+\r
+ result = responseForReloadURI(request, client, "/reservation.html#_keywords");\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ return result;\r
+}\r
+\r
+HTTPResponse *Controller::responseForModKeywordsCGI(HTTPRequest *request, SOCKADDR_IN *client)\r
+{\r
+ DebugLog2("Controller::responseForModKeywordsCGI()");\r
+\r
+ HTTPResponse *result = NULL;\r
+\r
+ // CGIリクエストとして解析\r
+ Dictionary *cgi = request->parseAsCGI();\r
+ if (cgi != NULL)\r
+ {\r
+ DebugLog3("cgi != NULL");\r
+\r
+ // CGIパスが一致しているか\r
+ if ((cgi->stringForKey(HTTPRequest::KEY_CGI) != NULL) && (cgi->stringForKey(HTTPRequest::KEY_CGI)->isEqualToString("/mod_keywords.cgi")))\r
+ {\r
+ DebugLog3("CGI path OK.");\r
+\r
+ // パラメータがあるか\r
+ Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
+ if (params != NULL)\r
+ {\r
+ DebugLog3("params != NULL");\r
+\r
+ // パラメータのチェック\r
+ String *keywords = NULL;\r
+ String *service_id = NULL;\r
+ String *start_hour = NULL;\r
+ String *start_min = NULL;\r
+ String *req_mod = NULL;\r
+ String *req_del = NULL;\r
+\r
+ struct {\r
+ const char *name;\r
+ String **variable;\r
+ const char *regex;\r
+ }\r
+ cgi[] =\r
+ {\r
+ {"keywords", &keywords, ".*"},\r
+ {"service_id", &service_id, "^(\\d+|-)$"},\r
+ {"start_hour", &start_hour, "^(\\d{2}|-)$"},\r
+ {"start_min", &start_min, "^(\\d{2}|-)$"},\r
+ {"req_mod", &req_mod, ".*"},\r
+ {"req_del", &req_del, ".*"},\r
+ {NULL, NULL, NULL}\r
+ };\r
+\r
+ for (uint i = 0; cgi[i].name != NULL; ++i)\r
+ {\r
+ for (uint j = 0; j < params->count(); ++j)\r
+ {\r
+ Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
+ String *value = param->stringForKey(cgi[i].name);\r
+ if ((value != NULL) && value->isMatch(cgi[i].regex))\r
+ {\r
+ *(cgi[i].variable) = value;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (keywords != NULL)\r
+ {\r
+ keywords = keywords->stringByReplacingOccurrencesOfString("+", " ");\r
+ if (keywords != NULL)\r
+ {\r
+ keywords = keywords->stringByRemovingPercentEncoding();\r
+ }\r
+ }\r
+\r
+ if ((keywords != NULL) && (service_id != NULL) && (start_hour != NULL) && (start_min != NULL) &&\r
+ ((req_mod != NULL) && (req_del == NULL)) || ((req_mod == NULL) && (req_del != NULL)))\r
+ {\r
+\r
+ // lock\r
+ RaymLock(this);\r
+\r
+ Dictionary *keywords_info = _reservations->dictionaryForKey(KEY_EPG_KEYWORDS);\r
+ if (keywords_info != NULL)\r
+ {\r
+ if (req_mod != NULL)\r
+ {\r
+ DebugLog0("keywords: %s", keywords->cString());\r
+ DebugLog0("service_id: %s", service_id->cString());\r
+ DebugLog0("start_hour: %s", start_hour->cString());\r
+ DebugLog0("start_min: %s", start_min->cString());\r
+ Dictionary *kwd_inf = keywords_info->dictionaryForKey(keywords);\r
+ if (kwd_inf != NULL)\r
+ {\r
+ if (!service_id->isEqualToString("-"))\r
+ {\r
+ kwd_inf->setString(service_id, KEY_EPG_SERVICE_ID);\r
+ }\r
+ else\r
+ {\r
+ kwd_inf->removeObjectForKey(KEY_EPG_SERVICE_ID);\r
+ }\r
+ if (!start_hour->isEqualToString("-") && !start_min->isEqualToString("-"))\r
+ {\r
+ kwd_inf->setString(String::stringWithFormat("%s:%s", start_hour->cString(), start_min->cString()), KEY_EPG_START);\r
+ }\r
+ else\r
+ {\r
+ kwd_inf->removeObjectForKey(KEY_EPG_START);\r
+ }\r
+ }\r
+ }\r
+ else if (req_del != NULL)\r
+ {\r
+ DebugLog0("keywords: %s", keywords->cString());\r
+ keywords_info->removeObjectForKey(keywords);\r
+ }\r
+\r
+ // キーワード予約更新\r
+ updateKeywordsReservation();\r
+\r
+ // スケジュール更新\r
+ updateSchedule();\r
+\r
+ // 保存\r
+ _reservations->writeToFile(_reservations_path, true);\r
+ }\r
+\r
+ // unlock\r
+ RaymUnlock(this);\r
+\r
+ result = responseForReloadURI(request, client, "/reservation.html#_keywords");\r
+ }\r
+ }\r
+ }\r
+ }\r
+ return result;\r
+}\r
+\r
+HTTPResponse *Controller::responseForReloadURI(NET::HTTPRequest *request, SOCKADDR_IN *client, const char *uri, int sec)\r
+{\r
+ HTTPResponse *result = NULL;\r
+\r
+ if (uri != NULL)\r
+ {\r
+ std::string contents;\r
+ contents = "<html>";\r
+ contents += "<head>";\r
+#if 0\r
+ contents += "<meta http-equiv=\"refresh\" content=\"0;URL=";\r
+#else\r
+ contents += "<meta http-equiv=\"refresh\" content=\"";\r
+ contents += sec;\r
+ contents += ";URL=";\r
+#endif\r
+ contents += uri;\r
+ contents += "\">";\r
+ contents += "</head>";\r
+ contents += "</html>";\r
+ String *html = String::stringWithUTF8String(contents.c_str());\r
+ if (html != NULL)\r
+ {\r
+ result = responseWithHTML(request, html);\r
+ }\r
+ }\r
+\r
+ return result;\r
+}\r
+\r
+HTTPResponse *Controller::responseForPlaylist(HTTPRequest *request, SOCKADDR_IN *client)\r
+{\r
+ HTTPResponse *result = NULL;\r
+ // http://bit.ly/iptv_feb2015\r
+\r
+#if 0\r
+\r
+#EXTM3U\r
+\r
+#EXTINF:-1, [COLOR yellow]Updated 15/04/2015 @ 03:45 [/COLOR] \r
+plugin://plugin.video.youtube/?action=play_video&videoid=eMduu81gNgM\r
+#EXTINF:-1, [COLOR green] --Uk Live Tv--[/COLOR] \r
+plugin://plugin.video.youtube/?action=play_video&videoid=eMduu81gNgM\r
+#EXTINF:-1, [COLOR green] --Free Collection of IPTV sports links--[/COLOR] \r
+plugin://plugin.video.youtube/?action=play_video&videoid=eMduu81gNgM\r
+#EXTINF:-1, [COLOR red] --Links will go down. Please note that it will take time to update--[/COLOR]\r
+plugin://plugin.video.youtube/?action=play_video&videoid=eMduu81gNgM\r
+#EXTINF:-1, [COLOR red] --Please contact me at the husham.com website--[/COLOR]\r
+plugin://plugin.video.youtube/?action=play_video&videoid=eMduu81gNgM\r
+\r
+#EXTINF:-1, Sky sports news\r
+rtmp://89.248.172.159:443/liverepeater playpath=35 swfUrl=http://popeoftheplayers.eu/atdedead.swf pageUrl=http://popeoftheplayers.eu/crichd.php?id=35&width=600&height=450 token=#atd%#$ZH\r
+#EXTINF:-1,Bt sports 1\r
+rtmp://80.82.78.87:443/liverepeater/starsp pageUrl=http://xxxxxxxxxxxxxxxx.xx/rtmpe://strm.dcast.tv:1935/live/asdfadfaa/pageUrl=http://xxxxx.xx/rtmp://80.82.64.90:443/liverepeater/79/pageUrl=http://filotv.pw/rtmpe://strm.ukcast.tv:1935/redirect/FUNKTSN/pageUrl=http://ukcast.tv/rtmp://173.192.81.176:443/liverepeater/stream1/token%ZZri(nKa@#Z/pageUrl=http://hdcast.org/cdn.kingofplayers.com/rtmpe://46.246.29.152:1935/redirect/HDMNBC playpath=41?18?49?33?48?38?11 pageUrl=http://popeoftheplayers.eu/hdcast.org/rtmp://31.220.0.134:1935/live/tsn2/pageUrl=http://www.eucast.tv/rtmp://195.154.236.152:80/liverepeater/141449/pageUrl=http://goodcast.pw/rtmp://77.81.98.134/tv/bt1h28qn?v=pageUrl=http://castok.com/rtmp://89.46.102.70:443/liveedge/bt1pageUrl=http://hqstreams.tv/rtmpe://play.finecast.tv/live/hqbt1page/playpath=42?finecast.tv/rtmpe://cdn.hdcast.org:1935/redirect/swfUrl=http://www.hdcast.org/aplayer/jwplayer.flash.swfpageUrl=http://www.hdcast.org/token=Fo5_n0w?U.rA6l3-70w47ch@#8x12pX@ token=#atd%#$ZH\r
+\r
+#endif\r
+\r
+\r
+ std::string contents;\r
+ contents = "#EXTM3U\r\n";\r
+ contents += "\r\n";\r
+\r
+ Dictionary *tuner_channel_to_udp = _streaming_ctrls->dictionaryForKey(KEY_MAPPING_TUNER_CHANNEL_TO_UDP);\r
+ if (tuner_channel_to_udp != NULL)\r
+ {\r
+ Tuner::Type types[] = {Tuner::ISDB_T, Tuner::ISDB_S};\r
+ for (int t = 0; t < sizeof(types) / sizeof(Tuner::Type); ++t)\r
+ {\r
+ for (int i = 0; i < _tunerCount; ++i)\r
+ {\r
+ if (isTunerInitialized(i) && (_tuners[i]->type() == types[t]))\r
+ {\r
+ uint ch_max = ((types[t] == Tuner::ISDB_T) ? Tuner::MAX_CHANNELS_ISDB_T : Tuner::MAX_CHANNELS_ISDB_S);\r
+ for (uint ch = 0; ch <= ch_max; ++ch)\r
+ {\r
+ if (isChannelEnabled(i, ch))\r
+ {\r
+ char tuner_and_channel[10];\r
+ sprintf_s(tuner_and_channel, "%d,%d", i, ch);\r
+ char *chstr = strchr(tuner_and_channel, ',');\r
+ if (chstr != NULL)\r
+ {\r
+ ++chstr;\r
+ }\r
+\r
+ String *udp_str = tuner_channel_to_udp->stringForKey(tuner_and_channel);\r
+ if (udp_str != NULL)\r
+ {\r
+ contents += "#EXTINF:-1 tvg-id=\"";\r
+ contents += tuner_and_channel;\r
+ contents += "\" tvg-logo=\"";\r
+ contents += ((types[t] == Tuner::ISDB_T) ? "t_" : "s_");\r
+ if (chstr != NULL)\r
+ {\r
+ contents += chstr;\r
+ }\r
+ else\r
+ {\r
+ contents += "ffff";\r
+ }\r
+ contents += "\" group-title=\"";\r
+ contents += _tuners[i]->name();\r
+ contents += "\", ";\r
+ String *station_name = stationName(types[t], ch);\r
+ if (station_name != NULL)\r
+ {\r
+ contents += station_name->cString();\r
+ }\r
+ else\r
+ {\r
+ contents += tuner_and_channel;\r
+ }\r
+ contents += "\r\n";\r
+ contents += "udp://0.0.0.0:";\r
+ contents += udp_str->cString();\r
+ contents += "\r\n";\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ }\r
+\r
+ String *text = String::stringWithUTF8String(contents.c_str());\r
+ if (text != NULL)\r
+ {\r
+ result = responseWithUTF8Text(request, text);\r
+ }\r
+\r
+ return result;\r
+}\r
+\r
+/**\r
+ * @brief HTTP Live Streaming制御\r
+ *\r
+ * http://hogehoge/チューナ番号/チャンネル番号/streaming[-プリセット名].m3u8 がリクエスト(プリセットはオプション)され、\r
+ * チューナ番号/チャンネル番号/プリセット名(ある場合)が有効値の場合にコールされる\r
+ *\r
+ * @param [in] request HTTPリクエスト\r
+ * @param [in] client リクエストしたクライアントのアドレス\r
+ * @param [in] tuner チューナ番号\r
+ * @param [in] channel チャンネル番号\r
+ * @param [in] preset プリセット(URLで省略された場合 "default" )\r
+ */\r
+HTTPResponse *Controller::responseForHLSControl(HTTPRequest *request, SOCKADDR_IN *client, int tuner, int channel, String *preset)\r
+{\r
+ DebugLog0("Controller::responseForHLSControl()");\r
+\r
+ HTTPResponse *result = NULL;\r
+\r
+ // client からホスト名を取得\r
+ char hostname[NI_MAXHOST];\r
+ if (getnameinfo((SOCKADDR *)client, sizeof(SOCKADDR_IN), hostname, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) == 0)\r
+ {\r
+ // 取得OK\r
+ DebugLog0("host: %s", hostname);\r
+\r
+ // ストリーミング制御情報からHLS情報を取得\r
+ Dictionary *hls_info = _streaming_ctrls->dictionaryForKey(KEY_HLS_INFO);\r
+ if (hls_info == NULL)\r
+ {\r
+ DebugLog0("hls_info == NULL");\r
+ hls_info = Dictionary::dictionaryWithCapacity(0);\r
+ _streaming_ctrls->setObject(hls_info, KEY_HLS_INFO);\r
+ }\r
+\r
+ // HLS情報から指定チューナの情報を取得\r
+ Dictionary *hls_info_tuner = hls_info->dictionaryForKey(_tuners[tuner]->name());\r
+ if (hls_info_tuner == NULL)\r
+ {\r
+ DebugLog0("hls_info_tuner == NULL");\r
+ hls_info_tuner = Dictionary::dictionaryWithCapacity(0);\r
+ hls_info->setObject(hls_info_tuner, _tuners[tuner]->name());\r
+ }\r
+\r
+ // 指定チューナの情報からホスト名を確認\r
+ if ((hls_info_tuner->stringForKey(KEY_HOSTNAME) == NULL) || hls_info_tuner->stringForKey(KEY_HOSTNAME)->isEqualToString(hostname))\r
+ {\r
+ DebugLog0("new host or reconnect");\r
+ // ホスト名を設定\r
+ hls_info_tuner->setString(hostname, KEY_HOSTNAME);\r
+\r
+ // HLS制御インスタンス取得\r
+ HTTPLiveStreaming *hls = (HTTPLiveStreaming *)hls_info_tuner->objectForKey(KEY_HLS_INSTANCE);\r
+ if (hls == NULL)\r
+ {\r
+ DebugLog0("hls == NULL");\r
+ // 生成\r
+ hls = HTTPLiveStreaming::alloc()->init()->autorelease();\r
+ hls_info_tuner->setObject(hls, KEY_HLS_INSTANCE);\r
+ }\r
+\r
+ // 異チャンネルへのリクエスト、または、プリセット変更の場合\r
+ if (((hls_info_tuner->integerForKey(KEY_CHANNEL) != 0) && (hls_info_tuner->integerForKey(KEY_CHANNEL) != channel)) ||\r
+ ((hls_info_tuner->stringForKey(KEY_PRESET) != NULL) && !hls_info_tuner->stringForKey(KEY_PRESET)->isEqualToString(preset)))\r
+ {\r
+ DebugLog0("hls->stop()");\r
+\r
+ // 停止\r
+ hls->stop();\r
+\r
+ // チャンネルとプリセットは一旦削除\r
+ hls_info_tuner->removeObjectForKey(KEY_CHANNEL);\r
+ hls_info_tuner->removeObjectForKey(KEY_PRESET);\r
+ }\r
+\r
+ DebugLog0("start ?");\r
+\r
+ // 初回リクエスト or チャンネル/プリセット変更 か?\r
+ if ((hls_info_tuner->integerForKey(KEY_CHANNEL) == 0) && (hls_info_tuner->stringForKey(KEY_PRESET) == NULL))\r
+ {\r
+ DebugLog0("start or restart");\r
+\r
+ // UDPポートを検索\r
+ Dictionary *mapping = _streaming_ctrls->dictionaryForKey(KEY_MAPPING_UDP_TO_TUNER_CHANNEL);\r
+ if (mapping != NULL)\r
+ {\r
+ Array *ports = mapping->allKeys();\r
+ if (ports != NULL)\r
+ {\r
+ char tuner_and_channel[10];\r
+ sprintf_s(tuner_and_channel, "%d,%d", tuner, channel);\r
+\r
+ for (uint i = 0; i < ports->count(); ++i)\r
+ {\r
+ String *port = (String *)ports->objectAtIndex(i);\r
+ String *tmp = mapping->stringForKey(port);\r
+ if (tmp->isEqualToString(tuner_and_channel))\r
+ {\r
+ DebugLog0("udp mapping %d -> %d,%d", port->intValue(), tuner, channel);\r
+\r
+ // ソースを設定\r
+ hls->setSource(String::stringWithFormat("udp://@:%d", port->intValue()));\r
+\r
+ // トランスコード\r
+// hls->setTranscode(_props->dictionaryForKey(KEY_PRESETS)->dictionaryForKey(preset));\r
+\r
+ // 出力先を設定\r
+ hls->setOutputPath(_props->stringForKey(KEY_CACHE_PATH));\r
+\r
+ // インデックス名\r
+ hls->setIndexName(String::stringWithFormat("live_%03d_%03d", tuner, channel));\r
+\r
+ // 開始\r
+ if (hls->start())\r
+ {\r
+ DebugLog0("hls->start() success");\r
+\r
+ // チャンネルを保存\r
+ hls_info_tuner->setInteger(0, KEY_CHANNEL);\r
+\r
+ // プリセットを保存\r
+ hls_info_tuner->setString(preset, KEY_PRESET);\r
+ }\r
+\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+\r
+ String *index_path = hls->indexPath();\r
+ FileManager *fm = FileManager::defaultManager();\r
+ bool isDirectory = false;\r
+ if (fm->fileExistsAtPath(index_path, &isDirectory))\r
+ {\r
+ if (!isDirectory)\r
+ {\r
+ DebugLog0("file exists");\r
+ result = _httpd->responseWithPath(index_path, request);\r
+ }\r
+ }\r
+ if (result == NULL)\r
+ {\r
+ DebugLog0("file no exists");\r
+ result = responseForReloadURI(request, client, request->URI()->cString(), 10);\r
+ }\r
+\r
+// result = responseForFailed(request);\r
+ }\r
+ else\r
+ {\r
+ // 他ホストが使用中\r
+ }\r
+ }\r
+\r
+ return result;\r
+}\r
+\r
+HTTPResponse *Controller::requestTunerControl(HTTPRequest *request, SOCKADDR_IN *client, int tuner)\r
+{\r
+ DebugLog0("%s\n", __FUNCTION__);\r
+\r
+ HTTPResponse *result = NULL;\r
+\r
+ // lock\r
+ RaymLock(this);\r
+\r
+ // URI取得\r
+ String *uri = request->URI();\r
+ while (uri != NULL)\r
+ {\r
+ // CGIリクエストとして解析\r
+ Dictionary *cgi = request->parseAsCGI();\r
+ if (cgi != NULL)\r
+ {\r
+ uri = cgi->stringForKey(HTTPRequest::KEY_CGI);\r
+ if (uri == NULL)\r
+ {\r
+ break;\r
+ }\r
+ }\r
+\r
+ //\r
+ // チューナ情報\r
+ //\r
+ if (uri->isMatch("^/[0-9]{3}/info.xml$") && (cgi == NULL))\r
+ {\r
+ }\r
+\r
+ //\r
+ // チャンネル設定\r
+ // /ttt/channel=nnn\r
+ //\r
+ else if (uri->isMatch("^/[0-9]{3}/channel=[0-9]{1,3}$") && (cgi == NULL))\r
+ {\r
+DebugLog0("ch set");\r
+ String *ch = uri->substringFromIndex(13);\r
+ if (ch == NULL)\r
+ {\r
+ break;\r
+ }\r
+ int channel = ch->intValue();\r
+DebugLog0("set %d channel:%d(%s)\n", tuner, channel, ch->cString());\r
+ DebugLog2("set channel:%d(%s)\n", channel, ch->cString());\r
+ if (setChannel(tuner, channel))\r
+ {\r
+ // success\r
+ DebugLog2("success.\n");\r
+ result = responseForSuccess(request);\r
+ }\r
+ else\r
+ {\r
+ // failed\r
+ DebugLog2("failed.\n");\r
+ result = responseForFailed(request);\r
+ }\r
+ }\r
+\r
+ //\r
+ // 録画開始(最大23:59まで)\r
+ // /ttt/recording=on?hour=hh&min=mm[&channel=nnn]\r
+ //\r
+ else if (uri->isMatch("^/[0-9]{3}/recording=on$") && (cgi != NULL))\r
+ {\r
+ // パラメータがあるか\r
+ Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
+ if (params == NULL)\r
+ {\r
+ break;\r
+ }\r
+\r
+ // パラメータ数は2〜3か\r
+ if ((params->count() != 2) && (params->count() != 3))\r
+ {\r
+ break;\r
+ }\r
+\r
+ // パラメータのチェック\r
+ String *p_hour = NULL;\r
+ String *p_min = NULL;\r
+ String *p_channel = NULL;\r
+\r
+ struct {\r
+ const char *name;\r
+ String **variable;\r
+ const char *regex;\r
+ }\r
+ cgi[] =\r
+ {\r
+ {"hour", &p_hour, "^[0-2][0-9]$"},\r
+ {"min", &p_min, "^[0-5][0-9]$"},\r
+ {"channel", &p_channel, "^[0-9]{3}$"},\r
+ {NULL, NULL, NULL}\r
+ };\r
+\r
+ for (uint i = 0; cgi[i].name != NULL; ++i)\r
+ {\r
+ *(cgi[i].variable) = NULL;\r
+ for (uint j = 0; j < params->count(); ++j)\r
+ {\r
+ Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
+ String *value = param->stringForKey(cgi[i].name);\r
+ if ((value != NULL) && value->isMatch(cgi[i].regex))\r
+ {\r
+ *(cgi[i].variable) = value;\r
+ }\r
+ }\r
+ }\r
+\r
+ // パラメータは有効か\r
+ if ((p_hour == NULL) || (p_min == NULL))\r
+ {\r
+ break;\r
+ }\r
+\r
+ // チャンネル設定\r
+ int channel = 0;\r
+ if (p_channel != NULL)\r
+ {\r
+ channel = p_channel->intValue();\r
+ }\r
+ else\r
+ {\r
+ channel = _tuners[tuner]->channel();\r
+ }\r
+\r
+ if (channel >= 0)\r
+ {\r
+ // recording on\r
+ int hour = p_hour->intValue();\r
+ int min = p_min->intValue();\r
+ if (hour < 24)\r
+ {\r
+ // EPG生成\r
+ Dictionary *epg = Dictionary::dictionaryWithCapacity(0);\r
+ while (true)\r
+ {\r
+ time_t now;\r
+ time(&now);\r
+ now += 1; // margin\r
+ TM tm;\r
+ if (localtime_s(&tm, &now) != 0)\r
+ {\r
+ epg = NULL;\r
+ break;\r
+ }\r
+ TM end;\r
+ end = tm;\r
+ end.tm_hour += hour;\r
+ end.tm_min += min;\r
+ end.tm_sec += 1; // margin\r
+ if (mktime(&end) == -1)\r
+ {\r
+ epg = NULL;\r
+ break;\r
+ }\r
+\r
+ char tmp[16];\r
+\r
+ // Date\r
+ sprintf_s(tmp, sizeof(tmp), "%04d/%02d/%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);\r
+ epg->setString(tmp, KEY_EPG_DATE);\r
+\r
+ // Start\r
+ sprintf_s(tmp, sizeof(tmp), "%02d:%02d:%02d", tm.tm_hour, tm.tm_min, tm.tm_sec);\r
+ epg->setString(tmp, KEY_EPG_START);\r
+\r
+ // End\r
+ sprintf_s(tmp, sizeof(tmp), "%02d:%02d:%02d", end.tm_hour, end.tm_min, end.tm_sec);\r
+ epg->setString(tmp, KEY_EPG_END);\r
+\r
+ // Channel\r
+ sprintf_s(tmp, sizeof(tmp), "%d", channel);\r
+ epg->setString(tmp, KEY_EPG_CHANNEL);\r
+\r
+ // 繰り返し\r
+ epg->setString("off", KEY_EPG_REPEAT); \r
+\r
+ // Status\r
+ epg->setString("ready", KEY_EPG_STATUS);\r
+\r
+ break;\r
+ }\r
+ \r
+ if (epg != NULL)\r
+ {\r
+ // 録画開始&結果生成\r
+ result = responseByResultAndReferer(request, reserve(tuner, epg), URI_RESERVATION_HTML);\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // 録画停止\r
+ // /ttt/recording=off\r
+ //\r
+ else if (uri->isMatch("^/[0-9]{3}/recording=off$") && (cgi == NULL))\r
+ {\r
+ // recording off\r
+ DebugLog2("recording off: %s\n", uri->cString());\r
+ if (cancel(tuner, -1))\r
+ {\r
+ // success\r
+ DebugLog2("success.\n");\r
+ result = responseForSuccess(request);\r
+ }\r
+ else\r
+ {\r
+ // failed\r
+ DebugLog2("failed.\n");\r
+ result = responseForFailed(request);\r
+ }\r
+ }\r
+\r
+ //\r
+ // ストリーミング開始\r
+ // /ttt/streaming=on?udp=nnnnn(&host=aaaaaa)\r
+ //\r
+ else if (uri->isMatch("^/[0-9]{3}/streaming=on$") && (cgi != NULL))\r
+ {\r
+ // パラメータがあるか\r
+ Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
+ if (params == NULL)\r
+ {\r
+ break;\r
+ }\r
+\r
+ // パラメータ数は1〜2か\r
+ if ((params->count() != 1) && (params->count() != 2))\r
+ {\r
+ break;\r
+ }\r
+\r
+ // パラメータのチェック\r
+ String *p_udp = NULL;\r
+ String *p_host = NULL;\r
+\r
+ struct {\r
+ const char *name;\r
+ String **variable;\r
+ const char *regex;\r
+ }\r
+ cgi[] =\r
+ {\r
+ {"udp", &p_udp, "^[0-9]{1,5}$"},\r
+ {"host", &p_host, "^.+$"},\r
+ {NULL, NULL, NULL}\r
+ };\r
+\r
+ for (uint i = 0; cgi[i].name != NULL; ++i)\r
+ {\r
+ *(cgi[i].variable) = NULL;\r
+ for (uint j = 0; j < params->count(); ++j)\r
+ {\r
+ Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
+ String *value = param->stringForKey(cgi[i].name);\r
+ if ((value != NULL) && value->isMatch(cgi[i].regex))\r
+ {\r
+ *(cgi[i].variable) = value;\r
+ }\r
+ }\r
+ }\r
+\r
+ // パラメータチェック\r
+ if (p_udp == NULL)\r
+ {\r
+ break;\r
+ }\r
+\r
+ SOCKADDR_IN dst_addr;\r
+ \r
+ if (p_host != NULL)\r
+ {\r
+ #if 0\r
+ std::string host = udpstr.substr(idx + 5);\r
+ udpstr = udpstr.substr(0, idx - 1);\r
+ DebugLog2("udp: %s\n", udpstr.c_str());\r
+ DebugLog2("host: %s\n", host.c_str());\r
+ struct hostent *ent = gethostbyname(host.c_str());\r
+ #endif\r
+ }\r
+ else\r
+ {\r
+ memcpy(&dst_addr, client, sizeof(SOCKADDR_IN));\r
+ }\r
+ dst_addr.sin_port = htons(p_udp->intValue());\r
+ \r
+ if (_tuners[tuner]->startStreaming(&dst_addr))\r
+ {\r
+ // success\r
+ DebugLog2("success.\n");\r
+ result = responseForSuccess(request);\r
+ }\r
+ else\r
+ {\r
+ // failed\r
+ DebugLog2("failed.\n");\r
+ result = responseForFailed(request);\r
+ }\r
+ }\r
+\r
+ //\r
+ // ストリーミング停止\r
+ // /ttt/streaming=off(?host=aaaa)\r
+ //\r
+ else if (uri->isMatch("^/[0-9]{3}/streaming=off$"))\r
+ {\r
+ // パラメータ\r
+ String *p_host = NULL;\r
+\r
+ // パラメータがあるか\r
+ if (cgi != NULL)\r
+ {\r
+ Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
+ if (params == NULL)\r
+ {\r
+ break;\r
+ }\r
+\r
+ // パラメータ数は0〜1か\r
+ if ((params->count() != 0) && (params->count() != 1))\r
+ {\r
+ break;\r
+ }\r
+\r
+ struct {\r
+ const char *name;\r
+ String **variable;\r
+ const char *regex;\r
+ }\r
+ cgi[] =\r
+ {\r
+ {"host", &p_host, "^.+$"},\r
+ {NULL, NULL, NULL}\r
+ };\r
+\r
+ for (uint i = 0; cgi[i].name != NULL; ++i)\r
+ {\r
+ *(cgi[i].variable) = NULL;\r
+ for (uint j = 0; j < params->count(); ++j)\r
+ {\r
+ Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
+ String *value = param->stringForKey(cgi[i].name);\r
+ if ((value != NULL) && value->isMatch(cgi[i].regex))\r
+ {\r
+ *(cgi[i].variable) = value;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ SOCKADDR_IN dst_addr;\r
+ if (p_host != NULL)\r
+ {\r
+ }\r
+ else\r
+ {\r
+ }\r
+\r
+ _tuners[tuner]->stopStreaming();\r
+ \r
+ // success\r
+ DebugLog2("success.\n");\r
+ result = responseForSuccess(request);\r
+ }\r
+\r
+ //\r
+ // HLS制御\r
+ //\r
+ else if (uri->isMatch("^/[0-9]{3}/[0-9]{3}/streaming(-[^\\.]+)?.m3u8$") && (cgi == NULL))\r
+ {\r
+ DebugLog0("uri: %s", uri->cString());\r
+\r
+ // URIからチャンネル番号を抽出\r
+ // Range実装したい...\r
+ int ch = uri->substringFromIndex(5)->substringToIndex(3)->intValue();\r
+ DebugLog0("ch: %d", ch);\r
+\r
+ // presetが指定されている場合、presetを抽出\r
+ String *preset = NULL;\r
+ if (uri->isMatch("streaming-"))\r
+ {\r
+ preset = uri->substringFromIndex(19);\r
+ preset = preset->substringToIndex(preset->length() - 5);\r
+ DebugLog0("opt: preset: %s", preset->cString());\r
+ }\r
+ else\r
+ {\r
+ // なければ "default"\r
+ preset = String::stringWithUTF8String(KEY_DEFAULT);\r
+ }\r
+\r
+ // チャンネル/presetが有効か確認\r
+ if (isChannelEnabled(tuner, ch) &&\r
+ (_props->dictionaryForKey(KEY_PRESETS) != NULL) &&\r
+ (_props->dictionaryForKey(KEY_PRESETS)->objectForKey(preset) != NULL))\r
+ {\r
+ // \r
+ result = responseForHLSControl(request, client, tuner, ch, preset);\r
+ }\r
+ else\r
+ {\r
+ result = responseForFailed(request);\r
+ }\r
+ DebugLog0("hls req. done");\r
+ }\r
+ else if (uri->isMatch("^/[0-9]{3}/[0-9]{3}/streaming-[0-9]+.ts$") && (cgi == NULL))\r
+ {\r
+ // 分割されたTS\r
+ DebugLog0("uri: %s", uri->cString());\r
+ }\r
+\r
+ break;\r
+ }\r
+\r
+ // unlock\r
+ RaymUnlock(this);\r
+\r
+ return result;\r
+}\r
+\r
+HTTPResponse *Controller::request(HTTPRequest *request, SOCKADDR_IN *client)\r
+{\r
+ DebugLog2("%s\n", __FUNCTION__);\r
+\r
+ // 初期化チェック\r
+ bool flag = false;\r
+ RaymLock(this);\r
+ flag = _initialized;\r
+ RaymUnlock(this);\r
+ if (!flag)\r
+ {\r
+ return NULL;\r
+ }\r
+\r
+ HTTPResponse *response = NULL;\r
+\r
+ if (request->method()->isEqualToString("GET") ||\r
+ request->method()->isEqualToString("HEAD"))\r
+ {\r
+ // URI\r
+ String *uri = request->URI();\r
+ DebugLog0("request: %s\n", uri->cString());\r
+\r
+ //\r
+ //\r
+ //\r
+ if (uri->isMatch("^(/|/index\\.(htm|html))$"))\r
+ {\r
+ response = responseForMain(request, client);\r
+ }\r
+\r
+ else if (uri->isMatch("^/status.html$"))\r
+ {\r
+ response = responseForStatus(request, client);\r
+ }\r
+\r
+ //\r
+ else if (uri->isMatch("^/info.xml$"))\r
+ {\r
+ }\r
+ else if (uri->isMatch("^/video.xml$"))\r
+ {\r
+ }\r
+\r
+ //\r
+ else if (uri->isMatch("^/suspend.xml$"))\r
+ {\r
+ if (canSuspend())\r
+ {\r
+// response = responseForSuccess(request);\r
+// delaySuspend();\r
+ }\r
+ else\r
+ {\r
+// response = responseForFailed(request);\r
+ }\r
+ }\r
+\r
+ //\r
+ else if (uri->isMatch("^/exec_vlc.html$"))\r
+ {\r
+// _vlc->execute();\r
+// return responseForRefreshMain(request, client);\r
+ }\r
+\r
+ //\r
+ // tuner control\r
+ //\r
+ else if (uri->isMatch("^/[0-9]{3}/"))\r
+ {\r
+DebugLog0("tuner control");\r
+ // String::substringWithRange() を実装するのを後回しにするので。。\r
+ std::string s = uri->cString();\r
+ int tuner = atoi(s.substr(1, 3).c_str());\r
+ if ((0 <= tuner) && (tuner < _tunerCount))\r
+ {\r
+ response = requestTunerControl(request, client, tuner);\r
+DebugLog0("tuner control done");\r
+ }\r
+ }\r
+\r
+ //\r
+ // reservation control\r
+ //\r
+ else if (uri->isMatch("^/reservation.html$"))\r
+ {\r
+ response = responseForReservation(request, client);\r
+ }\r
+ else if (uri->isMatch("^/regist.cgi"))\r
+ {\r
+ response = responseForRegistCGI(request, client);\r
+ }\r
+ else if (uri->isMatch("^/cancel.cgi"))\r
+ {\r
+ response = responseForCancelCGI(request, client);\r
+ }\r
+ else if (uri->isMatch("^/add_keywords.cgi"))\r
+ {\r
+ response = responseForAddKeywordsCGI(request, client);\r
+ }\r
+ else if (uri->isMatch("^/mod_keywords.cgi"))\r
+ {\r
+ response = responseForModKeywordsCGI(request, client);\r
+ }\r
+ else if (uri->isMatch("^/reservation.xml$"))\r
+ {\r
+ }\r
+ else if (uri->isMatch("^/[0-9]{6}/"))\r
+ {\r
+// DebugLog2("reservation: %s\n", uri.c_str());\r
+ }\r
+\r
+ //\r
+ // video control\r
+ //\r
+ else if (uri->isMatch("^/[0-9]{9}/"))\r
+ {\r
+// DebugLog2("video: %s\n", uri.c_str());\r
+ }\r
+\r
+ //\r
+ // program\r
+ //\r
+// else if (match(uri.c_str(), "^/programs.html$"))\r
+ else if (uri->isMatch("^/" URI_PROGRAMS_HTML "$"))\r
+ {\r
+ response = responseForPrograms(request, client);\r
+ }\r
+ else if (uri->isMatch("^/programs.xml$"))\r
+ {\r
+// collectEPG();\r
+// return responseWithDictionary(request, _programs);\r
+ }\r
+\r
+ //\r
+ //\r
+ else if (uri->isMatch("^/tv.html$"))\r
+ {\r
+// return responseForTV(request, client);\r
+ }\r
+\r
+ // IPTV対応\r
+ else if (uri->isMatch("^/iptv.m3u8$"))\r
+ {\r
+ return responseForPlaylist(request, client);\r
+ }\r
+\r
+ //\r
+ else if (uri->isMatch("^/iptd.log$"))\r
+ {\r
+ String *path = _system_path->stringByAppendingPathComponent("log");\r
+ if (path != NULL)\r
+ {\r
+ path = path->stringByAppendingPathComponent("iptd.log");\r
+ if (path != NULL)\r
+ {\r
+ response = _httpd->responseWithPath(path, request);\r
+ if (response != NULL)\r
+ {\r
+ if (response->message() != NULL)\r
+ {\r
+ if (response->message()->header() != NULL)\r
+ {\r
+ response->message()->header()->setFieldBodyWithName("text/plane; charset=UTF-8", "Content-Type");\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ else if (request->method()->isEqualToString("POST"))\r
+ {\r
+ // URI\r
+ String *uri = request->URI();\r
+ DebugLog1("POST: %s\n", uri->cString());\r
+ InternetTextMessage *message = request->message();\r
+ if (message != NULL)\r
+ {\r
+ DebugLog3("message: ");\r
+ InternetTextMessageHeader *header = message->header();\r
+ if (header != NULL)\r
+ {\r
+ DebugLog3("header: ");\r
+ }\r
+ InternetTextMessageBody *body = message->body();\r
+ if (body != NULL)\r
+ {\r
+ DebugLog3("body: ");\r
+ }\r
+ }\r
+ }\r
+\r
+ return response;\r
+}\r
+\r
+#ifndef _WIN32\r
+#pragma mark '\r
+#pragma mark ------- プロパティ取得 -------\r
+#endif\r
+\r
+bool Controller::isTunerInitialized(int tuner)\r
+{\r
+ DebugLog2("Controller::isTunerInitialized()");\r
+\r
+ bool result = false;\r
+\r
+ if ((0 <= tuner) && (tuner < _tunerCount))\r
+ {\r
+ // lock\r
+ RaymLock(this);\r
+\r
+ Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
+ if (tunersInfo != NULL)\r
+ {\r
+ Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
+ if (tunerInfo != NULL)\r
+ {\r
+ result = tunerInfo->boolForKey(KEY_INITIALIZED);\r
+ }\r
+ }\r
+\r
+ // unlock\r
+ RaymUnlock(this);\r
+ }\r
+\r
+ return result;\r
+}\r
+\r
+bool Controller::isTunerEnabled(int tuner)\r
+{\r
+ DebugLog2("Controller::isTunerEnabled()");\r
+\r
+ bool result = false;\r
+\r
+ if ((0 <= tuner) && (tuner < _tunerCount))\r
+ {\r
+ // lock\r
+ RaymLock(this);\r
+\r
+ Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
+ if (tunersInfo != NULL)\r
+ {\r
+ Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
+ if (tunerInfo != NULL)\r
+ {\r
+ result = tunerInfo->boolForKey(KEY_ENABLED);\r
+ }\r
+ }\r
+\r
+ // unlock\r
+ RaymUnlock(this);\r
+ }\r
+\r
+ return result;\r
+}\r
+\r
+bool Controller::isChannelEnabled(int tuner, int channel)\r
+{\r
+ DebugLog2("Controller::isChannelEnabled()");\r
+\r
+ bool result = false;\r
+\r
+ if ((0 <= tuner) && (tuner < _tunerCount) && (0 <= channel) && (channel <= Tuner::MAX_CHANNELS_ISDB_T))\r
+ {\r
+ // lock\r
+ RaymLock(this);\r
+\r
+ Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
+ if (tunersInfo != NULL)\r
+ {\r
+ Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
+ if (tunerInfo != NULL)\r
+ {\r
+ Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
+ if (channels != NULL)\r
+ {\r
+ char key[4];\r
+ sprintf_s(key, "%03d", channel);\r
+ Dictionary *chInfo = channels->dictionaryForKey(key);\r
+ if (chInfo != NULL)\r
+ {\r
+ result = chInfo->boolForKey(KEY_ENABLED);\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ // unlock\r
+ RaymUnlock(this);\r
+ }\r
+\r
+ return result;\r
+}\r
+\r
+Array *Controller::stationInfos(Tuner::Type type)\r
+{\r
+ DebugLog2("Controller::stationInfosForISDB_T()");\r
+\r
+ Array *result = Array::arrayWithCapacity(0);\r
+\r
+ // lock\r
+ RaymLock(this);\r
+\r
+ Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
+ if (tunersInfo != NULL)\r
+ {\r
+ for (int i = 0; i < _tunerCount; ++i)\r
+ {\r
+ if (_tuners[i]->type() == type)\r
+ {\r
+ Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[i]->name());\r
+ if (tunerInfo != NULL)\r
+ {\r
+ Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
+ if (channels != NULL)\r
+ {\r
+ for (uint ch = 0; ch <= (type == Tuner::ISDB_T ? Tuner::MAX_CHANNELS_ISDB_T : Tuner::MAX_CHANNELS_ISDB_S); ++ch)\r
+ {\r
+ char chkey[4];\r
+ sprintf_s(chkey, "%03d", ch);\r
+ Dictionary *channel = channels->dictionaryForKey(chkey);\r
+ if ((channel != NULL) && channel->boolForKey(KEY_ENABLED))\r
+ {\r
+ result->addObject(channel);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ // unlock\r
+ RaymUnlock(this);\r
+\r
+ return result;\r
+}\r
+\r
+String *Controller::stationName(Tuner::Type type, int channel)\r
+{\r
+ DebugLog2("Controller::stationName()");\r
+\r
+ String *result = NULL;\r
+\r
+ // lock\r
+ RaymLock(this);\r
+\r
+ Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
+ if (tunersInfo != NULL)\r
+ {\r
+ for (int i = 0; i < _tunerCount; ++i)\r
+ {\r
+ if (_tuners[i]->type() == type)\r
+ {\r
+ Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[i]->name());\r
+ if (tunerInfo != NULL)\r
+ {\r
+ Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
+ if (channels != NULL)\r
+ {\r
+ char chkey[4];\r
+ sprintf_s(chkey, "%03d", channel);\r
+ Dictionary *channel = channels->dictionaryForKey(chkey);\r
+ if ((channel != NULL) && channel->boolForKey(KEY_ENABLED))\r
+ {\r
+ result = channel->stringForKey(KEY_NAME);\r
+ }\r
+ }\r
+ }\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ // unlock\r
+ RaymUnlock(this);\r
+\r
+ return result;\r
+}\r
+\r
+// epgの開始時刻でソートする為の比較関数\r
+Integer compareFunction(Object *obj1, Object *obj2, void *context)\r
+{\r
+ if (isKindOfClass(Dictionary, obj1) && isKindOfClass(Dictionary, obj2))\r
+ {\r
+ time_t st1;\r
+ time_t ed1;\r
+ Controller::getTimeWithEPG((Dictionary *)obj1, &st1, &ed1);\r
+\r
+ time_t st2;\r
+ time_t ed2;\r
+ Controller::getTimeWithEPG((Dictionary *)obj2, &st2, &ed2);\r
+\r
+ if (st1 < st2)\r
+ {\r
+ return OrderedAscending;\r
+ }\r
+ else if (st1 > st2)\r
+ {\r
+ return OrderedDescending;\r
+ }\r
+ else\r
+ {\r
+ if (ed1 < ed2)\r
+ {\r
+ return OrderedAscending;\r
+ }\r
+ else if (ed1 > ed2)\r
+ {\r
+ return OrderedDescending;\r
+ }\r
+ }\r
+ }\r
+ return OrderedSame;\r
+}\r
+\r
+Array *Controller::programsForServices(Array *services)\r
+{\r
+ DebugLog2("Controller::programsForServices()");\r
+\r
+ Array *result = Array::arrayWithCapacity(0);\r
+\r
+ // lock\r
+ RaymLock(this);\r
+\r
+ for (uint i = 0; i < services->count(); ++i)\r
+ {\r
+ Dictionary *service = (Dictionary *)services->objectAtIndex(i);\r
+ Dictionary *events = _epgs->dictionaryForKey(service->stringForKey(KEY_SERVICE_ID));\r
+ if (events != NULL)\r
+ {\r
+ Array *keys = events->allKeys();\r
+ for (uint j = 0; j < keys->count(); ++j)\r
+ {\r
+ result->addObject(events->objectForKey((String *)keys->objectAtIndex(j)));\r
+ }\r
+ }\r
+ }\r
+\r
+ // sort\r
+ result = result->sortedArrayUsingFunction(compareFunction, this);\r
+\r
+\r
+ // unlock\r
+ RaymUnlock(this);\r
+\r
+ return result;\r
+}\r
+\r
+String *Controller::stationNameForServiceID(String *service_id)\r
+{\r
+ DebugLog2("Controller::stationNameForServiceID()");\r
+\r
+ String *result = NULL;\r
+\r
+ if (service_id != NULL)\r
+ {\r
+ // lock\r
+ RaymLock(this);\r
+\r
+ Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
+ if (tunersInfo != NULL)\r
+ {\r
+ Array *tuners_key = tunersInfo->allKeys();\r
+\r
+ for (uint i = 0; (result == NULL) && (i < tuners_key->count()); ++i)\r
+ {\r
+ Dictionary *tunerInfo = tunersInfo->dictionaryForKey((String *)tuners_key->objectAtIndex(i));\r
+ if (tunerInfo != NULL)\r
+ {\r
+ Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
+ if (channels != NULL)\r
+ {\r
+ Array *chkey = channels->allKeys();\r
+ for (uint ch = 0; (result == NULL) && (ch < chkey->count()); ++ch)\r
+ {\r
+ Dictionary *channel = channels->dictionaryForKey((String *)chkey->objectAtIndex(ch));\r
+ if (channel != NULL)\r
+ {\r
+ Array *services = channel->arrayForKey(KEY_SERVICES);\r
+ if (services != NULL)\r
+ {\r
+ for (uint j = 0; j < services->count(); ++j)\r
+ {\r
+ Dictionary *service = (Dictionary *)services->objectAtIndex(j);\r
+ String *sid = service->stringForKey(KEY_SERVICE_ID);\r
+ if (sid != NULL)\r
+ {\r
+ if (sid->isEqualToString(service_id))\r
+ {\r
+ result = service->stringForKey(KEY_NAME);\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ // unlock\r
+ RaymUnlock(this);\r
+ }\r
+\r
+ return result;\r
+}\r
+\r
+#ifndef _WIN32\r
+#pragma mark '\r
+#pragma mark ------- タイマディスパッチャ -------\r
+#endif\r
+\r
+void Controller::timerExpired(Timer *timer, void *userInfo)\r
+{\r
+ DebugLog2("Controller::timerExpired()");\r
+\r
+ //\r
+\r
+ switch ((long long)userInfo)\r
+ {\r
+ case CMD_RESTART:\r
+ if (restart() > 0)\r
+ {\r
+ // 初期化成功\r
+ DebugLog2("tuner initialize success.");\r
+\r
+ // EPG収集用タイマ起動(ISDB-S)\r
+ _timer_epg_s = Timer::alloc()->initWithTimeInterval(DEF_COLLECT_EPG_DELAY, this, (void *)CMD_COLLECT_EPG_ISDB_S, true);\r
+ if (_timer_epg_s != NULL)\r
+ {\r
+ _timer_epg_s->fire();\r
+ }\r
+\r
+ // EPG収集用タイマ起動(ISDB-T)\r
+ _timer_epg_t = Timer::alloc()->initWithTimeInterval(DEF_COLLECT_EPG_DELAY, this, (void *)CMD_COLLECT_EPG_ISDB_T, true);\r
+ if (_timer_epg_t != NULL)\r
+ {\r
+ _timer_epg_t->fire();\r
+ }\r
+ }\r
+ else\r
+ {\r
+ // 失敗\r
+ DebugLog0("tuner initialize failed.");\r
+ }\r
+ break;\r
+\r
+ case CMD_SUSPEND:\r
+ if (canSuspend())\r
+ {\r
+ suspend();\r
+ }\r
+ break;\r
+\r
+ case CMD_PERIODIC: // 周期処理\r
+ periodic();\r
+ break;\r
+\r
+ case CMD_PERIODIC_2: // 周期処理2\r
+ periodic_2();\r
+ break;\r
+\r
+ case CMD_COLLECT_EPG_ISDB_S: // EPG収集(ISDB-S)\r
+ // 番組情報収集(ISDB-S)\r
+ if (collectEPGs(Tuner::ISDB_S))\r
+ {\r
+ // 終了(繰り返し無し)\r
+ _timer_epg_s->setRepeats(false);\r
+ }\r
+ else\r
+ {\r
+ // リトライ\r
+ _timer_epg_s->setTimeInterval(DEF_COLLECT_EPG_RETRY);\r
+ }\r
+ break;\r
+\r
+ case CMD_COLLECT_EPG_ISDB_T: // 番組情報収集(ISDB-T)\r
+ if (collectEPGs(Tuner::ISDB_T))\r
+ {\r
+ // 終了(繰り返し無し)\r
+ _timer_epg_t->setRepeats(false);\r
+ }\r
+ else\r
+ {\r
+ // リトライ\r
+ _timer_epg_t->setTimeInterval(DEF_COLLECT_EPG_RETRY);\r
+ }\r
+ break;\r
+ }\r
+}\r
+\r
+#ifndef _WIN32\r
+#pragma mark '\r
+#pragma mark ------- 起動/停止関連 -------\r
+#endif\r
+\r
+// システム状態をチェックしてサスペンド可能かを返す\r
+bool Controller::canSuspend()\r
+{\r
+ return isIdleState(true);\r
+}\r
+\r
+bool Controller::canTerminate()\r
+{\r
+ return isIdleState(false);\r
+}\r
+\r
+bool Controller::isIdleState(bool suspend)\r
+{\r
+ DebugLog2("Controller::isIdleState() start.");\r
+\r
+ bool result = false;\r
+\r
+ // lock\r
+ RaymLock(this);\r
+\r
+ // 初期化済みか\r
+ while (_initialized)\r
+ {\r
+ // EPG(ISDB-S)収集中か\r
+ if (suspend && (_timer_epg_s != NULL) && _timer_epg_s->valid())\r
+ {\r
+ // サスペンド不可\r
+ break;\r
+ }\r
+\r
+ // EPG(ISDB-T)収集中か\r
+ if (suspend && (_timer_epg_t != NULL) && _timer_epg_t->valid())\r
+ {\r
+ // サスペンド不可\r
+ break;\r
+ }\r
+\r
+ // サスペンド可に設定\r
+ result = true;\r
+\r
+ // 予約情報をチェック\r
+ // 10分後以内に開始の予約があったらサスペンド不可\r
+\r
+ // 現在時刻取得\r
+ time_t now = time(NULL);\r
+\r
+ for (int tuner = 0; tuner < _tunerCount; ++tuner)\r
+ {\r
+ Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
+ if ((array == NULL) || (array->count() == 0))\r
+ {\r
+ // next tuner\r
+ continue;\r
+ }\r
+\r
+ //\r
+ Dictionary *epg = (Dictionary *)array->objectAtIndex(0);\r
+ time_t start;\r
+ time_t end;\r
+ getTimeWithEPG(epg, &start, &end);\r
+\r
+ if (start + OFFSET_OF_SUPPRESSION_TIME <= now)\r
+ {\r
+ // サスペンド不可\r
+ result = false;\r
+ break;\r
+ }\r
+ }\r
+\r
+ break;\r
+ }\r
+\r
+ // unlock\r
+ RaymUnlock(this);\r
+\r
+ DebugLog2("Controller::isIdleState() end.");\r
+\r
+ return result;\r
+}\r
+\r
+/*\r
+ * TrayApp::WndProc() からコールされる\r
+ *\r
+ * スレッドコンテキスト:メインスレッド\r
+ */\r
+void Controller::systemWillSuspend()\r
+{\r
+ DebugLog2("Controller::systemWillSuspend() start");\r
+\r
+ RaymLock(this);\r
+ _cancel_epg_collect = true;\r
+ RaymUnlock(this);\r
+\r
+ // タイマ停止\r
+ if ((_timer_restart != NULL) && _timer_restart->valid())\r
+ {\r
+ _timer_restart->invalidate();\r
+ }\r
+ if ((_timer_epg_s != NULL) && _timer_epg_s->valid())\r
+ {\r
+ _timer_epg_s->invalidate();\r
+ }\r
+ if ((_timer_epg_t != NULL) && _timer_epg_t->valid())\r
+ {\r
+ _timer_epg_t->invalidate();\r
+ }\r
+ if ((_timer_periodic != NULL) && _timer_periodic->valid())\r
+ {\r
+ _timer_periodic->invalidate();\r
+ }\r
+ if ((_timer_periodic_2 != NULL) && _timer_periodic_2->valid())\r
+ {\r
+ _timer_periodic_2->invalidate();\r
+ }\r
+\r
+ // lock\r
+ RaymLock(this);\r
+\r
+ // タイマ解放\r
+ RELEASE(_timer_restart);\r
+ RELEASE(_timer_epg_s);\r
+ RELEASE(_timer_epg_t);\r
+ RELEASE(_timer_periodic);\r
+ RELEASE(_timer_periodic_2);\r
+\r
+ // スケジュール更新\r
+ updateSchedule();\r
+\r
+ // 未初期化に設定\r
+ _initialized = false;\r
+\r
+ // チューナ解放\r
+ for (int i = 0; i < _tunerCount; ++i)\r
+ {\r
+ if (_tuners[i] != NULL)\r
+ {\r
+ delete _tuners[i];\r
+ _tuners[i] = NULL;\r
+ }\r
+ }\r
+\r
+ _cancel_epg_collect = false;\r
+\r
+ DebugLog0("system will suspend...");\r
+\r
+ // unlock\r
+ RaymUnlock(this);\r
+\r
+#ifdef OBJC_MEMORY_CHECK\r
+ DebugLog0("global_objc_count_ = %d", Raym::global_objc_count_);\r
+#endif\r
+\r
+ DebugLog2("Controller::systemWillSuspend() end");\r
+}\r
+\r
+/*\r
+ * TrayApp::WndProc() からコールされる\r
+ *\r
+ * スレッドコンテキスト:メインスレッド\r
+ */\r
+void Controller::systemResumed()\r
+{\r
+ DebugLog2("Controller::systemResumed() start");\r
+\r
+ DebugLog0("system resumed.");\r
+\r
+ if (_timer_restart == NULL)\r
+ {\r
+ // 再開タイマ起動\r
+ _timer_restart = Timer::alloc()->initWithTimeInterval(1.0, this, (void *)CMD_RESTART, false);\r
+ _timer_restart->fire();\r
+ }\r
+\r
+ DebugLog2("Controller::systemResumed()");\r
+}\r
+\r
+/*\r
+ * TrayApp::WndProc() からコールされる\r
+ *\r
+ * スレッドコンテキスト:メインスレッド\r
+ */\r
+void Controller::detectIdle()\r
+{\r
+ DebugLog2("Controller::detectIdle()");\r
+\r
+ // ここはメインスレッドなのでARPを用意する\r
+ AutoreleasePool *pool = AutoreleasePool::alloc()->init();\r
+\r
+ // lock\r
+ RaymLock(this);\r
+\r
+ // サスペンド可能か確認\r
+ if (canSuspend())\r
+ {\r
+ if (_idle_count == 0)\r
+ {\r
+ DebugLog0("detect idle...");\r
+ }\r
+\r
+ // アイドルカウンタを更新\r
+ ++_idle_count;\r
+\r
+ // 起動中アプリと休止状態抑止アプリのチェック\r
+ bool found = false;\r
+\r
+ Array *dont_in_suspend = _props->arrayForKey(KEY_DO_NOT_IN_SUSPEND);\r
+ Array *running_apps = Workspace::sharedWorkspace()->runningApplications();\r
+ for (uint i = 0; (i < running_apps->count()) && !found; ++i)\r
+ {\r
+ RunningApplication *ra = (RunningApplication *)running_apps->objectAtIndex(i);\r
+\r
+ // 実行中でなければ次へ\r
+ if (!ra->isRunning())\r
+ {\r
+ continue;\r
+ }\r
+\r
+ // 実行ファイルのチェック\r
+ String *path = ra->executePath();\r
+ if ((path == NULL) || (path->length() == 0))\r
+ {\r
+ continue;\r
+ }\r
+ DebugLog3("exec path: %s", path->cString());\r
+\r
+ // 休止状態抑止アプリリストのチェック\r
+ for (uint j = 0; (j < dont_in_suspend->count()) && !found; ++j)\r
+ {\r
+ found = path->isMatch((String *)dont_in_suspend->objectAtIndex(j));\r
+ }\r
+ }\r
+\r
+ // 抑止有効なら KEY_FORCED_SUSPEND_TIME、無効なら KEY_SUSPEND_TIME で取得\r
+ int limit = _props->integerForKey(found ? KEY_FORCED_SUSPEND_TIME : KEY_SUSPEND_TIME);\r
+ DebugLog3("found: %d, _idle_count: %d, limit: %d", found, _idle_count, limit);\r
+ if (_idle_count >= limit)\r
+ {\r
+ // unlock\r
+ RaymUnlock(this);\r
+\r
+ // サスペンド前にARPを解放しておく\r
+ pool->release();\r
+\r
+ // 下記の TrayApp::suspend() を使用する場合、自アプリへsuspendメッセージが\r
+ // ブロードキャストされないため、自分でコールしておく\r
+ systemWillSuspend();\r
+\r
+ // サスペンド\r
+ suspend();\r
+\r
+ // 再度 ARPを用意\r
+ pool = AutoreleasePool::alloc()->init();\r
+\r
+ // lock\r
+ RaymLock(this);\r
\r
- *time_var = mktime(&now_tm);\r
+ // アイドルカウンタをクリア\r
+ _idle_count = 0;\r
}\r
}\r
+ else\r
+ {\r
+ // アイドルカウンタをクリア\r
+ _idle_count = 0;\r
+ }\r
+\r
+ // unlock\r
+ RaymUnlock(this);\r
+\r
+ // ARP解放\r
+ pool->release();\r
}\r
\r
-void Controller::getTimeWithEPG(Dictionary *epg, time_t *start, time_t *end)\r
+void Controller::detectNonIdle()\r
{\r
- if ((epg == NULL) || (start == NULL) || (end == NULL))\r
- {\r
- return;\r
- }\r
- String *date = epg->stringForKey(KEY_EPG_DATE);\r
- String *st = epg->stringForKey(KEY_EPG_START);\r
- String *ed = epg->stringForKey(KEY_EPG_END);\r
- if ((date == NULL) || (st == NULL) || (ed == NULL))\r
- {\r
- return;\r
- }\r
+ DebugLog2("Controller::detectNonIdle()");\r
\r
- std::string dateStr = date->cString();\r
- std::string stStr = st->cString();\r
- std::string edStr = ed->cString();\r
- TM tm_start;\r
- tm_start.tm_year = atoi(dateStr.substr(0, 4).c_str()) - 1900;\r
- tm_start.tm_mon = atoi(dateStr.substr(5, 2).c_str()) - 1;\r
- tm_start.tm_mday = atoi(dateStr.substr(8, 2).c_str());\r
- tm_start.tm_hour = atoi(stStr.substr(0, 2).c_str());\r
- tm_start.tm_min = atoi(stStr.substr(3, 2).c_str());\r
- tm_start.tm_sec = atoi(stStr.substr(6, 2).c_str());\r
+ // lock\r
+ RaymLock(this);\r
\r
- TM tm_end;\r
- tm_end.tm_year = atoi(dateStr.substr(0, 4).c_str()) - 1900;\r
- tm_end.tm_mon = atoi(dateStr.substr(5, 2).c_str()) - 1;\r
- tm_end.tm_mday = atoi(dateStr.substr(8, 2).c_str());\r
- tm_end.tm_hour = atoi(edStr.substr(0, 2).c_str());\r
- tm_end.tm_min = atoi(edStr.substr(3, 2).c_str());\r
- tm_end.tm_sec = atoi(edStr.substr(6, 2).c_str());\r
- if (stStr > edStr)\r
+ if (_idle_count > 0)\r
{\r
- tm_end.tm_mday += 1;\r
+ DebugLog0("detect non idle...");\r
}\r
- *start = mktime(&tm_start);\r
- *end = mktime(&tm_end);\r
-}\r
+ _idle_count = 0;\r
\r
+ // unlock\r
+ RaymUnlock(this);\r
+}\r
\r
int Controller::restart()\r
{\r
+ // lock\r
RaymLock(this);\r
\r
// 未初期化に設定\r
_initialized = false;\r
\r
// チューナ解放\r
- for (int i = 0; i < _tuner_count; ++i)\r
+ for (int i = 0; i < _tunerCount; ++i)\r
{\r
if (_tuners[i] != NULL)\r
{\r
\r
\r
// チューナ初期化\r
- _tuner_count = ry0::device::TunerFactory::scan(_tuners, _multi2_dll);\r
+ _tunerCount = ry0::device::TunerFactory::scan(_tuners, _multi2_dll);\r
\r
// unlock\r
RaymUnlock(this);\r
\r
// チューナでループ\r
- for (int i = 0; i < _tuner_count; ++i)\r
+ for (int i = 0; i < _tunerCount; ++i)\r
{\r
// チューナが初期化済みか\r
if (!isTunerInitialized(i))\r
}\r
}\r
\r
- DebugLog0("");\r
- DebugLog0("UDP Port Mapping");\r
- DebugLog0(" Tuner, CH -> Port");\r
+ // UDPポートマッピング\r
+ DebugLog0(" UDP Port Mapping :");\r
+ DebugLog0(" tuner, ch -> port");\r
int udpport = _props->integerForKey(KEY_BEGIN_UDP_PORT);\r
- for (int tuner = 0; tuner < _tuner_count; ++tuner)\r
+ for (int tuner = 0; tuner < _tunerCount; ++tuner)\r
{\r
- int max_channel = (_tuners[tuner]->type() == Tuner::ISDB_S ? Tuner::MAX_CHANNELS_ISDB_S : Tuner::MAX_CHANNELS_ISDB_T);\r
- for (int ch = 0; ch <= max_channel; ++ch)\r
+ uint max_channel = (_tuners[tuner]->type() == Tuner::ISDB_S ? Tuner::MAX_CHANNELS_ISDB_S : Tuner::MAX_CHANNELS_ISDB_T);\r
+ for (uint ch = 0; ch <= max_channel; ++ch)\r
{\r
if (isChannelEnabled(tuner, ch))\r
{\r
- DebugLog0(" %5d,%3d -> %5d", tuner, ch, udpport);\r
- _streaming->mapping(tuner, ch, udpport++);\r
+ DebugLog0(" %5d,%3d -> %5d", tuner, ch, udpport);\r
+\r
+ Dictionary *udp_to_tuner_channel = _streaming_ctrls->dictionaryForKey(KEY_MAPPING_UDP_TO_TUNER_CHANNEL);\r
+ if (udp_to_tuner_channel == NULL)\r
+ {\r
+ udp_to_tuner_channel = Dictionary::dictionaryWithCapacity(0);\r
+ _streaming_ctrls->setObject(udp_to_tuner_channel, KEY_MAPPING_UDP_TO_TUNER_CHANNEL);\r
+ }\r
+\r
+ Dictionary *tuner_channel_to_udp = _streaming_ctrls->dictionaryForKey(KEY_MAPPING_TUNER_CHANNEL_TO_UDP);\r
+ if (tuner_channel_to_udp == NULL)\r
+ {\r
+ tuner_channel_to_udp = Dictionary::dictionaryWithCapacity(0);\r
+ _streaming_ctrls->setObject(tuner_channel_to_udp, KEY_MAPPING_TUNER_CHANNEL_TO_UDP);\r
+ }\r
+\r
+ char port_str[10];\r
+ sprintf_s(port_str, "%d", udpport);\r
+ char tuner_and_channel[10];\r
+ sprintf_s(tuner_and_channel, "%d,%d", tuner, ch);\r
+\r
+ udp_to_tuner_channel->setString(tuner_and_channel, port_str);\r
+ tuner_channel_to_udp->setString(port_str, tuner_and_channel);\r
+\r
+ ++udpport;\r
}\r
}\r
}\r
\r
- _reservation->systemResumed();\r
+ // 周期タイマ起動\r
+ _timer_periodic = Timer::alloc()->initWithTimeInterval(1.0, this, (void *)CMD_PERIODIC, true);\r
+ _timer_periodic->fire();\r
\r
+ // 周期タイマ2起動\r
+ _timer_periodic_2 = Timer::alloc()->initWithTimeInterval(1.0, this, (void *)CMD_PERIODIC_2, true);\r
+ _timer_periodic_2->fire();\r
+\r
+ // lock\r
RaymLock(this);\r
+\r
+ // 初期化済みに設定\r
_initialized = true;\r
+\r
+ // unlock\r
RaymUnlock(this);\r
\r
- return (_tuner_count > 0);\r
+ return (_tunerCount > 0);\r
}\r
\r
int Controller::start()\r
// 設定以前にログ出力しないこと\r
Raym::LOG_NUM_MAX = 8;\r
\r
- // 変数初期化\r
+#ifdef RAYM_MEMORY_CHECK\r
+ DebugLog0("");\r
+ DebugLog0("global_raym_count_ = %d", Raym::global_raym_count_);\r
+#endif\r
+\r
+ //\r
+ AutoreleasePool *pool = AutoreleasePool::alloc()->init();\r
+\r
+ // メンバ初期化\r
_system_path = NULL;\r
_props_path = NULL;\r
_props = NULL;\r
_status_path = NULL;\r
_status = NULL;\r
-\r
+ _epgs_path = NULL;\r
+ _epgs = NULL;\r
+ _store_path = NULL;\r
+ _reservations = NULL;\r
+ _reservations_path = NULL;\r
_timer_restart = NULL;\r
-\r
+ _timer_periodic = NULL;\r
+ _timer_periodic_2 = NULL;\r
+ _timer_epg_s = NULL;\r
+ _timer_epg_t = NULL;\r
_multi2_dll = NULL;\r
+ _streaming_ctrls = NULL;\r
\r
_idle_count = 0;\r
-\r
_initialized = false;\r
+ _reservation_seq_id = 0;\r
+ _cancel_epg_collect = false;\r
\r
- _reservation = NULL;\r
- _streaming = NULL;\r
- _httpd = NULL;\r
-\r
- _tuner_count = 0;\r
+ _tunerCount = 0;\r
for (int i = 0; i < ry0::device::MAX_TUNERS; ++i)\r
{\r
_tuners[i] = NULL;\r
DebugLog0("iPTd ver %s (rev.%d)", VERSION, REVISION);\r
DebugLog0("initialize...");\r
\r
- HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());\r
- if (hProcess != NULL)\r
- {\r
- if (SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS) == FALSE)\r
- {\r
- DebugLog0("SetPriorityClass failed.");\r
- }\r
- CloseHandle(hProcess);\r
- }\r
-\r
int result = 0;\r
\r
// 初期化\r
}\r
_system_path = _system_path->stringByReplacingOccurrencesOfString("iPTd.exe", "");\r
_system_path->retain();\r
- DebugLog1("system path: %s", _system_path->cString());\r
+ DebugLog2("_system_path: %s", _system_path->cString());\r
\r
// プロパティファイルのパス設定\r
- _props_path = String::alloc()->initWithFormat("%s%s.iptd.plist", _system_path->cString(), _plist_prefix);\r
+ _props_path = String::alloc()->initWithFormat("%s%s.iptd.plist", _system_path->cString(), PLIST_PREFIX);\r
if (_props_path == NULL)\r
{\r
DebugLog0("error: set property file path.");\r
}\r
\r
// ステータスファイルのパス設定\r
- _status_path = String::alloc()->initWithFormat("%s%s.iptd.status.plist", _system_path->cString(), _plist_prefix);\r
+ _status_path = String::alloc()->initWithFormat("%s%s.iptd.status.plist", _system_path->cString(), PLIST_PREFIX);\r
if (_status_path == NULL)\r
{\r
DebugLog0("error: set status file path.");\r
}\r
\r
// 録画データ格納先の確認\r
- if (_props->stringForKey(KEY_STORE_PATH) == NULL)\r
+ _store_path = _props->stringForKey(KEY_STORE_PATH);\r
+ if (_store_path == NULL)\r
{\r
// プロパティに未設定の場合\r
// <Public Directory>/Videos を設定\r
result = -1;\r
break;\r
}\r
- _props->setString(String::stringWithFormat("%s\\Videos", public_dir), KEY_STORE_PATH);\r
+ _store_path = String::alloc()->initWithFormat("%s\\Videos", public_dir);\r
+ _props->setString(_store_path, KEY_STORE_PATH);\r
\r
// 更新フラグ\r
updated = true;\r
}\r
+ else\r
+ {\r
+ _store_path->retain();\r
+ }\r
\r
// 実際にディレクトリが存在しているか確認\r
FileManager *fm = FileManager::defaultManager();\r
bool isDir = false;\r
- if (!fm->fileExistsAtPath(_props->stringForKey(KEY_STORE_PATH), &isDir))\r
+ if (!fm->fileExistsAtPath(_store_path, &isDir))\r
{\r
isDir = false;\r
}\r
if (!isDir)\r
{\r
- DebugLog0("error: \"%s\" is not exists.", _props->stringForKey(KEY_STORE_PATH)->cString());\r
+ DebugLog0("error: \"%s\" is not exists.", _store_path->cString());\r
result = -1;\r
break;\r
}\r
\r
- //\r
- _reservation = Reservation::alloc()->initWithController(this);\r
- if (_reservation == NULL)\r
+ // HLS Preset\r
+ if (_props->dictionaryForKey(KEY_PRESETS) == NULL)\r
{\r
- result = -1;\r
- break;\r
- }\r
+ Dictionary *presets = Dictionary::dictionaryWithCapacity(0);\r
+ _props->setObject(presets, KEY_PRESETS);\r
+\r
+// とりあえずこのまま\r
+// プリセットは、あとで実装。。。 \r
+ Array *preset;\r
+\r
+ // "default"\r
+ preset = STR_ARRAY("-vcodec", "libx264",\r
+ "-b:v", "768k",\r
+ "-s", "640x360",\r
+ "-acodec", "libfaac",\r
+ "-b:a", "96k",\r
+ "-ar", "44100",\r
+ "-flags", "+loop-global_header",\r
+ "-map", "0",\r
+ "-bsf", "h264_mp4toannexb",\r
+ "-f", "segment",\r
+ "-segment_format", "mpegts",\r
+ "-segment_time", "10",\r
+ NULL);\r
+ presets->setObject(preset, KEY_DEFAULT);\r
\r
- // httpdのルートパス\r
- String *rootPath = _system_path->stringByAppendingPathComponent("html");\r
- isDir = false;\r
- if (!fm->fileExistsAtPath(rootPath, &isDir))\r
- {\r
- isDir = false;\r
+ updated = true;\r
}\r
- if (!isDir)\r
+\r
+ // キャッシュ(HLSの一時ファイル格納先)パス\r
+ if (_props->stringForKey(KEY_CACHE_PATH) == NULL)\r
{\r
-/*\r
- DebugLog0("error: \"%s\" is not exists.", rootPath->cString());\r
- result = -1;\r
- break;\r
-*/\r
+ _props->setString(_store_path, KEY_CACHE_PATH);\r
+ updated = true;\r
}\r
\r
// プロパティファイルを保存\r
}\r
}\r
\r
+ // 番組データファイルのパス設定\r
+ _epgs_path = String::alloc()->initWithFormat("%s%s.iptd.epgs.plist", _system_path->cString(), PLIST_PREFIX);\r
+ if (_epgs_path == NULL)\r
+ {\r
+ DebugLog0("error: set epgs file path.");\r
+ result = -1;\r
+ break;\r
+ }\r
+\r
+ // 番組データの読み込み\r
+ _epgs = Dictionary::alloc()->initWithContentsOfFile(_epgs_path);\r
+ if (_epgs == NULL)\r
+ {\r
+ DebugLog1("epgs file: %s (created)", _epgs_path->cString());\r
+ _epgs = Dictionary::alloc()->initWithCapacity(0);\r
+ }\r
+ else\r
+ {\r
+ DebugLog1("epgs file: %s", _epgs_path->cString());\r
+\r
+ // 過去の番組データは削除\r
+ removePastEPGs();\r
+ }\r
\r
- //\r
- _streaming = Streaming::alloc()->initWithController(this);\r
- if (_streaming == NULL)\r
+ // 予約データファイルのパス設定\r
+ _reservations_path = String::alloc()->initWithFormat("%s%s.iptd.reservations.plist", _system_path->cString(), PLIST_PREFIX);\r
+ if (_reservations_path == NULL)\r
{\r
+ DebugLog0("error: set reservations file path.");\r
result = -1;\r
break;\r
}\r
\r
- //\r
- _httpd = HTTPD::alloc()->initWithController(this, _props->integerForKey(KEY_HTTP_PORT), rootPath);\r
- if (_httpd == NULL)\r
+ // 予約データの読み込み\r
+ _reservations = Dictionary::alloc()->initWithContentsOfFile(_reservations_path);\r
+ if (_reservations == NULL)\r
+ {\r
+ DebugLog1("reservations file: %s (created)", _reservations_path->cString());\r
+ _reservations = Dictionary::alloc()->initWithCapacity(0);\r
+ }\r
+ else\r
+ {\r
+ DebugLog1("reservations file: %s", _reservations_path->cString());\r
+\r
+ // 予約情報シーケンスID\r
+ _reservation_seq_id = _reservations->integerForKey(KEY_EPG_LAST_RESV_ID);\r
+ }\r
+\r
+ // ストリーミング制御情報格納用\r
+ _streaming_ctrls = Dictionary::alloc()->initWithCapacity(0);\r
+\r
+ // httpdのルートパス\r
+ String *rootPath = _system_path->stringByAppendingPathComponent("iptd_html");\r
+ if (!fm->fileExistsAtPath(rootPath, &isDir))\r
+ {\r
+ isDir = false;\r
+ }\r
+ if (!isDir)\r
{\r
+ DebugLog0("error: \"%s\" is not exists.", rootPath->cString());\r
result = -1;\r
break;\r
}\r
\r
+ // httpd開始\r
+ int port = _props->integerForKey(KEY_HTTP_PORT);\r
+ _httpd = NET::HTTPDaemon::alloc()->initWithPort(port, 10);\r
+ _httpd->setRootPath(rootPath);\r
+ _httpd->setDelegate(this);\r
if (!_httpd->start())\r
{\r
DebugLog0("Can't start httpd.");\r
_timer_restart = Timer::alloc()->initWithTimeInterval(1.0, this, (void *)CMD_RESTART, false);\r
_timer_restart->fire();\r
\r
- // 初期化完了\r
break;\r
}\r
\r
_httpd->stop();\r
}\r
\r
+ RaymLock(this);\r
+ _cancel_epg_collect = true;\r
+ RaymUnlock(this);\r
\r
+ // タイマ停止\r
if ((_timer_restart != NULL) && _timer_restart->valid())\r
{\r
_timer_restart->invalidate();\r
}\r
+ if ((_timer_epg_s != NULL) && _timer_epg_s->valid())\r
+ {\r
+ _timer_epg_s->invalidate();\r
+ }\r
+ if ((_timer_epg_t != NULL) && _timer_epg_t->valid())\r
+ {\r
+ _timer_epg_t->invalidate();\r
+ }\r
+ if ((_timer_periodic != NULL) && _timer_periodic->valid())\r
+ {\r
+ _timer_periodic->invalidate();\r
+ }\r
+ if ((_timer_periodic_2 != NULL) && _timer_periodic_2->valid())\r
+ {\r
+ _timer_periodic_2->invalidate();\r
+ }\r
\r
- _reservation->systemWillSuspend();\r
- _streaming->systemWillSuspend();\r
-\r
- for (int i = 0; i < _tuner_count; ++i)\r
+ // チューナ解放\r
+ for (int i = 0; i < _tunerCount; ++i)\r
{\r
if (_tuners[i] != NULL)\r
{\r
}\r
}\r
\r
+ // スケジュールリセット\r
resetWakeSchedule();\r
\r
+ // 解放\r
RELEASE(_timer_restart);\r
+ RELEASE(_timer_epg_s);\r
+ RELEASE(_timer_epg_t);\r
+ RELEASE(_timer_periodic);\r
+ RELEASE(_timer_periodic_2);\r
+ RELEASE(_streaming_ctrls);\r
+ RELEASE(_httpd);\r
RELEASE(_system_path);\r
RELEASE(_props_path);\r
RELEASE(_props);\r
RELEASE(_status_path);\r
RELEASE(_status);\r
-\r
- RELEASE(_reservation);\r
- RELEASE(_streaming);\r
- RELEASE(_httpd);\r
+ RELEASE(_epgs_path);\r
+ RELEASE(_epgs);\r
+ RELEASE(_store_path);\r
+ RELEASE(_reservations_path);\r
+ RELEASE(_reservations);\r
\r
if (_multi2_dll != NULL)\r
{\r
FreeLibrary(_multi2_dll);\r
}\r
\r
+ pool->release();\r
+\r
+ // 終了\r
DebugLog0("finished.");\r
\r
+#ifdef RAYM_MEMORY_CHECK\r
+ DebugLog0("global_raym_count_ = %d", Raym::global_raym_count_);\r
+#endif\r
+\r
return result;\r
}\r
\r
+#ifndef _WIN32\r
+#pragma mark '\r
+#pragma mark ------- コンストラクタ/デストラクタ -------\r
+#endif\r
+\r
+Controller::Controller()\r
+{\r
+}\r
+\r
+Controller::~Controller()\r
+{\r
+}\r
+\r
+Controller *Controller::alloc()\r
+{\r
+ return new Controller();\r
+}\r
+\r
+#ifndef _WIN32\r
+#pragma mark '\r
+#pragma mark ------- その他 -------\r
+#endif\r
+\r
+//\r
+// HH:MM:SS 形式の文字列から time_t に変換\r
+// 現在時刻が指定文字列の時刻以前の場合、当日の時刻。指定文字列の時刻よりも過ぎている場合、翌日の時刻を返す。\r
+//\r
+void Controller::getTimeWithString(String *str, time_t *time_var)\r
+{\r
+ if ((str != NULL) && str->isMatch(String::stringWithUTF8String("^\\d\\d:\\d\\d:\\d\\d$")) && (time_var != NULL))\r
+ {\r
+ // 時:分:秒 を int型に分解\r
+ std::string time_str = str->cString();\r
+ int hour = atoi(time_str.substr(0, 2).c_str());\r
+ int min = atoi(time_str.substr(3, 2).c_str());\r
+ int sec = atoi(time_str.substr(6, 2).c_str());\r
+ DebugLog2("%02d:%02d:%02d", hour, min, sec);\r
+\r
+ // 現在時刻取得\r
+ time_t now = time(NULL);\r
+ TM now_tm;\r
+ if (localtime_s(&now_tm, &now) == 0)\r
+ {\r
+ int now_sec = now_tm.tm_hour * 3600 + now_tm.tm_min * 60 + now_tm.tm_sec;\r
+ int col_sec = hour * 3600 + min * 3600 + sec;\r
+ if (now_sec > col_sec)\r
+ {\r
+ ++now_tm.tm_mday;\r
+ }\r
+ now_tm.tm_hour = hour;\r
+ now_tm.tm_min = min;\r
+ now_tm.tm_sec = sec;\r
+\r
+ *time_var = mktime(&now_tm);\r
+ }\r
+ }\r
+}\r
+\r
+void Controller::getTimeWithEPG(Dictionary *epg, time_t *start, time_t *end)\r
+{\r
+ if ((epg == NULL) || (start == NULL) || (end == NULL))\r
+ {\r
+ return;\r
+ }\r
+ String *date = epg->stringForKey(KEY_EPG_DATE);\r
+ String *st = epg->stringForKey(KEY_EPG_START);\r
+ String *ed = epg->stringForKey(KEY_EPG_END);\r
+ if ((date == NULL) || (st == NULL) || (ed == NULL))\r
+ {\r
+ return;\r
+ }\r
+\r
+ std::string dateStr = date->cString();\r
+ std::string stStr = st->cString();\r
+ std::string edStr = ed->cString();\r
+ TM tm_start;\r
+ tm_start.tm_year = atoi(dateStr.substr(0, 4).c_str()) - 1900;\r
+ tm_start.tm_mon = atoi(dateStr.substr(5, 2).c_str()) - 1;\r
+ tm_start.tm_mday = atoi(dateStr.substr(8, 2).c_str());\r
+ tm_start.tm_hour = atoi(stStr.substr(0, 2).c_str());\r
+ tm_start.tm_min = atoi(stStr.substr(3, 2).c_str());\r
+ tm_start.tm_sec = atoi(stStr.substr(6, 2).c_str());\r
+\r
+ TM tm_end;\r
+ tm_end.tm_year = atoi(dateStr.substr(0, 4).c_str()) - 1900;\r
+ tm_end.tm_mon = atoi(dateStr.substr(5, 2).c_str()) - 1;\r
+ tm_end.tm_mday = atoi(dateStr.substr(8, 2).c_str());\r
+ tm_end.tm_hour = atoi(edStr.substr(0, 2).c_str());\r
+ tm_end.tm_min = atoi(edStr.substr(3, 2).c_str());\r
+ tm_end.tm_sec = atoi(edStr.substr(6, 2).c_str());\r
+ if (stStr > edStr)\r
+ {\r
+ tm_end.tm_mday += 1;\r
+ }\r
+ *start = mktime(&tm_start);\r
+ *end = mktime(&tm_end);\r
+}\r
+\r
} // iPTd\r
} // ry0\r
+\r