2 * @file Reservation.cpp
\r
13 #include "Raym/Log.h"
\r
16 #include "ry0/iPTd/Reservation.h"
\r
17 #include "ry0/iPTd/Controller.h"
\r
19 using namespace Raym;
\r
26 static const time_t OFFSET_OF_START_TIME = -2; // 録画開始時刻の補正(秒単位)
\r
27 static const time_t OFFSET_OF_END_TIME = -3; // 録画停止時刻の補正(秒単位)
\r
28 static const time_t OFFSET_OF_WAKEUP = -240; // 起動スケジュールの補正(秒単位) 注:休止するまでの時間(DEF_SUSPEND_TIME)よりも短くすること
\r
29 static const time_t OFFSET_OF_SUPPRESSION_TIME = -600; // 録画開始前に休止の抑制を開始する時間(秒単位)
\r
31 Reservation::Reservation()
\r
36 _reservations_path = NULL;
\r
37 _reservations = NULL;
\r
38 _reservation_seq_id = -1;
\r
41 Reservation::~Reservation()
\r
43 RELEASE(_reservations_path);
\r
44 RELEASE(_reservations);
\r
47 Reservation *Reservation::alloc()
\r
49 return new Reservation();
\r
52 Reservation *Reservation::initWithController(Controller *controller)
\r
54 _controller = controller;
\r
55 _tuner_count = controller->_tunerCount;
\r
56 _tuners = controller->_tuners;
\r
58 _reservations_path = String::alloc()->initWithFormat("%s%s.iptd.reservations.plist", _controller->_system_path->cString(), Controller::_plist_prefix);
\r
59 if (_reservations_path == NULL)
\r
65 _reservations = Dictionary::alloc()->initWithContentsOfFile(_reservations_path);
\r
66 if (_reservations == NULL)
\r
68 DebugLog1("because \"%s\" is not exists, created.", _reservations_path->cString());
\r
69 _reservations = Dictionary::alloc()->initWithCapacity(0);
\r
70 if (_reservations == NULL)
\r
75 _reservation_seq_id = 1;
\r
79 DebugLog1("reservations file: \"%s\"", _reservations_path->cString());
\r
81 _reservation_seq_id = _reservations->integerForKey(KEY_EPG_LAST_RESV_ID);
\r
84 _timer_periodic = Timer::alloc()->initWithTimeInterval(1.0, this, NULL, true);
\r
85 if (_timer_periodic == NULL)
\r
91 _timer_periodic->fire();
\r
96 bool Reservation::canTerminate()
\r
101 time_t now = time(NULL);
\r
103 for (int tuner = 0; tuner < _tuner_count; ++tuner)
\r
105 Array *array = _reservations->arrayForKey(_tuners[tuner]->name());
\r
106 if ((array == NULL) || (array->count() == 0))
\r
113 Dictionary *epg = (Dictionary *)array->objectAtIndex(0);
\r
116 Controller::getTimeWithEPG(epg, &start, &end);
\r
118 if (start + OFFSET_OF_SUPPRESSION_TIME <= now)
\r
132 bool Reservation::reserve(int tuner, Dictionary *in_epg)
\r
134 DebugLog2("Reservation::reserve(tuner, epg)");
\r
136 bool result = false;
\r
139 RaymLock(_controller);
\r
141 while ((0 <= tuner) && (tuner < _tuner_count) && (in_epg != NULL))
\r
143 Dictionary *epg = Dictionary::dictionaryWithDictionary(in_epg);
\r
146 DebugLog3("Dictionary::dictionaryWithDictionary() ng.");
\r
150 Array *array = _reservations->arrayForKey(_tuners[tuner]->name());
\r
153 array = Array::arrayWithCapacity(0);
\r
154 _reservations->setObject(array, _tuners[tuner]->name());
\r
159 Controller::getTimeWithEPG(epg, &epg_start, &epg_end);
\r
160 DebugLog2("epg start: %ld, end: %ld\n", epg_start, epg_end);
\r
162 time_t pre_start = 0;
\r
163 time_t pre_end = 0;
\r
164 pre_end = time(NULL);
\r
165 DebugLog2("pre_end: %ld", pre_end);
\r
167 for (uint i = 0; i < array->count(); ++i)
\r
169 Dictionary *cur = (Dictionary *)array->objectAtIndex(i);
\r
172 Controller::getTimeWithEPG(cur, &cur_start, &cur_end);
\r
173 DebugLog2("cur start: %ld, end: %ld\n", cur_start, cur_end);
\r
174 if ((pre_end <= epg_start) && (epg_end <= cur_start))
\r
176 DebugLog2("insert: %d\n", i);
\r
177 array->insertObject(epg, i);
\r
181 pre_start = cur_start;
\r
187 if (pre_end <= epg_start)
\r
189 DebugLog2("add\n");
\r
190 array->addObject(epg);
\r
195 DebugLog2("no add\n");
\r
200 epg->setInteger(_reservation_seq_id, KEY_EPG_RESV_ID);
\r
201 _reservation_seq_id = (_reservation_seq_id + 1) % 1000000;
\r
202 _reservations->setInteger(_reservation_seq_id, KEY_EPG_LAST_RESV_ID);
\r
205 _reservations->writeToFile(_reservations_path, true);
\r
212 RaymUnlock(_controller);
\r
218 // tuner: 0 - (_tunerCount - 1) cancel current
\r
219 // tuner: -1 cancel reserve_id
\r
221 bool Reservation::cancel(int tuner, int reserve_id)
\r
223 bool result = false;
\r
226 RaymLock(_controller);
\r
229 if ((0 <= tuner) && (tuner < _tuner_count))
\r
231 Array *array = _reservations->arrayForKey(_tuners[tuner]->name());
\r
234 if (array->count() > 0)
\r
236 Dictionary *epg = (Dictionary *)array->objectAtIndex(0);
\r
237 String *status = epg->stringForKey(KEY_EPG_STATUS);
\r
238 if (status != NULL)
\r
240 if (status->isEqualToString("running"))
\r
242 epg->setString("stop", KEY_EPG_STATUS);
\r
251 else if ((tuner < 0) && (0 <= reserve_id) && (reserve_id < 1000000))
\r
253 for (int i = 0; i < _tuner_count; ++i)
\r
255 Array *array = _reservations->arrayForKey(_tuners[i]->name());
\r
258 for (uint j = 0; j < array->count(); ++j)
\r
260 Dictionary *epg = (Dictionary *)array->objectAtIndex(j);
\r
261 if (reserve_id == epg->integerForKey(KEY_EPG_RESV_ID))
\r
263 String *status = epg->stringForKey(KEY_EPG_STATUS);
\r
264 if ((status != NULL) && status->isEqualToString("running"))
\r
266 epg->setString("stop", KEY_EPG_STATUS);
\r
270 array->removeObjectAtIndex(j);
\r
286 _reservations->writeToFile(_reservations_path, true);
\r
290 RaymUnlock(_controller);
\r
295 std::string Reservation::createVideoPath(int tuner)
\r
297 DebugLog2("Reservation::createVideoPath()");
\r
299 std::string result = "";
\r
306 if (localtime_s(&tm, &now) != 0)
\r
311 result = _controller->_props->stringForKey(KEY_STORE_PATH)->cString();
\r
312 DebugLog2("result: %s\n", result.c_str());
\r
315 if (sprintf_s(tmp, sizeof(tmp), "\\%04d", tm.tm_year + 1900) < 0)
\r
317 DebugLog0("sprintf_s() error: year\n");
\r
322 DebugLog2("result: %s\n", result.c_str());
\r
325 if (_stat(result.c_str(), &stat) != 0)
\r
327 if (_mkdir(result.c_str()) != 0)
\r
329 DebugLog0("_mkdir() error: year\n");
\r
333 _stat(result.c_str(), &stat);
\r
335 if ((stat.st_mode & _S_IFDIR) != _S_IFDIR)
\r
337 DebugLog0("%s is not directory.\n", result.c_str());
\r
342 if (sprintf_s(tmp, sizeof(tmp), "\\%02d", tm.tm_mon + 1) < 0)
\r
344 DebugLog0("sprintf_s() error: month\n");
\r
349 DebugLog2("result: %s\n", result.c_str());
\r
351 if (_stat(result.c_str(), &stat) != 0)
\r
353 if (_mkdir(result.c_str()) != 0)
\r
355 DebugLog0("_mkdir() error: month\n");
\r
359 _stat(result.c_str(), &stat);
\r
361 if ((stat.st_mode & _S_IFDIR) != _S_IFDIR)
\r
363 DebugLog0("%s is not directory.", result.c_str());
\r
368 if (sprintf_s(tmp, sizeof(tmp),
\r
369 "\\%04d%02d%02d_%02d%02d%02d_%03d_%s.ts",
\r
370 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
\r
371 _tuners[tuner]->channel(), _tuners[tuner]->name()) < 0)
\r
373 DebugLog0("sprintf_s() error: filename");
\r
378 DebugLog2("result: %s\n", result.c_str());
\r
386 void Reservation::timerExpired(Timer *timer, void *userInfo)
\r
388 bool need_update = false;
\r
390 #ifdef RAYM_MEMORY_CHECK
\r
391 DebugLog0("global_objc_count_ = %d", Raym::global_raym_count_);
\r
395 RaymLock(_controller);
\r
398 time_t now = time(NULL);
\r
400 DebugLog2("periodic: %d", now);
\r
403 for (int tuner = 0; tuner < _tuner_count; ++tuner)
\r
405 Array *array = _reservations->arrayForKey(_tuners[tuner]->name());
\r
406 if ((array == NULL) || (array->count() == 0))
\r
413 Dictionary *epg = (Dictionary *)array->objectAtIndex(0);
\r
416 Controller::getTimeWithEPG(epg, &start, &end);
\r
421 bool stop_need = false;
\r
424 String *status = epg->stringForKey(KEY_EPG_STATUS);
\r
425 if (status != NULL)
\r
427 if (status->isEqualToString("stop"))
\r
432 if (!status->isEqualToString("running"))
\r
437 if (end + OFFSET_OF_END_TIME <= now)
\r
445 DebugLog2("I try stop\n");
\r
446 int fd = _tuners[tuner]->stopRecording();
\r
449 DebugLog1("stopRecording() error.\n");
\r
453 DebugLog2("stopRecording() ok\n");
\r
454 DebugLog0("stop recording of \"%s\"", _tuners[tuner]->name());
\r
457 array->removeObject(epg);
\r
459 if (array->count() > 0)
\r
461 epg = (Dictionary *)array->objectAtIndex(0);
\r
467 need_update = true;
\r
479 bool start_need = false;
\r
481 Controller::getTimeWithEPG(epg, &start, &end);
\r
482 if ((start != 0) && (end != 0))
\r
484 String *status = epg->stringForKey(KEY_EPG_STATUS);
\r
485 if ((status == NULL) || !(status->isEqualToString("running")))
\r
487 if (end + OFFSET_OF_END_TIME <= now)
\r
489 // 既に終了時間が経過しているので削除する
\r
490 array->removeObject(epg);
\r
492 else if (start + OFFSET_OF_START_TIME <= now)
\r
501 DebugLog2("I need start.\n");
\r
502 String *ch = epg->stringForKey(KEY_EPG_CHANNEL);
\r
505 int channel = atoi(ch->cString());
\r
506 DebugLog2("channel: %d\n", channel);
\r
507 std::string videopath = createVideoPath(tuner);
\r
508 if (videopath != "")
\r
510 DebugLog2("videopath: %s\n", videopath.c_str());
\r
512 if (_sopen_s(&fd, videopath.c_str(),
\r
513 (_O_CREAT | _O_EXCL | _O_WRONLY | _O_BINARY | _O_TRUNC), _SH_DENYRW, (_S_IREAD | _S_IWRITE)) == 0)
\r
515 DebugLog2("open ok.\n");
\r
516 bool startResult = true;
\r
517 if (_tuners[tuner]->channel() != channel)
\r
519 if (!_controller->setChannel(tuner, channel))
\r
521 DebugLog3("setChannel() ng.");
\r
522 startResult = false;
\r
528 if (_tuners[tuner]->startRecording(fd))
\r
530 DebugLog2("startRecording() ok.");
\r
531 DebugLog0("start recording of \"%s\" to %s.", _tuners[tuner]->name(), videopath.c_str());
\r
535 DebugLog3("Tuner::startRecording() failed.");
\r
536 startResult = false;
\r
542 epg->setString("running", KEY_EPG_STATUS);
\r
551 DebugLog0("open ng. 0x%08x\n", errno);
\r
556 DebugLog0("Can't create videopath.\n");
\r
561 DebugLog0("error.\n");
\r
569 _reservations->writeToFile(_reservations_path, true);
\r
574 NSString *collect_str = _props->stringForKey(KEY_COLLECT_EPG_TIME);
\r
575 if (collect_str != NULL)
\r
578 time_t collect_time = 0;
\r
579 getTimeWithString(collect_str, &collect_time);
\r
582 if ((collect_time <= now) && (now < collect_time + 1))
\r
585 if ((_timer_epg_s == NULL) || !_timer_epg_s->valid())
\r
587 // EPG収集用タイマ起動(ISDB-S)
\r
588 RELEASE(_timer_epg_s);
\r
589 _timer_epg_s = NSTimer::alloc()->initWithTimeInterval(DEF_COLLECT_EPG_DELAY, this, (void *)CMD_COLLECT_EPG_ISDB_S, true);
\r
590 if (_timer_epg_s != NULL)
\r
592 _timer_epg_s->fire();
\r
597 if ((_timer_epg_t == NULL) || !_timer_epg_t->valid())
\r
599 // EPG収集用タイマ起動(ISDB-T)
\r
600 RELEASE(_timer_epg_t);
\r
601 _timer_epg_t = NSTimer::alloc()->initWithTimeInterval(DEF_COLLECT_EPG_DELAY, this, (void *)CMD_COLLECT_EPG_ISDB_T, true);
\r
602 if (_timer_epg_t != NULL)
\r
604 _timer_epg_t->fire();
\r
612 RaymUnlock(_controller);
\r
615 // 1/100秒単位が 0 に近くなるように次回T.O.を微調整
\r
616 // ただし、windowsは精度が低いので期待しないことw
\r
619 #if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
\r
620 static const __time64_t DELTA_EPOCH_IN_MICROSECS = 11644473600000000Ui64;
\r
622 static const __time64_t DELTA_EPOCH_IN_MICROSECS = 11644473600000000ULL;
\r
626 GetSystemTimeAsFileTime(&ft);
\r
629 __time64_t now_sec;
\r
630 __time64_t now_usec;
\r
631 now_sec = ft.dwHighDateTime;
\r
633 now_sec |= ft.dwLowDateTime;
\r
634 now_sec /= 10; /*convert into microseconds*/
\r
635 now_sec -= DELTA_EPOCH_IN_MICROSECS;
\r
636 now_usec = (now_sec % 1000000UL);
\r
637 now_sec = now_sec / 1000000UL;
\r
639 TimeInterval interval = (TimeInterval)now_usec;
\r
640 interval = interval / 1000000;
\r
641 _timer_periodic->setTimeInterval(1.005 - interval);
\r
643 #ifdef RAYM_MEMORY_CHECK
\r
644 DebugLog0("global_objc_count_ = %d", Raym::global_raym_count_);
\r