--- /dev/null
+/**\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