+++ /dev/null
-/**\r
- * @file Reservation.cpp\r
- *\r
- */\r
-\r
-#include <time.h>\r
-#include <fcntl.h>\r
-#include <io.h>\r
-#include <direct.h>\r
-\r
-\r
-#define DBG_LEVEL 3\r
-#include "Raym/Log.h"\r
-\r
-#include "keys.h"\r
-#include "mpeg2/ts/Analyzer.h"\r
-#include "ry0/iPTd/Reservation.h"\r
-#include "ry0/iPTd/Controller.h"\r
-\r
-using namespace Raym;\r
-using namespace ry0::device;\r
-\r
-namespace ry0\r
-{\r
-namespace iPTd\r
-{\r
-\r
-static const long long CMD_PERIODIC = 0x0001; // 周期処理\r
-static const long long CMD_COLLECT_EPG_ISDB_S = 0x0002; // 番組情報取得(ISDB-S)\r
-static const long long CMD_COLLECT_EPG_ISDB_T = 0x0003; // 番組情報取得(ISDB-T)\r
-\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
-static const TimeInterval DEF_COLLECT_EPG_DELAY = 1.0;\r
-static const TimeInterval DEF_COLLECT_EPG_RETRY = 60.0;\r
-static const TimeInterval DEF_COLLECT_EPG_LIMIT_S = 10.5;\r
-static const TimeInterval DEF_COLLECT_EPG_LIMIT_T = 20.5;\r
-\r
-Reservation::Reservation()\r
-{\r
- _controller = NULL;\r
- _tuners = NULL;\r
- _reservations_path = NULL;\r
- _reservations = NULL;\r
- _reservation_seq_id = -1;\r
- _epgs_path = NULL;\r
- _epgs = NULL;\r
- _timer_periodic = NULL;\r
- _timer_epg_s = NULL;\r
- _timer_epg_t = NULL;\r
- _cancel_epg_collect = false;\r
-}\r
-\r
-Reservation::~Reservation()\r
-{\r
- RELEASE(_reservations_path);\r
- RELEASE(_reservations);\r
- RELEASE(_epgs_path);\r
- RELEASE(_epgs);\r
- RELEASE(_timer_periodic);\r
- RELEASE(_timer_epg_s);\r
- RELEASE(_timer_epg_t);\r
-}\r
-\r
-Reservation *Reservation::alloc()\r
-{\r
- return new Reservation();\r
-}\r
-\r
-Reservation *Reservation::initWithController(Controller *controller)\r
-{\r
- _controller = controller;\r
- _tuners = controller->_tuners;\r
-\r
- _reservations_path = String::alloc()->initWithFormat("%s%s.iptd.reservations.plist", _controller->_system_path->cString(), Controller::_plist_prefix);\r
- if (_reservations_path == NULL)\r
- {\r
- release();\r
- return NULL;\r
- }\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
- if (_reservations == NULL)\r
- {\r
- release();\r
- return NULL;\r
- }\r
- _reservation_seq_id = 1;\r
- }\r
- else\r
- {\r
- DebugLog1("reservations file: %s", _reservations_path->cString());\r
-\r
- _reservation_seq_id = _reservations->integerForKey(KEY_EPG_LAST_RESV_ID);\r
- }\r
-\r
- _epgs_path = String::alloc()->initWithFormat("%s%s.iptd.epgs.plist", _controller->_system_path->cString(), Controller::_plist_prefix);\r
- if (_epgs_path == NULL)\r
- {\r
- release();\r
- return NULL;\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
- if (_epgs == NULL)\r
- {\r
- release();\r
- return NULL;\r
- }\r
- }\r
- else\r
- {\r
- DebugLog1("epgs file: %s", _epgs_path->cString());\r
-\r
- // 過去の番組データは削除\r
- removePastEPGs();\r
- }\r
-\r
- return this;\r
-}\r
-\r
-bool Reservation::canSuspend()\r
-{\r
- bool result = true;\r
-\r
- RaymLock(_controller);\r
- if (((_timer_epg_s != NULL) && _timer_epg_s->valid()) ||\r
- ((_timer_epg_t != NULL) && _timer_epg_t->valid()))\r
- {\r
- result = false;\r
- }\r
- RaymUnlock(_controller);\r
-\r
- if (result)\r
- {\r
- result = canTerminate();\r
- }\r
-\r
- return result;\r
-}\r
-\r
-bool Reservation::canTerminate()\r
-{\r
- bool result = true;\r
-\r
- RaymLock(_controller);\r
-\r
- time_t now = time(NULL);\r
-\r
- for (int tuner = 0; tuner < _controller->_tuner_count; ++tuner)\r
- {\r
- Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
- if ((array == NULL) || (array->count() == 0))\r
- {\r
- continue;\r
- }\r
-\r
- Dictionary *epg = (Dictionary *)array->objectAtIndex(0);\r
- time_t start;\r
- time_t end;\r
- Controller::getTimeWithEPG(epg, &start, &end);\r
-\r
- if (start + OFFSET_OF_SUPPRESSION_TIME <= now)\r
- {\r
- result = false;\r
- break;\r
- }\r
- }\r
-\r
- RaymUnlock(_controller);\r
-\r
- return result;\r
-}\r
-\r
-void Reservation::systemWillSuspend()\r
-{\r
- RaymLock(_controller);\r
- _cancel_epg_collect = true;\r
- RaymUnlock(_controller);\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
-\r
- RaymLock(_controller);\r
- _cancel_epg_collect = false;\r
- RaymUnlock(_controller);\r
-}\r
-\r
-void Reservation::systemResumed()\r
-{\r
- DebugLog2("%s", __FUNCTION__);\r
-\r
- RaymLock(_controller);\r
-\r
- _cancel_epg_collect = false;\r
-\r
- if (_timer_periodic == NULL)\r
- {\r
- _timer_periodic = Timer::alloc()->initWithTimeInterval(1.0, this, (void *)CMD_PERIODIC, true);\r
- if (_timer_periodic != NULL)\r
- {\r
- _timer_periodic->fire();\r
- }\r
- }\r
-\r
- if (_timer_epg_s == NULL)\r
- {\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
- if (_timer_epg_t == NULL)\r
- {\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
- RaymUnlock(_controller);\r
-}\r
-\r
-//\r
-// 録画予約:サービスID/イベントID指定\r
-// EPGデータから指定のサービスID/イベントIDのEPGを取り出し、EPG指定の録画予約をコール\r
-//\r
-bool Reservation::reserve(int service_id, int event_id)\r
-{\r
- DebugLog2("Reservation::reserve(service_id, event_id)");\r
-\r
- bool result = false;\r
-\r
- RaymLock(_controller);\r
-\r
- if (_epgs != NULL)\r
- {\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
-\r
- RaymUnlock(_controller);\r
-\r
- return result;\r
-}\r
-\r
-//\r
-// 録画予約:EPG指定\r
-// EPGからサービスIDを取り出し、チューナ情報を検索して一致するサービスIDがあったらチューナ/EPG指定の録画予約を試行\r
-//\r
-bool Reservation::reserve(Dictionary *in_epg)\r
-{\r
- DebugLog2("Reservation::reserve(epg)");\r
-\r
- bool result = false;\r
-\r
- RaymLock(_controller);\r
-\r
- while (in_epg != NULL)\r
- {\r
- // EPGを複製\r
- Dictionary *epg = Dictionary::dictionaryWithDictionary(in_epg);\r
- if (epg == NULL)\r
- {\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 = _controller->_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 < _controller->_tuner_count); ++i)\r
- {\r
- // チューナ情報取得\r
- Dictionary *tunerInfo = tunerInfos->dictionaryForKey(_tuners[i]->name());\r
- if (tunerInfo == NULL)\r
- {\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
- RaymUnlock(_controller);\r
-\r
- return result;\r
-}\r
-\r
-//\r
-// 録画予約:チューナ/EPG指定\r
-//\r
-bool Reservation::reserve(int tuner, Dictionary *in_epg)\r
-{\r
- DebugLog2("Reservation::reserve(tuner, epg)");\r
-\r
- bool result = false;\r
-\r
- // lock\r
- RaymLock(_controller);\r
-\r
- while ((0 <= tuner) && (tuner < _controller->_tuner_count) && (in_epg != NULL))\r
- {\r
- Dictionary *epg = Dictionary::dictionaryWithDictionary(in_epg);\r
- if (epg == NULL)\r
- {\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
- Controller::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
- Controller::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
- 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(_controller);\r
-\r
- return result;\r
-}\r
-\r
-//\r
-// tuner: 0 - (_tunerCount - 1) cancel current\r
-// tuner: -1 cancel reserve_id\r
-//\r
-bool Reservation::cancel(int tuner, int reserve_id)\r
-{\r
- bool result = false;\r
-\r
- // lock\r
- RaymLock(_controller);\r
-\r
- //\r
- if ((0 <= tuner) && (tuner < _controller->_tuner_count))\r
- {\r
- Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
- if (array != NULL)\r
- {\r
- if (array->count() > 0)\r
- {\r
- Dictionary *epg = (Dictionary *)array->objectAtIndex(0);\r
- String *status = epg->stringForKey(KEY_EPG_STATUS);\r
- if (status != NULL)\r
- {\r
- if (status->isEqualToString("running"))\r
- {\r
- epg->setString("stop", KEY_EPG_STATUS);\r
- result = true;\r
- }\r
- }\r
- }\r
- }\r
- }\r
-\r
- //\r
- else if ((tuner < 0) && (0 <= reserve_id) && (reserve_id < 1000000))\r
- {\r
- for (int i = 0; i < _controller->_tuner_count; ++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
- if (result)\r
- {\r
- _reservations->writeToFile(_reservations_path, true);\r
- }\r
-\r
- // unlock\r
- RaymUnlock(_controller);\r
-\r
- return result;\r
-}\r
-\r
-void Reservation::updateSchedule()\r
-{\r
- DebugLog2("Reservation::updateSchedule()");\r
-\r
- RaymLock(_controller);\r
-\r
- // レジューム時刻\r
- time_t resume_time = 0;\r
-\r
- // EPG収集時刻を取得\r
- String *collect_epg_time = _controller->_props->stringForKey(KEY_COLLECT_EPG_TIME);\r
- if (collect_epg_time != NULL)\r
- {\r
- Controller::getTimeWithString(collect_epg_time, &resume_time);\r
- }\r
-\r
- // 録画予約をチェック\r
- for (int tuner = 0; tuner < _controller->_tuner_count; ++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
- Controller::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
- _controller->resetWakeSchedule();\r
- if (_controller->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(_controller);\r
-}\r
-\r
-std::string Reservation::createVideoPath(int tuner)\r
-{\r
- DebugLog2("Reservation::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 = _controller->_props->stringForKey(KEY_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
-void Reservation::removePastEPGs()\r
-{\r
- DebugLog2("Reservation::removePastEPGs()");\r
-\r
- time_t now = time(NULL);\r
-\r
- RaymLock(_controller);\r
-\r
- Array *keys1 = _epgs->allKeys();\r
- for (uint i = 0; i < keys1->count(); ++i)\r
- {\r
- Dictionary *events = _epgs->dictionaryForKey((String *)keys1->objectAtIndex(i));\r
- if (events != NULL)\r
- {\r
- Array *keys2 = events->allKeys();\r
- for (uint j = 0; j < keys2->count(); ++j)\r
- {\r
- String *key = (String *)keys2->objectAtIndex(j);\r
- Dictionary *epg = events->dictionaryForKey(key);\r
- if (epg != NULL)\r
- {\r
- time_t start = 0;\r
- time_t end = 0;\r
- Controller::getTimeWithEPG(epg, &start, &end);\r
-\r
- if (now > end)\r
- {\r
- events->removeObjectForKey(key);\r
- }\r
- }\r
- }\r
- }\r
- }\r
-\r
- RaymUnlock(_controller);\r
-}\r
-\r
-void Reservation::collectEPGsForTuner(int tuner, TimeInterval limit)\r
-{\r
- DebugLog2("Reservation::collectEPGsForTuner(%d) start.", tuner);\r
-\r
- // 既にロックされた状態でコールされる前提\r
- bool locked = false;\r
- RaymLock(_controller);\r
- if ((0 <= tuner) && (tuner < _controller->_tuner_count))\r
- {\r
- locked = _tuners[tuner]->isLocked();\r
- }\r
- RaymUnlock(_controller);\r
- if (!locked)\r
- {\r
- DebugLog2("Reservation::collectEPGsForTuner(%d) end(no locked).", tuner);\r
- return;\r
- }\r
-\r
- // \r
- Array *collected = Array::arrayWithCapacity(0);\r
-\r
- // 開始時刻取得\r
- Date *start = Date::date();\r
-\r
- // 取得開始\r
- bool done = false;\r
- while (!done)\r
- {\r
- AutoreleasePool *pool = AutoreleasePool::alloc()->init();\r
-\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
- if (epgs != NULL)\r
- {\r
- // 取得成功\r
- collected->addObjectsFromArray(epgs);\r
- }\r
-\r
- // リミットチェック\r
- if (Date::date()->timeIntervalSinceDate(start) >= limit)\r
- {\r
- done = true;\r
- }\r
-\r
- // キャンセル確認\r
- RaymLock(_controller);\r
- if (!done)\r
- {\r
- done = _cancel_epg_collect;\r
- }\r
- RaymUnlock(_controller);\r
-\r
- pool->release();\r
- }\r
-\r
- // lock\r
- RaymLock(_controller);\r
-\r
- for (uint j = 0; j < collected->count(); ++j)\r
- {\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
- // ファイルへ書き出し\r
- _epgs->writeToFile(_epgs_path, true);\r
-\r
- // unlock\r
- RaymUnlock(_controller);\r
-\r
- DebugLog2("Reservation::collectEPGsForTuner(%d) end.", tuner);\r
-}\r
-\r
-bool Reservation::collectEPGs(Tuner::Type type)\r
-{\r
- DebugLog0("Reservation::collectEPGs(%s) start.", type == Tuner::ISDB_S ? "ISDB-S" : "ISDB-T");\r
-\r
- // 使用するチューナを決定\r
- int tuner = 0;\r
- bool locked = false;\r
- RaymLock(_controller);\r
- for (int i = _controller->_tuner_count - 1; i >= 0; --i)\r
- {\r
- if (_controller->isTunerEnabled(i))\r
- {\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(_controller);\r
-\r
- // ロック確認\r
- if (!locked)\r
- {\r
- DebugLog0("Controller::collectEPGs(%s) end(Can't locked).", type == Tuner::ISDB_S ? "ISDB-S" : "ISDB-T");\r
- // ロックできない場合は収集しない\r
- return false;\r
- }\r
-\r
- bool canceled = false;\r
- DebugLog0("start collect EPG of \"%s(#%d)\".", _tuners[tuner]->name(), tuner);\r
-\r
- // 現在のチャンネルを保存\r
- int channel = _tuners[tuner]->channel();\r
-\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 (_controller->isChannelEnabled(tuner, ch))\r
- {\r
- // チャンネル設定\r
- if (_tuners[tuner]->setChannel(ch))\r
- {\r
- // EPG取集\r
- collectEPGsForTuner(tuner, ((type == Tuner::ISDB_S) ? DEF_COLLECT_EPG_LIMIT_S : DEF_COLLECT_EPG_LIMIT_T));\r
-\r
- RaymLock(_controller);\r
- if (_cancel_epg_collect)\r
- {\r
- ch = Tuner::MAX_CHANNELS_ISDB_T + 1;\r
- canceled = true;\r
- }\r
- RaymUnlock(_controller);\r
-\r
- // キャンセル確認\r
- // 終了不可 -> 録画待機中/録画中 なので、収集は諦める\r
- if (!canTerminate())\r
- {\r
- ch = Tuner::MAX_CHANNELS_ISDB_T + 1;\r
- canceled = true;\r
- }\r
- }\r
- }\r
- }\r
-\r
- // 元のチャンネルに戻す\r
- _tuners[tuner]->setChannel(channel);\r
-\r
- RaymLock(_controller);\r
-\r
- // チューナをアンロック\r
- _tuners[tuner]->unlock();\r
-\r
- RaymUnlock(_controller);\r
-\r
- if (canceled)\r
- {\r
- DebugLog0("collect EPG of \"%s\" was canceled.", _tuners[tuner]->name());\r
- }\r
- else\r
- {\r
- DebugLog0("collect EPG of \"%s\" was finished.", _tuners[tuner]->name());\r
-\r
- // 過去のEPGを削除\r
- removePastEPGs();\r
- }\r
-\r
- DebugLog2("Reservation::collectEPGs(%s) end.", type == Tuner::ISDB_S ? "ISDB-S" : "ISDB-T");\r
-\r
- return true;\r
-}\r
-\r
-void Reservation::periodic()\r
-{\r
- bool need_update = false;\r
-\r
-#ifdef RAYM_MEMORY_CHECK\r
-// DebugLog0("global_objc_count_ = %d", Raym::global_raym_count_);\r
-#endif\r
-\r
- // lock\r
- RaymLock(_controller);\r
-\r
- // 現在時刻取得\r
- time_t now = time(NULL);\r
-\r
-// DebugLog2("periodic: %d", now);\r
-\r
- //\r
- for (int tuner = 0; tuner < _controller->_tuner_count; ++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
- Controller::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
- if (status->isEqualToString("stop"))\r
- {\r
- stop_need = true;\r
- break;\r
- }\r
- if (!status->isEqualToString("running"))\r
- {\r
- break;\r
- }\r
- }\r
- if (end + OFFSET_OF_END_TIME <= now)\r
- {\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
- 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
-\r
- if (epg == NULL)\r
- {\r
- // next tuner\r
- continue;\r
- }\r
-\r
- //\r
- // 録画開始要否チェック\r
- //\r
- bool start_need = false;\r
- start = end = 0;\r
- Controller::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 (end + OFFSET_OF_END_TIME <= now)\r
- {\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
- 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 (!_controller->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
- // EPG収集時刻を取得\r
- String *collect_str = _controller->_props->stringForKey(KEY_COLLECT_EPG_TIME);\r
- if (collect_str != NULL)\r
- {\r
- // 秒に変換\r
- time_t collect_time = 0;\r
- Controller::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
-\r
- // unlock\r
- RaymUnlock(_controller);\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 RAYM_MEMORY_CHECK\r
-// DebugLog0("global_objc_count_ = %d", Raym::global_raym_count_);\r
-#endif\r
-}\r
-\r
-void Reservation::timerExpired(Timer *timer, void *userInfo)\r
-{\r
- switch ((long long)userInfo)\r
- {\r
- case CMD_PERIODIC:\r
- periodic();\r
- break;\r
-\r
- case CMD_COLLECT_EPG_ISDB_S:\r
- if (collectEPGs(Tuner::ISDB_S))\r
- {\r
- _timer_epg_s->setRepeats(false);\r
- }\r
- else\r
- {\r
- _timer_epg_s->setTimeInterval(DEF_COLLECT_EPG_RETRY);\r
- }\r
- break;\r
-\r
- case CMD_COLLECT_EPG_ISDB_T:\r
- if (collectEPGs(Tuner::ISDB_T))\r
- {\r
- _timer_epg_t->setRepeats(false);\r
- }\r
- else\r
- {\r
- _timer_epg_t->setTimeInterval(DEF_COLLECT_EPG_RETRY);\r
- }\r
- break;\r
- }\r
-}\r
-\r
-} // iPTd\r
-} // ry0\r