OSDN Git Service

initial files
[iptd/iPTd_R3.git] / src / ry0 / iPTd / Controller.cpp
1 //\r
2 //\r
3 //\r
4 \r
5 #define DBG_LEVEL 0\r
6 #include <Raym/Log.h>\r
7 \r
8 #include <time.h>\r
9 #include <direct.h>\r
10 #include <sys/types.h>\r
11 #include <sys/stat.h>\r
12 #include <stdio.h>\r
13 #include <fcntl.h>\r
14 #include <io.h>\r
15 #include <share.h>\r
16 #include <winsock2.h>\r
17 #include <ws2tcpip.h>\r
18 #include <Iphlpapi.h>\r
19 \r
20 #include "b25/aribstr.h"\r
21 #include "b25/arib_std_b25.h"\r
22 #include "b25/b_cas_card.h"\r
23 \r
24 #include "net/RTSPRequest.h"\r
25 \r
26 #include "ry0/iPTd/Controller.h"\r
27 #include "ry0/iPTd/HTTPLiveStreaming.h"\r
28 #include "ry0/iPTd/Extractor.h"\r
29 \r
30 \r
31 using namespace Raym;\r
32 using namespace NET;\r
33 using namespace ry0::device;\r
34 \r
35 namespace ry0\r
36 {\r
37 namespace iPTd\r
38 {\r
39 \r
40 static const char *PLIST_PREFIX = "com.gmail.tim.and.pom";\r
41 \r
42 // プロパティデフォルト値\r
43 static const char * DEF_NAME                    = "iPTd_R3";\r
44 static const char * DEF_HOSTNAME                = "localhost";\r
45 static const int    DEF_HTTP_PORT               = 50080;        // HTTPポート\r
46 static const int    DEF_BEGIN_UDP_PORT          = 51000;        // UDPポートの開始位置\r
47 static const char * DEF_COLLECT_EPG_TIME        = "02:00:00";   // EPG収集する時刻 HH:MM::SS\r
48 static const int    DEF_SHUTDOWN_TIME_INIT      = 15;           // シャットダウンするまでの時間(分単位):起動後\r
49 static const int    DEF_SHUTDOWN_TIME           = 5;            // シャットダウンするまでの時間(分単位):録画後\r
50 static const int    DEF_FORCED_SHUTDOWN         = 120;          // 強制シャットダウンするまでの時間(分単位)\r
51 static const int    DEF_DISK_REMAIN             = 10;           //\r
52 \r
53 // 非同期実行コマンド\r
54 static const long long CMD_RESTART              = 0x0001;       // 再開処理\r
55 static const long long CMD_PERIODIC             = 0x0003;       // 周期処理\r
56 static const long long CMD_PERIODIC_2           = 0x0004;       // 周期処理2\r
57 static const long long CMD_COLLECT_EPG_ISDB_S   = 0x0005;       // 番組情報取得(ISDB-S)\r
58 static const long long CMD_COLLECT_EPG_ISDB_T   = 0x0006;       // 番組情報取得(ISDB-T)\r
59 \r
60 //\r
61 static const TimeInterval DEF_COLLECT_EPG_DELAY = 1.0;\r
62 static const TimeInterval DEF_COLLECT_EPG_RETRY = 60.0;\r
63 \r
64 static const time_t DEF_COLLECT_EPG_LIMIT_S     = 10;\r
65 static const time_t DEF_COLLECT_EPG_LIMIT_T     = 20;\r
66 \r
67 static const time_t OFFSET_OF_START_TIME        = -2;           // 録画開始時刻の補正(秒単位)\r
68 static const time_t OFFSET_OF_END_TIME          = -3;           // 録画停止時刻の補正(秒単位)\r
69 static const time_t OFFSET_OF_WAKEUP            = -600;         // 起動スケジュールの補正(秒単位)  注:休止するまでの時間(DEF_SUSPEND_TIME)よりも短くすること\r
70 static const time_t OFFSET_OF_SUPPRESSION_TIME  = -720;         // 録画開始前に休止の抑制を開始する時間(秒単位)\r
71 \r
72 \r
73 std::string Controller::createVideoPath(int tuner)\r
74 {\r
75     DebugLog2("Controller::createVideoPath()");\r
76 \r
77     std::string result = "";\r
78 \r
79     while (true)\r
80     {\r
81         time_t now;\r
82         time(&now);\r
83         TM tm;\r
84         if (localtime_s(&tm, &now) != 0)\r
85         {\r
86             break;\r
87         }\r
88 \r
89         result = _store_path->cString();\r
90         DebugLog2("result: %s\n", result.c_str());\r
91 \r
92         char tmp[128];\r
93         if (sprintf_s(tmp, sizeof(tmp), "\\%04d", tm.tm_year + 1900) < 0)\r
94         {\r
95             DebugLog0("sprintf_s() error: year\n");\r
96             result = "";\r
97             break;\r
98         }\r
99         result += tmp;\r
100         DebugLog2("result: %s\n", result.c_str());\r
101 \r
102         STAT stat;\r
103         if (_stat(result.c_str(), &stat) != 0)\r
104         {\r
105             if (_mkdir(result.c_str()) != 0)\r
106             {\r
107                 DebugLog0("_mkdir() error: year\n");\r
108                 result = "";\r
109                 break;\r
110             }\r
111             _stat(result.c_str(), &stat);\r
112         }\r
113         if ((stat.st_mode & _S_IFDIR) != _S_IFDIR)\r
114         {\r
115             DebugLog0("%s is not directory.\n", result.c_str());\r
116             result = "";\r
117             break;\r
118         }\r
119 \r
120         if (sprintf_s(tmp, sizeof(tmp), "\\%02d", tm.tm_mon + 1) < 0)\r
121         {\r
122             DebugLog0("sprintf_s() error: month\n");\r
123             result = "";\r
124             break;\r
125         }\r
126         result += tmp;\r
127         DebugLog2("result: %s\n", result.c_str());\r
128 \r
129         if (_stat(result.c_str(), &stat) != 0)\r
130         {\r
131             if (_mkdir(result.c_str()) != 0)\r
132             {\r
133                 DebugLog0("_mkdir() error: month\n");\r
134                 result = "";\r
135                 break;\r
136             }\r
137             _stat(result.c_str(), &stat);\r
138         }\r
139         if ((stat.st_mode & _S_IFDIR) != _S_IFDIR)\r
140         {\r
141             DebugLog0("%s is not directory.", result.c_str());\r
142             result = "";\r
143             break;\r
144         }\r
145 \r
146         if (sprintf_s(tmp, sizeof(tmp),\r
147                       "\\%04d%02d%02d_%02d%02d%02d_%03d_%s.ts",\r
148                       tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,\r
149                       _tuners[tuner]->channel(), _tuners[tuner]->name()) < 0)\r
150         {\r
151             DebugLog0("sprintf_s() error: filename");\r
152             result = "";\r
153             break;\r
154         }\r
155         result += tmp;\r
156         DebugLog2("result: %s\n", result.c_str());\r
157 \r
158         break;\r
159     }\r
160 \r
161     return result;\r
162 }\r
163 \r
164 // epgの開始時刻でソートする為の比較関数\r
165 Integer compareFunction(Object *obj1, Object *obj2, void *context)\r
166 {\r
167     if (isKindOfClass(Dictionary, obj1) && isKindOfClass(Dictionary, obj2))\r
168     {\r
169         time_t st1;\r
170         time_t ed1;\r
171         Controller::getTimeWithEPG((Dictionary *)obj1, &st1, &ed1);\r
172 \r
173         time_t st2;\r
174         time_t ed2;\r
175         Controller::getTimeWithEPG((Dictionary *)obj2, &st2, &ed2);\r
176 \r
177         if (st1 < st2)\r
178         {\r
179             return OrderedAscending;\r
180         }\r
181         else if (st1 > st2)\r
182         {\r
183             return OrderedDescending;\r
184         }\r
185         else\r
186         {\r
187             if (ed1 < ed2)\r
188             {\r
189                 return OrderedAscending;\r
190             }\r
191             else if (ed1 > ed2)\r
192             {\r
193                 return OrderedDescending;\r
194             }\r
195         }\r
196     }\r
197     return OrderedSame;\r
198 }\r
199 \r
200 #ifndef _WIN32\r
201 #pragma mark '\r
202 #pragma mark ------- EPG関連 -------\r
203 #endif\r
204 \r
205 void Controller::removePastEPGs()\r
206 {\r
207     DebugLog2("Controller::removePastEPGs()");\r
208 \r
209     time_t now = time(NULL);\r
210 \r
211     RaymLock(_epgs);\r
212 \r
213     Dictionary *temp_data = _epgs->dictionaryForKey(KEY_TEMP_DATA);\r
214     if (temp_data != NULL)\r
215     {\r
216         temp_data->retain();\r
217         _epgs->removeObjectForKey(KEY_TEMP_DATA);\r
218     }\r
219     else\r
220     {\r
221         temp_data = Dictionary::alloc()->initWithCapacity(0);\r
222     }\r
223 \r
224     Dictionary *services = _epgs->dictionaryForKey(KEY_SERVICES);\r
225     if (services != NULL)\r
226     {\r
227         Array *keys = services->allKeys();\r
228         for (uint i = 0; i < keys->count(); ++i)\r
229         {\r
230             String *key = (String *)keys->objectAtIndex(i);\r
231 \r
232             std::string xmltv_programs;\r
233             Dictionary *temp_service = temp_data->dictionaryForKey(key);\r
234             if (temp_service == NULL)\r
235             {\r
236                 temp_service = Dictionary::dictionaryWithCapacity(0);\r
237                 temp_data->setObject(temp_service, key);\r
238             }\r
239 \r
240             Array *old_epgs = services->arrayForKey(key);\r
241             Array *new_epgs = Array::arrayWithCapacity(0);\r
242             for (uint j = 0; j < old_epgs->count(); ++j)\r
243             {\r
244                 Dictionary *epg = (Dictionary *)old_epgs->objectAtIndex(j);\r
245                 time_t start = 0;\r
246                 time_t end = 0;\r
247                 getTimeWithEPG(epg, &start, &end);\r
248                 if (now <= end)\r
249                 {\r
250                     new_epgs->addObject(epg);\r
251 \r
252                     Array *ch_list = temp_service->arrayForKey(KEY_CHANNELS);\r
253                     if (ch_list != NULL)\r
254                     {\r
255                         std::string epg_date     = epg->stringForKey(KEY_EPG_DATE)->stringByReplacingOccurrencesOfString("/", "")->cString();\r
256                         std::string epg_start    = epg->stringForKey(KEY_EPG_START)->stringByReplacingOccurrencesOfString(":", "")->cString();\r
257                         std::string epg_end      = epg->stringForKey(KEY_EPG_END)->stringByReplacingOccurrencesOfString(":", "")->cString();\r
258                         std::string epg_event_id = epg->stringForKey(KEY_EPG_EVENT_ID)->cString();\r
259                         std::string epg_duration = epg->stringForKey(KEY_EPG_DURATION)->cString();\r
260                         std::string epg_title    = epg->stringForKey(KEY_EPG_TITLE)->cString();\r
261 \r
262                         for (uint ch_idx = 0; ch_idx < ch_list->count(); ++ch_idx)\r
263                         {\r
264                             xmltv_programs += "  <programme start=\"" + epg_date + epg_start + " +0900\" stop=\"";\r
265                             xmltv_programs += epg_date + epg_end + " +0900\" channel=\"";\r
266                             xmltv_programs += ((String *)ch_list->objectAtIndex(ch_idx))->cString();\r
267                             xmltv_programs += "\" event_id=\"" + epg_event_id + "\" duration=\"" + epg_duration + "\">\r\n";\r
268 \r
269                             xmltv_programs += "    <title lang=\"ja_JP\">" + epg_title + "</title>\r\n";\r
270 \r
271                             xmltv_programs += "  </programme>\r\n";\r
272                         }\r
273                     }\r
274                 }\r
275             }\r
276             services->setObject(new_epgs, key);\r
277 \r
278             if (xmltv_programs.length() > 0)\r
279             {\r
280                 temp_service->setObject(String::stringWithUTF8String(xmltv_programs.c_str()), KEY_PROGRAMS);\r
281             }\r
282         }\r
283     }\r
284 \r
285     _epgs->writeToFile(_epgs_path, true);\r
286 \r
287     _epgs->setObject(temp_data, KEY_TEMP_DATA);\r
288     temp_data->release();\r
289 \r
290     RaymUnlock(_epgs);\r
291 }\r
292 \r
293 void Controller::collectEPGsForTuner(Tuner::Type type, int tuner, time_t limit)\r
294 {\r
295     DebugLog2("Controller::collectEPGsForTuner(%d) start.", tuner);\r
296 \r
297     // 既にロックされた状態でコールされる前提\r
298     bool locked = false;\r
299     RaymLock(this);\r
300     if ((0 <= tuner) && (tuner < _tunerCount))\r
301     {\r
302         locked = _tuners[tuner]->isLocked();\r
303     }\r
304     RaymUnlock(this);\r
305     if (!locked)\r
306     {\r
307         DebugLog2("Controller::collectEPGsForTuner(%d) end(no locked).", tuner);\r
308         return;\r
309     }\r
310 \r
311     for (uint cnt = 0; cnt * 5 < limit; ++cnt)\r
312     {\r
313         Extractor *extractor = Extractor::alloc()->init();\r
314 \r
315         RaymLock(this);\r
316         _tuners[tuner]->setListener(extractor);\r
317         RaymUnlock(this);\r
318 \r
319         Array *collected = extractor->collectEPGs(5);\r
320 \r
321         RaymLock(this);\r
322         _tuners[tuner]->setListener(NULL);\r
323         RaymUnlock(this);\r
324 \r
325         extractor->release();\r
326 \r
327         RaymLock(_epgs);\r
328 \r
329         for (uint j = 0; j < collected->count(); ++j)\r
330         {\r
331             Dictionary *epg = (Dictionary *)collected->objectAtIndex(j);\r
332 \r
333             if (epg->stringForKey(KEY_EPG_TITLE) == NULL)\r
334             {\r
335                 // タイトルが無い場合は不要\r
336                 continue;\r
337             }\r
338 \r
339             // Service ID を Primary Key\r
340             String *key = epg->stringForKey(KEY_EPG_SERVICE_ID);\r
341             if (key != NULL)\r
342             {\r
343                 Dictionary *services = _epgs->dictionaryForKey(KEY_SERVICES);\r
344                 if (services == NULL)\r
345                 {\r
346                     services = Dictionary::dictionaryWithCapacity(0);\r
347                     _epgs->setObject(services, KEY_SERVICES);\r
348                 }\r
349                 Array *epgs = services->arrayForKey(key);\r
350                 if (epgs == NULL)\r
351                 {\r
352                     epgs = Array::arrayWithCapacity(0);\r
353                     services->setObject(epgs, key);\r
354                 }\r
355                 bool inserted = false;\r
356                 for (uint idx = 0; idx < epgs->count(); ++idx)\r
357                 {\r
358                     Dictionary *epg2 = (Dictionary *)epgs->objectAtIndex(idx);\r
359                     if (epg->stringForKey(KEY_EPG_EVENT_ID)->isEqualToString(epg2->stringForKey(KEY_EPG_EVENT_ID)))\r
360                     {\r
361                         inserted = true;\r
362                         break;\r
363                     }\r
364                     if (compareFunction(epg, epgs->objectAtIndex(idx), NULL) == OrderedAscending)\r
365                     {\r
366                         epgs->insertObject(epg, idx);\r
367                         inserted = true;\r
368                         break;\r
369                     }\r
370                 }\r
371                 if (!inserted)\r
372                 {\r
373                     epgs->addObject(epg);\r
374                 }\r
375             }\r
376         }\r
377 \r
378         RaymUnlock(_epgs);\r
379 \r
380         RaymLock(this);\r
381         if (((type == Tuner::ISDB_S) && _cancel_epg_collect_s) ||\r
382             ((type == Tuner::ISDB_T) && _cancel_epg_collect_t))\r
383         {\r
384             cnt = (uint)limit;\r
385         }\r
386         RaymUnlock(this);\r
387     }\r
388 \r
389     removePastEPGs();\r
390 \r
391     DebugLog2("Controller::collectEPGsForTuner(%d) end.", tuner);\r
392 }\r
393 \r
394 bool Controller::collectEPGs(Tuner::Type type)\r
395 {\r
396     // 使用するチューナを決定\r
397     int tuner = 0;\r
398     bool locked = false;\r
399     RaymLock(this);\r
400     for (int i = _tunerCount - 1; i >= 0; --i)\r
401     {\r
402         if (isTunerEnabled(i))\r
403         {\r
404             if ((!_tuners[i]->isLocked()) && (_tuners[i]->type() == type))\r
405             {\r
406                 if (_tuners[i]->lock())\r
407                 {\r
408                     tuner = i;\r
409                     locked = true;\r
410                     break;\r
411                 }\r
412             }\r
413         }\r
414     }\r
415     RaymUnlock(this);\r
416 \r
417     // ロック確認\r
418     if (!locked)\r
419     {\r
420         DebugLog3("Controller::collectEPGs(%s) end(Can't locled).", type == Tuner::ISDB_S ? "ISDB-S" : "ISDB-T");\r
421         // ロックできない場合は収集しない\r
422         return false;\r
423     }\r
424 \r
425     bool canceled = false;\r
426     DebugLog0("Collect EPG of \"%s(#%d)\" is started.", _tuners[tuner]->name(), tuner);\r
427 \r
428     // 現在のチャンネルを保存\r
429     int channel = _tuners[tuner]->channel();\r
430 \r
431     // チャンネルを変更しつつEPGを取得\r
432     int max_channel = (type == Tuner::ISDB_S ? Tuner::MAX_CHANNELS_ISDB_S : Tuner::MAX_CHANNELS_ISDB_T);\r
433     for (int ch = 0; ch <= max_channel; ++ch)\r
434     {\r
435         // チャンネルが有効かチェック\r
436         if (isChannelEnabled(tuner, ch))\r
437         {\r
438             // チャンネル設定\r
439             if (_tuners[tuner]->setChannel(ch))\r
440             {\r
441 //                DebugLog0("Collect EPG of \"%s(#%d)\" ch %d", _tuners[tuner]->name(), tuner, ch);\r
442                 time_t limit = (time_t)_epgs->integerForKey(stationName(type, ch));\r
443                 if (limit == 0)\r
444                 {\r
445                     limit = ((type == Tuner::ISDB_S) ? DEF_COLLECT_EPG_LIMIT_S : DEF_COLLECT_EPG_LIMIT_T);\r
446                     _epgs->setInteger((int)limit, stationName(type, ch));\r
447                 }\r
448 \r
449                 collectEPGsForTuner(type, tuner, limit);\r
450 \r
451                 RaymLock(this);\r
452                 if (((type == Tuner::ISDB_S) && _cancel_epg_collect_s) ||\r
453                     ((type == Tuner::ISDB_T) && _cancel_epg_collect_t))\r
454                 {\r
455                     ch = Tuner::MAX_CHANNELS_ISDB_T + 1;\r
456                     canceled = true;\r
457                 }\r
458                 RaymUnlock(this);\r
459             }\r
460         }\r
461     }\r
462 \r
463     // 元のチャンネルに戻す\r
464     _tuners[tuner]->setChannel(channel);\r
465 \r
466     // lock\r
467     RaymLock(this);\r
468 \r
469     // チューナをアンロック\r
470     _tuners[tuner]->unlock();\r
471 \r
472     if (type == Tuner::ISDB_S)\r
473     {\r
474         _cancel_epg_collect_s = false;\r
475     }\r
476     else\r
477     {\r
478         _cancel_epg_collect_t = false;\r
479     }\r
480 \r
481     // キーワード予約更新\r
482     updateKeywordsReservation();\r
483 \r
484     // unlock\r
485     RaymUnlock(this);\r
486 \r
487     DebugLog0("Collect EPG of \"%s(#%d)\" was %s.", _tuners[tuner]->name(), tuner, canceled ? "canceled" : "finished");\r
488 \r
489     return !canceled;\r
490 }\r
491 \r
492 #ifndef _WIN32\r
493 #pragma mark '\r
494 #pragma mark ------- 予約録画関連 -------\r
495 #endif\r
496 \r
497 //\r
498 // 録画予約:サービスID/イベントID指定\r
499 //   EPGデータから指定のサービスID/イベントIDのEPGを取り出し、EPG指定の録画予約をコール\r
500 //\r
501 bool Controller::reserve(int service_id, int event_id)\r
502 {\r
503     DebugLog0("Controller::reserve(service_id, event_id)");\r
504     DebugLog0("event_id: %d, service_id: %d", event_id, service_id);\r
505 \r
506     bool result = false;\r
507 \r
508     // lock\r
509     RaymLock(this);\r
510 \r
511     if ((_epgs != NULL) && (_epgs->dictionaryForKey(KEY_SERVICES) != NULL))\r
512     {\r
513         Array *events = _epgs->dictionaryForKey(KEY_SERVICES)->arrayForKey(String::stringWithFormat("%d", service_id));\r
514         if (events != NULL)\r
515         {\r
516             DebugLog0("count: %d, event_id: %d, service_id: %d", events->count(), event_id, service_id);\r
517             for (uint i = 0; i < events->count(); ++i)\r
518             {\r
519                 Dictionary *epg = (Dictionary *)events->objectAtIndex(i);\r
520                 DebugLog0("epg event_id: %s", epg->stringForKey(KEY_EPG_EVENT_ID)->cString());\r
521                 if (epg->stringForKey(KEY_EPG_EVENT_ID)->isEqualToString(String::stringWithFormat("%d", event_id)))\r
522                 {\r
523                     result = reserve(epg);\r
524                 }\r
525             }\r
526         }\r
527         else\r
528         {\r
529             DebugLog0("events is NULL");\r
530         }\r
531     }\r
532     else\r
533     {\r
534         DebugLog0("_epgs is NULL");\r
535     }\r
536 \r
537     // unlock\r
538     RaymUnlock(this);\r
539 \r
540     return result;\r
541 }\r
542 \r
543 //\r
544 // 録画予約:EPG指定\r
545 //   EPGからサービスIDを取り出し、チューナ情報を検索して一致するサービスIDがあったらチューナ/EPG指定の録画予約を試行\r
546 //\r
547 bool Controller::reserve(Dictionary *in_epg)\r
548 {\r
549     DebugLog0("Controller::reserve(epg)");\r
550 \r
551     bool result = false;\r
552 \r
553     // lock\r
554     RaymLock(this);\r
555 \r
556     while (in_epg != NULL)\r
557     {\r
558         // EPGを複製\r
559         Dictionary *epg = Dictionary::dictionaryWithDictionary(in_epg);\r
560         if (epg == NULL)\r
561         {\r
562             DebugLog3("Dictionary::dictionaryWithDictionary() ng.");\r
563             break;\r
564         }\r
565 \r
566         // サービスID取得\r
567         String *service_id = epg->stringForKey(KEY_EPG_SERVICE_ID);\r
568         if (service_id == NULL)\r
569         {\r
570             DebugLog3("epg->stringForKey(KEY_EPG_SERVICE_ID) ng.");\r
571             break;\r
572         }\r
573         DebugLog3("service_id: %s\n", service_id->cString());\r
574 \r
575         // 全チューナ情報取得\r
576         Dictionary *tunerInfos = _props->dictionaryForKey(KEY_TUNERS);\r
577         if (tunerInfos == NULL)\r
578         {\r
579             DebugLog3("_props->dictionaryForKey(KEY_TUNERS) ng.");\r
580             break;\r
581         }\r
582 \r
583         // チューナを検索\r
584         for (int i = 0; (!result) && (i < _tunerCount); ++i)\r
585         {\r
586             // チューナ情報取得\r
587             Dictionary *tunerInfo = tunerInfos->dictionaryForKey(_tuners[i]->name());\r
588             if (tunerInfo == NULL)\r
589             {\r
590                 DebugLog3("tunerInfos->dictionaryForKey(_tuners[%d]->name()) ng.", i);\r
591                 continue;\r
592             }\r
593 \r
594             // 全チャンネル情報取得\r
595             Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
596             if (channels == NULL)\r
597             {\r
598                 DebugLog3("tunerInfo->dictionaryForKey(KEY_CHANNELS) ng.");\r
599                 continue;\r
600             }\r
601 \r
602             // キー取得\r
603             Array *chkeys = channels->allKeys();\r
604             for (uint ch = 0; ch < chkeys->count(); ++ch)\r
605             {\r
606                 // チャンネル情報取得\r
607                 Dictionary *channel = channels->dictionaryForKey((String *)chkeys->objectAtIndex(ch));\r
608                 if (channel == NULL)\r
609                 {\r
610                     DebugLog3("channels->dictionaryForKey() ng.");\r
611                     continue;\r
612                 }\r
613 \r
614                 // 全サービス情報取得\r
615                 Array *services = (Array *)channel->objectForKey(KEY_SERVICES);\r
616                 if (services == NULL)\r
617                 {\r
618                     DebugLog3("channel->objectForKey() ng.");\r
619                     continue;\r
620                 }\r
621 \r
622                 for (uint s = 0; s < services->count(); ++s)\r
623                 {\r
624                     // サービス情報取得\r
625                     Dictionary *service = (Dictionary *)services->objectAtIndex(s);\r
626                     if (service == NULL)\r
627                     {\r
628                         DebugLog3("service->objectAtIndex() ng.");\r
629                         continue;\r
630                     }\r
631 \r
632                     // サービスIDを比較\r
633                     String *sid = service->stringForKey(KEY_SERVICE_ID);\r
634                     if ((sid != NULL) && sid->isEqualToString(service_id))\r
635                     {\r
636                         // チャンネルを設定\r
637                         epg->setString((String *)chkeys->objectAtIndex(ch), KEY_EPG_CHANNEL);\r
638 \r
639                         // 録画予約\r
640                         result = reserve(i, epg);\r
641 \r
642                         // チャンネルループのカウンタを更新(=ループを終了させる)\r
643                         ch = chkeys->count();\r
644                         break;\r
645                     }\r
646                 }\r
647             }\r
648         }\r
649 \r
650         break;\r
651     }\r
652 \r
653     // unlock\r
654     RaymUnlock(this);\r
655 \r
656     return result;\r
657 }\r
658 \r
659 //\r
660 // 録画予約:チューナ/EPG指定\r
661 //\r
662 bool Controller::reserve(int tuner, Dictionary *in_epg)\r
663 {\r
664     DebugLog0("Controller::reserve(tuner, epg)");\r
665 \r
666     bool result = false;\r
667 \r
668     // lock\r
669     RaymLock(this);\r
670 \r
671     while ((0 <= tuner) && (tuner < _tunerCount) && (in_epg != NULL))\r
672     {\r
673         Dictionary *epg = Dictionary::dictionaryWithDictionary(in_epg);\r
674         if (epg == NULL)\r
675         {\r
676             DebugLog3("Dictionary::dictionaryWithDictionary() ng.");\r
677             break;\r
678         }\r
679 \r
680         Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
681         if (array == NULL)\r
682         {\r
683             array = Array::arrayWithCapacity(0);\r
684             _reservations->setObject(array, _tuners[tuner]->name());\r
685         }\r
686 \r
687         time_t epg_start;\r
688         time_t epg_end;\r
689         getTimeWithEPG(epg, &epg_start, &epg_end);\r
690         DebugLog2("epg start: %ld, end: %ld\n", epg_start, epg_end);\r
691 \r
692         time_t pre_start = 0;\r
693         time_t pre_end = 0;\r
694         pre_end = time(NULL);\r
695         DebugLog2("pre_end: %ld", pre_end);\r
696 \r
697         for (uint i = 0; i < array->count(); ++i)\r
698         {\r
699             Dictionary *cur = (Dictionary *)array->objectAtIndex(i);\r
700             time_t cur_start;\r
701             time_t cur_end;\r
702             getTimeWithEPG(cur, &cur_start, &cur_end);\r
703             DebugLog2("cur start: %ld, end: %ld\n", cur_start, cur_end);\r
704             if ((pre_end <= epg_start) && (epg_end <= cur_start))\r
705             {\r
706                 DebugLog2("insert: %d\n", i);\r
707                 array->insertObject(epg, i);\r
708                 result = true;\r
709                 break;\r
710             }\r
711             pre_start = cur_start;\r
712             pre_end = cur_end;\r
713         }\r
714 \r
715         if (!result)\r
716         {\r
717             if (pre_end <= epg_start)\r
718             {\r
719                 DebugLog2("add\n");\r
720                 array->addObject(epg);\r
721                 result = true;\r
722             }\r
723             else\r
724             {\r
725                 DebugLog2("no add\n");\r
726             }\r
727         }\r
728         if (result)\r
729         {\r
730             epg->setInteger(_reservation_seq_id, KEY_EPG_RESV_ID);\r
731             _reservation_seq_id = (_reservation_seq_id + 1) % 1000000;\r
732             _reservations->setInteger(_reservation_seq_id, KEY_EPG_LAST_RESV_ID);\r
733 \r
734             //\r
735             _reservations->writeToFile(_reservations_path, true);\r
736         }\r
737 \r
738         break;\r
739     }\r
740 \r
741     // unlock\r
742     RaymUnlock(this);\r
743 \r
744     return result;\r
745 }\r
746 \r
747 //\r
748 // tuner: 0 - (_tunerCount - 1)  cancel current\r
749 // tuner: -1  cancel reserve_id\r
750 //\r
751 bool Controller::cancel(int tuner, int reserve_id)\r
752 {\r
753     bool result = false;\r
754 \r
755     // lock\r
756     RaymLock(this);\r
757 \r
758     //\r
759     if ((0 <= tuner) && (tuner < _tunerCount))\r
760     {\r
761         Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
762         if (array != NULL)\r
763         {\r
764             if (array->count() > 0)\r
765             {\r
766                 Dictionary *epg = (Dictionary *)array->objectAtIndex(0);\r
767                 String *status = epg->stringForKey(KEY_EPG_STATUS);\r
768                 if (status != NULL)\r
769                 {\r
770                     if (status->isEqualToString("running"))\r
771                     {\r
772                         epg->setString("stop", KEY_EPG_STATUS);\r
773                         result = true;\r
774                     }\r
775                 }\r
776             }\r
777         }\r
778     }\r
779 \r
780     //\r
781     else if ((tuner < 0) && (0 <= reserve_id) && (reserve_id < 1000000))\r
782     {\r
783         for (int i = 0; i < _tunerCount; ++i)\r
784         {\r
785             Array *array = _reservations->arrayForKey(_tuners[i]->name());\r
786             if (array != NULL)\r
787             {\r
788                 for (uint j = 0; j < array->count(); ++j)\r
789                 {\r
790                     Dictionary *epg = (Dictionary *)array->objectAtIndex(j);\r
791                     if (reserve_id == epg->integerForKey(KEY_EPG_RESV_ID))\r
792                     {\r
793                         String *status = epg->stringForKey(KEY_EPG_STATUS);\r
794                         if ((status != NULL) && status->isEqualToString("running"))\r
795                         {\r
796                             epg->setString("stop", KEY_EPG_STATUS);\r
797                         }\r
798                         else\r
799                         {\r
800                             array->removeObjectAtIndex(j);\r
801                         }\r
802                         result = true;\r
803                         break;\r
804                     }\r
805                 }\r
806             }\r
807             if (result)\r
808             {\r
809                 break;\r
810             }\r
811         }\r
812     }\r
813 \r
814     if (result)\r
815     {\r
816         _reservations->writeToFile(_reservations_path, true);\r
817     }\r
818 \r
819     // unlock\r
820     RaymUnlock(this);\r
821 \r
822     return result;\r
823 }\r
824 \r
825 //\r
826 // 録画制御\r
827 //\r
828 void Controller::periodic(void)\r
829 {\r
830     bool need_update = false;\r
831 \r
832 #ifdef OBJC_MEMORY_CHECK\r
833     DebugLog0("global_objc_count_ = %d", Raym::global_objc_count_);\r
834 #endif\r
835 \r
836     // lock\r
837     RaymLock(this);\r
838 \r
839     // 現在時刻取得\r
840     time_t now = time(NULL);\r
841 \r
842     DebugLog2("periodic: %d", now);\r
843 \r
844     //\r
845     for (int tuner = 0; tuner < _tunerCount; ++tuner)\r
846     {\r
847         Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
848         if ((array == NULL) || (array->count() == 0))\r
849         {\r
850             // next tuner\r
851             continue;\r
852         }\r
853 \r
854         //\r
855         Dictionary *epg = (Dictionary *)array->objectAtIndex(0);\r
856         time_t start;\r
857         time_t end;\r
858         getTimeWithEPG(epg, &start, &end);\r
859         \r
860         //\r
861         // 録画停止要否チェック\r
862         //\r
863         bool stop_need = false;\r
864         while (true)\r
865         {\r
866             String *status = epg->stringForKey(KEY_EPG_STATUS);\r
867             if (status != NULL)\r
868             {\r
869                 if (status->isEqualToString("stop"))\r
870                 {\r
871                     stop_need = true;\r
872                     break;\r
873                 }\r
874                 if (!status->isEqualToString("running"))\r
875                 {\r
876                     break;\r
877                 }\r
878                 else if (_store_remain <= DEF_DISK_REMAIN)\r
879                 {\r
880                     stop_need = true;\r
881                     break;\r
882                 }\r
883             }\r
884             if (end + OFFSET_OF_END_TIME <= now)\r
885             {\r
886                 stop_need = true;\r
887             }\r
888             break;\r
889         }\r
890         if (stop_need)\r
891         {\r
892             DebugLog2("I try stop\n");\r
893             int fd =_tuners[tuner]->stopRecording();\r
894             if (fd < 0)\r
895             {\r
896                 DebugLog1("stopRecording() error.\n");\r
897             }\r
898             else\r
899             {\r
900                 DebugLog2("stopRecording() ok\n");\r
901                 DebugLog0("stop recording of \"%s\"", _tuners[tuner]->name());\r
902                 _close(fd);\r
903 \r
904                 if (_shutdown_time == 0)\r
905                 {\r
906                     _shutdown_time = DEF_SHUTDOWN_TIME;\r
907                 }\r
908             }\r
909             array->removeObject(epg);\r
910             \r
911             if (array->count() > 0)\r
912             {\r
913                 epg = (Dictionary *)array->objectAtIndex(0);\r
914             }\r
915             else\r
916             {\r
917                 epg = NULL;\r
918             }\r
919             need_update = true;\r
920         }\r
921 \r
922         if (epg == NULL)\r
923         {\r
924             // next tuner\r
925             continue;\r
926         }\r
927 \r
928         //\r
929         // 録画開始要否チェック\r
930         //\r
931         bool start_need = false;\r
932         start = end = 0;\r
933         getTimeWithEPG(epg, &start, &end);\r
934         if ((start != 0) && (end != 0))\r
935         {\r
936             String *status = epg->stringForKey(KEY_EPG_STATUS);\r
937             if ((status == NULL) || !(status->isEqualToString("running")))\r
938             {\r
939                 if (end + OFFSET_OF_END_TIME <= now)\r
940                 {\r
941                     // 既に終了時間が経過しているので削除する\r
942                     array->removeObject(epg);\r
943                 }\r
944                 else if (start + OFFSET_OF_START_TIME <= now)\r
945                 {\r
946                     start_need = true;\r
947                 }\r
948             }\r
949         }\r
950 \r
951         if (start_need)\r
952         {\r
953             DebugLog2("I need start.\n");\r
954             String *ch = epg->stringForKey(KEY_EPG_CHANNEL);\r
955             if (ch != NULL)\r
956             {\r
957                 int channel = atoi(ch->cString());\r
958                 DebugLog2("channel: %d\n", channel);\r
959                 std::string videopath = createVideoPath(tuner);\r
960                 if (videopath != "")\r
961                 {\r
962                     DebugLog2("videopath: %s\n", videopath.c_str());\r
963                     int fd = -1;\r
964                     if (_sopen_s(&fd, videopath.c_str(),\r
965                                  (_O_CREAT | _O_EXCL | _O_WRONLY | _O_BINARY | _O_TRUNC), _SH_DENYRW, (_S_IREAD | _S_IWRITE)) == 0)\r
966                     {\r
967                         DebugLog2("open ok.\n");\r
968                         bool startResult = true;\r
969                         if (_tuners[tuner]->channel() != channel)\r
970                         {\r
971                             if (!setChannel(tuner, channel))\r
972                             {\r
973                                 DebugLog3("setChannel() ng.");\r
974                                 startResult = false;\r
975                             }\r
976                         }\r
977 \r
978                         if (startResult)\r
979                         {\r
980                             if (_tuners[tuner]->startRecording(fd))\r
981                             {\r
982                                 DebugLog2("startRecording() ok.");\r
983                                 DebugLog0("start recording of \"%s\" to %s.", _tuners[tuner]->name(), videopath.c_str());\r
984                             }\r
985                             else\r
986                             {\r
987                                 DebugLog3("Tuner::startRecording() failed.");\r
988                                 startResult = false;\r
989                             }\r
990                         }\r
991 \r
992                         if (startResult)\r
993                         {\r
994                             epg->setString("running", KEY_EPG_STATUS);\r
995                         }\r
996                         else\r
997                         {\r
998                             _close(fd);\r
999                         }\r
1000                     }\r
1001                     else\r
1002                     {\r
1003                         DebugLog0("open ng. 0x%08x\n", errno);\r
1004                     }\r
1005                 }\r
1006                 else\r
1007                 {\r
1008                     DebugLog0("Can't create videopath.\n");\r
1009                 }\r
1010             }\r
1011             else\r
1012             {\r
1013                 DebugLog0("error.\n");\r
1014             }\r
1015         }\r
1016     }\r
1017 \r
1018     if (need_update)\r
1019     {\r
1020         //\r
1021         _reservations->writeToFile(_reservations_path, true);\r
1022     }\r
1023 \r
1024 #if 0\r
1025     // EPG収集時刻を取得\r
1026     String *collect_str = _props->stringForKey(KEY_COLLECT_EPG_TIME);\r
1027     if (collect_str != NULL)\r
1028     {\r
1029         // 秒に変換\r
1030         time_t collect_time = 0;\r
1031         getTimeWithString(collect_str, &collect_time);\r
1032 \r
1033         // 現在時刻と比較\r
1034         if ((collect_time <= now) && (now < collect_time + 1))\r
1035         {\r
1036             // タイマが起動中か確認\r
1037             if ((_timer_epg_s == NULL) || !_timer_epg_s->valid())\r
1038             {\r
1039                 // EPG収集用タイマ起動(ISDB-S)\r
1040                 RELEASE(_timer_epg_s);\r
1041                 _timer_epg_s = Timer::alloc()->initWithTimeInterval(DEF_COLLECT_EPG_DELAY, this, (void *)CMD_COLLECT_EPG_ISDB_S, true);\r
1042                 if (_timer_epg_s != NULL)\r
1043                 {\r
1044                     _timer_epg_s->fire();\r
1045                 }\r
1046             }\r
1047 \r
1048             // タイマが起動中か確認\r
1049             if ((_timer_epg_t == NULL) || !_timer_epg_t->valid())\r
1050             {\r
1051                 // EPG収集用タイマ起動(ISDB-T)\r
1052                 RELEASE(_timer_epg_t);\r
1053                 _timer_epg_t = Timer::alloc()->initWithTimeInterval(DEF_COLLECT_EPG_DELAY, this, (void *)CMD_COLLECT_EPG_ISDB_T, true);\r
1054                 if (_timer_epg_t != NULL)\r
1055                 {\r
1056                     _timer_epg_t->fire();\r
1057                 }\r
1058             }\r
1059         }\r
1060     }\r
1061 #endif\r
1062 \r
1063     // unlock\r
1064     RaymUnlock(this);\r
1065 \r
1066     //\r
1067     // 1/100秒単位が 0 に近くなるように次回T.O.を微調整\r
1068     // ただし、windowsは精度が低いので期待しないことw\r
1069     //\r
1070 \r
1071 #if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)\r
1072     static const __time64_t DELTA_EPOCH_IN_MICROSECS = 11644473600000000Ui64;\r
1073 #else\r
1074     static const __time64_t DELTA_EPOCH_IN_MICROSECS = 11644473600000000ULL;\r
1075 #endif\r
1076     // 現在時刻を取得\r
1077     FILETIME ft;\r
1078     GetSystemTimeAsFileTime(&ft);\r
1079 \r
1080     // EPOCH秒への変換\r
1081     __time64_t now_sec;\r
1082     __time64_t now_usec;\r
1083     now_sec = ft.dwHighDateTime;\r
1084     now_sec <<= 32;\r
1085     now_sec |= ft.dwLowDateTime;\r
1086     now_sec /= 10;  /*convert into microseconds*/\r
1087     now_sec -= DELTA_EPOCH_IN_MICROSECS;\r
1088     now_usec = (now_sec % 1000000UL);\r
1089     now_sec = now_sec / 1000000UL;\r
1090 \r
1091     TimeInterval interval = (TimeInterval)now_usec;\r
1092     interval = interval / 1000000;\r
1093     _timer_periodic->setTimeInterval(1.005 - interval);\r
1094 \r
1095 #ifdef OBJC_MEMORY_CHECK\r
1096     DebugLog0("global_objc_count_ = %d", Raym::global_objc_count_);\r
1097 #endif\r
1098 }\r
1099 \r
1100 void Controller::updateKeywordsReservation()\r
1101 {\r
1102     DebugLog2("Controller::updateKeywordsReservation()");\r
1103 \r
1104     // lock\r
1105     RaymLock(this);\r
1106 \r
1107     // 予約情報からキーワード予約フラグが設定されているものを削除\r
1108     for (int tuner = 0; tuner < _tunerCount; ++tuner)\r
1109     {\r
1110         Array *epgs = _reservations->arrayForKey(_tuners[tuner]->name());\r
1111         if (epgs != NULL)\r
1112         {\r
1113             for (uint epgs_idx_offset = epgs->count(); epgs_idx_offset > 0; --epgs_idx_offset)\r
1114             {\r
1115                 Dictionary *epg = (Dictionary *)epgs->objectAtIndex(epgs_idx_offset - 1);\r
1116                 if (epg->boolForKey(KEY_EPG_RESERVED_BY_KEYWORDS))\r
1117                 {\r
1118                     epgs->removeObject(epg);\r
1119                 }\r
1120             }\r
1121         }\r
1122     }\r
1123 \r
1124     // キーワードで検索\r
1125     Dictionary *keywords_info = _reservations->dictionaryForKey(KEY_EPG_KEYWORDS);\r
1126 \r
1127     if ((keywords_info != NULL) && (_epgs != NULL) && (_epgs->dictionaryForKey(KEY_SERVICES) != NULL))\r
1128     {\r
1129         // キーワード、EPG有り\r
1130 \r
1131         // _epgs からキー(サービスID)を取得\r
1132         Array *service_id_keys = _epgs->dictionaryForKey(KEY_SERVICES)->allKeys();\r
1133 \r
1134         // サービスIDでループ\r
1135         for (uint service_id_keys_idx = 0; service_id_keys_idx < service_id_keys->count(); ++service_id_keys_idx)\r
1136         {\r
1137             // サービスID\r
1138             String *service_id = (String *)service_id_keys->objectAtIndex(service_id_keys_idx);\r
1139 \r
1140             // 局名\r
1141             String *stationName = stationNameForServiceID(service_id);\r
1142             if (stationName == NULL)\r
1143             {\r
1144                 // 局名が取得できなかったら次のサービスIDへ\r
1145                 continue;\r
1146             }\r
1147 \r
1148             // 指定サービスIDのEPGを取得\r
1149             Array *events = _epgs->dictionaryForKey(KEY_SERVICES)->arrayForKey(service_id);\r
1150 \r
1151             //\r
1152             for (uint events_idx = 0; events_idx < events->count(); ++events_idx)\r
1153             {\r
1154                 // 指定イベントIDのEPGを取得\r
1155                 Dictionary *epg = (Dictionary *)events->objectAtIndex(events_idx);\r
1156 \r
1157                 // 取得したEPGがキーワードの条件にマッチするか\r
1158 //                DebugLog0("sid: %s, eid: %s", service_id->cString(), epg->stringForKey(KEY_EPG_EVENT_ID)->cString());\r
1159 \r
1160                 time_t start;\r
1161                 getTimeWithString(epg->stringForKey(KEY_EPG_START), &start);\r
1162 \r
1163                 // キーワードの配列を取得\r
1164                 Array *keywords = keywords_info->allKeys();\r
1165                 for (uint keywords_idx = 0; keywords_idx < keywords->count(); ++keywords_idx)\r
1166                 {\r
1167                     // キーワード\r
1168                     String *kwd = (String *)keywords->objectAtIndex(keywords_idx);\r
1169 //                    DebugLog0("kwd: %s", kwd->cString());\r
1170 \r
1171                     // キーワード(分割)\r
1172                     Array *kwds = kwd->componentsSeparatedByString(",");\r
1173                     if (kwds == NULL)\r
1174                     {\r
1175                         continue;\r
1176                     }\r
1177 //                    DebugLog0("check 01");\r
1178 \r
1179                     // キーワード情報\r
1180                     Dictionary *kwdinf = keywords_info->dictionaryForKey(kwd);\r
1181 \r
1182                     // 局名フィルタ\r
1183                     String *kwdinf_service_id = kwdinf->stringForKey(KEY_EPG_SERVICE_ID);\r
1184                     if (kwdinf_service_id != NULL)\r
1185                     {\r
1186                         // 局名フィルタ有り\r
1187                         String *sname = stationNameForServiceID(kwdinf_service_id);\r
1188                         if (sname != NULL)\r
1189                         {\r
1190                             if (!stationName->isEqualToString(sname))\r
1191                             {\r
1192                                 // 局名が異なる場合は次のキーワードへ\r
1193                                 continue;\r
1194                             }\r
1195                         }\r
1196                     }\r
1197 //                    DebugLog0("check 02");\r
1198 \r
1199                     // 開始時刻フィルタ\r
1200                     String *kwdinf_start = kwdinf->stringForKey(KEY_EPG_START);\r
1201                     if (kwdinf_start != NULL)\r
1202                     {\r
1203                         // 開始時刻フィルタ有り\r
1204                         String *st = kwdinf_start->stringByAppendingString(":00");\r
1205                         time_t kwd_st, kwd_ed;\r
1206                         getTimeWithString(st, &kwd_st);\r
1207                         kwd_ed = kwd_st + 3600;\r
1208 \r
1209                         // 開始時刻フィルタの時刻 <= EPGの開始時刻 <= 開始時刻フィルタの時刻+60min ならOK\r
1210                         if ((kwd_st > start) || (start > kwd_ed))\r
1211                         {\r
1212                             // 範囲外なので次のキーワードへ\r
1213                             continue;\r
1214                         }\r
1215                     }\r
1216 //                    DebugLog0("check 03");\r
1217 \r
1218                     // タイトル\r
1219                     String *title = epg->stringForKey(KEY_EPG_TITLE);\r
1220                     bool title_flag = (title != NULL);\r
1221 \r
1222                     // 概要\r
1223                     String *desc = epg->stringForKey(KEY_EPG_DESCRIPTION);\r
1224                     bool desc_flag = (desc != NULL);\r
1225 \r
1226                     // キーワード(分割)がタイトル/概要のどちらかに、全て(AND)含まれていたら予約対象とする\r
1227                     for (uint kwds_idx = 0; kwds_idx < kwds->count(); ++kwds_idx)\r
1228                     {\r
1229                         String *kw = ((String *)kwds->objectAtIndex(kwds_idx))->stringByTrimming();\r
1230 \r
1231                         // タイトル\r
1232                         if (title != NULL)\r
1233                         {\r
1234                             Range r = title->rangeOfString(kw);\r
1235                             if (r.location == NotFound)\r
1236                             {\r
1237                                 title_flag = false;\r
1238                             }\r
1239                         }\r
1240 \r
1241                         // 概要\r
1242                         if (desc != NULL)\r
1243                         {\r
1244                             Range r = desc->rangeOfString(kw);\r
1245                             if (r.location == NotFound)\r
1246                             {\r
1247                                 desc_flag = false;\r
1248                             }\r
1249                         }\r
1250                     }\r
1251 \r
1252                     if (title_flag || desc_flag)\r
1253                     {\r
1254                         // タイトル/概要のどちらかに、キーワード(分割)が全て含まれていたので\r
1255                         // この EPG を予約対象とする\r
1256                         Dictionary *epg2 = Dictionary::dictionaryWithDictionary(epg);\r
1257                         if (epg2 != NULL)\r
1258                         {\r
1259 //                            DebugLog0("matched kwd: %s", kwd->cString());\r
1260                             if (title != NULL)\r
1261                             {\r
1262 //                                DebugLog0("title: %s", title->cString());\r
1263                             }\r
1264                             if (desc != NULL)\r
1265                             {\r
1266 //                                DebugLog0("desc: %s", desc->cString());\r
1267                             }\r
1268                             epg2->setBool(true, KEY_EPG_RESERVED_BY_KEYWORDS);\r
1269 //DebugLog0("epg2: %s", epg2->toString().c_str());\r
1270                             reserve(epg2);\r
1271                         }\r
1272                         else\r
1273                         {\r
1274 //DebugLog0("epg copy failed");\r
1275                         }\r
1276                     }\r
1277                 }\r
1278             }\r
1279         }\r
1280     }\r
1281 \r
1282     // unlock\r
1283     RaymUnlock(this);\r
1284 }\r
1285 \r
1286 void Controller::updateSchedule()\r
1287 {\r
1288     DebugLog2("Controller::updateSchedule()");\r
1289 \r
1290     // lock\r
1291     RaymLock(this);\r
1292 \r
1293     // レジューム時刻\r
1294     time_t resume_time = 0;\r
1295 \r
1296     // EPG収集時刻を取得\r
1297     String *collect_epg_time = _props->stringForKey(KEY_COLLECT_EPG_TIME);\r
1298     if (collect_epg_time != NULL)\r
1299     {\r
1300         getTimeWithString(collect_epg_time, &resume_time);\r
1301     }\r
1302 \r
1303     // 録画予約をチェック\r
1304     for (int tuner = 0; tuner < _tunerCount; ++tuner)\r
1305     {\r
1306         Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
1307         if ((array == NULL) || (array->count() == 0))\r
1308         {\r
1309             // next tuner\r
1310             continue;\r
1311         }\r
1312 \r
1313         Dictionary *epg = (Dictionary *)array->objectAtIndex(0);\r
1314         time_t start = 0;\r
1315         time_t end = 0;\r
1316         getTimeWithEPG(epg, &start, &end);\r
1317         if ((start != 0) && (end != 0))\r
1318         {\r
1319             if (start < resume_time)\r
1320             {\r
1321                 resume_time = start;\r
1322             }\r
1323         }\r
1324     }\r
1325 \r
1326     //\r
1327     TM resume_tm;\r
1328     resume_time += OFFSET_OF_WAKEUP;    // 起動時刻を調整\r
1329     if (localtime_s(&resume_tm, &resume_time) == 0)\r
1330     {\r
1331         if (_props->stringForKey(KEY_POWER_MANAGER) != NULL)\r
1332         {\r
1333             char http_req[256];\r
1334             sprintf_s(http_req, "http://%s/iptv.pl?wakeup=%04d/%02d/%02d_%02d:%02d:00",\r
1335                       _props->stringForKey(KEY_POWER_MANAGER)->cString(),\r
1336                       resume_tm.tm_year + 1900, resume_tm.tm_mon + 1,\r
1337                       resume_tm.tm_mday, resume_tm.tm_hour, resume_tm.tm_min);\r
1338             URL *url = URL::URLWithString(http_req);\r
1339             URLRequest *req = URLRequest::requestWithURL(url);\r
1340             URLResponse *resp = NULL;\r
1341             Error *error = NULL;\r
1342             Data *data = URLConnection::sendSynchronousRequest(req, &resp, &error);\r
1343             if (data != NULL)\r
1344             {\r
1345                 String *html = String::stringWithUTF8String((const char *)data->bytes());\r
1346                 if ((html != NULL) && (html->rangeOfString("<body>OK</body>").location != NotFound))\r
1347                 {\r
1348                     DebugLog0("set wake schedule to \"%04d/%02d/%02d %02d:%02d:00\".",\r
1349                               resume_tm.tm_year + 1900, resume_tm.tm_mon + 1, resume_tm.tm_mday, resume_tm.tm_hour, resume_tm.tm_min);\r
1350                 }\r
1351             }\r
1352         }\r
1353         else\r
1354         {\r
1355             resetWakeSchedule();\r
1356             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
1357             {\r
1358                 DebugLog0("set wake schedule to \"%04d/%02d/%02d %02d:%02d:00\".",\r
1359                           resume_tm.tm_year + 1900, resume_tm.tm_mon + 1, resume_tm.tm_mday, resume_tm.tm_hour, resume_tm.tm_min);\r
1360             }\r
1361         }\r
1362     }\r
1363 \r
1364     // unlock\r
1365     RaymUnlock(this);\r
1366 }\r
1367 \r
1368 #ifndef _WIN32\r
1369 #pragma mark '\r
1370 #pragma mark ------- チューナ制御 -------\r
1371 #endif\r
1372 \r
1373 void Controller::scanChannel(int tuner)\r
1374 {\r
1375     DebugLog2("Controller::scanChannel(%d)", tuner);\r
1376 \r
1377     if ((tuner < 0) || (_tunerCount <= tuner))\r
1378     {\r
1379         DebugLog3("Invalid tuner: %d", tuner);\r
1380         return;\r
1381     }\r
1382 \r
1383     DebugLog0("start channel scan of \"%s\".", _tuners[tuner]->name());\r
1384 \r
1385     // lock\r
1386     RaymLock(this);\r
1387 \r
1388     // 設定ファイルから全チューナ情報取得\r
1389     Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
1390     if (tunersInfo == NULL)\r
1391     {\r
1392         // 無ければ作成\r
1393         tunersInfo = Dictionary::dictionaryWithCapacity(0);\r
1394         _props->setObject(tunersInfo, KEY_TUNERS);\r
1395     }\r
1396 \r
1397     // 全チューナ情報から指定のチューナ情報を取得\r
1398     Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
1399     if (tunerInfo == NULL)\r
1400     {\r
1401         // 無ければ作成\r
1402         tunerInfo = Dictionary::dictionaryWithCapacity(0);\r
1403         tunersInfo->setObject(tunerInfo, _tuners[tuner]->name());\r
1404     }\r
1405 \r
1406     // チューナを未初期化に設定\r
1407     tunerInfo->setBool(false, KEY_INITIALIZED);\r
1408 \r
1409     // unlock\r
1410     RaymUnlock(this);\r
1411 \r
1412     // 全チャンネル情報を作成\r
1413     Dictionary *channels = Dictionary::dictionaryWithCapacity(0);\r
1414     tunerInfo->setObject(channels, KEY_CHANNELS);\r
1415 \r
1416     // チューナタイプにより最大チャンネル数を設定\r
1417     int max_channel;\r
1418     Tuner::Type type = _tuners[tuner]->type();\r
1419     if (type == Tuner::ISDB_S)\r
1420     {\r
1421         max_channel = Tuner::MAX_CHANNELS_ISDB_S;\r
1422     }\r
1423     else\r
1424     {\r
1425         max_channel = Tuner::MAX_CHANNELS_ISDB_T;\r
1426     }\r
1427 \r
1428     // 最終設定成功チャンネル保持用変数\r
1429     int lastChannel = -1;\r
1430 \r
1431     // チャンネルサーチ\r
1432     for (int ch = 0; ch <= max_channel; ++ch)\r
1433     {\r
1434         // チャンネル情報\r
1435         Dictionary *channelInfo = Dictionary::dictionaryWithCapacity(0);\r
1436 \r
1437         // チャンネルキー(10進数:PTに対するチャンネル番号)\r
1438         char chkey[4];\r
1439         sprintf_s(chkey, sizeof(chkey), "%03d", ch);\r
1440         channels->setObject(channelInfo, chkey);\r
1441 \r
1442         // チャンネルID(論理チャンネル番号)\r
1443         char channelID[8];\r
1444         if (type == Tuner::ISDB_S)\r
1445         {\r
1446             if (ch < 12)\r
1447             {\r
1448                 sprintf_s(channelID, sizeof(channelID), "BS%02d", 1 + 2 * ch);\r
1449             }\r
1450             else if (ch < 24)\r
1451             {\r
1452                 sprintf_s(channelID, sizeof(channelID), "ND%02d", 2 + 2 * (ch - 12));\r
1453             }\r
1454             else\r
1455             {\r
1456                 sprintf_s(channelID, sizeof(channelID), "ND%02d", 1 + 2 * (ch -24));\r
1457             }\r
1458         }\r
1459         else\r
1460         {\r
1461             static int TABLE[][3] =\r
1462             {\r
1463                 {   2,  0,   3 },\r
1464                 {  12,  1,  22 },\r
1465                 {  21,  0,  12 },\r
1466                 {  62,  1,  63 },\r
1467                 { 112,  0,  62 }\r
1468             };\r
1469 \r
1470             uint i;\r
1471             for (i = 0; i < sizeof(TABLE)/sizeof(*TABLE); ++i)\r
1472             {\r
1473                 if (ch <= TABLE[i][0])\r
1474                 {\r
1475                     sprintf_s(channelID, sizeof(channelID), "%s%d", TABLE[i][1] ? "C" : "", ch + TABLE[i][2] - TABLE[i][0]);\r
1476                     break;\r
1477                 }\r
1478             }\r
1479         }\r
1480 \r
1481         // チャンネルIDを設定\r
1482         channelInfo->setString(channelID, KEY_CHANNEL_ID);\r
1483 \r
1484         // lock\r
1485         RaymLock(this);\r
1486 \r
1487         // チャンネル設定\r
1488         if (_tuners[tuner]->setChannel(ch))\r
1489         {\r
1490             // 設定成功\r
1491             DebugLog0("  CH %s: OK", chkey);\r
1492 \r
1493             // チャンネルを有効に設定\r
1494             channelInfo->setBool(true, KEY_ENABLED);\r
1495 \r
1496             // 局情報を取得\r
1497             Extractor *extractor = Extractor::alloc()->init();\r
1498             _tuners[tuner]->setListener(extractor);\r
1499 \r
1500             Dictionary *stationInfo = extractor->stationInfo();\r
1501             if (stationInfo != NULL)\r
1502             {\r
1503                 if (stationInfo->stringForKey(KEY_NAME) != NULL)\r
1504                 {\r
1505                     channelInfo->setString(stationInfo->stringForKey(KEY_NAME), KEY_NAME);\r
1506                 }\r
1507                 if (stationInfo->objectForKey(KEY_SERVICES) != NULL)\r
1508                 {\r
1509                     channelInfo->setObject(stationInfo->objectForKey(KEY_SERVICES), KEY_SERVICES);\r
1510                 }\r
1511                 if (channelInfo->stringForKey(KEY_NAME) != NULL)\r
1512                 {\r
1513                     DebugLog0("    Name: %s", channelInfo->stringForKey(KEY_NAME)->cString());\r
1514                 }\r
1515             }\r
1516 \r
1517             _tuners[tuner]->setListener(NULL);\r
1518             extractor->release();\r
1519 \r
1520             // 成功したチャンネルを保持\r
1521             lastChannel = ch;\r
1522         }\r
1523         else\r
1524         {\r
1525             DebugLog0("  CH %s: NG", chkey);\r
1526         }\r
1527 \r
1528         RaymUnlock(this);\r
1529     }\r
1530 \r
1531     RaymLock(this);\r
1532 \r
1533     // チューナを初期化済みに更新\r
1534     tunerInfo->setBool(true, KEY_INITIALIZED);\r
1535 \r
1536     // 設定が成功したチャンネルが有るか\r
1537     if (lastChannel >= 0)\r
1538     {\r
1539         // チューナを有効に設定\r
1540         tunerInfo->setBool(true, KEY_ENABLED);\r
1541 \r
1542         // 最終チャンネルをステータスファイルに設定\r
1543         Dictionary *tunersInfo = _status->dictionaryForKey(KEY_TUNERS);\r
1544         if (tunersInfo == NULL)\r
1545         {\r
1546             tunersInfo = Dictionary::dictionaryWithCapacity(0);\r
1547             _status->setObject(tunersInfo, KEY_TUNERS);\r
1548         }\r
1549 \r
1550         Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
1551         if (tunerInfo == NULL)\r
1552         {\r
1553             tunerInfo = Dictionary::dictionaryWithCapacity(0);\r
1554             tunersInfo->setObject(tunerInfo, _tuners[tuner]->name());\r
1555         }\r
1556 \r
1557         tunerInfo->setInteger(lastChannel, KEY_CHANNEL);\r
1558 \r
1559         _status->writeToFile(_status_path, true);\r
1560     }\r
1561 \r
1562     // 設定ファイルを更新\r
1563     _props->writeToFile(_props_path, true);\r
1564 \r
1565     // unlock\r
1566     RaymUnlock(this);\r
1567 }\r
1568 \r
1569 int Controller::getChannel(int tuner)\r
1570 {\r
1571     DebugLog2("Controller::getChannel()");\r
1572 \r
1573     int channel = -1;\r
1574 \r
1575     if ((0 <= tuner) && (tuner < _tunerCount))\r
1576     {\r
1577         // lock\r
1578         RaymLock(this);\r
1579 \r
1580         Dictionary *tunersInfo = _status->dictionaryForKey(KEY_TUNERS);\r
1581         if (tunersInfo != NULL)\r
1582         {\r
1583             Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
1584             if (tunerInfo != NULL)\r
1585             {\r
1586                 channel = tunerInfo->integerForKey(KEY_CHANNEL);\r
1587             }\r
1588         }\r
1589 \r
1590         // unlock\r
1591         RaymUnlock(this);\r
1592     }\r
1593 \r
1594     return channel;\r
1595 }\r
1596 \r
1597 bool Controller::setChannel(int tuner, int channel)\r
1598 {\r
1599     DebugLog2("Controller::setChannel()");\r
1600 \r
1601     bool result = false;\r
1602 \r
1603     if ((0 <= tuner) && (tuner < _tunerCount))\r
1604     {\r
1605         // lock\r
1606         RaymLock(this);\r
1607 \r
1608         if (!_tuners[tuner]->isLocked())\r
1609         {\r
1610             Dictionary *tunersInfo = _status->dictionaryForKey(KEY_TUNERS);\r
1611             if (tunersInfo == NULL)\r
1612             {\r
1613                 tunersInfo = Dictionary::dictionaryWithCapacity(0);\r
1614                 _status->setObject(tunersInfo, KEY_TUNERS);\r
1615             }\r
1616 \r
1617             Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
1618             if (tunerInfo == NULL)\r
1619             {\r
1620                 tunerInfo = Dictionary::dictionaryWithCapacity(0);\r
1621                 tunersInfo->setObject(tunerInfo, _tuners[tuner]->name());\r
1622             }\r
1623 \r
1624             if (channel != _tuners[tuner]->channel())\r
1625             {\r
1626                 if (_tuners[tuner]->setChannel(channel))\r
1627                 {\r
1628                     tunerInfo->setInteger(channel, KEY_CHANNEL);\r
1629 \r
1630                     _status->writeToFile(_status_path, true);\r
1631 \r
1632                     result = true;\r
1633                 }\r
1634                 else\r
1635                 {\r
1636                     DebugLog0("set channel failed. %d, %d", tuner, channel);\r
1637                 }\r
1638             }\r
1639         }\r
1640 \r
1641         // unlock\r
1642         RaymUnlock(this);\r
1643     }\r
1644 \r
1645     return result;\r
1646 }\r
1647 \r
1648 #ifndef _WIN32\r
1649 #pragma mark '\r
1650 #pragma mark ------- システム関連 周期処理 -------\r
1651 #endif\r
1652 \r
1653 //\r
1654 // チューナ(streaming)制御/システム関連 周期処理\r
1655 //\r
1656 void Controller::periodic_2(void)\r
1657 {\r
1658     time_t now = time(NULL);\r
1659 \r
1660     //\r
1661     // UDPポート監視\r
1662     //\r
1663 \r
1664     // マッピング(UDPPort:tuner,ch)情報取得\r
1665     Dictionary *mapping = NULL;\r
1666     if ((_streaming_ctrls != NULL) && ((mapping = _streaming_ctrls->dictionaryForKey(KEY_MAPPING_UDP_TO_TUNER_SERVICE_ID)) != NULL))\r
1667     {\r
1668         // マッピング情報取得OK\r
1669 \r
1670         // 使用中のUDPの情報を取得\r
1671         // どれだけ使用中なのか不明なので、まずは必要サイズを調べる\r
1672         DWORD size = 0;\r
1673         if (GetExtendedUdpTable(NULL, &size, true, AF_INET, UDP_TABLE_OWNER_PID, 0) == ERROR_INSUFFICIENT_BUFFER)\r
1674         {\r
1675             // ERROR_INSUFFICIENT_BUFFER の場合、必要なバッファサイズが size に格納される\r
1676 \r
1677             // バッファ確保\r
1678             PMIB_UDPTABLE_OWNER_PID udptable = (PMIB_UDPTABLE_OWNER_PID)malloc(size);\r
1679             if (udptable != NULL)\r
1680             {\r
1681                 // バッファ確保OK\r
1682 \r
1683                 // UDP情報取得\r
1684                 if (GetExtendedUdpTable(udptable, &size, true, AF_INET, UDP_TABLE_OWNER_PID, 0) == NO_ERROR)\r
1685                 {\r
1686                     // 取得OK\r
1687                     DebugLog3("udptable->dwNumEntries: %d", udptable->dwNumEntries);\r
1688 \r
1689                     // 停止要否確認\r
1690                     Dictionary *using_port = _streaming_ctrls->dictionaryForKey(KEY_UDP_IN_USE);\r
1691                     if (using_port != NULL)\r
1692                     {\r
1693                         // key = 使用中ポート\r
1694                         Array *using_ports = using_port->allKeys();\r
1695                         if (using_ports != NULL)\r
1696                         {\r
1697                             // 使用中ポートでループ\r
1698                             for (uint i = 0; i < using_ports->count(); ++i)\r
1699                             {\r
1700                                 // 停止要否フラグ\r
1701                                 bool stop_need = true;\r
1702 \r
1703                                 // 使用中のUDP情報でループ\r
1704                                 for (uint j = 0; j < udptable->dwNumEntries; ++j)\r
1705                                 {\r
1706                                     if (((String *)using_ports->objectAtIndex(i))->intValue() == ntohs((WORD)udptable->table[j].dwLocalPort))\r
1707                                     {\r
1708                                         // 使用中なので停止不要\r
1709                                         stop_need = false;\r
1710                                         break;\r
1711                                     }\r
1712                                 }\r
1713 \r
1714                                 // 停止要否\r
1715                                 if (stop_need)\r
1716                                 {\r
1717                                     // マッピング情報を取得\r
1718                                     String *tuner_and_service_id = mapping->stringForKey((String *)using_ports->objectAtIndex(i));\r
1719                                     if (tuner_and_service_id != NULL)\r
1720                                     {\r
1721                                          // チューナとサービスIDに分割\r
1722                                         Range r = tuner_and_service_id->rangeOfString("_");\r
1723                                         if (r.location != NotFound)\r
1724                                         {\r
1725                                             int tuner = tuner_and_service_id->substringToIndex(r.location)->intValue();\r
1726                                             int service_id = tuner_and_service_id->substringFromIndex(r.location + 1)->intValue();\r
1727                                             DebugLog3("tuner: %d, service_id: %d", tuner, service_id);\r
1728 \r
1729                                             DebugLog0("auto streaming stop: %s", ((String *)using_ports->objectAtIndex(i))->cString());\r
1730 \r
1731                                             _tuners[tuner]->stopStreaming();\r
1732                                             using_port->removeObjectForKey((String *)using_ports->objectAtIndex(i));\r
1733                                         }\r
1734                                     }\r
1735                                 }\r
1736                             }\r
1737                         }\r
1738                     }\r
1739 \r
1740 \r
1741                     // 起動要否確認\r
1742                     for (uint i = 0; i < udptable->dwNumEntries; ++i)\r
1743                     {\r
1744                         // ポート番号を文字列に変換して\r
1745                         char port[10];\r
1746                         sprintf_s(port, "%d", ntohs((WORD)udptable->table[i].dwLocalPort));\r
1747                         DebugLog3("port = %s", port);\r
1748 \r
1749                         // マッピング情報を取得\r
1750                         String *tuner_and_service_id = mapping->stringForKey(port);\r
1751                         if (tuner_and_service_id != NULL)\r
1752                         {\r
1753                             // 取得OK: 監視対象ポートが使用されている\r
1754 \r
1755                             // 使用アプリを調べる\r
1756                             bool auto_streaming = false;\r
1757                             char exec_path[MAX_PATH];\r
1758                             memset(exec_path, 0, sizeof(exec_path));\r
1759 \r
1760                             // プロセスハンドル取得\r
1761                             size_t returnValue;\r
1762                             HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, udptable->table[i].dwOwningPid);\r
1763                             if (hProcess != NULL)\r
1764                             {\r
1765                                 TCHAR exec[MAX_PATH];\r
1766                                 memset(exec, 0, sizeof(exec));\r
1767                                 DWORD len = sizeof(exec) - 1;\r
1768 \r
1769                                 // イメージ取得\r
1770                                 if (QueryFullProcessImageName(hProcess, 0, exec, &len))\r
1771                                 {\r
1772                                     // ワイド -> マルチ 変換\r
1773                                     if (wcstombs_s(&returnValue, exec_path, sizeof(exec_path), exec, _TRUNCATE) == 0)\r
1774                                     {\r
1775                                         // 成功\r
1776 \r
1777                                         // とりあえず、、、現状は "ffmpeg.exe" / "vlc.exe" / "Kodi.exe" があったら auto_streaming を true にする\r
1778                                         if ((strstr(exec_path, "ffmpeg.exe") != NULL) ||\r
1779                                             (strstr(exec_path, "vlc.exe") != NULL) ||\r
1780                                             (strstr(exec_path, "Kodi.exe") != NULL))\r
1781                                         {\r
1782                                             auto_streaming = true;\r
1783                                         }\r
1784                                     }\r
1785                                 }\r
1786 \r
1787                                 // プロセスハンドル解放\r
1788                                 CloseHandle(hProcess);\r
1789                             }\r
1790 \r
1791                             if (auto_streaming)\r
1792                             {\r
1793                                 // チューナとサービスIDに分割\r
1794                                 Range r = tuner_and_service_id->rangeOfString("_");\r
1795                                 if (r.location != NotFound)\r
1796                                 {\r
1797                                     int tuner = tuner_and_service_id->substringToIndex(r.location)->intValue();\r
1798                                     int service_id = tuner_and_service_id->substringFromIndex(r.location + 1)->intValue();\r
1799                                     DebugLog3("tuner: %d, service_id: %d", tuner, service_id);\r
1800 \r
1801                                     // 暫定\r
1802                                     int channel = -1;\r
1803                                     if (_streaming_ctrls->dictionaryForKey(KEY_MAPPING_TUNER_SERVICE_ID_TO_CHANNEL) != NULL)\r
1804                                     {\r
1805                                         channel = _streaming_ctrls->dictionaryForKey(KEY_MAPPING_TUNER_SERVICE_ID_TO_CHANNEL)->integerForKey(tuner_and_service_id);\r
1806                                     }\r
1807 \r
1808                                     // lock\r
1809                                     RaymLock(this);\r
1810 \r
1811                                     // ロックされてない場合\r
1812                                     if (!_tuners[tuner]->isLocked())\r
1813                                     {\r
1814                                         // ストリーミング開始可能\r
1815 \r
1816                                         if (_tuners[tuner]->channel() != channel)\r
1817                                         {\r
1818                                             setChannel(tuner, channel);\r
1819                                         }\r
1820 \r
1821                                         SOCKADDR_IN dst_addr;\r
1822                                         dst_addr.sin_family = AF_INET;\r
1823                                         dst_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);\r
1824                                         dst_addr.sin_port = (WORD)udptable->table[i].dwLocalPort;\r
1825 \r
1826                                         if (_tuners[tuner]->startStreaming(&dst_addr))\r
1827                                         {\r
1828                                             // 成功\r
1829                                             DebugLog0("auto streaming start: %d", ntohs((WORD)udptable->table[i].dwLocalPort));\r
1830 \r
1831                                             // 使用中ポートに登録\r
1832                                             using_port = _streaming_ctrls->dictionaryForKey(KEY_UDP_IN_USE);\r
1833                                             if (using_port == NULL)\r
1834                                             {\r
1835                                                 using_port = Dictionary::dictionaryWithCapacity(0);\r
1836                                                 _streaming_ctrls->setObject(using_port, KEY_UDP_IN_USE);\r
1837                                             }\r
1838                                             using_port->setBool(true, port);\r
1839                                         }\r
1840                                     }\r
1841 \r
1842                                     // unlock\r
1843                                     RaymUnlock(this);\r
1844                                 }\r
1845                             }\r
1846                         }\r
1847                     }\r
1848                 }\r
1849 \r
1850                 // バッファ解放\r
1851                 free(udptable);\r
1852             }\r
1853         }\r
1854     }\r
1855 \r
1856     //\r
1857     // HLS\r
1858     //\r
1859     Dictionary *hls_info = _streaming_ctrls->dictionaryForKey(KEY_HLS_INFO);\r
1860     if (hls_info != NULL)\r
1861     {\r
1862         for (int i = 0; i < _tunerCount; ++i)\r
1863         {\r
1864             RaymLock(this);\r
1865             Dictionary *hls_info_tuner = hls_info->dictionaryForKey(_tuners[i]->name());\r
1866             if (hls_info_tuner != NULL)\r
1867             {\r
1868                 int counter = hls_info_tuner->integerForKey(KEY_COUNTER);\r
1869                 if (counter < 60)\r
1870                 {\r
1871                     hls_info_tuner->setInteger(counter + 1, KEY_COUNTER);\r
1872                 }\r
1873                 else\r
1874                 {\r
1875                     hls_info->removeObjectForKey(_tuners[i]->name());\r
1876                 }\r
1877             }\r
1878             RaymUnlock(this);\r
1879         }\r
1880     }\r
1881 \r
1882     if ((now % 60) == 10)\r
1883     {\r
1884         if (isIdleState())\r
1885         {\r
1886             RaymLock(this);\r
1887             if (((_timer_epg_s != NULL) && _timer_epg_s->valid()) ||\r
1888                 ((_timer_epg_t != NULL) && _timer_epg_t->valid()))\r
1889             {\r
1890                 _idle_count = 0;\r
1891             }\r
1892             else\r
1893             {\r
1894                 ++_idle_count;\r
1895 \r
1896                 DebugLog0("_idle_count: %d, _shutdown_time: %d", _idle_count, ((_shutdown_time == 0) ? DEF_SHUTDOWN_TIME_INIT : _shutdown_time));\r
1897                 if (_idle_count >= ((_shutdown_time == 0) ? DEF_SHUTDOWN_TIME_INIT : _shutdown_time))\r
1898                 {\r
1899                     RaymUnlock(this);\r
1900 \r
1901                     bool shutdown = (_props->stringForKey(KEY_POWER_MANAGER) != NULL);\r
1902 \r
1903                     //\r
1904                     if (shutdown)\r
1905                     {\r
1906                         Service::shutdown();\r
1907                     }\r
1908                     else\r
1909                     {\r
1910                         Service::sleep();\r
1911                     }\r
1912 \r
1913                     RaymLock(this);\r
1914                     _idle_count = 0;\r
1915                 }\r
1916             }\r
1917             RaymUnlock(this);\r
1918         }\r
1919         else\r
1920         {\r
1921             RaymLock(this);\r
1922             _idle_count = 0;\r
1923             RaymUnlock(this);\r
1924         }\r
1925     }\r
1926 \r
1927     if ((now % 60) == 20)\r
1928     {\r
1929         RaymLock(this);\r
1930         _store_remain = FileManager::freeSpaceForPath(_store_path);\r
1931         RaymUnlock(this);\r
1932     }\r
1933 \r
1934 \r
1935     //\r
1936     // 1/100秒単位が 0 に近くなるように次回T.O.を微調整\r
1937     //\r
1938 \r
1939 #if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)\r
1940     static const __time64_t DELTA_EPOCH_IN_MICROSECS = 11644473600000000Ui64;\r
1941 #else\r
1942     static const __time64_t DELTA_EPOCH_IN_MICROSECS = 11644473600000000ULL;\r
1943 #endif\r
1944     // 現在時刻を取得\r
1945     FILETIME ft;\r
1946     GetSystemTimeAsFileTime(&ft);\r
1947 \r
1948     // EPOCH秒への変換\r
1949     __time64_t now_sec;\r
1950     __time64_t now_usec;\r
1951     now_sec = ft.dwHighDateTime;\r
1952     now_sec <<= 32;\r
1953     now_sec |= ft.dwLowDateTime;\r
1954     now_sec /= 10;  /*convert into microseconds*/\r
1955     now_sec -= DELTA_EPOCH_IN_MICROSECS;\r
1956     now_usec = (now_sec % 1000000UL);\r
1957     now_sec = now_sec / 1000000UL;\r
1958 \r
1959     TimeInterval interval = (TimeInterval)now_usec;\r
1960     interval = interval / 1000000;\r
1961     _timer_periodic_2->setTimeInterval(1.005 - interval);\r
1962 }\r
1963 \r
1964 #ifndef _WIN32\r
1965 #pragma mark '\r
1966 #pragma mark ------- HTTP制御 -------\r
1967 #endif\r
1968 \r
1969 static std::string epg_regist_form(Dictionary *epg)\r
1970 {\r
1971     DebugLog3("epg_regist_form() start.");\r
1972 \r
1973     std::string epgs;\r
1974 \r
1975     if ((epg != NULL) &&\r
1976         (epg->stringForKey(KEY_EPG_SERVICE_ID) != NULL) &&\r
1977         (epg->stringForKey(KEY_EPG_EVENT_ID) != NULL) &&\r
1978         (epg->stringForKey(KEY_EPG_START) != NULL) &&\r
1979         (epg->stringForKey(KEY_EPG_END) != NULL) &&\r
1980         (epg->stringForKey(KEY_EPG_DATE) != NULL) &&\r
1981         (epg->stringForKey(KEY_EPG_TITLE) != NULL))\r
1982     {\r
1983         epgs += "<form id=\"";\r
1984         epgs += "epg_";\r
1985         epgs += epg->stringForKey(KEY_EPG_SERVICE_ID)->cString();\r
1986         epgs += "_";\r
1987         epgs += epg->stringForKey(KEY_EPG_EVENT_ID)->cString();\r
1988         epgs += "\" title=\"";\r
1989         epgs += epg->stringForKey(KEY_EPG_START)->substringToIndex(5)->cString();\r
1990         epgs += "-";\r
1991         epgs += epg->stringForKey(KEY_EPG_END)->substringToIndex(5)->cString();\r
1992         epgs += "\" class=\"panel\" action=\"regist.cgi\" method=\"GET\" target=\"_self\" onclick='return confirm(\"Is it ok?\");'>";\r
1993 \r
1994         // 放送時間\r
1995         epgs += "<h2>";\r
1996         epgs += LocalizedString(KEY_I18N_Broadcasting_Time, NULL)->cString();\r
1997         epgs += "</h2>";\r
1998         epgs += "<fieldset>";\r
1999         epgs += "<p class=\"normalText\">&nbsp;&nbsp;";\r
2000         epgs += epg->stringForKey(KEY_EPG_DATE)->substringFromIndex(5)->cString();\r
2001         epgs += "&nbsp;";\r
2002         epgs += epg->stringForKey(KEY_EPG_START)->substringToIndex(5)->cString();\r
2003         epgs += "-";\r
2004         epgs += epg->stringForKey(KEY_EPG_END)->substringToIndex(5)->cString();\r
2005         epgs += "</p>";\r
2006         epgs += "</fieldset>";\r
2007 \r
2008         // 番組名\r
2009         epgs += "<h2>";\r
2010         epgs += LocalizedString(KEY_I18N_Program_Title, NULL)->cString();\r
2011         epgs += "</h2>";\r
2012         epgs += "<fieldset>";\r
2013         epgs += "<p class=\"normalText\">&nbsp;&nbsp;";\r
2014         epgs += epg->stringForKey(KEY_EPG_TITLE)->cString();\r
2015         epgs += "</p>";\r
2016         epgs += "</fieldset>";\r
2017 \r
2018         // 概要\r
2019         if (epg->stringForKey(KEY_EPG_DESCRIPTION) != NULL)\r
2020         {\r
2021             epgs += "<h2>";\r
2022             epgs += LocalizedString(KEY_I18N_Description, NULL)->cString();\r
2023             epgs += "</h2>";\r
2024             epgs += "<fieldset>";\r
2025             epgs += "<p class=\"normalText\">&nbsp;&nbsp;";\r
2026             epgs += epg->stringForKey(KEY_EPG_DESCRIPTION)->cString();\r
2027             epgs += "</p>";\r
2028             epgs += "</fieldset>";\r
2029         }\r
2030 \r
2031         //\r
2032         epgs += "<input type=\"hidden\" name=\"service_id\" value=\"";\r
2033         epgs += epg->stringForKey(KEY_EPG_SERVICE_ID)->cString();\r
2034         epgs += "\"/>";\r
2035         epgs += "<input type=\"hidden\" name=\"event_id\" value=\"";\r
2036         epgs += epg->stringForKey(KEY_EPG_EVENT_ID)->cString();\r
2037         epgs += "\"/>";\r
2038         epgs += "<input class=\"redButton\" type=\"submit\" value=\"";\r
2039         epgs += LocalizedString(KEY_I18N_New_Reservation, NULL)->cString();\r
2040         epgs += "\"/>";\r
2041     //    epgs += "<a class=\"redButton\" type=\"submit\">";\r
2042     //    epgs += LocalizedString(KEY_I18N_New_Reservation, NULL)->cString();\r
2043     //    epgs += "</a>";\r
2044         epgs += "</form>";\r
2045     }\r
2046 \r
2047     return epgs;\r
2048 }\r
2049 \r
2050 HTTPResponse *responseWithDictionary(HTTPRequest *request, Dictionary *dictionary)\r
2051 {\r
2052     HTTPResponse *result = NULL;\r
2053     if ((request != NULL) && (dictionary != NULL))\r
2054     {\r
2055         std::string xml = dictionary->toString();\r
2056 \r
2057         // header\r
2058         InternetTextMessageHeader *header = InternetTextMessageHeader::alloc()->init();\r
2059         // Date\r
2060         // Server\r
2061         // Content-Encoding\r
2062         // Last-Modified\r
2063         // Content-Type\r
2064         header->setFieldBodyWithName("application/xml", "Content-Type");\r
2065         // Connection\r
2066         // Tranfer-Encoding\r
2067         // Content-Length\r
2068         header->setFieldBodyWithName(String::stringWithFormat("%I64u", xml.length()), "Content-Length");\r
2069 \r
2070         // body\r
2071         InternetTextMessageBody *body = InternetTextMessageBody::alloc()->initWithString(String::stringWithUTF8String(xml.c_str()));\r
2072 \r
2073         // message\r
2074         InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, body);\r
2075         RELEASE(header);\r
2076         RELEASE(body);\r
2077         if (message != NULL)\r
2078         {\r
2079             result = HTTPResponse::alloc()->init();\r
2080             result->autorelease();\r
2081             result->setVersion(request->version());\r
2082             result->setReason(NET::HTTPDaemon::reasonForStatus(200));\r
2083             result->setStatus(200);\r
2084             result->setMessage(message);\r
2085             RELEASE(message);\r
2086         }\r
2087     }\r
2088     return result;\r
2089 }\r
2090 \r
2091 // positive response by XML\r
2092 HTTPResponse *responseForSuccess(HTTPRequest *request)\r
2093 {\r
2094     Dictionary *dict = Dictionary::dictionaryWithCapacity(0);\r
2095     dict->setString("Success", KEY_RESULT);\r
2096     return responseWithDictionary(request, dict);\r
2097 }\r
2098 \r
2099 // negative response by XML\r
2100 HTTPResponse *responseForFailed(HTTPRequest *request)\r
2101 {\r
2102     Dictionary *dict = Dictionary::dictionaryWithCapacity(0);\r
2103     dict->setString("Failed", KEY_RESULT);\r
2104     return responseWithDictionary(request, dict);\r
2105 }\r
2106 \r
2107 HTTPResponse *Controller::responseWithHTML(HTTPRequest *request, String *html)\r
2108 {\r
2109     HTTPResponse *result = NULL;\r
2110     if ((html != NULL) && (request != NULL))\r
2111     {\r
2112         // header\r
2113         InternetTextMessageHeader *header = InternetTextMessageHeader::alloc()->init();\r
2114         // Date\r
2115         // Server\r
2116         // Content-Encoding\r
2117         // Last-Modified\r
2118         // Content-Type\r
2119         header->setFieldBodyWithName("text/html", "Content-Type");\r
2120         // Connection\r
2121         // Tranfer-Encoding\r
2122         // Content-Length\r
2123         header->setFieldBodyWithName(String::stringWithFormat("%I64u", html->length()), "Content-Length");\r
2124 \r
2125         // body\r
2126         InternetTextMessageBody *body = InternetTextMessageBody::alloc()->initWithString(html);\r
2127 \r
2128         // message\r
2129         InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, body);\r
2130         RELEASE(header);\r
2131         RELEASE(body);\r
2132         if (message != NULL)\r
2133         {\r
2134 //            result = HTTPResponse::response();\r
2135             result = HTTPResponse::alloc()->init();\r
2136             result->setVersion(request->version());\r
2137             result->setReason(NET::HTTPDaemon::reasonForStatus(200));\r
2138             result->setStatus(200);\r
2139             result->setMessage(message);\r
2140             result->autorelease();\r
2141             RELEASE(message);\r
2142         }\r
2143     }\r
2144     return result;\r
2145 }\r
2146 \r
2147 HTTPResponse *Controller::responseWithUTF8Text(HTTPRequest *request, String *text)\r
2148 {\r
2149     HTTPResponse *result = NULL;\r
2150     if ((text != NULL) && (request != NULL))\r
2151     {\r
2152         // header\r
2153         InternetTextMessageHeader *header = InternetTextMessageHeader::alloc()->init();\r
2154         // Date\r
2155         // Server\r
2156         // Content-Encoding\r
2157         // Last-Modified\r
2158         // Content-Type\r
2159         header->setFieldBodyWithName("text/plane; charset=UTF-8", "Content-Type");\r
2160         // Connection\r
2161         // Tranfer-Encoding\r
2162         // Content-Length\r
2163         header->setFieldBodyWithName(String::stringWithFormat("%I64u", text->length()), "Content-Length");\r
2164 \r
2165         // body\r
2166         InternetTextMessageBody *body = InternetTextMessageBody::alloc()->initWithString(text);\r
2167 \r
2168         // message\r
2169         InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, body);\r
2170         RELEASE(header);\r
2171         RELEASE(body);\r
2172         if (message != NULL)\r
2173         {\r
2174 //            result = HTTPResponse::response();\r
2175             result = HTTPResponse::alloc()->init();\r
2176             result->setVersion(request->version());\r
2177             result->setReason(NET::HTTPDaemon::reasonForStatus(200));\r
2178             result->setStatus(200);\r
2179             result->setMessage(message);\r
2180             result->autorelease();\r
2181             RELEASE(message);\r
2182         }\r
2183     }\r
2184     return result;\r
2185 }\r
2186 \r
2187 HTTPResponse *Controller::responseByResult(HTTPRequest *request, bool result)\r
2188 {\r
2189     HTTPResponse *retval = NULL;\r
2190 \r
2191     bool iui = false;\r
2192 \r
2193     String *ref = NULL;\r
2194     InternetTextMessage *msg = request->message();\r
2195     if (msg != NULL)\r
2196     {\r
2197         InternetTextMessageHeader *header = msg->header();\r
2198         if (header != NULL)\r
2199         {\r
2200             String *field_referer = header->fieldBodyForName("Referer");\r
2201             if (field_referer != NULL)\r
2202             {\r
2203                 String *field_host    = header->fieldBodyForName("Host");\r
2204                 if (field_host != NULL)\r
2205                 {\r
2206                     std::string tmp = "^http://";\r
2207                     tmp += field_host->cString();\r
2208                     tmp += "/(programs_(t|s)_\\d+|reservation)\\.html$";\r
2209                     iui = field_referer->isMatch(tmp.c_str());\r
2210                     DebugLog0("tmp: %s", tmp.c_str());\r
2211                     DebugLog0("ref: %s", field_referer->cString());\r
2212                 }\r
2213             }\r
2214         }\r
2215     }\r
2216 \r
2217     if (iui)\r
2218     {\r
2219         String *path = _httpd->rootPath()->stringByAppendingPathComponent("template2.html");\r
2220         String *html = String::stringWithContentsOfFile(path->cString(), UTF8StringEncoding);\r
2221         if (html != NULL)\r
2222         {\r
2223             html = html->stringByReplacingOccurrencesOfString("%%TITLE%%", "Result");\r
2224             std::string contents;\r
2225             std::string reservations;\r
2226             contents += "<div id=\"home\" class=\"panel\" title=\"Result\" selected=\"true\">";\r
2227             if (result)\r
2228             {\r
2229                 contents += "<h2>Success</h2>";\r
2230             }\r
2231             else\r
2232             {\r
2233                 contents += "<h2>Failed</h2>";\r
2234             }\r
2235             contents += "</div>";\r
2236 \r
2237             html = html->stringByReplacingOccurrencesOfString("%%CONTENTS%%", contents.c_str());\r
2238             retval = responseWithHTML(request, html);\r
2239         }\r
2240     }\r
2241     else\r
2242     {\r
2243         if (result)\r
2244         {\r
2245             retval = responseForSuccess(request);\r
2246         }\r
2247         else\r
2248         {\r
2249             retval = responseForFailed(request);\r
2250         }\r
2251     }\r
2252 \r
2253     return retval;\r
2254 }\r
2255 \r
2256 HTTPResponse *Controller::responseForMain(HTTPRequest *request, SOCKADDR_IN *client)\r
2257 {\r
2258     DebugLog2("Controller::responseForMain()");\r
2259 \r
2260     HTTPResponse *result = NULL;\r
2261     while ((request != NULL) && (client != NULL))\r
2262     {\r
2263         String *path = _httpd->rootPath();\r
2264         if (path == NULL)\r
2265         {\r
2266             DebugLog3("_httpd->rootPath() ng.");\r
2267             break;\r
2268         }\r
2269 \r
2270         path = path->stringByAppendingPathComponent("template1.html");\r
2271         if (path == NULL)\r
2272         {\r
2273             DebugLog3("path->stringByAppendingPathComponent() ng.");\r
2274             break;\r
2275         }\r
2276 \r
2277         String *html = String::stringWithContentsOfFile(path->cString(), UTF8StringEncoding);\r
2278         if (html == NULL)\r
2279         {\r
2280             DebugLog3("String::stringWithContentsOfFile() ng.");\r
2281             break;\r
2282         }\r
2283 \r
2284         String *server_name = _props->stringForKey(KEY_NAME);\r
2285         if (server_name == NULL)\r
2286         {\r
2287             DebugLog3("_props->stringForKey(KEY_NAME) ng.");\r
2288             break;\r
2289         }\r
2290 \r
2291         html = html->stringByReplacingOccurrencesOfString("%%TITLE%%", server_name);\r
2292         if (html == NULL)\r
2293         {\r
2294             DebugLog3("html->stringByReplacingOccurrencesOfString() ng.");\r
2295             break;\r
2296         }\r
2297 \r
2298         html = html->stringByReplacingOccurrencesOfString("%%PAGE_TITLE%%", LocalizedString(KEY_I18N_Main_Menu, NULL));\r
2299         if (html == NULL)\r
2300         {\r
2301             DebugLog3("html->stringByReplacingOccurrencesOfString() ng.");\r
2302         }\r
2303 \r
2304         std::string contents;\r
2305         contents += "<div id=\"home\" class=\"panel\" selected=\"true\">";\r
2306         contents += "<ul>";\r
2307         contents += "<li><a target=\"_self\" href=\"/programs.html\">";\r
2308         contents += LocalizedString(KEY_I18N_Programs, NULL)->cString();\r
2309         contents += "</a></li>";\r
2310         contents += "<li><a target=\"_self\" href=\"/tv.html\">";\r
2311         contents += LocalizedString(KEY_I18N_TV, NULL)->cString();\r
2312         contents += "</a></li>";\r
2313         contents += "<li><a target=\"_self\" href=\"/video.html\">";\r
2314         contents += LocalizedString(KEY_I18N_Video, NULL)->cString();\r
2315         contents += "</a></li>";\r
2316         contents += "<li><a target=\"_self\" href=\"/reservation.html\">";\r
2317         contents += LocalizedString(KEY_I18N_Reservation, NULL)->cString();\r
2318         contents += "</a></li>";\r
2319         contents += "</ul>";\r
2320 #if 0\r
2321         contents += "<ul>";\r
2322         contents += "<li><a target=\"_self\" href=\"/exec_vlc.html\">VLC media player";\r
2323         contents += "</ul>";\r
2324 #endif\r
2325         contents += "<ul>";\r
2326         contents += "<li><a target=\"_self\" href=\"/status.html\">";\r
2327         contents += LocalizedString(KEY_I18N_Tuner_Status, NULL)->cString();\r
2328         contents += "</a></li>";\r
2329         contents += "</ul>";\r
2330         contents += "<center><a target=\"_self\" href=\"/iptd.log\">Ver. ";\r
2331         contents += VERSION;\r
2332         contents += "</a></center>";\r
2333         contents += "</div>";\r
2334 \r
2335         html = html->stringByReplacingOccurrencesOfString("%%CONTENTS%%", contents.c_str());\r
2336         if (html != NULL)\r
2337         {\r
2338             result = responseWithHTML(request, html);\r
2339         }\r
2340 \r
2341         break;\r
2342     }\r
2343 \r
2344     return result;\r
2345 }\r
2346 \r
2347 HTTPResponse *Controller::responseForPrograms(HTTPRequest *request, SOCKADDR_IN *client)\r
2348 {\r
2349     DebugLog2("Controller::responseForPrograms() start.");\r
2350 \r
2351     HTTPResponse *result = NULL;\r
2352     while ((request != NULL) && (client != NULL))\r
2353     {\r
2354         String *path = _httpd->rootPath();\r
2355         if (path == NULL)\r
2356         {\r
2357             DebugLog3("_httpd->rootPath() ng.");\r
2358             break;\r
2359         }\r
2360 \r
2361         path = path->stringByAppendingPathComponent("template2.html");\r
2362         if (path == NULL)\r
2363         {\r
2364             DebugLog0("path->stringByAppendingPathComponent() ng.");\r
2365             break;\r
2366         }\r
2367 \r
2368         String *html = String::stringWithContentsOfFile(path->cString(), UTF8StringEncoding);\r
2369         if (html == NULL)\r
2370         {\r
2371             DebugLog0("String::stringWithContentsOfFile() ng.");\r
2372             break;\r
2373         }\r
2374 \r
2375         html = html->stringByReplacingOccurrencesOfString("%%TITLE%%", LocalizedString(KEY_I18N_Programs, NULL));\r
2376         if (html == NULL)\r
2377         {\r
2378             DebugLog0("html->stringByReplacingOccurrencesOfString() ng.");\r
2379             break;\r
2380         }\r
2381 \r
2382         std::string contents;\r
2383 \r
2384         String *uri = request->URI();\r
2385         if (uri->isMatch("^/programs\\.html$"))\r
2386         {\r
2387             contents += "<ul id=\"home\" title=\"";\r
2388             contents += LocalizedString(KEY_I18N_Programs, NULL)->cString();\r
2389             contents += "\" selected=\"true\">";\r
2390 \r
2391             // 地デジ\r
2392             contents += "<li>";\r
2393             contents += "<a target=\"_self\" href=\"/programs_t.html\">";\r
2394             contents += LocalizedString(KEY_I18N_Digital_Terrestrial_Television_Broadcasting, NULL)->cString();\r
2395             contents += "</a></li>";\r
2396             // BS/CS\r
2397             contents += "<li>";\r
2398             contents += "<a target=\"_self\" href=\"/programs_s.html\">";\r
2399             contents += "BS/CS";\r
2400             contents += "</a></li>";\r
2401 \r
2402             contents += "</ul>";\r
2403         }\r
2404         else if (uri->isMatch("^/programs_t\\.html$"))\r
2405         {\r
2406             contents += "<ul id=\"home\" title=\"";\r
2407             contents += LocalizedString(KEY_I18N_Digital_Terrestrial_Television_Broadcasting, NULL)->cString();\r
2408             contents += "\" selected=\"true\">";\r
2409 \r
2410             // 地デジ(局名)\r
2411             Array *stations = stationInfos(Tuner::ISDB_T);\r
2412             for (uint i = 0; i < stations->count(); ++i)\r
2413             {\r
2414                 Dictionary *station = (Dictionary *)stations->objectAtIndex(i);\r
2415                 if ((station != NULL) &&\r
2416                     (station->stringForKey(KEY_CHANNEL_ID) != NULL) &&\r
2417                     (station->stringForKey(KEY_NAME) != NULL))\r
2418                 {\r
2419                     contents += "<li>";\r
2420                     contents += "<a target=\"_self\" href=\"/programs_t_";\r
2421                     contents += station->stringForKey(KEY_CHANNEL_ID)->cString();\r
2422                     contents += ".html\">";\r
2423                     contents += station->stringForKey(KEY_NAME)->cString();\r
2424                     contents += "</a></li>";\r
2425                 }\r
2426             }\r
2427 \r
2428             contents += "</ul>";\r
2429         }\r
2430         else if (uri->isMatch("^/programs_s\\.html$"))\r
2431         {\r
2432             contents += "<ul id=\"home\" title=\"";\r
2433             contents += "BS/CS";\r
2434             contents += "\" selected=\"true\">";\r
2435 \r
2436             Array *stations = stationInfos(Tuner::ISDB_S);\r
2437             for (uint i = 0; i < stations->count(); ++i)\r
2438             {\r
2439                 Dictionary *station = (Dictionary *)stations->objectAtIndex(i);\r
2440                 if ((station != NULL) &&\r
2441                     (station->stringForKey(KEY_CHANNEL_ID) != NULL) &&\r
2442                     (station->stringForKey(KEY_NAME) != NULL))\r
2443                 {\r
2444                     contents += "<li>";\r
2445                     contents += "<a target=\"_self\" href=\"/programs_s_";\r
2446                     contents += station->stringForKey(KEY_CHANNEL_ID)->cString();\r
2447                     contents += ".html\">";\r
2448                     contents += station->stringForKey(KEY_NAME)->cString();\r
2449                     contents += "</a></li>";\r
2450                 }\r
2451             }\r
2452 \r
2453             contents += "</ul>";\r
2454         }\r
2455         else if (uri->isMatch("^/programs_(t|s)_.+\\.html$"))\r
2456         {\r
2457             Array *stations = NULL;\r
2458             String *ch_id = NULL;\r
2459             if (uri->isMatch("programs_t_"))\r
2460             {\r
2461                 stations = stationInfos(Tuner::ISDB_T);\r
2462                 ch_id = uri->stringByReplacingOccurrencesOfString("/programs_t_", "");\r
2463                 ch_id = ch_id->stringByReplacingOccurrencesOfString(".html", "");\r
2464             }\r
2465             else\r
2466             {\r
2467                 stations = stationInfos(Tuner::ISDB_S);\r
2468                 ch_id = uri->stringByReplacingOccurrencesOfString("/programs_s_", "");\r
2469                 ch_id = ch_id->stringByReplacingOccurrencesOfString(".html", "");\r
2470             }\r
2471 \r
2472             for (uint i = 0; i < stations->count(); ++i)\r
2473             {\r
2474                 Dictionary *station = (Dictionary *)stations->objectAtIndex(i);\r
2475                 if ((station != NULL) &&\r
2476                     (station->stringForKey(KEY_CHANNEL_ID) != NULL) &&\r
2477                     station->stringForKey(KEY_CHANNEL_ID)->isEqualToString(ch_id))\r
2478                 {\r
2479                     std::string epgs;\r
2480 \r
2481                     contents += "<ul id=\"home\" title=\"";\r
2482                     contents += station->stringForKey(KEY_NAME)->cString();\r
2483                     contents += "\" selected=\"true\">";\r
2484 \r
2485                     Array *programs = programsForServices(station->arrayForKey(KEY_SERVICES));\r
2486                     for (uint i = 0; i < programs->count(); ++i)\r
2487                     {\r
2488                         Dictionary *epg = (Dictionary *)programs->objectAtIndex(i);\r
2489                         if ((epg != NULL) &&\r
2490                             (epg->stringForKey(KEY_EPG_SERVICE_ID) != NULL) &&\r
2491                             (epg->stringForKey(KEY_EPG_EVENT_ID) != NULL) &&\r
2492                             (epg->stringForKey(KEY_EPG_DATE) != NULL) &&\r
2493                             (epg->stringForKey(KEY_EPG_START) != NULL) &&\r
2494                             (epg->stringForKey(KEY_EPG_END) != NULL))\r
2495                         {\r
2496                             contents += "<li>";\r
2497                             contents += "<a href=\"#epg_";\r
2498                             contents += epg->stringForKey(KEY_EPG_SERVICE_ID)->cString();\r
2499                             contents += "_";\r
2500                             contents += epg->stringForKey(KEY_EPG_EVENT_ID)->cString();\r
2501                             contents += "\">";\r
2502                             contents += epg->stringForKey(KEY_EPG_DATE)->substringFromIndex(5)->cString();\r
2503                             contents += "&nbsp;";\r
2504                             contents += epg->stringForKey(KEY_EPG_START)->substringToIndex(5)->cString();\r
2505                             contents += "-";\r
2506                             contents += epg->stringForKey(KEY_EPG_END)->substringToIndex(5)->cString();\r
2507                             contents += "</a></li>";\r
2508 \r
2509                             epgs += epg_regist_form(epg);\r
2510                         }\r
2511                     }\r
2512 \r
2513                     contents += "</ul>";\r
2514 \r
2515                     contents += epgs;\r
2516 \r
2517                     break;\r
2518                 }\r
2519             }\r
2520         }\r
2521 \r
2522         html = html->stringByReplacingOccurrencesOfString("%%CONTENTS%%", contents.c_str());\r
2523         if (html != NULL)\r
2524         {\r
2525             result = responseWithHTML(request, html);\r
2526         }\r
2527 \r
2528         break;\r
2529     }\r
2530 \r
2531     return result;\r
2532 }\r
2533 \r
2534 #if 0\r
2535     HTTPResponse *result = NULL;\r
2536     while ((request != NULL) && (client != NULL))\r
2537     {\r
2538         String *path = _httpd->rootPath();\r
2539         if (path == NULL)\r
2540         {\r
2541             DebugLog0("_httpd->rootPath() ng.");\r
2542             break;\r
2543         }\r
2544 \r
2545         path = path->stringByAppendingPathComponent("template2.html");\r
2546         if (path == NULL)\r
2547         {\r
2548             DebugLog0("path->stringByAppendingPathComponent() ng.");\r
2549             break;\r
2550         }\r
2551 \r
2552         String *html = String::stringWithContentsOfFile(path->cString(), UTF8StringEncoding);\r
2553         if (html == NULL)\r
2554         {\r
2555             DebugLog0("String::stringWithContentsOfFile() ng.");\r
2556             break;\r
2557         }\r
2558 \r
2559         html = html->stringByReplacingOccurrencesOfString("%%TITLE%%", LocalizedString(KEY_I18N_Programs, NULL));\r
2560         if (html == NULL)\r
2561         {\r
2562             DebugLog0("html->stringByReplacingOccurrencesOfString() ng.");\r
2563             break;\r
2564         }\r
2565 \r
2566         std::string contents;\r
2567         std::string epgs;\r
2568 \r
2569         contents += "<ul id=\"home\" title=\"";\r
2570         contents += LocalizedString(KEY_I18N_Programs, NULL)->cString();\r
2571         contents += "\" selected=\"true\">";\r
2572         // 地デジ\r
2573         contents += "<li>";\r
2574         contents += "<a href=\"#isdb_t\">";\r
2575         contents += LocalizedString(KEY_I18N_Digital_Terrestrial_Television_Broadcasting, NULL)->cString();\r
2576         contents += "</a></li>";\r
2577         // BS/CS\r
2578         contents += "<li>";\r
2579         contents += "<a href=\"#isdb_s\">";\r
2580         contents += "BS/CS";\r
2581         contents += "</a></li>";\r
2582 \r
2583         contents += "</ul>";\r
2584 \r
2585         // 地デジ(局名)\r
2586         contents += "<ul id=\"isdb_t\" title=\"";\r
2587         contents += LocalizedString(KEY_I18N_Digital_Terrestrial_Television_Broadcasting, NULL)->cString();\r
2588         contents += "\">";\r
2589         Array *stations = stationInfos(Tuner::ISDB_T);\r
2590         for (uint i = 0; i < stations->count(); ++i)\r
2591         {\r
2592             Dictionary *station = (Dictionary *)stations->objectAtIndex(i);\r
2593             if ((station != NULL) &&\r
2594                 (station->stringForKey(KEY_CHANNEL_ID) != NULL) &&\r
2595                 (station->stringForKey(KEY_NAME) != NULL))\r
2596             {\r
2597                 contents += "<li>";\r
2598                 contents += "<a href=\"#isdb_t_";\r
2599                 contents += station->stringForKey(KEY_CHANNEL_ID)->cString();\r
2600                 contents += "\">";\r
2601                 contents += station->stringForKey(KEY_NAME)->cString();\r
2602                 contents += "</a></li>";\r
2603             }\r
2604         }\r
2605         contents += "</ul>";\r
2606 \r
2607         // 地デジ(番組データ)\r
2608         for (uint i = 0; i < stations->count(); ++i)\r
2609         {\r
2610             Dictionary *station = (Dictionary *)stations->objectAtIndex(i);\r
2611             if ((station != NULL) &&\r
2612                 (station->stringForKey(KEY_CHANNEL_ID) != NULL) &&\r
2613                 (station->stringForKey(KEY_NAME) != NULL))\r
2614             {\r
2615 \r
2616                 contents += "<ul id=\"isdb_t_";\r
2617                 contents += station->stringForKey(KEY_CHANNEL_ID)->cString();\r
2618                 contents += "\" title=\"";\r
2619                 contents += station->stringForKey(KEY_NAME)->cString();\r
2620                 contents += "\">";\r
2621 \r
2622                 Array *programs = programsForServices(station->arrayForKey(KEY_SERVICES));\r
2623                 for (uint i = 0; i < programs->count(); ++i)\r
2624                 {\r
2625                     Dictionary *epg = (Dictionary *)programs->objectAtIndex(i);\r
2626                     if ((epg != NULL) &&\r
2627                         (epg->stringForKey(KEY_EPG_SERVICE_ID) != NULL) &&\r
2628                         (epg->stringForKey(KEY_EPG_EVENT_ID) != NULL) &&\r
2629                         (epg->stringForKey(KEY_EPG_DATE) != NULL) &&\r
2630                         (epg->stringForKey(KEY_EPG_START) != NULL) &&\r
2631                         (epg->stringForKey(KEY_EPG_END) != NULL))\r
2632                     {\r
2633                         contents += "<li>";\r
2634                         contents += "<a href=\"#epg_";\r
2635                         contents += epg->stringForKey(KEY_EPG_SERVICE_ID)->cString();\r
2636                         contents += "_";\r
2637                         contents += epg->stringForKey(KEY_EPG_EVENT_ID)->cString();\r
2638                         contents += "\">";\r
2639                         contents += epg->stringForKey(KEY_EPG_DATE)->substringFromIndex(5)->cString();\r
2640                         contents += "&nbsp;";\r
2641                         contents += epg->stringForKey(KEY_EPG_START)->substringToIndex(5)->cString();\r
2642                         contents += "-";\r
2643                         contents += epg->stringForKey(KEY_EPG_END)->substringToIndex(5)->cString();\r
2644                         contents += "</a></li>";\r
2645 \r
2646                         epgs += epg_regist_form(epg);\r
2647                     }\r
2648                 }\r
2649                 contents += "</ul>";\r
2650             }\r
2651         }\r
2652 \r
2653         // BS/CS(局名)\r
2654         contents += "<ul id=\"isdb_s\" title=\"";\r
2655         contents += LocalizedString(KEY_I18N_Digital_Terrestrial_Television_Broadcasting, NULL)->cString();\r
2656         contents += "\">";\r
2657         stations = stationInfos(Tuner::ISDB_S);\r
2658         for (uint i = 0; i < stations->count(); ++i)\r
2659         {\r
2660             Dictionary *station = (Dictionary *)stations->objectAtIndex(i);\r
2661             if ((station != NULL) &&\r
2662                 (station->stringForKey(KEY_CHANNEL_ID) != NULL) &&\r
2663                 (station->stringForKey(KEY_NAME) != NULL))\r
2664             {\r
2665                 contents += "<li>";\r
2666                 contents += "<a href=\"#isdb_s_";\r
2667                 contents += station->stringForKey(KEY_CHANNEL_ID)->cString();\r
2668                 contents += "\">";\r
2669                 contents += station->stringForKey(KEY_NAME)->cString();\r
2670                 contents += "</a></li>";\r
2671             }\r
2672         }\r
2673         contents += "</ul>";\r
2674 \r
2675         // BS/CS(番組データ)\r
2676         for (uint i = 0; i < stations->count(); ++i)\r
2677         {\r
2678             Dictionary *station = (Dictionary *)stations->objectAtIndex(i);\r
2679             if ((station != NULL) &&\r
2680                 (station->stringForKey(KEY_CHANNEL_ID) != NULL) &&\r
2681                 (station->stringForKey(KEY_NAME) != NULL))\r
2682             {\r
2683                 contents += "<ul id=\"isdb_s_";\r
2684                 contents += station->stringForKey(KEY_CHANNEL_ID)->cString();\r
2685                 contents += "\" title=\"";\r
2686                 contents += station->stringForKey(KEY_NAME)->cString();\r
2687                 contents += "\">";\r
2688 \r
2689                 Array *programs = programsForServices(station->arrayForKey(KEY_SERVICES));\r
2690                 for (uint i = 0; i < programs->count(); ++i)\r
2691                 {\r
2692                     Dictionary *epg = (Dictionary *)programs->objectAtIndex(i);\r
2693                     if ((epg != NULL) &&\r
2694                         (epg->stringForKey(KEY_EPG_SERVICE_ID) != NULL) &&\r
2695                         (epg->stringForKey(KEY_EPG_EVENT_ID) != NULL) &&\r
2696                         (epg->stringForKey(KEY_EPG_DATE) != NULL) &&\r
2697                         (epg->stringForKey(KEY_EPG_START) != NULL) &&\r
2698                         (epg->stringForKey(KEY_EPG_END) != NULL))\r
2699                     {\r
2700                         contents += "<li>";\r
2701                         contents += "<a href=\"#epg_";\r
2702                         contents += epg->stringForKey(KEY_EPG_SERVICE_ID)->cString();\r
2703                         contents += "_";\r
2704                         contents += epg->stringForKey(KEY_EPG_EVENT_ID)->cString();\r
2705                         contents += "\">";\r
2706                         contents += epg->stringForKey(KEY_EPG_DATE)->substringFromIndex(5)->cString();\r
2707                         contents += "&nbsp;";\r
2708                         contents += epg->stringForKey(KEY_EPG_START)->substringToIndex(5)->cString();\r
2709                         contents += "-";\r
2710                         contents += epg->stringForKey(KEY_EPG_END)->substringToIndex(5)->cString();\r
2711                         contents += "</a></li>";\r
2712 \r
2713                         epgs += epg_regist_form(epg);\r
2714                     }\r
2715                 }\r
2716                 contents += "</ul>";\r
2717             }\r
2718         }\r
2719 \r
2720         contents += epgs;\r
2721         html = html->stringByReplacingOccurrencesOfString("%%CONTENTS%%", contents.c_str());\r
2722         if (html != NULL)\r
2723         {\r
2724             result = responseWithHTML(request, html);\r
2725         }\r
2726 \r
2727         break;\r
2728     }    \r
2729     return result;\r
2730 }\r
2731 #endif\r
2732 \r
2733 HTTPResponse *Controller::responseForReservation(HTTPRequest *request, SOCKADDR_IN *client)\r
2734 {\r
2735     DebugLog2("Controller::responseForReservation() start.");\r
2736 \r
2737     // lock\r
2738     RaymLock(this);\r
2739 \r
2740     HTTPResponse *result = NULL;\r
2741     while ((request != NULL) && (client != NULL))\r
2742     {\r
2743         // create array of reservations sorted by seq_id.\r
2744         Array *array = Array::arrayWithCapacity(0);\r
2745         for (int i = 0; i < _tunerCount; ++i)\r
2746         {\r
2747             Array *tmp = _reservations->arrayForKey(_tuners[i]->name());\r
2748             if (tmp == NULL)\r
2749             {\r
2750                 continue;\r
2751             }\r
2752             for (uint idx = 0; idx < tmp->count(); ++idx)\r
2753             {\r
2754                 Dictionary *epg = (Dictionary *)tmp->objectAtIndex(idx);\r
2755                 if (epg == NULL)\r
2756                 {\r
2757                     continue;\r
2758                 }\r
2759 \r
2760                 time_t epg_start;\r
2761                 time_t tmp_end;\r
2762                 getTimeWithEPG(epg, &epg_start, &tmp_end);\r
2763 \r
2764                 bool inserted = false;\r
2765                 for (uint j = 0; j < array->count(); ++j)\r
2766                 {\r
2767                     Dictionary *epg2 = (Dictionary *)array->objectAtIndex(j);\r
2768                     if (epg2 == NULL)\r
2769                     {\r
2770                         continue;\r
2771                     }\r
2772 \r
2773                     time_t epg2_start;\r
2774                     getTimeWithEPG(epg2, &epg2_start, &tmp_end);\r
2775 \r
2776                     if (epg_start <= epg2_start)\r
2777                     {\r
2778                         array->insertObject(epg, j);\r
2779                         inserted = true;\r
2780                         break;\r
2781                     }\r
2782                 }\r
2783 \r
2784                 if (!inserted)\r
2785                 {\r
2786                     array->addObject(epg);\r
2787                 }\r
2788             }\r
2789         }\r
2790 \r
2791         String *path = _httpd->rootPath();\r
2792         if (path == NULL)\r
2793         {\r
2794             break;\r
2795         }\r
2796 \r
2797         path = path->stringByAppendingPathComponent("template2.html");\r
2798         if (path == NULL)\r
2799         {\r
2800             break;\r
2801         }\r
2802 \r
2803         String *html = String::stringWithContentsOfFile(path->cString(), UTF8StringEncoding);\r
2804         if (html == NULL)\r
2805         {\r
2806             break;\r
2807         }\r
2808 \r
2809         html = html->stringByReplacingOccurrencesOfString("%%TITLE%%", LocalizedString(KEY_I18N_Reservation, NULL));\r
2810         if (html == NULL)\r
2811         {\r
2812             break;\r
2813         }\r
2814 \r
2815         std::string contents;\r
2816         std::string reservations;\r
2817         contents += "<ul id=\"home\" title=\"";\r
2818         contents += LocalizedString(KEY_I18N_Reservation, NULL)->cString();\r
2819         contents += "\" selected=\"true\">";\r
2820 \r
2821 #if 0\r
2822         contents += "<li><a href=\"#tuners\">";\r
2823         contents += "<font color=\"blue\">[";\r
2824         contents += LocalizedString(KEY_I18N_Tuner, NULL)->cString();\r
2825         contents += "]</font>";\r
2826         contents += "</a></li>";\r
2827 #endif\r
2828 \r
2829         //\r
2830         // "[新規予約]"\r
2831         //\r
2832         contents += "<li><a href=\"#new_reservation\">";\r
2833         contents += "<font color=\"red\">[";\r
2834         contents += LocalizedString(KEY_I18N_New_Reservation, NULL)->cString();\r
2835         contents += "]</font>";\r
2836         contents += "</a></li>";\r
2837 \r
2838         //\r
2839         // "[キーワード]"\r
2840         //\r
2841         contents += "<li><a href=\"#keywords\">";\r
2842         contents += "<font color=\"blue\">[";\r
2843         contents += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
2844         contents += "]</font>";\r
2845         contents += "</a></li>";\r
2846 \r
2847         //\r
2848         // 登録済み予約情報\r
2849         //\r
2850         for (uint i = 0; i < array->count(); ++i)\r
2851         {\r
2852             Dictionary *epg = (Dictionary *)array->objectAtIndex(i);\r
2853             char seq_id[7];\r
2854             sprintf_s(seq_id, "%06d", epg->integerForKey(KEY_EPG_RESV_ID) % 1000000);\r
2855             contents += "<li>";\r
2856             contents += "<a href=\"#seqid";\r
2857             contents += seq_id;\r
2858             contents += "\">";\r
2859             String *title = epg->stringForKey(KEY_EPG_TITLE);\r
2860             if (title != NULL)\r
2861             {\r
2862                 contents += title->cString();\r
2863             }\r
2864             else\r
2865             {\r
2866                 time_t start;\r
2867                 time_t end;\r
2868                 Controller::getTimeWithEPG(epg, &start, &end);\r
2869                 TM tm;\r
2870                 if (localtime_s(&tm, &start) == 0)\r
2871                 {\r
2872                     char date_time[24];\r
2873                     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
2874                     contents += date_time;\r
2875                 }\r
2876                 else\r
2877                 {\r
2878                     contents += seq_id;\r
2879                 }\r
2880             }\r
2881             contents += "</a></li>";\r
2882 \r
2883             // reservations\r
2884             reservations += "<div id=\"";\r
2885             reservations += "seqid";\r
2886             reservations += seq_id;\r
2887             reservations += "\" title=\"";\r
2888             reservations += "xxxxxx";\r
2889             reservations += "\" class=\"panel\">";\r
2890 \r
2891             //reservations += "<h2>Status</h2>";\r
2892             reservations += "<fieldset>";\r
2893 \r
2894             //\r
2895             reservations += "<div class=\"row\">";\r
2896             reservations += "<label>Date</label>";\r
2897             reservations += "<span>";\r
2898             String *tmp = epg->stringForKey(KEY_EPG_DATE);\r
2899             reservations += tmp->cString();\r
2900             reservations += "</span>";\r
2901             reservations += "</div>";\r
2902 \r
2903             reservations += "<div class=\"row\">";\r
2904             reservations += "<label>Start</label>";\r
2905             reservations += "<span>";\r
2906             tmp = epg->stringForKey(KEY_EPG_START);\r
2907             reservations += tmp->cString();\r
2908             reservations += "</span>";\r
2909             reservations += "</div>";\r
2910 \r
2911             reservations += "<div class=\"row\">";\r
2912             reservations += "<label>End</label>";\r
2913             reservations += "<span>";\r
2914             tmp = epg->stringForKey(KEY_EPG_END);\r
2915             reservations += tmp->cString();\r
2916             reservations += "</span>";\r
2917             reservations += "</div>";\r
2918 \r
2919             reservations += "</fieldset>";\r
2920 \r
2921             reservations += "<a class=\"whiteButton\" type=\"submit\" href=\"/cancel.cgi?";\r
2922             reservations += "resv_id=";\r
2923             reservations += seq_id;\r
2924             reservations += "\" onclick='return confirm(\"To cancel. Is it ok?\");'>";\r
2925             reservations += LocalizedString(KEY_I18N_Cancel, NULL)->cString();\r
2926             reservations += "</a>";\r
2927 \r
2928             reservations += "</div>";\r
2929         }\r
2930 \r
2931         contents += "</ul>";\r
2932         contents += reservations;\r
2933 \r
2934         //\r
2935         // 新規予約:ここから\r
2936         //\r
2937 \r
2938         // 予約時間の初期値作成\r
2939         TM tm1, tm2;\r
2940         time_t time1 = time(NULL);\r
2941         time1 = (time1 + 15 * 60) / (15 * 60);\r
2942         time1 = time1 * 15 * 60;\r
2943         time_t time2 = time1 + 15 * 60;\r
2944         localtime_s(&tm1, &time1);\r
2945         localtime_s(&tm2, &time2);\r
2946         \r
2947         contents += "<form name=\"reservation\" id=\"new_reservation\" class=\"panel\" method=\"GET\" action=\"regist.cgi\" target=\"_self\">";\r
2948         contents += "<fieldset>";\r
2949         contents += "<div class=\"row\">";\r
2950         contents += "<label>";\r
2951         contents += LocalizedString(KEY_I18N_Station_Name, NULL)->cString();\r
2952         contents += " : </label>";\r
2953         contents += "<span>";\r
2954         contents += "<select name=\"service_id\" size=\"1\">";\r
2955 \r
2956         // 局情報の収集\r
2957         Array *stationInfos = Controller::stationInfos(Tuner::ISDB_T);\r
2958         if (stationInfos != NULL)\r
2959         {\r
2960             stationInfos->addObjectsFromArray(Controller::stationInfos(Tuner::ISDB_S));\r
2961         }\r
2962         else\r
2963         {\r
2964             stationInfos = Controller::stationInfos(Tuner::ISDB_S);\r
2965         }\r
2966         for (uint i = 0; (stationInfos != NULL) && (i < stationInfos->count()); ++i)\r
2967         {\r
2968             Array *services = ((Dictionary *)stationInfos->objectAtIndex(i))->arrayForKey(KEY_SERVICES);\r
2969 \r
2970 // とりえあず、現状は最初のサービスのみ表示\r
2971 //                for (uint j = 0; (services != NULL) &&  (j < services->count()); ++j)\r
2972             for (uint j = 0; (services != NULL) &&  (j < 1); ++j)\r
2973             {\r
2974                 Dictionary *service = (Dictionary *)services->objectAtIndex(j);\r
2975 \r
2976                 // テレビ放送かチェック\r
2977                 if ((service->stringForKey(KEY_SERVICE_TYPE) != NULL) && service->stringForKey(KEY_SERVICE_TYPE)->isEqualToString("1"))\r
2978                 {\r
2979                     String *name = service->stringForKey(KEY_NAME);\r
2980                     String *sid  = service->stringForKey(KEY_SERVICE_ID);\r
2981                     if ((name != NULL) && (sid != NULL))\r
2982                     {\r
2983                         String *tmp = String::stringWithFormat("<option value=\"%s\">%s[%s]</option>", sid->cString(), name->cString(), sid->cString());\r
2984                         contents += tmp->cString();\r
2985                     }\r
2986                 }\r
2987             }\r
2988         }\r
2989 \r
2990         contents += "</select>";\r
2991         contents += "</span>";\r
2992         contents += "</div>";\r
2993         contents += "<div class=\"row\">";\r
2994         contents += "<label>";\r
2995         contents += LocalizedString(KEY_I18N_Date, NULL)->cString();\r
2996         contents += " : </label>";\r
2997         contents += "<span>";\r
2998         contents += "<select name=\"year\" size=\"1\">";\r
2999         for (int year = tm1.tm_year + 1900; year < tm1.tm_year + 1900 + 3; ++year)\r
3000         {\r
3001             char year_buf[64];\r
3002             sprintf_s(year_buf, "<option value=\"%d\">%d</option>", year, year);\r
3003             contents += year_buf;\r
3004         }\r
3005         contents += "</select>";\r
3006         contents += "<select name=\"month\" size=\"1\">";\r
3007         for (int month = 1; month < 13; ++month)\r
3008         {\r
3009             char month_buf[64];\r
3010             if (month == tm1.tm_mon + 1)\r
3011             {\r
3012                 sprintf_s(month_buf, "<option value=\"%d\" selected>%d</option>", month, month);\r
3013             }\r
3014             else\r
3015             {\r
3016                 sprintf_s(month_buf, "<option value=\"%d\">%d</option>", month, month);\r
3017             }\r
3018             contents += month_buf;\r
3019         }\r
3020         contents += "</select>";\r
3021         contents += "<select name=\"day\" size=\"1\">";\r
3022         for (int day = 1; day < 32; ++day)\r
3023         {\r
3024             char day_buf[64];\r
3025             if (day == tm1.tm_mday)\r
3026             {\r
3027                 sprintf_s(day_buf, "<option value=\"%d\" selected>%d</option>", day, day);\r
3028             }\r
3029             else\r
3030             {\r
3031                 sprintf_s(day_buf, "<option value=\"%d\">%d</option>", day, day);\r
3032             }\r
3033             contents += day_buf;\r
3034         }\r
3035         contents += "</select>";\r
3036         contents += "</span>";\r
3037         contents += "</div>";\r
3038         contents += "<div class=\"row\">";\r
3039         contents += "<label>";\r
3040         contents += LocalizedString(KEY_I18N_Start_Time, NULL)->cString();\r
3041         contents += " : </label>";\r
3042         contents += "<span>";\r
3043         contents += "<select name=\"start_hour\" size=\"1\">";\r
3044         for (int sh = 0; sh < 24; ++sh)\r
3045         {\r
3046             char sh_buf[64];\r
3047             if (sh == tm1.tm_hour)\r
3048             {\r
3049                 sprintf_s(sh_buf, "<option value=\"%02d\" selected>%02d</option>", sh, sh);\r
3050             }\r
3051             else\r
3052             {\r
3053                 sprintf_s(sh_buf, "<option value=\"%02d\">%02d</option>", sh, sh);\r
3054             }\r
3055             contents += sh_buf;\r
3056         }\r
3057         contents += "</select>";\r
3058         contents += "<select name=\"start_min\" size=\"1\">";\r
3059         for (int sm = 0; sm < 60; ++sm)\r
3060         {\r
3061             char sm_buf[64];\r
3062             if (sm == tm1.tm_min)\r
3063             {\r
3064                 sprintf_s(sm_buf, "<option value=\"%02d\" selected>%02d</option>", sm, sm);\r
3065             }\r
3066             else\r
3067             {\r
3068                 sprintf_s(sm_buf, "<option value=\"%02d\">%02d</option>", sm, sm);\r
3069             }\r
3070             contents += sm_buf;\r
3071         }\r
3072         contents += "</select>";\r
3073         contents += "</span>";\r
3074         contents += "</div>";\r
3075         contents += "<div class=\"row\">";\r
3076         contents += "<label>";\r
3077         contents += LocalizedString(KEY_I18N_End_Time, NULL)->cString();\r
3078         contents += " : </label>";\r
3079         contents += "<span>";\r
3080         contents += "<select name=\"end_hour\" size=\"1\">";\r
3081         for (int eh = 0; eh < 24; ++eh)\r
3082         {\r
3083             char eh_buf[64];\r
3084             if (eh == tm2.tm_hour)\r
3085             {\r
3086                 sprintf_s(eh_buf, "<option value=\"%02d\" selected>%02d</option>", eh, eh);\r
3087             }\r
3088             else\r
3089             {\r
3090                 sprintf_s(eh_buf, "<option value=\"%02d\">%02d</option>", eh, eh);\r
3091             }\r
3092             contents += eh_buf;\r
3093         }\r
3094         contents += "</select>";\r
3095         contents += "<select name=\"end_min\" size=\"1\">";\r
3096         for (int em = 0; em < 60; ++em)\r
3097         {\r
3098             char em_buf[64];\r
3099             if (em == tm2.tm_min)\r
3100             {\r
3101                 sprintf_s(em_buf, "<option value=\"%02d\" selected>%02d</option>", em, em);\r
3102             }\r
3103             else\r
3104             {\r
3105                 sprintf_s(em_buf, "<option value=\"%02d\">%02d</option>", em, em);\r
3106             }\r
3107             contents += em_buf;\r
3108         }\r
3109         contents += "</select>";\r
3110         contents += "</span>";\r
3111         contents += "</div>";\r
3112         contents += "<div class=\"row\">";\r
3113         contents += "<label>";\r
3114         contents += LocalizedString(KEY_I18N_Repeat, NULL)->cString();\r
3115         contents += " : </label>";\r
3116         contents += "<span>";\r
3117         contents += "<select name=\"repeat\" size=\"1\">";\r
3118         contents += "<option value=\"off\" selected>";\r
3119         contents += LocalizedString(KEY_I18N_Repeat_off, NULL)->cString();\r
3120         contents += "</option>";\r
3121         contents += "<option value=\"everyday\">";\r
3122         contents += LocalizedString(KEY_I18N_Repeat_everyday, NULL)->cString();\r
3123         contents += "</option>";\r
3124         contents += "<option value=\"weekly\">";\r
3125         contents += LocalizedString(KEY_I18N_Repeat_weekly, NULL)->cString();\r
3126         contents += "</option>";\r
3127         contents += "<option value=\"weekday\">";\r
3128         contents += LocalizedString(KEY_I18N_Repeat_weekday, NULL)->cString();\r
3129         contents += "</option>";\r
3130         contents += "</select>";\r
3131         contents += "</span>";\r
3132         contents += "</div>";\r
3133         contents += "</fieldset>";\r
3134 \r
3135         contents += "<input type=\"submit\" class=\"whiteButton\" value=\"";\r
3136         contents += LocalizedString(KEY_I18N_Registration, NULL)->cString();\r
3137         contents += "\">";\r
3138         contents += "</form>";\r
3139 \r
3140         //\r
3141         // 新規予約:ここまで\r
3142         //\r
3143 \r
3144 \r
3145         //\r
3146         // チューナ:ここから\r
3147         //\r
3148         contents += "<ul id=\"tuners\" title=\"";\r
3149         contents += LocalizedString(KEY_I18N_Tuner, NULL)->cString();\r
3150         contents += "\">";\r
3151 \r
3152         std::string controls;\r
3153         Dictionary *tunerInfos = _props->dictionaryForKey(KEY_TUNERS);\r
3154         if (tunerInfos != NULL)\r
3155         {\r
3156             for (int i = 0; i < _tunerCount; ++i)\r
3157             {\r
3158                 Dictionary *tunerInfo = tunerInfos->dictionaryForKey(_tuners[i]->name());\r
3159                 if (tunerInfo != NULL)\r
3160                 {\r
3161                     if (tunerInfo->boolForKey(KEY_INITIALIZED) && tunerInfo->boolForKey(KEY_ENABLED))\r
3162                     {\r
3163                         char key[4];\r
3164                         sprintf_s(key, "%03d", i);\r
3165 \r
3166                         // tuner list\r
3167                         contents += "<li>";\r
3168                         contents += "<a href=\"#tuner";\r
3169                         contents += key;\r
3170                         contents += "\">";\r
3171                         contents += key;\r
3172                         contents += ": ";\r
3173                         Dictionary *dict = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
3174                         if (dict != NULL)\r
3175                         {\r
3176                             char chkey[4];\r
3177                             sprintf_s(chkey, "%03d", tunerInfo->integerForKey(KEY_CHANNEL));\r
3178                             dict = dict->dictionaryForKey(chkey);\r
3179                             String *name = dict->stringForKey(KEY_NAME);\r
3180                             if (name != NULL)\r
3181                             {\r
3182                                 contents += name->cString();\r
3183                             }\r
3184                             else\r
3185                             {\r
3186                                 dict = NULL;\r
3187                             }\r
3188                         }\r
3189                         if (dict == NULL)\r
3190                         {\r
3191                             contents += _tuners[i]->name();\r
3192                         }\r
3193                         contents += "</a></li>";\r
3194 \r
3195                         // controls\r
3196                         controls += "<form name=\"control\" id=\"tuner";\r
3197                         controls += key;\r
3198                         controls += "\" class=\"panel\" method=\"GET\" action=\"/";\r
3199                         controls += key;\r
3200                         controls += "/recording=on\" target=\"_self\">";\r
3201 \r
3202                         // 白い枠:ここから\r
3203                         controls += "<fieldset>";\r
3204 \r
3205                         // 局指定:ここから\r
3206                         controls += "<div class=\"row\">";\r
3207                         controls += "<label>";\r
3208                         controls += LocalizedString(KEY_I18N_Station_Name, NULL)->cString();\r
3209                         controls += " : </label>";\r
3210                         controls += "<span>";\r
3211                         controls += "<select name=\"channel\" size=\"1\">";\r
3212                         Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
3213                         if (channels != NULL)\r
3214                         {\r
3215                             for (int ch = 0; ch <= Tuner::MAX_CHANNELS_ISDB_T; ++ch)\r
3216                             {\r
3217                                 char chkey[4];\r
3218                                 sprintf_s(chkey, "%03d", ch);\r
3219                                 dict = channels->dictionaryForKey(chkey);\r
3220                                 if (dict == NULL)\r
3221                                 {\r
3222                                     break;\r
3223                                 }\r
3224                                 Array *services = (Array *)dict->objectForKey(KEY_SERVICES);\r
3225                                 if (services != NULL)\r
3226                                 {\r
3227                                     for (uint s = 0; s < services->count(); ++s)\r
3228                                     {\r
3229                                         Dictionary *service = (Dictionary *)services->objectAtIndex(s);\r
3230                                         if (service != NULL)\r
3231                                         {\r
3232                                             char sid[8];\r
3233                                             sprintf_s(sid, "%d", service->integerForKey(KEY_SERVICE_ID));\r
3234                                             controls += "<option value=\"";\r
3235                                             controls += chkey;\r
3236                                             controls += "\"";\r
3237                                             // selected\r
3238                                             controls += ">";\r
3239                                             String *name = service->stringForKey(KEY_NAME);\r
3240                                             if (name != NULL)\r
3241                                             {\r
3242                                                 controls += name->cString();\r
3243                                             }\r
3244                                             controls += "[";\r
3245                                             controls += sid;\r
3246                                             controls += "]</option>";\r
3247                                         }\r
3248                                     }\r
3249                                 }\r
3250                             }\r
3251                         }\r
3252                         controls += "</select>";\r
3253                         controls += "</span>";\r
3254                         controls += "</div>";\r
3255                         // 局指定:ここまで\r
3256 \r
3257                         // 時間指定:ここから\r
3258                         controls += "<div class=\"row\">";\r
3259                         controls += "<label>";\r
3260                         controls += LocalizedString(KEY_I18N_Time, NULL)->cString();\r
3261                         controls += " : </label>";\r
3262                         controls += "<span>";\r
3263                         controls += "<select name=\"hour\" size=\"1\">";\r
3264                         for (int sh = 0; sh < 24; ++sh)\r
3265                         {\r
3266                             char sh_buf[64];\r
3267                             if (sh == 1)\r
3268                             {\r
3269                                 sprintf_s(sh_buf, "<option value=\"%02d\" selected>%02d</option>", sh, sh);\r
3270                             }\r
3271                             else\r
3272                             {\r
3273                                 sprintf_s(sh_buf, "<option value=\"%02d\">%02d</option>", sh, sh);\r
3274                             }\r
3275                             controls += sh_buf;\r
3276                         }\r
3277                         controls += "</select>";\r
3278                         controls += "<select name=\"min\" size=\"1\">";\r
3279                         for (int sm = 0; sm < 60; ++sm)\r
3280                         {\r
3281                             char sm_buf[64];\r
3282                             if (sm == 0)\r
3283                             {\r
3284                                 sprintf_s(sm_buf, "<option value=\"%02d\" selected>%02d</option>", sm, sm);\r
3285                             }\r
3286                             else\r
3287                             {\r
3288                                 sprintf_s(sm_buf, "<option value=\"%02d\">%02d</option>", sm, sm);\r
3289                             }\r
3290                             controls += sm_buf;\r
3291                         }\r
3292                         controls += "</select>";\r
3293                         controls += "</span>";\r
3294                         controls += "</div>";\r
3295                         // 時間指定:ここまで\r
3296 \r
3297                         controls += "</fieldset>";\r
3298                         // 白い枠:ここまで\r
3299 \r
3300                         controls += "<input type=\"submit\" class=\"whiteButton\" value=\"";\r
3301                         controls += LocalizedString(KEY_I18N_Registration, NULL)->cString();\r
3302                         controls += "\">";\r
3303                         controls += "</form>";\r
3304                     }\r
3305                 }\r
3306             }\r
3307         }\r
3308         contents += "</ul>";\r
3309         contents += controls;\r
3310 \r
3311         //\r
3312         // チューナ:ここまで\r
3313         //\r
3314 \r
3315         //\r
3316         // キーワード:ここから\r
3317         //\r
3318         contents += "<ul id=\"keywords\" title=\"";\r
3319         contents += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
3320         contents += "\">";\r
3321 \r
3322         contents += "<li><a href=\"#dialogForm\">";\r
3323         contents += "<font color=\"red\">[";\r
3324 //        contents += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
3325         contents += LocalizedString(KEY_I18N_Add, NULL)->cString();\r
3326         contents += "]</font>";\r
3327         contents += "</a></li>";\r
3328 \r
3329         // 登録済みキーワード\r
3330         Dictionary *keywords_info = _reservations->dictionaryForKey(KEY_EPG_KEYWORDS);\r
3331         if (keywords_info != NULL)\r
3332         {\r
3333             controls = "";\r
3334             Array *keys = keywords_info->allKeys();\r
3335             if (keys != NULL)\r
3336             {\r
3337                 for (uint i = 0; i < keys->count(); ++i)\r
3338                 {\r
3339                     Dictionary *kwd_inf = keywords_info->dictionaryForKey((String *)keys->objectAtIndex(i));\r
3340                     if (kwd_inf == NULL)\r
3341                     {\r
3342                         continue;\r
3343                     }\r
3344 \r
3345                     char tmp[32];\r
3346                     sprintf_s(tmp, "%d", i);\r
3347                     contents += "<li><a href=\"#keywords_";\r
3348                     contents += tmp;\r
3349                     contents += "\">";\r
3350                     contents += ((String *)keys->objectAtIndex(i))->cString();\r
3351                     contents += "</a></li>";\r
3352 \r
3353                     controls += "<form name=\"filter\" id=\"keywords_";\r
3354                     controls += tmp;\r
3355                     controls += "\" class=\"panel\" method=\"GET\" action=\"mod_keywords.cgi\" target=\"_self\">";\r
3356                     controls += "<input type=\"hidden\" name=\"keywords\" value=\"";\r
3357                     controls += ((String *)keys->objectAtIndex(i))->cString();\r
3358                     controls += "\">";\r
3359                     controls += "<fieldset>";\r
3360 \r
3361                     controls += "<div class=\"row\">";\r
3362                     controls += "<label>";\r
3363                     controls += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
3364                     controls += " : </label>";\r
3365                     controls += "<span>";\r
3366                     controls += ((String *)keys->objectAtIndex(i))->cString();\r
3367                     controls += "</span>";\r
3368                     controls += "</div>";\r
3369 \r
3370                     controls += "<div class=\"row\">";\r
3371                     controls += "<label>";\r
3372                     controls += LocalizedString(KEY_I18N_Station_Name, NULL)->cString();\r
3373                     controls += " : </label>";\r
3374                     controls += "<span>";\r
3375                     controls += "<select name=\"service_id\" size=\"1\">";\r
3376                     controls += "<option value=\"-\">----</option>";\r
3377 \r
3378                     String *service_id = kwd_inf->stringForKey(KEY_EPG_SERVICE_ID);\r
3379 \r
3380                     // 局情報の収集\r
3381                     Array *stationInfos = Controller::stationInfos(Tuner::ISDB_T);\r
3382                     if (stationInfos != NULL)\r
3383                     {\r
3384                         stationInfos->addObjectsFromArray(Controller::stationInfos(Tuner::ISDB_S));\r
3385                     }\r
3386                     else\r
3387                     {\r
3388                         stationInfos = Controller::stationInfos(Tuner::ISDB_S);\r
3389                     }\r
3390                     for (uint i = 0; (stationInfos != NULL) && (i < stationInfos->count()); ++i)\r
3391                     {\r
3392                         Array *services = ((Dictionary *)stationInfos->objectAtIndex(i))->arrayForKey(KEY_SERVICES);\r
3393 \r
3394                         for (uint j = 0; (services != NULL) &&  (j < 1); ++j)\r
3395                         {\r
3396                             Dictionary *service = (Dictionary *)services->objectAtIndex(j);\r
3397 \r
3398                             // テレビ放送かチェック\r
3399                             if ((service->stringForKey(KEY_SERVICE_TYPE) != NULL) && service->stringForKey(KEY_SERVICE_TYPE)->isEqualToString("1"))\r
3400                             {\r
3401                                 String *name = service->stringForKey(KEY_NAME);\r
3402                                 String *sid  = service->stringForKey(KEY_SERVICE_ID);\r
3403                                 if ((name != NULL) && (sid != NULL))\r
3404                                 {\r
3405                                     if ((service_id != NULL) && (service_id->isEqualToString(sid)))\r
3406                                     {\r
3407                                         String *tmp = String::stringWithFormat("<option value=\"%s\" selected>%s[%s]</option>", sid->cString(), name->cString(), sid->cString());\r
3408                                         controls += tmp->cString();\r
3409                                     }\r
3410                                     else\r
3411                                     {\r
3412                                         String *tmp = String::stringWithFormat("<option value=\"%s\">%s[%s]</option>", sid->cString(), name->cString(), sid->cString());\r
3413                                         controls += tmp->cString();\r
3414                                     }\r
3415                                 }\r
3416                             }\r
3417                         }\r
3418                     }\r
3419 \r
3420                     controls += "</select>";\r
3421                     controls += "</span>";\r
3422                     controls += "</div>";\r
3423 \r
3424                     controls += "<div class=\"row\">";\r
3425                     controls += "<label>";\r
3426                     controls += LocalizedString(KEY_I18N_Start_Time, NULL)->cString();\r
3427                     controls += " : </label>";\r
3428                     controls += "<span>";\r
3429                     controls += "<select name=\"start_hour\" size=\"1\">";\r
3430                     controls += "<option value=\"-\">--</option>";\r
3431                     String *sh_str = kwd_inf->stringForKey(KEY_EPG_START);\r
3432                     for (int sh = 0; sh < 24; ++sh)\r
3433                     {\r
3434                         char sh_buf[64];\r
3435                         if ((sh_str != NULL) && (sh_str->length() == 5) && (sh_str->substringToIndex(2)->intValue() == sh))\r
3436                         {\r
3437                             sprintf_s(sh_buf, "<option value=\"%02d\" selected>%02d</option>", sh, sh);\r
3438                         }\r
3439                         else\r
3440                         {\r
3441                             sprintf_s(sh_buf, "<option value=\"%02d\">%02d</option>", sh, sh);\r
3442                         }\r
3443                         controls += sh_buf;\r
3444                     }\r
3445                     controls += "</select>";\r
3446                     controls += "<select name=\"start_min\" size=\"1\">";\r
3447                     controls += "<option value=\"-\">--</option>";\r
3448                     for (int sm = 0; sm < 60; ++sm)\r
3449                     {\r
3450                         char sm_buf[64];\r
3451                         if ((sh_str != NULL) && (sh_str->length() == 5) && (sh_str->substringFromIndex(3)->intValue() == sm))\r
3452                         {\r
3453                             sprintf_s(sm_buf, "<option value=\"%02d\" selected>%02d</option>", sm, sm);\r
3454                         }\r
3455                         else\r
3456                         {\r
3457                             sprintf_s(sm_buf, "<option value=\"%02d\">%02d</option>", sm, sm);\r
3458                         }\r
3459                         controls += sm_buf;\r
3460                     }\r
3461                     controls += "</select>";\r
3462                     controls += "</span>";\r
3463                     controls += "</div>";\r
3464 \r
3465                     controls += "</fieldset>";\r
3466 \r
3467                     controls += "<input type=\"submit\" class=\"whiteButton\" name=\"req_mod\" value=\"";\r
3468                     controls += LocalizedString(KEY_I18N_Registration, NULL)->cString();\r
3469                     controls += "\">";\r
3470 \r
3471                     controls += "<input type=\"submit\" class=\"redButton\" name=\"req_del\" value=\"";\r
3472                     controls += LocalizedString(KEY_I18N_Delete, NULL)->cString();\r
3473 //                    controls += "\">";\r
3474                     controls += "\" onclick='return confirm(\"To delete. Is it ok?\");'>";\r
3475 \r
3476                     controls += "</form>";\r
3477                 }\r
3478             }\r
3479         }\r
3480 \r
3481         contents += "</ul>";\r
3482         contents += controls;\r
3483 \r
3484         controls = "<form id=\"dialogForm\" title=\"";\r
3485         controls += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
3486         controls += LocalizedString(KEY_I18N_Add, NULL)->cString();\r
3487         controls += "\" class=\"dialog\" target=\"_self\" action=\"add_keywords.cgi\" method=\"GET\">";\r
3488         controls += "<fieldset>";\r
3489         controls += "<h1>";\r
3490         controls += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
3491         controls += LocalizedString(KEY_I18N_Add, NULL)->cString();\r
3492         controls += "</h1>";\r
3493         controls += "<a class=\"button leftButton\" type=\"cancel\">Cancel</a>";\r
3494         controls += "<a class=\"button blueButton\" type=\"submit\">Submit</a>";\r
3495         controls += "<label>Parm1:</label>";\r
3496         controls += "<input type=\"text\" name=\"keywords\" value=\"";\r
3497 //        controls += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
3498         controls += "\"/>";\r
3499         controls += "</fieldset>";\r
3500         controls += "<div class=\"spinner\"></div>";\r
3501         controls += "</form>";\r
3502 \r
3503         contents += controls;\r
3504         //\r
3505         // キーワード:ここまで\r
3506         //\r
3507 \r
3508         html = html->stringByReplacingOccurrencesOfString("%%CONTENTS%%", contents.c_str());\r
3509         if (html != NULL)\r
3510         {\r
3511             result = responseWithHTML(request, html);\r
3512         }\r
3513 \r
3514         break;\r
3515     }\r
3516 \r
3517     // unlock\r
3518     RaymUnlock(this);\r
3519 \r
3520     return result;\r
3521 }\r
3522 \r
3523 HTTPResponse *Controller::responseForStatus(HTTPRequest *request, SOCKADDR_IN *client)\r
3524 {\r
3525     HTTPResponse *result = NULL;\r
3526     if ((request != NULL) && (client != NULL))\r
3527     {\r
3528         String *path = _httpd->rootPath()->stringByAppendingPathComponent("template2.html");\r
3529         String *html = String::stringWithContentsOfFile(path->cString(), UTF8StringEncoding);\r
3530         if (html != NULL)\r
3531         {\r
3532             html = html->stringByReplacingOccurrencesOfString("%%TITLE%%", LocalizedString(KEY_I18N_Tuner_Status, NULL));\r
3533             std::string contents;\r
3534             std::string status;\r
3535             contents += "<ul id=\"home\" title=\"";\r
3536             contents += LocalizedString(KEY_I18N_Tuner_Status, NULL)->cString();\r
3537             contents += "\" selected=\"true\">";\r
3538             DebugLog2("_tunerCount = %d", _tunerCount);\r
3539             for (int i = 0; i < _tunerCount; ++i)\r
3540             {\r
3541                 char key[4];\r
3542                 sprintf_s(key, sizeof(key), "%03d", i);\r
3543 \r
3544                 if (isTunerInitialized(i))\r
3545                 {\r
3546                     contents += "<li>";\r
3547                     contents += "<a href=\"#tuner";\r
3548                     contents += key;\r
3549                     contents += "\">";\r
3550                     contents += key;\r
3551                     contents += ": ";\r
3552                     contents += _tuners[i]->name();\r
3553                     contents += "</a></li>";\r
3554 \r
3555                     //\r
3556                     status += "<div id=\"";\r
3557                     status += "tuner";\r
3558                     status += key;\r
3559                     status += "\" title=\"";\r
3560                     status += _tuners[i]->name();\r
3561                     status += "\" class=\"panel\">";\r
3562 \r
3563                     status += "<h2>Status</h2>";\r
3564                     status += "<fieldset>";\r
3565 \r
3566                     // Tuner Type\r
3567                     status += "<div class=\"row\">";\r
3568                     status += "<label>Type</label>";\r
3569                     status += "<span>";\r
3570                     switch (_tuners[i]->type())\r
3571                     {\r
3572                     case Tuner::ISDB_S:\r
3573                         status += "ISDB-S";\r
3574                         break;\r
3575                     case Tuner::ISDB_T:\r
3576                         status += "ISDB-T";\r
3577                         break;\r
3578                     case Tuner::TYPE_NA:\r
3579                     default:\r
3580                         status += "N/A";\r
3581                         break;\r
3582                     }\r
3583                     status += "</span>";\r
3584                     status += "</div>";\r
3585 \r
3586                     // LnbPower\r
3587                     status += "<div class=\"row\">";\r
3588                     status += "<label>LnbPower</label>";\r
3589                     status += "<span>";\r
3590                     switch (_tuners[i]->lnbPower())\r
3591                     {\r
3592                     case Tuner::LNB_POWER_11V:\r
3593                         status += "11V";\r
3594                         break;\r
3595                     case Tuner::LNB_POWER_15V:\r
3596                         status += "15V";\r
3597                         break;\r
3598                     case Tuner::LNB_POWER_OFF:\r
3599                     default:\r
3600                         status += "OFF";\r
3601                         break;\r
3602                     }\r
3603                     status += "</span>";\r
3604                     status += "</div>";\r
3605 \r
3606                     // Channel\r
3607                     char tmpstr[32];\r
3608                     status += "<div class=\"row\">";\r
3609                     status += "<label>Channel</label>";\r
3610                     status += "<span>";\r
3611                     sprintf_s(tmpstr, sizeof(tmpstr), "%03d", _tuners[i]->channel());\r
3612                     status += tmpstr;\r
3613                     status += "</span>";\r
3614                     status += "</div>";\r
3615 \r
3616                     // C/N[dB] AGC xxx/255\r
3617                     uint32_t cn100 = 0;\r
3618                     uint32_t agc = 0;\r
3619                     uint32_t maxAgc = 0;\r
3620                     _tuners[i]->getCnAgc(&cn100, &agc, &maxAgc);\r
3621 \r
3622                     sprintf_s(tmpstr, sizeof(tmpstr), "%d.%02d[dB]", cn100 / 100, cn100 % 100);\r
3623 \r
3624                     status += "<div class=\"row\">";\r
3625                     status += "<label>C/N</label>";\r
3626                     status += "<span>";\r
3627                     status += tmpstr;\r
3628                     status += "</span>";\r
3629                     status += "</div>";\r
3630 \r
3631                     sprintf_s(tmpstr, sizeof(tmpstr), "%03d/%03d", agc, maxAgc);\r
3632 \r
3633                     status += "<div class=\"row\">";\r
3634                     status += "<label>AGC</label>";\r
3635                     status += "<span>";\r
3636                     status += tmpstr;\r
3637                     status += "</span>";\r
3638                     status += "</div>";\r
3639 \r
3640                     status += "</fieldset>";\r
3641                     status += "</div>";\r
3642 \r
3643                 }\r
3644                 else\r
3645                 {\r
3646                     contents += "<li>";\r
3647                     contents += _tuners[i]->name();\r
3648                     contents += "[uninitialized]";\r
3649                     contents += "</li>";\r
3650                 }\r
3651             }\r
3652             contents += "</ul>";\r
3653             contents += status;\r
3654             html = html->stringByReplacingOccurrencesOfString("%%CONTENTS%%", contents.c_str());\r
3655             result = responseWithHTML(request, html);\r
3656         }\r
3657         else\r
3658         {\r
3659             DebugLog2("responseForStatus() html is null\n");\r
3660         }\r
3661     }\r
3662     return result;\r
3663 }\r
3664 \r
3665 HTTPResponse *Controller::responseForRegistCGI(HTTPRequest *request, SOCKADDR_IN *client)\r
3666 {\r
3667     DebugLog2("Controller::responseForRegistCGI()");\r
3668 \r
3669     HTTPResponse *result = NULL;\r
3670 \r
3671     // CGIリクエストとして解析\r
3672     Dictionary *cgi = request->parseAsCGI();\r
3673     if (cgi != NULL)\r
3674     {\r
3675         // CGIパスが一致しているか\r
3676         if ((cgi->stringForKey(HTTPRequest::KEY_CGI) != NULL) && (cgi->stringForKey(HTTPRequest::KEY_CGI)->isEqualToString("/regist.cgi")))\r
3677         {\r
3678             // パラメータがあるか\r
3679             Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
3680             if (params != NULL)\r
3681             {\r
3682                 // パラメータ数が2か\r
3683                 if (params->count() == 2)\r
3684                 {\r
3685                     // パラメータのチェック\r
3686                     String *service_id = NULL;\r
3687                     String *event_id = NULL;\r
3688 \r
3689                     for (uint i = 0; i < params->count(); ++i)\r
3690                     {\r
3691                         Dictionary *param = (Dictionary *)params->objectAtIndex(i);\r
3692                         String *value = param->stringForKey("service_id");\r
3693                         if ((value != NULL) && value->isMatch("^\\d+$"))\r
3694                         {\r
3695                             service_id = value;\r
3696                         }\r
3697                         value = param->stringForKey("event_id");\r
3698                         if ((value != NULL) && value->isMatch("^\\d+$"))\r
3699                         {\r
3700                             event_id = value;\r
3701                         }\r
3702                     }\r
3703 \r
3704                     // 有効なパラメータか\r
3705                     if ((service_id != NULL) && (event_id != NULL))\r
3706                     {\r
3707                         DebugLog2("valid request");\r
3708 \r
3709                         result = responseByResult(request, reserve(service_id->intValue(), event_id->intValue()));\r
3710                     }\r
3711                 }\r
3712 \r
3713                 // パラメータ数が9か\r
3714                 else if (params->count() == 9)\r
3715                 {\r
3716                     // パラメータのチェック\r
3717                     String *service_id = NULL;\r
3718                     String *year       = NULL;\r
3719                     String *month      = NULL;\r
3720                     String *day        = NULL;\r
3721                     String *start_hour = NULL;\r
3722                     String *start_min  = NULL;\r
3723                     String *end_hour   = NULL;\r
3724                     String *end_min    = NULL;\r
3725                     String *repeat     = NULL;\r
3726 \r
3727                     struct {\r
3728                         const char *name;\r
3729                         String **variable;\r
3730                         const char *regex;\r
3731                     }\r
3732                     cgi[] =\r
3733                     {\r
3734                         {"service_id", &service_id, "^\\d+$"},\r
3735                         {"year",       &year,       "^\\d{4}$"},\r
3736                         {"month",      &month,      "^([1-9]|1[0-2])$"},\r
3737                         {"day",        &day,        "^([1-9]|[12][0-9]|3[01])$"},\r
3738                         {"start_hour", &start_hour, "^\\d{2}$"},\r
3739                         {"start_min",  &start_min,  "^\\d{2}$"},\r
3740                         {"end_hour",   &end_hour,   "^\\d{2}$"},\r
3741                         {"end_min",    &end_min,    "^\\d{2}$"},\r
3742                         {"repeat",     &repeat,     "^(off|everyday|weekly|weekday)$"},\r
3743                         {NULL, NULL, NULL}\r
3744                     };\r
3745 \r
3746                     for (uint i = 0; cgi[i].name != NULL; ++i)\r
3747                     {\r
3748                         for (uint j = 0; j < params->count(); ++j)\r
3749                         {\r
3750                             Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
3751                             String *value = param->stringForKey(cgi[i].name);\r
3752                             if ((value != NULL) && value->isMatch(cgi[i].regex))\r
3753                             {\r
3754                                 *(cgi[i].variable) = value;\r
3755                                 break;\r
3756                             }\r
3757                         }\r
3758                     }\r
3759 \r
3760                     // 有効なパラメータか\r
3761                     if ((service_id != NULL) && (year != NULL) && (month != NULL) && (day != NULL) &&\r
3762                         (start_hour != NULL) && (start_min != NULL) && (end_hour != NULL) && (end_min != NULL) && (repeat != NULL))\r
3763                     {\r
3764                         //\r
3765                         DebugLog1("valid param");\r
3766 \r
3767                         Dictionary *epg = Dictionary::dictionaryWithCapacity(0);\r
3768 \r
3769                         // 日付\r
3770                         epg->setString(String::stringWithFormat("%s/%02d/%02d", year->cString(), month->intValue(), day->intValue()), KEY_EPG_DATE);\r
3771 \r
3772                         // 開始時刻\r
3773                         epg->setString(String::stringWithFormat("%s:%s:00", start_hour->cString(), start_min->cString()), KEY_EPG_START);\r
3774 \r
3775                         // 終了時刻\r
3776                         epg->setString(String::stringWithFormat("%s:%s:00", end_hour->cString(), end_min->cString()), KEY_EPG_END);\r
3777 \r
3778                         // 繰り返し\r
3779                         epg->setString(repeat, KEY_EPG_REPEAT);\r
3780 \r
3781                         // Service ID\r
3782                         epg->setString(service_id, KEY_EPG_SERVICE_ID);\r
3783 \r
3784                         // Status\r
3785                         epg->setString("ready", KEY_EPG_STATUS);\r
3786 \r
3787                         result = responseByResult(request, reserve(epg));\r
3788                     }\r
3789                 }\r
3790             }\r
3791         }\r
3792     }\r
3793 \r
3794     return result;\r
3795 }\r
3796 \r
3797 HTTPResponse *Controller::responseForCancelCGI(HTTPRequest *request, SOCKADDR_IN *client)\r
3798 {\r
3799     DebugLog2("Controller::responseForCancelCGI()");\r
3800 \r
3801     HTTPResponse *result = NULL;\r
3802 \r
3803     // CGIリクエストとして解析\r
3804     Dictionary *cgi = request->parseAsCGI();\r
3805     if (cgi != NULL)\r
3806     {\r
3807         // CGIパスが一致しているか\r
3808         if ((cgi->stringForKey(HTTPRequest::KEY_CGI) != NULL) && (cgi->stringForKey(HTTPRequest::KEY_CGI)->isEqualToString("/cancel.cgi")))\r
3809         {\r
3810             // パラメータがあるか\r
3811             Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
3812             if (params != NULL)\r
3813             {\r
3814                 // パラメータ数が1か\r
3815                 if (params->count() == 1)\r
3816                 {\r
3817                     Dictionary *param = (Dictionary *)params->objectAtIndex(0);\r
3818                     String *value = param->stringForKey("resv_id");\r
3819                     if ((value != NULL) && value->isMatch("^\\d{6}$"))\r
3820                     {\r
3821                         result = responseByResult(request, cancel(-1, value->intValue()));\r
3822                     }\r
3823                 }\r
3824             }\r
3825         }\r
3826     }\r
3827     return result;\r
3828 }\r
3829 \r
3830 HTTPResponse *Controller::responseForAddKeywordsCGI(HTTPRequest *request, SOCKADDR_IN *client)\r
3831 {\r
3832     DebugLog2("Controller::responseForAddKeywordsCGI()");\r
3833 \r
3834     HTTPResponse *result = NULL;\r
3835 \r
3836     // CGIリクエストとして解析\r
3837     Dictionary *cgi = request->parseAsCGI();\r
3838     if (cgi != NULL)\r
3839     {\r
3840         // CGIパスが一致しているか\r
3841         if ((cgi->stringForKey(HTTPRequest::KEY_CGI) != NULL) && (cgi->stringForKey(HTTPRequest::KEY_CGI)->isEqualToString("/add_keywords.cgi")))\r
3842         {\r
3843             // パラメータがあるか\r
3844             Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
3845             if (params != NULL)\r
3846             {\r
3847                 // パラメータ数が1か\r
3848                 if (params->count() == 1)\r
3849                 {\r
3850                     Dictionary *param = (Dictionary *)params->objectAtIndex(0);\r
3851                     String *value = param->stringForKey("keywords");\r
3852                     if (value != NULL)\r
3853                     {\r
3854                         value = value->stringByReplacingOccurrencesOfString("+", " ");\r
3855                         if (value != NULL)\r
3856                         {\r
3857                             value = value->stringByRemovingPercentEncoding();\r
3858                         }\r
3859                     }\r
3860                     if (value != NULL)\r
3861                     {\r
3862                         // lock\r
3863                         RaymLock(this);\r
3864 \r
3865                         Dictionary *keywords_info = _reservations->dictionaryForKey(KEY_EPG_KEYWORDS);\r
3866                         if (keywords_info == NULL)\r
3867                         {\r
3868                             keywords_info = Dictionary::dictionaryWithCapacity(0);\r
3869                             _reservations->setObject(keywords_info, KEY_EPG_KEYWORDS);\r
3870                         }\r
3871 \r
3872                         if (keywords_info->dictionaryForKey(value) == NULL)\r
3873                         {\r
3874                             // 新規キーワードの場合\r
3875                             keywords_info->setObject(Dictionary::dictionaryWithCapacity(0), value);\r
3876 \r
3877                             // キーワード予約更新\r
3878                             updateKeywordsReservation();\r
3879 \r
3880                             // スケジュール更新\r
3881                             updateSchedule();\r
3882 \r
3883                             // 保存\r
3884                             _reservations->writeToFile(_reservations_path, true);\r
3885                         }\r
3886 \r
3887                         RaymUnlock(this);\r
3888 \r
3889                         result = responseForReloadURI(request, client, "/reservation.html#_keywords");\r
3890                     }\r
3891                 }\r
3892             }\r
3893         }\r
3894     }\r
3895     return result;\r
3896 }\r
3897 \r
3898 HTTPResponse *Controller::responseForModKeywordsCGI(HTTPRequest *request, SOCKADDR_IN *client)\r
3899 {\r
3900     DebugLog2("Controller::responseForModKeywordsCGI()");\r
3901 \r
3902     HTTPResponse *result = NULL;\r
3903 \r
3904     // CGIリクエストとして解析\r
3905     Dictionary *cgi = request->parseAsCGI();\r
3906     if (cgi != NULL)\r
3907     {\r
3908         DebugLog3("cgi != NULL");\r
3909 \r
3910         // CGIパスが一致しているか\r
3911         if ((cgi->stringForKey(HTTPRequest::KEY_CGI) != NULL) && (cgi->stringForKey(HTTPRequest::KEY_CGI)->isEqualToString("/mod_keywords.cgi")))\r
3912         {\r
3913             DebugLog3("CGI path OK.");\r
3914 \r
3915             // パラメータがあるか\r
3916             Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
3917             if (params != NULL)\r
3918             {\r
3919                 DebugLog3("params != NULL");\r
3920 \r
3921                 // パラメータのチェック\r
3922                 String *keywords   = NULL;\r
3923                 String *service_id = NULL;\r
3924                 String *start_hour = NULL;\r
3925                 String *start_min  = NULL;\r
3926                 String *req_mod    = NULL;\r
3927                 String *req_del    = NULL;\r
3928 \r
3929                 struct {\r
3930                     const char *name;\r
3931                     String **variable;\r
3932                     const char *regex;\r
3933                 }\r
3934                 cgi[] =\r
3935                 {\r
3936                     {"keywords",   &keywords,   ".*"},\r
3937                     {"service_id", &service_id, "^(\\d+|-)$"},\r
3938                     {"start_hour", &start_hour, "^(\\d{2}|-)$"},\r
3939                     {"start_min",  &start_min,  "^(\\d{2}|-)$"},\r
3940                     {"req_mod",    &req_mod,    ".*"},\r
3941                     {"req_del",    &req_del,    ".*"},\r
3942                     {NULL, NULL, NULL}\r
3943                 };\r
3944 \r
3945                 for (uint i = 0; cgi[i].name != NULL; ++i)\r
3946                 {\r
3947                     for (uint j = 0; j < params->count(); ++j)\r
3948                     {\r
3949                         Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
3950                         String *value = param->stringForKey(cgi[i].name);\r
3951                         if ((value != NULL) && value->isMatch(cgi[i].regex))\r
3952                         {\r
3953                             *(cgi[i].variable) = value;\r
3954                             break;\r
3955                         }\r
3956                     }\r
3957                 }\r
3958 \r
3959                 if (keywords != NULL)\r
3960                 {\r
3961                     keywords = keywords->stringByReplacingOccurrencesOfString("+", " ");\r
3962                     if (keywords != NULL)\r
3963                     {\r
3964                         keywords = keywords->stringByRemovingPercentEncoding();\r
3965                     }\r
3966                 }\r
3967 \r
3968                 if ((keywords != NULL) && (service_id != NULL) && (start_hour != NULL) && (start_min != NULL) &&\r
3969                     ((req_mod != NULL) && (req_del == NULL)) || ((req_mod == NULL) && (req_del != NULL)))\r
3970                 {\r
3971 \r
3972                     // lock\r
3973                     RaymLock(this);\r
3974 \r
3975                     Dictionary *keywords_info = _reservations->dictionaryForKey(KEY_EPG_KEYWORDS);\r
3976                     if (keywords_info != NULL)\r
3977                     {\r
3978                         if (req_mod != NULL)\r
3979                         {\r
3980                             DebugLog0("keywords: %s", keywords->cString());\r
3981                             DebugLog0("service_id: %s", service_id->cString());\r
3982                             DebugLog0("start_hour: %s", start_hour->cString());\r
3983                             DebugLog0("start_min: %s", start_min->cString());\r
3984                             Dictionary *kwd_inf = keywords_info->dictionaryForKey(keywords);\r
3985                             if (kwd_inf != NULL)\r
3986                             {\r
3987                                 if (!service_id->isEqualToString("-"))\r
3988                                 {\r
3989                                     kwd_inf->setString(service_id, KEY_EPG_SERVICE_ID);\r
3990                                 }\r
3991                                 else\r
3992                                 {\r
3993                                     kwd_inf->removeObjectForKey(KEY_EPG_SERVICE_ID);\r
3994                                 }\r
3995                                 if (!start_hour->isEqualToString("-") && !start_min->isEqualToString("-"))\r
3996                                 {\r
3997                                     kwd_inf->setString(String::stringWithFormat("%s:%s", start_hour->cString(), start_min->cString()), KEY_EPG_START);\r
3998                                 }\r
3999                                 else\r
4000                                 {\r
4001                                     kwd_inf->removeObjectForKey(KEY_EPG_START);\r
4002                                 }\r
4003                             }\r
4004                         }\r
4005                         else if (req_del != NULL)\r
4006                         {\r
4007                             DebugLog0("keywords: %s", keywords->cString());\r
4008                             keywords_info->removeObjectForKey(keywords);\r
4009                         }\r
4010 \r
4011                         // キーワード予約更新\r
4012                         updateKeywordsReservation();\r
4013 \r
4014                         // スケジュール更新\r
4015                         updateSchedule();\r
4016 \r
4017                         // 保存\r
4018                         _reservations->writeToFile(_reservations_path, true);\r
4019                     }\r
4020 \r
4021                     // unlock\r
4022                     RaymUnlock(this);\r
4023 \r
4024                     result = responseForReloadURI(request, client, "/reservation.html#_keywords");\r
4025                 }\r
4026             }\r
4027         }\r
4028     }\r
4029     return result;\r
4030 }\r
4031 \r
4032 HTTPResponse *Controller::responseForReloadURI(NET::HTTPRequest *request, SOCKADDR_IN *client, const char *uri, int sec)\r
4033 {\r
4034     HTTPResponse *result = NULL;\r
4035 \r
4036     if (uri != NULL)\r
4037     {\r
4038         std::string contents;\r
4039         contents = "<html>";\r
4040         contents += "<head>";\r
4041 #if 0\r
4042         contents += "<meta http-equiv=\"refresh\" content=\"0;URL=";\r
4043 #else\r
4044         contents += "<meta http-equiv=\"refresh\" content=\"";\r
4045         char tmp[16];\r
4046         sprintf_s(tmp, "%d", sec);\r
4047         contents += tmp;\r
4048         contents += ";URL=";\r
4049 #endif\r
4050         contents += uri;\r
4051         contents += "\">";\r
4052         contents += "</head>";\r
4053         contents += "</html>";\r
4054         String *html = String::stringWithUTF8String(contents.c_str());\r
4055         if (html != NULL)\r
4056         {\r
4057             result = responseWithHTML(request, html);\r
4058         }\r
4059     }\r
4060 \r
4061     return result;\r
4062 }\r
4063 \r
4064 HTTPResponse *Controller::responseForPlaylist(HTTPRequest *request, SOCKADDR_IN *client)\r
4065 {\r
4066     HTTPResponse *result = NULL;\r
4067 \r
4068     char hostname[NI_MAXHOST];\r
4069     if (getnameinfo((SOCKADDR *)client, sizeof(SOCKADDR_IN), hostname, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) == 0)\r
4070     {\r
4071         if (_props->stringForKey(KEY_HTTP_HOST)->isEqualToString(hostname) || strcmp("127.0.0.1", hostname) == 0)\r
4072         {\r
4073             if (_iptv_m3u8_local != NULL)\r
4074             {\r
4075                 result = responseWithUTF8Text(request, _iptv_m3u8_local);\r
4076             }\r
4077         }\r
4078         else\r
4079         {\r
4080             if (_iptv_m3u8_remote != NULL)\r
4081             {\r
4082                 result = responseWithUTF8Text(request, _iptv_m3u8_remote);\r
4083             }\r
4084         }\r
4085     }\r
4086 \r
4087     return result;\r
4088 }\r
4089 \r
4090 HTTPResponse *Controller::responseForXmltv(HTTPRequest *request, SOCKADDR_IN *client)\r
4091 {\r
4092     HTTPResponse *result = NULL;\r
4093 \r
4094     std::string str_xmltv_xml;\r
4095     str_xmltv_xml =  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n";\r
4096     str_xmltv_xml += "<!DOCTYPE tv SYSTEM \"xmltv.dtd\">\r\n";\r
4097     str_xmltv_xml += "<tv generator-info-name=\"iPTd_R2\" generator-info-url=\"http://localhost/\">\r\n";\r
4098     str_xmltv_xml += _xmltv_channels;\r
4099 \r
4100     Dictionary *temp_data = _epgs->dictionaryForKey(KEY_TEMP_DATA);\r
4101     if (temp_data != NULL)\r
4102     {\r
4103         Array *keys = temp_data->allKeys();\r
4104         for (uint i = 0; i < keys->count(); ++i)\r
4105         {\r
4106             Dictionary *temp_service = temp_data->dictionaryForKey((String *)keys->objectAtIndex(i));\r
4107             if (temp_service != NULL)\r
4108             {\r
4109                 String *xmltv_programs = temp_service->stringForKey(KEY_PROGRAMS);\r
4110                 if (xmltv_programs != NULL)\r
4111                 {\r
4112                     str_xmltv_xml += xmltv_programs->cString();\r
4113                 }\r
4114             }\r
4115         }\r
4116     }\r
4117 \r
4118     str_xmltv_xml += "</tv>\r\n";\r
4119 \r
4120     String *tmp = String::stringWithUTF8String(str_xmltv_xml.c_str());\r
4121     if (tmp != NULL)\r
4122     {\r
4123         result = responseWithUTF8Text(request, tmp);\r
4124     }\r
4125 \r
4126     return result;\r
4127 }\r
4128 \r
4129 /**\r
4130  * @brief HTTP Live Streaming制御\r
4131  *\r
4132  *     http://hogehoge/チューナ番号_サービスID/streaming[-プリセット名].m3u8 がリクエスト(プリセットはオプション)され、\r
4133  *     チューナ番号/サービスID/プリセット名(ある場合)が有効値の場合にコールされる\r
4134  *\r
4135  * @param [in] request    HTTPリクエスト\r
4136  * @param [in] client     リクエストしたクライアントのアドレス\r
4137  * @param [in] tuner      チューナ番号\r
4138  * @param [in] service_id チャンネル番号\r
4139  * @param [in] preset     プリセット(URLで省略された場合 "default" )\r
4140  */\r
4141 HTTPResponse *Controller::responseForHLSControl(HTTPRequest *request, SOCKADDR_IN *client, int tuner, int service_id, String *preset)\r
4142 {\r
4143     DebugLog2("Controller::responseForHLSControl()");\r
4144 \r
4145     HTTPResponse *result = NULL;\r
4146 \r
4147     // client からホスト名を取得\r
4148     char hostname[NI_MAXHOST];\r
4149     if (getnameinfo((SOCKADDR *)client, sizeof(SOCKADDR_IN), hostname, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) == 0)\r
4150     {\r
4151         // ストリーミング制御情報からHLS情報を取得\r
4152         Dictionary *hls_info = _streaming_ctrls->dictionaryForKey(KEY_HLS_INFO);\r
4153         if (hls_info == NULL)\r
4154         {\r
4155             hls_info = Dictionary::dictionaryWithCapacity(0);\r
4156             _streaming_ctrls->setObject(hls_info, KEY_HLS_INFO);\r
4157         }\r
4158 \r
4159         // HLS情報から指定チューナの情報を取得\r
4160         Dictionary *hls_info_tuner = hls_info->dictionaryForKey(_tuners[tuner]->name());\r
4161         if (hls_info_tuner == NULL)\r
4162         {\r
4163             hls_info_tuner = Dictionary::dictionaryWithCapacity(0);\r
4164             hls_info->setObject(hls_info_tuner, _tuners[tuner]->name());\r
4165         }\r
4166 \r
4167         // 指定チューナの情報からホスト名を確認\r
4168         if ((hls_info_tuner->stringForKey(KEY_HOSTNAME) == NULL) || hls_info_tuner->stringForKey(KEY_HOSTNAME)->isEqualToString(hostname))\r
4169         {\r
4170              // ホスト名を設定\r
4171             hls_info_tuner->setString(hostname, KEY_HOSTNAME);\r
4172 \r
4173             RaymLock(this);\r
4174             if (!_tuners[tuner]->isRecording() && !_tuners[tuner]->isStreaming() && _tuners[tuner]->isLocked())\r
4175             {\r
4176                 if (_tuners[tuner]->type() == Tuner::ISDB_S)\r
4177                 {\r
4178                     _cancel_epg_collect_s = true;\r
4179                 }\r
4180                 else\r
4181                 {\r
4182                     _cancel_epg_collect_t = true;\r
4183                 }\r
4184             }\r
4185             RaymUnlock(this);\r
4186 \r
4187             // HLS制御インスタンス取得\r
4188             HTTPLiveStreaming *hls = (HTTPLiveStreaming *)hls_info_tuner->objectForKey(KEY_HLS_INSTANCE);\r
4189             if (hls == NULL)\r
4190             {\r
4191                 // 生成\r
4192                 hls = HTTPLiveStreaming::alloc()->init()->autorelease();\r
4193                 hls_info_tuner->setObject(hls, KEY_HLS_INSTANCE);\r
4194             }\r
4195 \r
4196             char tuner_and_service_id[10];\r
4197             sprintf_s(tuner_and_service_id, "%03d_%d", tuner, service_id);\r
4198 \r
4199             // 異チャンネルへのリクエスト、または、プリセット変更の場合\r
4200             if (((hls_info_tuner->stringForKey(KEY_CHANNEL) != NULL) && !hls_info_tuner->stringForKey(KEY_CHANNEL)->isEqualToString(tuner_and_service_id)) ||\r
4201                 ((hls_info_tuner->stringForKey(KEY_PRESET) != NULL) && !hls_info_tuner->stringForKey(KEY_PRESET)->isEqualToString(preset)))\r
4202             {\r
4203                 // 停止\r
4204                 hls->stop();\r
4205 \r
4206                 // チャンネルとプリセットは一旦削除\r
4207                 hls_info_tuner->removeObjectForKey(KEY_CHANNEL);\r
4208                 hls_info_tuner->removeObjectForKey(KEY_PRESET);\r
4209             }\r
4210 \r
4211             // 初回リクエスト or チャンネル/プリセット変更 か?\r
4212             if ((hls_info_tuner->stringForKey(KEY_CHANNEL) == NULL) && (hls_info_tuner->stringForKey(KEY_PRESET) == NULL))\r
4213             {\r
4214                 // UDPポートを検索\r
4215                 Dictionary *mapping = _streaming_ctrls->dictionaryForKey(KEY_MAPPING_UDP_TO_TUNER_SERVICE_ID);\r
4216                 if (mapping != NULL)\r
4217                 {\r
4218                     Array *ports = mapping->allKeys();\r
4219                     if (ports != NULL)\r
4220                     {\r
4221                         for (uint i = 0; i < ports->count(); ++i)\r
4222                         {\r
4223                             String *port = (String *)ports->objectAtIndex(i);\r
4224                             String *tmp = mapping->stringForKey(port);\r
4225                             if (tmp->isEqualToString(tuner_and_service_id))\r
4226                             {\r
4227                                 DebugLog0("udp mapping %d -> %s", port->intValue(), tuner_and_service_id);\r
4228 \r
4229                                 // ソースを設定\r
4230                                 hls->setSource(String::stringWithFormat("udp://@0.0.0.0:%d", port->intValue()));\r
4231 \r
4232                                 // トランスコード\r
4233 //                                    hls->setTranscode(_props->dictionaryForKey(KEY_PRESETS)->dictionaryForKey(preset));\r
4234 \r
4235                                 // 出力先を設定\r
4236                                 String *outpath = _props->stringForKey(KEY_CACHE_PATH)->stringByAppendingPathComponent(String::stringWithFormat("%03d", tuner));\r
4237                                 _mkdir(outpath->cString());\r
4238                                 hls->setOutputPath(outpath);\r
4239 \r
4240                                 // インデックス名\r
4241                                 hls->setIndexName(String::stringWithFormat("live_%s", tuner_and_service_id));\r
4242 \r
4243                                 // 開始\r
4244                                 if (hls->start())\r
4245                                 {\r
4246                                     DebugLog0("hls->start() success");\r
4247 \r
4248                                     // チャンネルを保存\r
4249                                     hls_info_tuner->setString(tuner_and_service_id, KEY_CHANNEL);\r
4250 \r
4251                                     // プリセットを保存\r
4252                                     hls_info_tuner->setString(preset, KEY_PRESET);\r
4253                                 }\r
4254 \r
4255                                 break;\r
4256                             }\r
4257                         }\r
4258                     }\r
4259                 }\r
4260             }\r
4261 \r
4262             RaymUnlock(this);\r
4263 \r
4264             String *index_path = hls->indexPath();\r
4265             FileManager *fm = FileManager::defaultManager();\r
4266             int count = 0;\r
4267             while (count++ < 5)\r
4268             {\r
4269                 bool isDirectory = false;\r
4270                 if (fm->fileExistsAtPath(index_path, &isDirectory))\r
4271                 {\r
4272                     if (!isDirectory)\r
4273                     {\r
4274                         DebugLog0("file exists");\r
4275                         bool done = false;\r
4276                         while (!done)\r
4277                         {\r
4278                             RaymLock(this);\r
4279                             result = _httpd->responseWithPath(index_path, request);\r
4280                             RaymUnlock(this);\r
4281                             if (result != NULL)\r
4282                             {\r
4283                                 break;\r
4284                             }\r
4285                             ::Sleep(100);\r
4286                         }\r
4287                         break;\r
4288                     }\r
4289                 }\r
4290                 ::Sleep(1000);\r
4291             }\r
4292 \r
4293             RaymLock(this);\r
4294 \r
4295             hls_info_tuner->setInteger(0, KEY_COUNTER);\r
4296 \r
4297             if (result == NULL)\r
4298             {\r
4299                 DebugLog0("file no exists");\r
4300                 result = responseForReloadURI(request, client, request->URI()->cString(), 10);\r
4301             }\r
4302         }\r
4303         else\r
4304         {\r
4305             // 他ホストが使用中\r
4306         }\r
4307     }\r
4308 \r
4309     return result;\r
4310 }\r
4311 \r
4312 HTTPResponse *Controller::requestTunerControl(HTTPRequest *request, SOCKADDR_IN *client, int tuner)\r
4313 {\r
4314     DebugLog2("%s\n", __FUNCTION__);\r
4315 \r
4316     HTTPResponse *result = NULL;\r
4317 \r
4318     // lock\r
4319     RaymLock(this);\r
4320 \r
4321     // URI取得\r
4322     String *uri = request->URI();\r
4323     while (uri != NULL)\r
4324     {\r
4325         // CGIリクエストとして解析\r
4326         Dictionary *cgi = request->parseAsCGI();\r
4327         if (cgi != NULL)\r
4328         {\r
4329             uri = cgi->stringForKey(HTTPRequest::KEY_CGI);\r
4330             if (uri == NULL)\r
4331             {\r
4332                 break;\r
4333             }\r
4334         }\r
4335         DebugLog0("uri: %s", uri->cString());\r
4336 \r
4337         //\r
4338         // チューナ情報\r
4339         //\r
4340         if (uri->isMatch("^/[0-9]{3}/info.xml$") && (cgi == NULL))\r
4341         {\r
4342         }\r
4343 \r
4344         //\r
4345         // チャンネル設定\r
4346         //   /ttt/channel=nnn\r
4347         //\r
4348         else if (uri->isMatch("^/[0-9]{3}/channel=[0-9]{1,3}$") && (cgi == NULL))\r
4349         {\r
4350 DebugLog0("ch set");\r
4351             String *ch = uri->substringFromIndex(13);\r
4352             if (ch == NULL)\r
4353             {\r
4354                 break;\r
4355             }\r
4356             int channel = ch->intValue();\r
4357 DebugLog0("set %d channel:%d(%s)\n", tuner, channel, ch->cString());\r
4358             DebugLog2("set channel:%d(%s)\n", channel, ch->cString());\r
4359             if (setChannel(tuner, channel))\r
4360             {\r
4361                 // success\r
4362                 DebugLog2("success.\n");\r
4363                 result = responseForSuccess(request);\r
4364             }\r
4365             else\r
4366             {\r
4367                 // failed\r
4368                 DebugLog2("failed.\n");\r
4369                 result = responseForFailed(request);\r
4370             }\r
4371         }\r
4372 \r
4373         //\r
4374         // 録画開始(最大23:59まで)\r
4375         //   /ttt/recording=on?hour=hh&min=mm[&channel=nnn]\r
4376         //\r
4377         else if (uri->isMatch("^/[0-9]{3}/recording=on$") && (cgi != NULL))\r
4378         {\r
4379             // パラメータがあるか\r
4380             Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
4381             if (params == NULL)\r
4382             {\r
4383                 break;\r
4384             }\r
4385 \r
4386             // パラメータ数は2〜3か\r
4387             if ((params->count() != 2) && (params->count() != 3))\r
4388             {\r
4389                 break;\r
4390             }\r
4391 \r
4392             // パラメータのチェック\r
4393             String *p_hour    = NULL;\r
4394             String *p_min     = NULL;\r
4395             String *p_channel = NULL;\r
4396 \r
4397             struct {\r
4398                 const char *name;\r
4399                 String **variable;\r
4400                 const char *regex;\r
4401             }\r
4402             cgi[] =\r
4403             {\r
4404                 {"hour",    &p_hour,    "^[0-2][0-9]$"},\r
4405                 {"min",     &p_min,     "^[0-5][0-9]$"},\r
4406                 {"channel", &p_channel, "^[0-9]{3}$"},\r
4407                 {NULL, NULL, NULL}\r
4408             };\r
4409 \r
4410             for (uint i = 0; cgi[i].name != NULL; ++i)\r
4411             {\r
4412                 *(cgi[i].variable) = NULL;\r
4413                 for (uint j = 0; j < params->count(); ++j)\r
4414                 {\r
4415                     Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
4416                     String *value = param->stringForKey(cgi[i].name);\r
4417                     if ((value != NULL) && value->isMatch(cgi[i].regex))\r
4418                     {\r
4419                         *(cgi[i].variable) = value;\r
4420                     }\r
4421                 }\r
4422             }\r
4423 \r
4424             // パラメータは有効か\r
4425             if ((p_hour == NULL) || (p_min == NULL))\r
4426             {\r
4427                 break;\r
4428             }\r
4429 \r
4430             // チャンネル設定\r
4431             int channel = 0;\r
4432             if (p_channel != NULL)\r
4433             {\r
4434                 channel = p_channel->intValue();\r
4435             }\r
4436             else\r
4437             {\r
4438                 channel = _tuners[tuner]->channel();\r
4439             }\r
4440 \r
4441             if (channel >= 0)\r
4442             {\r
4443                 // recording on\r
4444                 int hour = p_hour->intValue();\r
4445                 int min = p_min->intValue();\r
4446                 if (hour < 24)\r
4447                 {\r
4448                     // EPG生成\r
4449                     Dictionary *epg = Dictionary::dictionaryWithCapacity(0);\r
4450                     while (true)\r
4451                     {\r
4452                         time_t now;\r
4453                         time(&now);\r
4454                         now += 1; // margin\r
4455                         TM tm;\r
4456                         if (localtime_s(&tm, &now) != 0)\r
4457                         {\r
4458                             epg = NULL;\r
4459                             break;\r
4460                         }\r
4461                         TM end;\r
4462                         end = tm;\r
4463                         end.tm_hour += hour;\r
4464                         end.tm_min += min;\r
4465                         end.tm_sec += 1; // margin\r
4466                         if (mktime(&end) == -1)\r
4467                         {\r
4468                             epg = NULL;\r
4469                             break;\r
4470                         }\r
4471 \r
4472                         char tmp[16];\r
4473 \r
4474                         // Date\r
4475                         sprintf_s(tmp, sizeof(tmp), "%04d/%02d/%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);\r
4476                         epg->setString(tmp, KEY_EPG_DATE);\r
4477 \r
4478                         // Start\r
4479                         sprintf_s(tmp, sizeof(tmp), "%02d:%02d:%02d", tm.tm_hour, tm.tm_min, tm.tm_sec);\r
4480                         epg->setString(tmp, KEY_EPG_START);\r
4481 \r
4482                         // End\r
4483                         sprintf_s(tmp, sizeof(tmp), "%02d:%02d:%02d", end.tm_hour, end.tm_min, end.tm_sec);\r
4484                         epg->setString(tmp, KEY_EPG_END);\r
4485 \r
4486                         // Channel\r
4487                         sprintf_s(tmp, sizeof(tmp), "%d", channel);\r
4488                         epg->setString(tmp, KEY_EPG_CHANNEL);\r
4489 \r
4490                         // 繰り返し\r
4491                         epg->setString("off", KEY_EPG_REPEAT); \r
4492 \r
4493                         // Status\r
4494                         epg->setString("ready", KEY_EPG_STATUS);\r
4495 \r
4496                         break;\r
4497                     }\r
4498                 \r
4499                     if (epg != NULL)\r
4500                     {\r
4501                         // 録画開始&結果生成\r
4502                         result = responseByResult(request, reserve(tuner, epg));\r
4503                     }\r
4504                 }\r
4505             }\r
4506         }\r
4507 \r
4508         //\r
4509         // 録画停止\r
4510         //   /ttt/recording=off\r
4511         //\r
4512         else if (uri->isMatch("^/[0-9]{3}/recording=off$") && (cgi == NULL))\r
4513         {\r
4514             // recording off\r
4515             DebugLog2("recording off: %s\n", uri->cString());\r
4516             if (cancel(tuner, -1))\r
4517             {\r
4518                 // success\r
4519                 DebugLog2("success.\n");\r
4520                 result = responseForSuccess(request);\r
4521             }\r
4522             else\r
4523             {\r
4524                 // failed\r
4525                 DebugLog2("failed.\n");\r
4526                 result = responseForFailed(request);\r
4527             }\r
4528         }\r
4529 \r
4530         //\r
4531         // ストリーミング開始\r
4532         //   /ttt/streaming=on?udp=nnnnn[&host=aaaaaa][&sid=sssss]\r
4533         //\r
4534         else if (uri->isMatch("^/[0-9]{3}/streaming=on$") && (cgi != NULL))\r
4535         {\r
4536             //\r
4537             Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
4538             if ((params == NULL) || (params->count() < 0) || (params->count() > 3))\r
4539             {\r
4540                 break;\r
4541             }\r
4542 \r
4543             //\r
4544             String *p_udp  = NULL;\r
4545             String *p_host = NULL;\r
4546             String *p_sid  = NULL;\r
4547 \r
4548             struct {\r
4549                 const char *name;\r
4550                 String **variable;\r
4551                 const char *regex;\r
4552             }\r
4553             cgi[] =\r
4554             {\r
4555                 {"udp",  &p_udp,  "^[0-9]{1,5}$"},\r
4556                 {"host", &p_host, "^.+$"},\r
4557                 {"sid",  &p_sid,  "^[0-9]{1,5}$"},\r
4558                 {NULL, NULL, NULL}\r
4559             };\r
4560 \r
4561             for (uint i = 0; cgi[i].name != NULL; ++i)\r
4562             {\r
4563                 *(cgi[i].variable) = NULL;\r
4564                 for (uint j = 0; j < params->count(); ++j)\r
4565                 {\r
4566                     Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
4567                     String *value = param->stringForKey(cgi[i].name);\r
4568                     if ((value != NULL) && value->isMatch(cgi[i].regex))\r
4569                     {\r
4570                         *(cgi[i].variable) = value;\r
4571                     }\r
4572                 }\r
4573             }\r
4574 \r
4575             // パラメータチェック\r
4576             if (p_udp == NULL)\r
4577             {\r
4578                 break;\r
4579             }\r
4580 \r
4581             SOCKADDR_IN dst_addr;\r
4582             \r
4583             if (p_host != NULL)\r
4584             {\r
4585     #if 0\r
4586                 std::string host = udpstr.substr(idx + 5);\r
4587                 udpstr = udpstr.substr(0, idx - 1);\r
4588                 DebugLog2("udp: %s\n", udpstr.c_str());\r
4589                 DebugLog2("host: %s\n", host.c_str());\r
4590                 struct hostent *ent = gethostbyname(host.c_str());\r
4591     #endif\r
4592             }\r
4593             else\r
4594             {\r
4595                 memcpy(&dst_addr, client, sizeof(SOCKADDR_IN));\r
4596             }\r
4597             dst_addr.sin_port = htons(p_udp->intValue());\r
4598 \r
4599             if (p_sid == NULL)\r
4600             {\r
4601                 if (_tuners[tuner]->startStreaming(&dst_addr))\r
4602                 {\r
4603                     // success\r
4604                     DebugLog2("success.\n");\r
4605                     result = responseForSuccess(request);\r
4606                 }\r
4607                 else\r
4608                 {\r
4609                     // failed\r
4610                     DebugLog2("failed.\n");\r
4611                     result = responseForFailed(request);\r
4612                 }\r
4613             }\r
4614             else\r
4615             {\r
4616                 DebugLog0("sid: %s", p_sid->cString());\r
4617                 DebugLog0("sid: %d", p_sid->intValue());\r
4618                 result = responseForFailed(request);\r
4619             }\r
4620         }\r
4621 \r
4622         //\r
4623         // ストリーミング停止\r
4624         //   /ttt/streaming=off(?host=aaaa)\r
4625         //\r
4626         else if (uri->isMatch("^/[0-9]{3}/streaming=off$"))\r
4627         {\r
4628             // パラメータ\r
4629             String *p_host = NULL;\r
4630 \r
4631             // パラメータがあるか\r
4632             if (cgi != NULL)\r
4633             {\r
4634                 Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
4635                 if (params == NULL)\r
4636                 {\r
4637                     break;\r
4638                 }\r
4639 \r
4640                 // パラメータ数は0〜1か\r
4641                 if ((params->count() != 0) && (params->count() != 1))\r
4642                 {\r
4643                     break;\r
4644                 }\r
4645 \r
4646                 struct {\r
4647                     const char *name;\r
4648                     String **variable;\r
4649                     const char *regex;\r
4650                 }\r
4651                 cgi[] =\r
4652                 {\r
4653                     {"host", &p_host, "^.+$"},\r
4654                     {NULL, NULL, NULL}\r
4655                 };\r
4656 \r
4657                 for (uint i = 0; cgi[i].name != NULL; ++i)\r
4658                 {\r
4659                     *(cgi[i].variable) = NULL;\r
4660                     for (uint j = 0; j < params->count(); ++j)\r
4661                     {\r
4662                         Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
4663                         String *value = param->stringForKey(cgi[i].name);\r
4664                         if ((value != NULL) && value->isMatch(cgi[i].regex))\r
4665                         {\r
4666                             *(cgi[i].variable) = value;\r
4667                         }\r
4668                     }\r
4669                 }\r
4670             }\r
4671 \r
4672             SOCKADDR_IN dst_addr;\r
4673             if (p_host != NULL)\r
4674             {\r
4675             }\r
4676             else\r
4677             {\r
4678             }\r
4679 \r
4680             _tuners[tuner]->stopStreaming();\r
4681             \r
4682             // success\r
4683             DebugLog2("success.\n");\r
4684             result = responseForSuccess(request);\r
4685         }\r
4686 \r
4687         //\r
4688         // HLS制御\r
4689         //\r
4690         else if (uri->isMatch("^/[0-9]{3}/[0-9]+/streaming(-[^\\.]+)?.m3u8$") && (cgi == NULL))\r
4691         {\r
4692             // URIからサービスIDを抽出\r
4693             Range r = uri->rangeOfString("/streaming");\r
4694             int service_id = uri->substringToIndex(r.location)->substringFromIndex(5)->intValue();\r
4695 \r
4696             // presetが指定されている場合、presetを抽出\r
4697             String *preset = NULL;\r
4698             if (uri->isMatch("streaming-"))\r
4699             {\r
4700                 preset = uri->substringFromIndex(r.location + 11);\r
4701                 preset = preset->substringToIndex(preset->length() - 5);\r
4702             }\r
4703             else\r
4704             {\r
4705                 // なければ "default"\r
4706                 preset = String::stringWithUTF8String(KEY_DEFAULT);\r
4707             }\r
4708 \r
4709             // チャンネル/presetが有効か確認\r
4710             Dictionary *mapping = _streaming_ctrls->dictionaryForKey(KEY_MAPPING_TUNER_SERVICE_ID_TO_UDP);\r
4711             if ((mapping != NULL) && (mapping->stringForKey(String::stringWithFormat("%03d_%d", tuner, service_id)) != NULL) &&\r
4712                 (_props->dictionaryForKey(KEY_PRESETS) != NULL) &&\r
4713                 (_props->dictionaryForKey(KEY_PRESETS)->objectForKey(preset) != NULL))\r
4714             {\r
4715                 // \r
4716                 result = responseForHLSControl(request, client, tuner, service_id, preset);\r
4717             }\r
4718             else\r
4719             {\r
4720                 result = responseForFailed(request);\r
4721             }\r
4722         }\r
4723         else if (uri->isMatch("^/[0-9]{3}/[0-9]+/streaming-[0-9]+.ts$") && (cgi == NULL))\r
4724         {\r
4725             // URIからサービスIDを抽出\r
4726             Range r = uri->rangeOfString("/streaming");\r
4727             int service_id = uri->substringToIndex(r.location)->substringFromIndex(5)->intValue();\r
4728 \r
4729             // client からホスト名を取得\r
4730             char hostname[NI_MAXHOST];\r
4731             if (getnameinfo((SOCKADDR *)client, sizeof(SOCKADDR_IN), hostname, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) == 0)\r
4732             {\r
4733                 Dictionary *hls_info = _streaming_ctrls->dictionaryForKey(KEY_HLS_INFO);\r
4734                 if (hls_info != NULL)\r
4735                 {\r
4736                     Dictionary *hls_info_tuner = hls_info->dictionaryForKey(_tuners[tuner]->name());\r
4737                     if (hls_info_tuner != NULL)\r
4738                     {\r
4739                         if (hls_info_tuner->stringForKey(KEY_HOSTNAME)->isEqualToString(hostname) &&\r
4740                             hls_info_tuner->stringForKey(KEY_CHANNEL)->isEqualToString(String::stringWithFormat("%03d_%d", tuner, service_id)))\r
4741                         {\r
4742                             String *path = ((HTTPLiveStreaming *)hls_info_tuner->objectForKey(KEY_HLS_INSTANCE))->outputPath();\r
4743                             if (path != NULL)\r
4744                             {\r
4745                                 result = _httpd->responseWithPath(path->stringByAppendingPathComponent(uri->substringFromIndex(r.location + 1)), request);\r
4746                             }\r
4747                         }\r
4748                     }\r
4749                 }\r
4750             }\r
4751         }\r
4752 \r
4753         break;\r
4754     }\r
4755 \r
4756     // unlock\r
4757     RaymUnlock(this);\r
4758 \r
4759     return result;\r
4760 }\r
4761 \r
4762 HTTPResponse *Controller::requestRTSP(RTSPRequest *request, SOCKET sock, SOCKADDR_IN *client)\r
4763 {\r
4764     HTTPResponse *result = NULL;\r
4765 \r
4766     static int streaming_port = 0;\r
4767 \r
4768     String *uri = request->URI();\r
4769     if (uri->isMatch("^rtsp://[^/]+(:\\d+)/[0-9]{3}/[0-9]+/streaming.sdp/?$"))\r
4770     {\r
4771         if (request->method()->isEqualToString("OPTIONS"))\r
4772         {\r
4773             result = HTTPResponse::alloc()->init();\r
4774             result->setVersion(request->version());\r
4775             result->setStatus(HTTP_STATUS_OK);\r
4776             result->setReason(HTTPDaemon::reasonForStatus(HTTP_STATUS_OK));\r
4777 \r
4778             InternetTextMessageHeader *header =InternetTextMessageHeader::alloc()->init();\r
4779             header->setFieldBodyWithName(request->message()->header()->fieldBodyForName("CSeq"), String::stringWithUTF8String("CSeq"));\r
4780             header->setFieldBodyWithName(String::stringWithUTF8String("DESCRIBE, SETUP, TEARDOWN, PLAY"), String::stringWithUTF8String("Public"));\r
4781 \r
4782             InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, NULL);\r
4783             RELEASE(header);\r
4784 \r
4785             result->setMessage(message);\r
4786             result->autorelease();\r
4787             RELEASE(message);\r
4788         }\r
4789         else if (request->method()->isEqualToString("DESCRIBE"))\r
4790         {\r
4791             String *accept = request->message()->header()->fieldBodyForName("Accept");\r
4792             if ((accept != NULL) && accept->isMatch("application/sdp"))\r
4793             {\r
4794                 result = HTTPResponse::alloc()->init();\r
4795                 result->setVersion(request->version());\r
4796                 result->setStatus(HTTP_STATUS_OK);\r
4797                 result->setReason(HTTPDaemon::reasonForStatus(HTTP_STATUS_OK));\r
4798 \r
4799                 InternetTextMessageHeader *header =InternetTextMessageHeader::alloc()->init();\r
4800                 header->setFieldBodyWithName(request->message()->header()->fieldBodyForName("CSeq"), "CSeq");\r
4801                 header->setFieldBodyWithName("23 Jan 2007 15:35:06 JST", "Date");\r
4802                 header->setFieldBodyWithName("application/sdp", "Content-Type");\r
4803 \r
4804                 std::string str_body;\r
4805                 str_body =  "v=0\r\n";                                    // プロトコルのバージョン\r
4806                 str_body += "o=hoge 1234 5678 IN IP4 172.19.29.9\r\n";   // 発信元およびセッション識別子\r
4807                 str_body += "s=test\r\n";                                // セッション名\r
4808                 str_body += "c=IN IP4 172.19.29.9\r\n";                                // \r
4809                 str_body += "t=0 0\r\n";\r
4810                 str_body += "a=control:*\r\n";\r
4811                 str_body += "a=range:npt=0-\r\n";\r
4812                 str_body += "m=video 0 RAW/RAW/UDP 33\r\n";\r
4813 \r
4814                 // Content-Length\r
4815                 header->setFieldBodyWithName(String::stringWithFormat("%I64u", str_body.length()), "Content-Length");\r
4816 \r
4817                 InternetTextMessageBody *body = InternetTextMessageBody::alloc()->initWithString(String::stringWithUTF8String(str_body.c_str()));\r
4818 \r
4819                 InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, body);\r
4820                 RELEASE(header);\r
4821                 RELEASE(body);\r
4822 \r
4823                 result->setMessage(message);\r
4824                 result->autorelease();\r
4825                 RELEASE(message);\r
4826             }\r
4827         }\r
4828         else if (request->method()->isEqualToString("SETUP"))\r
4829         {\r
4830             String *transport = request->message()->header()->fieldBodyForName("Transport");\r
4831             if ((transport != NULL) && transport->isMatch("UDP.*;unicast;client_port=\\d+"))\r
4832             {\r
4833                 Range r = transport->rangeOfString("_port=");\r
4834                 String *port = transport->substringFromIndex(r.location + 6);\r
4835                 r = port->rangeOfString("-");\r
4836                 port = port->substringToIndex(r.location);\r
4837                 DebugLog0("port: %s", port->cString());\r
4838                 streaming_port = port->intValue();\r
4839 \r
4840                 result = HTTPResponse::alloc()->init();\r
4841                 result->setVersion(request->version());\r
4842                 result->setStatus(HTTP_STATUS_OK);\r
4843                 result->setReason(HTTPDaemon::reasonForStatus(HTTP_STATUS_OK));\r
4844 \r
4845                 InternetTextMessageHeader *header =InternetTextMessageHeader::alloc()->init();\r
4846                 header->setFieldBodyWithName(request->message()->header()->fieldBodyForName("CSeq"), "CSeq");\r
4847                 header->setFieldBodyWithName("12345678", "Session");\r
4848                 String *tr = transport->stringByAppendingString(";server_port=50020-50021");\r
4849                 header->setFieldBodyWithName(tr, "Transport");\r
4850 \r
4851                 InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, NULL);\r
4852                 RELEASE(header);\r
4853 \r
4854                 result->setMessage(message);\r
4855                 result->autorelease();\r
4856                 RELEASE(message);\r
4857             }\r
4858         }\r
4859         else if (request->method()->isEqualToString("PLAY"))\r
4860         {\r
4861             String *range = request->message()->header()->fieldBodyForName("Range");\r
4862             if (range != NULL)\r
4863             {\r
4864                 result = HTTPResponse::alloc()->init();\r
4865                 result->setVersion(request->version());\r
4866                 result->setStatus(HTTP_STATUS_OK);\r
4867                 result->setReason(HTTPDaemon::reasonForStatus(HTTP_STATUS_OK));\r
4868 \r
4869                 InternetTextMessageHeader *header =InternetTextMessageHeader::alloc()->init();\r
4870                 header->setFieldBodyWithName(request->message()->header()->fieldBodyForName("CSeq"), "CSeq");\r
4871                 header->setFieldBodyWithName(request->message()->header()->fieldBodyForName("Session"), "Session");\r
4872                 header->setFieldBodyWithName(range, "Range");\r
4873 \r
4874                 InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, NULL);\r
4875                 RELEASE(header);\r
4876 \r
4877                 result->setMessage(message);\r
4878                 result->autorelease();\r
4879                 RELEASE(message);\r
4880 \r
4881                 SOCKADDR_IN dst_addr;\r
4882 \r
4883                 dst_addr.sin_family = AF_INET;\r
4884                 dst_addr.sin_addr.s_addr = htonl(0xac131d01);\r
4885                 dst_addr.sin_port = htons(streaming_port);\r
4886 \r
4887                 if (_tuners[1]->startStreaming(&dst_addr))\r
4888                 {\r
4889                     DebugLog0("streaming start ok");\r
4890                 }\r
4891             }\r
4892         }\r
4893         else if (request->method()->isEqualToString("TEARDOWN"))\r
4894         {\r
4895             result = HTTPResponse::alloc()->init();\r
4896             result->setVersion(request->version());\r
4897             result->setStatus(HTTP_STATUS_OK);\r
4898             result->setReason(HTTPDaemon::reasonForStatus(HTTP_STATUS_OK));\r
4899 \r
4900             InternetTextMessageHeader *header =InternetTextMessageHeader::alloc()->init();\r
4901             header->setFieldBodyWithName(request->message()->header()->fieldBodyForName("CSeq"), String::stringWithUTF8String("CSeq"));\r
4902 \r
4903             InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, NULL);\r
4904             RELEASE(header);\r
4905 \r
4906             result->setMessage(message);\r
4907             result->autorelease();\r
4908             RELEASE(message);\r
4909         }\r
4910     }\r
4911     else\r
4912     {\r
4913         DebugLog0("no match");\r
4914     }\r
4915 \r
4916     if (result == NULL)\r
4917     {\r
4918         result = HTTPResponse::alloc()->init();\r
4919         result->setVersion(request->version());\r
4920         result->setStatus(HTTP_STATUS_NOT_FOUND);\r
4921         result->setReason(HTTPDaemon::reasonForStatus(HTTP_STATUS_NOT_FOUND));\r
4922         result->autorelease();\r
4923     }\r
4924 \r
4925     return result;\r
4926 }\r
4927 \r
4928 HTTPResponse *Controller::request(HTTPRequest *request, SOCKET sock, SOCKADDR_IN *client)\r
4929 {\r
4930     DebugLog2("%s\n", __FUNCTION__);\r
4931 \r
4932     // 初期化チェック\r
4933     bool flag = false;\r
4934     RaymLock(this);\r
4935     flag = _initialized;\r
4936     RaymUnlock(this);\r
4937     if (!flag)\r
4938     {\r
4939         return NULL;\r
4940     }\r
4941 \r
4942     if (request->version()->isEqualToString("RTSP/1.0"))\r
4943     {\r
4944         return requestRTSP((RTSPRequest *)request, sock, client);\r
4945     }\r
4946 \r
4947     HTTPResponse *response = NULL;\r
4948 \r
4949     if (request->method()->isEqualToString("GET") ||\r
4950         request->method()->isEqualToString("HEAD"))\r
4951     {\r
4952         // URI\r
4953         String *uri = request->URI();\r
4954         DebugLog0("request(%d): %s\n", sock, uri->cString());\r
4955 \r
4956         //\r
4957         //\r
4958         //\r
4959         if (uri->isMatch("^(/|/index\\.(htm|html))$"))\r
4960         {\r
4961             response = responseForMain(request, client);\r
4962         }\r
4963 \r
4964         else if (uri->isMatch("^/status.html$"))\r
4965         {\r
4966             response = responseForStatus(request, client);\r
4967         }\r
4968 \r
4969         //\r
4970         else if (uri->isMatch("^/info.xml$"))\r
4971         {\r
4972         }\r
4973         else if (uri->isMatch("^/video.xml$"))\r
4974         {\r
4975         }\r
4976 \r
4977         //\r
4978         // tuner control\r
4979         //\r
4980         else if (uri->isMatch("^/[0-9]{3}/"))\r
4981         {\r
4982             // String::substringWithRange() を実装するのを後回しにするので。。\r
4983             std::string s = uri->cString();\r
4984             int tuner = atoi(s.substr(1, 3).c_str());\r
4985             if ((0 <= tuner) && (tuner < _tunerCount))\r
4986             {\r
4987                 response = requestTunerControl(request, client, tuner);\r
4988             }\r
4989         }\r
4990 \r
4991         //\r
4992         // reservation control\r
4993         //\r
4994         else if (uri->isMatch("^/reservation.html$"))\r
4995         {\r
4996             response = responseForReservation(request, client);\r
4997         }\r
4998         else if (uri->isMatch("^/regist.cgi"))\r
4999         {\r
5000             response = responseForRegistCGI(request, client);\r
5001         }\r
5002         else if (uri->isMatch("^/cancel.cgi"))\r
5003         {\r
5004             response = responseForCancelCGI(request, client);\r
5005         }\r
5006         else if (uri->isMatch("^/add_keywords.cgi"))\r
5007         {\r
5008             response = responseForAddKeywordsCGI(request, client);\r
5009         }\r
5010         else if (uri->isMatch("^/mod_keywords.cgi"))\r
5011         {\r
5012             response = responseForModKeywordsCGI(request, client);\r
5013         }\r
5014         else if (uri->isMatch("^/reservation.xml$"))\r
5015         {\r
5016         }\r
5017         else if (uri->isMatch("^/[0-9]{6}/"))\r
5018         {\r
5019 //            DebugLog2("reservation: %s\n", uri.c_str());\r
5020         }\r
5021 \r
5022         //\r
5023         // video control\r
5024         //\r
5025         else if (uri->isMatch("^/[0-9]{9}/"))\r
5026         {\r
5027 //            DebugLog2("video: %s\n", uri.c_str());\r
5028         }\r
5029 \r
5030         //\r
5031         // program\r
5032         //\r
5033         else if (uri->isMatch("^/programs.*\\.html$"))\r
5034         {\r
5035             response = responseForPrograms(request, client);\r
5036         }\r
5037         else if (uri->isMatch("^/programs.xml$"))\r
5038         {\r
5039 //            collectEPG();\r
5040 //            return responseWithDictionary(request, _programs);\r
5041         }\r
5042 \r
5043         //\r
5044         //\r
5045         else if (uri->isMatch("^/tv.html$"))\r
5046         {\r
5047 //            return responseForTV(request, client);\r
5048         }\r
5049 \r
5050         // IPTV対応\r
5051         else if (uri->isMatch("^/iptv.m3u8$"))\r
5052         {\r
5053             return responseForPlaylist(request, client);\r
5054         }\r
5055         else if (uri->isMatch("^/xmltv.xml$"))\r
5056         {\r
5057             return responseForXmltv(request, client);\r
5058         }\r
5059 \r
5060         //\r
5061         else if (uri->isMatch("^/iptd\\.log$"))\r
5062         {\r
5063             String *path = _system_path->stringByAppendingPathComponent("log");\r
5064             if (path != NULL)\r
5065             {\r
5066                 path = path->stringByAppendingPathComponent("iptd_core.log");\r
5067                 if (path != NULL)\r
5068                 {\r
5069                     response = _httpd->responseWithPath(path, request);\r
5070                     if (response != NULL)\r
5071                     {\r
5072                         if (response->message() != NULL)\r
5073                         {\r
5074                             if (response->message()->header() != NULL)\r
5075                             {\r
5076                                 response->message()->header()->setFieldBodyWithName("text/plane; charset=UTF-8", "Content-Type");\r
5077                             }\r
5078                         }\r
5079                     }\r
5080                 }\r
5081             }\r
5082         }\r
5083         else if (uri->isMatch("^/non_idle\\.xml$"))\r
5084         {\r
5085             RaymLock(this);\r
5086             _idle_count = 0;\r
5087             RaymUnlock(this);\r
5088             Service::cancelShutdown();\r
5089             return responseForSuccess(request);\r
5090         }\r
5091         else if (uri->isMatch("^/config\\.xml\\?shutdown_time=\\d+$"))\r
5092         {\r
5093             Range r = uri->rangeOfString("time=");\r
5094             int val = uri->substringFromIndex(r.location + 5)->intValue();\r
5095             if (val >= DEF_SHUTDOWN_TIME)\r
5096             {\r
5097                 RaymLock(this);\r
5098                 _shutdown_time = val;\r
5099                 RaymUnlock(this);\r
5100                 return responseForSuccess(request);\r
5101             }\r
5102             else\r
5103             {\r
5104                 return responseForFailed(request);\r
5105             }\r
5106         }\r
5107     }\r
5108     else if (request->method()->isEqualToString("POST"))\r
5109     {\r
5110         // URI\r
5111         String *uri = request->URI();\r
5112         DebugLog1("POST: %s\n", uri->cString());\r
5113         InternetTextMessage *message = request->message();\r
5114         if (message != NULL)\r
5115         {\r
5116             DebugLog3("message: ");\r
5117             InternetTextMessageHeader *header = message->header();\r
5118             if (header != NULL)\r
5119             {\r
5120                 DebugLog3("header: ");\r
5121             }\r
5122             InternetTextMessageBody *body = message->body();\r
5123             if (body != NULL)\r
5124             {\r
5125                 DebugLog3("body: ");\r
5126             }\r
5127         }\r
5128     }\r
5129 \r
5130     return response;\r
5131 }\r
5132 \r
5133 HTTPRequest * Controller::readRequest(SOCKET sock)\r
5134 {\r
5135     return RTSPRequest::requestWithSocket(sock);\r
5136 }\r
5137 \r
5138 #ifndef _WIN32\r
5139 #pragma mark '\r
5140 #pragma mark ------- プロパティ取得 -------\r
5141 #endif\r
5142 \r
5143 bool Controller::isTunerInitialized(int tuner)\r
5144 {\r
5145     DebugLog2("Controller::isTunerInitialized()");\r
5146 \r
5147     bool result = false;\r
5148 \r
5149     if ((0 <= tuner) && (tuner < _tunerCount))\r
5150     {\r
5151         // lock\r
5152         RaymLock(this);\r
5153 \r
5154         Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
5155         if (tunersInfo != NULL)\r
5156         {\r
5157             Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
5158             if (tunerInfo != NULL)\r
5159             {\r
5160                 result = tunerInfo->boolForKey(KEY_INITIALIZED);\r
5161             }\r
5162         }\r
5163 \r
5164         // unlock\r
5165         RaymUnlock(this);\r
5166     }\r
5167 \r
5168     return result;\r
5169 }\r
5170 \r
5171 bool Controller::isTunerEnabled(int tuner)\r
5172 {\r
5173     DebugLog2("Controller::isTunerEnabled()");\r
5174 \r
5175     bool result = false;\r
5176 \r
5177     if ((0 <= tuner) && (tuner < _tunerCount))\r
5178     {\r
5179         // lock\r
5180         RaymLock(this);\r
5181 \r
5182         Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
5183         if (tunersInfo != NULL)\r
5184         {\r
5185             Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
5186             if (tunerInfo != NULL)\r
5187             {\r
5188                 result = tunerInfo->boolForKey(KEY_ENABLED);\r
5189             }\r
5190         }\r
5191 \r
5192         // unlock\r
5193         RaymUnlock(this);\r
5194     }\r
5195 \r
5196     return result;\r
5197 }\r
5198 \r
5199 bool Controller::isChannelEnabled(int tuner, int channel)\r
5200 {\r
5201     DebugLog2("Controller::isChannelEnabled()");\r
5202 \r
5203     bool result = false;\r
5204 \r
5205     if ((0 <= tuner) && (tuner < _tunerCount) && (0 <= channel) && (channel <= Tuner::MAX_CHANNELS_ISDB_T))\r
5206     {\r
5207         // lock\r
5208         RaymLock(this);\r
5209 \r
5210         Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
5211         if (tunersInfo != NULL)\r
5212         {\r
5213             Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
5214             if (tunerInfo != NULL)\r
5215             {\r
5216                 Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
5217                 if (channels != NULL)\r
5218                 {\r
5219                     char key[4];\r
5220                     sprintf_s(key, "%03d", channel);\r
5221                     Dictionary *chInfo = channels->dictionaryForKey(key);\r
5222                     if (chInfo != NULL)\r
5223                     {\r
5224                         result = chInfo->boolForKey(KEY_ENABLED);\r
5225                     }\r
5226                 }\r
5227             }\r
5228         }\r
5229 \r
5230         // unlock\r
5231         RaymUnlock(this);\r
5232     }\r
5233 \r
5234     return result;\r
5235 }\r
5236 \r
5237 Array *Controller::stationInfos(Tuner::Type type)\r
5238 {\r
5239     DebugLog2("Controller::stationInfosForISDB_T()");\r
5240 \r
5241     Array *result = Array::arrayWithCapacity(0);\r
5242 \r
5243     // lock\r
5244     RaymLock(this);\r
5245 \r
5246     Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
5247     if (tunersInfo != NULL)\r
5248     {\r
5249         for (int i = 0; i < _tunerCount; ++i)\r
5250         {\r
5251             if (_tuners[i]->type() == type)\r
5252             {\r
5253                 Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[i]->name());\r
5254                 if (tunerInfo != NULL)\r
5255                 {\r
5256                     Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
5257                     if (channels != NULL)\r
5258                     {\r
5259                         for (uint ch = 0; ch <= (type == Tuner::ISDB_T ? Tuner::MAX_CHANNELS_ISDB_T : Tuner::MAX_CHANNELS_ISDB_S); ++ch)\r
5260                         {\r
5261                             char chkey[4];\r
5262                             sprintf_s(chkey, "%03d", ch);\r
5263                             Dictionary *channel = channels->dictionaryForKey(chkey);\r
5264                             if ((channel != NULL) && channel->boolForKey(KEY_ENABLED))\r
5265                             {\r
5266                                 result->addObject(channel);\r
5267                             }\r
5268                         }\r
5269                     }\r
5270                 }\r
5271                 break;\r
5272             }\r
5273         }\r
5274     }\r
5275 \r
5276     // unlock\r
5277     RaymUnlock(this);\r
5278 \r
5279     return result;\r
5280 }\r
5281 \r
5282 String *Controller::stationName(Tuner::Type type, int channel)\r
5283 {\r
5284     DebugLog2("Controller::stationName()");\r
5285 \r
5286     String *result = NULL;\r
5287 \r
5288     // lock\r
5289     RaymLock(this);\r
5290 \r
5291     Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
5292     if (tunersInfo != NULL)\r
5293     {\r
5294         for (int i = 0; i < _tunerCount; ++i)\r
5295         {\r
5296             if (_tuners[i]->type() == type)\r
5297             {\r
5298                 Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[i]->name());\r
5299                 if (tunerInfo != NULL)\r
5300                 {\r
5301                     Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
5302                     if (channels != NULL)\r
5303                     {\r
5304                         char chkey[4];\r
5305                         sprintf_s(chkey, "%03d", channel);\r
5306                         Dictionary *channel = channels->dictionaryForKey(chkey);\r
5307                         if ((channel != NULL) && channel->boolForKey(KEY_ENABLED))\r
5308                         {\r
5309                             result = channel->stringForKey(KEY_NAME);\r
5310                         }\r
5311                     }\r
5312                 }\r
5313                 break;\r
5314             }\r
5315         }\r
5316     }\r
5317 \r
5318     // unlock\r
5319     RaymUnlock(this);\r
5320 \r
5321     return result;\r
5322 }\r
5323 \r
5324 Array *Controller::programsForServices(Array *services)\r
5325 {\r
5326     DebugLog2("Controller::programsForServices()");\r
5327 \r
5328     Array *result = Array::arrayWithCapacity(0);\r
5329 \r
5330     // lock\r
5331     RaymLock(_epgs);\r
5332 \r
5333     for (uint i = 0; i < services->count(); ++i)\r
5334     {\r
5335         Dictionary *service = (Dictionary *)services->objectAtIndex(i);\r
5336         Dictionary *services = _epgs->dictionaryForKey(KEY_SERVICES);\r
5337         Array *epgs = services->arrayForKey(service->stringForKey(KEY_SERVICE_ID));\r
5338         if (epgs != NULL)\r
5339         {\r
5340             result->addObjectsFromArray(epgs);\r
5341         }\r
5342     }\r
5343 \r
5344     // sort\r
5345     result = result->sortedArrayUsingFunction(compareFunction, this);\r
5346 \r
5347     // unlock\r
5348     RaymUnlock(_epgs);\r
5349 \r
5350     return result;\r
5351 }\r
5352 \r
5353 String *Controller::stationNameForServiceID(String *service_id)\r
5354 {\r
5355     DebugLog2("Controller::stationNameForServiceID()");\r
5356 \r
5357     String *result = NULL;\r
5358 \r
5359     if (service_id != NULL)\r
5360     {\r
5361         // lock\r
5362         RaymLock(this);\r
5363 \r
5364         Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
5365         if (tunersInfo != NULL)\r
5366         {\r
5367             Array *tuners_key = tunersInfo->allKeys();\r
5368 \r
5369             for (uint i = 0; (result == NULL) && (i < tuners_key->count()); ++i)\r
5370             {\r
5371                 Dictionary *tunerInfo = tunersInfo->dictionaryForKey((String *)tuners_key->objectAtIndex(i));\r
5372                 if (tunerInfo != NULL)\r
5373                 {\r
5374                     Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
5375                     if (channels != NULL)\r
5376                     {\r
5377                         Array *chkey = channels->allKeys();\r
5378                         for (uint ch = 0; (result == NULL) && (ch < chkey->count()); ++ch)\r
5379                         {\r
5380                             Dictionary *channel = channels->dictionaryForKey((String *)chkey->objectAtIndex(ch));\r
5381                             if (channel != NULL)\r
5382                             {\r
5383                                 Array *services = channel->arrayForKey(KEY_SERVICES);\r
5384                                 if (services != NULL)\r
5385                                 {\r
5386                                     for (uint j = 0; j < services->count(); ++j)\r
5387                                     {\r
5388                                         Dictionary *service = (Dictionary *)services->objectAtIndex(j);\r
5389                                         String *sid = service->stringForKey(KEY_SERVICE_ID);\r
5390                                         if (sid != NULL)\r
5391                                         {\r
5392                                             if (sid->isEqualToString(service_id))\r
5393                                             {\r
5394                                                 result = service->stringForKey(KEY_NAME);\r
5395                                                 break;\r
5396                                             }\r
5397                                         }\r
5398                                     }\r
5399                                 }\r
5400                             }\r
5401                         }\r
5402                     }\r
5403                 }\r
5404             }\r
5405         }\r
5406 \r
5407         // unlock\r
5408         RaymUnlock(this);\r
5409     }\r
5410 \r
5411     return result;\r
5412 }\r
5413 \r
5414 #ifndef _WIN32\r
5415 #pragma mark '\r
5416 #pragma mark ------- タイマディスパッチャ -------\r
5417 #endif\r
5418 \r
5419 void Controller::timerExpired(Timer *timer, void *userInfo)\r
5420 {\r
5421     DebugLog2("Controller::timerExpired()");\r
5422 \r
5423     //\r
5424 \r
5425     switch ((long long)userInfo)\r
5426     {\r
5427     case CMD_RESTART:\r
5428         if (restart() > 0)\r
5429         {\r
5430             // 初期化成功\r
5431             DebugLog2("tuner initialize success.");\r
5432 \r
5433             _cancel_epg_collect_s = false;\r
5434             _cancel_epg_collect_t = false;\r
5435 \r
5436             // EPG収集用タイマ起動(ISDB-S)\r
5437             _timer_epg_s = Timer::alloc()->initWithTimeInterval(DEF_COLLECT_EPG_DELAY, this, (void *)CMD_COLLECT_EPG_ISDB_S, true);\r
5438             if (_timer_epg_s != NULL)\r
5439             {\r
5440                 _timer_epg_s->fire();\r
5441             }\r
5442 \r
5443             // EPG収集用タイマ起動(ISDB-T)\r
5444             _timer_epg_t = Timer::alloc()->initWithTimeInterval(DEF_COLLECT_EPG_DELAY, this, (void *)CMD_COLLECT_EPG_ISDB_T, true);\r
5445             if (_timer_epg_t != NULL)\r
5446             {\r
5447                 _timer_epg_t->fire();\r
5448             }\r
5449         }\r
5450         else\r
5451         {\r
5452             // 失敗\r
5453             DebugLog0("tuner initialize failed.");\r
5454         }\r
5455         break;\r
5456 \r
5457     case CMD_PERIODIC:                                  // 周期処理\r
5458         periodic();\r
5459         break;\r
5460 \r
5461     case CMD_PERIODIC_2:                                // 周期処理2\r
5462         periodic_2();\r
5463         break;\r
5464 \r
5465     case CMD_COLLECT_EPG_ISDB_S:                        // EPG収集(ISDB-S)\r
5466         // 番組情報収集(ISDB-S)\r
5467         if (collectEPGs(Tuner::ISDB_S))\r
5468         {\r
5469             // 終了(繰り返し無し)\r
5470             _timer_epg_s->setRepeats(false);\r
5471         }\r
5472         else\r
5473         {\r
5474             // リトライ\r
5475             _timer_epg_s->setTimeInterval(DEF_COLLECT_EPG_RETRY);\r
5476         }\r
5477         break;\r
5478 \r
5479     case CMD_COLLECT_EPG_ISDB_T:                        // 番組情報収集(ISDB-T)\r
5480         if (collectEPGs(Tuner::ISDB_T))\r
5481         {\r
5482             // 終了(繰り返し無し)\r
5483             _timer_epg_t->setRepeats(false);\r
5484         }\r
5485         else\r
5486         {\r
5487             // リトライ\r
5488             _timer_epg_t->setTimeInterval(DEF_COLLECT_EPG_RETRY);\r
5489         }\r
5490         break;\r
5491     }\r
5492 }\r
5493 \r
5494 #ifndef _WIN32\r
5495 #pragma mark '\r
5496 #pragma mark ------- 起動/停止関連 -------\r
5497 #endif\r
5498 \r
5499 bool Controller::isIdleState()\r
5500 {\r
5501     DebugLog2("Controller::isIdleState() start.");\r
5502 \r
5503     bool result = _initialized;\r
5504 \r
5505     RaymLock(this);\r
5506 \r
5507     if (_initialized)\r
5508     {\r
5509         time_t now = time(NULL);\r
5510 \r
5511         for (int tuner = 0; tuner < _tunerCount; ++tuner)\r
5512         {\r
5513             Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
5514             if ((array == NULL) || (array->count() == 0))\r
5515             {\r
5516                 // next tuner\r
5517                 continue;\r
5518             }\r
5519 \r
5520             //\r
5521             Dictionary *epg = (Dictionary *)array->objectAtIndex(0);\r
5522             time_t start;\r
5523             time_t end;\r
5524             getTimeWithEPG(epg, &start, &end);\r
5525 \r
5526             if (start + OFFSET_OF_SUPPRESSION_TIME <= now)\r
5527             {\r
5528                 // busy\r
5529                 result = false;\r
5530                 break;\r
5531             }\r
5532         }\r
5533     }\r
5534 \r
5535     RaymUnlock(this);\r
5536 \r
5537     DebugLog2("Controller::isIdleState() end.");\r
5538 \r
5539     return result;\r
5540 }\r
5541 \r
5542 /*\r
5543  * TrayApp::WndProc() からコールされる\r
5544  *\r
5545  *   スレッドコンテキスト:メインスレッド\r
5546  */\r
5547 void Controller::systemWillSuspend()\r
5548 {\r
5549     DebugLog2("Controller::systemWillSuspend() start");\r
5550 \r
5551     RaymLock(this);\r
5552     _cancel_epg_collect_s = true;\r
5553     _cancel_epg_collect_t = true;\r
5554     RaymUnlock(this);\r
5555 \r
5556     // タイマ停止\r
5557     if ((_timer_restart != NULL) && _timer_restart->valid())\r
5558     {\r
5559         _timer_restart->invalidate();\r
5560     }\r
5561     if ((_timer_epg_s != NULL) && _timer_epg_s->valid())\r
5562     {\r
5563         _timer_epg_s->invalidate();\r
5564     }\r
5565     if ((_timer_epg_t != NULL) && _timer_epg_t->valid())\r
5566     {\r
5567         _timer_epg_t->invalidate();\r
5568     }\r
5569     if ((_timer_periodic != NULL) && _timer_periodic->valid())\r
5570     {\r
5571         _timer_periodic->invalidate();\r
5572     }\r
5573     if ((_timer_periodic_2 != NULL) && _timer_periodic_2->valid())\r
5574     {\r
5575         _timer_periodic_2->invalidate();\r
5576     }\r
5577 \r
5578     // lock\r
5579     RaymLock(this);\r
5580 \r
5581     // タイマ解放\r
5582     RELEASE(_timer_restart);\r
5583     RELEASE(_timer_epg_s);\r
5584     RELEASE(_timer_epg_t);\r
5585     RELEASE(_timer_periodic);\r
5586     RELEASE(_timer_periodic_2);\r
5587 \r
5588     // スケジュール更新\r
5589     updateSchedule();\r
5590 \r
5591     // 未初期化に設定\r
5592     _initialized = false;\r
5593 \r
5594     // チューナ解放\r
5595     for (int i = 0; i < _tunerCount; ++i)\r
5596     {\r
5597         if (_tuners[i] != NULL)\r
5598         {\r
5599             delete _tuners[i];\r
5600             _tuners[i] = NULL;\r
5601         }\r
5602     }\r
5603 \r
5604     DebugLog0("system will suspend...");\r
5605 \r
5606     // unlock\r
5607     RaymUnlock(this);\r
5608 \r
5609 #ifdef OBJC_MEMORY_CHECK\r
5610     DebugLog0("global_objc_count_ = %d", Raym::global_objc_count_);\r
5611 #endif\r
5612 \r
5613     DebugLog2("Controller::systemWillSuspend() end");\r
5614 }\r
5615 \r
5616 /*\r
5617  * TrayApp::WndProc() からコールされる\r
5618  *\r
5619  *   スレッドコンテキスト:メインスレッド\r
5620  */\r
5621 void Controller::systemResumed()\r
5622 {\r
5623     DebugLog2("Controller::systemResumed() start");\r
5624 \r
5625     DebugLog0("system resumed.");\r
5626 \r
5627     if (_timer_restart == NULL)\r
5628     {\r
5629         // 再開タイマ起動\r
5630         _timer_restart = Timer::alloc()->initWithTimeInterval(1.0, this, (void *)CMD_RESTART, false);\r
5631         _timer_restart->fire();\r
5632     }\r
5633 \r
5634     DebugLog2("Controller::systemResumed()");\r
5635 }\r
5636 \r
5637 void Controller::detectNonIdle()\r
5638 {\r
5639     DebugLog2("Controller::detectNonIdle()");\r
5640 \r
5641     // lock\r
5642     RaymLock(this);\r
5643 \r
5644     if (_idle_count > 0)\r
5645     {\r
5646         DebugLog0("detect non idle...");\r
5647     }\r
5648     _idle_count = 0;\r
5649 \r
5650     // unlock\r
5651     RaymUnlock(this);\r
5652 }\r
5653 \r
5654 int Controller::restart()\r
5655 {\r
5656     // lock\r
5657     RaymLock(this);\r
5658 \r
5659     // 未初期化に設定\r
5660     _initialized = false;\r
5661 \r
5662     // チューナ解放\r
5663     for (int i = 0; i < _tunerCount; ++i)\r
5664     {\r
5665         if (_tuners[i] != NULL)\r
5666         {\r
5667             delete _tuners[i];\r
5668             _tuners[i] = NULL;\r
5669         }\r
5670     }\r
5671 \r
5672     // 古い予約情報の破棄\r
5673 \r
5674 \r
5675     // チューナ初期化\r
5676     _tunerCount = ry0::device::TunerFactory::scan(_tuners, _multi2_dll);\r
5677 \r
5678     // unlock\r
5679     RaymUnlock(this);\r
5680 \r
5681     // チューナでループ\r
5682     for (int i = 0; i < _tunerCount; ++i)\r
5683     {\r
5684         // チューナが初期化済みか\r
5685         if (!isTunerInitialized(i))\r
5686         {\r
5687             // チャンネルスキャン\r
5688             scanChannel(i);\r
5689         }\r
5690 \r
5691         if (isTunerEnabled(i))\r
5692         {\r
5693             // チャンネル設定\r
5694             setChannel(i, getChannel(i));\r
5695         }\r
5696     }\r
5697 \r
5698     Dictionary *tunerInfos = _props->dictionaryForKey(KEY_TUNERS);\r
5699 \r
5700     // EPG未収集の場合\r
5701     //   最大チューナ番号の各サービスにIPTV有効フラグを立てる\r
5702     if (_epgs->count() == 0)\r
5703     {\r
5704         Dictionary *flags = Dictionary::dictionaryWithCapacity(0);\r
5705         for (int idx = _tunerCount - 1; idx >= 0; --idx)\r
5706         {\r
5707             Dictionary *tunerInfo = tunerInfos->dictionaryForKey(_tuners[idx]->name());\r
5708             if (tunerInfo->boolForKey(KEY_ENABLED))\r
5709             {\r
5710                 int ch_max = (_tuners[idx]->type() == Tuner::Type::ISDB_T) ? Tuner::MAX_CHANNELS_ISDB_T : Tuner::MAX_CHANNELS_ISDB_S;\r
5711                 for (int ch = 0; ch < ch_max; ++ch)\r
5712                 {\r
5713                     char key[8];\r
5714                     sprintf_s(key, "%03d", ch);\r
5715                     Dictionary *channelInfo = tunerInfo->dictionaryForKey(KEY_CHANNELS)->dictionaryForKey(key);\r
5716                     if ((channelInfo != NULL) && channelInfo->boolForKey(KEY_ENABLED))\r
5717                     {\r
5718                         Array *services = channelInfo->arrayForKey(KEY_SERVICES);\r
5719                         for (uint service_idx = 0; service_idx < services->count(); ++service_idx)\r
5720                         {\r
5721                             Dictionary *service = (Dictionary *)services->objectAtIndex(service_idx);\r
5722                             if (!flags->boolForKey(service->stringForKey(KEY_SERVICE_ID)))\r
5723                             {\r
5724                                 service->setBool(true, KEY_IPTV_ENABLED);\r
5725                                 flags->setBool(true, service->stringForKey(KEY_SERVICE_ID));\r
5726                             }\r
5727                         }\r
5728                     }\r
5729                 }\r
5730             }\r
5731         }\r
5732         _props->writeToFile(_props_path, true);\r
5733     }\r
5734 \r
5735     // IPTV用データの準備/UDPポートマッピング\r
5736     Dictionary *temp_data = Dictionary::dictionaryWithCapacity(0);\r
5737     _epgs->setObject(temp_data, KEY_TEMP_DATA);\r
5738 \r
5739     _xmltv_channels = "";\r
5740     std::string str_iptv_m3u8_local;\r
5741     std::string str_iptv_m3u8_remote;\r
5742 \r
5743     str_iptv_m3u8_local = "#EXTM3U\r\n";\r
5744     str_iptv_m3u8_local += "\r\n";\r
5745     str_iptv_m3u8_remote = "#EXTM3U\r\n";\r
5746     str_iptv_m3u8_remote += "\r\n";\r
5747 \r
5748     int udpport = _props->integerForKey(KEY_BEGIN_UDP_PORT);\r
5749     char http_port_str[10];\r
5750     sprintf_s(http_port_str, "%d", _props->integerForKey(KEY_HTTP_PORT));\r
5751     int idx_isdb_t = 0;\r
5752     int idx_isdb_s = 0;\r
5753     Tuner::Type type = Tuner::Type::ISDB_T;\r
5754     while ((idx_isdb_t < _tunerCount) || (idx_isdb_s < _tunerCount))\r
5755     {\r
5756         int *idx = (type == Tuner::Type::ISDB_T) ? &idx_isdb_t : &idx_isdb_s;\r
5757         while (*idx < _tunerCount)\r
5758         {\r
5759             if (_tuners[*idx]->type() == type)\r
5760             {\r
5761                 Dictionary *tunerInfo = tunerInfos->dictionaryForKey(_tuners[*idx]->name());\r
5762                 if (tunerInfo->boolForKey(KEY_ENABLED))\r
5763                 {\r
5764                     int ch_max = (type == Tuner::Type::ISDB_T) ? Tuner::MAX_CHANNELS_ISDB_T : Tuner::MAX_CHANNELS_ISDB_S;\r
5765                     for (int ch = 0; ch < ch_max; ++ch)\r
5766                     {\r
5767                         char key[8];\r
5768                         sprintf_s(key, "%03d", ch);\r
5769                         Dictionary *channelInfo = tunerInfo->dictionaryForKey(KEY_CHANNELS)->dictionaryForKey(key);\r
5770                         if ((channelInfo != NULL) && channelInfo->boolForKey(KEY_ENABLED))\r
5771                         {\r
5772                             Array *services = channelInfo->arrayForKey(KEY_SERVICES);\r
5773                             for (uint service_idx = 0; service_idx < services->count(); ++service_idx)\r
5774                             {\r
5775                                 Dictionary *service = (Dictionary *)services->objectAtIndex(service_idx);\r
5776                                 if (service->boolForKey(KEY_IPTV_ENABLED))\r
5777                                 {\r
5778                                     char channel_name[32];\r
5779                                     sprintf_s(channel_name, "%03d_%s", *idx, service->stringForKey(KEY_SERVICE_ID)->cString());\r
5780                                     char channel_service_id[32];\r
5781                                     sprintf_s(channel_service_id, "%03d/%s", *idx, service->stringForKey(KEY_SERVICE_ID)->cString());\r
5782 \r
5783                                     Dictionary *temp_service = temp_data->dictionaryForKey(service->stringForKey(KEY_SERVICE_ID));\r
5784                                     if (temp_service == NULL)\r
5785                                     {\r
5786                                         temp_service = Dictionary::dictionaryWithCapacity(0);\r
5787                                         temp_data->setObject(temp_service, service->stringForKey(KEY_SERVICE_ID));\r
5788                                     }\r
5789                                     Array *ch_list = temp_service->arrayForKey(KEY_CHANNELS);\r
5790                                     if (ch_list == NULL)\r
5791                                     {\r
5792                                         ch_list = Array::arrayWithCapacity(0);\r
5793                                         temp_service->setObject(ch_list, KEY_CHANNELS);\r
5794                                     }\r
5795                                     ch_list->addObject(String::stringWithUTF8String(channel_name));\r
5796 \r
5797                                     Dictionary *udp_to_tuner_service_id = _streaming_ctrls->dictionaryForKey(KEY_MAPPING_UDP_TO_TUNER_SERVICE_ID);\r
5798                                     if (udp_to_tuner_service_id == NULL)\r
5799                                     {\r
5800                                         udp_to_tuner_service_id = Dictionary::dictionaryWithCapacity(0);\r
5801                                         _streaming_ctrls->setObject(udp_to_tuner_service_id, KEY_MAPPING_UDP_TO_TUNER_SERVICE_ID);\r
5802                                     }\r
5803 \r
5804                                     Dictionary *tuner_service_id_to_udp = _streaming_ctrls->dictionaryForKey(KEY_MAPPING_TUNER_SERVICE_ID_TO_UDP);\r
5805                                     if (tuner_service_id_to_udp == NULL)\r
5806                                     {\r
5807                                         tuner_service_id_to_udp = Dictionary::dictionaryWithCapacity(0);\r
5808                                         _streaming_ctrls->setObject(tuner_service_id_to_udp, KEY_MAPPING_TUNER_SERVICE_ID_TO_UDP);\r
5809                                     }\r
5810 \r
5811                                     Dictionary *tuner_service_id_to_channel = _streaming_ctrls->dictionaryForKey(KEY_MAPPING_TUNER_SERVICE_ID_TO_CHANNEL);\r
5812                                     if (tuner_service_id_to_channel == NULL)\r
5813                                     {\r
5814                                         tuner_service_id_to_channel = Dictionary::dictionaryWithCapacity(0);\r
5815                                         _streaming_ctrls->setObject(tuner_service_id_to_channel, KEY_MAPPING_TUNER_SERVICE_ID_TO_CHANNEL);\r
5816                                     }\r
5817 \r
5818                                     char port_str[10];\r
5819                                     sprintf_s(port_str, "%d", udpport++);\r
5820 \r
5821                                     udp_to_tuner_service_id->setString(channel_name, port_str);\r
5822                                     tuner_service_id_to_udp->setString(port_str, channel_name);\r
5823                                     tuner_service_id_to_channel->setInteger(ch, channel_name);\r
5824 \r
5825 \r
5826                                     _xmltv_channels += "  <channel id=\"";\r
5827                                     _xmltv_channels += channel_name;\r
5828                                     _xmltv_channels += "\"";\r
5829                                     // transport_stream_id\r
5830                                     // original_network_id\r
5831                                     _xmltv_channels += " service_id=\"";\r
5832                                     _xmltv_channels += service->stringForKey(KEY_SERVICE_ID)->cString();\r
5833                                     _xmltv_channels += "\">\r\n";\r
5834 \r
5835                                     _xmltv_channels += "    <display-name lang=\"ja_JP\">";\r
5836                                     _xmltv_channels += service->stringForKey(KEY_NAME)->cString();\r
5837                                     _xmltv_channels += "</display-name>\r\n";\r
5838 \r
5839                                     _xmltv_channels += "  </channel>\r\n";\r
5840 \r
5841                                     str_iptv_m3u8_local += "#EXTINF:-1 tvg-id=\"";\r
5842                                     str_iptv_m3u8_local += channel_name;\r
5843                                     str_iptv_m3u8_local += "\" tvg-logo=\"logo_";\r
5844                                     str_iptv_m3u8_local += channel_name;\r
5845                                     str_iptv_m3u8_local += "\" group-title=\"";\r
5846                                     str_iptv_m3u8_local += _tuners[*idx]->name();\r
5847                                     str_iptv_m3u8_local += "\", ";\r
5848                                     str_iptv_m3u8_local += service->stringForKey(KEY_NAME)->cString();\r
5849                                     str_iptv_m3u8_local += "\r\n";\r
5850                                     str_iptv_m3u8_local += "udp://0.0.0.0:";\r
5851                                     str_iptv_m3u8_local += port_str;\r
5852                                     str_iptv_m3u8_local += "\r\n";\r
5853 \r
5854                                     str_iptv_m3u8_remote += "#EXTINF:-1 tvg-id=\"";\r
5855                                     str_iptv_m3u8_remote += channel_name;\r
5856                                     str_iptv_m3u8_remote += "\" tvg-logo=\"logo_";\r
5857                                     str_iptv_m3u8_remote += channel_name;\r
5858                                     str_iptv_m3u8_remote += "\" group-title=\"";\r
5859                                     str_iptv_m3u8_remote += _tuners[*idx]->name();\r
5860                                     str_iptv_m3u8_remote += "\", ";\r
5861                                     str_iptv_m3u8_remote += service->stringForKey(KEY_NAME)->cString();\r
5862                                     str_iptv_m3u8_remote += "\r\n";\r
5863 #if 1\r
5864                                     if (_props->stringForKey(KEY_HTTP_HOST) != NULL)\r
5865                                     {\r
5866                                         str_iptv_m3u8_remote += "http://";\r
5867                                         str_iptv_m3u8_remote += _props->stringForKey(KEY_HTTP_HOST)->cString();\r
5868                                         str_iptv_m3u8_remote += ":";\r
5869                                         str_iptv_m3u8_remote += http_port_str;\r
5870                                         str_iptv_m3u8_remote += "/";\r
5871                                         str_iptv_m3u8_remote += channel_service_id;\r
5872                                         str_iptv_m3u8_remote += "/streaming.m3u8\r\n";\r
5873                                     }\r
5874 #else\r
5875 //                                    str_iptv_m3u8_remote += "rtsp://";\r
5876                                     str_iptv_m3u8_remote += "sip://";\r
5877                                     str_iptv_m3u8_remote += _props->stringForKey(KEY_HTTP_HOST)->cString();\r
5878                                     str_iptv_m3u8_remote += ":";\r
5879                                     str_iptv_m3u8_remote += http_port_str;\r
5880                                     str_iptv_m3u8_remote += "/";\r
5881                                     str_iptv_m3u8_remote += channel_service_id;\r
5882                                     str_iptv_m3u8_remote += "/streaming.sdp\r\n";\r
5883 #endif\r
5884                                 }\r
5885                             }\r
5886                         }\r
5887                     }\r
5888                 }\r
5889                 break;\r
5890             }\r
5891             ++(*idx);\r
5892         }\r
5893         ++(*idx);\r
5894         type = (type == Tuner::Type::ISDB_T) ? Tuner::Type::ISDB_S : Tuner::Type::ISDB_T;\r
5895     }\r
5896 \r
5897     RELEASE(_iptv_m3u8_local);\r
5898     _iptv_m3u8_local  = String::alloc()->initWithUTF8String(str_iptv_m3u8_local.c_str());\r
5899     RELEASE(_iptv_m3u8_remote);\r
5900     _iptv_m3u8_remote = String::alloc()->initWithUTF8String(str_iptv_m3u8_remote.c_str());\r
5901 \r
5902     // 過去の番組データは削除\r
5903     removePastEPGs();\r
5904 \r
5905     // 周期タイマ起動\r
5906     _timer_periodic = Timer::alloc()->initWithTimeInterval(1.0, this, (void *)CMD_PERIODIC, true);\r
5907     _timer_periodic->fire();\r
5908 \r
5909     // 周期タイマ2起動\r
5910     _timer_periodic_2 = Timer::alloc()->initWithTimeInterval(1.0, this, (void *)CMD_PERIODIC_2, true);\r
5911     _timer_periodic_2->fire();\r
5912 \r
5913     // lock\r
5914     RaymLock(this);\r
5915 \r
5916     // 初期化済みに設定\r
5917     _initialized = true;\r
5918 \r
5919     // unlock\r
5920     RaymUnlock(this);\r
5921 \r
5922     DebugLog0("initialize done.");\r
5923 \r
5924     return (_tunerCount > 0);\r
5925 }\r
5926 \r
5927 void Controller::start()\r
5928 {\r
5929     // ログファイル数設定\r
5930     //   設定以前にログ出力しないこと\r
5931     Raym::LOG_NUM_MAX = 8;\r
5932 \r
5933 #if 1\r
5934     HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());\r
5935     if (hProcess != NULL)\r
5936     {\r
5937         if (SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS) == FALSE)\r
5938         {\r
5939             DebugLog0("SetPriorityClass failed.");\r
5940         }\r
5941         CloseHandle(hProcess);\r
5942     }\r
5943 #endif\r
5944 \r
5945 #ifdef RAYM_MEMORY_CHECK\r
5946     DebugLog0("");\r
5947     DebugLog0("global_raym_count_ = %d", Raym::global_raym_count_);\r
5948 #endif\r
5949 \r
5950     //\r
5951     AutoreleasePool *pool = AutoreleasePool::alloc()->init();\r
5952 \r
5953     // メンバ初期化\r
5954     _state                  = ST_IDLE;\r
5955     _system_path            = NULL;\r
5956     _props_path             = NULL;\r
5957     _props                  = NULL;\r
5958     _status_path            = NULL;\r
5959     _status                 = NULL;\r
5960     _epgs_path              = NULL;\r
5961     _epgs                   = NULL;\r
5962     _store_path             = NULL;\r
5963     _store_remain           = -1;\r
5964     _reservations           = NULL;\r
5965     _reservations_path      = NULL;\r
5966     _timer_restart          = NULL;\r
5967     _timer_periodic         = NULL;\r
5968     _timer_periodic_2       = NULL;\r
5969     _timer_epg_s            = NULL;\r
5970     _timer_epg_t            = NULL;\r
5971     _multi2_dll             = NULL;\r
5972     _streaming_ctrls        = NULL;\r
5973 \r
5974     _idle_count             = 0;\r
5975     _shutdown_time          = 0;\r
5976     _initialized            = false;\r
5977     _reservation_seq_id     = 0;\r
5978     _cancel_epg_collect_s   = false;\r
5979     _cancel_epg_collect_t   = false;\r
5980 \r
5981     _iptv_m3u8_local        = NULL;\r
5982     _iptv_m3u8_remote       = NULL; \r
5983 \r
5984     _tunerCount = 0;\r
5985     for (int i = 0; i < ry0::device::MAX_TUNERS; ++i)\r
5986     {\r
5987         _tuners[i] = NULL;\r
5988     }\r
5989 \r
5990     DebugLog0("");\r
5991     DebugLog0("------------------------------------------------------------------------");\r
5992     DebugLog0("iPTd ver %s (rev.%d)", VERSION, REVISION);\r
5993     DebugLog0("initialize...");\r
5994 \r
5995     // 初期化\r
5996     while (true)\r
5997     {\r
5998         bool updated = false;\r
5999 \r
6000         // システムパス設定\r
6001         _system_path = String::stringWithFormat("%s", GetExecutePath());\r
6002         if (_system_path == NULL)\r
6003         {\r
6004             DebugLog0("error: GetExecutePath()");\r
6005             break;\r
6006         }\r
6007         _system_path = _system_path->stringByReplacingOccurrencesOfString("iptd_core.exe", "");\r
6008         _system_path->retain();\r
6009         DebugLog2("_system_path: %s", _system_path->cString());\r
6010 \r
6011         // プロパティファイルのパス設定\r
6012         _props_path = String::alloc()->initWithFormat("%s%s.iptd.plist", _system_path->cString(), PLIST_PREFIX);\r
6013         if (_props_path == NULL)\r
6014         {\r
6015             DebugLog0("error: set property file path.");\r
6016             break;\r
6017         }\r
6018 \r
6019         // プロパティの読み込み\r
6020         _props = Dictionary::alloc()->initWithContentsOfFile(_props_path);\r
6021         if (_props == NULL)\r
6022         {\r
6023             DebugLog1("property file: %s (created)", _props_path->cString());\r
6024             _props = Dictionary::alloc()->initWithCapacity(0);\r
6025         }\r
6026         else\r
6027         {\r
6028             DebugLog1("property file: %s", _props_path->cString());\r
6029         }\r
6030 \r
6031         // ステータスファイルのパス設定\r
6032         _status_path = String::alloc()->initWithFormat("%s%s.iptd.status.plist", _system_path->cString(), PLIST_PREFIX);\r
6033         if (_status_path == NULL)\r
6034         {\r
6035             DebugLog0("error: set status file path.");\r
6036             break;\r
6037         }\r
6038 \r
6039         // ステータスの読み込み\r
6040         _status = Dictionary::alloc()->initWithContentsOfFile(_status_path);\r
6041         if (_status == NULL)\r
6042         {\r
6043             DebugLog1("status file: %s (created)", _status_path->cString());\r
6044             _status = Dictionary::alloc()->initWithCapacity(0);\r
6045         }\r
6046         else\r
6047         {\r
6048             DebugLog1("status file: %s", _status_path->cString());\r
6049         }\r
6050 \r
6051         // システム名\r
6052         if (_props->stringForKey(KEY_NAME) == NULL)\r
6053         {\r
6054             _props->setString(DEF_NAME, KEY_NAME);\r
6055             updated = true;\r
6056         }\r
6057 \r
6058         // ホスト名\r
6059         if (_props->stringForKey(KEY_HOSTNAME) == NULL)\r
6060         {\r
6061             _props->setString(DEF_HOSTNAME, KEY_HOSTNAME);\r
6062             updated = true;\r
6063         }\r
6064 \r
6065         // multi2デコード可否判定: multi2.dll が使えるか確認する\r
6066         _multi2_dll = ::LoadLibrary(L"multi2.dll");\r
6067         while (_multi2_dll != NULL)\r
6068         {\r
6069             // b25: 関数アドレス取得\r
6070             ARIB_STD_B25 *(*func_b25)();\r
6071             func_b25 = reinterpret_cast<ARIB_STD_B25 *(*)()>(::GetProcAddress(_multi2_dll, "create_arib_std_b25"));\r
6072             if (func_b25 == NULL)\r
6073             {\r
6074                 // 関数アドレス取得NG\r
6075                 FreeLibrary(_multi2_dll);\r
6076                 _multi2_dll = NULL;\r
6077                 break;\r
6078             }\r
6079 \r
6080             // b25: インスタンス生成\r
6081             ARIB_STD_B25 * _b25;\r
6082             _b25 = func_b25();\r
6083             if (_b25 == NULL)\r
6084             {\r
6085                 // インスタンス生成NG\r
6086                 FreeLibrary(_multi2_dll);\r
6087                 _multi2_dll = NULL;\r
6088                 break;\r
6089             }\r
6090             _b25->release(_b25);\r
6091 \r
6092             // bcas: 関数アドレス取得\r
6093             B_CAS_CARD *(*func_bcas)();\r
6094             func_bcas = reinterpret_cast<B_CAS_CARD *(*)()>(::GetProcAddress(_multi2_dll, "create_b_cas_card"));\r
6095             if (func_bcas == NULL)\r
6096             {\r
6097                 // 関数アドレス取得NG\r
6098                 FreeLibrary(_multi2_dll);\r
6099                 _multi2_dll = NULL;\r
6100                 break;\r
6101             }\r
6102 \r
6103             // bcas: インスタンス生成\r
6104             B_CAS_CARD * _bcas;\r
6105             _bcas = func_bcas();\r
6106             if (_bcas == NULL)\r
6107             {\r
6108                 // インスタンス生成NG\r
6109                 FreeLibrary(_multi2_dll);\r
6110                 _multi2_dll = NULL;\r
6111                 break;\r
6112             }\r
6113             if (_bcas->init(_bcas) != 0)\r
6114             {\r
6115                 // bcas初期化NG\r
6116                 FreeLibrary(_multi2_dll);\r
6117                 _multi2_dll = NULL;\r
6118                 break;\r
6119             }\r
6120             _bcas->release(_bcas);\r
6121 \r
6122             // デコード可\r
6123             break;\r
6124         }\r
6125 \r
6126         // HTTP ポート\r
6127         if (_props->integerForKey(KEY_HTTP_PORT) == 0)\r
6128         {\r
6129             _props->setInteger(DEF_HTTP_PORT, KEY_HTTP_PORT);\r
6130             updated = true;\r
6131         }\r
6132 \r
6133         if (_props->stringForKey(KEY_HTTP_HOST) == NULL)\r
6134         {\r
6135             char hostname[NI_MAXHOST];\r
6136             if (gethostname(hostname, sizeof(hostname)) == 0)\r
6137             {\r
6138                 addrinfo ai;\r
6139                 ai.ai_family = PF_INET;\r
6140                 ai.ai_flags = AI_CANONNAME;\r
6141                 addrinfo *p = NULL;\r
6142                 if (getaddrinfo(hostname, 0, &ai, &p) == 0)\r
6143                 {\r
6144                     char hostaddr[40];\r
6145                     if (getnameinfo(p->ai_addr, (int)p->ai_addrlen, hostaddr, sizeof(hostaddr), 0, 0, NI_NUMERICHOST) == 0)\r
6146                     {\r
6147                         _props->setString(hostaddr, KEY_HTTP_HOST);\r
6148                         updated = true;\r
6149                     }\r
6150                 }\r
6151             }\r
6152         }\r
6153 \r
6154         // UDPポートの開始番号\r
6155         if (_props->integerForKey(KEY_BEGIN_UDP_PORT) == 0)\r
6156         {\r
6157             _props->setInteger(DEF_BEGIN_UDP_PORT, KEY_BEGIN_UDP_PORT);\r
6158             updated = true;\r
6159         }\r
6160 \r
6161         // EPGを収集する時間\r
6162         if ((_props->stringForKey(KEY_COLLECT_EPG_TIME) == NULL) ||\r
6163             !_props->stringForKey(KEY_COLLECT_EPG_TIME)->isMatch(String::stringWithUTF8String("^\\d\\d:\\d\\d:\\d\\d$")))\r
6164         {\r
6165             _props->setString(DEF_COLLECT_EPG_TIME, KEY_COLLECT_EPG_TIME);\r
6166             updated = true;\r
6167         }\r
6168 \r
6169         // 録画データ格納先の確認\r
6170         _store_path = _props->stringForKey(KEY_STORE_PATH);\r
6171         if (_store_path ==  NULL)\r
6172         {\r
6173             // プロパティに未設定の場合\r
6174             //   <Public Directory>/Videos を設定\r
6175             const char *public_dir = GetPublicDirectory();\r
6176             if (public_dir == NULL)\r
6177             {\r
6178                 DebugLog0("error: GetPublicDirectory().");\r
6179                 break;\r
6180             }\r
6181             _store_path = String::alloc()->initWithFormat("%s\\Videos", public_dir);\r
6182             _props->setString(_store_path, KEY_STORE_PATH);\r
6183 \r
6184             // 更新フラグ\r
6185             updated = true;\r
6186         }\r
6187         else\r
6188         {\r
6189             _store_path->retain();\r
6190         }\r
6191 \r
6192         // 実際にディレクトリが存在しているか確認\r
6193         FileManager *fm = FileManager::defaultManager();\r
6194         bool isDir = false;\r
6195         if (!fm->fileExistsAtPath(_store_path, &isDir))\r
6196         {\r
6197             isDir = false;\r
6198         }\r
6199         if (!isDir)\r
6200         {\r
6201             DebugLog0("error: \"%s\" is not exists.", _store_path->cString());\r
6202             break;\r
6203         }\r
6204 \r
6205         _store_remain = FileManager::freeSpaceForPath(_store_path);\r
6206 \r
6207         // HLS Preset\r
6208         if (_props->dictionaryForKey(KEY_PRESETS) == NULL)\r
6209         {\r
6210             Dictionary *presets = Dictionary::dictionaryWithCapacity(0);\r
6211             _props->setObject(presets, KEY_PRESETS);\r
6212 \r
6213 // とりあえずこのまま\r
6214 // プリセットは、あとで実装。。。            \r
6215             Array *preset;\r
6216 \r
6217             // "default"\r
6218             preset = STR_ARRAY("-vcodec",         "libx264",\r
6219                               "-b:v",            "768k",\r
6220                               "-s",              "640x360",\r
6221                               "-acodec",         "libfaac",\r
6222                               "-b:a",            "96k",\r
6223                               "-ar",             "44100",\r
6224                               "-flags",          "+loop-global_header",\r
6225                               "-map",            "0",\r
6226                               "-bsf",            "h264_mp4toannexb",\r
6227                               "-f",              "segment",\r
6228                               "-segment_format", "mpegts",\r
6229                               "-segment_time",   "10",\r
6230                               NULL);\r
6231             presets->setObject(preset, KEY_DEFAULT);\r
6232 \r
6233             updated = true;\r
6234         }\r
6235 \r
6236         // キャッシュ(HLSの一時ファイル格納先)パス\r
6237         if (_props->stringForKey(KEY_CACHE_PATH) == NULL)\r
6238         {\r
6239             _props->setString(_store_path->stringByAppendingPathComponent("Cache"), KEY_CACHE_PATH);\r
6240             _mkdir(_props->stringForKey(KEY_CACHE_PATH)->cString());\r
6241             updated = true;\r
6242         }\r
6243         isDir = false;\r
6244         if (!fm->fileExistsAtPath(_props->stringForKey(KEY_CACHE_PATH), &isDir))\r
6245         {\r
6246             isDir = false;\r
6247         }\r
6248         if (!isDir)\r
6249         {\r
6250             DebugLog0("error: \"%s\" is not exists.", _props->stringForKey(KEY_CACHE_PATH)->cString());\r
6251             break;\r
6252         }\r
6253 \r
6254         // 強制シャットダウンまでの時間\r
6255         if ((_props->integerForKey(KEY_FORCED_SHUTDOWN) == 0) || (_props->integerForKey(KEY_FORCED_SHUTDOWN) < DEF_FORCED_SHUTDOWN))\r
6256         {\r
6257             _props->setInteger(DEF_FORCED_SHUTDOWN, KEY_FORCED_SHUTDOWN);\r
6258             updated = true;\r
6259         }\r
6260 \r
6261         // シャットダウン抑止アプリリスト\r
6262         if (_props->arrayForKey(KEY_DO_NOT_IN_SHUTDOWN) == NULL)\r
6263         {\r
6264             Array *apps = Array::arrayWithCapacity(0);\r
6265             apps->addObject(String::stringWithUTF8String("PIXELACORPORATION.*DtvView\\.exe$"));\r
6266             apps->addObject(String::stringWithUTF8String("Common7\\\\IDE\\\\devenv\\.exe$"));\r
6267             apps->addObject(String::stringWithUTF8String("Kodi\\\\Kodi\\.exe$"));\r
6268             _props->setObject(apps, KEY_DO_NOT_IN_SHUTDOWN);\r
6269             updated = true;\r
6270         }\r
6271 \r
6272         // プロパティファイルを保存\r
6273         if (updated)\r
6274         {\r
6275             DebugLog0("props updated.");\r
6276             if (!_props->writeToFile(_props_path, true))\r
6277             {\r
6278                 DebugLog0("Can't write property file.");\r
6279                 break;\r
6280             }\r
6281         }\r
6282 \r
6283         // プロパティの確認\r
6284         DebugLog0("  Name                : %s", _props->stringForKey(KEY_NAME)->cString());\r
6285         DebugLog0("  Decode Enabled      : %s", (_multi2_dll != NULL) ? "true" : "false");\r
6286         DebugLog0("  HTTP Port           : %d", _props->integerForKey(KEY_HTTP_PORT));\r
6287         if (_props->stringForKey(KEY_POWER_MANAGER) != NULL)\r
6288         {\r
6289             DebugLog0("  Power Manager       : %s", _props->stringForKey(KEY_POWER_MANAGER)->cString());\r
6290         }\r
6291 \r
6292         // 番組データファイルのパス設定\r
6293         _epgs_path = String::alloc()->initWithFormat("%s%s.iptd.epgs.plist", _system_path->cString(), PLIST_PREFIX);\r
6294         if (_epgs_path == NULL)\r
6295         {\r
6296             DebugLog0("error: set epgs file path.");\r
6297             break;\r
6298         }\r
6299 \r
6300         // 番組データの読み込み\r
6301         _epgs = Dictionary::alloc()->initWithContentsOfFile(_epgs_path);\r
6302         if (_epgs == NULL)\r
6303         {\r
6304             DebugLog1("epgs file: %s (created)", _epgs_path->cString());\r
6305             _epgs = Dictionary::alloc()->initWithCapacity(0);\r
6306         }\r
6307         else\r
6308         {\r
6309             DebugLog1("epgs file: %s", _epgs_path->cString());\r
6310         }\r
6311 \r
6312         // 予約データファイルのパス設定\r
6313         _reservations_path = String::alloc()->initWithFormat("%s%s.iptd.reservations.plist", _system_path->cString(), PLIST_PREFIX);\r
6314         if (_reservations_path == NULL)\r
6315         {\r
6316             DebugLog0("error: set reservations file path.");\r
6317             break;\r
6318         }\r
6319 \r
6320         // 予約データの読み込み\r
6321         _reservations = Dictionary::alloc()->initWithContentsOfFile(_reservations_path);\r
6322         if (_reservations == NULL)\r
6323         {\r
6324             DebugLog1("reservations file: %s (created)", _reservations_path->cString());\r
6325             _reservations = Dictionary::alloc()->initWithCapacity(0);\r
6326         }\r
6327         else\r
6328         {\r
6329             DebugLog1("reservations file: %s", _reservations_path->cString());\r
6330 \r
6331             // 予約情報シーケンスID\r
6332             _reservation_seq_id = _reservations->integerForKey(KEY_EPG_LAST_RESV_ID);\r
6333         }\r
6334 \r
6335         // ストリーミング制御情報格納用\r
6336         _streaming_ctrls = Dictionary::alloc()->initWithCapacity(0);\r
6337 \r
6338         // httpdのルートパス\r
6339         String *rootPath = _system_path->stringByAppendingPathComponent("iptd_html");\r
6340         if (!fm->fileExistsAtPath(rootPath, &isDir))\r
6341         {\r
6342             isDir = false;\r
6343         }\r
6344         if (!isDir)\r
6345         {\r
6346             DebugLog0("error: \"%s\" is not exists.", rootPath->cString());\r
6347             break;\r
6348         }\r
6349 \r
6350         // httpd開始\r
6351         int port = _props->integerForKey(KEY_HTTP_PORT);\r
6352         _httpd = NET::HTTPDaemon::alloc()->initWithPort(port, 30);\r
6353         _httpd->setRootPath(rootPath);\r
6354         _httpd->setDelegate(this);\r
6355         if (!_httpd->start())\r
6356         {\r
6357             DebugLog0("Can't start httpd.");\r
6358             break;\r
6359         }\r
6360 \r
6361         // 再開タイマ起動\r
6362         _timer_restart = Timer::alloc()->initWithTimeInterval(1.0, this, (void *)CMD_RESTART, false);\r
6363         _timer_restart->fire();\r
6364 \r
6365 \r
6366         SERVICE_STATUS ss;\r
6367         ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;\r
6368         ss.dwWin32ExitCode = NO_ERROR;\r
6369         ss.dwServiceSpecificExitCode = 0;\r
6370         ss.dwCheckPoint = 0;\r
6371         ss.dwWaitHint = 0;\r
6372         ss.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PRESHUTDOWN;\r
6373         ss.dwCurrentState = SERVICE_RUNNING;\r
6374 \r
6375         if (setServiceStatus(&ss))\r
6376         {\r
6377             RaymLock(this);\r
6378             _state = ST_RUN;\r
6379             while (_state == ST_RUN)\r
6380             {\r
6381                 RaymCondWait(this);\r
6382             }\r
6383             RaymUnlock(this);\r
6384 \r
6385             RaymLock(this);\r
6386             _cancel_epg_collect_s = true;\r
6387             _cancel_epg_collect_t = true;\r
6388             RaymUnlock(this);\r
6389 \r
6390             // httpd終了待ち\r
6391             _httpd->stop();\r
6392 \r
6393             // スケジュール更新\r
6394             updateSchedule();\r
6395         }\r
6396         else\r
6397         {\r
6398             DebugLog0("error: SetServiceStatus(): %u", GetLastError());\r
6399         }\r
6400 \r
6401         break;\r
6402     }\r
6403 \r
6404     // タイマ停止\r
6405     if ((_timer_restart != NULL) && _timer_restart->valid())\r
6406     {\r
6407         _timer_restart->invalidate();\r
6408     }\r
6409     if ((_timer_epg_s != NULL) && _timer_epg_s->valid())\r
6410     {\r
6411         _timer_epg_s->invalidate();\r
6412     }\r
6413     if ((_timer_epg_t != NULL) && _timer_epg_t->valid())\r
6414     {\r
6415         _timer_epg_t->invalidate();\r
6416     }\r
6417     if ((_timer_periodic != NULL) && _timer_periodic->valid())\r
6418     {\r
6419         _timer_periodic->invalidate();\r
6420     }\r
6421     if ((_timer_periodic_2 != NULL) && _timer_periodic_2->valid())\r
6422     {\r
6423         _timer_periodic_2->invalidate();\r
6424     }\r
6425 \r
6426     // チューナ解放\r
6427     for (int i = 0; i < _tunerCount; ++i)\r
6428     {\r
6429         if (_tuners[i] != NULL)\r
6430         {\r
6431             delete _tuners[i];\r
6432             _tuners[i] = NULL;\r
6433         }\r
6434     }\r
6435 \r
6436     // 解放\r
6437     RELEASE(_timer_restart);\r
6438     RELEASE(_timer_epg_s);\r
6439     RELEASE(_timer_epg_t);\r
6440     RELEASE(_timer_periodic);\r
6441     RELEASE(_timer_periodic_2);\r
6442     RELEASE(_streaming_ctrls);\r
6443     RELEASE(_httpd);\r
6444     RELEASE(_system_path);\r
6445     RELEASE(_props_path);\r
6446     RELEASE(_props);\r
6447     RELEASE(_status_path);\r
6448     RELEASE(_status);\r
6449     RELEASE(_epgs_path);\r
6450     RELEASE(_epgs);\r
6451     RELEASE(_store_path);\r
6452     RELEASE(_reservations_path);\r
6453     RELEASE(_reservations);\r
6454     RELEASE(_iptv_m3u8_local);\r
6455     RELEASE(_iptv_m3u8_remote);\r
6456 \r
6457     if (_multi2_dll != NULL)\r
6458     {\r
6459         FreeLibrary(_multi2_dll);\r
6460     }\r
6461 \r
6462     RaymLock(this);\r
6463     _state = ST_IDLE;\r
6464     RaymCondSignal(this);\r
6465     RaymUnlock(this);\r
6466 \r
6467     pool->release();\r
6468 \r
6469     // 終了\r
6470     DebugLog0("finished.");\r
6471 \r
6472 #ifdef RAYM_MEMORY_CHECK\r
6473     DebugLog0("global_raym_count_ = %d", Raym::global_raym_count_);\r
6474 #endif\r
6475 }\r
6476 \r
6477 void Controller::stop()\r
6478 {\r
6479     RaymLock(this);\r
6480     if (_state == ST_RUN)\r
6481     {\r
6482         _state = ST_DONE;\r
6483         RaymCondSignal(this);\r
6484         while (_state != ST_IDLE)\r
6485         {\r
6486             RaymCondWait(this);\r
6487         }\r
6488     }\r
6489     RaymUnlock(this);\r
6490 }\r
6491 \r
6492 #ifndef _WIN32\r
6493 #pragma mark '\r
6494 #pragma mark ------- コンストラクタ/デストラクタ -------\r
6495 #endif\r
6496 \r
6497 Controller::Controller()\r
6498 {\r
6499 }\r
6500 \r
6501 Controller::~Controller()\r
6502 {\r
6503 }\r
6504 \r
6505 Controller *Controller::alloc()\r
6506 {\r
6507     return new Controller();\r
6508 }\r
6509 \r
6510 #ifndef _WIN32\r
6511 #pragma mark '\r
6512 #pragma mark ------- その他 -------\r
6513 #endif\r
6514 \r
6515 //\r
6516 // HH:MM:SS 形式の文字列から time_t に変換\r
6517 // 現在時刻が指定文字列の時刻以前の場合、当日の時刻。指定文字列の時刻よりも過ぎている場合、翌日の時刻を返す。\r
6518 //\r
6519 void Controller::getTimeWithString(String *str, time_t *time_var)\r
6520 {\r
6521     if ((str != NULL) && str->isMatch(String::stringWithUTF8String("^\\d\\d:\\d\\d:\\d\\d$")) && (time_var != NULL))\r
6522     {\r
6523         // 時:分:秒 を int型に分解\r
6524         std::string time_str = str->cString();\r
6525         int hour = atoi(time_str.substr(0, 2).c_str());\r
6526         int min  = atoi(time_str.substr(3, 2).c_str());\r
6527         int sec  = atoi(time_str.substr(6, 2).c_str());\r
6528         DebugLog2("%02d:%02d:%02d", hour, min, sec);\r
6529 \r
6530         // 現在時刻取得\r
6531         time_t now = time(NULL);\r
6532         TM now_tm;\r
6533         if (localtime_s(&now_tm, &now) == 0)\r
6534         {\r
6535             int now_sec = now_tm.tm_hour * 3600 + now_tm.tm_min * 60 + now_tm.tm_sec;\r
6536             int col_sec = hour * 3600 + min * 3600 + sec;\r
6537             if (now_sec > col_sec)\r
6538             {\r
6539                 ++now_tm.tm_mday;\r
6540             }\r
6541             now_tm.tm_hour = hour;\r
6542             now_tm.tm_min  = min;\r
6543             now_tm.tm_sec  = sec;\r
6544 \r
6545             *time_var = mktime(&now_tm);\r
6546         }\r
6547     }\r
6548 }\r
6549 \r
6550 void Controller::getTimeWithEPG(Dictionary *epg, time_t *start, time_t *end)\r
6551 {\r
6552     if ((epg == NULL) || (start == NULL) || (end == NULL))\r
6553     {\r
6554         return;\r
6555     }\r
6556     String *date = epg->stringForKey(KEY_EPG_DATE);\r
6557     String *st   = epg->stringForKey(KEY_EPG_START);\r
6558     String *ed   = epg->stringForKey(KEY_EPG_END);\r
6559     if ((date == NULL) || (st == NULL) || (ed == NULL))\r
6560     {\r
6561         return;\r
6562     }\r
6563 \r
6564     std::string dateStr = date->cString();\r
6565     std::string stStr = st->cString();\r
6566     std::string edStr = ed->cString();\r
6567     TM tm_start;\r
6568     tm_start.tm_year = atoi(dateStr.substr(0, 4).c_str()) - 1900;\r
6569     tm_start.tm_mon  = atoi(dateStr.substr(5, 2).c_str()) - 1;\r
6570     tm_start.tm_mday = atoi(dateStr.substr(8, 2).c_str());\r
6571     tm_start.tm_hour = atoi(stStr.substr(0, 2).c_str());\r
6572     tm_start.tm_min  = atoi(stStr.substr(3, 2).c_str());\r
6573     tm_start.tm_sec  = atoi(stStr.substr(6, 2).c_str());\r
6574 \r
6575     TM tm_end;\r
6576     tm_end.tm_year = atoi(dateStr.substr(0, 4).c_str()) - 1900;\r
6577     tm_end.tm_mon  = atoi(dateStr.substr(5, 2).c_str()) - 1;\r
6578     tm_end.tm_mday = atoi(dateStr.substr(8, 2).c_str());\r
6579     tm_end.tm_hour = atoi(edStr.substr(0, 2).c_str());\r
6580     tm_end.tm_min  = atoi(edStr.substr(3, 2).c_str());\r
6581     tm_end.tm_sec  = atoi(edStr.substr(6, 2).c_str());\r
6582     if (stStr > edStr)\r
6583     {\r
6584         tm_end.tm_mday += 1;\r
6585     }\r
6586     *start = mktime(&tm_start);\r
6587     *end = mktime(&tm_end);\r
6588 }\r
6589 \r
6590 } // iPTd\r
6591 } // ry0\r
6592 \r