+//\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->_tunerCount) && (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(_controller->_tuners[tuner]->name());\r
+ if (array == NULL)\r
+ {\r
+ array = Array::arrayWithCapacity(0);\r
+ _reservations->setObject(array, _controller->_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->_tunerCount))\r
+ {\r
+ Array *array = _reservations->arrayForKey(_controller->_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->_tunerCount; ++i)\r
+ {\r
+ Array *array = _reservations->arrayForKey(_controller->_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
+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
+ _controller->_tuners[tuner]->channel(), _controller->_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::timerExpired(Timer *timer, void *userInfo)\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->_tunerCount; ++tuner)\r
+ {\r
+ Array *array = _reservations->arrayForKey(_controller->_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 = _controller->_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\"", _controller->_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 (_controller->_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 (_controller->_tuners[tuner]->startRecording(fd))\r
+ {\r
+ DebugLog2("startRecording() ok.");\r
+ DebugLog0("start recording of \"%s\" to %s.", _controller->_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
+ NSString *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 = NSTimer::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 = NSTimer::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(_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