OSDN Git Service

udp streaming
authorosx86_pt1 <rmitachi@ta2.so-net.ne.jp>
Sun, 6 Mar 2016 14:16:42 +0000 (23:16 +0900)
committerosx86_pt1 <rmitachi@ta2.so-net.ne.jp>
Sun, 6 Mar 2016 14:16:42 +0000 (23:16 +0900)
src/ry0/iPTd/HTTPDaemon.cpp [new file with mode: 0644]
src/ry0/iPTd/HTTPDaemon.h [new file with mode: 0644]
src/ry0/iPTd/Streaming.cpp [new file with mode: 0644]
src/ry0/iPTd/Streaming.h [new file with mode: 0644]

diff --git a/src/ry0/iPTd/HTTPDaemon.cpp b/src/ry0/iPTd/HTTPDaemon.cpp
new file mode 100644 (file)
index 0000000..a0ddae9
--- /dev/null
@@ -0,0 +1,45 @@
+/**\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/HTTPDaemon.h"\r
+#include "ry0/iPTd/Controller.h"\r
+\r
+using namespace Raym;\r
+\r
+namespace ry0\r
+{\r
+namespace iPTd\r
+{\r
+\r
+HTTPDaemon::HTTPDaemon()\r
+{\r
+    _controller         = NULL;\r
+}\r
+\r
+HTTPDaemon::~HTTPDaemon()\r
+{\r
+    _controller = NULL;\r
+}\r
+\r
+HTTPDaemon *HTTPDaemon::alloc()\r
+{\r
+    return new HTTPDaemon();\r
+}\r
+\r
+HTTPDaemon *HTTPDaemon::initWithController(Controller *controller)\r
+{\r
+    _controller = controller;\r
+\r
+    return this;\r
+}\r
+\r
+} // iPTd\r
+} // ry0\r
diff --git a/src/ry0/iPTd/HTTPDaemon.h b/src/ry0/iPTd/HTTPDaemon.h
new file mode 100644 (file)
index 0000000..7614ec4
--- /dev/null
@@ -0,0 +1,33 @@
+/**\r
+ * @file HTTPDaemon.h\r
+ *\r
+ */\r
+\r
+#pragma once\r
+\r
+#include <Raym/Raym.h>\r
+\r
+namespace ry0\r
+{\r
+namespace iPTd\r
+{\r
+\r
+class Controller;\r
+\r
+class HTTPDaemon : public Raym::Object\r
+{\r
+private:\r
+    Controller *        _controller;\r
+\r
+protected:\r
+    HTTPDaemon();\r
+    ~HTTPDaemon();\r
+\r
+public:\r
+    static HTTPDaemon *alloc();\r
+    HTTPDaemon *initWithController(Controller *controller);\r
+};\r
+\r
+\r
+} // iPTd\r
+} // ry0\r
diff --git a/src/ry0/iPTd/Streaming.cpp b/src/ry0/iPTd/Streaming.cpp
new file mode 100644 (file)
index 0000000..ea53a97
--- /dev/null
@@ -0,0 +1,316 @@
+/**\r
+ * @file Streaming.cpp\r
+ *\r
+ */\r
+\r
+#include <windows.h>\r
+#include <Iphlpapi.h>\r
+#include <time.h>\r
+\r
+#define DBG_LEVEL 3\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
+    _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
+\r
+    // 制御情報\r
+    _ctrls = Dictionary::alloc()->initWithCapacity(0);\r
+\r
+    // 周期タイマ起動\r
+    _timer_periodic = Timer::alloc()->initWithTimeInterval(1.0, this, NULL, true);\r
+    _timer_periodic->fire();\r
+\r
+    return this;\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 *userInfo)\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
+                                            _controller->_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
+                                    EnterCriticalSection(&_cs);\r
+\r
+                                    // 非ストリーミング中 かつ 非レコーディング中 または チャンネルが同じ 場合\r
+                                    if (!_controller->_tuners[tuner]->isStreaming() && (!_controller->_tuners[tuner]->isRecording() || _controller->_tuners[tuner]->channel() == channel))\r
+                                    {\r
+                                        // ストリーミング開始可能\r
+\r
+                                        if (_controller->_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 (_controller->_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
+                                    // unlock\r
+                                    LeaveCriticalSection(&_cs);\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
new file mode 100644 (file)
index 0000000..b84968b
--- /dev/null
@@ -0,0 +1,41 @@
+/**\r
+ * @file Streaming.h\r
+ *\r
+ */\r
+\r
+#pragma once\r
+\r
+#include <Raym/Raym.h>\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
+    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 mapping(int tuner, int channel, int port);\r
+\r
+    // タイマ満了IF (from Timer)\r
+    void timerExpired(Raym::Timer *timer, void *userInfo);\r
+};\r
+\r
+\r
+} // iPTd\r
+} // ry0\r