OSDN Git Service

0.01版
authorosx86_pt1 <rmitachi@ta2.so-net.ne.jp>
Sat, 19 Mar 2016 11:32:29 +0000 (20:32 +0900)
committerosx86_pt1 <rmitachi@ta2.so-net.ne.jp>
Sat, 19 Mar 2016 11:32:29 +0000 (20:32 +0900)
制御コードを1クラスにまとめた。
HTLSの試作

22 files changed:
Localizable.strings [new file with mode: 0644]
iPTd/iPTd.vcxproj
iPTd/iPTd.vcxproj.filters
iPTd_R2.sdf
iptd_html/template1.html [new file with mode: 0644]
iptd_html/template2.html [new file with mode: 0644]
src/Raym/Bundle.cpp
src/Raym/Raym.h
src/ry0/iPTd/CommandRunner.cpp [new file with mode: 0644]
src/ry0/iPTd/CommandRunner.h [new file with mode: 0644]
src/ry0/iPTd/Controller.cpp
src/ry0/iPTd/Controller.h
src/ry0/iPTd/FFmpeg.cpp [new file with mode: 0644]
src/ry0/iPTd/FFmpeg.h [new file with mode: 0644]
src/ry0/iPTd/HTTPD.cpp [deleted file]
src/ry0/iPTd/HTTPD.h [deleted file]
src/ry0/iPTd/HTTPLiveStreaming.cpp [new file with mode: 0644]
src/ry0/iPTd/HTTPLiveStreaming.h [new file with mode: 0644]
src/ry0/iPTd/Reservation.cpp [deleted file]
src/ry0/iPTd/Reservation.h [deleted file]
src/ry0/iPTd/Streaming.cpp [deleted file]
src/ry0/iPTd/Streaming.h [deleted file]

diff --git a/Localizable.strings b/Localizable.strings
new file mode 100644 (file)
index 0000000..bd6746c
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>Main Menu</key>
+       <string>メインメニュー</string>
+       <key>Reservation</key>
+       <string>録画予約</string>
+       <key>New Reservation</key>
+       <string>新規予約</string>
+       <key>Keywords</key>
+       <string>キーワード</string>
+       <key>Add</key>
+       <string>追加</string>
+       <key>Registration</key>
+       <string>登録</string>
+       <key>Tuner Status</key>
+       <string>チューナ状態</string>
+       <key>Tuner</key>
+       <string>チューナ</string>
+       <key>Station Name</key>
+       <string>放送局</string>
+       <key>Date</key>
+       <string>日 付</string>
+       <key>Start Time</key>
+       <string>開 始</string>
+       <key>End Time</key>
+       <string>終 了</string>
+       <key>Stop</key>
+       <string>停止</string>
+       <key>Cancel</key>
+       <string>取り消し</string>
+       <key>Delete</key>
+       <string>削除</string>
+       <key>Programs</key>
+       <string>番組表</string>
+       <key>TV</key>
+       <string>テレビ</string>
+       <key>Video</key>
+       <string>ビデオ</string>
+       <key>Digital Terrestrial Television Broadcasting</key>
+       <string>地デジ</string>
+       <key>Broadcasting Time</key>
+       <string>放送時間</string>
+       <key>Program Title</key>
+       <string>番組名</string>
+       <key>Description</key>
+       <string>概要</string>
+       <key>Repeat</key>
+       <string>繰返し</string>
+       <key>Repeat_off</key>
+       <string>なし</string>
+       <key>Repeat_everyday</key>
+       <string>毎日</string>
+       <key>Repeat_weekly</key>
+       <string>毎週</string>
+       <key>Repeat_weekday</key>
+       <string>月〜金</string>
+</dict>
+</plist>
index 63cafe2..24aad15 100644 (file)
     <ClInclude Include="..\src\Raym\URLResponse.h" />\r
     <ClInclude Include="..\src\Raym\Workspace.h" />\r
     <ClInclude Include="..\src\ry0\device\TunerFactory.h" />\r
+    <ClInclude Include="..\src\ry0\iPTd\CommandRunner.h" />\r
     <ClInclude Include="..\src\ry0\iPTd\Controller.h" />\r
-    <ClInclude Include="..\src\ry0\iPTd\HTTPD.h" />\r
-    <ClInclude Include="..\src\ry0\iPTd\Reservation.h" />\r
-    <ClInclude Include="..\src\ry0\iPTd\Streaming.h" />\r
+    <ClInclude Include="..\src\ry0\iPTd\FFmpeg.h" />\r
+    <ClInclude Include="..\src\ry0\iPTd\HTTPLiveStreaming.h" />\r
     <ClInclude Include="iPTd.h" />\r
     <ClInclude Include="Resource.h" />\r
     <ClInclude Include="stdafx.h" />\r
     <ClCompile Include="..\src\Raym\URLResponse.cpp" />\r
     <ClCompile Include="..\src\Raym\Workspace.cpp" />\r
     <ClCompile Include="..\src\ry0\device\TunerFactory.cpp" />\r
+    <ClCompile Include="..\src\ry0\iPTd\CommandRunner.cpp" />\r
     <ClCompile Include="..\src\ry0\iPTd\Controller.cpp" />\r
-    <ClCompile Include="..\src\ry0\iPTd\HTTPD.cpp" />\r
-    <ClCompile Include="..\src\ry0\iPTd\Reservation.cpp" />\r
-    <ClCompile Include="..\src\ry0\iPTd\Streaming.cpp" />\r
+    <ClCompile Include="..\src\ry0\iPTd\FFmpeg.cpp" />\r
+    <ClCompile Include="..\src\ry0\iPTd\HTTPLiveStreaming.cpp" />\r
     <ClCompile Include="iPTd.cpp" />\r
     <ClCompile Include="stdafx.cpp">\r
       <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>\r
index 0f6a348..b02849e 100644 (file)
     <ClInclude Include="..\src\ry0\iPTd\Controller.h">\r
       <Filter>ソース ファイル\ry0\iPTd</Filter>\r
     </ClInclude>\r
-    <ClInclude Include="..\src\ry0\iPTd\Reservation.h">\r
-      <Filter>ソース ファイル\ry0\iPTd</Filter>\r
-    </ClInclude>\r
-    <ClInclude Include="..\src\ry0\iPTd\Streaming.h">\r
-      <Filter>ソース ファイル\ry0\iPTd</Filter>\r
-    </ClInclude>\r
-    <ClInclude Include="..\src\ry0\iPTd\HTTPD.h">\r
-      <Filter>ソース ファイル\ry0\iPTd</Filter>\r
-    </ClInclude>\r
     <ClInclude Include="..\src\net\HTTPDaemon.h">\r
       <Filter>ソース ファイル\net</Filter>\r
     </ClInclude>\r
     <ClInclude Include="..\src\net\InternetTextMessageHeader.h">\r
       <Filter>ソース ファイル\net</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="..\src\ry0\iPTd\CommandRunner.h">\r
+      <Filter>ソース ファイル\ry0\iPTd</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="..\src\ry0\iPTd\FFmpeg.h">\r
+      <Filter>ソース ファイル\ry0\iPTd</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="..\src\ry0\iPTd\HTTPLiveStreaming.h">\r
+      <Filter>ソース ファイル\ry0\iPTd</Filter>\r
+    </ClInclude>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClCompile Include="stdafx.cpp">\r
     <ClCompile Include="..\src\ry0\device\TunerFactory.cpp">\r
       <Filter>ソース ファイル\ry0\device</Filter>\r
     </ClCompile>\r
-    <ClCompile Include="..\src\ry0\iPTd\Reservation.cpp">\r
-      <Filter>ソース ファイル\ry0\iPTd</Filter>\r
-    </ClCompile>\r
-    <ClCompile Include="..\src\ry0\iPTd\Streaming.cpp">\r
-      <Filter>ソース ファイル\ry0\iPTd</Filter>\r
-    </ClCompile>\r
-    <ClCompile Include="..\src\ry0\iPTd\HTTPD.cpp">\r
-      <Filter>ソース ファイル\ry0\iPTd</Filter>\r
-    </ClCompile>\r
     <ClCompile Include="..\src\net\HTTPDaemon.cpp">\r
       <Filter>ソース ファイル\net</Filter>\r
     </ClCompile>\r
     <ClCompile Include="..\src\net\InternetTextMessageHeader.cpp">\r
       <Filter>ソース ファイル\net</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="..\src\ry0\iPTd\CommandRunner.cpp">\r
+      <Filter>ソース ファイル\ry0\iPTd</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="..\src\ry0\iPTd\FFmpeg.cpp">\r
+      <Filter>ソース ファイル\ry0\iPTd</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="..\src\ry0\iPTd\HTTPLiveStreaming.cpp">\r
+      <Filter>ソース ファイル\ry0\iPTd</Filter>\r
+    </ClCompile>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ResourceCompile Include="iPTd.rc">\r
index 5c676f5..360268b 100644 (file)
Binary files a/iPTd_R2.sdf and b/iPTd_R2.sdf differ
diff --git a/iptd_html/template1.html b/iptd_html/template1.html
new file mode 100644 (file)
index 0000000..8dfcbfb
--- /dev/null
@@ -0,0 +1,64 @@
+<!DOCTYPE html>\r
+<html>\r
+<head>\r
+  <meta http-equiv="content-type" content="text/html; charset=UTF-8">\r
+  <title>%%TITLE%%</title>\r
+  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>\r
+  <link rel="icon" type="image/png" href="iui/iui-favicon.png">\r
+  <link rel="apple-touch-icon" href="iui/iui-logo-touch-icon.png" />\r
+  <link rel="stylesheet" href="iui/iui.css" type="text/css" />\r
+  <link rel="stylesheet" title="Default" href="iui/t/default/default-theme.css"  type="text/css"/>\r
+  <link rel="stylesheet" href="css/iui-panel-list.css" type="text/css" />\r
+  <style type="text/css">\r
+.panel p.normalText { text-align: left;  padding: 0 10px 0 10px; }\r
+  </style>\r
+  <script type="text/javascript">\r
+    var _gaq = _gaq || [];\r
+    _gaq.push(['_setAccount', 'UA-8634743-10'], ['_trackPageview']);\r
+  </script>\r
+  <script type="text/javascript">\r
+    (function() {\r
+        var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;\r
+        ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';\r
+        var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);\r
+    })();\r
+  </script>\r
+</head>\r
+<body>\r
+    <div class="toolbar">\r
+        <h1 id="pageTitle">%%PAGE_TITLE%%</h1>\r
+        <a id="backButton" class="button" href="#"></a>\r
+    </div>\r
+\r
+    %%CONTENTS%%\r
+<!--\r
+<div id="home" class="panel" selected="true">\r
+\r
+  <fieldset>\r
+         <p class="normalText"><b>Welcome to the iUI Demo Site.</b></p>\r
+  </fieldset>\r
+  <h2>Featured Sample Apps</h2>\r
+  <ul>\r
+       <li><a target="_blank" href="samples/music/music.html">Music Sample</a></li>\r
+       <li><a target="_blank" href="samples/digg/index.html">Digg Sample</a></li>\r
+  </ul>\r
+  <h2>Samples &amp; Tests</h2>\r
+  <ul>\r
+       <li><a target="_blank" href="mobile/demos.html">All Samples &amp; Tests</a></li>\r
+  </ul>\r
+  <h2>Other iUI Sites</h2>\r
+  <ul>\r
+       <li><a target="_blank" href="http://www.iui-js.org">iUI Project Website</a></li>\r
+       <li><a target="_blank" href="http://code.google.com/p/iui/">iUI on Google Code</a></li>\r
+  </ul>\r
+  <h2>About This Page</h2>\r
+  <fieldset>\r
+         <p class="normalText">This page is using iUI in CSS-only mode and supports a wide range of desktop and mobile browsers.  The other pages on this site require modern, W3C-compliant mobile browsers.</p>\r
+         <p class="normalText">Current Version: iUI 0.4</p>\r
+  </fieldset>\r
+\r
+</div>\r
+-->\r
+\r
+</body>\r
+</html>\r
diff --git a/iptd_html/template2.html b/iptd_html/template2.html
new file mode 100644 (file)
index 0000000..3c2d082
--- /dev/null
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html manifest="music.manifest">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<title>%%TITLE%%</title>
+<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>
+<link rel="icon" type="image/png" href="../../iui/iui-favicon.png">
+<link rel="apple-touch-icon" href="../../iui/iui-logo-touch-icon.png" />
+<meta name="apple-mobile-web-app-capable" content="yes" />
+<link rel="stylesheet" href="../../iui/iui.css" type="text/css" />
+
+<link rel="stylesheet" id="iui-theme" title="iUI Theme" href="../../iui/t/default/default-theme.css"  type="text/css"/>
+<script type="application/x-javascript" src="../../iui/iui.js"></script>
+<script type="application/x-javascript" src="../../iui/js/iui-theme-switcher.js"></script>
+<script type="application/x-javascript">iui.ts.basePath = "../../iui/";</script>
+<script type="application/x-javascript" src="../../iui/ext-sandbox/cache-manager/iui-cache-manager.js"></script>
+<script type="application/x-javascript" src="music-app.js"></script>
+<!--
+<script type="application/x-javascript" src="http://10.0.1.2:1840/ibug.js"></script>
+-->
+</head>
+
+<body>
+    <div class="toolbar">
+        <h1 id="pageTitle"></h1>
+        <a id="backButton" class="button" href="#"></a>
+        <a class="button" target="_self" href="/">Main Menu</a>
+    </div>
+    
+    %%CONTENTS%%
+    
+</body>
+</html>
index cf90ad0..4ef766f 100644 (file)
@@ -63,17 +63,13 @@ Bundle *Bundle::mainBundle()
             e = wcstombs_s(&returnValue, execute_path_, sizeof(execute_path_), strbuf, _TRUNCATE);\r
             if (e == 0)\r
             {\r
-                char fname[_MAX_FNAME];\r
+                char drive[_MAX_DRIVE];\r
+                char dir[_MAX_DIR];\r
 \r
-                _splitpath_s(execute_path_, NULL, 0, NULL, 0, fname, sizeof(fname), NULL, 0);\r
-                char *p = strstr(execute_path_, fname);\r
-                if (p != NULL)\r
+                if (_splitpath_s(execute_path_, drive, sizeof(drive), dir, sizeof(dir), NULL, 0, NULL, 0) == 0)\r
                 {\r
-                    --p;\r
-                    *p = '\0';\r
+                    path = String::stringWithFormat("%s%s", drive, dir);\r
                 }\r
-                \r
-                path = String::stringWithFormat("%s", execute_path_);\r
             }\r
         }\r
 #else\r
@@ -84,6 +80,11 @@ Bundle *Bundle::mainBundle()
             mainBundle_ = Bundle::alloc()->initWithPath(path);\r
             ((Object *)mainBundle_)->autorelease(true);\r
         }\r
+        else\r
+        {\r
+            DebugLog0("Bundle::mainBundle() abort.");\r
+            abort();\r
+        }\r
     }\r
     mutex_.unlock();\r
 \r
@@ -93,6 +94,7 @@ Bundle *Bundle::mainBundle()
 Bundle *Bundle::initWithPath(String *path)\r
 {\r
     DebugLog2("Bundle::initWithPath(path)");\r
+    DebugLog3("path: %s", path->cString());\r
 \r
     RELEASE(_path);\r
     if (_path != NULL)\r
@@ -109,6 +111,10 @@ Bundle *Bundle::initWithPath(String *path)
     }\r
 \r
     _localizedStrings = Dictionary::alloc()->initWithContentsOfFile(_path->stringByAppendingPathComponent("Localizable.strings"));\r
+    if (_localizedStrings == NULL)\r
+    {\r
+        DebugLog3("_localizedStrings is NULL");\r
+    }\r
 \r
     return this;\r
 }\r
index 14feb2a..3ad180e 100644 (file)
 
 namespace Raym
 {
-/*
+
 inline String *LocalizedString(const char *key, const char *comment)
 {
     return Bundle::mainBundle()->localizedStringForKey(key, key, NULL);
 }
-*/
 
 } // Raym
 
diff --git a/src/ry0/iPTd/CommandRunner.cpp b/src/ry0/iPTd/CommandRunner.cpp
new file mode 100644 (file)
index 0000000..d1ee053
--- /dev/null
@@ -0,0 +1,267 @@
+//
+//
+//
+
+#define DBG_LEVEL 3
+#include "Raym/Log.h"
+
+#include <process.h>
+
+#include "CommandRunner.h"
+
+using namespace Raym;
+
+namespace ry0
+{
+namespace iPTd
+{
+
+CommandRunner::CommandRunner()
+{
+    DebugLog2("CommandRunner::CommandRunner()");
+
+    _state     = ST_IDLE;
+    _command   = NULL;
+    _arguments = NULL;
+}
+
+CommandRunner::~CommandRunner()
+{
+    RELEASE(_command);
+    RELEASE(_arguments);
+
+    DebugLog2("CommandRunner::~CommandRunner()");
+}
+
+CommandRunner *CommandRunner::alloc()
+{
+    return new CommandRunner();
+}
+
+CommandRunner *CommandRunner::init()
+{
+    DebugLog2("CommandRunner::init()");
+
+    _state = ST_IDLE;
+
+    return this;
+}
+
+CommandRunner *CommandRunner::retain()
+{
+    DebugLog2("CommandRunner::retain()");
+
+    Object::retain();
+    return this;
+}
+
+CommandRunner *CommandRunner::autorelease()
+{
+    DebugLog2("CommandRunner::autorelease()");
+
+    Object::autorelease();
+    return this;
+}
+
+void CommandRunner::setCommandPath(String *path)
+{
+    DebugLog2("CommandRunner::setCommandPath()");
+
+    RELEASE(_command);
+    if (path != NULL)
+    {
+        _command = path->retain();
+    }
+}
+
+void CommandRunner::setArguments(Array *args)
+{
+    DebugLog2("CommandRunner::setArguments()");
+
+    RELEASE(_arguments);
+    if (args != NULL)
+    {
+        _arguments = args->retain();
+    }
+}
+
+bool CommandRunner::readLine(String *line)
+{
+    return false;
+}
+
+void CommandRunner::write(String *line)
+{
+}
+
+unsigned __stdcall CommandRunner_run(void *arg)
+{
+    ((CommandRunner *)arg)->run();
+    return 0;
+}
+
+void CommandRunner::run()
+{
+    DebugLog2("CommandRunner::run()");
+
+    AutoreleasePool *pool = AutoreleasePool::alloc()->init();
+
+    // lock
+    RaymLock(this);
+
+    while ((_command != NULL) && (_state == ST_READY))
+    {
+        DebugLog3("pre processing");
+
+        // パイプ生成
+        Pipe *pipe = Pipe::alloc()->init();
+
+        // タスク生成
+        Task *task = Task::alloc()->init();
+        task->setLaunchPath(_command);
+
+        // 引数設定
+        if (_arguments != NULL)
+        {
+            task->setArguments(_arguments);
+        }
+
+        // 標準出力設定
+        task->setStandardOutput(pipe->fileHandleForWriting());
+        task->setStandardError(pipe->fileHandleForWriting());
+
+        // タスク実行
+        task->launch();
+
+        // 読み込み
+        FileInputStream *fis = FileInputStream::fileInputStream(pipe->fileHandleForReading());
+
+        // 状態変更
+        _state = ST_RUN;
+
+        // unlock
+        RaymUnlock(this);
+
+        bool done = false;
+        while (!done)
+        {
+            // ループ処理
+            AutoreleasePool *pool2 = AutoreleasePool::alloc()->init();
+
+            // 1行読み込み
+            bool done2 = readLine(fis->readLine());
+
+            // lock
+            RaymLock(this);
+
+            // 終了チェック
+            done = (done2 || (_state == ST_DONE));
+
+            // unlock
+            RaymUnlock(this);
+
+            pool2->release();
+        }
+
+        DebugLog3("post processing");
+
+        // タスク終了
+        task->terminate();
+        task->release();
+
+        // パイプ破棄
+        pipe->release();
+
+        // lock
+        RaymLock(this);
+        break;
+    }
+
+    // 状態変更
+    _state = ST_IDLE;
+
+    // unlock
+    RaymUnlock(this);
+
+    pool->release();
+
+    DebugLog2("CommandRunner::run() done.");
+}
+
+bool CommandRunner::start()
+{
+    DebugLog2("CommandRunner::start()");
+    
+    bool result = false;
+    
+    RaymLock(this);
+    
+    if (_state == ST_IDLE)
+    {
+        HANDLE h;
+        unsigned int uiThreadId;
+        
+        h = (HANDLE)_beginthreadex(NULL, 0, CommandRunner_run, this, 0, &uiThreadId);
+        if (h != NULL)
+        {
+            _state = ST_READY;
+
+            RaymUnlock(this);
+
+            bool done = false;
+            while (!done)
+            {
+                bool needSleep = false;
+
+                RaymLock(this);
+
+                if (_state == ST_IDLE)
+                {
+                    done = true;
+                }
+                else if (_state == ST_RUN)
+                {
+                    done = true;
+                    result = true;
+                }
+                else if (_state == ST_READY)
+                {
+                    needSleep = true;
+                }
+                RaymUnlock(this);
+
+                if (needSleep)
+                {
+                    ::Sleep(100); // 100 ms
+                }
+            }
+
+            RaymLock(this);
+        }
+    }
+
+    RaymUnlock(this);
+
+    return result;
+}
+
+void CommandRunner::stop()
+{
+}
+
+bool CommandRunner::isRunning()
+{
+    bool result;
+    RaymLock(this);
+    result = (_state == ST_RUN);
+    RaymUnlock(this);
+    return result;
+}
+
+const char *CommandRunner::className()
+{
+    return "CommandRunner";
+}
+
+} // iPTd
+} // ry0
diff --git a/src/ry0/iPTd/CommandRunner.h b/src/ry0/iPTd/CommandRunner.h
new file mode 100644 (file)
index 0000000..e3cdee3
--- /dev/null
@@ -0,0 +1,52 @@
+//
+//
+//
+
+#pragma once
+
+#include "Raym/Raym.h"
+
+namespace ry0
+{
+namespace iPTd
+{
+
+class CommandRunner : public Raym::Object
+{
+private:
+    enum _state
+    {
+        ST_IDLE,
+        ST_READY,
+        ST_RUN,
+        ST_DONE
+    }                       _state;
+    Raym::String *  _command;
+    Raym::Array *   _arguments;
+
+protected:
+    CommandRunner();
+    ~CommandRunner();
+
+    virtual bool readLine(Raym::String *line);
+    void write(Raym::String *line);
+
+public:
+    static CommandRunner *alloc();
+    CommandRunner *init();
+    CommandRunner *retain();
+    CommandRunner *autorelease();
+
+    void setCommandPath(Raym::String *path);
+    void setArguments(Raym::Array *args);
+
+    void run();
+    bool start();
+    void stop();
+    bool isRunning();
+
+    virtual const char *className();
+};
+
+} // iPTd
+} // ry0
index 52e902e..9515469 100644 (file)
@@ -1,20 +1,33 @@
-/**\r
- * @file Controller.cpp\r
- *\r
- */\r
+//\r
+//\r
+//\r
+\r
+//#include "stdafx.h"\r
+\r
+#define DBG_LEVEL 0\r
+#include <Raym/Log.h>\r
 \r
 #include <time.h>\r
+#include <direct.h>\r
+#include <sys/types.h>\r
+#include <sys/stat.h>\r
+#include <stdio.h>\r
+#include <fcntl.h>\r
+#include <io.h>\r
+#include <share.h>\r
+#include <winsock2.h>\r
+#include <ws2tcpip.h>\r
+#include <Iphlpapi.h>\r
 \r
-#define DBG_LEVEL 1\r
-#include "Raym/Log.h"\r
+#include "ry0/iPTd/Controller.h"\r
+#include "ry0/iPTd/HTTPLiveStreaming.h"\r
+#include "mpeg2/ts/Analyzer.h"\r
 \r
 #include "b25/arib_std_b25.h"\r
 #include "b25/b_cas_card.h"\r
-#include "mpeg2/ts/Analyzer.h"\r
-\r
-#include "ry0/iPTd/Controller.h"\r
 \r
 using namespace Raym;\r
+using namespace NET;\r
 using namespace ry0::device;\r
 \r
 namespace ry0\r
@@ -22,6 +35,8 @@ namespace ry0
 namespace iPTd\r
 {\r
 \r
+static const char *PLIST_PREFIX = "com.gmail.tim.and.pom";\r
+\r
 // プロパティデフォルト値\r
 static const char * DEF_NAME                = "iPTd_R2";\r
 static const char * DEF_HOSTNAME            = "localhost";\r
@@ -45,445 +60,1310 @@ static const TimeInterval DEF_COLLECT_EPG_RETRY    = 60.0;
 static const TimeInterval DEF_COLLECT_EPG_LIMIT_S  = 10.5;\r
 static const TimeInterval DEF_COLLECT_EPG_LIMIT_T  = 20.5;\r
 \r
-const char *Controller::_plist_prefix = "com.gmail.tim.and.pom";\r
+static const time_t OFFSET_OF_START_TIME        = -2;       // 録画開始時刻の補正(秒単位)\r
+static const time_t OFFSET_OF_END_TIME          = -3;       // 録画停止時刻の補正(秒単位)\r
+static const time_t OFFSET_OF_WAKEUP            = -240;     // 起動スケジュールの補正(秒単位)  注:休止するまでの時間(DEF_SUSPEND_TIME)よりも短くすること\r
+static const time_t OFFSET_OF_SUPPRESSION_TIME  = -600;     // 録画開始前に休止の抑制を開始する時間(秒単位)\r
 \r
-#ifndef _WIN32\r
-#pragma mark '\r
-#pragma mark ------- コンストラクタ/デストラクタ -------\r
-#endif\r
 \r
-Controller::Controller()\r
-{\r
-    DebugLog3("%s", __FUNCTION__);\r
-}\r
+#if 0\r
 \r
-Controller::~Controller()\r
+void Controller::delaySuspend()\r
 {\r
-    DebugLog3("%s", __FUNCTION__);\r
+    // \r
+    Timer *timer = Timer::scheduledTimerWithTimeInterval(1.0, this, (void *)CMD_SUSPEND, false);\r
+    if (timer == NULL)\r
+    {\r
+        DebugLog0("Can't start  timer.");\r
+    }\r
 }\r
+#endif\r
 \r
-Controller *Controller::alloc()\r
+std::string Controller::createVideoPath(int tuner)\r
 {\r
-    DebugLog3("%s", __FUNCTION__);\r
-    return new Controller();\r
+    DebugLog2("Controller::createVideoPath()");\r
+\r
+    std::string result = "";\r
+\r
+    while (true)\r
+    {\r
+        time_t now;\r
+        time(&now);\r
+        TM tm;\r
+        if (localtime_s(&tm, &now) != 0)\r
+        {\r
+            break;\r
+        }\r
+\r
+        result = _store_path->cString();\r
+        DebugLog2("result: %s\n", result.c_str());\r
+\r
+        char tmp[128];\r
+        if (sprintf_s(tmp, sizeof(tmp), "\\%04d", tm.tm_year + 1900) < 0)\r
+        {\r
+            DebugLog0("sprintf_s() error: year\n");\r
+            result = "";\r
+            break;\r
+        }\r
+        result += tmp;\r
+        DebugLog2("result: %s\n", result.c_str());\r
+\r
+        STAT stat;\r
+        if (_stat(result.c_str(), &stat) != 0)\r
+        {\r
+            if (_mkdir(result.c_str()) != 0)\r
+            {\r
+                DebugLog0("_mkdir() error: year\n");\r
+                result = "";\r
+                break;\r
+            }\r
+            _stat(result.c_str(), &stat);\r
+        }\r
+        if ((stat.st_mode & _S_IFDIR) != _S_IFDIR)\r
+        {\r
+            DebugLog0("%s is not directory.\n", result.c_str());\r
+            result = "";\r
+            break;\r
+        }\r
+\r
+        if (sprintf_s(tmp, sizeof(tmp), "\\%02d", tm.tm_mon + 1) < 0)\r
+        {\r
+            DebugLog0("sprintf_s() error: month\n");\r
+            result = "";\r
+            break;\r
+        }\r
+        result += tmp;\r
+        DebugLog2("result: %s\n", result.c_str());\r
+\r
+        if (_stat(result.c_str(), &stat) != 0)\r
+        {\r
+            if (_mkdir(result.c_str()) != 0)\r
+            {\r
+                DebugLog0("_mkdir() error: month\n");\r
+                result = "";\r
+                break;\r
+            }\r
+            _stat(result.c_str(), &stat);\r
+        }\r
+        if ((stat.st_mode & _S_IFDIR) != _S_IFDIR)\r
+        {\r
+            DebugLog0("%s is not directory.", result.c_str());\r
+            result = "";\r
+            break;\r
+        }\r
+\r
+        if (sprintf_s(tmp, sizeof(tmp),\r
+                      "\\%04d%02d%02d_%02d%02d%02d_%03d_%s.ts",\r
+                      tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,\r
+                      _tuners[tuner]->channel(), _tuners[tuner]->name()) < 0)\r
+        {\r
+            DebugLog0("sprintf_s() error: filename");\r
+            result = "";\r
+            break;\r
+        }\r
+        result += tmp;\r
+        DebugLog2("result: %s\n", result.c_str());\r
+\r
+        break;\r
+    }\r
+\r
+    return result;\r
 }\r
 \r
 #ifndef _WIN32\r
 #pragma mark '\r
-#pragma mark ------- 起動/停止関連 -------\r
+#pragma mark ------- EPG関連 -------\r
 #endif\r
 \r
-// システム状態をチェックしてサスペンド可能かを返す\r
-bool Controller::canSuspend()\r
+void Controller::removePastEPGs()\r
 {\r
-    bool result = false;\r
+    DebugLog2("Controller::removePastEPGs()");\r
+\r
+    // 現在時刻\r
+    time_t now = time(NULL);\r
+\r
+    // lock\r
     RaymLock(this);\r
-    if (_initialized)\r
+\r
+    // サービスIDでループ\r
+    Array *keys1 = _epgs->allKeys();\r
+    for (uint i = 0; i < keys1->count(); ++i)\r
     {\r
-        result = _reservation->canSuspend();\r
+        // イベント取得\r
+        Dictionary *events = _epgs->dictionaryForKey((String *)keys1->objectAtIndex(i));\r
+        if (events != NULL)\r
+        {\r
+            // イベントでループ\r
+            Array *keys2 = events->allKeys();\r
+            for (uint j = 0; j < keys2->count(); ++j)\r
+            {\r
+                // キーとEPG取得\r
+                String *key = (String *)keys2->objectAtIndex(j);\r
+                Dictionary *epg = events->dictionaryForKey(key);\r
+                if (epg != NULL)\r
+                {\r
+                    // EPG取得できた場合\r
+                    time_t start = 0;\r
+                    time_t end = 0;\r
+                    getTimeWithEPG(epg, &start, &end);\r
+\r
+                    // 終了時刻が過ぎていたら\r
+                    if (now > end)\r
+                    {\r
+                        // 削除する\r
+                        events->removeObjectForKey(key);\r
+                    }\r
+                }\r
+            }\r
+        }\r
     }\r
+\r
+    // unlock\r
     RaymUnlock(this);\r
-    return result;\r
 }\r
 \r
-bool Controller::canTerminate()\r
+void Controller::collectEPGsForTuner(int tuner, TimeInterval limit)\r
 {\r
-    bool result = false;\r
+    DebugLog2("Controller::collectEPGsForTuner(%d) start.", tuner);\r
+\r
+    // 既にロックされた状態でコールされる前提\r
+    bool locked = false;\r
     RaymLock(this);\r
-    if (_initialized)\r
+    if ((0 <= tuner) && (tuner < _tunerCount))\r
     {\r
-        result = _reservation->canTerminate();\r
+        locked = _tuners[tuner]->isLocked();\r
     }\r
     RaymUnlock(this);\r
-    return result;\r
-}\r
-\r
-/*\r
- * Application::WndProc() からコールされる\r
- */\r
-void Controller::systemWillSuspend()\r
-{\r
-    DebugLog2("Controller::systemWillSuspend() start");\r
-\r
-    if ((_timer_restart != NULL) && _timer_restart->valid())\r
+    if (!locked)\r
     {\r
-        _timer_restart->invalidate();\r
+        DebugLog2("Controller::collectEPGsForTuner(%d) end(no locked).", tuner);\r
+        return;\r
     }\r
-    _reservation->systemWillSuspend();\r
-    _streaming->systemWillSuspend();\r
 \r
-    RaymLock(this);\r
+    // \r
+    Array *collected = Array::arrayWithCapacity(0);\r
 \r
-    RELEASE(_timer_restart);\r
+    // 開始時刻取得\r
+    Date *start = Date::date();\r
 \r
-    _reservation->updateSchedule();\r
+    // 取得開始\r
+    bool done = false;\r
+    while (!done)\r
+    {\r
+        AutoreleasePool *pool = AutoreleasePool::alloc()->init();\r
 \r
-    _initialized = false;\r
+        // EPG取得\r
+        MPEG2::TS::Analyzer an;\r
+        an.setFlag(MPEG2::TS::Analyzer::FLAG_EIT);\r
+        _tuners[tuner]->setListener(&an);\r
+        Array *epgs = an.epgInfos();\r
+        _tuners[tuner]->setListener(NULL);\r
 \r
-    for (int i = 0; i < _tuner_count; ++i)\r
-    {\r
-        if (_tuners[i] != NULL)\r
+        if (epgs != NULL)\r
         {\r
-            delete _tuners[i];\r
-            _tuners[i] = NULL;\r
+            // 取得成功\r
+            collected->addObjectsFromArray(epgs);\r
         }\r
-    }\r
 \r
-    DebugLog0("system will suspend...");\r
-\r
-    RaymUnlock(this);\r
+        // リミットチェック\r
+        if (Date::date()->timeIntervalSinceDate(start) >= limit)\r
+        {\r
+            done = true;\r
+        }\r
 \r
-#ifdef RAYM_MEMORY_CHECK\r
-    DebugLog0("global_raym_count_ = %d", Raym::global_raym_count_);\r
-#endif\r
-    DebugLog2("Controller::systemWillSuspend() end");\r
-}\r
+        // キャンセル確認\r
+        RaymLock(this);\r
+        if (!done)\r
+        {\r
+            done = _cancel_epg_collect;\r
+        }\r
+        RaymUnlock(this);\r
 \r
-/*\r
- * Application::WndProc() からコールされる\r
- */\r
-void Controller::systemResumed()\r
-{\r
-    DebugLog2("Controller::systemResumed() start");\r
+        pool->release();\r
+    }\r
 \r
-    DebugLog0("system resumed.");\r
+    // lock\r
+    RaymLock(this);\r
 \r
-    if (_timer_restart == NULL)\r
+    for (uint j = 0; j < collected->count(); ++j)\r
     {\r
-        _timer_restart = Timer::alloc()->initWithTimeInterval(1.0, this, (void *)CMD_RESTART, false);\r
-        _timer_restart->fire();\r
+        Dictionary *epg1 = (Dictionary *)collected->objectAtIndex(j);\r
+\r
+        if (epg1->stringForKey(KEY_EPG_TITLE) == NULL)\r
+        {\r
+            // タイトルが無い場合は不要\r
+            continue;\r
+        }\r
+\r
+        // Service ID を Primary Key\r
+        String *key = epg1->stringForKey(KEY_EPG_SERVICE_ID);\r
+        if (key != NULL)\r
+        {\r
+            Dictionary *epgs_of_service = _epgs->dictionaryForKey(key);\r
+            if (epgs_of_service == NULL)\r
+            {\r
+                // 無い場合は作成\r
+                epgs_of_service = Dictionary::dictionaryWithCapacity(0);\r
+                _epgs->setObject(epgs_of_service, key);\r
+            }\r
+            // Event ID を Secondary Key\r
+            key = epg1->stringForKey(KEY_EPG_EVENT_ID);\r
+            if (key != NULL)\r
+            {\r
+                epgs_of_service->setObject(epg1, key);\r
+            }\r
+        }\r
     }\r
 \r
-    DebugLog2("Controller::systemResumed()");\r
+    // ファイルへ書き出し\r
+    _epgs->writeToFile(_epgs_path, true);\r
+\r
+    // unlock\r
+    RaymUnlock(this);\r
+\r
+    DebugLog2("Controller::collectEPGsForTuner(%d) end.", tuner);\r
 }\r
 \r
-/*\r
- * Application::WndProc() からコールされる\r
- */\r
-void Controller::detectIdle()\r
+bool Controller::collectEPGs(Tuner::Type type)\r
 {\r
-    DebugLog2("Controller::detectIdle()");\r
-\r
-    // ARP生成\r
-    AutoreleasePool *pool = AutoreleasePool::alloc()->init();\r
+    DebugLog0("Controller::collectEPGs(%s) start.", type == Tuner::ISDB_S ? "ISDB-S" : "ISDB-T");\r
 \r
+    // 使用するチューナを決定\r
+    int tuner = 0;\r
+    bool locked = false;\r
     RaymLock(this);\r
-\r
-    // サスペンド可能か確認\r
-    if (canSuspend())\r
+    for (int i = _tunerCount - 1; i >= 0; --i)\r
     {\r
-        if (_idle_count == 0)\r
+        if (isTunerEnabled(i))\r
         {\r
-            DebugLog0("detect idle...");\r
+            if ((!_tuners[i]->isLocked()) && (_tuners[i]->type() == type))\r
+            {\r
+                if (_tuners[i]->lock())\r
+                {\r
+                    tuner = i;\r
+                    locked = true;\r
+                    break;\r
+                }\r
+            }\r
         }\r
+    }\r
+    RaymUnlock(this);\r
 \r
-        // アイドルカウンタを更新\r
-        ++_idle_count;\r
+    // ロック確認\r
+    if (!locked)\r
+    {\r
+        DebugLog0("Controller::collectEPGs(%s) end(Can't locled).", type == Tuner::ISDB_S ? "ISDB-S" : "ISDB-T");\r
+        // ロックできない場合は収集しない\r
+        return false;\r
+    }\r
 \r
-        // 起動中アプリと休止状態抑止アプリのチェック\r
-        bool found = false;\r
+    bool canceled = false;\r
+    DebugLog0("start collect EPG of \"%s(#%d)\".", _tuners[tuner]->name(), tuner);\r
 \r
-        Array *dont_in_suspend = _props->arrayForKey(KEY_DO_NOT_IN_SUSPEND);\r
-        Array *running_apps = Workspace::sharedWorkspace()->runningApplications();\r
-        for (uint i = 0; (i < running_apps->count()) && !found; ++i)\r
-        {\r
-            RunningApplication *ra = (RunningApplication *)running_apps->objectAtIndex(i);\r
+    // 現在のチャンネルを保存\r
+    int channel = _tuners[tuner]->channel();\r
 \r
-            // 実行中でなければ次へ\r
-            if (!ra->isRunning())\r
+    // チャンネルを変更しつつEPGを取得\r
+    int max_channel = (type == Tuner::ISDB_S ? Tuner::MAX_CHANNELS_ISDB_S : Tuner::MAX_CHANNELS_ISDB_T);\r
+    for (int ch = 0; ch <= max_channel; ++ch)\r
+    {\r
+        // チャンネルが有効かチェック\r
+        if (isChannelEnabled(tuner, ch))\r
+        {\r
+            // チャンネル設定\r
+            if (_tuners[tuner]->setChannel(ch))\r
             {\r
-                continue;\r
-            }\r
+                // EPG取集\r
+                collectEPGsForTuner(tuner, ((type == Tuner::ISDB_S) ? DEF_COLLECT_EPG_LIMIT_S : DEF_COLLECT_EPG_LIMIT_T));\r
 \r
-            // 実行ファイルのチェック\r
-            String *path = ra->executePath();\r
-            if ((path == NULL) || (path->length() == 0))\r
-            {\r
-                continue;\r
-            }\r
-            DebugLog3("exec path: %s", path->cString());\r
+                // キャンセル確認\r
+                RaymLock(this);\r
+                if (_cancel_epg_collect)\r
+                {\r
+                    ch = Tuner::MAX_CHANNELS_ISDB_T + 1;\r
+                    canceled = true;\r
+                }\r
+                RaymUnlock(this);\r
 \r
-            // 休止状態抑止アプリリストのチェック\r
-            for (uint j = 0; (j < dont_in_suspend->count()) && !found; ++j)\r
-            {\r
-                found = path->isMatch((String *)dont_in_suspend->objectAtIndex(j));\r
+                // キャンセル確認\r
+                //   終了不可 -> 録画待機中/録画中  なので、収集は諦める\r
+                if (!canTerminate())\r
+                {\r
+                    ch = Tuner::MAX_CHANNELS_ISDB_T + 1;\r
+                    canceled = true;\r
+                }\r
             }\r
         }\r
+    }\r
 \r
-        // 抑止有効なら KEY_FORCED_SUSPEND_TIME、無効なら KEY_SUSPEND_TIME で取得\r
-        int limit = _props->integerForKey(found ? KEY_FORCED_SUSPEND_TIME : KEY_SUSPEND_TIME);\r
-        DebugLog3("found: %d, _idle_count: %d, limit: %d", found, _idle_count, limit);\r
-        if (_idle_count >= limit)\r
-        {\r
-            RaymUnlock(this);\r
-\r
-            // サスペンド前にARPを解放しておく\r
-            pool->release();\r
-\r
-            // 下記の TrayApp::suspend() を使用する場合、自アプリへsuspendメッセージが\r
-            // ブロードキャストされないため、自分でコールしておく\r
-            systemWillSuspend();\r
+    // 元のチャンネルに戻す\r
+    _tuners[tuner]->setChannel(channel);\r
 \r
-            // サスペンド\r
-            suspend();\r
+    // lock\r
+    RaymLock(this);\r
 \r
-            // 再度 ARPを用意\r
-            pool = AutoreleasePool::alloc()->init();\r
+    // チューナをアンロック\r
+    _tuners[tuner]->unlock();\r
 \r
-            RaymLock(this);\r
+    // unlock\r
+    RaymUnlock(this);\r
 \r
-            // アイドルカウンタをクリア\r
-            _idle_count = 0;\r
-        }\r
+    if (canceled)\r
+    {\r
+        DebugLog0("collect EPG of \"%s\" was canceled.", _tuners[tuner]->name());\r
     }\r
     else\r
     {\r
-        // アイドルカウンタをクリア\r
-        _idle_count = 0;\r
+        DebugLog0("collect EPG of \"%s\" was finished.", _tuners[tuner]->name());\r
+\r
+        // 過去のEPGを削除\r
+        removePastEPGs();\r
     }\r
 \r
-    RaymUnlock(this);\r
+    DebugLog2("Controller::collectEPGs(%s) end.", type == Tuner::ISDB_S ? "ISDB-S" : "ISDB-T");\r
 \r
-    // ARP解放\r
-    pool->release();\r
+    return true;\r
 }\r
 \r
-void Controller::detectNonIdle()\r
+#ifndef _WIN32\r
+#pragma mark '\r
+#pragma mark ------- 予約録画関連 -------\r
+#endif\r
+\r
+//\r
+// 録画予約:サービスID/イベントID指定\r
+//   EPGデータから指定のサービスID/イベントIDのEPGを取り出し、EPG指定の録画予約をコール\r
+//\r
+bool Controller::reserve(int service_id, int event_id)\r
 {\r
-    DebugLog2("Controller::detectNonIdle()");\r
+    DebugLog2("Controller::reserve(service_id, event_id)");\r
+\r
+    bool result = false;\r
 \r
+    // lock\r
     RaymLock(this);\r
 \r
-    if (_idle_count > 0)\r
+    if (_epgs != NULL)\r
     {\r
-        DebugLog0("detect non idle...");\r
+        Dictionary *events = _epgs->dictionaryForKey(String::stringWithFormat("%d", service_id));\r
+        if (events != NULL)\r
+        {\r
+            Dictionary *epg = events->dictionaryForKey(String::stringWithFormat("%d", event_id));\r
+            if (epg != NULL)\r
+            {\r
+                result = reserve(epg);\r
+            }\r
+        }\r
     }\r
-    _idle_count = 0;\r
 \r
+    // unlock\r
     RaymUnlock(this);\r
-}\r
 \r
-#ifndef _WIN32\r
-#pragma mark '\r
-#pragma mark ------- プロパティ取得 -------\r
-#endif\r
+    return result;\r
+}\r
 \r
-/*\r
- *\r
- */\r
-bool Controller::isTunerInitialized(int tuner)\r
+//\r
+// 録画予約:EPG指定\r
+//   EPGからサービスIDを取り出し、チューナ情報を検索して一致するサービスIDがあったらチューナ/EPG指定の録画予約を試行\r
+//\r
+bool Controller::reserve(Dictionary *in_epg)\r
 {\r
-    DebugLog2("Controller::isTunerInitialized()");\r
+    DebugLog2("Controller::reserve(epg)");\r
 \r
     bool result = false;\r
 \r
+    // lock\r
     RaymLock(this);\r
 \r
-    if ((0 <= tuner) && (tuner < _tuner_count))\r
+    while (in_epg != NULL)\r
     {\r
-        Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
-        if (tunersInfo != NULL)\r
+        // EPGを複製\r
+        Dictionary *epg = Dictionary::dictionaryWithDictionary(in_epg);\r
+        if (epg == NULL)\r
         {\r
-            Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
-            if (tunerInfo != NULL)\r
+            DebugLog3("Dictionary::dictionaryWithDictionary() ng.");\r
+            break;\r
+        }\r
+\r
+        // サービスID取得\r
+        String *service_id = epg->stringForKey(KEY_EPG_SERVICE_ID);\r
+        if (service_id == NULL)\r
+        {\r
+            DebugLog3("epg->stringForKey(KEY_EPG_SERVICE_ID) ng.");\r
+            break;\r
+        }\r
+        DebugLog3("service_id: %s\n", service_id->cString());\r
+\r
+        // 全チューナ情報取得\r
+        Dictionary *tunerInfos = _props->dictionaryForKey(KEY_TUNERS);\r
+        if (tunerInfos == NULL)\r
+        {\r
+            DebugLog3("_props->dictionaryForKey(KEY_TUNERS) ng.");\r
+            break;\r
+        }\r
+\r
+        // チューナを検索\r
+        for (int i = 0; (!result) && (i < _tunerCount); ++i)\r
+        {\r
+            // チューナ情報取得\r
+            Dictionary *tunerInfo = tunerInfos->dictionaryForKey(_tuners[i]->name());\r
+            if (tunerInfo == NULL)\r
             {\r
-                result = tunerInfo->boolForKey(KEY_INITIALIZED);\r
+                DebugLog3("tunerInfos->dictionaryForKey(_tuners[%d]->name()) ng.", i);\r
+                continue;\r
+            }\r
+\r
+            // 全チャンネル情報取得\r
+            Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
+            if (channels == NULL)\r
+            {\r
+                DebugLog3("tunerInfo->dictionaryForKey(KEY_CHANNELS) ng.");\r
+                continue;\r
+            }\r
+\r
+            // キー取得\r
+            Array *chkeys = channels->allKeys();\r
+            for (uint ch = 0; ch < chkeys->count(); ++ch)\r
+            {\r
+                // チャンネル情報取得\r
+                Dictionary *channel = channels->dictionaryForKey((String *)chkeys->objectAtIndex(ch));\r
+                if (channel == NULL)\r
+                {\r
+                    DebugLog3("channels->dictionaryForKey() ng.");\r
+                    continue;\r
+                }\r
+\r
+                // 全サービス情報取得\r
+                Array *services = (Array *)channel->objectForKey(KEY_SERVICES);\r
+                if (services == NULL)\r
+                {\r
+                    DebugLog3("channel->objectForKey() ng.");\r
+                    continue;\r
+                }\r
+\r
+                for (uint s = 0; s < services->count(); ++s)\r
+                {\r
+                    // サービス情報取得\r
+                    Dictionary *service = (Dictionary *)services->objectAtIndex(s);\r
+                    if (service == NULL)\r
+                    {\r
+                        DebugLog3("service->objectAtIndex() ng.");\r
+                        continue;\r
+                    }\r
+\r
+                    // サービスIDを比較\r
+                    String *sid = service->stringForKey(KEY_SERVICE_ID);\r
+                    if ((sid != NULL) && sid->isEqualToString(service_id))\r
+                    {\r
+                        // チャンネルを設定\r
+                        epg->setString((String *)chkeys->objectAtIndex(ch), KEY_EPG_CHANNEL);\r
+\r
+                        // 録画予約\r
+                        result = reserve(i, epg);\r
+\r
+                        // チャンネルループのカウンタを更新(=ループを終了させる)\r
+                        ch = chkeys->count();\r
+                        break;\r
+                    }\r
+                }\r
             }\r
         }\r
+\r
+        break;\r
     }\r
 \r
+    // unlock\r
     RaymUnlock(this);\r
 \r
     return result;\r
 }\r
 \r
-/*\r
- *\r
- */\r
-bool Controller::isTunerEnabled(int tuner)\r
+//\r
+// 録画予約:チューナ/EPG指定\r
+//\r
+bool Controller::reserve(int tuner, Dictionary *in_epg)\r
 {\r
-    DebugLog2("Controller::isTunerEnabled()");\r
+    DebugLog2("Controller::reserve(tuner, epg)");\r
 \r
     bool result = false;\r
 \r
+    // lock\r
     RaymLock(this);\r
 \r
-    if ((0 <= tuner) && (tuner < _tuner_count))\r
+    while ((0 <= tuner) && (tuner < _tunerCount) && (in_epg != NULL))\r
     {\r
-        Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
-        if (tunersInfo != NULL)\r
+        Dictionary *epg = Dictionary::dictionaryWithDictionary(in_epg);\r
+        if (epg == NULL)\r
         {\r
-            Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
-            if (tunerInfo != NULL)\r
+            DebugLog3("Dictionary::dictionaryWithDictionary() ng.");\r
+            break;\r
+        }\r
+\r
+        Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
+        if (array == NULL)\r
+        {\r
+            array = Array::arrayWithCapacity(0);\r
+            _reservations->setObject(array, _tuners[tuner]->name());\r
+        }\r
+\r
+        time_t epg_start;\r
+        time_t epg_end;\r
+        getTimeWithEPG(epg, &epg_start, &epg_end);\r
+        DebugLog2("epg start: %ld, end: %ld\n", epg_start, epg_end);\r
+\r
+        time_t pre_start = 0;\r
+        time_t pre_end = 0;\r
+        pre_end = time(NULL);\r
+        DebugLog2("pre_end: %ld", pre_end);\r
+\r
+        for (uint i = 0; i < array->count(); ++i)\r
+        {\r
+            Dictionary *cur = (Dictionary *)array->objectAtIndex(i);\r
+            time_t cur_start;\r
+            time_t cur_end;\r
+            getTimeWithEPG(cur, &cur_start, &cur_end);\r
+            DebugLog2("cur start: %ld, end: %ld\n", cur_start, cur_end);\r
+            if ((pre_end <= epg_start) && (epg_end <= cur_start))\r
             {\r
-                result = tunerInfo->boolForKey(KEY_ENABLED);\r
+                DebugLog2("insert: %d\n", i);\r
+                array->insertObject(epg, i);\r
+                result = true;\r
+                break;\r
+            }\r
+            pre_start = cur_start;\r
+            pre_end = cur_end;\r
+        }\r
+\r
+        if (!result)\r
+        {\r
+            if (pre_end <= epg_start)\r
+            {\r
+                DebugLog2("add\n");\r
+                array->addObject(epg);\r
+                result = true;\r
             }\r
+            else\r
+            {\r
+                DebugLog2("no add\n");\r
+            }\r
+        }\r
+        if (result)\r
+        {\r
+            epg->setInteger(_reservation_seq_id, KEY_EPG_RESV_ID);\r
+            _reservation_seq_id = (_reservation_seq_id + 1) % 1000000;\r
+            _reservations->setInteger(_reservation_seq_id, KEY_EPG_LAST_RESV_ID);\r
+\r
+            //\r
+            _reservations->writeToFile(_reservations_path, true);\r
         }\r
+\r
+        break;\r
     }\r
 \r
+    // unlock\r
     RaymUnlock(this);\r
 \r
     return result;\r
 }\r
 \r
-/*\r
- *\r
- */\r
-bool Controller::isChannelEnabled(int tuner, int channel)\r
+//\r
+// tuner: 0 - (_tunerCount - 1)  cancel current\r
+// tuner: -1  cancel reserve_id\r
+//\r
+bool Controller::cancel(int tuner, int reserve_id)\r
 {\r
-    DebugLog2("Controller::isChannelEnabled()");\r
-\r
     bool result = false;\r
 \r
+    // lock\r
     RaymLock(this);\r
 \r
-    if ((0 <= tuner) && (tuner < _tuner_count) && (0 <= channel) && (channel <= Tuner::MAX_CHANNELS_ISDB_T))\r
+    //\r
+    if ((0 <= tuner) && (tuner < _tunerCount))\r
     {\r
-        Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
-        if (tunersInfo != NULL)\r
+        Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
+        if (array != NULL)\r
         {\r
-            Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
-            if (tunerInfo != NULL)\r
+            if (array->count() > 0)\r
             {\r
-                Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
-                if (channels != NULL)\r
+                Dictionary *epg = (Dictionary *)array->objectAtIndex(0);\r
+                String *status = epg->stringForKey(KEY_EPG_STATUS);\r
+                if (status != NULL)\r
                 {\r
-                    char key[4];\r
-                    sprintf_s(key, "%03d", channel);\r
-                    Dictionary *chInfo = channels->dictionaryForKey(key);\r
-                    if (chInfo != NULL)\r
+                    if (status->isEqualToString("running"))\r
                     {\r
-                        result = chInfo->boolForKey(KEY_ENABLED);\r
+                        epg->setString("stop", KEY_EPG_STATUS);\r
+                        result = true;\r
                     }\r
                 }\r
             }\r
         }\r
     }\r
 \r
-    RaymUnlock(this);\r
-\r
-    return result;\r
-}\r
-\r
-#ifndef _WIN32\r
-#pragma mark '\r
-#pragma mark ------- チューナ制御 -------\r
-#endif\r
-\r
-void Controller::scanChannel(int tuner)\r
-{\r
-    DebugLog2("Controller::scanChannel(%d)", tuner);\r
-\r
-    RaymLock(this);\r
-    if ((tuner < 0) || (_tuner_count <= tuner))\r
-    {\r
-        RaymUnlock(this);\r
-        DebugLog3("Invalid tuner: %d", tuner);\r
-        return;\r
-    }\r
-\r
-    DebugLog0("");\r
-    DebugLog0("start channel scan of \"%s\".", _tuners[tuner]->name());\r
-\r
-    // 設定ファイルから全チューナ情報取得\r
-    Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
-    if (tunersInfo == NULL)\r
+    //\r
+    else if ((tuner < 0) && (0 <= reserve_id) && (reserve_id < 1000000))\r
     {\r
-        // 無ければ作成\r
-        tunersInfo = Dictionary::dictionaryWithCapacity(0);\r
-        _props->setObject(tunersInfo, KEY_TUNERS);\r
+        for (int i = 0; i < _tunerCount; ++i)\r
+        {\r
+            Array *array = _reservations->arrayForKey(_tuners[i]->name());\r
+            if (array != NULL)\r
+            {\r
+                for (uint j = 0; j < array->count(); ++j)\r
+                {\r
+                    Dictionary *epg = (Dictionary *)array->objectAtIndex(j);\r
+                    if (reserve_id == epg->integerForKey(KEY_EPG_RESV_ID))\r
+                    {\r
+                        String *status = epg->stringForKey(KEY_EPG_STATUS);\r
+                        if ((status != NULL) && status->isEqualToString("running"))\r
+                        {\r
+                            epg->setString("stop", KEY_EPG_STATUS);\r
+                        }\r
+                        else\r
+                        {\r
+                            array->removeObjectAtIndex(j);\r
+                        }\r
+                        result = true;\r
+                        break;\r
+                    }\r
+                }\r
+            }\r
+            if (result)\r
+            {\r
+                break;\r
+            }\r
+        }\r
     }\r
 \r
-    // 全チューナ情報から指定のチューナ情報を取得\r
-    Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
-    if (tunerInfo == NULL)\r
+    if (result)\r
     {\r
-        // 無ければ作成\r
-        tunerInfo = Dictionary::dictionaryWithCapacity(0);\r
-        tunersInfo->setObject(tunerInfo, _tuners[tuner]->name());\r
+        _reservations->writeToFile(_reservations_path, true);\r
     }\r
 \r
-    // チューナを未初期化に設定\r
-    tunerInfo->setBool(false, KEY_INITIALIZED);\r
-\r
     // unlock\r
     RaymUnlock(this);\r
 \r
-    // 全チャンネル情報を作成\r
-    Dictionary *channels = Dictionary::dictionaryWithCapacity(0);\r
-    tunerInfo->setObject(channels, KEY_CHANNELS);\r
+    return result;\r
+}\r
 \r
-    // チューナタイプにより最大チャンネル数を設定\r
-    int max_channel;\r
-    Tuner::Type type = _tuners[tuner]->type();\r
-    if (type == Tuner::ISDB_S)\r
-    {\r
-        max_channel = Tuner::MAX_CHANNELS_ISDB_S;\r
-    }\r
-    else\r
-    {\r
-        max_channel = Tuner::MAX_CHANNELS_ISDB_T;\r
-    }\r
+//\r
+// 録画制御\r
+//\r
+void Controller::periodic(void)\r
+{\r
+    bool need_update = false;\r
 \r
-    // 最終設定成功チャンネル保持用変数\r
-    int lastChannel = -1;\r
+#ifdef OBJC_MEMORY_CHECK\r
+    DebugLog0("global_objc_count_ = %d", Raym::global_objc_count_);\r
+#endif\r
 \r
-    // チャンネルサーチ\r
-    for (int ch = 0; ch <= max_channel; ++ch)\r
-    {\r
-        // チャンネル情報\r
-        Dictionary *channelInfo = Dictionary::dictionaryWithCapacity(0);\r
+    // lock\r
+    RaymLock(this);\r
 \r
-        // チャンネルキー(10進数:PTに対するチャンネル番号)\r
-        char chkey[4];\r
-        sprintf_s(chkey, sizeof(chkey), "%03d", ch);\r
-        channels->setObject(channelInfo, chkey);\r
+    // 現在時刻取得\r
+    time_t now = time(NULL);\r
 \r
-        // チャンネルID(論理チャンネル番号)\r
-        char channelID[8];\r
-        if (type == Tuner::ISDB_S)\r
+    DebugLog2("periodic: %d", now);\r
+\r
+    //\r
+    for (int tuner = 0; tuner < _tunerCount; ++tuner)\r
+    {\r
+        Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
+        if ((array == NULL) || (array->count() == 0))\r
         {\r
-            if (ch < 12)\r
+            // next tuner\r
+            continue;\r
+        }\r
+\r
+        //\r
+        Dictionary *epg = (Dictionary *)array->objectAtIndex(0);\r
+        time_t start;\r
+        time_t end;\r
+        getTimeWithEPG(epg, &start, &end);\r
+        \r
+        //\r
+        // 録画停止要否チェック\r
+        //\r
+        bool stop_need = false;\r
+        while (true)\r
+        {\r
+            String *status = epg->stringForKey(KEY_EPG_STATUS);\r
+            if (status != NULL)\r
             {\r
-                sprintf_s(channelID, sizeof(channelID), "BS%02d", 1 + 2 * ch);\r
+                if (status->isEqualToString("stop"))\r
+                {\r
+                    stop_need = true;\r
+                    break;\r
+                }\r
+                if (!status->isEqualToString("running"))\r
+                {\r
+                    break;\r
+                }\r
             }\r
-            else if (ch < 24)\r
+            if (end + OFFSET_OF_END_TIME <= now)\r
             {\r
-                sprintf_s(channelID, sizeof(channelID), "ND%02d", 2 + 2 * (ch - 12));\r
+                stop_need = true;\r
+            }\r
+            break;\r
+        }\r
+        if (stop_need)\r
+        {\r
+            DebugLog2("I try stop\n");\r
+            int fd =_tuners[tuner]->stopRecording();\r
+            if (fd < 0)\r
+            {\r
+                DebugLog1("stopRecording() error.\n");\r
             }\r
             else\r
             {\r
-                sprintf_s(channelID, sizeof(channelID), "ND%02d", 1 + 2 * (ch -24));\r
+                DebugLog2("stopRecording() ok\n");\r
+                DebugLog0("stop recording of \"%s\"", _tuners[tuner]->name());\r
+                _close(fd);\r
             }\r
+            array->removeObject(epg);\r
+            \r
+            if (array->count() > 0)\r
+            {\r
+                epg = (Dictionary *)array->objectAtIndex(0);\r
+            }\r
+            else\r
+            {\r
+                epg = NULL;\r
+            }\r
+            need_update = true;\r
         }\r
-        else\r
+\r
+        if (epg == NULL)\r
         {\r
-            static int TABLE[][3] =\r
-            {\r
-                {   2,  0,   3 },\r
-                {  12,  1,  22 },\r
-                {  21,  0,  12 },\r
-                {  62,  1,  63 },\r
-                { 112,  0,  62 }\r
-            };\r
+            // next tuner\r
+            continue;\r
+        }\r
 \r
-            uint i;\r
-            for (i = 0; i < sizeof(TABLE)/sizeof(*TABLE); ++i)\r
+        //\r
+        // 録画開始要否チェック\r
+        //\r
+        bool start_need = false;\r
+        start = end = 0;\r
+        getTimeWithEPG(epg, &start, &end);\r
+        if ((start != 0) && (end != 0))\r
+        {\r
+            String *status = epg->stringForKey(KEY_EPG_STATUS);\r
+            if ((status == NULL) || !(status->isEqualToString("running")))\r
             {\r
-                if (ch <= TABLE[i][0])\r
+                if (end + OFFSET_OF_END_TIME <= now)\r
                 {\r
-                    sprintf_s(channelID, sizeof(channelID), "%s%d", TABLE[i][1] ? "C" : "", ch + TABLE[i][2] - TABLE[i][0]);\r
-                    break;\r
+                    // 既に終了時間が経過しているので削除する\r
+                    array->removeObject(epg);\r
+                }\r
+                else if (start + OFFSET_OF_START_TIME <= now)\r
+                {\r
+                    start_need = true;\r
                 }\r
             }\r
         }\r
 \r
-        // チャンネルIDを設定\r
-        channelInfo->setString(channelID, KEY_CHANNEL_ID);\r
-\r
-        RaymLock(this);\r
-\r
-        // チャンネル設定\r
+        if (start_need)\r
+        {\r
+            DebugLog2("I need start.\n");\r
+            String *ch = epg->stringForKey(KEY_EPG_CHANNEL);\r
+            if (ch != NULL)\r
+            {\r
+                int channel = atoi(ch->cString());\r
+                DebugLog2("channel: %d\n", channel);\r
+                std::string videopath = createVideoPath(tuner);\r
+                if (videopath != "")\r
+                {\r
+                    DebugLog2("videopath: %s\n", videopath.c_str());\r
+                    int fd = -1;\r
+                    if (_sopen_s(&fd, videopath.c_str(),\r
+                                 (_O_CREAT | _O_EXCL | _O_WRONLY | _O_BINARY | _O_TRUNC), _SH_DENYRW, (_S_IREAD | _S_IWRITE)) == 0)\r
+                    {\r
+                        DebugLog2("open ok.\n");\r
+                        bool startResult = true;\r
+                        if (_tuners[tuner]->channel() != channel)\r
+                        {\r
+                            if (!setChannel(tuner, channel))\r
+                            {\r
+                                DebugLog3("setChannel() ng.");\r
+                                startResult = false;\r
+                            }\r
+                        }\r
+\r
+                        if (startResult)\r
+                        {\r
+                            if (_tuners[tuner]->startRecording(fd))\r
+                            {\r
+                                DebugLog2("startRecording() ok.");\r
+                                DebugLog0("start recording of \"%s\" to %s.", _tuners[tuner]->name(), videopath.c_str());\r
+                            }\r
+                            else\r
+                            {\r
+                                DebugLog3("Tuner::startRecording() failed.");\r
+                                startResult = false;\r
+                            }\r
+                        }\r
+\r
+                        if (startResult)\r
+                        {\r
+                            epg->setString("running", KEY_EPG_STATUS);\r
+                        }\r
+                        else\r
+                        {\r
+                            _close(fd);\r
+                        }\r
+                    }\r
+                    else\r
+                    {\r
+                        DebugLog0("open ng. 0x%08x\n", errno);\r
+                    }\r
+                }\r
+                else\r
+                {\r
+                    DebugLog0("Can't create videopath.\n");\r
+                }\r
+            }\r
+            else\r
+            {\r
+                DebugLog0("error.\n");\r
+            }\r
+        }\r
+    }\r
+\r
+    if (need_update)\r
+    {\r
+        //\r
+        _reservations->writeToFile(_reservations_path, true);\r
+    }\r
+\r
+#if 0\r
+    // EPG収集時刻を取得\r
+    String *collect_str = _props->stringForKey(KEY_COLLECT_EPG_TIME);\r
+    if (collect_str != NULL)\r
+    {\r
+        // 秒に変換\r
+        time_t collect_time = 0;\r
+        getTimeWithString(collect_str, &collect_time);\r
+\r
+        // 現在時刻と比較\r
+        if ((collect_time <= now) && (now < collect_time + 1))\r
+        {\r
+            // タイマが起動中か確認\r
+            if ((_timer_epg_s == NULL) || !_timer_epg_s->valid())\r
+            {\r
+                // EPG収集用タイマ起動(ISDB-S)\r
+                RELEASE(_timer_epg_s);\r
+                _timer_epg_s = Timer::alloc()->initWithTimeInterval(DEF_COLLECT_EPG_DELAY, this, (void *)CMD_COLLECT_EPG_ISDB_S, true);\r
+                if (_timer_epg_s != NULL)\r
+                {\r
+                    _timer_epg_s->fire();\r
+                }\r
+            }\r
+\r
+            // タイマが起動中か確認\r
+            if ((_timer_epg_t == NULL) || !_timer_epg_t->valid())\r
+            {\r
+                // EPG収集用タイマ起動(ISDB-T)\r
+                RELEASE(_timer_epg_t);\r
+                _timer_epg_t = Timer::alloc()->initWithTimeInterval(DEF_COLLECT_EPG_DELAY, this, (void *)CMD_COLLECT_EPG_ISDB_T, true);\r
+                if (_timer_epg_t != NULL)\r
+                {\r
+                    _timer_epg_t->fire();\r
+                }\r
+            }\r
+        }\r
+    }\r
+#endif\r
+\r
+    // unlock\r
+    RaymUnlock(this);\r
+\r
+    //\r
+    // 1/100秒単位が 0 に近くなるように次回T.O.を微調整\r
+    // ただし、windowsは精度が低いので期待しないことw\r
+    //\r
+\r
+#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)\r
+    static const __time64_t DELTA_EPOCH_IN_MICROSECS = 11644473600000000Ui64;\r
+#else\r
+    static const __time64_t DELTA_EPOCH_IN_MICROSECS = 11644473600000000ULL;\r
+#endif\r
+    // 現在時刻を取得\r
+    FILETIME ft;\r
+    GetSystemTimeAsFileTime(&ft);\r
+\r
+    // EPOCH秒への変換\r
+    __time64_t now_sec;\r
+    __time64_t now_usec;\r
+    now_sec = ft.dwHighDateTime;\r
+    now_sec <<= 32;\r
+    now_sec |= ft.dwLowDateTime;\r
+    now_sec /= 10;  /*convert into microseconds*/\r
+    now_sec -= DELTA_EPOCH_IN_MICROSECS;\r
+    now_usec = (now_sec % 1000000UL);\r
+    now_sec = now_sec / 1000000UL;\r
+\r
+    TimeInterval interval = (TimeInterval)now_usec;\r
+    interval = interval / 1000000;\r
+    _timer_periodic->setTimeInterval(1.005 - interval);\r
+\r
+#ifdef OBJC_MEMORY_CHECK\r
+    DebugLog0("global_objc_count_ = %d", Raym::global_objc_count_);\r
+#endif\r
+}\r
+\r
+void Controller::updateKeywordsReservation()\r
+{\r
+    DebugLog2("Controller::updateKeywordsReservation()");\r
+\r
+    // lock\r
+    RaymLock(this);\r
+\r
+    // 予約情報からキーワード予約フラグが設定されているものを削除\r
+    for (int tuner = 0; tuner < _tunerCount; ++tuner)\r
+    {\r
+        Array *epgs = _reservations->arrayForKey(_tuners[tuner]->name());\r
+        if (epgs != NULL)\r
+        {\r
+            for (uint epgs_idx_offset = epgs->count(); epgs_idx_offset > 0; --epgs_idx_offset)\r
+            {\r
+                Dictionary *epg = (Dictionary *)epgs->objectAtIndex(epgs_idx_offset - 1);\r
+                if (epg->boolForKey(KEY_EPG_RESERVED_BY_KEYWORDS))\r
+                {\r
+                    epgs->removeObject(epg);\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+    // キーワードで検索\r
+    Dictionary *keywords_info = _reservations->dictionaryForKey(KEY_EPG_KEYWORDS);\r
+    if (keywords_info != NULL)\r
+    {\r
+        // キーワード有り\r
+\r
+        // _epgs からキー(サービスID)を取得\r
+        Array *service_id_keys = _epgs->allKeys();\r
+\r
+        // サービスIDでループ\r
+        for (uint service_id_keys_idx = 0; service_id_keys_idx < service_id_keys->count(); ++service_id_keys_idx)\r
+        {\r
+            // サービスID\r
+            String *service_id = (String *)service_id_keys->objectAtIndex(service_id_keys_idx);\r
+\r
+            // 局名\r
+            String *stationName = stationNameForServiceID(service_id);\r
+            if (stationName == NULL)\r
+            {\r
+                // 局名が取得できなかったら次のサービスIDへ\r
+                continue;\r
+            }\r
+\r
+            // 指定サービスIDのEPGを取得\r
+            Dictionary *events = _epgs->dictionaryForKey((String *)service_id_keys->objectAtIndex(service_id_keys_idx));\r
+\r
+            // キー(イベントID)を取得\r
+            Array *event_id_keys = events->allKeys();\r
+\r
+            // イベントIDでループ\r
+            for (uint event_id_keys_idx = 0; event_id_keys_idx < event_id_keys->count(); ++event_id_keys_idx)\r
+            {\r
+                // 指定イベントIDのEPGを取得\r
+                Dictionary *epg = events->dictionaryForKey((String *)event_id_keys->objectAtIndex(event_id_keys_idx));\r
+\r
+                // 取得したEPGがキーワードの条件にマッチするか\r
+\r
+                time_t start;\r
+                getTimeWithString(epg->stringForKey(KEY_EPG_START), &start);\r
+\r
+                // キーワードの配列を取得\r
+                Array *keywords = keywords_info->allKeys();\r
+                for (uint keywords_idx = 0; keywords_idx < keywords->count(); ++keywords_idx)\r
+                {\r
+                    // キーワード\r
+                    String *kwd = (String *)keywords->objectAtIndex(keywords_idx);\r
+\r
+                    // キーワード(分割)\r
+                    Array *kwds = kwd->componentsSeparatedByString(",");\r
+                    if (kwds == NULL)\r
+                    {\r
+                        continue;\r
+                    }\r
+\r
+                    // キーワード情報\r
+                    Dictionary *kwdinf = keywords_info->dictionaryForKey(kwd);\r
+\r
+                    // 局名フィルタ\r
+                    String *kwdinf_service_id = kwdinf->stringForKey(KEY_EPG_SERVICE_ID);\r
+                    if (kwdinf_service_id != NULL)\r
+                    {\r
+                        // 局名フィルタ有り\r
+                        String *sname = stationNameForServiceID(kwdinf_service_id);\r
+                        if (sname != NULL)\r
+                        {\r
+                            if (!stationName->isEqualToString(sname))\r
+                            {\r
+                                // 局名が異なる場合は次のキーワードへ\r
+                                continue;\r
+                            }\r
+                        }\r
+                    }\r
+\r
+                    // 開始時刻フィルタ\r
+                    String *kwdinf_start = kwdinf->stringForKey(KEY_EPG_START);\r
+                    if (kwdinf_start != NULL)\r
+                    {\r
+                        // 開始時刻フィルタ有り\r
+                        String *st = kwdinf_start->stringByAppendingString(":00");\r
+                        time_t kwd_st, kwd_ed;\r
+                        getTimeWithString(st, &kwd_st);\r
+                        kwd_ed = kwd_st + 3600;\r
+\r
+                        // 開始時刻フィルタの時刻 <= EPGの開始時刻 <= 開始時刻フィルタの時刻+60min ならOK\r
+                        if ((kwd_st > start) || (start > kwd_ed))\r
+                        {\r
+                            // 範囲外なので次のキーワードへ\r
+                            continue;\r
+                        }\r
+                    }\r
+\r
+                    // タイトル\r
+                    String *title = epg->stringForKey(KEY_EPG_TITLE);\r
+                    bool title_flag = (title != NULL);\r
+\r
+                    // 概要\r
+                    String *desc = epg->stringForKey(KEY_EPG_DESCRIPTION);\r
+                    bool desc_flag = (desc != NULL);\r
+\r
+                    // キーワード(分割)がタイトル/概要のどちらかに、全て(AND)含まれていたら予約対象とする\r
+                    for (uint kwds_idx = 0; kwds_idx < kwds->count(); ++kwds_idx)\r
+                    {\r
+                        String *kw = ((String *)kwds->objectAtIndex(kwds_idx))->stringByTrimming();\r
+\r
+                        // タイトル\r
+                        if (title != NULL)\r
+                        {\r
+                            Range r = title->rangeOfString(kw);\r
+                            if (r.location == NotFound)\r
+                            {\r
+                                title_flag = false;\r
+                            }\r
+                        }\r
+\r
+                        // 概要\r
+                        if (desc != NULL)\r
+                        {\r
+                            Range r = desc->rangeOfString(kw);\r
+                            if (r.location == NotFound)\r
+                            {\r
+                                desc_flag = false;\r
+                            }\r
+                        }\r
+                    }\r
+\r
+                    if (title_flag || desc_flag)\r
+                    {\r
+                        // タイトル/概要のどちらかに、キーワード(分割)が全て含まれていたので\r
+                        // この EPG を予約対象とする\r
+                        Dictionary *epg2 = Dictionary::dictionaryWithDictionary(epg);\r
+                        if (epg2 != NULL)\r
+                        {\r
+                            DebugLog0("kwd: %s", kwd->cString());\r
+                            if (title != NULL)\r
+                            {\r
+                                DebugLog0("title: %s", title->cString());\r
+                            }\r
+                            if (desc != NULL)\r
+                            {\r
+                                DebugLog0("desc: %s", desc->cString());\r
+                            }\r
+                            epg2->setBool(true, KEY_EPG_RESERVED_BY_KEYWORDS);\r
+                            reserve(epg2);\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+    // unlock\r
+    RaymUnlock(this);\r
+}\r
+\r
+void Controller::updateSchedule()\r
+{\r
+    DebugLog2("Controller::updateSchedule()");\r
+\r
+    // lock\r
+    RaymLock(this);\r
+\r
+    // レジューム時刻\r
+    time_t resume_time = 0;\r
+\r
+    // EPG収集時刻を取得\r
+    String *collect_epg_time = _props->stringForKey(KEY_COLLECT_EPG_TIME);\r
+    if (collect_epg_time != NULL)\r
+    {\r
+        getTimeWithString(collect_epg_time, &resume_time);\r
+    }\r
+\r
+    // 録画予約をチェック\r
+    for (int tuner = 0; tuner < _tunerCount; ++tuner)\r
+    {\r
+        Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
+        if ((array == NULL) || (array->count() == 0))\r
+        {\r
+            // next tuner\r
+            continue;\r
+        }\r
+\r
+        Dictionary *epg = (Dictionary *)array->objectAtIndex(0);\r
+        time_t start = 0;\r
+        time_t end = 0;\r
+        getTimeWithEPG(epg, &start, &end);\r
+        if ((start != 0) && (end != 0))\r
+        {\r
+            if (start < resume_time)\r
+            {\r
+                resume_time = start;\r
+            }\r
+        }\r
+    }\r
+\r
+    //\r
+    TM resume_tm;\r
+    resume_time += OFFSET_OF_WAKEUP;    // 起動時刻を 5分前に設定(OFFSET_OF_WAKEUPで調整)\r
+    if (localtime_s(&resume_tm, &resume_time) == 0)\r
+    {\r
+        resetWakeSchedule();\r
+        if (setWakeSchedule(resume_tm.tm_year + 1900, resume_tm.tm_mon + 1, resume_tm.tm_mday, resume_tm.tm_hour, resume_tm.tm_min))\r
+        {\r
+            DebugLog0("set wake schedule to \"%04d/%02d/%02d %02d:%02d:00\".",\r
+                      resume_tm.tm_year + 1900, resume_tm.tm_mon + 1, resume_tm.tm_mday, resume_tm.tm_hour, resume_tm.tm_min);\r
+        }\r
+    }\r
+\r
+    // unlock\r
+    RaymUnlock(this);\r
+}\r
+\r
+#ifndef _WIN32\r
+#pragma mark '\r
+#pragma mark ------- チューナ制御 -------\r
+#endif\r
+\r
+void Controller::scanChannel(int tuner)\r
+{\r
+    DebugLog2("Controller::scanChannel(%d)", tuner);\r
+\r
+    if ((tuner < 0) || (_tunerCount <= tuner))\r
+    {\r
+        DebugLog3("Invalid tuner: %d", tuner);\r
+        return;\r
+    }\r
+\r
+    DebugLog0("start channel scan of \"%s\".", _tuners[tuner]->name());\r
+\r
+    // lock\r
+    RaymLock(this);\r
+\r
+    // 設定ファイルから全チューナ情報取得\r
+    Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
+    if (tunersInfo == NULL)\r
+    {\r
+        // 無ければ作成\r
+        tunersInfo = Dictionary::dictionaryWithCapacity(0);\r
+        _props->setObject(tunersInfo, KEY_TUNERS);\r
+    }\r
+\r
+    // 全チューナ情報から指定のチューナ情報を取得\r
+    Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
+    if (tunerInfo == NULL)\r
+    {\r
+        // 無ければ作成\r
+        tunerInfo = Dictionary::dictionaryWithCapacity(0);\r
+        tunersInfo->setObject(tunerInfo, _tuners[tuner]->name());\r
+    }\r
+\r
+    // チューナを未初期化に設定\r
+    tunerInfo->setBool(false, KEY_INITIALIZED);\r
+\r
+    // unlock\r
+    RaymUnlock(this);\r
+\r
+    // 全チャンネル情報を作成\r
+    Dictionary *channels = Dictionary::dictionaryWithCapacity(0);\r
+    tunerInfo->setObject(channels, KEY_CHANNELS);\r
+\r
+    // チューナタイプにより最大チャンネル数を設定\r
+    int max_channel;\r
+    Tuner::Type type = _tuners[tuner]->type();\r
+    if (type == Tuner::ISDB_S)\r
+    {\r
+        max_channel = Tuner::MAX_CHANNELS_ISDB_S;\r
+    }\r
+    else\r
+    {\r
+        max_channel = Tuner::MAX_CHANNELS_ISDB_T;\r
+    }\r
+\r
+    // 最終設定成功チャンネル保持用変数\r
+    int lastChannel = -1;\r
+\r
+    // チャンネルサーチ\r
+    for (int ch = 0; ch <= max_channel; ++ch)\r
+    {\r
+        // チャンネル情報\r
+        Dictionary *channelInfo = Dictionary::dictionaryWithCapacity(0);\r
+\r
+        // チャンネルキー(10進数:PTに対するチャンネル番号)\r
+        char chkey[4];\r
+        sprintf_s(chkey, sizeof(chkey), "%03d", ch);\r
+        channels->setObject(channelInfo, chkey);\r
+\r
+        // チャンネルID(論理チャンネル番号)\r
+        char channelID[8];\r
+        if (type == Tuner::ISDB_S)\r
+        {\r
+            if (ch < 12)\r
+            {\r
+                sprintf_s(channelID, sizeof(channelID), "BS%02d", 1 + 2 * ch);\r
+            }\r
+            else if (ch < 24)\r
+            {\r
+                sprintf_s(channelID, sizeof(channelID), "ND%02d", 2 + 2 * (ch - 12));\r
+            }\r
+            else\r
+            {\r
+                sprintf_s(channelID, sizeof(channelID), "ND%02d", 1 + 2 * (ch -24));\r
+            }\r
+        }\r
+        else\r
+        {\r
+            static int TABLE[][3] =\r
+            {\r
+                {   2,  0,   3 },\r
+                {  12,  1,  22 },\r
+                {  21,  0,  12 },\r
+                {  62,  1,  63 },\r
+                { 112,  0,  62 }\r
+            };\r
+\r
+            uint i;\r
+            for (i = 0; i < sizeof(TABLE)/sizeof(*TABLE); ++i)\r
+            {\r
+                if (ch <= TABLE[i][0])\r
+                {\r
+                    sprintf_s(channelID, sizeof(channelID), "%s%d", TABLE[i][1] ? "C" : "", ch + TABLE[i][2] - TABLE[i][0]);\r
+                    break;\r
+                }\r
+            }\r
+        }\r
+\r
+        // チャンネルIDを設定\r
+        channelInfo->setString(channelID, KEY_CHANNEL_ID);\r
+\r
+        // lock\r
+        RaymLock(this);\r
+\r
+        // チャンネル設定\r
         if (_tuners[tuner]->setChannel(ch))\r
         {\r
             // 設定成功\r
@@ -532,9 +1412,11 @@ void Controller::scanChannel(int tuner)
             DebugLog0("  CH %s: NG", chkey);\r
         }\r
 \r
+        // unlock\r
         RaymUnlock(this);\r
     }\r
 \r
+    // lock\r
     RaymLock(this);\r
 \r
     // チューナを初期化済みに更新\r
@@ -569,23 +1451,22 @@ void Controller::scanChannel(int tuner)
     // 設定ファイルを更新\r
     _props->writeToFile(_props_path, true);\r
 \r
+    // unlock\r
     RaymUnlock(this);\r
 }\r
 \r
-/*\r
- *\r
- */\r
 int Controller::getChannel(int tuner)\r
 {\r
     DebugLog2("Controller::getChannel()");\r
 \r
     int channel = -1;\r
 \r
-    RaymLock(this);\r
-\r
-    if ((0 <= tuner) && (tuner < _tuner_count))\r
+    if ((0 <= tuner) && (tuner < _tunerCount))\r
     {\r
-        Dictionary *tunersInfo = _status->dictionaryForKey(KEY_TUNERS);\r
+        // lock\r
+        RaymLock(this);\r
+\r
+        Dictionary *tunersInfo = _status->dictionaryForKey(KEY_TUNERS);\r
         if (tunersInfo != NULL)\r
         {\r
             Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
@@ -594,26 +1475,25 @@ int Controller::getChannel(int tuner)
                 channel = tunerInfo->integerForKey(KEY_CHANNEL);\r
             }\r
         }\r
-    }\r
 \r
-    RaymUnlock(this);\r
+        // unlock\r
+        RaymUnlock(this);\r
+    }\r
 \r
     return channel;\r
 }\r
 \r
-/*\r
- *\r
- */\r
 bool Controller::setChannel(int tuner, int channel)\r
 {\r
     DebugLog2("Controller::setChannel()");\r
 \r
     bool result = false;\r
 \r
-    RaymLock(this);\r
-\r
-    if ((0 <= tuner) && (tuner < _tuner_count))\r
+    if ((0 <= tuner) && (tuner < _tunerCount))\r
     {\r
+        // lock\r
+        RaymLock(this);\r
+\r
         if (!_tuners[tuner]->isLocked())\r
         {\r
             Dictionary *tunersInfo = _status->dictionaryForKey(KEY_TUNERS);\r
@@ -640,142 +1520,3748 @@ bool Controller::setChannel(int tuner, int channel)
 \r
                     result = true;\r
                 }\r
+                else\r
+                {\r
+                    DebugLog0("set channel failed. %d, %d", tuner, channel);\r
+                }\r
             }\r
         }\r
-    }\r
 \r
-    RaymUnlock(this);\r
+        // unlock\r
+        RaymUnlock(this);\r
+    }\r
 \r
     return result;\r
 }\r
 \r
+//\r
+// チューナ(streaming)制御/システム関連 周期処理\r
+//\r
+void Controller::periodic_2(void)\r
+{\r
+    //\r
+    // UDPポート監視\r
+    //\r
+\r
+    // マッピング(UDPPort:tuner,ch)情報取得\r
+    Dictionary *mapping = NULL;\r
+    if ((_streaming_ctrls != NULL) && ((mapping = _streaming_ctrls->dictionaryForKey(KEY_MAPPING_UDP_TO_TUNER_CHANNEL)) != NULL))\r
+    {\r
+        // マッピング情報取得OK\r
+\r
+        // 使用中のUDPの情報を取得\r
+        // どれだけ使用中なのか不明なので、まずは必要サイズを調べる\r
+        DWORD size = 0;\r
+        if (GetExtendedUdpTable(NULL, &size, true, AF_INET, UDP_TABLE_OWNER_PID, 0) == ERROR_INSUFFICIENT_BUFFER)\r
+        {\r
+            // ERROR_INSUFFICIENT_BUFFER の場合、必要なバッファサイズが size に格納される\r
+\r
+            // バッファ確保\r
+            PMIB_UDPTABLE_OWNER_PID udptable = (PMIB_UDPTABLE_OWNER_PID)malloc(size);\r
+            if (udptable != NULL)\r
+            {\r
+                // バッファ確保OK\r
+\r
+                // UDP情報取得\r
+                if (GetExtendedUdpTable(udptable, &size, true, AF_INET, UDP_TABLE_OWNER_PID, 0) == NO_ERROR)\r
+                {\r
+                    // 取得OK\r
+                    DebugLog3("udptable->dwNumEntries: %d", udptable->dwNumEntries);\r
+\r
+                    // 停止要否確認\r
+                    Dictionary *using_port = _streaming_ctrls->dictionaryForKey(KEY_UDP_IN_USE);\r
+                    if (using_port != NULL)\r
+                    {\r
+                        // key = 使用中ポート\r
+                        Array *using_ports = using_port->allKeys();\r
+                        if (using_ports != NULL)\r
+                        {\r
+                            // 使用中ポートでループ\r
+                            for (uint i = 0; i < using_ports->count(); ++i)\r
+                            {\r
+                                // 停止要否フラグ\r
+                                bool stop_need = true;\r
+\r
+                                // 使用中のUDP情報でループ\r
+                                for (uint j = 0; j < udptable->dwNumEntries; ++j)\r
+                                {\r
+                                    if (((String *)using_ports->objectAtIndex(i))->intValue() == ntohs((WORD)udptable->table[j].dwLocalPort))\r
+                                    {\r
+                                        // 使用中なので停止不要\r
+                                        stop_need = false;\r
+                                        break;\r
+                                    }\r
+                                }\r
+\r
+                                // 停止要否\r
+                                if (stop_need)\r
+                                {\r
+                                    // マッピング情報を取得\r
+                                    String *tuner_and_channel = mapping->stringForKey((String *)using_ports->objectAtIndex(i));\r
+                                    if (tuner_and_channel != NULL)\r
+                                    {\r
+                                         // チューナとチャンネルに分割\r
+                                        Range r = tuner_and_channel->rangeOfString(",");\r
+                                        if (r.location != NotFound)\r
+                                        {\r
+                                            int tuner = tuner_and_channel->substringToIndex(r.location)->intValue();\r
+                                            int channel = tuner_and_channel->substringFromIndex(r.location + 1)->intValue();\r
+                                            DebugLog3("tuner: %d, channel: %d", tuner, channel);\r
+\r
+                                            DebugLog0("auto streaming stop: %s", ((String *)using_ports->objectAtIndex(i))->cString());\r
+\r
+                                            _tuners[tuner]->stopStreaming();\r
+                                            using_port->removeObjectForKey((String *)using_ports->objectAtIndex(i));\r
+                                        }\r
+                                    }\r
+                                }\r
+                            }\r
+                        }\r
+                    }\r
+\r
+\r
+                    // 起動要否確認\r
+                    for (uint i = 0; i < udptable->dwNumEntries; ++i)\r
+                    {\r
+                        // ポート番号を文字列に変換して\r
+                        char port[10];\r
+                        sprintf_s(port, "%d", ntohs((WORD)udptable->table[i].dwLocalPort));\r
+                        DebugLog3("port = %s", port);\r
+\r
+                        // マッピング情報を取得\r
+                        String *tuner_and_channel = mapping->stringForKey(port);\r
+                        if (tuner_and_channel != NULL)\r
+                        {\r
+                            // 取得OK: 監視対象ポートが使用されている\r
+\r
+                            // 使用アプリを調べる\r
+                            bool auto_streaming = false;\r
+                            char exec_path[MAX_PATH];\r
+                            memset(exec_path, 0, sizeof(exec_path));\r
+\r
+                            // プロセスハンドル取得\r
+                            size_t returnValue;\r
+                            HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, udptable->table[i].dwOwningPid);\r
+                            if (hProcess != NULL)\r
+                            {\r
+                                TCHAR exec[MAX_PATH];\r
+                                memset(exec, 0, sizeof(exec));\r
+                                DWORD len = sizeof(exec) - 1;\r
+\r
+                                // イメージ取得\r
+                                if (QueryFullProcessImageName(hProcess, 0, exec, &len))\r
+                                {\r
+                                    // ワイド -> マルチ 変換\r
+                                    if (wcstombs_s(&returnValue, exec_path, sizeof(exec_path), exec, _TRUNCATE) == 0)\r
+                                    {\r
+                                        // 成功\r
+\r
+                                        // とりあえず、、、現状は "ffmpeg.exe" / "vlc.exe" / "Kodi.exe" があったら auto_streaming を true にする\r
+                                        if ((strstr(exec_path, "ffmpeg.exe") != NULL) ||\r
+                                            (strstr(exec_path, "vlc.exe") != NULL) ||\r
+                                            (strstr(exec_path, "Kodi.exe") != NULL))\r
+                                        {\r
+                                            auto_streaming = true;\r
+                                        }\r
+                                    }\r
+                                }\r
+\r
+                                // プロセスハンドル解放\r
+                                CloseHandle(hProcess);\r
+                            }\r
+\r
+                            if (auto_streaming)\r
+                            {\r
+                                // チューナとチャンネルに分割\r
+                                Range r = tuner_and_channel->rangeOfString(",");\r
+                                if (r.location != NotFound)\r
+                                {\r
+                                    int tuner = tuner_and_channel->substringToIndex(r.location)->intValue();\r
+                                    int channel = tuner_and_channel->substringFromIndex(r.location + 1)->intValue();\r
+                                    DebugLog3("tuner: %d, channel: %d", tuner, channel);\r
+\r
+                                    // lock\r
+                                    RaymLock(this);\r
+\r
+                                    // 非ストリーミング中 かつ 非レコーディング中 または チャンネルが同じ 場合\r
+                                    if (!_tuners[tuner]->isStreaming() && (!_tuners[tuner]->isRecording() || _tuners[tuner]->channel() == channel))\r
+                                    {\r
+                                        // ストリーミング開始可能\r
+\r
+                                        if (_tuners[tuner]->channel() != channel)\r
+                                        {\r
+                                            setChannel(tuner, channel);\r
+                                        }\r
+\r
+                                        SOCKADDR_IN dst_addr;\r
+                                        dst_addr.sin_family = AF_INET;\r
+                                        dst_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);\r
+                                        dst_addr.sin_port = (WORD)udptable->table[i].dwLocalPort;\r
+\r
+                                        if (_tuners[tuner]->startStreaming(&dst_addr))\r
+                                        {\r
+                                            // 成功\r
+                                            DebugLog0("auto streaming start: %d", ntohs((WORD)udptable->table[i].dwLocalPort));\r
+\r
+                                            // 使用中ポートに登録\r
+                                            using_port = _streaming_ctrls->dictionaryForKey(KEY_UDP_IN_USE);\r
+                                            if (using_port == NULL)\r
+                                            {\r
+                                                using_port = Dictionary::dictionaryWithCapacity(0);\r
+                                                _streaming_ctrls->setObject(using_port, KEY_UDP_IN_USE);\r
+                                            }\r
+                                            using_port->setBool(true, port);\r
+                                        }\r
+                                    }\r
+\r
+                                    // unlock\r
+                                    RaymUnlock(this);\r
+                                }\r
+                            }\r
+                        }\r
+                    }\r
+                }\r
+\r
+                // バッファ解放\r
+                free(udptable);\r
+            }\r
+        }\r
+    }\r
+\r
+\r
+\r
+    //\r
+    // 1/100秒単位が 0 に近くなるように次回T.O.を微調整\r
+    //\r
+\r
+#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)\r
+    static const __time64_t DELTA_EPOCH_IN_MICROSECS = 11644473600000000Ui64;\r
+#else\r
+    static const __time64_t DELTA_EPOCH_IN_MICROSECS = 11644473600000000ULL;\r
+#endif\r
+    // 現在時刻を取得\r
+    FILETIME ft;\r
+    GetSystemTimeAsFileTime(&ft);\r
+\r
+    // EPOCH秒への変換\r
+    __time64_t now_sec;\r
+    __time64_t now_usec;\r
+    now_sec = ft.dwHighDateTime;\r
+    now_sec <<= 32;\r
+    now_sec |= ft.dwLowDateTime;\r
+    now_sec /= 10;  /*convert into microseconds*/\r
+    now_sec -= DELTA_EPOCH_IN_MICROSECS;\r
+    now_usec = (now_sec % 1000000UL);\r
+    now_sec = now_sec / 1000000UL;\r
+\r
+    TimeInterval interval = (TimeInterval)now_usec;\r
+    interval = interval / 1000000;\r
+    _timer_periodic_2->setTimeInterval(1.005 - interval);\r
+}\r
+\r
 #ifndef _WIN32\r
 #pragma mark '\r
-#pragma mark ------- タイマディスパッチャ -------\r
+#pragma mark ------- HTTP制御 -------\r
 #endif\r
 \r
-void Controller::timerExpired(Timer *timer, void *userInfo)\r
+static std::string epg_regist_form(Dictionary *epg)\r
 {\r
-    DebugLog2("Controller::timerExpired()");\r
+    DebugLog3("epg_regist_form() start.");\r
 \r
-    //\r
+    std::string epgs;\r
 \r
-    switch ((long long)userInfo)\r
+    if ((epg != NULL) &&\r
+        (epg->stringForKey(KEY_EPG_SERVICE_ID) != NULL) &&\r
+        (epg->stringForKey(KEY_EPG_EVENT_ID) != NULL) &&\r
+        (epg->stringForKey(KEY_EPG_START) != NULL) &&\r
+        (epg->stringForKey(KEY_EPG_END) != NULL) &&\r
+        (epg->stringForKey(KEY_EPG_DATE) != NULL) &&\r
+        (epg->stringForKey(KEY_EPG_TITLE) != NULL))\r
     {\r
-    case CMD_RESTART:\r
-        if (restart() > 0)\r
+        epgs += "<form id=\"";\r
+        epgs += "epg_";\r
+        epgs += epg->stringForKey(KEY_EPG_SERVICE_ID)->cString();\r
+        epgs += "_";\r
+        epgs += epg->stringForKey(KEY_EPG_EVENT_ID)->cString();\r
+        epgs += "\" title=\"";\r
+        epgs += epg->stringForKey(KEY_EPG_START)->substringToIndex(5)->cString();\r
+        epgs += "-";\r
+        epgs += epg->stringForKey(KEY_EPG_END)->substringToIndex(5)->cString();\r
+        epgs += "\" class=\"panel\" action=\"regist.cgi\" method=\"GET\" target=\"_self\" onclick='return confirm(\"Is it ok?\");'>";\r
+\r
+        // 放送時間\r
+        epgs += "<h2>";\r
+        epgs += LocalizedString(KEY_I18N_Broadcasting_Time, NULL)->cString();\r
+        epgs += "</h2>";\r
+        epgs += "<fieldset>";\r
+        epgs += "<p class=\"normalText\">&nbsp;&nbsp;";\r
+        epgs += epg->stringForKey(KEY_EPG_DATE)->substringFromIndex(5)->cString();\r
+        epgs += "&nbsp;";\r
+        epgs += epg->stringForKey(KEY_EPG_START)->substringToIndex(5)->cString();\r
+        epgs += "-";\r
+        epgs += epg->stringForKey(KEY_EPG_END)->substringToIndex(5)->cString();\r
+        epgs += "</p>";\r
+        epgs += "</fieldset>";\r
+\r
+        // 番組名\r
+        epgs += "<h2>";\r
+        epgs += LocalizedString(KEY_I18N_Program_Title, NULL)->cString();\r
+        epgs += "</h2>";\r
+        epgs += "<fieldset>";\r
+        epgs += "<p class=\"normalText\">&nbsp;&nbsp;";\r
+        epgs += epg->stringForKey(KEY_EPG_TITLE)->cString();\r
+        epgs += "</p>";\r
+        epgs += "</fieldset>";\r
+\r
+        // 概要\r
+        if (epg->stringForKey(KEY_EPG_DESCRIPTION) != NULL)\r
         {\r
-            // 初期化成功\r
-            DebugLog2("tuner initialize success.");\r
+            epgs += "<h2>";\r
+            epgs += LocalizedString(KEY_I18N_Description, NULL)->cString();\r
+            epgs += "</h2>";\r
+            epgs += "<fieldset>";\r
+            epgs += "<p class=\"normalText\">&nbsp;&nbsp;";\r
+            epgs += epg->stringForKey(KEY_EPG_DESCRIPTION)->cString();\r
+            epgs += "</p>";\r
+            epgs += "</fieldset>";\r
+        }\r
+\r
+        //\r
+        epgs += "<input type=\"hidden\" name=\"service_id\" value=\"";\r
+        epgs += epg->stringForKey(KEY_EPG_SERVICE_ID)->cString();\r
+        epgs += "\"/>";\r
+        epgs += "<input type=\"hidden\" name=\"event_id\" value=\"";\r
+        epgs += epg->stringForKey(KEY_EPG_EVENT_ID)->cString();\r
+        epgs += "\"/>";\r
+        epgs += "<input class=\"redButton\" type=\"submit\" value=\"";\r
+        epgs += LocalizedString(KEY_I18N_New_Reservation, NULL)->cString();\r
+        epgs += "\"/>";\r
+    //    epgs += "<a class=\"redButton\" type=\"submit\">";\r
+    //    epgs += LocalizedString(KEY_I18N_New_Reservation, NULL)->cString();\r
+    //    epgs += "</a>";\r
+        epgs += "</form>";\r
+    }\r
+\r
+    return epgs;\r
+}\r
+\r
+HTTPResponse *responseWithDictionary(HTTPRequest *request, Dictionary *dictionary)\r
+{\r
+    HTTPResponse *result = NULL;\r
+    if ((request != NULL) && (dictionary != NULL))\r
+    {\r
+        std::string xml = dictionary->toString();\r
+\r
+        // header\r
+        InternetTextMessageHeader *header = InternetTextMessageHeader::alloc()->init();\r
+        // Date\r
+        // Server\r
+        // Content-Encoding\r
+        // Last-Modified\r
+        // Content-Type\r
+        header->setFieldBodyWithName("application/xml", "Content-Type");\r
+        // Connection\r
+        // Tranfer-Encoding\r
+        // Content-Length\r
+        header->setFieldBodyWithName(String::stringWithFormat("%I64u", xml.length()), "Content-Length");\r
+\r
+        // body\r
+        InternetTextMessageBody *body = InternetTextMessageBody::alloc()->initWithString(String::stringWithUTF8String(xml.c_str()));\r
+\r
+        // message\r
+        InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, body);\r
+        RELEASE(header);\r
+        RELEASE(body);\r
+        if (message != NULL)\r
+        {\r
+            result = HTTPResponse::alloc()->init();\r
+            result->autorelease();\r
+            result->setVersion(request->version());\r
+            result->setReason(NET::HTTPDaemon::reasonForStatus(200));\r
+            result->setStatus(200);\r
+            result->setMessage(message);\r
+            RELEASE(message);\r
+        }\r
+    }\r
+    return result;\r
+}\r
+\r
+// positive response by XML\r
+HTTPResponse *responseForSuccess(HTTPRequest *request)\r
+{\r
+    Dictionary *dict = Dictionary::dictionaryWithCapacity(0);\r
+    dict->setString("Success", KEY_RESULT);\r
+    return responseWithDictionary(request, dict);\r
+}\r
+\r
+// negative response by XML\r
+HTTPResponse *responseForFailed(HTTPRequest *request)\r
+{\r
+    Dictionary *dict = Dictionary::dictionaryWithCapacity(0);\r
+    dict->setString("Failed", KEY_RESULT);\r
+    return responseWithDictionary(request, dict);\r
+}\r
+\r
+HTTPResponse *Controller::responseWithHTML(HTTPRequest *request, String *html)\r
+{\r
+    HTTPResponse *result = NULL;\r
+    if ((html != NULL) && (request != NULL))\r
+    {\r
+        // header\r
+        InternetTextMessageHeader *header = InternetTextMessageHeader::alloc()->init();\r
+        // Date\r
+        // Server\r
+        // Content-Encoding\r
+        // Last-Modified\r
+        // Content-Type\r
+        header->setFieldBodyWithName("text/html", "Content-Type");\r
+        // Connection\r
+        // Tranfer-Encoding\r
+        // Content-Length\r
+        header->setFieldBodyWithName(String::stringWithFormat("%I64u", html->length()), "Content-Length");\r
+\r
+        // body\r
+        InternetTextMessageBody *body = InternetTextMessageBody::alloc()->initWithString(html);\r
+\r
+        // message\r
+        InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, body);\r
+        RELEASE(header);\r
+        RELEASE(body);\r
+        if (message != NULL)\r
+        {\r
+//            result = HTTPResponse::response();\r
+            result = HTTPResponse::alloc()->init();\r
+            result->setVersion(request->version());\r
+            result->setReason(NET::HTTPDaemon::reasonForStatus(200));\r
+            result->setStatus(200);\r
+            result->setMessage(message);\r
+            result->autorelease();\r
+            RELEASE(message);\r
+        }\r
+    }\r
+    return result;\r
+}\r
+\r
+HTTPResponse *Controller::responseWithUTF8Text(HTTPRequest *request, String *text)\r
+{\r
+    HTTPResponse *result = NULL;\r
+    if ((text != NULL) && (request != NULL))\r
+    {\r
+        // header\r
+        InternetTextMessageHeader *header = InternetTextMessageHeader::alloc()->init();\r
+        // Date\r
+        // Server\r
+        // Content-Encoding\r
+        // Last-Modified\r
+        // Content-Type\r
+        header->setFieldBodyWithName("text/plane; charset=UTF-8", "Content-Type");\r
+        // Connection\r
+        // Tranfer-Encoding\r
+        // Content-Length\r
+        header->setFieldBodyWithName(String::stringWithFormat("%I64u", text->length()), "Content-Length");\r
+\r
+        // body\r
+        InternetTextMessageBody *body = InternetTextMessageBody::alloc()->initWithString(text);\r
+\r
+        // message\r
+        InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, body);\r
+        RELEASE(header);\r
+        RELEASE(body);\r
+        if (message != NULL)\r
+        {\r
+//            result = HTTPResponse::response();\r
+            result = HTTPResponse::alloc()->init();\r
+            result->setVersion(request->version());\r
+            result->setReason(NET::HTTPDaemon::reasonForStatus(200));\r
+            result->setStatus(200);\r
+            result->setMessage(message);\r
+            result->autorelease();\r
+            RELEASE(message);\r
+        }\r
+    }\r
+    return result;\r
+}\r
+\r
+HTTPResponse *Controller::responseByResultAndReferer(HTTPRequest *request, bool result, const char *referer)\r
+{\r
+    HTTPResponse *retval = NULL;\r
+\r
+    bool iui = false;\r
+\r
+    String *ref = NULL;\r
+    InternetTextMessage *msg = request->message();\r
+    if (msg != NULL)\r
+    {\r
+        InternetTextMessageHeader *header = msg->header();\r
+        if (header != NULL)\r
+        {\r
+            String *field_referer = header->fieldBodyForName("Referer");\r
+            if (field_referer != NULL)\r
+            {\r
+                String *field_host    = header->fieldBodyForName("Host");\r
+                if (field_host != NULL)\r
+                {\r
+                    std::string tmp = "http://";\r
+                    tmp += field_host->cString();\r
+                    tmp += "/";\r
+                    tmp += referer;\r
+                    iui = field_referer->isEqualToString(tmp.c_str());\r
+                    DebugLog2("tmp: %s", tmp.c_str());\r
+                    DebugLog2("ref: %s", field_referer->cString());\r
+                }\r
+            }\r
+        }\r
+    }\r
+//    if ((ref != NULL) && match(ref->cString(), referer))\r
+//    if ((ref != NULL) && ref->isEqualToString(referer))\r
+    if (iui)\r
+    {\r
+        String *path = _httpd->rootPath()->stringByAppendingPathComponent("template2.html");\r
+        String *html = String::stringWithContentsOfFile(path->cString(), UTF8StringEncoding);\r
+        if (html != NULL)\r
+        {\r
+            html = html->stringByReplacingOccurrencesOfString("%%TITLE%%", "Result");\r
+            std::string contents;\r
+            std::string reservations;\r
+            contents += "<div id=\"home\" class=\"panel\" title=\"Result\" selected=\"true\">";\r
+            if (result)\r
+            {\r
+                contents += "<h2>Success</h2>";\r
+            }\r
+            else\r
+            {\r
+                contents += "<h2>Failed</h2>";\r
+            }\r
+            contents += "</div>";\r
+\r
+            html = html->stringByReplacingOccurrencesOfString("%%CONTENTS%%", contents.c_str());\r
+            retval = responseWithHTML(request, html);\r
+        }\r
+    }\r
+    else\r
+    {\r
+        if (result)\r
+        {\r
+            retval = responseForSuccess(request);\r
         }\r
         else\r
         {\r
-            // 失敗\r
-            DebugLog0("tuner initialize failed.");\r
+            retval = responseForFailed(request);\r
         }\r
-        break;\r
+    }\r
 \r
+    return retval;\r
+}\r
+\r
+HTTPResponse *Controller::responseForMain(HTTPRequest *request, SOCKADDR_IN *client)\r
+{\r
+    DebugLog2("Controller::responseForMain()");\r
+\r
+    HTTPResponse *result = NULL;\r
+    while ((request != NULL) && (client != NULL))\r
+    {\r
+        String *path = _httpd->rootPath();\r
+        if (path == NULL)\r
+        {\r
+            DebugLog3("_httpd->rootPath() ng.");\r
+            break;\r
+        }\r
+\r
+        path = path->stringByAppendingPathComponent("template1.html");\r
+        if (path == NULL)\r
+        {\r
+            DebugLog3("path->stringByAppendingPathComponent() ng.");\r
+            break;\r
+        }\r
+\r
+        String *html = String::stringWithContentsOfFile(path->cString(), UTF8StringEncoding);\r
+        if (html == NULL)\r
+        {\r
+            DebugLog3("String::stringWithContentsOfFile() ng.");\r
+            break;\r
+        }\r
+\r
+        String *server_name = _props->stringForKey(KEY_NAME);\r
+        if (server_name == NULL)\r
+        {\r
+            DebugLog3("_props->stringForKey(KEY_NAME) ng.");\r
+            break;\r
+        }\r
+\r
+        html = html->stringByReplacingOccurrencesOfString("%%TITLE%%", server_name);\r
+        if (html == NULL)\r
+        {\r
+            DebugLog3("html->stringByReplacingOccurrencesOfString() ng.");\r
+            break;\r
+        }\r
+\r
+        html = html->stringByReplacingOccurrencesOfString("%%PAGE_TITLE%%", LocalizedString(KEY_I18N_Main_Menu, NULL));\r
+        if (html == NULL)\r
+        {\r
+            DebugLog3("html->stringByReplacingOccurrencesOfString() ng.");\r
+        }\r
+\r
+        std::string contents;\r
+        contents += "<div id=\"home\" class=\"panel\" selected=\"true\">";\r
+        contents += "<ul>";\r
+        contents += "<li><a target=\"_self\" href=\"/programs.html\">";\r
+        contents += LocalizedString(KEY_I18N_Programs, NULL)->cString();\r
+        contents += "</a></li>";\r
+        contents += "<li><a target=\"_self\" href=\"/tv.html\">";\r
+        contents += LocalizedString(KEY_I18N_TV, NULL)->cString();\r
+        contents += "</a></li>";\r
+        contents += "<li><a target=\"_self\" href=\"/video.html\">";\r
+        contents += LocalizedString(KEY_I18N_Video, NULL)->cString();\r
+        contents += "</a></li>";\r
+        contents += "<li><a target=\"_self\" href=\"/reservation.html\">";\r
+        contents += LocalizedString(KEY_I18N_Reservation, NULL)->cString();\r
+        contents += "</a></li>";\r
+        contents += "</ul>";\r
 #if 0\r
-    case CMD_SUSPEND:\r
-        if (canSuspend())\r
+        contents += "<ul>";\r
+        contents += "<li><a target=\"_self\" href=\"/exec_vlc.html\">VLC media player";\r
+        contents += "</ul>";\r
+#endif\r
+        contents += "<ul>";\r
+        contents += "<li><a target=\"_self\" href=\"/status.html\">";\r
+        contents += LocalizedString(KEY_I18N_Tuner_Status, NULL)->cString();\r
+        contents += "</a></li>";\r
+        contents += "</ul>";\r
+        contents += "<center><a target=\"_self\" href=\"/iptd.log\">Ver. ";\r
+        contents += VERSION;\r
+        contents += "</a></center>";\r
+        contents += "</div>";\r
+\r
+        html = html->stringByReplacingOccurrencesOfString("%%CONTENTS%%", contents.c_str());\r
+        if (html != NULL)\r
         {\r
-            suspend();\r
+            result = responseWithHTML(request, html);\r
         }\r
+\r
         break;\r
-#endif\r
     }\r
-}\r
 \r
-#ifndef _WIN32\r
-#pragma mark '\r
-#pragma mark ------- その他 -------\r
-#endif\r
+    return result;\r
+}\r
 \r
-//\r
-// HH:MM:SS 形式の文字列から time_t に変換\r
-// 現在時刻が指定文字列の時刻以前の場合、当日の時刻。指定文字列の時刻よりも過ぎている場合、翌日の時刻を返す。\r
-//\r
-void Controller::getTimeWithString(String *str, time_t *time_var)\r
+HTTPResponse *Controller::responseForPrograms(HTTPRequest *request, SOCKADDR_IN *client)\r
 {\r
-    if ((str != NULL) && str->isMatch(String::stringWithUTF8String("^\\d\\d:\\d\\d:\\d\\d$")) && (time_var != NULL))\r
+    DebugLog2("Controller::responseForPrograms() start.");\r
+\r
+    HTTPResponse *result = NULL;\r
+    while ((request != NULL) && (client != NULL))\r
     {\r
-        // 時:分:秒 を int型に分解\r
-        std::string time_str = str->cString();\r
-        int hour = atoi(time_str.substr(0, 2).c_str());\r
-        int min  = atoi(time_str.substr(3, 2).c_str());\r
-        int sec  = atoi(time_str.substr(6, 2).c_str());\r
-        DebugLog2("%02d:%02d:%02d", hour, min, sec);\r
+        String *path = _httpd->rootPath();\r
+        if (path == NULL)\r
+        {\r
+            DebugLog3("_httpd->rootPath() ng.");\r
+            break;\r
+        }\r
 \r
-        // 現在時刻取得\r
-        time_t now = time(NULL);\r
-        TM now_tm;\r
-        if (localtime_s(&now_tm, &now) == 0)\r
+        path = path->stringByAppendingPathComponent("template2.html");\r
+        if (path == NULL)\r
         {\r
-            int now_sec = now_tm.tm_hour * 3600 + now_tm.tm_min * 60 + now_tm.tm_sec;\r
-            int col_sec = hour * 3600 + min * 3600 + sec;\r
-            if (now_sec > col_sec)\r
+            DebugLog3("path->stringByAppendingPathComponent() ng.");\r
+            break;\r
+        }\r
+\r
+        String *html = String::stringWithContentsOfFile(path->cString(), UTF8StringEncoding);\r
+        if (html == NULL)\r
+        {\r
+            DebugLog3("String::stringWithContentsOfFile() ng.");\r
+            break;\r
+        }\r
+\r
+        html = html->stringByReplacingOccurrencesOfString("%%TITLE%%", LocalizedString(KEY_I18N_Programs, NULL));\r
+        if (html == NULL)\r
+        {\r
+            DebugLog3("html->stringByReplacingOccurrencesOfString() ng.");\r
+            break;\r
+        }\r
+\r
+        std::string contents;\r
+        std::string epgs;\r
+\r
+        contents += "<ul id=\"home\" title=\"";\r
+        contents += LocalizedString(KEY_I18N_Programs, NULL)->cString();\r
+        contents += "\" selected=\"true\">";\r
+        // 地デジ\r
+        contents += "<li>";\r
+        contents += "<a href=\"#isdb_t\">";\r
+        contents += LocalizedString(KEY_I18N_Digital_Terrestrial_Television_Broadcasting, NULL)->cString();\r
+        contents += "</a></li>";\r
+        // BS/CS\r
+        contents += "<li>";\r
+        contents += "<a href=\"#isdb_s\">";\r
+        contents += "BS/CS";\r
+        contents += "</a></li>";\r
+\r
+        contents += "</ul>";\r
+\r
+        // 地デジ(局名)\r
+        contents += "<ul id=\"isdb_t\" title=\"";\r
+        contents += LocalizedString(KEY_I18N_Digital_Terrestrial_Television_Broadcasting, NULL)->cString();\r
+        contents += "\">";\r
+        Array *stations = stationInfos(Tuner::ISDB_T);\r
+        for (uint i = 0; i < stations->count(); ++i)\r
+        {\r
+            Dictionary *station = (Dictionary *)stations->objectAtIndex(i);\r
+            if ((station != NULL) &&\r
+                (station->stringForKey(KEY_CHANNEL_ID) != NULL) &&\r
+                (station->stringForKey(KEY_NAME) != NULL))\r
             {\r
-                ++now_tm.tm_mday;\r
+                contents += "<li>";\r
+                contents += "<a href=\"#isdb_t_";\r
+                contents += station->stringForKey(KEY_CHANNEL_ID)->cString();\r
+                contents += "\">";\r
+                contents += station->stringForKey(KEY_NAME)->cString();\r
+                contents += "</a></li>";\r
             }\r
-            now_tm.tm_hour = hour;\r
-            now_tm.tm_min  = min;\r
-            now_tm.tm_sec  = sec;\r
+        }\r
+        contents += "</ul>";\r
+\r
+        // 地デジ(番組データ)\r
+        for (uint i = 0; i < stations->count(); ++i)\r
+        {\r
+            Dictionary *station = (Dictionary *)stations->objectAtIndex(i);\r
+            if ((station != NULL) &&\r
+                (station->stringForKey(KEY_CHANNEL_ID) != NULL) &&\r
+                (station->stringForKey(KEY_NAME) != NULL))\r
+            {\r
+\r
+                contents += "<ul id=\"isdb_t_";\r
+                contents += station->stringForKey(KEY_CHANNEL_ID)->cString();\r
+                contents += "\" title=\"";\r
+                contents += station->stringForKey(KEY_NAME)->cString();\r
+                contents += "\">";\r
+\r
+                Array *programs = programsForServices(station->arrayForKey(KEY_SERVICES));\r
+                for (uint i = 0; i < programs->count(); ++i)\r
+                {\r
+                    Dictionary *epg = (Dictionary *)programs->objectAtIndex(i);\r
+                    if ((epg != NULL) &&\r
+                        (epg->stringForKey(KEY_EPG_SERVICE_ID) != NULL) &&\r
+                        (epg->stringForKey(KEY_EPG_EVENT_ID) != NULL) &&\r
+                        (epg->stringForKey(KEY_EPG_DATE) != NULL) &&\r
+                        (epg->stringForKey(KEY_EPG_START) != NULL) &&\r
+                        (epg->stringForKey(KEY_EPG_END) != NULL))\r
+                    {\r
+                        contents += "<li>";\r
+                        contents += "<a href=\"#epg_";\r
+                        contents += epg->stringForKey(KEY_EPG_SERVICE_ID)->cString();\r
+                        contents += "_";\r
+                        contents += epg->stringForKey(KEY_EPG_EVENT_ID)->cString();\r
+                        contents += "\">";\r
+                        contents += epg->stringForKey(KEY_EPG_DATE)->substringFromIndex(5)->cString();\r
+                        contents += "&nbsp;";\r
+                        contents += epg->stringForKey(KEY_EPG_START)->substringToIndex(5)->cString();\r
+                        contents += "-";\r
+                        contents += epg->stringForKey(KEY_EPG_END)->substringToIndex(5)->cString();\r
+                        contents += "</a></li>";\r
+\r
+                        epgs += epg_regist_form(epg);\r
+                    }\r
+                }\r
+                contents += "</ul>";\r
+            }\r
+        }\r
+\r
+        // BS/CS(局名)\r
+        contents += "<ul id=\"isdb_s\" title=\"";\r
+        contents += LocalizedString(KEY_I18N_Digital_Terrestrial_Television_Broadcasting, NULL)->cString();\r
+        contents += "\">";\r
+        stations = stationInfos(Tuner::ISDB_S);\r
+        for (uint i = 0; i < stations->count(); ++i)\r
+        {\r
+            Dictionary *station = (Dictionary *)stations->objectAtIndex(i);\r
+            if ((station != NULL) &&\r
+                (station->stringForKey(KEY_CHANNEL_ID) != NULL) &&\r
+                (station->stringForKey(KEY_NAME) != NULL))\r
+            {\r
+                contents += "<li>";\r
+                contents += "<a href=\"#isdb_s_";\r
+                contents += station->stringForKey(KEY_CHANNEL_ID)->cString();\r
+                contents += "\">";\r
+                contents += station->stringForKey(KEY_NAME)->cString();\r
+                contents += "</a></li>";\r
+            }\r
+        }\r
+        contents += "</ul>";\r
+\r
+        // BS/CS(番組データ)\r
+        for (uint i = 0; i < stations->count(); ++i)\r
+        {\r
+            Dictionary *station = (Dictionary *)stations->objectAtIndex(i);\r
+            if ((station != NULL) &&\r
+                (station->stringForKey(KEY_CHANNEL_ID) != NULL) &&\r
+                (station->stringForKey(KEY_NAME) != NULL))\r
+            {\r
+                contents += "<ul id=\"isdb_s_";\r
+                contents += station->stringForKey(KEY_CHANNEL_ID)->cString();\r
+                contents += "\" title=\"";\r
+                contents += station->stringForKey(KEY_NAME)->cString();\r
+                contents += "\">";\r
+\r
+                Array *programs = programsForServices(station->arrayForKey(KEY_SERVICES));\r
+                for (uint i = 0; i < programs->count(); ++i)\r
+                {\r
+                    Dictionary *epg = (Dictionary *)programs->objectAtIndex(i);\r
+                    if ((epg != NULL) &&\r
+                        (epg->stringForKey(KEY_EPG_SERVICE_ID) != NULL) &&\r
+                        (epg->stringForKey(KEY_EPG_EVENT_ID) != NULL) &&\r
+                        (epg->stringForKey(KEY_EPG_DATE) != NULL) &&\r
+                        (epg->stringForKey(KEY_EPG_START) != NULL) &&\r
+                        (epg->stringForKey(KEY_EPG_END) != NULL))\r
+                    {\r
+                        contents += "<li>";\r
+                        contents += "<a href=\"#epg_";\r
+                        contents += epg->stringForKey(KEY_EPG_SERVICE_ID)->cString();\r
+                        contents += "_";\r
+                        contents += epg->stringForKey(KEY_EPG_EVENT_ID)->cString();\r
+                        contents += "\">";\r
+                        contents += epg->stringForKey(KEY_EPG_DATE)->substringFromIndex(5)->cString();\r
+                        contents += "&nbsp;";\r
+                        contents += epg->stringForKey(KEY_EPG_START)->substringToIndex(5)->cString();\r
+                        contents += "-";\r
+                        contents += epg->stringForKey(KEY_EPG_END)->substringToIndex(5)->cString();\r
+                        contents += "</a></li>";\r
+\r
+                        epgs += epg_regist_form(epg);\r
+                    }\r
+                }\r
+                contents += "</ul>";\r
+            }\r
+        }\r
+\r
+        contents += epgs;\r
+        html = html->stringByReplacingOccurrencesOfString("%%CONTENTS%%", contents.c_str());\r
+        if (html != NULL)\r
+        {\r
+            result = responseWithHTML(request, html);\r
+        }\r
+\r
+        break;\r
+    }    \r
+    return result;\r
+}\r
+\r
+HTTPResponse *Controller::responseForReservation(HTTPRequest *request, SOCKADDR_IN *client)\r
+{\r
+    DebugLog2("Controller::responseForReservation() start.");\r
+\r
+    // lock\r
+    RaymLock(this);\r
+\r
+    HTTPResponse *result = NULL;\r
+    while ((request != NULL) && (client != NULL))\r
+    {\r
+        // create array of reservations sorted by seq_id.\r
+        Array *array = Array::arrayWithCapacity(0);\r
+        for (int i = 0; i < _tunerCount; ++i)\r
+        {\r
+            Array *tmp = _reservations->arrayForKey(_tuners[i]->name());\r
+            if (tmp == NULL)\r
+            {\r
+                continue;\r
+            }\r
+            for (uint idx = 0; idx < tmp->count(); ++idx)\r
+            {\r
+                Dictionary *epg = (Dictionary *)tmp->objectAtIndex(idx);\r
+                if (epg == NULL)\r
+                {\r
+                    continue;\r
+                }\r
+\r
+                time_t epg_start;\r
+                time_t tmp_end;\r
+                getTimeWithEPG(epg, &epg_start, &tmp_end);\r
+\r
+                bool inserted = false;\r
+                for (uint j = 0; j < array->count(); ++j)\r
+                {\r
+                    Dictionary *epg2 = (Dictionary *)array->objectAtIndex(j);\r
+                    if (epg2 == NULL)\r
+                    {\r
+                        continue;\r
+                    }\r
+\r
+                    time_t epg2_start;\r
+                    getTimeWithEPG(epg2, &epg2_start, &tmp_end);\r
+\r
+                    if (epg_start <= epg2_start)\r
+                    {\r
+                        array->insertObject(epg, j);\r
+                        inserted = true;\r
+                        break;\r
+                    }\r
+                }\r
+\r
+                if (!inserted)\r
+                {\r
+                    array->addObject(epg);\r
+                }\r
+            }\r
+        }\r
+\r
+        String *path = _httpd->rootPath();\r
+        if (path == NULL)\r
+        {\r
+            break;\r
+        }\r
+\r
+        path = path->stringByAppendingPathComponent("template2.html");\r
+        if (path == NULL)\r
+        {\r
+            break;\r
+        }\r
+\r
+        String *html = String::stringWithContentsOfFile(path->cString(), UTF8StringEncoding);\r
+        if (html == NULL)\r
+        {\r
+            break;\r
+        }\r
+\r
+        html = html->stringByReplacingOccurrencesOfString("%%TITLE%%", LocalizedString(KEY_I18N_Reservation, NULL));\r
+        if (html == NULL)\r
+        {\r
+            break;\r
+        }\r
+\r
+        std::string contents;\r
+        std::string reservations;\r
+        contents += "<ul id=\"home\" title=\"";\r
+        contents += LocalizedString(KEY_I18N_Reservation, NULL)->cString();\r
+        contents += "\" selected=\"true\">";\r
+\r
+#if 0\r
+        contents += "<li><a href=\"#tuners\">";\r
+        contents += "<font color=\"blue\">[";\r
+        contents += LocalizedString(KEY_I18N_Tuner, NULL)->cString();\r
+        contents += "]</font>";\r
+        contents += "</a></li>";\r
+#endif\r
+\r
+        //\r
+        // "[新規予約]"\r
+        //\r
+        contents += "<li><a href=\"#new_reservation\">";\r
+        contents += "<font color=\"red\">[";\r
+        contents += LocalizedString(KEY_I18N_New_Reservation, NULL)->cString();\r
+        contents += "]</font>";\r
+        contents += "</a></li>";\r
+\r
+        //\r
+        // "[キーワード]"\r
+        //\r
+        contents += "<li><a href=\"#keywords\">";\r
+        contents += "<font color=\"blue\">[";\r
+        contents += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
+        contents += "]</font>";\r
+        contents += "</a></li>";\r
+\r
+        //\r
+        // 登録済み予約情報\r
+        //\r
+        for (uint i = 0; i < array->count(); ++i)\r
+        {\r
+            Dictionary *epg = (Dictionary *)array->objectAtIndex(i);\r
+            char seq_id[7];\r
+            sprintf_s(seq_id, "%06d", epg->integerForKey(KEY_EPG_RESV_ID) % 1000000);\r
+            contents += "<li>";\r
+            contents += "<a href=\"#seqid";\r
+            contents += seq_id;\r
+            contents += "\">";\r
+            String *title = epg->stringForKey(KEY_EPG_TITLE);\r
+            if (title != NULL)\r
+            {\r
+                contents += title->cString();\r
+            }\r
+            else\r
+            {\r
+                time_t start;\r
+                time_t end;\r
+                Controller::getTimeWithEPG(epg, &start, &end);\r
+                TM tm;\r
+                if (localtime_s(&tm, &start) == 0)\r
+                {\r
+                    char date_time[24];\r
+                    sprintf_s(date_time, "%04d/%02d/%02d %02d:%02d:%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);\r
+                    contents += date_time;\r
+                }\r
+                else\r
+                {\r
+                    contents += seq_id;\r
+                }\r
+            }\r
+            contents += "</a></li>";\r
+\r
+            // reservations\r
+            reservations += "<div id=\"";\r
+            reservations += "seqid";\r
+            reservations += seq_id;\r
+            reservations += "\" title=\"";\r
+            reservations += "xxxxxx";\r
+            reservations += "\" class=\"panel\">";\r
+\r
+            //reservations += "<h2>Status</h2>";\r
+            reservations += "<fieldset>";\r
+\r
+            //\r
+            reservations += "<div class=\"row\">";\r
+            reservations += "<label>Date</label>";\r
+            reservations += "<span>";\r
+            String *tmp = epg->stringForKey(KEY_EPG_DATE);\r
+            reservations += tmp->cString();\r
+            reservations += "</span>";\r
+            reservations += "</div>";\r
+\r
+            reservations += "<div class=\"row\">";\r
+            reservations += "<label>Start</label>";\r
+            reservations += "<span>";\r
+            tmp = epg->stringForKey(KEY_EPG_START);\r
+            reservations += tmp->cString();\r
+            reservations += "</span>";\r
+            reservations += "</div>";\r
+\r
+            reservations += "<div class=\"row\">";\r
+            reservations += "<label>End</label>";\r
+            reservations += "<span>";\r
+            tmp = epg->stringForKey(KEY_EPG_END);\r
+            reservations += tmp->cString();\r
+            reservations += "</span>";\r
+            reservations += "</div>";\r
+\r
+            reservations += "</fieldset>";\r
+\r
+            reservations += "<a class=\"whiteButton\" type=\"submit\" href=\"/cancel.cgi?";\r
+            reservations += "resv_id=";\r
+            reservations += seq_id;\r
+            reservations += "\" onclick='return confirm(\"To cancel. Is it ok?\");'>";\r
+            reservations += LocalizedString(KEY_I18N_Cancel, NULL)->cString();\r
+            reservations += "</a>";\r
+\r
+            reservations += "</div>";\r
+        }\r
+\r
+        contents += "</ul>";\r
+        contents += reservations;\r
+\r
+        //\r
+        // 新規予約:ここから\r
+        //\r
+\r
+        // 予約時間の初期値作成\r
+        TM tm1, tm2;\r
+        time_t time1 = time(NULL);\r
+        time1 = (time1 + 15 * 60) / (15 * 60);\r
+        time1 = time1 * 15 * 60;\r
+        time_t time2 = time1 + 15 * 60;\r
+        localtime_s(&tm1, &time1);\r
+        localtime_s(&tm2, &time2);\r
+        \r
+        contents += "<form name=\"reservation\" id=\"new_reservation\" class=\"panel\" method=\"GET\" action=\"regist.cgi\" target=\"_self\">";\r
+        contents += "<fieldset>";\r
+        contents += "<div class=\"row\">";\r
+        contents += "<label>";\r
+        contents += LocalizedString(KEY_I18N_Station_Name, NULL)->cString();\r
+        contents += " : </label>";\r
+        contents += "<span>";\r
+        contents += "<select name=\"service_id\" size=\"1\">";\r
+\r
+        // 局情報の収集\r
+        Array *stationInfos = Controller::stationInfos(Tuner::ISDB_T);\r
+        if (stationInfos != NULL)\r
+        {\r
+            stationInfos->addObjectsFromArray(Controller::stationInfos(Tuner::ISDB_S));\r
+        }\r
+        else\r
+        {\r
+            stationInfos = Controller::stationInfos(Tuner::ISDB_S);\r
+        }\r
+        for (uint i = 0; (stationInfos != NULL) && (i < stationInfos->count()); ++i)\r
+        {\r
+            Array *services = ((Dictionary *)stationInfos->objectAtIndex(i))->arrayForKey(KEY_SERVICES);\r
+\r
+// とりえあず、現状は最初のサービスのみ表示\r
+//                for (uint j = 0; (services != NULL) &&  (j < services->count()); ++j)\r
+            for (uint j = 0; (services != NULL) &&  (j < 1); ++j)\r
+            {\r
+                Dictionary *service = (Dictionary *)services->objectAtIndex(j);\r
+\r
+                // テレビ放送かチェック\r
+                if ((service->stringForKey(KEY_SERVICE_TYPE) != NULL) && service->stringForKey(KEY_SERVICE_TYPE)->isEqualToString("1"))\r
+                {\r
+                    String *name = service->stringForKey(KEY_NAME);\r
+                    String *sid  = service->stringForKey(KEY_SERVICE_ID);\r
+                    if ((name != NULL) && (sid != NULL))\r
+                    {\r
+                        String *tmp = String::stringWithFormat("<option value=\"%s\">%s[%s]</option>", sid->cString(), name->cString(), sid->cString());\r
+                        contents += tmp->cString();\r
+                    }\r
+                }\r
+            }\r
+        }\r
+\r
+        contents += "</select>";\r
+        contents += "</span>";\r
+        contents += "</div>";\r
+        contents += "<div class=\"row\">";\r
+        contents += "<label>";\r
+        contents += LocalizedString(KEY_I18N_Date, NULL)->cString();\r
+        contents += " : </label>";\r
+        contents += "<span>";\r
+        contents += "<select name=\"year\" size=\"1\">";\r
+        for (int year = tm1.tm_year + 1900; year < tm1.tm_year + 1900 + 3; ++year)\r
+        {\r
+            char year_buf[64];\r
+            sprintf_s(year_buf, "<option value=\"%d\">%d</option>", year, year);\r
+            contents += year_buf;\r
+        }\r
+        contents += "</select>";\r
+        contents += "<select name=\"month\" size=\"1\">";\r
+        for (int month = 1; month < 13; ++month)\r
+        {\r
+            char month_buf[64];\r
+            if (month == tm1.tm_mon + 1)\r
+            {\r
+                sprintf_s(month_buf, "<option value=\"%d\" selected>%d</option>", month, month);\r
+            }\r
+            else\r
+            {\r
+                sprintf_s(month_buf, "<option value=\"%d\">%d</option>", month, month);\r
+            }\r
+            contents += month_buf;\r
+        }\r
+        contents += "</select>";\r
+        contents += "<select name=\"day\" size=\"1\">";\r
+        for (int day = 1; day < 32; ++day)\r
+        {\r
+            char day_buf[64];\r
+            if (day == tm1.tm_mday)\r
+            {\r
+                sprintf_s(day_buf, "<option value=\"%d\" selected>%d</option>", day, day);\r
+            }\r
+            else\r
+            {\r
+                sprintf_s(day_buf, "<option value=\"%d\">%d</option>", day, day);\r
+            }\r
+            contents += day_buf;\r
+        }\r
+        contents += "</select>";\r
+        contents += "</span>";\r
+        contents += "</div>";\r
+        contents += "<div class=\"row\">";\r
+        contents += "<label>";\r
+        contents += LocalizedString(KEY_I18N_Start_Time, NULL)->cString();\r
+        contents += " : </label>";\r
+        contents += "<span>";\r
+        contents += "<select name=\"start_hour\" size=\"1\">";\r
+        for (int sh = 0; sh < 24; ++sh)\r
+        {\r
+            char sh_buf[64];\r
+            if (sh == tm1.tm_hour)\r
+            {\r
+                sprintf_s(sh_buf, "<option value=\"%02d\" selected>%02d</option>", sh, sh);\r
+            }\r
+            else\r
+            {\r
+                sprintf_s(sh_buf, "<option value=\"%02d\">%02d</option>", sh, sh);\r
+            }\r
+            contents += sh_buf;\r
+        }\r
+        contents += "</select>";\r
+        contents += "<select name=\"start_min\" size=\"1\">";\r
+        for (int sm = 0; sm < 60; ++sm)\r
+        {\r
+            char sm_buf[64];\r
+            if (sm == tm1.tm_min)\r
+            {\r
+                sprintf_s(sm_buf, "<option value=\"%02d\" selected>%02d</option>", sm, sm);\r
+            }\r
+            else\r
+            {\r
+                sprintf_s(sm_buf, "<option value=\"%02d\">%02d</option>", sm, sm);\r
+            }\r
+            contents += sm_buf;\r
+        }\r
+        contents += "</select>";\r
+        contents += "</span>";\r
+        contents += "</div>";\r
+        contents += "<div class=\"row\">";\r
+        contents += "<label>";\r
+        contents += LocalizedString(KEY_I18N_End_Time, NULL)->cString();\r
+        contents += " : </label>";\r
+        contents += "<span>";\r
+        contents += "<select name=\"end_hour\" size=\"1\">";\r
+        for (int eh = 0; eh < 24; ++eh)\r
+        {\r
+            char eh_buf[64];\r
+            if (eh == tm2.tm_hour)\r
+            {\r
+                sprintf_s(eh_buf, "<option value=\"%02d\" selected>%02d</option>", eh, eh);\r
+            }\r
+            else\r
+            {\r
+                sprintf_s(eh_buf, "<option value=\"%02d\">%02d</option>", eh, eh);\r
+            }\r
+            contents += eh_buf;\r
+        }\r
+        contents += "</select>";\r
+        contents += "<select name=\"end_min\" size=\"1\">";\r
+        for (int em = 0; em < 60; ++em)\r
+        {\r
+            char em_buf[64];\r
+            if (em == tm2.tm_min)\r
+            {\r
+                sprintf_s(em_buf, "<option value=\"%02d\" selected>%02d</option>", em, em);\r
+            }\r
+            else\r
+            {\r
+                sprintf_s(em_buf, "<option value=\"%02d\">%02d</option>", em, em);\r
+            }\r
+            contents += em_buf;\r
+        }\r
+        contents += "</select>";\r
+        contents += "</span>";\r
+        contents += "</div>";\r
+        contents += "<div class=\"row\">";\r
+        contents += "<label>";\r
+        contents += LocalizedString(KEY_I18N_Repeat, NULL)->cString();\r
+        contents += " : </label>";\r
+        contents += "<span>";\r
+        contents += "<select name=\"repeat\" size=\"1\">";\r
+        contents += "<option value=\"off\" selected>";\r
+        contents += LocalizedString(KEY_I18N_Repeat_off, NULL)->cString();\r
+        contents += "</option>";\r
+        contents += "<option value=\"everyday\">";\r
+        contents += LocalizedString(KEY_I18N_Repeat_everyday, NULL)->cString();\r
+        contents += "</option>";\r
+        contents += "<option value=\"weekly\">";\r
+        contents += LocalizedString(KEY_I18N_Repeat_weekly, NULL)->cString();\r
+        contents += "</option>";\r
+        contents += "<option value=\"weekday\">";\r
+        contents += LocalizedString(KEY_I18N_Repeat_weekday, NULL)->cString();\r
+        contents += "</option>";\r
+        contents += "</select>";\r
+        contents += "</span>";\r
+        contents += "</div>";\r
+        contents += "</fieldset>";\r
+\r
+        contents += "<input type=\"submit\" class=\"whiteButton\" value=\"";\r
+        contents += LocalizedString(KEY_I18N_Registration, NULL)->cString();\r
+        contents += "\">";\r
+        contents += "</form>";\r
+\r
+        //\r
+        // 新規予約:ここまで\r
+        //\r
+\r
+\r
+        //\r
+        // チューナ:ここから\r
+        //\r
+        contents += "<ul id=\"tuners\" title=\"";\r
+        contents += LocalizedString(KEY_I18N_Tuner, NULL)->cString();\r
+        contents += "\">";\r
+\r
+        std::string controls;\r
+        Dictionary *tunerInfos = _props->dictionaryForKey(KEY_TUNERS);\r
+        if (tunerInfos != NULL)\r
+        {\r
+            for (int i = 0; i < _tunerCount; ++i)\r
+            {\r
+                Dictionary *tunerInfo = tunerInfos->dictionaryForKey(_tuners[i]->name());\r
+                if (tunerInfo != NULL)\r
+                {\r
+                    if (tunerInfo->boolForKey(KEY_INITIALIZED) && tunerInfo->boolForKey(KEY_ENABLED))\r
+                    {\r
+                        char key[4];\r
+                        sprintf_s(key, "%03d", i);\r
+\r
+                        // tuner list\r
+                        contents += "<li>";\r
+                        contents += "<a href=\"#tuner";\r
+                        contents += key;\r
+                        contents += "\">";\r
+                        contents += key;\r
+                        contents += ": ";\r
+                        Dictionary *dict = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
+                        if (dict != NULL)\r
+                        {\r
+                            char chkey[4];\r
+                            sprintf_s(chkey, "%03d", tunerInfo->integerForKey(KEY_CHANNEL));\r
+                            dict = dict->dictionaryForKey(chkey);\r
+                            String *name = dict->stringForKey(KEY_NAME);\r
+                            if (name != NULL)\r
+                            {\r
+                                contents += name->cString();\r
+                            }\r
+                            else\r
+                            {\r
+                                dict = NULL;\r
+                            }\r
+                        }\r
+                        if (dict == NULL)\r
+                        {\r
+                            contents += _tuners[i]->name();\r
+                        }\r
+                        contents += "</a></li>";\r
+\r
+                        // controls\r
+                        controls += "<form name=\"control\" id=\"tuner";\r
+                        controls += key;\r
+                        controls += "\" class=\"panel\" method=\"GET\" action=\"/";\r
+                        controls += key;\r
+                        controls += "/recording=on\" target=\"_self\">";\r
+\r
+                        // 白い枠:ここから\r
+                        controls += "<fieldset>";\r
+\r
+                        // 局指定:ここから\r
+                        controls += "<div class=\"row\">";\r
+                        controls += "<label>";\r
+                        controls += LocalizedString(KEY_I18N_Station_Name, NULL)->cString();\r
+                        controls += " : </label>";\r
+                        controls += "<span>";\r
+                        controls += "<select name=\"channel\" size=\"1\">";\r
+                        Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
+                        if (channels != NULL)\r
+                        {\r
+                            for (int ch = 0; ch <= Tuner::MAX_CHANNELS_ISDB_T; ++ch)\r
+                            {\r
+                                char chkey[4];\r
+                                sprintf_s(chkey, "%03d", ch);\r
+                                dict = channels->dictionaryForKey(chkey);\r
+                                if (dict == NULL)\r
+                                {\r
+                                    break;\r
+                                }\r
+                                Array *services = (Array *)dict->objectForKey(KEY_SERVICES);\r
+                                if (services != NULL)\r
+                                {\r
+                                    for (uint s = 0; s < services->count(); ++s)\r
+                                    {\r
+                                        Dictionary *service = (Dictionary *)services->objectAtIndex(s);\r
+                                        if (service != NULL)\r
+                                        {\r
+                                            char sid[8];\r
+                                            sprintf_s(sid, "%d", service->integerForKey(KEY_SERVICE_ID));\r
+                                            controls += "<option value=\"";\r
+                                            controls += chkey;\r
+                                            controls += "\"";\r
+                                            // selected\r
+                                            controls += ">";\r
+                                            String *name = service->stringForKey(KEY_NAME);\r
+                                            if (name != NULL)\r
+                                            {\r
+                                                controls += name->cString();\r
+                                            }\r
+                                            controls += "[";\r
+                                            controls += sid;\r
+                                            controls += "]</option>";\r
+                                        }\r
+                                    }\r
+                                }\r
+                            }\r
+                        }\r
+                        controls += "</select>";\r
+                        controls += "</span>";\r
+                        controls += "</div>";\r
+                        // 局指定:ここまで\r
+\r
+                        // 時間指定:ここから\r
+                        controls += "<div class=\"row\">";\r
+                        controls += "<label>";\r
+                        controls += LocalizedString(KEY_I18N_Time, NULL)->cString();\r
+                        controls += " : </label>";\r
+                        controls += "<span>";\r
+                        controls += "<select name=\"hour\" size=\"1\">";\r
+                        for (int sh = 0; sh < 24; ++sh)\r
+                        {\r
+                            char sh_buf[64];\r
+                            if (sh == 1)\r
+                            {\r
+                                sprintf_s(sh_buf, "<option value=\"%02d\" selected>%02d</option>", sh, sh);\r
+                            }\r
+                            else\r
+                            {\r
+                                sprintf_s(sh_buf, "<option value=\"%02d\">%02d</option>", sh, sh);\r
+                            }\r
+                            controls += sh_buf;\r
+                        }\r
+                        controls += "</select>";\r
+                        controls += "<select name=\"min\" size=\"1\">";\r
+                        for (int sm = 0; sm < 60; ++sm)\r
+                        {\r
+                            char sm_buf[64];\r
+                            if (sm == 0)\r
+                            {\r
+                                sprintf_s(sm_buf, "<option value=\"%02d\" selected>%02d</option>", sm, sm);\r
+                            }\r
+                            else\r
+                            {\r
+                                sprintf_s(sm_buf, "<option value=\"%02d\">%02d</option>", sm, sm);\r
+                            }\r
+                            controls += sm_buf;\r
+                        }\r
+                        controls += "</select>";\r
+                        controls += "</span>";\r
+                        controls += "</div>";\r
+                        // 時間指定:ここまで\r
+\r
+                        controls += "</fieldset>";\r
+                        // 白い枠:ここまで\r
+\r
+                        controls += "<input type=\"submit\" class=\"whiteButton\" value=\"";\r
+                        controls += LocalizedString(KEY_I18N_Registration, NULL)->cString();\r
+                        controls += "\">";\r
+                        controls += "</form>";\r
+                    }\r
+                }\r
+            }\r
+        }\r
+        contents += "</ul>";\r
+        contents += controls;\r
+\r
+        //\r
+        // チューナ:ここまで\r
+        //\r
+\r
+        //\r
+        // キーワード:ここから\r
+        //\r
+        contents += "<ul id=\"keywords\" title=\"";\r
+        contents += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
+        contents += "\">";\r
+\r
+        contents += "<li><a href=\"#dialogForm\">";\r
+        contents += "<font color=\"red\">[";\r
+//        contents += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
+        contents += LocalizedString(KEY_I18N_Add, NULL)->cString();\r
+        contents += "]</font>";\r
+        contents += "</a></li>";\r
+\r
+        // 登録済みキーワード\r
+        Dictionary *keywords_info = _reservations->dictionaryForKey(KEY_EPG_KEYWORDS);\r
+        if (keywords_info != NULL)\r
+        {\r
+            controls = "";\r
+            Array *keys = keywords_info->allKeys();\r
+            if (keys != NULL)\r
+            {\r
+                for (uint i = 0; i < keys->count(); ++i)\r
+                {\r
+                    Dictionary *kwd_inf = keywords_info->dictionaryForKey((String *)keys->objectAtIndex(i));\r
+                    if (kwd_inf == NULL)\r
+                    {\r
+                        continue;\r
+                    }\r
+\r
+                    char tmp[32];\r
+                    sprintf_s(tmp, "%d", i);\r
+                    contents += "<li><a href=\"#keywords_";\r
+                    contents += tmp;\r
+                    contents += "\">";\r
+                    contents += ((String *)keys->objectAtIndex(i))->cString();\r
+                    contents += "</a></li>";\r
+\r
+                    controls += "<form name=\"filter\" id=\"keywords_";\r
+                    controls += tmp;\r
+                    controls += "\" class=\"panel\" method=\"GET\" action=\"mod_keywords.cgi\" target=\"_self\">";\r
+                    controls += "<input type=\"hidden\" name=\"keywords\" value=\"";\r
+                    controls += ((String *)keys->objectAtIndex(i))->cString();\r
+                    controls += "\">";\r
+                    controls += "<fieldset>";\r
+\r
+                    controls += "<div class=\"row\">";\r
+                    controls += "<label>";\r
+                    controls += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
+                    controls += " : </label>";\r
+                    controls += "<span>";\r
+                    controls += ((String *)keys->objectAtIndex(i))->cString();\r
+                    controls += "</span>";\r
+                    controls += "</div>";\r
+\r
+                    controls += "<div class=\"row\">";\r
+                    controls += "<label>";\r
+                    controls += LocalizedString(KEY_I18N_Station_Name, NULL)->cString();\r
+                    controls += " : </label>";\r
+                    controls += "<span>";\r
+                    controls += "<select name=\"service_id\" size=\"1\">";\r
+                    controls += "<option value=\"-\">----</option>";\r
+\r
+                    String *service_id = kwd_inf->stringForKey(KEY_EPG_SERVICE_ID);\r
+\r
+                    // 局情報の収集\r
+                    Array *stationInfos = Controller::stationInfos(Tuner::ISDB_T);\r
+                    if (stationInfos != NULL)\r
+                    {\r
+                        stationInfos->addObjectsFromArray(Controller::stationInfos(Tuner::ISDB_S));\r
+                    }\r
+                    else\r
+                    {\r
+                        stationInfos = Controller::stationInfos(Tuner::ISDB_S);\r
+                    }\r
+                    for (uint i = 0; (stationInfos != NULL) && (i < stationInfos->count()); ++i)\r
+                    {\r
+                        Array *services = ((Dictionary *)stationInfos->objectAtIndex(i))->arrayForKey(KEY_SERVICES);\r
+\r
+                        for (uint j = 0; (services != NULL) &&  (j < 1); ++j)\r
+                        {\r
+                            Dictionary *service = (Dictionary *)services->objectAtIndex(j);\r
+\r
+                            // テレビ放送かチェック\r
+                            if ((service->stringForKey(KEY_SERVICE_TYPE) != NULL) && service->stringForKey(KEY_SERVICE_TYPE)->isEqualToString("1"))\r
+                            {\r
+                                String *name = service->stringForKey(KEY_NAME);\r
+                                String *sid  = service->stringForKey(KEY_SERVICE_ID);\r
+                                if ((name != NULL) && (sid != NULL))\r
+                                {\r
+                                    if ((service_id != NULL) && (service_id->isEqualToString(sid)))\r
+                                    {\r
+                                        String *tmp = String::stringWithFormat("<option value=\"%s\" selected>%s[%s]</option>", sid->cString(), name->cString(), sid->cString());\r
+                                        controls += tmp->cString();\r
+                                    }\r
+                                    else\r
+                                    {\r
+                                        String *tmp = String::stringWithFormat("<option value=\"%s\">%s[%s]</option>", sid->cString(), name->cString(), sid->cString());\r
+                                        controls += tmp->cString();\r
+                                    }\r
+                                }\r
+                            }\r
+                        }\r
+                    }\r
+\r
+                    controls += "</select>";\r
+                    controls += "</span>";\r
+                    controls += "</div>";\r
+\r
+                    controls += "<div class=\"row\">";\r
+                    controls += "<label>";\r
+                    controls += LocalizedString(KEY_I18N_Start_Time, NULL)->cString();\r
+                    controls += " : </label>";\r
+                    controls += "<span>";\r
+                    controls += "<select name=\"start_hour\" size=\"1\">";\r
+                    controls += "<option value=\"-\">--</option>";\r
+                    String *sh_str = kwd_inf->stringForKey(KEY_EPG_START);\r
+                    for (int sh = 0; sh < 24; ++sh)\r
+                    {\r
+                        char sh_buf[64];\r
+                        if ((sh_str != NULL) && (sh_str->length() == 5) && (sh_str->substringToIndex(2)->intValue() == sh))\r
+                        {\r
+                            sprintf_s(sh_buf, "<option value=\"%02d\" selected>%02d</option>", sh, sh);\r
+                        }\r
+                        else\r
+                        {\r
+                            sprintf_s(sh_buf, "<option value=\"%02d\">%02d</option>", sh, sh);\r
+                        }\r
+                        controls += sh_buf;\r
+                    }\r
+                    controls += "</select>";\r
+                    controls += "<select name=\"start_min\" size=\"1\">";\r
+                    controls += "<option value=\"-\">--</option>";\r
+                    for (int sm = 0; sm < 60; ++sm)\r
+                    {\r
+                        char sm_buf[64];\r
+                        if ((sh_str != NULL) && (sh_str->length() == 5) && (sh_str->substringFromIndex(3)->intValue() == sm))\r
+                        {\r
+                            sprintf_s(sm_buf, "<option value=\"%02d\" selected>%02d</option>", sm, sm);\r
+                        }\r
+                        else\r
+                        {\r
+                            sprintf_s(sm_buf, "<option value=\"%02d\">%02d</option>", sm, sm);\r
+                        }\r
+                        controls += sm_buf;\r
+                    }\r
+                    controls += "</select>";\r
+                    controls += "</span>";\r
+                    controls += "</div>";\r
+\r
+                    controls += "</fieldset>";\r
+\r
+                    controls += "<input type=\"submit\" class=\"whiteButton\" name=\"req_mod\" value=\"";\r
+                    controls += LocalizedString(KEY_I18N_Registration, NULL)->cString();\r
+                    controls += "\">";\r
+\r
+                    controls += "<input type=\"submit\" class=\"redButton\" name=\"req_del\" value=\"";\r
+                    controls += LocalizedString(KEY_I18N_Delete, NULL)->cString();\r
+//                    controls += "\">";\r
+                    controls += "\" onclick='return confirm(\"To delete. Is it ok?\");'>";\r
+\r
+                    controls += "</form>";\r
+                }\r
+            }\r
+        }\r
+\r
+        contents += "</ul>";\r
+        contents += controls;\r
+\r
+        controls = "<form id=\"dialogForm\" title=\"";\r
+        controls += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
+        controls += LocalizedString(KEY_I18N_Add, NULL)->cString();\r
+        controls += "\" class=\"dialog\" target=\"_self\" action=\"add_keywords.cgi\" method=\"GET\">";\r
+        controls += "<fieldset>";\r
+        controls += "<h1>";\r
+        controls += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
+        controls += LocalizedString(KEY_I18N_Add, NULL)->cString();\r
+        controls += "</h1>";\r
+        controls += "<a class=\"button leftButton\" type=\"cancel\">Cancel</a>";\r
+        controls += "<a class=\"button blueButton\" type=\"submit\">Submit</a>";\r
+        controls += "<label>Parm1:</label>";\r
+        controls += "<input type=\"text\" name=\"keywords\" value=\"";\r
+//        controls += LocalizedString(KEY_I18N_Keywords, NULL)->cString();\r
+        controls += "\"/>";\r
+        controls += "</fieldset>";\r
+        controls += "<div class=\"spinner\"></div>";\r
+        controls += "</form>";\r
+\r
+        contents += controls;\r
+        //\r
+        // キーワード:ここまで\r
+        //\r
+\r
+        html = html->stringByReplacingOccurrencesOfString("%%CONTENTS%%", contents.c_str());\r
+        if (html != NULL)\r
+        {\r
+            result = responseWithHTML(request, html);\r
+        }\r
+\r
+        break;\r
+    }\r
+\r
+    // unlock\r
+    RaymUnlock(this);\r
+\r
+    return result;\r
+}\r
+\r
+HTTPResponse *Controller::responseForStatus(HTTPRequest *request, SOCKADDR_IN *client)\r
+{\r
+    HTTPResponse *result = NULL;\r
+    if ((request != NULL) && (client != NULL))\r
+    {\r
+        String *path = _httpd->rootPath()->stringByAppendingPathComponent("template2.html");\r
+        String *html = String::stringWithContentsOfFile(path->cString(), UTF8StringEncoding);\r
+        if (html != NULL)\r
+        {\r
+            html = html->stringByReplacingOccurrencesOfString("%%TITLE%%", LocalizedString(KEY_I18N_Tuner_Status, NULL));\r
+            std::string contents;\r
+            std::string status;\r
+            contents += "<ul id=\"home\" title=\"";\r
+            contents += LocalizedString(KEY_I18N_Tuner_Status, NULL)->cString();\r
+            contents += "\" selected=\"true\">";\r
+            DebugLog2("_tunerCount = %d", _tunerCount);\r
+            for (int i = 0; i < _tunerCount; ++i)\r
+            {\r
+                char key[4];\r
+                sprintf_s(key, sizeof(key), "%03d", i);\r
+\r
+                if (isTunerInitialized(i))\r
+                {\r
+                    contents += "<li>";\r
+                    contents += "<a href=\"#tuner";\r
+                    contents += key;\r
+                    contents += "\">";\r
+                    contents += key;\r
+                    contents += ": ";\r
+                    contents += _tuners[i]->name();\r
+                    contents += "</a></li>";\r
+\r
+                    //\r
+                    status += "<div id=\"";\r
+                    status += "tuner";\r
+                    status += key;\r
+                    status += "\" title=\"";\r
+                    status += _tuners[i]->name();\r
+                    status += "\" class=\"panel\">";\r
+\r
+                    status += "<h2>Status</h2>";\r
+                    status += "<fieldset>";\r
+\r
+                    // Tuner Type\r
+                    status += "<div class=\"row\">";\r
+                    status += "<label>Type</label>";\r
+                    status += "<span>";\r
+                    switch (_tuners[i]->type())\r
+                    {\r
+                    case Tuner::ISDB_S:\r
+                        status += "ISDB-S";\r
+                        break;\r
+                    case Tuner::ISDB_T:\r
+                        status += "ISDB-T";\r
+                        break;\r
+                    case Tuner::TYPE_NA:\r
+                    default:\r
+                        status += "N/A";\r
+                        break;\r
+                    }\r
+                    status += "</span>";\r
+                    status += "</div>";\r
+\r
+                    // LnbPower\r
+                    status += "<div class=\"row\">";\r
+                    status += "<label>LnbPower</label>";\r
+                    status += "<span>";\r
+                    switch (_tuners[i]->lnbPower())\r
+                    {\r
+                    case Tuner::LNB_POWER_11V:\r
+                        status += "11V";\r
+                        break;\r
+                    case Tuner::LNB_POWER_15V:\r
+                        status += "15V";\r
+                        break;\r
+                    case Tuner::LNB_POWER_OFF:\r
+                    default:\r
+                        status += "OFF";\r
+                        break;\r
+                    }\r
+                    status += "</span>";\r
+                    status += "</div>";\r
+\r
+                    // Channel\r
+                    char tmpstr[32];\r
+                    status += "<div class=\"row\">";\r
+                    status += "<label>Channel</label>";\r
+                    status += "<span>";\r
+                    sprintf_s(tmpstr, sizeof(tmpstr), "%03d", _tuners[i]->channel());\r
+                    status += tmpstr;\r
+                    status += "</span>";\r
+                    status += "</div>";\r
+\r
+                    // C/N[dB] AGC xxx/255\r
+                    uint32_t cn100 = 0;\r
+                    uint32_t agc = 0;\r
+                    uint32_t maxAgc = 0;\r
+                    _tuners[i]->getCnAgc(&cn100, &agc, &maxAgc);\r
+\r
+                    sprintf_s(tmpstr, sizeof(tmpstr), "%d.%02d[dB]", cn100 / 100, cn100 % 100);\r
+\r
+                    status += "<div class=\"row\">";\r
+                    status += "<label>C/N</label>";\r
+                    status += "<span>";\r
+                    status += tmpstr;\r
+                    status += "</span>";\r
+                    status += "</div>";\r
+\r
+                    sprintf_s(tmpstr, sizeof(tmpstr), "%03d/%03d", agc, maxAgc);\r
+\r
+                    status += "<div class=\"row\">";\r
+                    status += "<label>AGC</label>";\r
+                    status += "<span>";\r
+                    status += tmpstr;\r
+                    status += "</span>";\r
+                    status += "</div>";\r
+\r
+                    status += "</fieldset>";\r
+                    status += "</div>";\r
+\r
+                }\r
+                else\r
+                {\r
+                    contents += "<li>";\r
+                    contents += _tuners[i]->name();\r
+                    contents += "[uninitialized]";\r
+                    contents += "</li>";\r
+                }\r
+            }\r
+            contents += "</ul>";\r
+            contents += status;\r
+            html = html->stringByReplacingOccurrencesOfString("%%CONTENTS%%", contents.c_str());\r
+            result = responseWithHTML(request, html);\r
+        }\r
+        else\r
+        {\r
+            DebugLog2("responseForStatus() html is null\n");\r
+        }\r
+    }\r
+    return result;\r
+}\r
+\r
+HTTPResponse *Controller::responseForRegistCGI(HTTPRequest *request, SOCKADDR_IN *client)\r
+{\r
+    DebugLog2("Controller::responseForRegistCGI()");\r
+\r
+    HTTPResponse *result = NULL;\r
+\r
+    // CGIリクエストとして解析\r
+    Dictionary *cgi = request->parseAsCGI();\r
+    if (cgi != NULL)\r
+    {\r
+        // CGIパスが一致しているか\r
+        if ((cgi->stringForKey(HTTPRequest::KEY_CGI) != NULL) && (cgi->stringForKey(HTTPRequest::KEY_CGI)->isEqualToString("/regist.cgi")))\r
+        {\r
+            // パラメータがあるか\r
+            Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
+            if (params != NULL)\r
+            {\r
+                // パラメータ数が2か\r
+                if (params->count() == 2)\r
+                {\r
+                    // パラメータのチェック\r
+                    String *service_id = NULL;\r
+                    String *event_id = NULL;\r
+\r
+                    for (uint i = 0; i < params->count(); ++i)\r
+                    {\r
+                        Dictionary *param = (Dictionary *)params->objectAtIndex(i);\r
+                        String *value = param->stringForKey("service_id");\r
+                        if ((value != NULL) && value->isMatch("^\\d+$"))\r
+                        {\r
+                            service_id = value;\r
+                        }\r
+                        value = param->stringForKey("event_id");\r
+                        if ((value != NULL) && value->isMatch("^\\d+$"))\r
+                        {\r
+                            event_id = value;\r
+                        }\r
+                    }\r
+\r
+                    // 有効なパラメータか\r
+                    if ((service_id != NULL) && (event_id != NULL))\r
+                    {\r
+                        DebugLog2("valid request");\r
+\r
+                        result = responseByResultAndReferer(request, reserve(service_id->intValue(), event_id->intValue()), URI_PROGRAMS_HTML);\r
+                    }\r
+                }\r
+\r
+                // パラメータ数が9か\r
+                else if (params->count() == 9)\r
+                {\r
+                    // パラメータのチェック\r
+                    String *service_id = NULL;\r
+                    String *year       = NULL;\r
+                    String *month      = NULL;\r
+                    String *day        = NULL;\r
+                    String *start_hour = NULL;\r
+                    String *start_min  = NULL;\r
+                    String *end_hour   = NULL;\r
+                    String *end_min    = NULL;\r
+                    String *repeat     = NULL;\r
+\r
+                    struct {\r
+                        const char *name;\r
+                        String **variable;\r
+                        const char *regex;\r
+                    }\r
+                    cgi[] =\r
+                    {\r
+                        {"service_id", &service_id, "^\\d+$"},\r
+                        {"year",       &year,       "^\\d{4}$"},\r
+                        {"month",      &month,      "^([1-9]|1[0-2])$"},\r
+                        {"day",        &day,        "^([1-9]|[12][0-9]|3[01])$"},\r
+                        {"start_hour", &start_hour, "^\\d{2}$"},\r
+                        {"start_min",  &start_min,  "^\\d{2}$"},\r
+                        {"end_hour",   &end_hour,   "^\\d{2}$"},\r
+                        {"end_min",    &end_min,    "^\\d{2}$"},\r
+                        {"repeat",     &repeat,     "^(off|everyday|weekly|weekday)$"},\r
+                        {NULL, NULL, NULL}\r
+                    };\r
+\r
+                    for (uint i = 0; cgi[i].name != NULL; ++i)\r
+                    {\r
+                        for (uint j = 0; j < params->count(); ++j)\r
+                        {\r
+                            Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
+                            String *value = param->stringForKey(cgi[i].name);\r
+                            if ((value != NULL) && value->isMatch(cgi[i].regex))\r
+                            {\r
+                                *(cgi[i].variable) = value;\r
+                                break;\r
+                            }\r
+                        }\r
+                    }\r
+\r
+                    // 有効なパラメータか\r
+                    if ((service_id != NULL) && (year != NULL) && (month != NULL) && (day != NULL) &&\r
+                        (start_hour != NULL) && (start_min != NULL) && (end_hour != NULL) && (end_min != NULL) && (repeat != NULL))\r
+                    {\r
+                        //\r
+                        DebugLog1("valid param");\r
+\r
+                        Dictionary *epg = Dictionary::dictionaryWithCapacity(0);\r
+\r
+                        // 日付\r
+                        epg->setString(String::stringWithFormat("%s/%02d/%02d", year->cString(), month->intValue(), day->intValue()), KEY_EPG_DATE);\r
+\r
+                        // 開始時刻\r
+                        epg->setString(String::stringWithFormat("%s:%s:00", start_hour->cString(), start_min->cString()), KEY_EPG_START);\r
+\r
+                        // 終了時刻\r
+                        epg->setString(String::stringWithFormat("%s:%s:00", end_hour->cString(), end_min->cString()), KEY_EPG_END);\r
+\r
+                        // 繰り返し\r
+                        epg->setString(repeat, KEY_EPG_REPEAT);\r
+\r
+                        // Service ID\r
+                        epg->setString(service_id, KEY_EPG_SERVICE_ID);\r
+\r
+                        // Status\r
+                        epg->setString("ready", KEY_EPG_STATUS);\r
+\r
+                        result = responseByResultAndReferer(request, reserve(epg), URI_RESERVATION_HTML);\r
+                    }\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+    return result;\r
+}\r
+\r
+HTTPResponse *Controller::responseForCancelCGI(HTTPRequest *request, SOCKADDR_IN *client)\r
+{\r
+    DebugLog2("Controller::responseForCancelCGI()");\r
+\r
+    HTTPResponse *result = NULL;\r
+\r
+    // CGIリクエストとして解析\r
+    Dictionary *cgi = request->parseAsCGI();\r
+    if (cgi != NULL)\r
+    {\r
+        // CGIパスが一致しているか\r
+        if ((cgi->stringForKey(HTTPRequest::KEY_CGI) != NULL) && (cgi->stringForKey(HTTPRequest::KEY_CGI)->isEqualToString("/cancel.cgi")))\r
+        {\r
+            // パラメータがあるか\r
+            Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
+            if (params != NULL)\r
+            {\r
+                // パラメータ数が1か\r
+                if (params->count() == 1)\r
+                {\r
+                    Dictionary *param = (Dictionary *)params->objectAtIndex(0);\r
+                    String *value = param->stringForKey("resv_id");\r
+                    if ((value != NULL) && value->isMatch("^\\d{6}$"))\r
+                    {\r
+                        result = responseByResultAndReferer(request, cancel(-1, value->intValue()), URI_RESERVATION_HTML);\r
+                    }\r
+                }\r
+            }\r
+        }\r
+    }\r
+    return result;\r
+}\r
+\r
+HTTPResponse *Controller::responseForAddKeywordsCGI(HTTPRequest *request, SOCKADDR_IN *client)\r
+{\r
+    DebugLog2("Controller::responseForAddKeywordsCGI()");\r
+\r
+    HTTPResponse *result = NULL;\r
+\r
+    // CGIリクエストとして解析\r
+    Dictionary *cgi = request->parseAsCGI();\r
+    if (cgi != NULL)\r
+    {\r
+        // CGIパスが一致しているか\r
+        if ((cgi->stringForKey(HTTPRequest::KEY_CGI) != NULL) && (cgi->stringForKey(HTTPRequest::KEY_CGI)->isEqualToString("/add_keywords.cgi")))\r
+        {\r
+            // パラメータがあるか\r
+            Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
+            if (params != NULL)\r
+            {\r
+                // パラメータ数が1か\r
+                if (params->count() == 1)\r
+                {\r
+                    Dictionary *param = (Dictionary *)params->objectAtIndex(0);\r
+                    String *value = param->stringForKey("keywords");\r
+                    if (value != NULL)\r
+                    {\r
+                        value = value->stringByReplacingOccurrencesOfString("+", " ");\r
+                        if (value != NULL)\r
+                        {\r
+                            value = value->stringByRemovingPercentEncoding();\r
+                        }\r
+                    }\r
+                    if (value != NULL)\r
+                    {\r
+                        // lock\r
+                        RaymLock(this);\r
+\r
+                        Dictionary *keywords_info = _reservations->dictionaryForKey(KEY_EPG_KEYWORDS);\r
+                        if (keywords_info == NULL)\r
+                        {\r
+                            keywords_info = Dictionary::dictionaryWithCapacity(0);\r
+                            _reservations->setObject(keywords_info, KEY_EPG_KEYWORDS);\r
+                        }\r
+\r
+                        if (keywords_info->dictionaryForKey(value) == NULL)\r
+                        {\r
+                            // 新規キーワードの場合\r
+                            keywords_info->setObject(Dictionary::dictionaryWithCapacity(0), value);\r
+\r
+                            // キーワード予約更新\r
+                            updateKeywordsReservation();\r
+\r
+                            // スケジュール更新\r
+                            updateSchedule();\r
+\r
+                            // 保存\r
+                            _reservations->writeToFile(_reservations_path, true);\r
+                        }\r
+\r
+                        RaymUnlock(this);\r
+\r
+                        result = responseForReloadURI(request, client, "/reservation.html#_keywords");\r
+                    }\r
+                }\r
+            }\r
+        }\r
+    }\r
+    return result;\r
+}\r
+\r
+HTTPResponse *Controller::responseForModKeywordsCGI(HTTPRequest *request, SOCKADDR_IN *client)\r
+{\r
+    DebugLog2("Controller::responseForModKeywordsCGI()");\r
+\r
+    HTTPResponse *result = NULL;\r
+\r
+    // CGIリクエストとして解析\r
+    Dictionary *cgi = request->parseAsCGI();\r
+    if (cgi != NULL)\r
+    {\r
+        DebugLog3("cgi != NULL");\r
+\r
+        // CGIパスが一致しているか\r
+        if ((cgi->stringForKey(HTTPRequest::KEY_CGI) != NULL) && (cgi->stringForKey(HTTPRequest::KEY_CGI)->isEqualToString("/mod_keywords.cgi")))\r
+        {\r
+            DebugLog3("CGI path OK.");\r
+\r
+            // パラメータがあるか\r
+            Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
+            if (params != NULL)\r
+            {\r
+                DebugLog3("params != NULL");\r
+\r
+                // パラメータのチェック\r
+                String *keywords   = NULL;\r
+                String *service_id = NULL;\r
+                String *start_hour = NULL;\r
+                String *start_min  = NULL;\r
+                String *req_mod    = NULL;\r
+                String *req_del    = NULL;\r
+\r
+                struct {\r
+                    const char *name;\r
+                    String **variable;\r
+                    const char *regex;\r
+                }\r
+                cgi[] =\r
+                {\r
+                    {"keywords",   &keywords,   ".*"},\r
+                    {"service_id", &service_id, "^(\\d+|-)$"},\r
+                    {"start_hour", &start_hour, "^(\\d{2}|-)$"},\r
+                    {"start_min",  &start_min,  "^(\\d{2}|-)$"},\r
+                    {"req_mod",    &req_mod,    ".*"},\r
+                    {"req_del",    &req_del,    ".*"},\r
+                    {NULL, NULL, NULL}\r
+                };\r
+\r
+                for (uint i = 0; cgi[i].name != NULL; ++i)\r
+                {\r
+                    for (uint j = 0; j < params->count(); ++j)\r
+                    {\r
+                        Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
+                        String *value = param->stringForKey(cgi[i].name);\r
+                        if ((value != NULL) && value->isMatch(cgi[i].regex))\r
+                        {\r
+                            *(cgi[i].variable) = value;\r
+                            break;\r
+                        }\r
+                    }\r
+                }\r
+\r
+                if (keywords != NULL)\r
+                {\r
+                    keywords = keywords->stringByReplacingOccurrencesOfString("+", " ");\r
+                    if (keywords != NULL)\r
+                    {\r
+                        keywords = keywords->stringByRemovingPercentEncoding();\r
+                    }\r
+                }\r
+\r
+                if ((keywords != NULL) && (service_id != NULL) && (start_hour != NULL) && (start_min != NULL) &&\r
+                    ((req_mod != NULL) && (req_del == NULL)) || ((req_mod == NULL) && (req_del != NULL)))\r
+                {\r
+\r
+                    // lock\r
+                    RaymLock(this);\r
+\r
+                    Dictionary *keywords_info = _reservations->dictionaryForKey(KEY_EPG_KEYWORDS);\r
+                    if (keywords_info != NULL)\r
+                    {\r
+                        if (req_mod != NULL)\r
+                        {\r
+                            DebugLog0("keywords: %s", keywords->cString());\r
+                            DebugLog0("service_id: %s", service_id->cString());\r
+                            DebugLog0("start_hour: %s", start_hour->cString());\r
+                            DebugLog0("start_min: %s", start_min->cString());\r
+                            Dictionary *kwd_inf = keywords_info->dictionaryForKey(keywords);\r
+                            if (kwd_inf != NULL)\r
+                            {\r
+                                if (!service_id->isEqualToString("-"))\r
+                                {\r
+                                    kwd_inf->setString(service_id, KEY_EPG_SERVICE_ID);\r
+                                }\r
+                                else\r
+                                {\r
+                                    kwd_inf->removeObjectForKey(KEY_EPG_SERVICE_ID);\r
+                                }\r
+                                if (!start_hour->isEqualToString("-") && !start_min->isEqualToString("-"))\r
+                                {\r
+                                    kwd_inf->setString(String::stringWithFormat("%s:%s", start_hour->cString(), start_min->cString()), KEY_EPG_START);\r
+                                }\r
+                                else\r
+                                {\r
+                                    kwd_inf->removeObjectForKey(KEY_EPG_START);\r
+                                }\r
+                            }\r
+                        }\r
+                        else if (req_del != NULL)\r
+                        {\r
+                            DebugLog0("keywords: %s", keywords->cString());\r
+                            keywords_info->removeObjectForKey(keywords);\r
+                        }\r
+\r
+                        // キーワード予約更新\r
+                        updateKeywordsReservation();\r
+\r
+                        // スケジュール更新\r
+                        updateSchedule();\r
+\r
+                        // 保存\r
+                        _reservations->writeToFile(_reservations_path, true);\r
+                    }\r
+\r
+                    // unlock\r
+                    RaymUnlock(this);\r
+\r
+                    result = responseForReloadURI(request, client, "/reservation.html#_keywords");\r
+                }\r
+            }\r
+        }\r
+    }\r
+    return result;\r
+}\r
+\r
+HTTPResponse *Controller::responseForReloadURI(NET::HTTPRequest *request, SOCKADDR_IN *client, const char *uri, int sec)\r
+{\r
+    HTTPResponse *result = NULL;\r
+\r
+    if (uri != NULL)\r
+    {\r
+        std::string contents;\r
+        contents = "<html>";\r
+        contents += "<head>";\r
+#if 0\r
+        contents += "<meta http-equiv=\"refresh\" content=\"0;URL=";\r
+#else\r
+        contents += "<meta http-equiv=\"refresh\" content=\"";\r
+        contents += sec;\r
+        contents += ";URL=";\r
+#endif\r
+        contents += uri;\r
+        contents += "\">";\r
+        contents += "</head>";\r
+        contents += "</html>";\r
+        String *html = String::stringWithUTF8String(contents.c_str());\r
+        if (html != NULL)\r
+        {\r
+            result = responseWithHTML(request, html);\r
+        }\r
+    }\r
+\r
+    return result;\r
+}\r
+\r
+HTTPResponse *Controller::responseForPlaylist(HTTPRequest *request, SOCKADDR_IN *client)\r
+{\r
+    HTTPResponse *result = NULL;\r
+    // http://bit.ly/iptv_feb2015\r
+\r
+#if 0\r
+\r
+#EXTM3U\r
+\r
+#EXTINF:-1, [COLOR yellow]Updated 15/04/2015 @ 03:45 [/COLOR]  \r
+plugin://plugin.video.youtube/?action=play_video&videoid=eMduu81gNgM\r
+#EXTINF:-1, [COLOR green] --Uk Live Tv--[/COLOR]  \r
+plugin://plugin.video.youtube/?action=play_video&videoid=eMduu81gNgM\r
+#EXTINF:-1, [COLOR green] --Free Collection of IPTV sports links--[/COLOR]  \r
+plugin://plugin.video.youtube/?action=play_video&videoid=eMduu81gNgM\r
+#EXTINF:-1, [COLOR red] --Links will go down. Please note that it will take time to update--[/COLOR]\r
+plugin://plugin.video.youtube/?action=play_video&videoid=eMduu81gNgM\r
+#EXTINF:-1, [COLOR red] --Please contact me at the husham.com website--[/COLOR]\r
+plugin://plugin.video.youtube/?action=play_video&videoid=eMduu81gNgM\r
+\r
+#EXTINF:-1, Sky sports news\r
+rtmp://89.248.172.159:443/liverepeater playpath=35 swfUrl=http://popeoftheplayers.eu/atdedead.swf pageUrl=http://popeoftheplayers.eu/crichd.php?id=35&width=600&height=450 token=#atd%#$ZH\r
+#EXTINF:-1,Bt sports 1\r
+rtmp://80.82.78.87:443/liverepeater/starsp pageUrl=http://xxxxxxxxxxxxxxxx.xx/rtmpe://strm.dcast.tv:1935/live/asdfadfaa/pageUrl=http://xxxxx.xx/rtmp://80.82.64.90:443/liverepeater/79/pageUrl=http://filotv.pw/rtmpe://strm.ukcast.tv:1935/redirect/FUNKTSN/pageUrl=http://ukcast.tv/rtmp://173.192.81.176:443/liverepeater/stream1/token%ZZri(nKa@#Z/pageUrl=http://hdcast.org/cdn.kingofplayers.com/rtmpe://46.246.29.152:1935/redirect/HDMNBC playpath=41?18?49?33?48?38?11 pageUrl=http://popeoftheplayers.eu/hdcast.org/rtmp://31.220.0.134:1935/live/tsn2/pageUrl=http://www.eucast.tv/rtmp://195.154.236.152:80/liverepeater/141449/pageUrl=http://goodcast.pw/rtmp://77.81.98.134/tv/bt1h28qn?v=pageUrl=http://castok.com/rtmp://89.46.102.70:443/liveedge/bt1pageUrl=http://hqstreams.tv/rtmpe://play.finecast.tv/live/hqbt1page/playpath=42?finecast.tv/rtmpe://cdn.hdcast.org:1935/redirect/swfUrl=http://www.hdcast.org/aplayer/jwplayer.flash.swfpageUrl=http://www.hdcast.org/token=Fo5_n0w?U.rA6l3-70w47ch@#8x12pX@ token=#atd%#$ZH\r
+\r
+#endif\r
+\r
+\r
+    std::string contents;\r
+    contents = "#EXTM3U\r\n";\r
+    contents += "\r\n";\r
+\r
+    Dictionary *tuner_channel_to_udp = _streaming_ctrls->dictionaryForKey(KEY_MAPPING_TUNER_CHANNEL_TO_UDP);\r
+    if (tuner_channel_to_udp != NULL)\r
+    {\r
+        Tuner::Type types[] = {Tuner::ISDB_T, Tuner::ISDB_S};\r
+        for (int t = 0; t < sizeof(types) / sizeof(Tuner::Type); ++t)\r
+        {\r
+            for (int i = 0; i < _tunerCount; ++i)\r
+            {\r
+                if (isTunerInitialized(i) && (_tuners[i]->type() == types[t]))\r
+                {\r
+                    uint ch_max = ((types[t] == Tuner::ISDB_T) ? Tuner::MAX_CHANNELS_ISDB_T : Tuner::MAX_CHANNELS_ISDB_S);\r
+                    for (uint ch = 0; ch <= ch_max; ++ch)\r
+                    {\r
+                        if (isChannelEnabled(i, ch))\r
+                        {\r
+                            char tuner_and_channel[10];\r
+                            sprintf_s(tuner_and_channel, "%d,%d", i, ch);\r
+                            char *chstr = strchr(tuner_and_channel, ',');\r
+                            if (chstr != NULL)\r
+                            {\r
+                                ++chstr;\r
+                            }\r
+\r
+                            String *udp_str = tuner_channel_to_udp->stringForKey(tuner_and_channel);\r
+                            if (udp_str != NULL)\r
+                            {\r
+                                contents += "#EXTINF:-1 tvg-id=\"";\r
+                                contents += tuner_and_channel;\r
+                                contents += "\" tvg-logo=\"";\r
+                                contents += ((types[t] == Tuner::ISDB_T) ? "t_" : "s_");\r
+                                if (chstr != NULL)\r
+                                {\r
+                                    contents += chstr;\r
+                                }\r
+                                else\r
+                                {\r
+                                    contents += "ffff";\r
+                                }\r
+                                contents += "\" group-title=\"";\r
+                                contents += _tuners[i]->name();\r
+                                contents += "\", ";\r
+                                String *station_name = stationName(types[t], ch);\r
+                                if (station_name != NULL)\r
+                                {\r
+                                    contents += station_name->cString();\r
+                                }\r
+                                else\r
+                                {\r
+                                    contents += tuner_and_channel;\r
+                                }\r
+                                contents += "\r\n";\r
+                                contents += "udp://0.0.0.0:";\r
+                                contents += udp_str->cString();\r
+                                contents += "\r\n";\r
+                            }\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+        }\r
+\r
+    }\r
+\r
+    String *text = String::stringWithUTF8String(contents.c_str());\r
+    if (text != NULL)\r
+    {\r
+        result = responseWithUTF8Text(request, text);\r
+    }\r
+\r
+    return result;\r
+}\r
+\r
+/**\r
+ * @brief HTTP Live Streaming制御\r
+ *\r
+ *     http://hogehoge/チューナ番号/チャンネル番号/streaming[-プリセット名].m3u8 がリクエスト(プリセットはオプション)され、\r
+ *     チューナ番号/チャンネル番号/プリセット名(ある場合)が有効値の場合にコールされる\r
+ *\r
+ * @param [in] request    HTTPリクエスト\r
+ * @param [in] client     リクエストしたクライアントのアドレス\r
+ * @param [in] tuner      チューナ番号\r
+ * @param [in] channel    チャンネル番号\r
+ * @param [in] preset     プリセット(URLで省略された場合 "default" )\r
+ */\r
+HTTPResponse *Controller::responseForHLSControl(HTTPRequest *request, SOCKADDR_IN *client, int tuner, int channel, String *preset)\r
+{\r
+    DebugLog0("Controller::responseForHLSControl()");\r
+\r
+    HTTPResponse *result = NULL;\r
+\r
+    // client からホスト名を取得\r
+    char hostname[NI_MAXHOST];\r
+    if (getnameinfo((SOCKADDR *)client, sizeof(SOCKADDR_IN), hostname, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) == 0)\r
+    {\r
+        // 取得OK\r
+        DebugLog0("host: %s", hostname);\r
+\r
+        // ストリーミング制御情報からHLS情報を取得\r
+        Dictionary *hls_info = _streaming_ctrls->dictionaryForKey(KEY_HLS_INFO);\r
+        if (hls_info == NULL)\r
+        {\r
+            DebugLog0("hls_info == NULL");\r
+            hls_info = Dictionary::dictionaryWithCapacity(0);\r
+            _streaming_ctrls->setObject(hls_info, KEY_HLS_INFO);\r
+        }\r
+\r
+        // HLS情報から指定チューナの情報を取得\r
+        Dictionary *hls_info_tuner = hls_info->dictionaryForKey(_tuners[tuner]->name());\r
+        if (hls_info_tuner == NULL)\r
+        {\r
+            DebugLog0("hls_info_tuner == NULL");\r
+            hls_info_tuner = Dictionary::dictionaryWithCapacity(0);\r
+            hls_info->setObject(hls_info_tuner, _tuners[tuner]->name());\r
+        }\r
+\r
+        // 指定チューナの情報からホスト名を確認\r
+        if ((hls_info_tuner->stringForKey(KEY_HOSTNAME) == NULL) || hls_info_tuner->stringForKey(KEY_HOSTNAME)->isEqualToString(hostname))\r
+        {\r
+            DebugLog0("new host or reconnect");\r
+             // ホスト名を設定\r
+            hls_info_tuner->setString(hostname, KEY_HOSTNAME);\r
+\r
+            // HLS制御インスタンス取得\r
+            HTTPLiveStreaming *hls = (HTTPLiveStreaming *)hls_info_tuner->objectForKey(KEY_HLS_INSTANCE);\r
+            if (hls == NULL)\r
+            {\r
+                DebugLog0("hls == NULL");\r
+                // 生成\r
+                hls = HTTPLiveStreaming::alloc()->init()->autorelease();\r
+                hls_info_tuner->setObject(hls, KEY_HLS_INSTANCE);\r
+            }\r
+\r
+            // 異チャンネルへのリクエスト、または、プリセット変更の場合\r
+            if (((hls_info_tuner->integerForKey(KEY_CHANNEL) != 0) && (hls_info_tuner->integerForKey(KEY_CHANNEL) != channel)) ||\r
+                ((hls_info_tuner->stringForKey(KEY_PRESET) != NULL) && !hls_info_tuner->stringForKey(KEY_PRESET)->isEqualToString(preset)))\r
+            {\r
+                DebugLog0("hls->stop()");\r
+\r
+                // 停止\r
+                hls->stop();\r
+\r
+                // チャンネルとプリセットは一旦削除\r
+                hls_info_tuner->removeObjectForKey(KEY_CHANNEL);\r
+                hls_info_tuner->removeObjectForKey(KEY_PRESET);\r
+            }\r
+\r
+            DebugLog0("start ?");\r
+\r
+            // 初回リクエスト or チャンネル/プリセット変更 か?\r
+            if ((hls_info_tuner->integerForKey(KEY_CHANNEL) == 0) && (hls_info_tuner->stringForKey(KEY_PRESET) == NULL))\r
+            {\r
+                DebugLog0("start or restart");\r
+\r
+                // UDPポートを検索\r
+                Dictionary *mapping = _streaming_ctrls->dictionaryForKey(KEY_MAPPING_UDP_TO_TUNER_CHANNEL);\r
+                if (mapping != NULL)\r
+                {\r
+                    Array *ports = mapping->allKeys();\r
+                    if (ports != NULL)\r
+                    {\r
+                        char tuner_and_channel[10];\r
+                        sprintf_s(tuner_and_channel, "%d,%d", tuner, channel);\r
+\r
+                        for (uint i = 0; i < ports->count(); ++i)\r
+                        {\r
+                            String *port = (String *)ports->objectAtIndex(i);\r
+                            String *tmp = mapping->stringForKey(port);\r
+                            if (tmp->isEqualToString(tuner_and_channel))\r
+                            {\r
+                                DebugLog0("udp mapping %d -> %d,%d", port->intValue(), tuner, channel);\r
+\r
+                                // ソースを設定\r
+                                hls->setSource(String::stringWithFormat("udp://@:%d", port->intValue()));\r
+\r
+                                // トランスコード\r
+//                                    hls->setTranscode(_props->dictionaryForKey(KEY_PRESETS)->dictionaryForKey(preset));\r
+\r
+                                // 出力先を設定\r
+                                hls->setOutputPath(_props->stringForKey(KEY_CACHE_PATH));\r
+\r
+                                // インデックス名\r
+                                hls->setIndexName(String::stringWithFormat("live_%03d_%03d", tuner, channel));\r
+\r
+                                // 開始\r
+                                if (hls->start())\r
+                                {\r
+                                    DebugLog0("hls->start() success");\r
+\r
+                                    // チャンネルを保存\r
+                                    hls_info_tuner->setInteger(0, KEY_CHANNEL);\r
+\r
+                                    // プリセットを保存\r
+                                    hls_info_tuner->setString(preset, KEY_PRESET);\r
+                                }\r
+\r
+                                break;\r
+                            }\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+\r
+\r
+            String *index_path = hls->indexPath();\r
+            FileManager *fm = FileManager::defaultManager();\r
+            bool isDirectory = false;\r
+            if (fm->fileExistsAtPath(index_path, &isDirectory))\r
+            {\r
+                if (!isDirectory)\r
+                {\r
+                    DebugLog0("file exists");\r
+                    result = _httpd->responseWithPath(index_path, request);\r
+                }\r
+            }\r
+            if (result == NULL)\r
+            {\r
+                DebugLog0("file no exists");\r
+                result = responseForReloadURI(request, client, request->URI()->cString(), 10);\r
+            }\r
+\r
+//            result = responseForFailed(request);\r
+        }\r
+        else\r
+        {\r
+            // 他ホストが使用中\r
+        }\r
+    }\r
+\r
+    return result;\r
+}\r
+\r
+HTTPResponse *Controller::requestTunerControl(HTTPRequest *request, SOCKADDR_IN *client, int tuner)\r
+{\r
+    DebugLog0("%s\n", __FUNCTION__);\r
+\r
+    HTTPResponse *result = NULL;\r
+\r
+    // lock\r
+    RaymLock(this);\r
+\r
+    // URI取得\r
+    String *uri = request->URI();\r
+    while (uri != NULL)\r
+    {\r
+        // CGIリクエストとして解析\r
+        Dictionary *cgi = request->parseAsCGI();\r
+        if (cgi != NULL)\r
+        {\r
+            uri = cgi->stringForKey(HTTPRequest::KEY_CGI);\r
+            if (uri == NULL)\r
+            {\r
+                break;\r
+            }\r
+        }\r
+\r
+        //\r
+        // チューナ情報\r
+        //\r
+        if (uri->isMatch("^/[0-9]{3}/info.xml$") && (cgi == NULL))\r
+        {\r
+        }\r
+\r
+        //\r
+        // チャンネル設定\r
+        //   /ttt/channel=nnn\r
+        //\r
+        else if (uri->isMatch("^/[0-9]{3}/channel=[0-9]{1,3}$") && (cgi == NULL))\r
+        {\r
+DebugLog0("ch set");\r
+            String *ch = uri->substringFromIndex(13);\r
+            if (ch == NULL)\r
+            {\r
+                break;\r
+            }\r
+            int channel = ch->intValue();\r
+DebugLog0("set %d channel:%d(%s)\n", tuner, channel, ch->cString());\r
+            DebugLog2("set channel:%d(%s)\n", channel, ch->cString());\r
+            if (setChannel(tuner, channel))\r
+            {\r
+                // success\r
+                DebugLog2("success.\n");\r
+                result = responseForSuccess(request);\r
+            }\r
+            else\r
+            {\r
+                // failed\r
+                DebugLog2("failed.\n");\r
+                result = responseForFailed(request);\r
+            }\r
+        }\r
+\r
+        //\r
+        // 録画開始(最大23:59まで)\r
+        //   /ttt/recording=on?hour=hh&min=mm[&channel=nnn]\r
+        //\r
+        else if (uri->isMatch("^/[0-9]{3}/recording=on$") && (cgi != NULL))\r
+        {\r
+            // パラメータがあるか\r
+            Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
+            if (params == NULL)\r
+            {\r
+                break;\r
+            }\r
+\r
+            // パラメータ数は2〜3か\r
+            if ((params->count() != 2) && (params->count() != 3))\r
+            {\r
+                break;\r
+            }\r
+\r
+            // パラメータのチェック\r
+            String *p_hour    = NULL;\r
+            String *p_min     = NULL;\r
+            String *p_channel = NULL;\r
+\r
+            struct {\r
+                const char *name;\r
+                String **variable;\r
+                const char *regex;\r
+            }\r
+            cgi[] =\r
+            {\r
+                {"hour",    &p_hour,    "^[0-2][0-9]$"},\r
+                {"min",     &p_min,     "^[0-5][0-9]$"},\r
+                {"channel", &p_channel, "^[0-9]{3}$"},\r
+                {NULL, NULL, NULL}\r
+            };\r
+\r
+            for (uint i = 0; cgi[i].name != NULL; ++i)\r
+            {\r
+                *(cgi[i].variable) = NULL;\r
+                for (uint j = 0; j < params->count(); ++j)\r
+                {\r
+                    Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
+                    String *value = param->stringForKey(cgi[i].name);\r
+                    if ((value != NULL) && value->isMatch(cgi[i].regex))\r
+                    {\r
+                        *(cgi[i].variable) = value;\r
+                    }\r
+                }\r
+            }\r
+\r
+            // パラメータは有効か\r
+            if ((p_hour == NULL) || (p_min == NULL))\r
+            {\r
+                break;\r
+            }\r
+\r
+            // チャンネル設定\r
+            int channel = 0;\r
+            if (p_channel != NULL)\r
+            {\r
+                channel = p_channel->intValue();\r
+            }\r
+            else\r
+            {\r
+                channel = _tuners[tuner]->channel();\r
+            }\r
+\r
+            if (channel >= 0)\r
+            {\r
+                // recording on\r
+                int hour = p_hour->intValue();\r
+                int min = p_min->intValue();\r
+                if (hour < 24)\r
+                {\r
+                    // EPG生成\r
+                    Dictionary *epg = Dictionary::dictionaryWithCapacity(0);\r
+                    while (true)\r
+                    {\r
+                        time_t now;\r
+                        time(&now);\r
+                        now += 1; // margin\r
+                        TM tm;\r
+                        if (localtime_s(&tm, &now) != 0)\r
+                        {\r
+                            epg = NULL;\r
+                            break;\r
+                        }\r
+                        TM end;\r
+                        end = tm;\r
+                        end.tm_hour += hour;\r
+                        end.tm_min += min;\r
+                        end.tm_sec += 1; // margin\r
+                        if (mktime(&end) == -1)\r
+                        {\r
+                            epg = NULL;\r
+                            break;\r
+                        }\r
+\r
+                        char tmp[16];\r
+\r
+                        // Date\r
+                        sprintf_s(tmp, sizeof(tmp), "%04d/%02d/%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);\r
+                        epg->setString(tmp, KEY_EPG_DATE);\r
+\r
+                        // Start\r
+                        sprintf_s(tmp, sizeof(tmp), "%02d:%02d:%02d", tm.tm_hour, tm.tm_min, tm.tm_sec);\r
+                        epg->setString(tmp, KEY_EPG_START);\r
+\r
+                        // End\r
+                        sprintf_s(tmp, sizeof(tmp), "%02d:%02d:%02d", end.tm_hour, end.tm_min, end.tm_sec);\r
+                        epg->setString(tmp, KEY_EPG_END);\r
+\r
+                        // Channel\r
+                        sprintf_s(tmp, sizeof(tmp), "%d", channel);\r
+                        epg->setString(tmp, KEY_EPG_CHANNEL);\r
+\r
+                        // 繰り返し\r
+                        epg->setString("off", KEY_EPG_REPEAT); \r
+\r
+                        // Status\r
+                        epg->setString("ready", KEY_EPG_STATUS);\r
+\r
+                        break;\r
+                    }\r
+                \r
+                    if (epg != NULL)\r
+                    {\r
+                        // 録画開始&結果生成\r
+                        result = responseByResultAndReferer(request, reserve(tuner, epg), URI_RESERVATION_HTML);\r
+                    }\r
+                }\r
+            }\r
+        }\r
+\r
+        //\r
+        // 録画停止\r
+        //   /ttt/recording=off\r
+        //\r
+        else if (uri->isMatch("^/[0-9]{3}/recording=off$") && (cgi == NULL))\r
+        {\r
+            // recording off\r
+            DebugLog2("recording off: %s\n", uri->cString());\r
+            if (cancel(tuner, -1))\r
+            {\r
+                // success\r
+                DebugLog2("success.\n");\r
+                result = responseForSuccess(request);\r
+            }\r
+            else\r
+            {\r
+                // failed\r
+                DebugLog2("failed.\n");\r
+                result = responseForFailed(request);\r
+            }\r
+        }\r
+\r
+        //\r
+        // ストリーミング開始\r
+        //   /ttt/streaming=on?udp=nnnnn(&host=aaaaaa)\r
+        //\r
+        else if (uri->isMatch("^/[0-9]{3}/streaming=on$") && (cgi != NULL))\r
+        {\r
+            // パラメータがあるか\r
+            Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
+            if (params == NULL)\r
+            {\r
+                break;\r
+            }\r
+\r
+            // パラメータ数は1〜2か\r
+            if ((params->count() != 1) && (params->count() != 2))\r
+            {\r
+                break;\r
+            }\r
+\r
+            // パラメータのチェック\r
+            String *p_udp  = NULL;\r
+            String *p_host = NULL;\r
+\r
+            struct {\r
+                const char *name;\r
+                String **variable;\r
+                const char *regex;\r
+            }\r
+            cgi[] =\r
+            {\r
+                {"udp",  &p_udp,  "^[0-9]{1,5}$"},\r
+                {"host", &p_host, "^.+$"},\r
+                {NULL, NULL, NULL}\r
+            };\r
+\r
+            for (uint i = 0; cgi[i].name != NULL; ++i)\r
+            {\r
+                *(cgi[i].variable) = NULL;\r
+                for (uint j = 0; j < params->count(); ++j)\r
+                {\r
+                    Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
+                    String *value = param->stringForKey(cgi[i].name);\r
+                    if ((value != NULL) && value->isMatch(cgi[i].regex))\r
+                    {\r
+                        *(cgi[i].variable) = value;\r
+                    }\r
+                }\r
+            }\r
+\r
+            // パラメータチェック\r
+            if (p_udp == NULL)\r
+            {\r
+                break;\r
+            }\r
+\r
+            SOCKADDR_IN dst_addr;\r
+            \r
+            if (p_host != NULL)\r
+            {\r
+    #if 0\r
+                std::string host = udpstr.substr(idx + 5);\r
+                udpstr = udpstr.substr(0, idx - 1);\r
+                DebugLog2("udp: %s\n", udpstr.c_str());\r
+                DebugLog2("host: %s\n", host.c_str());\r
+                struct hostent *ent = gethostbyname(host.c_str());\r
+    #endif\r
+            }\r
+            else\r
+            {\r
+                memcpy(&dst_addr, client, sizeof(SOCKADDR_IN));\r
+            }\r
+            dst_addr.sin_port = htons(p_udp->intValue());\r
+            \r
+            if (_tuners[tuner]->startStreaming(&dst_addr))\r
+            {\r
+                // success\r
+                DebugLog2("success.\n");\r
+                result = responseForSuccess(request);\r
+            }\r
+            else\r
+            {\r
+                // failed\r
+                DebugLog2("failed.\n");\r
+                result = responseForFailed(request);\r
+            }\r
+        }\r
+\r
+        //\r
+        // ストリーミング停止\r
+        //   /ttt/streaming=off(?host=aaaa)\r
+        //\r
+        else if (uri->isMatch("^/[0-9]{3}/streaming=off$"))\r
+        {\r
+            // パラメータ\r
+            String *p_host = NULL;\r
+\r
+            // パラメータがあるか\r
+            if (cgi != NULL)\r
+            {\r
+                Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
+                if (params == NULL)\r
+                {\r
+                    break;\r
+                }\r
+\r
+                // パラメータ数は0〜1か\r
+                if ((params->count() != 0) && (params->count() != 1))\r
+                {\r
+                    break;\r
+                }\r
+\r
+                struct {\r
+                    const char *name;\r
+                    String **variable;\r
+                    const char *regex;\r
+                }\r
+                cgi[] =\r
+                {\r
+                    {"host", &p_host, "^.+$"},\r
+                    {NULL, NULL, NULL}\r
+                };\r
+\r
+                for (uint i = 0; cgi[i].name != NULL; ++i)\r
+                {\r
+                    *(cgi[i].variable) = NULL;\r
+                    for (uint j = 0; j < params->count(); ++j)\r
+                    {\r
+                        Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
+                        String *value = param->stringForKey(cgi[i].name);\r
+                        if ((value != NULL) && value->isMatch(cgi[i].regex))\r
+                        {\r
+                            *(cgi[i].variable) = value;\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+\r
+            SOCKADDR_IN dst_addr;\r
+            if (p_host != NULL)\r
+            {\r
+            }\r
+            else\r
+            {\r
+            }\r
+\r
+            _tuners[tuner]->stopStreaming();\r
+            \r
+            // success\r
+            DebugLog2("success.\n");\r
+            result = responseForSuccess(request);\r
+        }\r
+\r
+        //\r
+        // HLS制御\r
+        //\r
+        else if (uri->isMatch("^/[0-9]{3}/[0-9]{3}/streaming(-[^\\.]+)?.m3u8$") && (cgi == NULL))\r
+        {\r
+            DebugLog0("uri: %s", uri->cString());\r
+\r
+            // URIからチャンネル番号を抽出\r
+            //   Range実装したい...\r
+            int ch = uri->substringFromIndex(5)->substringToIndex(3)->intValue();\r
+            DebugLog0("ch: %d", ch);\r
+\r
+            // presetが指定されている場合、presetを抽出\r
+            String *preset = NULL;\r
+            if (uri->isMatch("streaming-"))\r
+            {\r
+                preset = uri->substringFromIndex(19);\r
+                preset = preset->substringToIndex(preset->length() - 5);\r
+                DebugLog0("opt: preset: %s", preset->cString());\r
+            }\r
+            else\r
+            {\r
+                // なければ "default"\r
+                preset = String::stringWithUTF8String(KEY_DEFAULT);\r
+            }\r
+\r
+            // チャンネル/presetが有効か確認\r
+            if (isChannelEnabled(tuner, ch) &&\r
+                (_props->dictionaryForKey(KEY_PRESETS) != NULL) &&\r
+                (_props->dictionaryForKey(KEY_PRESETS)->objectForKey(preset) != NULL))\r
+            {\r
+                // \r
+                result = responseForHLSControl(request, client, tuner, ch, preset);\r
+            }\r
+            else\r
+            {\r
+                result = responseForFailed(request);\r
+            }\r
+            DebugLog0("hls req. done");\r
+        }\r
+        else if (uri->isMatch("^/[0-9]{3}/[0-9]{3}/streaming-[0-9]+.ts$") && (cgi == NULL))\r
+        {\r
+            // 分割されたTS\r
+            DebugLog0("uri: %s", uri->cString());\r
+        }\r
+\r
+        break;\r
+    }\r
+\r
+    // unlock\r
+    RaymUnlock(this);\r
+\r
+    return result;\r
+}\r
+\r
+HTTPResponse *Controller::request(HTTPRequest *request, SOCKADDR_IN *client)\r
+{\r
+    DebugLog2("%s\n", __FUNCTION__);\r
+\r
+    // 初期化チェック\r
+    bool flag = false;\r
+    RaymLock(this);\r
+    flag = _initialized;\r
+    RaymUnlock(this);\r
+    if (!flag)\r
+    {\r
+        return NULL;\r
+    }\r
+\r
+    HTTPResponse *response = NULL;\r
+\r
+    if (request->method()->isEqualToString("GET") ||\r
+        request->method()->isEqualToString("HEAD"))\r
+    {\r
+        // URI\r
+        String *uri = request->URI();\r
+        DebugLog0("request: %s\n", uri->cString());\r
+\r
+        //\r
+        //\r
+        //\r
+        if (uri->isMatch("^(/|/index\\.(htm|html))$"))\r
+        {\r
+            response = responseForMain(request, client);\r
+        }\r
+\r
+        else if (uri->isMatch("^/status.html$"))\r
+        {\r
+            response = responseForStatus(request, client);\r
+        }\r
+\r
+        //\r
+        else if (uri->isMatch("^/info.xml$"))\r
+        {\r
+        }\r
+        else if (uri->isMatch("^/video.xml$"))\r
+        {\r
+        }\r
+\r
+        //\r
+        else if (uri->isMatch("^/suspend.xml$"))\r
+        {\r
+            if (canSuspend())\r
+            {\r
+//                response = responseForSuccess(request);\r
+//                delaySuspend();\r
+            }\r
+            else\r
+            {\r
+//                response = responseForFailed(request);\r
+            }\r
+        }\r
+\r
+        //\r
+        else if (uri->isMatch("^/exec_vlc.html$"))\r
+        {\r
+//            _vlc->execute();\r
+//            return responseForRefreshMain(request, client);\r
+        }\r
+\r
+        //\r
+        // tuner control\r
+        //\r
+        else if (uri->isMatch("^/[0-9]{3}/"))\r
+        {\r
+DebugLog0("tuner control");\r
+            // String::substringWithRange() を実装するのを後回しにするので。。\r
+            std::string s = uri->cString();\r
+            int tuner = atoi(s.substr(1, 3).c_str());\r
+            if ((0 <= tuner) && (tuner < _tunerCount))\r
+            {\r
+                response = requestTunerControl(request, client, tuner);\r
+DebugLog0("tuner control done");\r
+            }\r
+        }\r
+\r
+        //\r
+        // reservation control\r
+        //\r
+        else if (uri->isMatch("^/reservation.html$"))\r
+        {\r
+            response = responseForReservation(request, client);\r
+        }\r
+        else if (uri->isMatch("^/regist.cgi"))\r
+        {\r
+            response = responseForRegistCGI(request, client);\r
+        }\r
+        else if (uri->isMatch("^/cancel.cgi"))\r
+        {\r
+            response = responseForCancelCGI(request, client);\r
+        }\r
+        else if (uri->isMatch("^/add_keywords.cgi"))\r
+        {\r
+            response = responseForAddKeywordsCGI(request, client);\r
+        }\r
+        else if (uri->isMatch("^/mod_keywords.cgi"))\r
+        {\r
+            response = responseForModKeywordsCGI(request, client);\r
+        }\r
+        else if (uri->isMatch("^/reservation.xml$"))\r
+        {\r
+        }\r
+        else if (uri->isMatch("^/[0-9]{6}/"))\r
+        {\r
+//            DebugLog2("reservation: %s\n", uri.c_str());\r
+        }\r
+\r
+        //\r
+        // video control\r
+        //\r
+        else if (uri->isMatch("^/[0-9]{9}/"))\r
+        {\r
+//            DebugLog2("video: %s\n", uri.c_str());\r
+        }\r
+\r
+        //\r
+        // program\r
+        //\r
+//        else if (match(uri.c_str(), "^/programs.html$"))\r
+        else if (uri->isMatch("^/" URI_PROGRAMS_HTML "$"))\r
+        {\r
+            response = responseForPrograms(request, client);\r
+        }\r
+        else if (uri->isMatch("^/programs.xml$"))\r
+        {\r
+//            collectEPG();\r
+//            return responseWithDictionary(request, _programs);\r
+        }\r
+\r
+        //\r
+        //\r
+        else if (uri->isMatch("^/tv.html$"))\r
+        {\r
+//            return responseForTV(request, client);\r
+        }\r
+\r
+        // IPTV対応\r
+        else if (uri->isMatch("^/iptv.m3u8$"))\r
+        {\r
+            return responseForPlaylist(request, client);\r
+        }\r
+\r
+        //\r
+        else if (uri->isMatch("^/iptd.log$"))\r
+        {\r
+            String *path = _system_path->stringByAppendingPathComponent("log");\r
+            if (path != NULL)\r
+            {\r
+                path = path->stringByAppendingPathComponent("iptd.log");\r
+                if (path != NULL)\r
+                {\r
+                    response = _httpd->responseWithPath(path, request);\r
+                    if (response != NULL)\r
+                    {\r
+                        if (response->message() != NULL)\r
+                        {\r
+                            if (response->message()->header() != NULL)\r
+                            {\r
+                                response->message()->header()->setFieldBodyWithName("text/plane; charset=UTF-8", "Content-Type");\r
+                            }\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+        }\r
+    }\r
+    else if (request->method()->isEqualToString("POST"))\r
+    {\r
+        // URI\r
+        String *uri = request->URI();\r
+        DebugLog1("POST: %s\n", uri->cString());\r
+        InternetTextMessage *message = request->message();\r
+        if (message != NULL)\r
+        {\r
+            DebugLog3("message: ");\r
+            InternetTextMessageHeader *header = message->header();\r
+            if (header != NULL)\r
+            {\r
+                DebugLog3("header: ");\r
+            }\r
+            InternetTextMessageBody *body = message->body();\r
+            if (body != NULL)\r
+            {\r
+                DebugLog3("body: ");\r
+            }\r
+        }\r
+    }\r
+\r
+    return response;\r
+}\r
+\r
+#ifndef _WIN32\r
+#pragma mark '\r
+#pragma mark ------- プロパティ取得 -------\r
+#endif\r
+\r
+bool Controller::isTunerInitialized(int tuner)\r
+{\r
+    DebugLog2("Controller::isTunerInitialized()");\r
+\r
+    bool result = false;\r
+\r
+    if ((0 <= tuner) && (tuner < _tunerCount))\r
+    {\r
+        // lock\r
+        RaymLock(this);\r
+\r
+        Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
+        if (tunersInfo != NULL)\r
+        {\r
+            Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
+            if (tunerInfo != NULL)\r
+            {\r
+                result = tunerInfo->boolForKey(KEY_INITIALIZED);\r
+            }\r
+        }\r
+\r
+        // unlock\r
+        RaymUnlock(this);\r
+    }\r
+\r
+    return result;\r
+}\r
+\r
+bool Controller::isTunerEnabled(int tuner)\r
+{\r
+    DebugLog2("Controller::isTunerEnabled()");\r
+\r
+    bool result = false;\r
+\r
+    if ((0 <= tuner) && (tuner < _tunerCount))\r
+    {\r
+        // lock\r
+        RaymLock(this);\r
+\r
+        Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
+        if (tunersInfo != NULL)\r
+        {\r
+            Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
+            if (tunerInfo != NULL)\r
+            {\r
+                result = tunerInfo->boolForKey(KEY_ENABLED);\r
+            }\r
+        }\r
+\r
+        // unlock\r
+        RaymUnlock(this);\r
+    }\r
+\r
+    return result;\r
+}\r
+\r
+bool Controller::isChannelEnabled(int tuner, int channel)\r
+{\r
+    DebugLog2("Controller::isChannelEnabled()");\r
+\r
+    bool result = false;\r
+\r
+    if ((0 <= tuner) && (tuner < _tunerCount) && (0 <= channel) && (channel <= Tuner::MAX_CHANNELS_ISDB_T))\r
+    {\r
+        // lock\r
+        RaymLock(this);\r
+\r
+        Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
+        if (tunersInfo != NULL)\r
+        {\r
+            Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[tuner]->name());\r
+            if (tunerInfo != NULL)\r
+            {\r
+                Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
+                if (channels != NULL)\r
+                {\r
+                    char key[4];\r
+                    sprintf_s(key, "%03d", channel);\r
+                    Dictionary *chInfo = channels->dictionaryForKey(key);\r
+                    if (chInfo != NULL)\r
+                    {\r
+                        result = chInfo->boolForKey(KEY_ENABLED);\r
+                    }\r
+                }\r
+            }\r
+        }\r
+\r
+        // unlock\r
+        RaymUnlock(this);\r
+    }\r
+\r
+    return result;\r
+}\r
+\r
+Array *Controller::stationInfos(Tuner::Type type)\r
+{\r
+    DebugLog2("Controller::stationInfosForISDB_T()");\r
+\r
+    Array *result = Array::arrayWithCapacity(0);\r
+\r
+    // lock\r
+    RaymLock(this);\r
+\r
+    Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
+    if (tunersInfo != NULL)\r
+    {\r
+        for (int i = 0; i < _tunerCount; ++i)\r
+        {\r
+            if (_tuners[i]->type() == type)\r
+            {\r
+                Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[i]->name());\r
+                if (tunerInfo != NULL)\r
+                {\r
+                    Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
+                    if (channels != NULL)\r
+                    {\r
+                        for (uint ch = 0; ch <= (type == Tuner::ISDB_T ? Tuner::MAX_CHANNELS_ISDB_T : Tuner::MAX_CHANNELS_ISDB_S); ++ch)\r
+                        {\r
+                            char chkey[4];\r
+                            sprintf_s(chkey, "%03d", ch);\r
+                            Dictionary *channel = channels->dictionaryForKey(chkey);\r
+                            if ((channel != NULL) && channel->boolForKey(KEY_ENABLED))\r
+                            {\r
+                                result->addObject(channel);\r
+                            }\r
+                        }\r
+                    }\r
+                }\r
+                break;\r
+            }\r
+        }\r
+    }\r
+\r
+    // unlock\r
+    RaymUnlock(this);\r
+\r
+    return result;\r
+}\r
+\r
+String *Controller::stationName(Tuner::Type type, int channel)\r
+{\r
+    DebugLog2("Controller::stationName()");\r
+\r
+    String *result = NULL;\r
+\r
+    // lock\r
+    RaymLock(this);\r
+\r
+    Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
+    if (tunersInfo != NULL)\r
+    {\r
+        for (int i = 0; i < _tunerCount; ++i)\r
+        {\r
+            if (_tuners[i]->type() == type)\r
+            {\r
+                Dictionary *tunerInfo = tunersInfo->dictionaryForKey(_tuners[i]->name());\r
+                if (tunerInfo != NULL)\r
+                {\r
+                    Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
+                    if (channels != NULL)\r
+                    {\r
+                        char chkey[4];\r
+                        sprintf_s(chkey, "%03d", channel);\r
+                        Dictionary *channel = channels->dictionaryForKey(chkey);\r
+                        if ((channel != NULL) && channel->boolForKey(KEY_ENABLED))\r
+                        {\r
+                            result = channel->stringForKey(KEY_NAME);\r
+                        }\r
+                    }\r
+                }\r
+                break;\r
+            }\r
+        }\r
+    }\r
+\r
+    // unlock\r
+    RaymUnlock(this);\r
+\r
+    return result;\r
+}\r
+\r
+// epgの開始時刻でソートする為の比較関数\r
+Integer compareFunction(Object *obj1, Object *obj2, void *context)\r
+{\r
+    if (isKindOfClass(Dictionary, obj1) && isKindOfClass(Dictionary, obj2))\r
+    {\r
+        time_t st1;\r
+        time_t ed1;\r
+        Controller::getTimeWithEPG((Dictionary *)obj1, &st1, &ed1);\r
+\r
+        time_t st2;\r
+        time_t ed2;\r
+        Controller::getTimeWithEPG((Dictionary *)obj2, &st2, &ed2);\r
+\r
+        if (st1 < st2)\r
+        {\r
+            return OrderedAscending;\r
+        }\r
+        else if (st1 > st2)\r
+        {\r
+            return OrderedDescending;\r
+        }\r
+        else\r
+        {\r
+            if (ed1 < ed2)\r
+            {\r
+                return OrderedAscending;\r
+            }\r
+            else if (ed1 > ed2)\r
+            {\r
+                return OrderedDescending;\r
+            }\r
+        }\r
+    }\r
+    return OrderedSame;\r
+}\r
+\r
+Array *Controller::programsForServices(Array *services)\r
+{\r
+    DebugLog2("Controller::programsForServices()");\r
+\r
+    Array *result = Array::arrayWithCapacity(0);\r
+\r
+    // lock\r
+    RaymLock(this);\r
+\r
+    for (uint i = 0; i < services->count(); ++i)\r
+    {\r
+        Dictionary *service = (Dictionary *)services->objectAtIndex(i);\r
+        Dictionary *events = _epgs->dictionaryForKey(service->stringForKey(KEY_SERVICE_ID));\r
+        if (events != NULL)\r
+        {\r
+            Array *keys = events->allKeys();\r
+            for (uint j = 0; j < keys->count(); ++j)\r
+            {\r
+                result->addObject(events->objectForKey((String *)keys->objectAtIndex(j)));\r
+            }\r
+        }\r
+    }\r
+\r
+    // sort\r
+    result = result->sortedArrayUsingFunction(compareFunction, this);\r
+\r
+\r
+    // unlock\r
+    RaymUnlock(this);\r
+\r
+    return result;\r
+}\r
+\r
+String *Controller::stationNameForServiceID(String *service_id)\r
+{\r
+    DebugLog2("Controller::stationNameForServiceID()");\r
+\r
+    String *result = NULL;\r
+\r
+    if (service_id != NULL)\r
+    {\r
+        // lock\r
+        RaymLock(this);\r
+\r
+        Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
+        if (tunersInfo != NULL)\r
+        {\r
+            Array *tuners_key = tunersInfo->allKeys();\r
+\r
+            for (uint i = 0; (result == NULL) && (i < tuners_key->count()); ++i)\r
+            {\r
+                Dictionary *tunerInfo = tunersInfo->dictionaryForKey((String *)tuners_key->objectAtIndex(i));\r
+                if (tunerInfo != NULL)\r
+                {\r
+                    Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
+                    if (channels != NULL)\r
+                    {\r
+                        Array *chkey = channels->allKeys();\r
+                        for (uint ch = 0; (result == NULL) && (ch < chkey->count()); ++ch)\r
+                        {\r
+                            Dictionary *channel = channels->dictionaryForKey((String *)chkey->objectAtIndex(ch));\r
+                            if (channel != NULL)\r
+                            {\r
+                                Array *services = channel->arrayForKey(KEY_SERVICES);\r
+                                if (services != NULL)\r
+                                {\r
+                                    for (uint j = 0; j < services->count(); ++j)\r
+                                    {\r
+                                        Dictionary *service = (Dictionary *)services->objectAtIndex(j);\r
+                                        String *sid = service->stringForKey(KEY_SERVICE_ID);\r
+                                        if (sid != NULL)\r
+                                        {\r
+                                            if (sid->isEqualToString(service_id))\r
+                                            {\r
+                                                result = service->stringForKey(KEY_NAME);\r
+                                                break;\r
+                                            }\r
+                                        }\r
+                                    }\r
+                                }\r
+                            }\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+        }\r
+\r
+        // unlock\r
+        RaymUnlock(this);\r
+    }\r
+\r
+    return result;\r
+}\r
+\r
+#ifndef _WIN32\r
+#pragma mark '\r
+#pragma mark ------- タイマディスパッチャ -------\r
+#endif\r
+\r
+void Controller::timerExpired(Timer *timer, void *userInfo)\r
+{\r
+    DebugLog2("Controller::timerExpired()");\r
+\r
+    //\r
+\r
+    switch ((long long)userInfo)\r
+    {\r
+    case CMD_RESTART:\r
+        if (restart() > 0)\r
+        {\r
+            // 初期化成功\r
+            DebugLog2("tuner initialize success.");\r
+\r
+            // EPG収集用タイマ起動(ISDB-S)\r
+            _timer_epg_s = Timer::alloc()->initWithTimeInterval(DEF_COLLECT_EPG_DELAY, this, (void *)CMD_COLLECT_EPG_ISDB_S, true);\r
+            if (_timer_epg_s != NULL)\r
+            {\r
+                _timer_epg_s->fire();\r
+            }\r
+\r
+            // EPG収集用タイマ起動(ISDB-T)\r
+            _timer_epg_t = Timer::alloc()->initWithTimeInterval(DEF_COLLECT_EPG_DELAY, this, (void *)CMD_COLLECT_EPG_ISDB_T, true);\r
+            if (_timer_epg_t != NULL)\r
+            {\r
+                _timer_epg_t->fire();\r
+            }\r
+        }\r
+        else\r
+        {\r
+            // 失敗\r
+            DebugLog0("tuner initialize failed.");\r
+        }\r
+        break;\r
+\r
+    case CMD_SUSPEND:\r
+        if (canSuspend())\r
+        {\r
+            suspend();\r
+        }\r
+        break;\r
+\r
+    case CMD_PERIODIC:                                  // 周期処理\r
+        periodic();\r
+        break;\r
+\r
+    case CMD_PERIODIC_2:                                // 周期処理2\r
+        periodic_2();\r
+        break;\r
+\r
+    case CMD_COLLECT_EPG_ISDB_S:                        // EPG収集(ISDB-S)\r
+        // 番組情報収集(ISDB-S)\r
+        if (collectEPGs(Tuner::ISDB_S))\r
+        {\r
+            // 終了(繰り返し無し)\r
+            _timer_epg_s->setRepeats(false);\r
+        }\r
+        else\r
+        {\r
+            // リトライ\r
+            _timer_epg_s->setTimeInterval(DEF_COLLECT_EPG_RETRY);\r
+        }\r
+        break;\r
+\r
+    case CMD_COLLECT_EPG_ISDB_T:                        // 番組情報収集(ISDB-T)\r
+        if (collectEPGs(Tuner::ISDB_T))\r
+        {\r
+            // 終了(繰り返し無し)\r
+            _timer_epg_t->setRepeats(false);\r
+        }\r
+        else\r
+        {\r
+            // リトライ\r
+            _timer_epg_t->setTimeInterval(DEF_COLLECT_EPG_RETRY);\r
+        }\r
+        break;\r
+    }\r
+}\r
+\r
+#ifndef _WIN32\r
+#pragma mark '\r
+#pragma mark ------- 起動/停止関連 -------\r
+#endif\r
+\r
+// システム状態をチェックしてサスペンド可能かを返す\r
+bool Controller::canSuspend()\r
+{\r
+    return isIdleState(true);\r
+}\r
+\r
+bool Controller::canTerminate()\r
+{\r
+    return isIdleState(false);\r
+}\r
+\r
+bool Controller::isIdleState(bool suspend)\r
+{\r
+    DebugLog2("Controller::isIdleState() start.");\r
+\r
+    bool result = false;\r
+\r
+    // lock\r
+    RaymLock(this);\r
+\r
+    // 初期化済みか\r
+    while (_initialized)\r
+    {\r
+        // EPG(ISDB-S)収集中か\r
+        if (suspend && (_timer_epg_s != NULL) && _timer_epg_s->valid())\r
+        {\r
+            // サスペンド不可\r
+            break;\r
+        }\r
+\r
+        // EPG(ISDB-T)収集中か\r
+        if (suspend && (_timer_epg_t != NULL) && _timer_epg_t->valid())\r
+        {\r
+            // サスペンド不可\r
+            break;\r
+        }\r
+\r
+        // サスペンド可に設定\r
+        result = true;\r
+\r
+        // 予約情報をチェック\r
+        // 10分後以内に開始の予約があったらサスペンド不可\r
+\r
+        // 現在時刻取得\r
+        time_t now = time(NULL);\r
+\r
+        for (int tuner = 0; tuner < _tunerCount; ++tuner)\r
+        {\r
+            Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
+            if ((array == NULL) || (array->count() == 0))\r
+            {\r
+                // next tuner\r
+                continue;\r
+            }\r
+\r
+            //\r
+            Dictionary *epg = (Dictionary *)array->objectAtIndex(0);\r
+            time_t start;\r
+            time_t end;\r
+            getTimeWithEPG(epg, &start, &end);\r
+\r
+            if (start + OFFSET_OF_SUPPRESSION_TIME <= now)\r
+            {\r
+                // サスペンド不可\r
+                result = false;\r
+                break;\r
+            }\r
+        }\r
+\r
+        break;\r
+    }\r
+\r
+    // unlock\r
+    RaymUnlock(this);\r
+\r
+    DebugLog2("Controller::isIdleState() end.");\r
+\r
+    return result;\r
+}\r
+\r
+/*\r
+ * TrayApp::WndProc() からコールされる\r
+ *\r
+ *   スレッドコンテキスト:メインスレッド\r
+ */\r
+void Controller::systemWillSuspend()\r
+{\r
+    DebugLog2("Controller::systemWillSuspend() start");\r
+\r
+    RaymLock(this);\r
+    _cancel_epg_collect = true;\r
+    RaymUnlock(this);\r
+\r
+    // タイマ停止\r
+    if ((_timer_restart != NULL) && _timer_restart->valid())\r
+    {\r
+        _timer_restart->invalidate();\r
+    }\r
+    if ((_timer_epg_s != NULL) && _timer_epg_s->valid())\r
+    {\r
+        _timer_epg_s->invalidate();\r
+    }\r
+    if ((_timer_epg_t != NULL) && _timer_epg_t->valid())\r
+    {\r
+        _timer_epg_t->invalidate();\r
+    }\r
+    if ((_timer_periodic != NULL) && _timer_periodic->valid())\r
+    {\r
+        _timer_periodic->invalidate();\r
+    }\r
+    if ((_timer_periodic_2 != NULL) && _timer_periodic_2->valid())\r
+    {\r
+        _timer_periodic_2->invalidate();\r
+    }\r
+\r
+    // lock\r
+    RaymLock(this);\r
+\r
+    // タイマ解放\r
+    RELEASE(_timer_restart);\r
+    RELEASE(_timer_epg_s);\r
+    RELEASE(_timer_epg_t);\r
+    RELEASE(_timer_periodic);\r
+    RELEASE(_timer_periodic_2);\r
+\r
+    // スケジュール更新\r
+    updateSchedule();\r
+\r
+    // 未初期化に設定\r
+    _initialized = false;\r
+\r
+    // チューナ解放\r
+    for (int i = 0; i < _tunerCount; ++i)\r
+    {\r
+        if (_tuners[i] != NULL)\r
+        {\r
+            delete _tuners[i];\r
+            _tuners[i] = NULL;\r
+        }\r
+    }\r
+\r
+    _cancel_epg_collect = false;\r
+\r
+    DebugLog0("system will suspend...");\r
+\r
+    // unlock\r
+    RaymUnlock(this);\r
+\r
+#ifdef OBJC_MEMORY_CHECK\r
+    DebugLog0("global_objc_count_ = %d", Raym::global_objc_count_);\r
+#endif\r
+\r
+    DebugLog2("Controller::systemWillSuspend() end");\r
+}\r
+\r
+/*\r
+ * TrayApp::WndProc() からコールされる\r
+ *\r
+ *   スレッドコンテキスト:メインスレッド\r
+ */\r
+void Controller::systemResumed()\r
+{\r
+    DebugLog2("Controller::systemResumed() start");\r
+\r
+    DebugLog0("system resumed.");\r
+\r
+    if (_timer_restart == NULL)\r
+    {\r
+        // 再開タイマ起動\r
+        _timer_restart = Timer::alloc()->initWithTimeInterval(1.0, this, (void *)CMD_RESTART, false);\r
+        _timer_restart->fire();\r
+    }\r
+\r
+    DebugLog2("Controller::systemResumed()");\r
+}\r
+\r
+/*\r
+ * TrayApp::WndProc() からコールされる\r
+ *\r
+ *   スレッドコンテキスト:メインスレッド\r
+ */\r
+void Controller::detectIdle()\r
+{\r
+    DebugLog2("Controller::detectIdle()");\r
+\r
+    // ここはメインスレッドなのでARPを用意する\r
+    AutoreleasePool *pool = AutoreleasePool::alloc()->init();\r
+\r
+    // lock\r
+    RaymLock(this);\r
+\r
+    // サスペンド可能か確認\r
+    if (canSuspend())\r
+    {\r
+        if (_idle_count == 0)\r
+        {\r
+            DebugLog0("detect idle...");\r
+        }\r
+\r
+        // アイドルカウンタを更新\r
+        ++_idle_count;\r
+\r
+        // 起動中アプリと休止状態抑止アプリのチェック\r
+        bool found = false;\r
+\r
+        Array *dont_in_suspend = _props->arrayForKey(KEY_DO_NOT_IN_SUSPEND);\r
+        Array *running_apps = Workspace::sharedWorkspace()->runningApplications();\r
+        for (uint i = 0; (i < running_apps->count()) && !found; ++i)\r
+        {\r
+            RunningApplication *ra = (RunningApplication *)running_apps->objectAtIndex(i);\r
+\r
+            // 実行中でなければ次へ\r
+            if (!ra->isRunning())\r
+            {\r
+                continue;\r
+            }\r
+\r
+            // 実行ファイルのチェック\r
+            String *path = ra->executePath();\r
+            if ((path == NULL) || (path->length() == 0))\r
+            {\r
+                continue;\r
+            }\r
+            DebugLog3("exec path: %s", path->cString());\r
+\r
+            // 休止状態抑止アプリリストのチェック\r
+            for (uint j = 0; (j < dont_in_suspend->count()) && !found; ++j)\r
+            {\r
+                found = path->isMatch((String *)dont_in_suspend->objectAtIndex(j));\r
+            }\r
+        }\r
+\r
+        // 抑止有効なら KEY_FORCED_SUSPEND_TIME、無効なら KEY_SUSPEND_TIME で取得\r
+        int limit = _props->integerForKey(found ? KEY_FORCED_SUSPEND_TIME : KEY_SUSPEND_TIME);\r
+        DebugLog3("found: %d, _idle_count: %d, limit: %d", found, _idle_count, limit);\r
+        if (_idle_count >= limit)\r
+        {\r
+            // unlock\r
+            RaymUnlock(this);\r
+\r
+            // サスペンド前にARPを解放しておく\r
+            pool->release();\r
+\r
+            // 下記の TrayApp::suspend() を使用する場合、自アプリへsuspendメッセージが\r
+            // ブロードキャストされないため、自分でコールしておく\r
+            systemWillSuspend();\r
+\r
+            // サスペンド\r
+            suspend();\r
+\r
+            // 再度 ARPを用意\r
+            pool = AutoreleasePool::alloc()->init();\r
+\r
+            // lock\r
+            RaymLock(this);\r
 \r
-            *time_var = mktime(&now_tm);\r
+            // アイドルカウンタをクリア\r
+            _idle_count = 0;\r
         }\r
     }\r
+    else\r
+    {\r
+        // アイドルカウンタをクリア\r
+        _idle_count = 0;\r
+    }\r
+\r
+    // unlock\r
+    RaymUnlock(this);\r
+\r
+    // ARP解放\r
+    pool->release();\r
 }\r
 \r
-void Controller::getTimeWithEPG(Dictionary *epg, time_t *start, time_t *end)\r
+void Controller::detectNonIdle()\r
 {\r
-    if ((epg == NULL) || (start == NULL) || (end == NULL))\r
-    {\r
-        return;\r
-    }\r
-    String *date = epg->stringForKey(KEY_EPG_DATE);\r
-    String *st   = epg->stringForKey(KEY_EPG_START);\r
-    String *ed   = epg->stringForKey(KEY_EPG_END);\r
-    if ((date == NULL) || (st == NULL) || (ed == NULL))\r
-    {\r
-        return;\r
-    }\r
+    DebugLog2("Controller::detectNonIdle()");\r
 \r
-    std::string dateStr = date->cString();\r
-    std::string stStr = st->cString();\r
-    std::string edStr = ed->cString();\r
-    TM tm_start;\r
-    tm_start.tm_year = atoi(dateStr.substr(0, 4).c_str()) - 1900;\r
-    tm_start.tm_mon  = atoi(dateStr.substr(5, 2).c_str()) - 1;\r
-    tm_start.tm_mday = atoi(dateStr.substr(8, 2).c_str());\r
-    tm_start.tm_hour = atoi(stStr.substr(0, 2).c_str());\r
-    tm_start.tm_min  = atoi(stStr.substr(3, 2).c_str());\r
-    tm_start.tm_sec  = atoi(stStr.substr(6, 2).c_str());\r
+    // lock\r
+    RaymLock(this);\r
 \r
-    TM tm_end;\r
-    tm_end.tm_year = atoi(dateStr.substr(0, 4).c_str()) - 1900;\r
-    tm_end.tm_mon  = atoi(dateStr.substr(5, 2).c_str()) - 1;\r
-    tm_end.tm_mday = atoi(dateStr.substr(8, 2).c_str());\r
-    tm_end.tm_hour = atoi(edStr.substr(0, 2).c_str());\r
-    tm_end.tm_min  = atoi(edStr.substr(3, 2).c_str());\r
-    tm_end.tm_sec  = atoi(edStr.substr(6, 2).c_str());\r
-    if (stStr > edStr)\r
+    if (_idle_count > 0)\r
     {\r
-        tm_end.tm_mday += 1;\r
+        DebugLog0("detect non idle...");\r
     }\r
-    *start = mktime(&tm_start);\r
-    *end = mktime(&tm_end);\r
-}\r
+    _idle_count = 0;\r
 \r
+    // unlock\r
+    RaymUnlock(this);\r
+}\r
 \r
 int Controller::restart()\r
 {\r
+    // lock\r
     RaymLock(this);\r
 \r
     // 未初期化に設定\r
     _initialized = false;\r
 \r
     // チューナ解放\r
-    for (int i = 0; i < _tuner_count; ++i)\r
+    for (int i = 0; i < _tunerCount; ++i)\r
     {\r
         if (_tuners[i] != NULL)\r
         {\r
@@ -788,13 +5274,13 @@ int Controller::restart()
 \r
 \r
     // チューナ初期化\r
-    _tuner_count = ry0::device::TunerFactory::scan(_tuners, _multi2_dll);\r
+    _tunerCount = ry0::device::TunerFactory::scan(_tuners, _multi2_dll);\r
 \r
     // unlock\r
     RaymUnlock(this);\r
 \r
     // チューナでループ\r
-    for (int i = 0; i < _tuner_count; ++i)\r
+    for (int i = 0; i < _tunerCount; ++i)\r
     {\r
         // チューナが初期化済みか\r
         if (!isTunerInitialized(i))\r
@@ -810,30 +5296,64 @@ int Controller::restart()
         }\r
     }\r
 \r
-    DebugLog0("");\r
-    DebugLog0("UDP Port Mapping");\r
-    DebugLog0("  Tuner, CH -> Port");\r
+    // UDPポートマッピング\r
+    DebugLog0("  UDP Port Mapping    :");\r
+    DebugLog0("    tuner, ch -> port");\r
     int udpport = _props->integerForKey(KEY_BEGIN_UDP_PORT);\r
-    for (int tuner = 0; tuner < _tuner_count; ++tuner)\r
+    for (int tuner = 0; tuner < _tunerCount; ++tuner)\r
     {\r
-        int max_channel = (_tuners[tuner]->type() == Tuner::ISDB_S ? Tuner::MAX_CHANNELS_ISDB_S : Tuner::MAX_CHANNELS_ISDB_T);\r
-        for (int ch = 0; ch <= max_channel; ++ch)\r
+        uint max_channel = (_tuners[tuner]->type() == Tuner::ISDB_S ? Tuner::MAX_CHANNELS_ISDB_S : Tuner::MAX_CHANNELS_ISDB_T);\r
+        for (uint ch = 0; ch <= max_channel; ++ch)\r
         {\r
             if (isChannelEnabled(tuner, ch))\r
             {\r
-                DebugLog0("  %5d,%3d -> %5d", tuner, ch, udpport);\r
-                _streaming->mapping(tuner, ch, udpport++);\r
+                DebugLog0("    %5d,%3d -> %5d", tuner, ch, udpport);\r
+\r
+                Dictionary *udp_to_tuner_channel = _streaming_ctrls->dictionaryForKey(KEY_MAPPING_UDP_TO_TUNER_CHANNEL);\r
+                if (udp_to_tuner_channel == NULL)\r
+                {\r
+                    udp_to_tuner_channel = Dictionary::dictionaryWithCapacity(0);\r
+                    _streaming_ctrls->setObject(udp_to_tuner_channel, KEY_MAPPING_UDP_TO_TUNER_CHANNEL);\r
+                }\r
+\r
+                Dictionary *tuner_channel_to_udp = _streaming_ctrls->dictionaryForKey(KEY_MAPPING_TUNER_CHANNEL_TO_UDP);\r
+                if (tuner_channel_to_udp == NULL)\r
+                {\r
+                    tuner_channel_to_udp = Dictionary::dictionaryWithCapacity(0);\r
+                    _streaming_ctrls->setObject(tuner_channel_to_udp, KEY_MAPPING_TUNER_CHANNEL_TO_UDP);\r
+                }\r
+\r
+                char port_str[10];\r
+                sprintf_s(port_str, "%d", udpport);\r
+                char tuner_and_channel[10];\r
+                sprintf_s(tuner_and_channel, "%d,%d", tuner, ch);\r
+\r
+                udp_to_tuner_channel->setString(tuner_and_channel, port_str);\r
+                tuner_channel_to_udp->setString(port_str, tuner_and_channel);\r
+\r
+                ++udpport;\r
             }\r
         }\r
     }\r
 \r
-    _reservation->systemResumed();\r
+    // 周期タイマ起動\r
+    _timer_periodic = Timer::alloc()->initWithTimeInterval(1.0, this, (void *)CMD_PERIODIC, true);\r
+    _timer_periodic->fire();\r
 \r
+    // 周期タイマ2起動\r
+    _timer_periodic_2 = Timer::alloc()->initWithTimeInterval(1.0, this, (void *)CMD_PERIODIC_2, true);\r
+    _timer_periodic_2->fire();\r
+\r
+    // lock\r
     RaymLock(this);\r
+\r
+    // 初期化済みに設定\r
     _initialized = true;\r
+\r
+    // unlock\r
     RaymUnlock(this);\r
 \r
-    return (_tuner_count > 0);\r
+    return (_tunerCount > 0);\r
 }\r
 \r
 int Controller::start()\r
@@ -842,26 +5362,39 @@ int Controller::start()
     //   設定以前にログ出力しないこと\r
     Raym::LOG_NUM_MAX = 8;\r
 \r
-    // 変数初期化\r
+#ifdef RAYM_MEMORY_CHECK\r
+    DebugLog0("");\r
+    DebugLog0("global_raym_count_ = %d", Raym::global_raym_count_);\r
+#endif\r
+\r
+    //\r
+    AutoreleasePool *pool = AutoreleasePool::alloc()->init();\r
+\r
+    // メンバ初期化\r
     _system_path        = NULL;\r
     _props_path         = NULL;\r
     _props              = NULL;\r
     _status_path        = NULL;\r
     _status             = NULL;\r
-\r
+    _epgs_path          = NULL;\r
+    _epgs               = NULL;\r
+    _store_path         = NULL;\r
+    _reservations       = NULL;\r
+    _reservations_path  = NULL;\r
     _timer_restart      = NULL;\r
-\r
+    _timer_periodic     = NULL;\r
+    _timer_periodic_2   = NULL;\r
+    _timer_epg_s        = NULL;\r
+    _timer_epg_t        = NULL;\r
     _multi2_dll         = NULL;\r
+    _streaming_ctrls    = NULL;\r
 \r
     _idle_count         = 0;\r
-\r
     _initialized        = false;\r
+    _reservation_seq_id = 0;\r
+    _cancel_epg_collect = false;\r
 \r
-    _reservation        = NULL;\r
-    _streaming          = NULL;\r
-    _httpd              = NULL;\r
-\r
-    _tuner_count = 0;\r
+    _tunerCount = 0;\r
     for (int i = 0; i < ry0::device::MAX_TUNERS; ++i)\r
     {\r
         _tuners[i] = NULL;\r
@@ -872,16 +5405,6 @@ int Controller::start()
     DebugLog0("iPTd ver %s (rev.%d)", VERSION, REVISION);\r
     DebugLog0("initialize...");\r
 \r
-    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());\r
-    if (hProcess != NULL)\r
-    {\r
-        if (SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS) == FALSE)\r
-        {\r
-            DebugLog0("SetPriorityClass failed.");\r
-        }\r
-        CloseHandle(hProcess);\r
-    }\r
-\r
     int result = 0;\r
 \r
     // 初期化\r
@@ -899,10 +5422,10 @@ int Controller::start()
         }\r
         _system_path = _system_path->stringByReplacingOccurrencesOfString("iPTd.exe", "");\r
         _system_path->retain();\r
-        DebugLog1("system path: %s", _system_path->cString());\r
+        DebugLog2("_system_path: %s", _system_path->cString());\r
 \r
         // プロパティファイルのパス設定\r
-        _props_path = String::alloc()->initWithFormat("%s%s.iptd.plist", _system_path->cString(), _plist_prefix);\r
+        _props_path = String::alloc()->initWithFormat("%s%s.iptd.plist", _system_path->cString(), PLIST_PREFIX);\r
         if (_props_path == NULL)\r
         {\r
             DebugLog0("error: set property file path.");\r
@@ -923,7 +5446,7 @@ int Controller::start()
         }\r
 \r
         // ステータスファイルのパス設定\r
-        _status_path = String::alloc()->initWithFormat("%s%s.iptd.status.plist", _system_path->cString(), _plist_prefix);\r
+        _status_path = String::alloc()->initWithFormat("%s%s.iptd.status.plist", _system_path->cString(), PLIST_PREFIX);\r
         if (_status_path == NULL)\r
         {\r
             DebugLog0("error: set status file path.");\r
@@ -1068,7 +5591,8 @@ int Controller::start()
         }\r
 \r
         // 録画データ格納先の確認\r
-        if (_props->stringForKey(KEY_STORE_PATH) ==  NULL)\r
+        _store_path = _props->stringForKey(KEY_STORE_PATH);\r
+        if (_store_path ==  NULL)\r
         {\r
             // プロパティに未設定の場合\r
             //   <Public Directory>/Videos を設定\r
@@ -1079,48 +5603,65 @@ int Controller::start()
                 result = -1;\r
                 break;\r
             }\r
-            _props->setString(String::stringWithFormat("%s\\Videos", public_dir), KEY_STORE_PATH);\r
+            _store_path = String::alloc()->initWithFormat("%s\\Videos", public_dir);\r
+            _props->setString(_store_path, KEY_STORE_PATH);\r
 \r
             // 更新フラグ\r
             updated = true;\r
         }\r
+        else\r
+        {\r
+            _store_path->retain();\r
+        }\r
 \r
         // 実際にディレクトリが存在しているか確認\r
         FileManager *fm = FileManager::defaultManager();\r
         bool isDir = false;\r
-        if (!fm->fileExistsAtPath(_props->stringForKey(KEY_STORE_PATH), &isDir))\r
+        if (!fm->fileExistsAtPath(_store_path, &isDir))\r
         {\r
             isDir = false;\r
         }\r
         if (!isDir)\r
         {\r
-            DebugLog0("error: \"%s\" is not exists.", _props->stringForKey(KEY_STORE_PATH)->cString());\r
+            DebugLog0("error: \"%s\" is not exists.", _store_path->cString());\r
             result = -1;\r
             break;\r
         }\r
 \r
-        //\r
-        _reservation = Reservation::alloc()->initWithController(this);\r
-        if (_reservation == NULL)\r
+        // HLS Preset\r
+        if (_props->dictionaryForKey(KEY_PRESETS) == NULL)\r
         {\r
-            result = -1;\r
-            break;\r
-        }\r
+            Dictionary *presets = Dictionary::dictionaryWithCapacity(0);\r
+            _props->setObject(presets, KEY_PRESETS);\r
+\r
+// とりあえずこのまま\r
+// プリセットは、あとで実装。。。            \r
+            Array *preset;\r
+\r
+            // "default"\r
+            preset = STR_ARRAY("-vcodec",         "libx264",\r
+                              "-b:v",            "768k",\r
+                              "-s",              "640x360",\r
+                              "-acodec",         "libfaac",\r
+                              "-b:a",            "96k",\r
+                              "-ar",             "44100",\r
+                              "-flags",          "+loop-global_header",\r
+                              "-map",            "0",\r
+                              "-bsf",            "h264_mp4toannexb",\r
+                              "-f",              "segment",\r
+                              "-segment_format", "mpegts",\r
+                              "-segment_time",   "10",\r
+                              NULL);\r
+            presets->setObject(preset, KEY_DEFAULT);\r
 \r
-        // httpdのルートパス\r
-        String *rootPath = _system_path->stringByAppendingPathComponent("html");\r
-        isDir = false;\r
-        if (!fm->fileExistsAtPath(rootPath, &isDir))\r
-        {\r
-            isDir = false;\r
+            updated = true;\r
         }\r
-        if (!isDir)\r
+\r
+        // キャッシュ(HLSの一時ファイル格納先)パス\r
+        if (_props->stringForKey(KEY_CACHE_PATH) == NULL)\r
         {\r
-/*\r
-            DebugLog0("error: \"%s\" is not exists.", rootPath->cString());\r
-            result = -1;\r
-            break;\r
-*/\r
+            _props->setString(_store_path, KEY_CACHE_PATH);\r
+            updated = true;\r
         }\r
 \r
         // プロパティファイルを保存\r
@@ -1151,23 +5692,75 @@ int Controller::start()
             }\r
         }\r
 \r
+        // 番組データファイルのパス設定\r
+        _epgs_path = String::alloc()->initWithFormat("%s%s.iptd.epgs.plist", _system_path->cString(), PLIST_PREFIX);\r
+        if (_epgs_path == NULL)\r
+        {\r
+            DebugLog0("error: set epgs file path.");\r
+            result = -1;\r
+            break;\r
+        }\r
+\r
+        // 番組データの読み込み\r
+        _epgs = Dictionary::alloc()->initWithContentsOfFile(_epgs_path);\r
+        if (_epgs == NULL)\r
+        {\r
+            DebugLog1("epgs file: %s (created)", _epgs_path->cString());\r
+            _epgs = Dictionary::alloc()->initWithCapacity(0);\r
+        }\r
+        else\r
+        {\r
+            DebugLog1("epgs file: %s", _epgs_path->cString());\r
+\r
+            // 過去の番組データは削除\r
+            removePastEPGs();\r
+        }\r
 \r
-        //\r
-        _streaming = Streaming::alloc()->initWithController(this);\r
-        if (_streaming == NULL)\r
+        // 予約データファイルのパス設定\r
+        _reservations_path = String::alloc()->initWithFormat("%s%s.iptd.reservations.plist", _system_path->cString(), PLIST_PREFIX);\r
+        if (_reservations_path == NULL)\r
         {\r
+            DebugLog0("error: set reservations file path.");\r
             result = -1;\r
             break;\r
         }\r
 \r
-        //\r
-        _httpd = HTTPD::alloc()->initWithController(this, _props->integerForKey(KEY_HTTP_PORT), rootPath);\r
-        if (_httpd == NULL)\r
+        // 予約データの読み込み\r
+        _reservations = Dictionary::alloc()->initWithContentsOfFile(_reservations_path);\r
+        if (_reservations == NULL)\r
+        {\r
+            DebugLog1("reservations file: %s (created)", _reservations_path->cString());\r
+            _reservations = Dictionary::alloc()->initWithCapacity(0);\r
+        }\r
+        else\r
+        {\r
+            DebugLog1("reservations file: %s", _reservations_path->cString());\r
+\r
+            // 予約情報シーケンスID\r
+            _reservation_seq_id = _reservations->integerForKey(KEY_EPG_LAST_RESV_ID);\r
+        }\r
+\r
+        // ストリーミング制御情報格納用\r
+        _streaming_ctrls = Dictionary::alloc()->initWithCapacity(0);\r
+\r
+        // httpdのルートパス\r
+        String *rootPath = _system_path->stringByAppendingPathComponent("iptd_html");\r
+        if (!fm->fileExistsAtPath(rootPath, &isDir))\r
+        {\r
+            isDir = false;\r
+        }\r
+        if (!isDir)\r
         {\r
+            DebugLog0("error: \"%s\" is not exists.", rootPath->cString());\r
             result = -1;\r
             break;\r
         }\r
 \r
+        // httpd開始\r
+        int port = _props->integerForKey(KEY_HTTP_PORT);\r
+        _httpd = NET::HTTPDaemon::alloc()->initWithPort(port, 10);\r
+        _httpd->setRootPath(rootPath);\r
+        _httpd->setDelegate(this);\r
         if (!_httpd->start())\r
         {\r
             DebugLog0("Can't start httpd.");\r
@@ -1179,7 +5772,6 @@ int Controller::start()
         _timer_restart = Timer::alloc()->initWithTimeInterval(1.0, this, (void *)CMD_RESTART, false);\r
         _timer_restart->fire();\r
 \r
-        // 初期化完了\r
         break;\r
     }\r
 \r
@@ -1192,16 +5784,34 @@ int Controller::start()
         _httpd->stop();\r
     }\r
 \r
+    RaymLock(this);\r
+    _cancel_epg_collect = true;\r
+    RaymUnlock(this);\r
 \r
+    // タイマ停止\r
     if ((_timer_restart != NULL) && _timer_restart->valid())\r
     {\r
         _timer_restart->invalidate();\r
     }\r
+    if ((_timer_epg_s != NULL) && _timer_epg_s->valid())\r
+    {\r
+        _timer_epg_s->invalidate();\r
+    }\r
+    if ((_timer_epg_t != NULL) && _timer_epg_t->valid())\r
+    {\r
+        _timer_epg_t->invalidate();\r
+    }\r
+    if ((_timer_periodic != NULL) && _timer_periodic->valid())\r
+    {\r
+        _timer_periodic->invalidate();\r
+    }\r
+    if ((_timer_periodic_2 != NULL) && _timer_periodic_2->valid())\r
+    {\r
+        _timer_periodic_2->invalidate();\r
+    }\r
 \r
-    _reservation->systemWillSuspend();\r
-    _streaming->systemWillSuspend();\r
-\r
-    for (int i = 0; i < _tuner_count; ++i)\r
+    // チューナ解放\r
+    for (int i = 0; i < _tunerCount; ++i)\r
     {\r
         if (_tuners[i] != NULL)\r
         {\r
@@ -1210,28 +5820,143 @@ int Controller::start()
         }\r
     }\r
 \r
+    // スケジュールリセット\r
     resetWakeSchedule();\r
 \r
+    // 解放\r
     RELEASE(_timer_restart);\r
+    RELEASE(_timer_epg_s);\r
+    RELEASE(_timer_epg_t);\r
+    RELEASE(_timer_periodic);\r
+    RELEASE(_timer_periodic_2);\r
+    RELEASE(_streaming_ctrls);\r
+    RELEASE(_httpd);\r
     RELEASE(_system_path);\r
     RELEASE(_props_path);\r
     RELEASE(_props);\r
     RELEASE(_status_path);\r
     RELEASE(_status);\r
-\r
-    RELEASE(_reservation);\r
-    RELEASE(_streaming);\r
-    RELEASE(_httpd);\r
+    RELEASE(_epgs_path);\r
+    RELEASE(_epgs);\r
+    RELEASE(_store_path);\r
+    RELEASE(_reservations_path);\r
+    RELEASE(_reservations);\r
 \r
     if (_multi2_dll != NULL)\r
     {\r
         FreeLibrary(_multi2_dll);\r
     }\r
 \r
+    pool->release();\r
+\r
+    // 終了\r
     DebugLog0("finished.");\r
 \r
+#ifdef RAYM_MEMORY_CHECK\r
+    DebugLog0("global_raym_count_ = %d", Raym::global_raym_count_);\r
+#endif\r
+\r
     return result;\r
 }\r
 \r
+#ifndef _WIN32\r
+#pragma mark '\r
+#pragma mark ------- コンストラクタ/デストラクタ -------\r
+#endif\r
+\r
+Controller::Controller()\r
+{\r
+}\r
+\r
+Controller::~Controller()\r
+{\r
+}\r
+\r
+Controller *Controller::alloc()\r
+{\r
+    return new Controller();\r
+}\r
+\r
+#ifndef _WIN32\r
+#pragma mark '\r
+#pragma mark ------- その他 -------\r
+#endif\r
+\r
+//\r
+// HH:MM:SS 形式の文字列から time_t に変換\r
+// 現在時刻が指定文字列の時刻以前の場合、当日の時刻。指定文字列の時刻よりも過ぎている場合、翌日の時刻を返す。\r
+//\r
+void Controller::getTimeWithString(String *str, time_t *time_var)\r
+{\r
+    if ((str != NULL) && str->isMatch(String::stringWithUTF8String("^\\d\\d:\\d\\d:\\d\\d$")) && (time_var != NULL))\r
+    {\r
+        // 時:分:秒 を int型に分解\r
+        std::string time_str = str->cString();\r
+        int hour = atoi(time_str.substr(0, 2).c_str());\r
+        int min  = atoi(time_str.substr(3, 2).c_str());\r
+        int sec  = atoi(time_str.substr(6, 2).c_str());\r
+        DebugLog2("%02d:%02d:%02d", hour, min, sec);\r
+\r
+        // 現在時刻取得\r
+        time_t now = time(NULL);\r
+        TM now_tm;\r
+        if (localtime_s(&now_tm, &now) == 0)\r
+        {\r
+            int now_sec = now_tm.tm_hour * 3600 + now_tm.tm_min * 60 + now_tm.tm_sec;\r
+            int col_sec = hour * 3600 + min * 3600 + sec;\r
+            if (now_sec > col_sec)\r
+            {\r
+                ++now_tm.tm_mday;\r
+            }\r
+            now_tm.tm_hour = hour;\r
+            now_tm.tm_min  = min;\r
+            now_tm.tm_sec  = sec;\r
+\r
+            *time_var = mktime(&now_tm);\r
+        }\r
+    }\r
+}\r
+\r
+void Controller::getTimeWithEPG(Dictionary *epg, time_t *start, time_t *end)\r
+{\r
+    if ((epg == NULL) || (start == NULL) || (end == NULL))\r
+    {\r
+        return;\r
+    }\r
+    String *date = epg->stringForKey(KEY_EPG_DATE);\r
+    String *st   = epg->stringForKey(KEY_EPG_START);\r
+    String *ed   = epg->stringForKey(KEY_EPG_END);\r
+    if ((date == NULL) || (st == NULL) || (ed == NULL))\r
+    {\r
+        return;\r
+    }\r
+\r
+    std::string dateStr = date->cString();\r
+    std::string stStr = st->cString();\r
+    std::string edStr = ed->cString();\r
+    TM tm_start;\r
+    tm_start.tm_year = atoi(dateStr.substr(0, 4).c_str()) - 1900;\r
+    tm_start.tm_mon  = atoi(dateStr.substr(5, 2).c_str()) - 1;\r
+    tm_start.tm_mday = atoi(dateStr.substr(8, 2).c_str());\r
+    tm_start.tm_hour = atoi(stStr.substr(0, 2).c_str());\r
+    tm_start.tm_min  = atoi(stStr.substr(3, 2).c_str());\r
+    tm_start.tm_sec  = atoi(stStr.substr(6, 2).c_str());\r
+\r
+    TM tm_end;\r
+    tm_end.tm_year = atoi(dateStr.substr(0, 4).c_str()) - 1900;\r
+    tm_end.tm_mon  = atoi(dateStr.substr(5, 2).c_str()) - 1;\r
+    tm_end.tm_mday = atoi(dateStr.substr(8, 2).c_str());\r
+    tm_end.tm_hour = atoi(edStr.substr(0, 2).c_str());\r
+    tm_end.tm_min  = atoi(edStr.substr(3, 2).c_str());\r
+    tm_end.tm_sec  = atoi(edStr.substr(6, 2).c_str());\r
+    if (stStr > edStr)\r
+    {\r
+        tm_end.tm_mday += 1;\r
+    }\r
+    *start = mktime(&tm_start);\r
+    *end = mktime(&tm_end);\r
+}\r
+\r
 } // iPTd\r
 } // ry0\r
+\r
index db63a55..08acdaf 100644 (file)
@@ -5,18 +5,15 @@
 \r
 #pragma once\r
 \r
-\r
 #include "Raym/Raym.h"\r
 \r
 #include "keys.h"\r
 #include "ry0/device/TunerFactory.h"\r
 \r
-#include "ry0/iPTd/Reservation.h"\r
-#include "ry0/iPTd/Streaming.h"\r
-#include "ry0/iPTd/HTTPD.h"\r
+#include "net/HTTPDaemon.h"\r
 \r
-#define VERSION "1.00"\r
-#define REVISION 13\r
+#define VERSION "0.01"\r
+#define REVISION 16\r
 \r
 namespace ry0\r
 {\r
@@ -29,46 +26,85 @@ typedef struct sockaddr_in SOCKADDR_IN;
 typedef struct _stat STAT;\r
 \r
 class Controller : public Raym::Application,\r
-                   public Raym::TimerDelegate\r
+                   public Raym::TimerDelegate,\r
+                   public NET::HTTPDaemonDelegate\r
 {\r
-protected:\r
-    Controller();\r
-    ~Controller();\r
+private:\r
+//    CRITICAL_SECTION                _cs;\r
+\r
+    Raym::String *          _system_path;           // システムパス(実行ファイルが配置されているディレクトリ)\r
+    Raym::String *          _props_path;            // プロパティファイルのパス\r
+    Raym::Dictionary *      _props;                 // プロパティ\r
+    Raym::String *          _status_path;           // ステータスファイルのパス\r
+    Raym::Dictionary *      _status;                // ステータス\r
+    Raym::String *          _epgs_path;             // 番組データファイルのパス\r
+    Raym::Dictionary *      _epgs;                  // 番組データ\r
+    Raym::String *          _store_path;            // 録画データ格納先\r
+    Raym::Dictionary *      _reservations;          // 予約情報\r
+    Raym::String *          _reservations_path;     // 予約情報ファイルのパス\r
+    int                             _reservation_seq_id;    // 予約情報シーケンスID\r
+    int                             _idle_count;            // アイドルカウンタ\r
 \r
-public:\r
-    static const char * _plist_prefix;\r
-    Raym::String *      _system_path;           // システムパス(実行ファイルが配置されているディレクトリ)\r
-    Raym::String *      _props_path;            // プロパティファイルのパス\r
-    Raym::Dictionary *  _props;                 // プロパティ\r
-    Raym::String *      _status_path;           // ステータスファイルのパス\r
-    Raym::Dictionary *  _status;                // ステータス\r
-    int                 _idle_count;            // アイドルカウンタ\r
+    bool                            _initialized;           // 初期化済み\r
+    HMODULE                         _multi2_dll;\r
+    bool                            _cancel_epg_collect;    // EPG収集キャンセル\r
 \r
-    Raym::Timer *       _timer_restart;         // 再開処理用\r
+    NET::HTTPDaemon *               _httpd;\r
 \r
-    Reservation *       _reservation;           // 予約録画制御\r
-    Streaming *         _streaming;             // ストリーミング制御\r
-    HTTPD *             _httpd;                 // HTTP制御\r
+    Raym::Dictionary *      _streaming_ctrls;       // ストリーミング制御情報格納用\r
 \r
-    bool                            _initialized;           // 初期化済み\r
-    HMODULE                         _multi2_dll;\r
+    // 非同期処理用タイマ\r
+    Raym::Timer *           _timer_restart;         // 再開処理用\r
+    Raym::Timer *           _timer_periodic;        // 周期処理用\r
+    Raym::Timer *           _timer_periodic_2;      // 周期処理用2\r
+    Raym::Timer *           _timer_epg_s;           // EPG(ISDB-S)収集用\r
+    Raym::Timer *           _timer_epg_t;           // EPG(ISDB-T)収集用\r
+\r
+    void scanChannel(int tuner);\r
+    void periodic();\r
+    void periodic_2();\r
 \r
-    int                             _tuner_count;\r
+    std::string createVideoPath(int tuner);\r
+\r
+public: // httpdからアクセス可能なように\r
+    int                             _tunerCount;\r
     ry0::device::Tuner *            _tuners[ry0::device::MAX_TUNERS];\r
 \r
+protected:\r
+    Controller();\r
+    ~Controller();\r
 \r
+public:\r
     static Controller *alloc();\r
-    int restart();\r
 \r
+    int restart();\r
+#if 0\r
+    void suspend();\r
+#endif\r
+    bool isIdleState(bool suspend);\r
+    bool canSuspend();\r
+#if 0\r
+    void delaySuspend();\r
+#endif\r
     // misc\r
     static void getTimeWithString(Raym::String *str, time_t *time_var);\r
     static void getTimeWithEPG(Raym::Dictionary *epg, time_t *start, time_t *end);\r
 \r
+    // EPG & 予約録画関連\r
+    void collectEPGsForTuner(int tuner, Raym::TimeInterval limit);\r
+    bool collectEPGs(ry0::device::Tuner::Type type);\r
+    void removePastEPGs();\r
+    bool reserve(int service_id, int event_id);\r
+    bool reserve(Raym::Dictionary *epg);\r
+    bool reserve(int tuner, Raym::Dictionary *epg);\r
+    bool cancel(int tuner, int reserve_id);\r
+    void updateKeywordsReservation();\r
+    void updateSchedule();\r
+\r
     // タイマ満了IF (from Timer)\r
     void timerExpired(Raym::Timer *timer, void *userInfo);\r
 \r
     // チューナ制御用IF\r
-    void scanChannel(int tuner);\r
     int getChannel(int tuner);\r
     bool setChannel(int tuner, int channel);\r
 \r
@@ -82,13 +118,32 @@ public:
     Raym::String *Controller::stationNameForServiceID(Raym::String *service_id);\r
 \r
 \r
-    int  start();\r
+    // HTTP制御関連\r
+    NET::HTTPResponse *responseWithHTML(NET::HTTPRequest *request, Raym::String *html);\r
+    NET::HTTPResponse *responseWithUTF8Text(NET::HTTPRequest *request, Raym::String *text);\r
+    NET::HTTPResponse *responseByResultAndReferer(NET::HTTPRequest *request, bool result, const char *referer);\r
+    NET::HTTPResponse *responseForMain(NET::HTTPRequest *request, SOCKADDR_IN *client);\r
+    NET::HTTPResponse *responseForPrograms(NET::HTTPRequest *request, SOCKADDR_IN *client);\r
+    NET::HTTPResponse *responseForReservation(NET::HTTPRequest *request, SOCKADDR_IN *client);\r
+    NET::HTTPResponse *responseForStatus(NET::HTTPRequest *request, SOCKADDR_IN *client);\r
+    NET::HTTPResponse *responseForRegistCGI(NET::HTTPRequest *request, SOCKADDR_IN *client);\r
+    NET::HTTPResponse *responseForCancelCGI(NET::HTTPRequest *request, SOCKADDR_IN *client);\r
+    NET::HTTPResponse *responseForAddKeywordsCGI(NET::HTTPRequest *request, SOCKADDR_IN *client);\r
+    NET::HTTPResponse *responseForModKeywordsCGI(NET::HTTPRequest *request, SOCKADDR_IN *client);\r
+    NET::HTTPResponse *responseForReloadURI(NET::HTTPRequest *request, SOCKADDR_IN *client, const char *uri, int sec = 0);\r
+    NET::HTTPResponse *responseForPlaylist(NET::HTTPRequest *request, SOCKADDR_IN *client);\r
+    NET::HTTPResponse *responseForHLSControl(NET::HTTPRequest *request, SOCKADDR_IN *client, int tuner, int channel, Raym::String *preset);\r
+    NET::HTTPResponse *requestTunerControl(NET::HTTPRequest *request, SOCKADDR_IN *client, int tuner);\r
+    NET::HTTPResponse *request(NET::HTTPRequest *request, SOCKADDR_IN *client);\r
+\r
+    // Windows用IF (from TrayApp)\r
+    int  start();    // エントリポイント\r
     void systemWillSuspend();\r
     void systemResumed();\r
     void detectIdle();\r
     void detectNonIdle();\r
-    bool canSuspend();\r
     bool canTerminate();\r
+    void debugLog(const char *message);\r
 };\r
 \r
 } // iPTd\r
diff --git a/src/ry0/iPTd/FFmpeg.cpp b/src/ry0/iPTd/FFmpeg.cpp
new file mode 100644 (file)
index 0000000..c0e2cd7
--- /dev/null
@@ -0,0 +1,224 @@
+//
+//
+//
+
+#define DBG_LEVEL 3
+#include "Raym/Log.h"
+
+#include <process.h>
+
+#include "FFmpeg.h"
+
+using namespace Raym;
+
+namespace ry0
+{
+namespace iPTd
+{
+
+DEFINE_STATIC_MUTEX(mutex_);
+static String *ffmpeg_exe_path_ = NULL;
+
+FFmpeg::FFmpeg()
+{
+    DebugLog2("FFmpeg::FFmpeg()");
+
+    //
+    // ffmpeg.exe の確認
+    //   自アプリの実行ファイルとと同じディレクトリにある前提
+    //
+    mutex_.lock();
+    while (ffmpeg_exe_path_ == NULL)
+    {
+        FileManager *fm = FileManager::defaultManager();
+
+        // 実行ファイルのパスを取得する関数は、TrayApp にあるのだが依存したくないので直に書く
+        char execute_path[MAX_PATH + 1];
+        memset(execute_path, 0x00, sizeof(execute_path));
+
+        TCHAR strbuf[MAX_PATH + 1];
+        if (GetModuleFileName(NULL, strbuf, MAX_PATH) == 0)
+        {
+            DebugLog0("FFmpeg()::FFmpeg()  GetModuleFileName() ng.");
+            break;
+        }
+
+        errno_t e;
+        size_t returnValue;
+        e = wcstombs_s(&returnValue, execute_path, sizeof(execute_path), strbuf, _TRUNCATE);
+        if (e != 0)
+        {
+            DebugLog0("FFmpeg()::FFmpeg()  wcstombs_s() ng.");
+            break;
+        }
+
+        char *p = strrchr(execute_path, '\\');
+        if (p == NULL)
+        {
+            DebugLog0("FFmpeg()::FFmpeg()  strrchr() ng.");
+            break;
+        }
+        ++p;
+        *p = '\0';
+        strcat_s(execute_path, sizeof(execute_path), "ffmpeg.exe");
+
+        String *path = String::alloc()->initWithUTF8String(execute_path);
+        if (path == NULL)
+        {
+            DebugLog0("FFmpeg()::FFmpeg()  initWithUTF8String() ng.");
+            break;
+        }
+
+        bool isDirectory = false;
+        if (fm->fileExistsAtPath(path, &isDirectory))
+        {
+            if (!isDirectory)
+            {
+                ffmpeg_exe_path_ = path;
+                ((Object *)ffmpeg_exe_path_)->autorelease(true);
+                break;
+            }
+            else
+            {
+                DebugLog0("FFmpeg()::FFmpeg()  is directory: \"%s\"", execute_path);
+            }
+        }
+        else
+        {
+            DebugLog0("FFmpeg()::FFmpeg()  not found: \"%s\"", execute_path);
+        }
+        path->release();
+
+        break;
+    }
+    mutex_.unlock();
+
+    // メンバ初期化
+
+}
+
+FFmpeg::~FFmpeg()
+{
+    stop();
+
+    // メンバ解放
+
+    DebugLog2("FFmpeg::~FFmpeg()");
+}
+
+FFmpeg *FFmpeg::alloc()
+{
+    DebugLog2("FFmpeg::alloc()");
+
+    return new FFmpeg();
+}
+
+FFmpeg *FFmpeg::init()
+{
+    DebugLog2("FFmpeg::init()");
+
+    if (CommandRunner::init() == NULL)
+    {
+        release();
+        return NULL;
+    }
+
+    return this;
+}
+
+FFmpeg *FFmpeg::retain()
+{
+    DebugLog2("FFmpeg::retain()");
+
+    CommandRunner::retain();
+    return this;
+}
+
+FFmpeg *FFmpeg::autorelease()
+{
+    DebugLog2("FFmpeg::autorelease()");
+
+    CommandRunner::autorelease();
+    return this;
+}
+
+bool FFmpeg::start()
+{
+    DebugLog2("FFmpeg::start()");
+
+    setCommandPath(ffmpeg_exe_path_);
+
+    return CommandRunner::start();
+}
+
+bool FFmpeg::readLine(String *line)
+{
+    return false;
+}
+
+const char *FFmpeg::className()
+{
+    return "FFmpeg";
+}
+
+#ifndef _WIN32
+#pragma mark '
+#pragma mark ------- util -------
+#endif
+
+Array *STR_ARRAY(const char *str, ...)
+{
+    Array *result = Array::arrayWithCapacity(0);
+
+    if (str != NULL)
+    {
+        result->addObject(String::stringWithUTF8String(str));
+
+        va_list ap;
+        va_start(ap, str);
+        while (true)
+        {
+            const char *p = va_arg(ap, const char *);
+            if (p == NULL)
+            {
+                break;
+            }
+            result->addObject(String::stringWithUTF8String(p));
+        }
+        va_end(ap);
+    }
+
+    return result;
+}
+/*
+Dictionary *MODULE(const char *name, Array *opts)
+{
+    Dictionary *result = Dictionary::dictionaryWithCapacity(0);
+    result->setObject(opts, name);
+    return result;
+}*/
+
+/*
+Dictionary *KEY_VAL(const char *key, const char *value)
+{
+    Dictionary *result = Dictionary::dictionaryWithCapacity(0);
+    result->setString(value, key);
+    return result;
+}
+
+Dictionary *KEY_VAL(const char *key, String *value)
+{
+    Dictionary *result = Dictionary::dictionaryWithCapacity(0);
+    result->setString(value, key);
+    return result;
+}
+
+Dictionary *KEY_VAL(const char *key, Dictionary *value)
+{
+    Dictionary *result = Dictionary::dictionaryWithCapacity(0);
+    result->setObject(value, key);
+    return result;
+}*/
+
+} // iPTd
+} // ry0
diff --git a/src/ry0/iPTd/FFmpeg.h b/src/ry0/iPTd/FFmpeg.h
new file mode 100644 (file)
index 0000000..b55374e
--- /dev/null
@@ -0,0 +1,42 @@
+//\r
+//\r
+//\r
+\r
+#pragma once\r
+\r
+#include "ry0/iPTd/CommandRunner.h"\r
+\r
+namespace ry0\r
+{\r
+namespace iPTd\r
+{\r
+\r
+Raym::Array *STR_ARRAY(const char *str, ...);\r
+\r
+class FFmpeg : public CommandRunner\r
+{\r
+private:\r
+\r
+\r
+protected:\r
+    FFmpeg();\r
+    ~FFmpeg();\r
+\r
+    virtual bool readLine(Raym::String *line);\r
+\r
+public:\r
+    static FFmpeg *alloc();\r
+    FFmpeg *init();\r
+    FFmpeg *retain();\r
+    FFmpeg *autorelease();\r
+\r
+//    void setSource(Raym::String *source);\r
+//    void setOutput(Raym::String *output);\r
+\r
+    bool start();\r
+\r
+    virtual const char *className();\r
+};\r
+\r
+} // iPTd\r
+} // ry0\r
diff --git a/src/ry0/iPTd/HTTPD.cpp b/src/ry0/iPTd/HTTPD.cpp
deleted file mode 100644 (file)
index 5d31a14..0000000
+++ /dev/null
@@ -1,839 +0,0 @@
-/**\r
- * @file HTTPDaemon.cpp\r
- *\r
- */\r
-\r
-#include <time.h>\r
-\r
-#define DBG_LEVEL 3\r
-#include "Raym/Log.h"\r
-\r
-#include "keys.h"\r
-#include "ry0/iPTd/HTTPD.h"\r
-#include "ry0/iPTd/Controller.h"\r
-\r
-using namespace Raym;\r
-using namespace NET;\r
-\r
-namespace ry0\r
-{\r
-namespace iPTd\r
-{\r
-\r
-HTTPD::HTTPD()\r
-{\r
-    _controller  = NULL;\r
-    _tuners      = NULL;\r
-    _httpd       = NULL;\r
-    _port        = -1;\r
-    _path        = NULL;\r
-}\r
-\r
-HTTPD::~HTTPD()\r
-{\r
-    RELEASE(_httpd);\r
-    RELEASE(_path);\r
-}\r
-\r
-HTTPD *HTTPD::alloc()\r
-{\r
-    return new HTTPD();\r
-}\r
-\r
-HTTPD *HTTPD::initWithController(Controller *controller, int port, String *path)\r
-{\r
-    _controller  = controller;\r
-    _tuners      = controller->_tuners;\r
-\r
-    _port        = port;\r
-    _path        = path->retain();\r
-\r
-    return this;\r
-}\r
-\r
-bool HTTPD::start()\r
-{\r
-    if (_httpd == NULL)\r
-    {\r
-        _httpd = HTTPDaemon::alloc()->initWithPort(_port, 10);\r
-        _httpd->setRootPath(_path);\r
-        _httpd->setDelegate(this);\r
-    }\r
-    return _httpd->start();\r
-}\r
-\r
-void HTTPD::stop()\r
-{\r
-    _httpd->stop();\r
-}\r
-\r
-HTTPResponse *HTTPD::request(HTTPRequest *request, SOCKADDR_IN *client)\r
-{\r
-    DebugLog2("%s\n", __FUNCTION__);\r
-\r
-    // 初期化チェック\r
-    bool flag = false;\r
-    RaymLock(_controller);\r
-    flag = _controller->_initialized;\r
-    RaymUnlock(_controller);\r
-    if (!flag)\r
-    {\r
-        return NULL;\r
-    }\r
-\r
-    HTTPResponse *response = NULL;\r
-\r
-    if (request->method()->isEqualToString("GET") ||\r
-        request->method()->isEqualToString("HEAD"))\r
-    {\r
-        // URI\r
-        String *uri = request->URI();\r
-        DebugLog0("request: %s\n", uri->cString());\r
-        if (uri->isMatch("^/config\\.xml$"))\r
-        {\r
-            RaymLock(_controller);\r
-            response = responseWithDictionary(request, _controller->_props);\r
-            RaymUnlock(_controller);\r
-        }\r
-        else if (uri->isMatch("^/status\\.xml$"))\r
-        {\r
-            RaymLock(_controller);\r
-            response = responseWithDictionary(request, _controller->_status);\r
-            RaymUnlock(_controller);\r
-        }\r
-        else if (uri->isMatch("^/iptv\\.m3u8$"))\r
-        {\r
-            return responseForPlaylist(request, client);\r
-        }\r
-        else if (uri->isMatch("^/regist\\.cgi"))\r
-        {\r
-            response = responseForRegistCGI(request, client);\r
-        }\r
-        else if (uri->isMatch("^/[0-9]{3}/"))\r
-        {\r
-            std::string s = uri->cString();\r
-            int tuner = atoi(s.substr(1, 3).c_str());\r
-            if ((0 <= tuner) && (tuner < _controller->_tuner_count))\r
-            {\r
-                response = requestTunerControl(request, client, tuner);\r
-            }\r
-        }\r
-\r
-        else if (uri->isMatch("^/iptd\\.log$"))\r
-        {\r
-            String *path = _controller->_system_path->stringByAppendingPathComponent("log");\r
-            if (path != NULL)\r
-            {\r
-                path = path->stringByAppendingPathComponent("iptd.log");\r
-                if (path != NULL)\r
-                {\r
-                    response = _httpd->responseWithPath(path, request);\r
-                    if (response != NULL)\r
-                    {\r
-                        if (response->message() != NULL)\r
-                        {\r
-                            if (response->message()->header() != NULL)\r
-                            {\r
-                                response->message()->header()->setFieldBodyWithName("text/plane; charset=UTF-8", "Content-Type");\r
-                            }\r
-                        }\r
-                    }\r
-                }\r
-            }\r
-        }\r
-    }\r
-\r
-    return response;\r
-}\r
-\r
-HTTPResponse *HTTPD::requestTunerControl(HTTPRequest *request, SOCKADDR_IN *client, int tuner)\r
-{\r
-    DebugLog0("%s\n", __FUNCTION__);\r
-\r
-    HTTPResponse *result = NULL;\r
-\r
-    // lock\r
-    RaymLock(_controller);\r
-\r
-    // URI取得\r
-    String *uri = request->URI();\r
-    while (uri != NULL)\r
-    {\r
-        // CGIリクエストとして解析\r
-        Dictionary *cgi = request->parseAsCGI();\r
-        if (cgi != NULL)\r
-        {\r
-            uri = cgi->stringForKey(HTTPRequest::KEY_CGI);\r
-            if (uri == NULL)\r
-            {\r
-                break;\r
-            }\r
-        }\r
-\r
-        //\r
-        // チャンネル設定\r
-        //   /ttt/channel=nnn\r
-        //\r
-        if (uri->isMatch("^/[0-9]{3}/channel=[0-9]{1,3}$") && (cgi == NULL))\r
-        {\r
-            String *ch = uri->substringFromIndex(13);\r
-            if (ch == NULL)\r
-            {\r
-                break;\r
-            }\r
-            int channel = ch->intValue();\r
-            DebugLog2("set channel:%d(%s)\n", channel, ch->cString());\r
-            if (_controller->setChannel(tuner, channel))\r
-            {\r
-                // success\r
-                DebugLog2("success.\n");\r
-                result = responseForSuccess(request);\r
-            }\r
-            else\r
-            {\r
-                // failed\r
-                DebugLog2("failed.\n");\r
-                result = responseForFailed(request);\r
-            }\r
-        }\r
-\r
-        //\r
-        // 録画開始(最大23:59まで)\r
-        //   /ttt/recording=on?hour=hh&min=mm[&channel=nnn]\r
-        //\r
-        else if (uri->isMatch("^/[0-9]{3}/recording=on$") && (cgi != NULL))\r
-        {\r
-            // パラメータがあるか\r
-            Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
-            if (params == NULL)\r
-            {\r
-                break;\r
-            }\r
-\r
-            // パラメータ数は2〜3か\r
-            if ((params->count() != 2) && (params->count() != 3))\r
-            {\r
-                break;\r
-            }\r
-\r
-            // パラメータのチェック\r
-            String *p_hour    = NULL;\r
-            String *p_min     = NULL;\r
-            String *p_channel = NULL;\r
-\r
-            struct {\r
-                const char *name;\r
-                String **variable;\r
-                const char *regex;\r
-            }\r
-            cgi[] =\r
-            {\r
-                {"hour",    &p_hour,    "^[0-2][0-9]$"},\r
-                {"min",     &p_min,     "^[0-5][0-9]$"},\r
-                {"channel", &p_channel, "^[0-9]{3}$"},\r
-                {NULL, NULL, NULL}\r
-            };\r
-\r
-            for (uint i = 0; cgi[i].name != NULL; ++i)\r
-            {\r
-                *(cgi[i].variable) = NULL;\r
-                for (uint j = 0; j < params->count(); ++j)\r
-                {\r
-                    Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
-                    String *value = param->stringForKey(cgi[i].name);\r
-                    if ((value != NULL) && value->isMatch(cgi[i].regex))\r
-                    {\r
-                        *(cgi[i].variable) = value;\r
-                    }\r
-                }\r
-            }\r
-\r
-            // パラメータは有効か\r
-            if ((p_hour == NULL) || (p_min == NULL))\r
-            {\r
-                break;\r
-            }\r
-\r
-            // チャンネル設定\r
-            int channel = 0;\r
-            if (p_channel != NULL)\r
-            {\r
-                channel = p_channel->intValue();\r
-            }\r
-            else\r
-            {\r
-                channel = _tuners[tuner]->channel();\r
-            }\r
-\r
-            if (channel >= 0)\r
-            {\r
-                // recording on\r
-                int hour = p_hour->intValue();\r
-                int min = p_min->intValue();\r
-                if (hour < 24)\r
-                {\r
-                    // EPG生成\r
-                    Dictionary *epg = Dictionary::dictionaryWithCapacity(0);\r
-                    while (true)\r
-                    {\r
-                        time_t now;\r
-                        time(&now);\r
-                        now += 1; // margin\r
-                        TM tm;\r
-                        if (localtime_s(&tm, &now) != 0)\r
-                        {\r
-                            epg = NULL;\r
-                            break;\r
-                        }\r
-                        TM end;\r
-                        end = tm;\r
-                        end.tm_hour += hour;\r
-                        end.tm_min += min;\r
-                        end.tm_sec += 1; // margin\r
-                        if (mktime(&end) == -1)\r
-                        {\r
-                            epg = NULL;\r
-                            break;\r
-                        }\r
-\r
-                        char tmp[16];\r
-\r
-                        // Date\r
-                        sprintf_s(tmp, sizeof(tmp), "%04d/%02d/%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);\r
-                        epg->setString(tmp, KEY_EPG_DATE);\r
-\r
-                        // Start\r
-                        sprintf_s(tmp, sizeof(tmp), "%02d:%02d:%02d", tm.tm_hour, tm.tm_min, tm.tm_sec);\r
-                        epg->setString(tmp, KEY_EPG_START);\r
-\r
-                        // End\r
-                        sprintf_s(tmp, sizeof(tmp), "%02d:%02d:%02d", end.tm_hour, end.tm_min, end.tm_sec);\r
-                        epg->setString(tmp, KEY_EPG_END);\r
-\r
-                        // Channel\r
-                        sprintf_s(tmp, sizeof(tmp), "%d", channel);\r
-                        epg->setString(tmp, KEY_EPG_CHANNEL);\r
-\r
-                        // 繰り返し\r
-                        epg->setString("off", KEY_EPG_REPEAT); \r
-\r
-                        // Status\r
-                        epg->setString("ready", KEY_EPG_STATUS);\r
-\r
-                        break;\r
-                    }\r
-                \r
-                    if (epg != NULL)\r
-                    {\r
-                        // 録画開始&結果生成\r
-                        if (_controller->_reservation->reserve(tuner, epg))\r
-                        {\r
-                            result = responseForSuccess(request);\r
-                        }\r
-                        else\r
-                        {\r
-                            result = responseForFailed(request);\r
-                        }\r
-                    }\r
-                }\r
-            }\r
-        }\r
-\r
-        //\r
-        // 録画停止\r
-        //   /ttt/recording=off\r
-        //\r
-        else if (uri->isMatch("^/[0-9]{3}/recording=off$") && (cgi == NULL))\r
-        {\r
-            // recording off\r
-            DebugLog2("recording off: %s\n", uri->cString());\r
-            if (_controller->_reservation->cancel(tuner, -1))\r
-            {\r
-                // success\r
-                DebugLog2("success.\n");\r
-                result = responseForSuccess(request);\r
-            }\r
-            else\r
-            {\r
-                // failed\r
-                DebugLog2("failed.\n");\r
-                result = responseForFailed(request);\r
-            }\r
-        }\r
-\r
-        //\r
-        // ストリーミング開始\r
-        //   /ttt/streaming=on?udp=nnnnn(&host=aaaaaa)\r
-        //\r
-        else if (uri->isMatch("^/[0-9]{3}/streaming=on$") && (cgi != NULL))\r
-        {\r
-            // パラメータがあるか\r
-            Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
-            if (params == NULL)\r
-            {\r
-                break;\r
-            }\r
-\r
-            // パラメータ数は1〜2か\r
-            if ((params->count() != 1) && (params->count() != 2))\r
-            {\r
-                break;\r
-            }\r
-\r
-            // パラメータのチェック\r
-            String *p_udp  = NULL;\r
-            String *p_host = NULL;\r
-\r
-            struct {\r
-                const char *name;\r
-                String **variable;\r
-                const char *regex;\r
-            }\r
-            cgi[] =\r
-            {\r
-                {"udp",  &p_udp,  "^[0-9]{1,5}$"},\r
-                {"host", &p_host, "^.+$"},\r
-                {NULL, NULL, NULL}\r
-            };\r
-\r
-            for (uint i = 0; cgi[i].name != NULL; ++i)\r
-            {\r
-                *(cgi[i].variable) = NULL;\r
-                for (uint j = 0; j < params->count(); ++j)\r
-                {\r
-                    Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
-                    String *value = param->stringForKey(cgi[i].name);\r
-                    if ((value != NULL) && value->isMatch(cgi[i].regex))\r
-                    {\r
-                        *(cgi[i].variable) = value;\r
-                    }\r
-                }\r
-            }\r
-\r
-            // パラメータチェック\r
-            if (p_udp == NULL)\r
-            {\r
-                break;\r
-            }\r
-\r
-            SOCKADDR_IN dst_addr;\r
-            \r
-            if (p_host != NULL)\r
-            {\r
-    #if 0\r
-                std::string host = udpstr.substr(idx + 5);\r
-                udpstr = udpstr.substr(0, idx - 1);\r
-                DebugLog2("udp: %s\n", udpstr.c_str());\r
-                DebugLog2("host: %s\n", host.c_str());\r
-                struct hostent *ent = gethostbyname(host.c_str());\r
-    #endif\r
-            }\r
-            else\r
-            {\r
-                memcpy(&dst_addr, client, sizeof(SOCKADDR_IN));\r
-            }\r
-            dst_addr.sin_port = htons(p_udp->intValue());\r
-            \r
-            if (_tuners[tuner]->startStreaming(&dst_addr))\r
-            {\r
-                // success\r
-                DebugLog2("success.\n");\r
-                result = responseForSuccess(request);\r
-            }\r
-            else\r
-            {\r
-                // failed\r
-                DebugLog2("failed.\n");\r
-                result = responseForFailed(request);\r
-            }\r
-        }\r
-\r
-        //\r
-        // ストリーミング停止\r
-        //   /ttt/streaming=off(?host=aaaa)\r
-        //\r
-        else if (uri->isMatch("^/[0-9]{3}/streaming=off$"))\r
-        {\r
-            // パラメータ\r
-            String *p_host = NULL;\r
-\r
-            // パラメータがあるか\r
-            if (cgi != NULL)\r
-            {\r
-                Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
-                if (params == NULL)\r
-                {\r
-                    break;\r
-                }\r
-\r
-                // パラメータ数は0〜1か\r
-                if ((params->count() != 0) && (params->count() != 1))\r
-                {\r
-                    break;\r
-                }\r
-\r
-                struct {\r
-                    const char *name;\r
-                    String **variable;\r
-                    const char *regex;\r
-                }\r
-                cgi[] =\r
-                {\r
-                    {"host", &p_host, "^.+$"},\r
-                    {NULL, NULL, NULL}\r
-                };\r
-\r
-                for (uint i = 0; cgi[i].name != NULL; ++i)\r
-                {\r
-                    *(cgi[i].variable) = NULL;\r
-                    for (uint j = 0; j < params->count(); ++j)\r
-                    {\r
-                        Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
-                        String *value = param->stringForKey(cgi[i].name);\r
-                        if ((value != NULL) && value->isMatch(cgi[i].regex))\r
-                        {\r
-                            *(cgi[i].variable) = value;\r
-                        }\r
-                    }\r
-                }\r
-            }\r
-\r
-//            SOCKADDR_IN dst_addr;\r
-            if (p_host != NULL)\r
-            {\r
-            }\r
-            else\r
-            {\r
-            }\r
-\r
-            _tuners[tuner]->stopStreaming();\r
-            \r
-            // success\r
-            DebugLog2("success.\n");\r
-            result = responseForSuccess(request);\r
-        }\r
-\r
-        //\r
-        // HLS制御\r
-        //\r
-        else if (uri->isMatch("^/[0-9]{3}/[0-9]{3}/streaming(-[^\\.]+)?.m3u8$") && (cgi == NULL))\r
-        {\r
-            DebugLog0("uri: %s", uri->cString());\r
-/*\r
-            // URIからチャンネル番号を抽出\r
-            //   Range実装したい...\r
-            int ch = uri->substringFromIndex(5)->substringToIndex(3)->intValue();\r
-            DebugLog0("ch: %d", ch);\r
-\r
-            // presetが指定されている場合、presetを抽出\r
-            String *preset = NULL;\r
-            if (uri->isMatch("streaming-"))\r
-            {\r
-                preset = uri->substringFromIndex(19);\r
-                preset = preset->substringToIndex(preset->length() - 5);\r
-                DebugLog0("opt: preset: %s", preset->cString());\r
-            }\r
-            else\r
-            {\r
-                // なければ "default"\r
-                preset = NSString::stringWithUTF8String(KEY_DEFAULT);\r
-            }\r
-\r
-            // チャンネル/presetが有効か確認\r
-            if (isChannelEnabled(tuner, ch) &&\r
-                (_props->dictionaryForKey(KEY_PRESETS) != NULL) &&\r
-                (_props->dictionaryForKey(KEY_PRESETS)->objectForKey(preset) != NULL))\r
-            {\r
-                // \r
-                result = responseForHLSControl(request, client, tuner, ch, preset);\r
-            }\r
-            else\r
-            {\r
-                result = responseForFailed(request);\r
-            }\r
-            DebugLog0("hls req. done");\r
-*/\r
-        }\r
-        else if (uri->isMatch("^/[0-9]{3}/[0-9]{3}/streaming-[0-9]+.ts$") && (cgi == NULL))\r
-        {\r
-            // 分割されたTS\r
-            DebugLog0("uri: %s", uri->cString());\r
-        }\r
-\r
-        break;\r
-    }\r
-\r
-    // unlock\r
-    RaymUnlock(_controller);\r
-\r
-    return result;\r
-}\r
-\r
-HTTPResponse *HTTPD::responseForRegistCGI(HTTPRequest *request, SOCKADDR_IN *client)\r
-{\r
-    DebugLog2("Controller::responseForRegistCGI()");\r
-\r
-    HTTPResponse *result = NULL;\r
-\r
-    // CGIリクエストとして解析\r
-    Dictionary *cgi = request->parseAsCGI();\r
-    if (cgi != NULL)\r
-    {\r
-        // CGIパスが一致しているか\r
-        if ((cgi->stringForKey(HTTPRequest::KEY_CGI) != NULL) && (cgi->stringForKey(HTTPRequest::KEY_CGI)->isEqualToString("/regist.cgi")))\r
-        {\r
-            // パラメータがあるか\r
-            Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
-            if (params != NULL)\r
-            {\r
-                // パラメータ数が2か\r
-                if (params->count() == 2)\r
-                {\r
-                    // パラメータのチェック\r
-                    String *service_id = NULL;\r
-                    String *event_id = NULL;\r
-\r
-                    for (uint i = 0; i < params->count(); ++i)\r
-                    {\r
-                        Dictionary *param = (Dictionary *)params->objectAtIndex(i);\r
-                        String *value = param->stringForKey("service_id");\r
-                        if ((value != NULL) && value->isMatch("^\\d+$"))\r
-                        {\r
-                            service_id = value;\r
-                        }\r
-                        value = param->stringForKey("event_id");\r
-                        if ((value != NULL) && value->isMatch("^\\d+$"))\r
-                        {\r
-                            event_id = value;\r
-                        }\r
-                    }\r
-\r
-                    // 有効なパラメータか\r
-                    if ((service_id != NULL) && (event_id != NULL))\r
-                    {\r
-                        DebugLog2("valid request");\r
-\r
-                        if (_controller->_reservation->reserve(service_id->intValue(), event_id->intValue()))\r
-                        {\r
-                            result = responseForSuccess(request);\r
-                        }\r
-                        else\r
-                        {\r
-                            result = responseForFailed(request);\r
-                        }\r
-                    }\r
-                }\r
-\r
-                // パラメータ数が9か\r
-                else if (params->count() == 9)\r
-                {\r
-                    // パラメータのチェック\r
-                    String *service_id = NULL;\r
-                    String *year       = NULL;\r
-                    String *month      = NULL;\r
-                    String *day        = NULL;\r
-                    String *start_hour = NULL;\r
-                    String *start_min  = NULL;\r
-                    String *end_hour   = NULL;\r
-                    String *end_min    = NULL;\r
-                    String *repeat     = NULL;\r
-\r
-                    struct {\r
-                        const char *name;\r
-                        String **variable;\r
-                        const char *regex;\r
-                    }\r
-                    cgi[] =\r
-                    {\r
-                        {"service_id", &service_id, "^\\d+$"},\r
-                        {"year",       &year,       "^\\d{4}$"},\r
-                        {"month",      &month,      "^([1-9]|1[0-2])$"},\r
-                        {"day",        &day,        "^([1-9]|[12][0-9]|3[01])$"},\r
-                        {"start_hour", &start_hour, "^\\d{2}$"},\r
-                        {"start_min",  &start_min,  "^\\d{2}$"},\r
-                        {"end_hour",   &end_hour,   "^\\d{2}$"},\r
-                        {"end_min",    &end_min,    "^\\d{2}$"},\r
-                        {"repeat",     &repeat,     "^(off|everyday|weekly|weekday)$"},\r
-                        {NULL, NULL, NULL}\r
-                    };\r
-\r
-                    for (uint i = 0; cgi[i].name != NULL; ++i)\r
-                    {\r
-                        for (uint j = 0; j < params->count(); ++j)\r
-                        {\r
-                            Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
-                            String *value = param->stringForKey(cgi[i].name);\r
-                            if ((value != NULL) && value->isMatch(cgi[i].regex))\r
-                            {\r
-                                *(cgi[i].variable) = value;\r
-                                break;\r
-                            }\r
-                        }\r
-                    }\r
-\r
-                    // 有効なパラメータか\r
-                    if ((service_id != NULL) && (year != NULL) && (month != NULL) && (day != NULL) &&\r
-                        (start_hour != NULL) && (start_min != NULL) && (end_hour != NULL) && (end_min != NULL) && (repeat != NULL))\r
-                    {\r
-                        //\r
-                        DebugLog1("valid param");\r
-\r
-                        Dictionary *epg = Dictionary::dictionaryWithCapacity(0);\r
-\r
-                        // 日付\r
-                        epg->setString(String::stringWithFormat("%s/%02d/%02d", year->cString(), month->intValue(), day->intValue()), KEY_EPG_DATE);\r
-\r
-                        // 開始時刻\r
-                        epg->setString(String::stringWithFormat("%s:%s:00", start_hour->cString(), start_min->cString()), KEY_EPG_START);\r
-\r
-                        // 終了時刻\r
-                        epg->setString(String::stringWithFormat("%s:%s:00", end_hour->cString(), end_min->cString()), KEY_EPG_END);\r
-\r
-                        // 繰り返し\r
-                        epg->setString(repeat, KEY_EPG_REPEAT);\r
-\r
-                        // Service ID\r
-                        epg->setString(service_id, KEY_EPG_SERVICE_ID);\r
-\r
-                        // Status\r
-                        epg->setString("ready", KEY_EPG_STATUS);\r
-\r
-                        if (_controller->_reservation->reserve(epg))\r
-                        {\r
-                            result = responseForSuccess(request);\r
-                        }\r
-                        else\r
-                        {\r
-                            result = responseForFailed(request);\r
-                        }\r
-                    }\r
-                }\r
-            }\r
-        }\r
-    }\r
-\r
-    return result;\r
-}\r
-\r
-\r
-HTTPResponse *HTTPD::responseForPlaylist(HTTPRequest *request, SOCKADDR_IN *client)\r
-{\r
-    HTTPResponse *result = NULL;\r
-\r
-    std::string contents;\r
-    contents = "#EXTM3U\r\n";\r
-    contents += "\r\n";\r
-\r
-    contents += "#EXTINF:-1, video\r\n";\r
-    contents += "http://172.19.29.9:50080/iptv/video.m3u8\r\n";\r
-    contents += "#EXTINF:-1, TV\r\n";\r
-    contents += "http://172.19.29.9:50080/iptv/tv.m3u8\r\n";\r
-\r
-    String *text = String::stringWithUTF8String(contents.c_str());\r
-    if (text != NULL)\r
-    {\r
-        result = responseWithUTF8Text(request, text);\r
-    }\r
-\r
-    return result;\r
-}\r
-\r
-// positive response by XML\r
-HTTPResponse *HTTPD::responseForSuccess(HTTPRequest *request)\r
-{\r
-    Dictionary *dict = Dictionary::dictionaryWithCapacity(0);\r
-    dict->setString("Success", KEY_RESULT);\r
-    return responseWithDictionary(request, dict);\r
-}\r
-\r
-// negative response by XML\r
-HTTPResponse *HTTPD::responseForFailed(HTTPRequest *request)\r
-{\r
-    Dictionary *dict = Dictionary::dictionaryWithCapacity(0);\r
-    dict->setString("Failed", KEY_RESULT);\r
-    return responseWithDictionary(request, dict);\r
-}\r
-\r
-HTTPResponse *HTTPD::responseWithDictionary(HTTPRequest *request, Dictionary *dictionary)\r
-{\r
-    HTTPResponse *result = NULL;\r
-    if ((request != NULL) && (dictionary != NULL))\r
-    {\r
-        std::string xml = dictionary->toString();\r
-\r
-        // header\r
-        InternetTextMessageHeader *header = InternetTextMessageHeader::alloc()->init();\r
-        // Date\r
-        // Server\r
-        // Content-Encoding\r
-        // Last-Modified\r
-        // Content-Type\r
-        header->setFieldBodyWithName("application/xml", "Content-Type");\r
-        // Connection\r
-        // Tranfer-Encoding\r
-        // Content-Length\r
-        header->setFieldBodyWithName(String::stringWithFormat("%I64u", xml.length()), "Content-Length");\r
-\r
-        // body\r
-        InternetTextMessageBody *body = InternetTextMessageBody::alloc()->initWithString(String::stringWithUTF8String(xml.c_str()));\r
-\r
-        // message\r
-        InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, body);\r
-        RELEASE(header);\r
-        RELEASE(body);\r
-        if (message != NULL)\r
-        {\r
-            result = HTTPResponse::alloc()->init();\r
-            result->autorelease();\r
-            result->setVersion(request->version());\r
-            result->setReason(NET::HTTPDaemon::reasonForStatus(200));\r
-            result->setStatus(200);\r
-            result->setMessage(message);\r
-            RELEASE(message);\r
-        }\r
-    }\r
-    return result;\r
-}\r
-\r
-HTTPResponse *HTTPD::responseWithUTF8Text(HTTPRequest *request, String *text)\r
-{\r
-    HTTPResponse *result = NULL;\r
-    if ((text != NULL) && (request != NULL))\r
-    {\r
-        // header\r
-        InternetTextMessageHeader *header = InternetTextMessageHeader::alloc()->init();\r
-        // Date\r
-        // Server\r
-        // Content-Encoding\r
-        // Last-Modified\r
-        // Content-Type\r
-        header->setFieldBodyWithName("text/plane; charset=UTF-8", "Content-Type");\r
-        // Connection\r
-        // Tranfer-Encoding\r
-        // Content-Length\r
-        header->setFieldBodyWithName(String::stringWithFormat("%I64u", text->length()), "Content-Length");\r
-\r
-        // body\r
-        InternetTextMessageBody *body = InternetTextMessageBody::alloc()->initWithString(text);\r
-\r
-        // message\r
-        InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, body);\r
-        RELEASE(header);\r
-        RELEASE(body);\r
-        if (message != NULL)\r
-        {\r
-//            result = HTTPResponse::response();\r
-            result = HTTPResponse::alloc()->init();\r
-            result->setVersion(request->version());\r
-            result->setReason(NET::HTTPDaemon::reasonForStatus(200));\r
-            result->setStatus(200);\r
-            result->setMessage(message);\r
-            result->autorelease();\r
-            RELEASE(message);\r
-        }\r
-    }\r
-    return result;\r
-}\r
-\r
-} // iPTd\r
-} // ry0\r
diff --git a/src/ry0/iPTd/HTTPD.h b/src/ry0/iPTd/HTTPD.h
deleted file mode 100644 (file)
index 2f9644d..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/**\r
- * @file HTTPDaemon.h\r
- *\r
- */\r
-\r
-#pragma once\r
-\r
-#include "Raym/Raym.h"\r
-#include "ry0/device/Tuner.h"\r
-#include "net/HTTPDaemon.h"\r
-\r
-namespace ry0\r
-{\r
-namespace iPTd\r
-{\r
-\r
-class Controller;\r
-\r
-class HTTPD : public Raym::Object,\r
-              public NET::HTTPDaemonDelegate\r
-{\r
-private:\r
-    Controller *            _controller;\r
-    ry0::device::Tuner **   _tuners;\r
-\r
-    NET::HTTPDaemon *       _httpd;\r
-    int                     _port;\r
-    Raym::String *          _path;\r
-\r
-protected:\r
-    HTTPD();\r
-    ~HTTPD();\r
-\r
-public:\r
-    static HTTPD *alloc();\r
-    HTTPD *initWithController(Controller *controller, int port, Raym::String *path);\r
-\r
-    bool start();\r
-    void stop();\r
-\r
-    NET::HTTPResponse *request(NET::HTTPRequest *request, struct sockaddr_in *client);\r
-    NET::HTTPResponse *requestTunerControl(NET::HTTPRequest *request, struct sockaddr_in *client, int tuner);\r
-    NET::HTTPResponse *responseForRegistCGI(NET::HTTPRequest *request, struct sockaddr_in *client);\r
-    NET::HTTPResponse *responseForPlaylist(NET::HTTPRequest *request, struct sockaddr_in *client);\r
-\r
-\r
-    static NET::HTTPResponse *responseForSuccess(NET::HTTPRequest *request);\r
-    static NET::HTTPResponse *responseForFailed(NET::HTTPRequest *request);\r
-    static NET::HTTPResponse *responseWithDictionary(NET::HTTPRequest *request, Raym::Dictionary *dictionary);\r
-    static NET::HTTPResponse *responseWithUTF8Text(NET::HTTPRequest *request, Raym::String *text);\r
-};\r
-\r
-\r
-} // iPTd\r
-} // ry0\r
diff --git a/src/ry0/iPTd/HTTPLiveStreaming.cpp b/src/ry0/iPTd/HTTPLiveStreaming.cpp
new file mode 100644 (file)
index 0000000..9ff5e16
--- /dev/null
@@ -0,0 +1,164 @@
+//
+//
+//
+
+#define DBG_LEVEL 3
+#include "Raym/Log.h"
+
+#include "HTTPLiveStreaming.h"
+
+using namespace Raym;
+
+namespace ry0
+{
+namespace iPTd
+{
+
+HTTPLiveStreaming::HTTPLiveStreaming()
+{
+    DebugLog2("HTTPLiveStreaming::HTTPLiveStreaming()");
+
+    _source      = NULL;
+    _output_path = NULL;
+    _index_name  = NULL;
+    _base_url    = NULL;
+    _counter     = 0;
+}
+
+HTTPLiveStreaming::~HTTPLiveStreaming()
+{
+    RELEASE(_source);
+    RELEASE(_output_path);
+    RELEASE(_index_name);
+    RELEASE(_base_url);
+
+    DebugLog2("HTTPLiveStreaming::~HTTPLiveStreaming()");
+}
+
+HTTPLiveStreaming *HTTPLiveStreaming::alloc()
+{
+    DebugLog2("HTTPLiveStreaming::alloc()");
+
+    return new HTTPLiveStreaming();
+}
+
+HTTPLiveStreaming *HTTPLiveStreaming::init()
+{
+    DebugLog2("HTTPLiveStreaming::init()");
+
+    if (FFmpeg::init() == NULL)
+    {
+        release();
+        return NULL;
+    }
+
+    return this;
+}
+
+HTTPLiveStreaming *HTTPLiveStreaming::retain()
+{
+    DebugLog2("HTTPLiveStreaming::retain()");
+
+    FFmpeg::retain();
+    return this;
+}
+
+HTTPLiveStreaming *HTTPLiveStreaming::autorelease()
+{
+    DebugLog2("HTTPLiveStreaming::autorelease()");
+
+    FFmpeg::autorelease();
+    return this;
+}
+
+void HTTPLiveStreaming::setSource(Raym::String *source)
+{
+    DebugLog2("HTTPLiveStreaming::setSource()");
+
+    RELEASE(_source);
+    if (source != NULL)
+    {
+        _source = source->retain();
+    }
+}
+
+void HTTPLiveStreaming::setOutputPath(Raym::String *output_path)
+{
+    DebugLog2("HTTPLiveStreaming::setOutputPath()");
+
+    RELEASE(_output_path);
+    if (output_path != NULL)
+    {
+        _output_path = output_path->retain();
+    }
+}
+
+void HTTPLiveStreaming::setIndexName(Raym::String *index_name)
+{
+    DebugLog2("HTTPLiveStreaming::setIndexName()");
+
+    RELEASE(_index_name);
+    if (index_name != NULL)
+    {
+        _index_name = index_name->retain();
+    }
+}
+
+bool HTTPLiveStreaming::start()
+{
+
+    //
+    // %>ffmpeg -i C:\WORK\20151024_211758_086_PT2@050000-01.ts -vcodec libx264 -r 30000/1001 -aspect 16:9 -s 1280x720
+    // -bufsize 20000k -maxrate 25000k -acodec libvo_aacenc -ac
+    // 2 -ar 48000 -ab 128k -threads 2 -f segment -segment_format mpegts -segment_time 10 -segment_list index.m3u8 test%03d.ts
+    //
+
+    // 暫定処理
+//    Array *args = STR_ARRAY("-i",              "C:\\WORK\\20151024_211758_086_PT2@050000-01.ts",
+//    Array *args = STR_ARRAY("-i",              "udp://0.0.0.0:51027",
+    Array *args = STR_ARRAY("-i",              "udp://@0.0.0.0:51027?overrun_nonfatal=1&fifo_size=50000000",
+                              "-vcodec",         "libx264",
+                              "-r",              "30000/1001",
+                              "-aspect",         "16:9",
+                              "-s",              "1280x720",
+                              "-bufsize",        "20000k",
+                              "-maxrate",        "25000k",
+                              "-acodec",         "libvo_aacenc",
+                              "-ac",             "2",
+                              "-ar",             "48000",
+                              "-ab",             "128k",
+                              "-threads",        "2",
+//                              "-programid",      "1074",
+                              "-f",              "segment",
+                              "-segment_format", "mpegts",
+                              "-segment_time",   "10",
+                              "-segment_list",   "C:\\WORK\\www\\index.m3u8",
+                              "C:\\WORK\\www\\test%03d.ts",
+                              NULL);
+    setArguments(args);
+
+    return FFmpeg::start();
+}
+
+String * HTTPLiveStreaming::indexPath()
+{
+    return NULL;
+}
+
+
+bool HTTPLiveStreaming::readLine(String *line)
+{
+    if (line != NULL)
+    {
+        DebugLog3("ffmpeg: %s", line->cString());
+    }
+    return false;
+}
+
+const char *HTTPLiveStreaming::className()
+{
+    return "HTTPLiveStreaming";
+}
+
+} // iPTd
+} // ry0
diff --git a/src/ry0/iPTd/HTTPLiveStreaming.h b/src/ry0/iPTd/HTTPLiveStreaming.h
new file mode 100644 (file)
index 0000000..64d7b77
--- /dev/null
@@ -0,0 +1,51 @@
+//
+//
+//
+
+#pragma once
+
+#include "Raym/Raym.h"
+#include "ry0/iPTd/FFmpeg.h"
+
+namespace ry0
+{
+namespace iPTd
+{
+
+class HTTPLiveStreaming : public FFmpeg
+{
+private:
+    Raym::String *      _source;
+    Raym::String *      _output_path;
+    Raym::String *      _index_name;
+    Raym::String *      _base_url;
+    int                         _counter;
+
+protected:
+    HTTPLiveStreaming();
+    ~HTTPLiveStreaming();
+
+    virtual bool readLine(Raym::String *line);
+
+public:
+    void run();
+
+public:
+    static HTTPLiveStreaming *alloc();
+    HTTPLiveStreaming *init();
+    HTTPLiveStreaming *retain();
+    HTTPLiveStreaming *autorelease();
+
+    void setSource(Raym::String *source);
+    void setOutputPath(Raym::String *output_path);
+    void setIndexName(Raym::String *index_name);
+
+    bool start();
+
+    Raym::String *indexPath();
+
+    virtual const char *className();
+};
+
+} // iPTd
+} // ry0
diff --git a/src/ry0/iPTd/Reservation.cpp b/src/ry0/iPTd/Reservation.cpp
deleted file mode 100644 (file)
index 5d47374..0000000
+++ /dev/null
@@ -1,1235 +0,0 @@
-/**\r
- * @file Reservation.cpp\r
- *\r
- */\r
-\r
-#include <time.h>\r
-#include <fcntl.h>\r
-#include <io.h>\r
-#include <direct.h>\r
-\r
-\r
-#define DBG_LEVEL 3\r
-#include "Raym/Log.h"\r
-\r
-#include "keys.h"\r
-#include "mpeg2/ts/Analyzer.h"\r
-#include "ry0/iPTd/Reservation.h"\r
-#include "ry0/iPTd/Controller.h"\r
-\r
-using namespace Raym;\r
-using namespace ry0::device;\r
-\r
-namespace ry0\r
-{\r
-namespace iPTd\r
-{\r
-\r
-static const long long CMD_PERIODIC                 = 0x0001;   // 周期処理\r
-static const long long CMD_COLLECT_EPG_ISDB_S       = 0x0002;   // 番組情報取得(ISDB-S)\r
-static const long long CMD_COLLECT_EPG_ISDB_T       = 0x0003;   // 番組情報取得(ISDB-T)\r
-\r
-static const time_t OFFSET_OF_START_TIME            = -2;       // 録画開始時刻の補正(秒単位)\r
-static const time_t OFFSET_OF_END_TIME              = -3;       // 録画停止時刻の補正(秒単位)\r
-static const time_t OFFSET_OF_WAKEUP                = -240;     // 起動スケジュールの補正(秒単位)  注:休止するまでの時間(DEF_SUSPEND_TIME)よりも短くすること\r
-static const time_t OFFSET_OF_SUPPRESSION_TIME      = -600;     // 録画開始前に休止の抑制を開始する時間(秒単位)\r
-\r
-static const TimeInterval DEF_COLLECT_EPG_DELAY     = 1.0;\r
-static const TimeInterval DEF_COLLECT_EPG_RETRY     = 60.0;\r
-static const TimeInterval DEF_COLLECT_EPG_LIMIT_S   = 10.5;\r
-static const TimeInterval DEF_COLLECT_EPG_LIMIT_T   = 20.5;\r
-\r
-Reservation::Reservation()\r
-{\r
-    _controller         = NULL;\r
-    _tuners             = NULL;\r
-    _reservations_path  = NULL;\r
-    _reservations       = NULL;\r
-    _reservation_seq_id = -1;\r
-    _epgs_path          = NULL;\r
-    _epgs               = NULL;\r
-    _timer_periodic     = NULL;\r
-    _timer_epg_s        = NULL;\r
-    _timer_epg_t        = NULL;\r
-    _cancel_epg_collect = false;\r
-}\r
-\r
-Reservation::~Reservation()\r
-{\r
-    RELEASE(_reservations_path);\r
-    RELEASE(_reservations);\r
-    RELEASE(_epgs_path);\r
-    RELEASE(_epgs);\r
-    RELEASE(_timer_periodic);\r
-    RELEASE(_timer_epg_s);\r
-    RELEASE(_timer_epg_t);\r
-}\r
-\r
-Reservation *Reservation::alloc()\r
-{\r
-    return new Reservation();\r
-}\r
-\r
-Reservation *Reservation::initWithController(Controller *controller)\r
-{\r
-    _controller  = controller;\r
-    _tuners      = controller->_tuners;\r
-\r
-    _reservations_path = String::alloc()->initWithFormat("%s%s.iptd.reservations.plist", _controller->_system_path->cString(), Controller::_plist_prefix);\r
-    if (_reservations_path == NULL)\r
-    {\r
-        release();\r
-        return NULL;\r
-    }\r
-\r
-    _reservations = Dictionary::alloc()->initWithContentsOfFile(_reservations_path);\r
-    if (_reservations == NULL)\r
-    {\r
-        DebugLog1("reservations file: %s (created)", _reservations_path->cString());\r
-        _reservations = Dictionary::alloc()->initWithCapacity(0);\r
-        if (_reservations == NULL)\r
-        {\r
-            release();\r
-            return NULL;\r
-        }\r
-        _reservation_seq_id = 1;\r
-    }\r
-    else\r
-    {\r
-        DebugLog1("reservations file: %s", _reservations_path->cString());\r
-\r
-        _reservation_seq_id = _reservations->integerForKey(KEY_EPG_LAST_RESV_ID);\r
-    }\r
-\r
-    _epgs_path = String::alloc()->initWithFormat("%s%s.iptd.epgs.plist", _controller->_system_path->cString(), Controller::_plist_prefix);\r
-    if (_epgs_path == NULL)\r
-    {\r
-        release();\r
-        return NULL;\r
-    }\r
-\r
-    _epgs = Dictionary::alloc()->initWithContentsOfFile(_epgs_path);\r
-    if (_epgs == NULL)\r
-    {\r
-        DebugLog1("epgs file: %s (created)", _epgs_path->cString());\r
-        _epgs = Dictionary::alloc()->initWithCapacity(0);\r
-        if (_epgs == NULL)\r
-        {\r
-            release();\r
-            return NULL;\r
-        }\r
-    }\r
-    else\r
-    {\r
-        DebugLog1("epgs file: %s", _epgs_path->cString());\r
-\r
-        // 過去の番組データは削除\r
-        removePastEPGs();\r
-    }\r
-\r
-    return this;\r
-}\r
-\r
-bool Reservation::canSuspend()\r
-{\r
-    bool result = true;\r
-\r
-    RaymLock(_controller);\r
-    if (((_timer_epg_s != NULL) && _timer_epg_s->valid()) ||\r
-        ((_timer_epg_t != NULL) && _timer_epg_t->valid()))\r
-    {\r
-        result = false;\r
-    }\r
-    RaymUnlock(_controller);\r
-\r
-    if (result)\r
-    {\r
-        result = canTerminate();\r
-    }\r
-\r
-    return result;\r
-}\r
-\r
-bool Reservation::canTerminate()\r
-{\r
-    bool result = true;\r
-\r
-    RaymLock(_controller);\r
-\r
-    time_t now = time(NULL);\r
-\r
-    for (int tuner = 0; tuner < _controller->_tuner_count; ++tuner)\r
-    {\r
-        Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
-        if ((array == NULL) || (array->count() == 0))\r
-        {\r
-            continue;\r
-        }\r
-\r
-        Dictionary *epg = (Dictionary *)array->objectAtIndex(0);\r
-        time_t start;\r
-        time_t end;\r
-        Controller::getTimeWithEPG(epg, &start, &end);\r
-\r
-        if (start + OFFSET_OF_SUPPRESSION_TIME <= now)\r
-        {\r
-            result = false;\r
-            break;\r
-        }\r
-    }\r
-\r
-    RaymUnlock(_controller);\r
-\r
-    return result;\r
-}\r
-\r
-void Reservation::systemWillSuspend()\r
-{\r
-    RaymLock(_controller);\r
-    _cancel_epg_collect = true;\r
-    RaymUnlock(_controller);\r
-\r
-    if ((_timer_epg_s != NULL) && _timer_epg_s->valid())\r
-    {\r
-        _timer_epg_s->invalidate();\r
-    }\r
-    if ((_timer_epg_t != NULL) && _timer_epg_t->valid())\r
-    {\r
-        _timer_epg_t->invalidate();\r
-    }\r
-    if ((_timer_periodic != NULL) && _timer_periodic->valid())\r
-    {\r
-        _timer_periodic->invalidate();\r
-    }\r
-\r
-    RaymLock(_controller);\r
-    _cancel_epg_collect = false;\r
-    RaymUnlock(_controller);\r
-}\r
-\r
-void Reservation::systemResumed()\r
-{\r
-    DebugLog2("%s", __FUNCTION__);\r
-\r
-    RaymLock(_controller);\r
-\r
-    _cancel_epg_collect = false;\r
-\r
-    if (_timer_periodic == NULL)\r
-    {\r
-        _timer_periodic = Timer::alloc()->initWithTimeInterval(1.0, this, (void *)CMD_PERIODIC, true);\r
-        if (_timer_periodic != NULL)\r
-        {\r
-            _timer_periodic->fire();\r
-        }\r
-    }\r
-\r
-    if (_timer_epg_s == NULL)\r
-    {\r
-        _timer_epg_s = Timer::alloc()->initWithTimeInterval(DEF_COLLECT_EPG_DELAY, this, (void *)CMD_COLLECT_EPG_ISDB_S, true);\r
-        if (_timer_epg_s != NULL)\r
-        {\r
-            _timer_epg_s->fire();\r
-        }\r
-    }\r
-\r
-    if (_timer_epg_t == NULL)\r
-    {\r
-        _timer_epg_t = Timer::alloc()->initWithTimeInterval(DEF_COLLECT_EPG_DELAY, this, (void *)CMD_COLLECT_EPG_ISDB_T, true);\r
-        if (_timer_epg_t != NULL)\r
-        {\r
-            _timer_epg_t->fire();\r
-        }\r
-    }\r
-\r
-    RaymUnlock(_controller);\r
-}\r
-\r
-//\r
-// 録画予約:サービスID/イベントID指定\r
-//   EPGデータから指定のサービスID/イベントIDのEPGを取り出し、EPG指定の録画予約をコール\r
-//\r
-bool Reservation::reserve(int service_id, int event_id)\r
-{\r
-    DebugLog2("Reservation::reserve(service_id, event_id)");\r
-\r
-    bool result = false;\r
-\r
-    RaymLock(_controller);\r
-\r
-    if (_epgs != NULL)\r
-    {\r
-        Dictionary *events = _epgs->dictionaryForKey(String::stringWithFormat("%d", service_id));\r
-        if (events != NULL)\r
-        {\r
-            Dictionary *epg = events->dictionaryForKey(String::stringWithFormat("%d", event_id));\r
-            if (epg != NULL)\r
-            {\r
-                result = reserve(epg);\r
-            }\r
-        }\r
-    }\r
-\r
-    RaymUnlock(_controller);\r
-\r
-    return result;\r
-}\r
-\r
-//\r
-// 録画予約:EPG指定\r
-//   EPGからサービスIDを取り出し、チューナ情報を検索して一致するサービスIDがあったらチューナ/EPG指定の録画予約を試行\r
-//\r
-bool Reservation::reserve(Dictionary *in_epg)\r
-{\r
-    DebugLog2("Reservation::reserve(epg)");\r
-\r
-    bool result = false;\r
-\r
-    RaymLock(_controller);\r
-\r
-    while (in_epg != NULL)\r
-    {\r
-        // EPGを複製\r
-        Dictionary *epg = Dictionary::dictionaryWithDictionary(in_epg);\r
-        if (epg == NULL)\r
-        {\r
-            DebugLog3("Dictionary::dictionaryWithDictionary() ng.");\r
-            break;\r
-        }\r
-\r
-        // サービスID取得\r
-        String *service_id = epg->stringForKey(KEY_EPG_SERVICE_ID);\r
-        if (service_id == NULL)\r
-        {\r
-            DebugLog3("epg->stringForKey(KEY_EPG_SERVICE_ID) ng.");\r
-            break;\r
-        }\r
-        DebugLog3("service_id: %s\n", service_id->cString());\r
-\r
-        // 全チューナ情報取得\r
-        Dictionary *tunerInfos = _controller->_props->dictionaryForKey(KEY_TUNERS);\r
-        if (tunerInfos == NULL)\r
-        {\r
-            DebugLog3("_props->dictionaryForKey(KEY_TUNERS) ng.");\r
-            break;\r
-        }\r
-\r
-        // チューナを検索\r
-        for (int i = 0; (!result) && (i < _controller->_tuner_count); ++i)\r
-        {\r
-            // チューナ情報取得\r
-            Dictionary *tunerInfo = tunerInfos->dictionaryForKey(_tuners[i]->name());\r
-            if (tunerInfo == NULL)\r
-            {\r
-                DebugLog3("tunerInfos->dictionaryForKey(_tuners[%d]->name()) ng.", i);\r
-                continue;\r
-            }\r
-\r
-            // 全チャンネル情報取得\r
-            Dictionary *channels = tunerInfo->dictionaryForKey(KEY_CHANNELS);\r
-            if (channels == NULL)\r
-            {\r
-                DebugLog3("tunerInfo->dictionaryForKey(KEY_CHANNELS) ng.");\r
-                continue;\r
-            }\r
-\r
-            // キー取得\r
-            Array *chkeys = channels->allKeys();\r
-            for (uint ch = 0; ch < chkeys->count(); ++ch)\r
-            {\r
-                // チャンネル情報取得\r
-                Dictionary *channel = channels->dictionaryForKey((String *)chkeys->objectAtIndex(ch));\r
-                if (channel == NULL)\r
-                {\r
-                    DebugLog3("channels->dictionaryForKey() ng.");\r
-                    continue;\r
-                }\r
-\r
-                // 全サービス情報取得\r
-                Array *services = (Array *)channel->objectForKey(KEY_SERVICES);\r
-                if (services == NULL)\r
-                {\r
-                    DebugLog3("channel->objectForKey() ng.");\r
-                    continue;\r
-                }\r
-\r
-                for (uint s = 0; s < services->count(); ++s)\r
-                {\r
-                    // サービス情報取得\r
-                    Dictionary *service = (Dictionary *)services->objectAtIndex(s);\r
-                    if (service == NULL)\r
-                    {\r
-                        DebugLog3("service->objectAtIndex() ng.");\r
-                        continue;\r
-                    }\r
-\r
-                    // サービスIDを比較\r
-                    String *sid = service->stringForKey(KEY_SERVICE_ID);\r
-                    if ((sid != NULL) && sid->isEqualToString(service_id))\r
-                    {\r
-                        // チャンネルを設定\r
-                        epg->setString((String *)chkeys->objectAtIndex(ch), KEY_EPG_CHANNEL);\r
-\r
-                        // 録画予約\r
-                        result = reserve(i, epg);\r
-\r
-                        // チャンネルループのカウンタを更新(=ループを終了させる)\r
-                        ch = chkeys->count();\r
-                        break;\r
-                    }\r
-                }\r
-            }\r
-        }\r
-\r
-        break;\r
-    }\r
-\r
-    RaymUnlock(_controller);\r
-\r
-    return result;\r
-}\r
-\r
-//\r
-// 録画予約:チューナ/EPG指定\r
-//\r
-bool Reservation::reserve(int tuner, Dictionary *in_epg)\r
-{\r
-    DebugLog2("Reservation::reserve(tuner, epg)");\r
-\r
-    bool result = false;\r
-\r
-    // lock\r
-    RaymLock(_controller);\r
-\r
-    while ((0 <= tuner) && (tuner < _controller->_tuner_count) && (in_epg != NULL))\r
-    {\r
-        Dictionary *epg = Dictionary::dictionaryWithDictionary(in_epg);\r
-        if (epg == NULL)\r
-        {\r
-            DebugLog3("Dictionary::dictionaryWithDictionary() ng.");\r
-            break;\r
-        }\r
-\r
-        Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
-        if (array == NULL)\r
-        {\r
-            array = Array::arrayWithCapacity(0);\r
-            _reservations->setObject(array, _tuners[tuner]->name());\r
-        }\r
-\r
-        time_t epg_start;\r
-        time_t epg_end;\r
-        Controller::getTimeWithEPG(epg, &epg_start, &epg_end);\r
-        DebugLog2("epg start: %ld, end: %ld\n", epg_start, epg_end);\r
-\r
-        time_t pre_start = 0;\r
-        time_t pre_end = 0;\r
-        pre_end = time(NULL);\r
-        DebugLog2("pre_end: %ld", pre_end);\r
-\r
-        for (uint i = 0; i < array->count(); ++i)\r
-        {\r
-            Dictionary *cur = (Dictionary *)array->objectAtIndex(i);\r
-            time_t cur_start;\r
-            time_t cur_end;\r
-            Controller::getTimeWithEPG(cur, &cur_start, &cur_end);\r
-            DebugLog2("cur start: %ld, end: %ld\n", cur_start, cur_end);\r
-            if ((pre_end <= epg_start) && (epg_end <= cur_start))\r
-            {\r
-                DebugLog2("insert: %d\n", i);\r
-                array->insertObject(epg, i);\r
-                result = true;\r
-                break;\r
-            }\r
-            pre_start = cur_start;\r
-            pre_end = cur_end;\r
-        }\r
-\r
-        if (!result)\r
-        {\r
-            if (pre_end <= epg_start)\r
-            {\r
-                DebugLog2("add\n");\r
-                array->addObject(epg);\r
-                result = true;\r
-            }\r
-            else\r
-            {\r
-                DebugLog2("no add\n");\r
-            }\r
-        }\r
-        if (result)\r
-        {\r
-            epg->setInteger(_reservation_seq_id, KEY_EPG_RESV_ID);\r
-            _reservation_seq_id = (_reservation_seq_id + 1) % 1000000;\r
-            _reservations->setInteger(_reservation_seq_id, KEY_EPG_LAST_RESV_ID);\r
-\r
-            //\r
-            _reservations->writeToFile(_reservations_path, true);\r
-        }\r
-\r
-        break;\r
-    }\r
-\r
-    // unlock\r
-    RaymUnlock(_controller);\r
-\r
-    return result;\r
-}\r
-\r
-//\r
-// tuner: 0 - (_tunerCount - 1)  cancel current\r
-// tuner: -1  cancel reserve_id\r
-//\r
-bool Reservation::cancel(int tuner, int reserve_id)\r
-{\r
-    bool result = false;\r
-\r
-    // lock\r
-    RaymLock(_controller);\r
-\r
-    //\r
-    if ((0 <= tuner) && (tuner < _controller->_tuner_count))\r
-    {\r
-        Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
-        if (array != NULL)\r
-        {\r
-            if (array->count() > 0)\r
-            {\r
-                Dictionary *epg = (Dictionary *)array->objectAtIndex(0);\r
-                String *status = epg->stringForKey(KEY_EPG_STATUS);\r
-                if (status != NULL)\r
-                {\r
-                    if (status->isEqualToString("running"))\r
-                    {\r
-                        epg->setString("stop", KEY_EPG_STATUS);\r
-                        result = true;\r
-                    }\r
-                }\r
-            }\r
-        }\r
-    }\r
-\r
-    //\r
-    else if ((tuner < 0) && (0 <= reserve_id) && (reserve_id < 1000000))\r
-    {\r
-        for (int i = 0; i < _controller->_tuner_count; ++i)\r
-        {\r
-            Array *array = _reservations->arrayForKey(_tuners[i]->name());\r
-            if (array != NULL)\r
-            {\r
-                for (uint j = 0; j < array->count(); ++j)\r
-                {\r
-                    Dictionary *epg = (Dictionary *)array->objectAtIndex(j);\r
-                    if (reserve_id == epg->integerForKey(KEY_EPG_RESV_ID))\r
-                    {\r
-                        String *status = epg->stringForKey(KEY_EPG_STATUS);\r
-                        if ((status != NULL) && status->isEqualToString("running"))\r
-                        {\r
-                            epg->setString("stop", KEY_EPG_STATUS);\r
-                        }\r
-                        else\r
-                        {\r
-                            array->removeObjectAtIndex(j);\r
-                        }\r
-                        result = true;\r
-                        break;\r
-                    }\r
-                }\r
-            }\r
-            if (result)\r
-            {\r
-                break;\r
-            }\r
-        }\r
-    }\r
-\r
-    if (result)\r
-    {\r
-        _reservations->writeToFile(_reservations_path, true);\r
-    }\r
-\r
-    // unlock\r
-    RaymUnlock(_controller);\r
-\r
-    return result;\r
-}\r
-\r
-void Reservation::updateSchedule()\r
-{\r
-    DebugLog2("Reservation::updateSchedule()");\r
-\r
-    RaymLock(_controller);\r
-\r
-    // レジューム時刻\r
-    time_t resume_time = 0;\r
-\r
-    // EPG収集時刻を取得\r
-    String *collect_epg_time = _controller->_props->stringForKey(KEY_COLLECT_EPG_TIME);\r
-    if (collect_epg_time != NULL)\r
-    {\r
-        Controller::getTimeWithString(collect_epg_time, &resume_time);\r
-    }\r
-\r
-    // 録画予約をチェック\r
-    for (int tuner = 0; tuner < _controller->_tuner_count; ++tuner)\r
-    {\r
-        Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
-        if ((array == NULL) || (array->count() == 0))\r
-        {\r
-            // next tuner\r
-            continue;\r
-        }\r
-\r
-        Dictionary *epg = (Dictionary *)array->objectAtIndex(0);\r
-        time_t start = 0;\r
-        time_t end = 0;\r
-        Controller::getTimeWithEPG(epg, &start, &end);\r
-        if ((start != 0) && (end != 0))\r
-        {\r
-            if (start < resume_time)\r
-            {\r
-                resume_time = start;\r
-            }\r
-        }\r
-    }\r
-\r
-    //\r
-    TM resume_tm;\r
-    resume_time += OFFSET_OF_WAKEUP;    // 起動時刻を 5分前に設定(OFFSET_OF_WAKEUPで調整)\r
-    if (localtime_s(&resume_tm, &resume_time) == 0)\r
-    {\r
-        _controller->resetWakeSchedule();\r
-        if (_controller->setWakeSchedule(resume_tm.tm_year + 1900, resume_tm.tm_mon + 1, resume_tm.tm_mday, resume_tm.tm_hour, resume_tm.tm_min))\r
-        {\r
-            DebugLog0("set wake schedule to \"%04d/%02d/%02d %02d:%02d:00\".",\r
-                      resume_tm.tm_year + 1900, resume_tm.tm_mon + 1, resume_tm.tm_mday, resume_tm.tm_hour, resume_tm.tm_min);\r
-        }\r
-    }\r
-\r
-    // unlock\r
-    RaymUnlock(_controller);\r
-}\r
-\r
-std::string Reservation::createVideoPath(int tuner)\r
-{\r
-    DebugLog2("Reservation::createVideoPath()");\r
-\r
-    std::string result = "";\r
-\r
-    while (true)\r
-    {\r
-        time_t now;\r
-        time(&now);\r
-        TM tm;\r
-        if (localtime_s(&tm, &now) != 0)\r
-        {\r
-            break;\r
-        }\r
-\r
-        result = _controller->_props->stringForKey(KEY_STORE_PATH)->cString();\r
-        DebugLog2("result: %s\n", result.c_str());\r
-\r
-        char tmp[128];\r
-        if (sprintf_s(tmp, sizeof(tmp), "\\%04d", tm.tm_year + 1900) < 0)\r
-        {\r
-            DebugLog0("sprintf_s() error: year\n");\r
-            result = "";\r
-            break;\r
-        }\r
-        result += tmp;\r
-        DebugLog2("result: %s\n", result.c_str());\r
-\r
-        STAT stat;\r
-        if (_stat(result.c_str(), &stat) != 0)\r
-        {\r
-            if (_mkdir(result.c_str()) != 0)\r
-            {\r
-                DebugLog0("_mkdir() error: year\n");\r
-                result = "";\r
-                break;\r
-            }\r
-            _stat(result.c_str(), &stat);\r
-        }\r
-        if ((stat.st_mode & _S_IFDIR) != _S_IFDIR)\r
-        {\r
-            DebugLog0("%s is not directory.\n", result.c_str());\r
-            result = "";\r
-            break;\r
-        }\r
-\r
-        if (sprintf_s(tmp, sizeof(tmp), "\\%02d", tm.tm_mon + 1) < 0)\r
-        {\r
-            DebugLog0("sprintf_s() error: month\n");\r
-            result = "";\r
-            break;\r
-        }\r
-        result += tmp;\r
-        DebugLog2("result: %s\n", result.c_str());\r
-\r
-        if (_stat(result.c_str(), &stat) != 0)\r
-        {\r
-            if (_mkdir(result.c_str()) != 0)\r
-            {\r
-                DebugLog0("_mkdir() error: month\n");\r
-                result = "";\r
-                break;\r
-            }\r
-            _stat(result.c_str(), &stat);\r
-        }\r
-        if ((stat.st_mode & _S_IFDIR) != _S_IFDIR)\r
-        {\r
-            DebugLog0("%s is not directory.", result.c_str());\r
-            result = "";\r
-            break;\r
-        }\r
-\r
-        if (sprintf_s(tmp, sizeof(tmp),\r
-                      "\\%04d%02d%02d_%02d%02d%02d_%03d_%s.ts",\r
-                      tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,\r
-                      _tuners[tuner]->channel(), _tuners[tuner]->name()) < 0)\r
-        {\r
-            DebugLog0("sprintf_s() error: filename");\r
-            result = "";\r
-            break;\r
-        }\r
-        result += tmp;\r
-        DebugLog2("result: %s\n", result.c_str());\r
-\r
-        break;\r
-    }\r
-\r
-    return result;\r
-}\r
-\r
-void Reservation::removePastEPGs()\r
-{\r
-    DebugLog2("Reservation::removePastEPGs()");\r
-\r
-    time_t now = time(NULL);\r
-\r
-    RaymLock(_controller);\r
-\r
-    Array *keys1 = _epgs->allKeys();\r
-    for (uint i = 0; i < keys1->count(); ++i)\r
-    {\r
-        Dictionary *events = _epgs->dictionaryForKey((String *)keys1->objectAtIndex(i));\r
-        if (events != NULL)\r
-        {\r
-            Array *keys2 = events->allKeys();\r
-            for (uint j = 0; j < keys2->count(); ++j)\r
-            {\r
-                String *key = (String *)keys2->objectAtIndex(j);\r
-                Dictionary *epg = events->dictionaryForKey(key);\r
-                if (epg != NULL)\r
-                {\r
-                    time_t start = 0;\r
-                    time_t end = 0;\r
-                    Controller::getTimeWithEPG(epg, &start, &end);\r
-\r
-                    if (now > end)\r
-                    {\r
-                        events->removeObjectForKey(key);\r
-                    }\r
-                }\r
-            }\r
-        }\r
-    }\r
-\r
-    RaymUnlock(_controller);\r
-}\r
-\r
-void Reservation::collectEPGsForTuner(int tuner, TimeInterval limit)\r
-{\r
-    DebugLog2("Reservation::collectEPGsForTuner(%d) start.", tuner);\r
-\r
-    // 既にロックされた状態でコールされる前提\r
-    bool locked = false;\r
-    RaymLock(_controller);\r
-    if ((0 <= tuner) && (tuner < _controller->_tuner_count))\r
-    {\r
-        locked = _tuners[tuner]->isLocked();\r
-    }\r
-    RaymUnlock(_controller);\r
-    if (!locked)\r
-    {\r
-        DebugLog2("Reservation::collectEPGsForTuner(%d) end(no locked).", tuner);\r
-        return;\r
-    }\r
-\r
-    // \r
-    Array *collected = Array::arrayWithCapacity(0);\r
-\r
-    // 開始時刻取得\r
-    Date *start = Date::date();\r
-\r
-    // 取得開始\r
-    bool done = false;\r
-    while (!done)\r
-    {\r
-        AutoreleasePool *pool = AutoreleasePool::alloc()->init();\r
-\r
-        // EPG取得\r
-        MPEG2::TS::Analyzer an;\r
-        an.setFlag(MPEG2::TS::Analyzer::FLAG_EIT);\r
-        _tuners[tuner]->setListener(&an);\r
-        Array *epgs = an.epgInfos();\r
-        _tuners[tuner]->setListener(NULL);\r
-\r
-        if (epgs != NULL)\r
-        {\r
-            // 取得成功\r
-            collected->addObjectsFromArray(epgs);\r
-        }\r
-\r
-        // リミットチェック\r
-        if (Date::date()->timeIntervalSinceDate(start) >= limit)\r
-        {\r
-            done = true;\r
-        }\r
-\r
-        // キャンセル確認\r
-        RaymLock(_controller);\r
-        if (!done)\r
-        {\r
-            done = _cancel_epg_collect;\r
-        }\r
-        RaymUnlock(_controller);\r
-\r
-        pool->release();\r
-    }\r
-\r
-    // lock\r
-    RaymLock(_controller);\r
-\r
-    for (uint j = 0; j < collected->count(); ++j)\r
-    {\r
-        Dictionary *epg1 = (Dictionary *)collected->objectAtIndex(j);\r
-\r
-        if (epg1->stringForKey(KEY_EPG_TITLE) == NULL)\r
-        {\r
-            // タイトルが無い場合は不要\r
-            continue;\r
-        }\r
-\r
-        // Service ID を Primary Key\r
-        String *key = epg1->stringForKey(KEY_EPG_SERVICE_ID);\r
-        if (key != NULL)\r
-        {\r
-            Dictionary *epgs_of_service = _epgs->dictionaryForKey(key);\r
-            if (epgs_of_service == NULL)\r
-            {\r
-                // 無い場合は作成\r
-                epgs_of_service = Dictionary::dictionaryWithCapacity(0);\r
-                _epgs->setObject(epgs_of_service, key);\r
-            }\r
-            // Event ID を Secondary Key\r
-            key = epg1->stringForKey(KEY_EPG_EVENT_ID);\r
-            if (key != NULL)\r
-            {\r
-                epgs_of_service->setObject(epg1, key);\r
-            }\r
-        }\r
-    }\r
-\r
-    // ファイルへ書き出し\r
-    _epgs->writeToFile(_epgs_path, true);\r
-\r
-    // unlock\r
-    RaymUnlock(_controller);\r
-\r
-    DebugLog2("Reservation::collectEPGsForTuner(%d) end.", tuner);\r
-}\r
-\r
-bool Reservation::collectEPGs(Tuner::Type type)\r
-{\r
-    DebugLog0("Reservation::collectEPGs(%s) start.", type == Tuner::ISDB_S ? "ISDB-S" : "ISDB-T");\r
-\r
-    // 使用するチューナを決定\r
-    int tuner = 0;\r
-    bool locked = false;\r
-    RaymLock(_controller);\r
-    for (int i = _controller->_tuner_count - 1; i >= 0; --i)\r
-    {\r
-        if (_controller->isTunerEnabled(i))\r
-        {\r
-            if ((!_tuners[i]->isLocked()) && (_tuners[i]->type() == type))\r
-            {\r
-                if (_tuners[i]->lock())\r
-                {\r
-                    tuner = i;\r
-                    locked = true;\r
-                    break;\r
-                }\r
-            }\r
-        }\r
-    }\r
-    RaymUnlock(_controller);\r
-\r
-    // ロック確認\r
-    if (!locked)\r
-    {\r
-        DebugLog0("Controller::collectEPGs(%s) end(Can't locked).", type == Tuner::ISDB_S ? "ISDB-S" : "ISDB-T");\r
-        // ロックできない場合は収集しない\r
-        return false;\r
-    }\r
-\r
-    bool canceled = false;\r
-    DebugLog0("start collect EPG of \"%s(#%d)\".", _tuners[tuner]->name(), tuner);\r
-\r
-    // 現在のチャンネルを保存\r
-    int channel = _tuners[tuner]->channel();\r
-\r
-    // チャンネルを変更しつつEPGを取得\r
-    int max_channel = (type == Tuner::ISDB_S ? Tuner::MAX_CHANNELS_ISDB_S : Tuner::MAX_CHANNELS_ISDB_T);\r
-    for (int ch = 0; ch <= max_channel; ++ch)\r
-    {\r
-        // チャンネルが有効かチェック\r
-        if (_controller->isChannelEnabled(tuner, ch))\r
-        {\r
-            // チャンネル設定\r
-            if (_tuners[tuner]->setChannel(ch))\r
-            {\r
-                // EPG取集\r
-                collectEPGsForTuner(tuner, ((type == Tuner::ISDB_S) ? DEF_COLLECT_EPG_LIMIT_S : DEF_COLLECT_EPG_LIMIT_T));\r
-\r
-                RaymLock(_controller);\r
-                if (_cancel_epg_collect)\r
-                {\r
-                    ch = Tuner::MAX_CHANNELS_ISDB_T + 1;\r
-                    canceled = true;\r
-                }\r
-                RaymUnlock(_controller);\r
-\r
-                // キャンセル確認\r
-                //   終了不可 -> 録画待機中/録画中  なので、収集は諦める\r
-                if (!canTerminate())\r
-                {\r
-                    ch = Tuner::MAX_CHANNELS_ISDB_T + 1;\r
-                    canceled = true;\r
-                }\r
-            }\r
-        }\r
-    }\r
-\r
-    // 元のチャンネルに戻す\r
-    _tuners[tuner]->setChannel(channel);\r
-\r
-    RaymLock(_controller);\r
-\r
-    // チューナをアンロック\r
-    _tuners[tuner]->unlock();\r
-\r
-    RaymUnlock(_controller);\r
-\r
-    if (canceled)\r
-    {\r
-        DebugLog0("collect EPG of \"%s\" was canceled.", _tuners[tuner]->name());\r
-    }\r
-    else\r
-    {\r
-        DebugLog0("collect EPG of \"%s\" was finished.", _tuners[tuner]->name());\r
-\r
-        // 過去のEPGを削除\r
-        removePastEPGs();\r
-    }\r
-\r
-    DebugLog2("Reservation::collectEPGs(%s) end.", type == Tuner::ISDB_S ? "ISDB-S" : "ISDB-T");\r
-\r
-    return true;\r
-}\r
-\r
-void Reservation::periodic()\r
-{\r
-    bool need_update = false;\r
-\r
-#ifdef RAYM_MEMORY_CHECK\r
-//    DebugLog0("global_objc_count_ = %d", Raym::global_raym_count_);\r
-#endif\r
-\r
-    // lock\r
-    RaymLock(_controller);\r
-\r
-    // 現在時刻取得\r
-    time_t now = time(NULL);\r
-\r
-//    DebugLog2("periodic: %d", now);\r
-\r
-    //\r
-    for (int tuner = 0; tuner < _controller->_tuner_count; ++tuner)\r
-    {\r
-        Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
-        if ((array == NULL) || (array->count() == 0))\r
-        {\r
-            // next tuner\r
-            continue;\r
-        }\r
-\r
-        //\r
-        Dictionary *epg = (Dictionary *)array->objectAtIndex(0);\r
-        time_t start;\r
-        time_t end;\r
-        Controller::getTimeWithEPG(epg, &start, &end);\r
-        \r
-        //\r
-        // 録画停止要否チェック\r
-        //\r
-        bool stop_need = false;\r
-        while (true)\r
-        {\r
-            String *status = epg->stringForKey(KEY_EPG_STATUS);\r
-            if (status != NULL)\r
-            {\r
-                if (status->isEqualToString("stop"))\r
-                {\r
-                    stop_need = true;\r
-                    break;\r
-                }\r
-                if (!status->isEqualToString("running"))\r
-                {\r
-                    break;\r
-                }\r
-            }\r
-            if (end + OFFSET_OF_END_TIME <= now)\r
-            {\r
-                stop_need = true;\r
-            }\r
-            break;\r
-        }\r
-        if (stop_need)\r
-        {\r
-            DebugLog2("I try stop\n");\r
-            int fd = _tuners[tuner]->stopRecording();\r
-            if (fd < 0)\r
-            {\r
-                DebugLog1("stopRecording() error.\n");\r
-            }\r
-            else\r
-            {\r
-                DebugLog2("stopRecording() ok\n");\r
-                DebugLog0("stop recording of \"%s\"", _tuners[tuner]->name());\r
-                _close(fd);\r
-            }\r
-            array->removeObject(epg);\r
-            \r
-            if (array->count() > 0)\r
-            {\r
-                epg = (Dictionary *)array->objectAtIndex(0);\r
-            }\r
-            else\r
-            {\r
-                epg = NULL;\r
-            }\r
-            need_update = true;\r
-        }\r
-\r
-        if (epg == NULL)\r
-        {\r
-            // next tuner\r
-            continue;\r
-        }\r
-\r
-        //\r
-        // 録画開始要否チェック\r
-        //\r
-        bool start_need = false;\r
-        start = end = 0;\r
-        Controller::getTimeWithEPG(epg, &start, &end);\r
-        if ((start != 0) && (end != 0))\r
-        {\r
-            String *status = epg->stringForKey(KEY_EPG_STATUS);\r
-            if ((status == NULL) || !(status->isEqualToString("running")))\r
-            {\r
-                if (end + OFFSET_OF_END_TIME <= now)\r
-                {\r
-                    // 既に終了時間が経過しているので削除する\r
-                    array->removeObject(epg);\r
-                }\r
-                else if (start + OFFSET_OF_START_TIME <= now)\r
-                {\r
-                    start_need = true;\r
-                }\r
-            }\r
-        }\r
-\r
-        if (start_need)\r
-        {\r
-            DebugLog2("I need start.\n");\r
-            String *ch = epg->stringForKey(KEY_EPG_CHANNEL);\r
-            if (ch != NULL)\r
-            {\r
-                int channel = atoi(ch->cString());\r
-                DebugLog2("channel: %d\n", channel);\r
-                std::string videopath = createVideoPath(tuner);\r
-                if (videopath != "")\r
-                {\r
-                    DebugLog2("videopath: %s\n", videopath.c_str());\r
-                    int fd = -1;\r
-                    if (_sopen_s(&fd, videopath.c_str(),\r
-                                 (_O_CREAT | _O_EXCL | _O_WRONLY | _O_BINARY | _O_TRUNC), _SH_DENYRW, (_S_IREAD | _S_IWRITE)) == 0)\r
-                    {\r
-                        DebugLog2("open ok.\n");\r
-                        bool startResult = true;\r
-                        if (_tuners[tuner]->channel() != channel)\r
-                        {\r
-                            if (!_controller->setChannel(tuner, channel))\r
-                            {\r
-                                DebugLog3("setChannel() ng.");\r
-                                startResult = false;\r
-                            }\r
-                        }\r
-\r
-                        if (startResult)\r
-                        {\r
-                            if (_tuners[tuner]->startRecording(fd))\r
-                            {\r
-                                DebugLog2("startRecording() ok.");\r
-                                DebugLog0("start recording of \"%s\" to %s.", _tuners[tuner]->name(), videopath.c_str());\r
-                            }\r
-                            else\r
-                            {\r
-                                DebugLog3("Tuner::startRecording() failed.");\r
-                                startResult = false;\r
-                            }\r
-                        }\r
-\r
-                        if (startResult)\r
-                        {\r
-                            epg->setString("running", KEY_EPG_STATUS);\r
-                        }\r
-                        else\r
-                        {\r
-                            _close(fd);\r
-                        }\r
-                    }\r
-                    else\r
-                    {\r
-                        DebugLog0("open ng. 0x%08x\n", errno);\r
-                    }\r
-                }\r
-                else\r
-                {\r
-                    DebugLog0("Can't create videopath.\n");\r
-                }\r
-            }\r
-            else\r
-            {\r
-                DebugLog0("error.\n");\r
-            }\r
-        }\r
-    }\r
-\r
-    if (need_update)\r
-    {\r
-        //\r
-        _reservations->writeToFile(_reservations_path, true);\r
-    }\r
-\r
-    // EPG収集時刻を取得\r
-    String *collect_str = _controller->_props->stringForKey(KEY_COLLECT_EPG_TIME);\r
-    if (collect_str != NULL)\r
-    {\r
-        // 秒に変換\r
-        time_t collect_time = 0;\r
-        Controller::getTimeWithString(collect_str, &collect_time);\r
-\r
-        // 現在時刻と比較\r
-        if ((collect_time <= now) && (now < collect_time + 1))\r
-        {\r
-            // タイマが起動中か確認\r
-            if ((_timer_epg_s == NULL) || !_timer_epg_s->valid())\r
-            {\r
-                // EPG収集用タイマ起動(ISDB-S)\r
-                RELEASE(_timer_epg_s);\r
-                _timer_epg_s = Timer::alloc()->initWithTimeInterval(DEF_COLLECT_EPG_DELAY, this, (void *)CMD_COLLECT_EPG_ISDB_S, true);\r
-                if (_timer_epg_s != NULL)\r
-                {\r
-                    _timer_epg_s->fire();\r
-                }\r
-            }\r
-\r
-            // タイマが起動中か確認\r
-            if ((_timer_epg_t == NULL) || !_timer_epg_t->valid())\r
-            {\r
-                // EPG収集用タイマ起動(ISDB-T)\r
-                RELEASE(_timer_epg_t);\r
-                _timer_epg_t = Timer::alloc()->initWithTimeInterval(DEF_COLLECT_EPG_DELAY, this, (void *)CMD_COLLECT_EPG_ISDB_T, true);\r
-                if (_timer_epg_t != NULL)\r
-                {\r
-                    _timer_epg_t->fire();\r
-                }\r
-            }\r
-        }\r
-    }\r
-\r
-    // unlock\r
-    RaymUnlock(_controller);\r
-\r
-    //\r
-    // 1/100秒単位が 0 に近くなるように次回T.O.を微調整\r
-    // ただし、windowsは精度が低いので期待しないことw\r
-    //\r
-\r
-#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)\r
-    static const __time64_t DELTA_EPOCH_IN_MICROSECS = 11644473600000000Ui64;\r
-#else\r
-    static const __time64_t DELTA_EPOCH_IN_MICROSECS = 11644473600000000ULL;\r
-#endif\r
-    // 現在時刻を取得\r
-    FILETIME ft;\r
-    GetSystemTimeAsFileTime(&ft);\r
-\r
-    // EPOCH秒への変換\r
-    __time64_t now_sec;\r
-    __time64_t now_usec;\r
-    now_sec = ft.dwHighDateTime;\r
-    now_sec <<= 32;\r
-    now_sec |= ft.dwLowDateTime;\r
-    now_sec /= 10;  /*convert into microseconds*/\r
-    now_sec -= DELTA_EPOCH_IN_MICROSECS;\r
-    now_usec = (now_sec % 1000000UL);\r
-    now_sec = now_sec / 1000000UL;\r
-\r
-    TimeInterval interval = (TimeInterval)now_usec;\r
-    interval = interval / 1000000;\r
-    _timer_periodic->setTimeInterval(1.005 - interval);\r
-\r
-#ifdef RAYM_MEMORY_CHECK\r
-//    DebugLog0("global_objc_count_ = %d", Raym::global_raym_count_);\r
-#endif\r
-}\r
-\r
-void Reservation::timerExpired(Timer *timer, void *userInfo)\r
-{\r
-    switch ((long long)userInfo)\r
-    {\r
-    case CMD_PERIODIC:\r
-        periodic();\r
-        break;\r
-\r
-    case CMD_COLLECT_EPG_ISDB_S:\r
-        if (collectEPGs(Tuner::ISDB_S))\r
-        {\r
-            _timer_epg_s->setRepeats(false);\r
-        }\r
-        else\r
-        {\r
-            _timer_epg_s->setTimeInterval(DEF_COLLECT_EPG_RETRY);\r
-        }\r
-        break;\r
-\r
-    case CMD_COLLECT_EPG_ISDB_T:\r
-        if (collectEPGs(Tuner::ISDB_T))\r
-        {\r
-            _timer_epg_t->setRepeats(false);\r
-        }\r
-        else\r
-        {\r
-            _timer_epg_t->setTimeInterval(DEF_COLLECT_EPG_RETRY);\r
-        }\r
-        break;\r
-    }\r
-}\r
-\r
-} // iPTd\r
-} // ry0\r
diff --git a/src/ry0/iPTd/Reservation.h b/src/ry0/iPTd/Reservation.h
deleted file mode 100644 (file)
index 01938a5..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-/**\r
- * @file Reservation.h\r
- *\r
- */\r
-\r
-#pragma once\r
-\r
-#include "Raym/Raym.h"\r
-#include "ry0/device/Tuner.h"\r
-\r
-namespace ry0\r
-{\r
-namespace iPTd\r
-{\r
-\r
-class Controller;\r
-\r
-class Reservation : public Raym::Object,\r
-                    public Raym::TimerDelegate\r
-{\r
-private:\r
-    Controller *            _controller;\r
-    ry0::device::Tuner **   _tuners;\r
-\r
-    Raym::String *          _reservations_path;\r
-    Raym::Dictionary *      _reservations;\r
-    int                     _reservation_seq_id;\r
-    Raym::String *          _epgs_path;\r
-    Raym::Dictionary *      _epgs;\r
-    Raym::Timer *           _timer_periodic;\r
-    Raym::Timer *           _timer_epg_s;\r
-    Raym::Timer *           _timer_epg_t;\r
-    bool                    _cancel_epg_collect;\r
-\r
-protected:\r
-    Reservation();\r
-    ~Reservation();\r
-\r
-public:\r
-    static Reservation *alloc();\r
-    Reservation *initWithController(Controller *controller);\r
-\r
-    bool canSuspend();\r
-    bool canTerminate();\r
-    void systemWillSuspend();\r
-    void systemResumed();\r
-\r
-    void collectEPGsForTuner(int tuner, Raym::TimeInterval limit);\r
-    bool collectEPGs(ry0::device::Tuner::Type type);\r
-    void removePastEPGs();\r
-\r
-    bool reserve(int service_id, int event_id);\r
-    bool reserve(Raym::Dictionary *epg);\r
-    bool reserve(int tuner, Raym::Dictionary *epg);\r
-    bool cancel(int tuner, int reserve_id);\r
-    void updateKeywordsReservation();\r
-    void updateSchedule();\r
-\r
-    std::string createVideoPath(int tuner);\r
-    void periodic();\r
-\r
-    void timerExpired(Raym::Timer *timer, void *userInfo);\r
-};\r
-\r
-\r
-} // iPTd\r
-} // ry0\r
diff --git a/src/ry0/iPTd/Streaming.cpp b/src/ry0/iPTd/Streaming.cpp
deleted file mode 100644 (file)
index da4f53d..0000000
+++ /dev/null
@@ -1,335 +0,0 @@
-/**\r
- * @file Streaming.cpp\r
- *\r
- */\r
-\r
-#include <windows.h>\r
-#include <Iphlpapi.h>\r
-#include <time.h>\r
-\r
-#define DBG_LEVEL 0\r
-#include "Raym/Log.h"\r
-\r
-#include "keys.h"\r
-#include "ry0/iPTd/Streaming.h"\r
-#include "ry0/iPTd/Controller.h"\r
-\r
-using namespace Raym;\r
-\r
-namespace ry0\r
-{\r
-namespace iPTd\r
-{\r
-\r
-Streaming::Streaming()\r
-{\r
-    DebugLog2("%s", __FUNCTION__);\r
-\r
-    _controller     = NULL;\r
-    _tuners         = NULL;\r
-    _ctrls          = NULL;\r
-    _timer_periodic = NULL;\r
-}\r
-\r
-Streaming::~Streaming()\r
-{\r
-    // タイマ停止\r
-    if ((_timer_periodic != NULL) && _timer_periodic->valid())\r
-    {\r
-        _timer_periodic->invalidate();\r
-    }\r
-\r
-    RELEASE(_ctrls);\r
-    RELEASE(_timer_periodic);\r
-    _controller = NULL;\r
-\r
-    DebugLog2("%s", __FUNCTION__);\r
-}\r
-\r
-Streaming *Streaming::alloc()\r
-{\r
-    return new Streaming();\r
-}\r
-\r
-Streaming *Streaming::initWithController(Controller *controller)\r
-{\r
-    _controller  = controller;\r
-    _tuners      = controller->_tuners;\r
-\r
-    // 制御情報\r
-    _ctrls = Dictionary::alloc()->initWithCapacity(0);\r
-    if (_ctrls == NULL)\r
-    {\r
-        release();\r
-        return NULL;\r
-    }\r
-\r
-    // 周期タイマ起動\r
-    _timer_periodic = Timer::alloc()->initWithTimeInterval(1.0, this, NULL, true);\r
-    if (_timer_periodic == NULL)\r
-    {\r
-        release();\r
-        return NULL;\r
-    }\r
-\r
-    _timer_periodic->fire();\r
-\r
-    return this;\r
-}\r
-\r
-void Streaming::systemWillSuspend()\r
-{\r
-}\r
-\r
-void Streaming::systemResumed()\r
-{\r
-}\r
-\r
-void Streaming::mapping(int tuner, int channel, int port)\r
-{\r
-    Dictionary *udp_to_tuner_channel = _ctrls->dictionaryForKey(KEY_MAPPING_UDP_TO_TUNER_CHANNEL);\r
-    if (udp_to_tuner_channel == NULL)\r
-    {\r
-        udp_to_tuner_channel = Dictionary::dictionaryWithCapacity(0);\r
-        _ctrls->setObject(udp_to_tuner_channel, KEY_MAPPING_UDP_TO_TUNER_CHANNEL);\r
-    }\r
-\r
-    Dictionary *tuner_channel_to_udp = _ctrls->dictionaryForKey(KEY_MAPPING_TUNER_CHANNEL_TO_UDP);\r
-    if (tuner_channel_to_udp == NULL)\r
-    {\r
-        tuner_channel_to_udp = Dictionary::dictionaryWithCapacity(0);\r
-        _ctrls->setObject(tuner_channel_to_udp, KEY_MAPPING_TUNER_CHANNEL_TO_UDP);\r
-    }\r
-\r
-    char port_str[10];\r
-    sprintf_s(port_str, "%d", port);\r
-    char tuner_and_channel[10];\r
-    sprintf_s(tuner_and_channel, "%d,%d", tuner, channel);\r
-\r
-    udp_to_tuner_channel->setString(tuner_and_channel, port_str);\r
-    tuner_channel_to_udp->setString(port_str, tuner_and_channel);\r
-}\r
-\r
-void Streaming::timerExpired(Timer *timer, void *user_info)\r
-{\r
-    DebugLog2("%s", __FUNCTION__);\r
-\r
-    //\r
-    // UDPポート監視\r
-    //\r
-\r
-    // マッピング(UDPPort:tuner,ch)情報取得\r
-    Dictionary *mapping = NULL;\r
-    if ((_ctrls != NULL) && ((mapping = _ctrls->dictionaryForKey(KEY_MAPPING_UDP_TO_TUNER_CHANNEL)) != NULL))\r
-    {\r
-        // マッピング情報取得OK\r
-\r
-        // 使用中のUDPの情報を取得\r
-        // どれだけ使用中なのか不明なので、まずは必要サイズを調べる\r
-        DWORD size = 0;\r
-        if (GetExtendedUdpTable(NULL, &size, true, AF_INET, UDP_TABLE_OWNER_PID, 0) == ERROR_INSUFFICIENT_BUFFER)\r
-        {\r
-            // ERROR_INSUFFICIENT_BUFFER の場合、必要なバッファサイズが size に格納される\r
-\r
-            // バッファ確保\r
-            PMIB_UDPTABLE_OWNER_PID udptable = (PMIB_UDPTABLE_OWNER_PID)malloc(size);\r
-            if (udptable != NULL)\r
-            {\r
-                // バッファ確保OK\r
-\r
-                // UDP情報取得\r
-                if (GetExtendedUdpTable(udptable, &size, true, AF_INET, UDP_TABLE_OWNER_PID, 0) == NO_ERROR)\r
-                {\r
-                    // 取得OK\r
-                    DebugLog3("udptable->dwNumEntries: %d", udptable->dwNumEntries);\r
-\r
-                    // 停止要否確認\r
-                    Dictionary *using_port = _ctrls->dictionaryForKey(KEY_UDP_IN_USE);\r
-                    if (using_port != NULL)\r
-                    {\r
-                        // key = 使用中ポート\r
-                        Array *using_ports = using_port->allKeys();\r
-                        if (using_ports != NULL)\r
-                        {\r
-                            // 使用中ポートでループ\r
-                            for (uint i = 0; i < using_ports->count(); ++i)\r
-                            {\r
-                                // 停止要否フラグ\r
-                                bool stop_need = true;\r
-\r
-                                // 使用中のUDP情報でループ\r
-                                for (uint j = 0; j < udptable->dwNumEntries; ++j)\r
-                                {\r
-                                    if (((String *)using_ports->objectAtIndex(i))->intValue() == ntohs((WORD)udptable->table[j].dwLocalPort))\r
-                                    {\r
-                                        // 使用中なので停止不要\r
-                                        stop_need = false;\r
-                                        break;\r
-                                    }\r
-                                }\r
-\r
-                                // 停止要否\r
-                                if (stop_need)\r
-                                {\r
-                                    // マッピング情報を取得\r
-                                    String *tuner_and_channel = mapping->stringForKey((String *)using_ports->objectAtIndex(i));\r
-                                    if (tuner_and_channel != NULL)\r
-                                    {\r
-                                         // チューナとチャンネルに分割\r
-                                        Range r = tuner_and_channel->rangeOfString(",");\r
-                                        if (r.location != NotFound)\r
-                                        {\r
-                                            int tuner = tuner_and_channel->substringToIndex(r.location)->intValue();\r
-                                            int channel = tuner_and_channel->substringFromIndex(r.location + 1)->intValue();\r
-                                            DebugLog3("tuner: %d, channel: %d", tuner, channel);\r
-\r
-                                            DebugLog0("auto streaming stop: %s", ((String *)using_ports->objectAtIndex(i))->cString());\r
-\r
-                                            _tuners[tuner]->stopStreaming();\r
-                                            using_port->removeObjectForKey((String *)using_ports->objectAtIndex(i));\r
-                                        }\r
-                                    }\r
-                                }\r
-                            }\r
-                        }\r
-                    }\r
-\r
-\r
-                    // 起動要否確認\r
-                    for (uint i = 0; i < udptable->dwNumEntries; ++i)\r
-                    {\r
-                        // ポート番号を文字列に変換して\r
-                        char port[10];\r
-                        sprintf_s(port, "%d", ntohs((WORD)udptable->table[i].dwLocalPort));\r
-                        DebugLog3("port = %s", port);\r
-\r
-                        // マッピング情報を取得\r
-                        String *tuner_and_channel = mapping->stringForKey(port);\r
-                        if (tuner_and_channel != NULL)\r
-                        {\r
-                            // 取得OK: 監視対象ポートが使用されている\r
-\r
-                            // 使用アプリを調べる\r
-                            bool auto_streaming = false;\r
-                            char exec_path[MAX_PATH];\r
-                            memset(exec_path, 0, sizeof(exec_path));\r
-\r
-                            // プロセスハンドル取得\r
-                            size_t returnValue;\r
-                            HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, udptable->table[i].dwOwningPid);\r
-                            if (hProcess != NULL)\r
-                            {\r
-                                TCHAR exec[MAX_PATH];\r
-                                memset(exec, 0, sizeof(exec));\r
-                                DWORD len = sizeof(exec) - 1;\r
-\r
-                                // イメージ取得\r
-                                if (QueryFullProcessImageName(hProcess, 0, exec, &len))\r
-                                {\r
-                                    // ワイド -> マルチ 変換\r
-                                    if (wcstombs_s(&returnValue, exec_path, sizeof(exec_path), exec, _TRUNCATE) == 0)\r
-                                    {\r
-                                        // 成功\r
-\r
-                                        // とりあえず、、、現状は "ffmpeg.exe" / "vlc.exe" / "Kodi.exe" があったら auto_streaming を true にする\r
-                                        if ((strstr(exec_path, "ffmpeg.exe") != NULL) ||\r
-                                            (strstr(exec_path, "vlc.exe") != NULL) ||\r
-                                            (strstr(exec_path, "Kodi.exe") != NULL))\r
-                                        {\r
-                                            auto_streaming = true;\r
-                                        }\r
-                                    }\r
-                                }\r
-\r
-                                // プロセスハンドル解放\r
-                                CloseHandle(hProcess);\r
-                            }\r
-\r
-                            if (auto_streaming)\r
-                            {\r
-                                // チューナとチャンネルに分割\r
-                                Range r = tuner_and_channel->rangeOfString(",");\r
-                                if (r.location != NotFound)\r
-                                {\r
-                                    int tuner = tuner_and_channel->substringToIndex(r.location)->intValue();\r
-                                    int channel = tuner_and_channel->substringFromIndex(r.location + 1)->intValue();\r
-                                    DebugLog3("tuner: %d, channel: %d", tuner, channel);\r
-\r
-                                    RaymLock(_controller);\r
-\r
-                                    // 非ストリーミング中 かつ 非レコーディング中 または チャンネルが同じ 場合\r
-                                    if (!_tuners[tuner]->isStreaming() && (!_tuners[tuner]->isRecording() || _tuners[tuner]->channel() == channel))\r
-                                    {\r
-                                        // ストリーミング開始可能\r
-\r
-                                        if (_tuners[tuner]->channel() != channel)\r
-                                        {\r
-                                            _controller->setChannel(tuner, channel);\r
-                                        }\r
-\r
-                                        SOCKADDR_IN dst_addr;\r
-                                        dst_addr.sin_family = AF_INET;\r
-                                        dst_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);\r
-                                        dst_addr.sin_port = (WORD)udptable->table[i].dwLocalPort;\r
-\r
-                                        if (_tuners[tuner]->startStreaming(&dst_addr))\r
-                                        {\r
-                                            // 成功\r
-                                            DebugLog0("auto streaming start: %d", ntohs((WORD)udptable->table[i].dwLocalPort));\r
-\r
-                                            // 使用中ポートに登録\r
-                                            using_port = _ctrls->dictionaryForKey(KEY_UDP_IN_USE);\r
-                                            if (using_port == NULL)\r
-                                            {\r
-                                                using_port = Dictionary::dictionaryWithCapacity(0);\r
-                                                _ctrls->setObject(using_port, KEY_UDP_IN_USE);\r
-                                            }\r
-                                            using_port->setBool(true, port);\r
-                                        }\r
-                                    }\r
-\r
-                                    RaymUnlock(_controller);\r
-                                }\r
-                            }\r
-                        }\r
-                    }\r
-                }\r
-\r
-                // バッファ解放\r
-                free(udptable);\r
-            }\r
-        }\r
-    }\r
-\r
-\r
-    //\r
-    // 1/100秒単位が 0 に近くなるように次回T.O.を微調整\r
-    //\r
-#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)\r
-    static const __time64_t DELTA_EPOCH_IN_MICROSECS = 11644473600000000Ui64;\r
-#else\r
-    static const __time64_t DELTA_EPOCH_IN_MICROSECS = 11644473600000000ULL;\r
-#endif\r
-    // 現在時刻を取得\r
-    FILETIME ft;\r
-    GetSystemTimeAsFileTime(&ft);\r
-\r
-    // EPOCH秒への変換\r
-    __time64_t now_sec;\r
-    __time64_t now_usec;\r
-    now_sec = ft.dwHighDateTime;\r
-    now_sec <<= 32;\r
-    now_sec |= ft.dwLowDateTime;\r
-    now_sec /= 10;  /*convert into microseconds*/\r
-    now_sec -= DELTA_EPOCH_IN_MICROSECS;\r
-    now_usec = (now_sec % 1000000UL);\r
-    now_sec = now_sec / 1000000UL;\r
-\r
-    TimeInterval interval = (TimeInterval)now_usec;\r
-    interval = interval / 1000000;\r
-    _timer_periodic->setTimeInterval(1.005 - interval);\r
-}\r
-\r
-\r
-} // iPTd\r
-} // ry0\r
diff --git a/src/ry0/iPTd/Streaming.h b/src/ry0/iPTd/Streaming.h
deleted file mode 100644 (file)
index b8a24c1..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/**\r
- * @file Streaming.h\r
- *\r
- */\r
-\r
-#pragma once\r
-\r
-#include "Raym/Raym.h"\r
-#include "ry0/device/Tuner.h"\r
-\r
-\r
-namespace ry0\r
-{\r
-namespace iPTd\r
-{\r
-\r
-class Controller;\r
-\r
-class Streaming : public Raym::Object,\r
-                  public Raym::TimerDelegate\r
-{\r
-private:\r
-    Controller *           _controller;\r
-    ry0::device::Tuner **  _tuners;\r
-\r
-    Raym::Dictionary *     _ctrls;\r
-    Raym::Timer *          _timer_periodic;\r
-\r
-protected:\r
-    Streaming();\r
-    ~Streaming();\r
-\r
-public:\r
-    static Streaming *alloc();\r
-    Streaming *initWithController(Controller *controller);\r
-\r
-    void systemWillSuspend();\r
-    void systemResumed();\r
-\r
-    void mapping(int tuner, int channel, int port);\r
-\r
-    void timerExpired(Raym::Timer *timer, void *user_info);\r
-};\r
-\r
-} // iPTd\r
-} // ry0\r