OSDN Git Service

ea53a97a9c26fada8b37fb3fb710318aac0d34f9
[iptd/iPTd.git] / src / ry0 / iPTd / Streaming.cpp
1 /**\r
2  * @file Streaming.cpp\r
3  *\r
4  */\r
5 \r
6 #include <windows.h>\r
7 #include <Iphlpapi.h>\r
8 #include <time.h>\r
9 \r
10 #define DBG_LEVEL 3\r
11 #include "Raym/Log.h"\r
12 \r
13 #include "keys.h"\r
14 #include "ry0/iPTd/Streaming.h"\r
15 #include "ry0/iPTd/Controller.h"\r
16 \r
17 using namespace Raym;\r
18 \r
19 namespace ry0\r
20 {\r
21 namespace iPTd\r
22 {\r
23 \r
24 Streaming::Streaming()\r
25 {\r
26     DebugLog2("%s", __FUNCTION__);\r
27 \r
28     _controller     = NULL;\r
29     _ctrls          = NULL;\r
30     _timer_periodic = NULL;\r
31 }\r
32 \r
33 Streaming::~Streaming()\r
34 {\r
35     // タイマ停止\r
36     if ((_timer_periodic != NULL) && _timer_periodic->valid())\r
37     {\r
38         _timer_periodic->invalidate();\r
39     }\r
40 \r
41     RELEASE(_ctrls);\r
42     RELEASE(_timer_periodic);\r
43     _controller = NULL;\r
44 \r
45     DebugLog2("%s", __FUNCTION__);\r
46 }\r
47 \r
48 Streaming *Streaming::alloc()\r
49 {\r
50     return new Streaming();\r
51 }\r
52 \r
53 Streaming *Streaming::initWithController(Controller *controller)\r
54 {\r
55     _controller = controller;\r
56 \r
57     // 制御情報\r
58     _ctrls = Dictionary::alloc()->initWithCapacity(0);\r
59 \r
60     // 周期タイマ起動\r
61     _timer_periodic = Timer::alloc()->initWithTimeInterval(1.0, this, NULL, true);\r
62     _timer_periodic->fire();\r
63 \r
64     return this;\r
65 }\r
66 \r
67 void Streaming::mapping(int tuner, int channel, int port)\r
68 {\r
69     Dictionary *udp_to_tuner_channel = _ctrls->dictionaryForKey(KEY_MAPPING_UDP_TO_TUNER_CHANNEL);\r
70     if (udp_to_tuner_channel == NULL)\r
71     {\r
72         udp_to_tuner_channel = Dictionary::dictionaryWithCapacity(0);\r
73         _ctrls->setObject(udp_to_tuner_channel, KEY_MAPPING_UDP_TO_TUNER_CHANNEL);\r
74     }\r
75 \r
76     Dictionary *tuner_channel_to_udp = _ctrls->dictionaryForKey(KEY_MAPPING_TUNER_CHANNEL_TO_UDP);\r
77     if (tuner_channel_to_udp == NULL)\r
78     {\r
79         tuner_channel_to_udp = Dictionary::dictionaryWithCapacity(0);\r
80         _ctrls->setObject(tuner_channel_to_udp, KEY_MAPPING_TUNER_CHANNEL_TO_UDP);\r
81     }\r
82 \r
83     char port_str[10];\r
84     sprintf_s(port_str, "%d", port);\r
85     char tuner_and_channel[10];\r
86     sprintf_s(tuner_and_channel, "%d,%d", tuner, channel);\r
87 \r
88     udp_to_tuner_channel->setString(tuner_and_channel, port_str);\r
89     tuner_channel_to_udp->setString(port_str, tuner_and_channel);\r
90 }\r
91 \r
92 void Streaming::timerExpired(Timer *timer, void *userInfo)\r
93 {\r
94     DebugLog2("%s", __FUNCTION__);\r
95 \r
96     //\r
97     // UDPポート監視\r
98     //\r
99 \r
100     // マッピング(UDPPort:tuner,ch)情報取得\r
101     Dictionary *mapping = NULL;\r
102     if ((_ctrls != NULL) && ((mapping = _ctrls->dictionaryForKey(KEY_MAPPING_UDP_TO_TUNER_CHANNEL)) != NULL))\r
103     {\r
104         // マッピング情報取得OK\r
105 \r
106         // 使用中のUDPの情報を取得\r
107         // どれだけ使用中なのか不明なので、まずは必要サイズを調べる\r
108         DWORD size = 0;\r
109         if (GetExtendedUdpTable(NULL, &size, true, AF_INET, UDP_TABLE_OWNER_PID, 0) == ERROR_INSUFFICIENT_BUFFER)\r
110         {\r
111             // ERROR_INSUFFICIENT_BUFFER の場合、必要なバッファサイズが size に格納される\r
112 \r
113             // バッファ確保\r
114             PMIB_UDPTABLE_OWNER_PID udptable = (PMIB_UDPTABLE_OWNER_PID)malloc(size);\r
115             if (udptable != NULL)\r
116             {\r
117                 // バッファ確保OK\r
118 \r
119                 // UDP情報取得\r
120                 if (GetExtendedUdpTable(udptable, &size, true, AF_INET, UDP_TABLE_OWNER_PID, 0) == NO_ERROR)\r
121                 {\r
122                     // 取得OK\r
123                     DebugLog3("udptable->dwNumEntries: %d", udptable->dwNumEntries);\r
124 \r
125                     // 停止要否確認\r
126                     Dictionary *using_port = _ctrls->dictionaryForKey(KEY_UDP_IN_USE);\r
127                     if (using_port != NULL)\r
128                     {\r
129                         // key = 使用中ポート\r
130                         Array *using_ports = using_port->allKeys();\r
131                         if (using_ports != NULL)\r
132                         {\r
133                             // 使用中ポートでループ\r
134                             for (uint i = 0; i < using_ports->count(); ++i)\r
135                             {\r
136                                 // 停止要否フラグ\r
137                                 bool stop_need = true;\r
138 \r
139                                 // 使用中のUDP情報でループ\r
140                                 for (uint j = 0; j < udptable->dwNumEntries; ++j)\r
141                                 {\r
142                                     if (((String *)using_ports->objectAtIndex(i))->intValue() == ntohs((WORD)udptable->table[j].dwLocalPort))\r
143                                     {\r
144                                         // 使用中なので停止不要\r
145                                         stop_need = false;\r
146                                         break;\r
147                                     }\r
148                                 }\r
149 \r
150                                 // 停止要否\r
151                                 if (stop_need)\r
152                                 {\r
153                                     // マッピング情報を取得\r
154                                     String *tuner_and_channel = mapping->stringForKey((String *)using_ports->objectAtIndex(i));\r
155                                     if (tuner_and_channel != NULL)\r
156                                     {\r
157                                          // チューナとチャンネルに分割\r
158                                         Range r = tuner_and_channel->rangeOfString(",");\r
159                                         if (r.location != NotFound)\r
160                                         {\r
161                                             int tuner = tuner_and_channel->substringToIndex(r.location)->intValue();\r
162                                             int channel = tuner_and_channel->substringFromIndex(r.location + 1)->intValue();\r
163                                             DebugLog3("tuner: %d, channel: %d", tuner, channel);\r
164 \r
165                                             DebugLog0("auto streaming stop: %s", ((String *)using_ports->objectAtIndex(i))->cString());\r
166 \r
167                                             _controller->_tuners[tuner]->stopStreaming();\r
168                                             using_port->removeObjectForKey((String *)using_ports->objectAtIndex(i));\r
169                                         }\r
170                                     }\r
171                                 }\r
172                             }\r
173                         }\r
174                     }\r
175 \r
176 \r
177                     // 起動要否確認\r
178                     for (uint i = 0; i < udptable->dwNumEntries; ++i)\r
179                     {\r
180                         // ポート番号を文字列に変換して\r
181                         char port[10];\r
182                         sprintf_s(port, "%d", ntohs((WORD)udptable->table[i].dwLocalPort));\r
183                         DebugLog3("port = %s", port);\r
184 \r
185                         // マッピング情報を取得\r
186                         String *tuner_and_channel = mapping->stringForKey(port);\r
187                         if (tuner_and_channel != NULL)\r
188                         {\r
189                             // 取得OK: 監視対象ポートが使用されている\r
190 \r
191                             // 使用アプリを調べる\r
192                             bool auto_streaming = false;\r
193                             char exec_path[MAX_PATH];\r
194                             memset(exec_path, 0, sizeof(exec_path));\r
195 \r
196                             // プロセスハンドル取得\r
197                             size_t returnValue;\r
198                             HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, udptable->table[i].dwOwningPid);\r
199                             if (hProcess != NULL)\r
200                             {\r
201                                 TCHAR exec[MAX_PATH];\r
202                                 memset(exec, 0, sizeof(exec));\r
203                                 DWORD len = sizeof(exec) - 1;\r
204 \r
205                                 // イメージ取得\r
206                                 if (QueryFullProcessImageName(hProcess, 0, exec, &len))\r
207                                 {\r
208                                     // ワイド -> マルチ 変換\r
209                                     if (wcstombs_s(&returnValue, exec_path, sizeof(exec_path), exec, _TRUNCATE) == 0)\r
210                                     {\r
211                                         // 成功\r
212 \r
213                                         // とりあえず、、、現状は "ffmpeg.exe" / "vlc.exe" / "Kodi.exe" があったら auto_streaming を true にする\r
214                                         if ((strstr(exec_path, "ffmpeg.exe") != NULL) ||\r
215                                             (strstr(exec_path, "vlc.exe") != NULL) ||\r
216                                             (strstr(exec_path, "Kodi.exe") != NULL))\r
217                                         {\r
218                                             auto_streaming = true;\r
219                                         }\r
220                                     }\r
221                                 }\r
222 \r
223                                 // プロセスハンドル解放\r
224                                 CloseHandle(hProcess);\r
225                             }\r
226 \r
227                             if (auto_streaming)\r
228                             {\r
229                                 // チューナとチャンネルに分割\r
230                                 Range r = tuner_and_channel->rangeOfString(",");\r
231                                 if (r.location != NotFound)\r
232                                 {\r
233                                     int tuner = tuner_and_channel->substringToIndex(r.location)->intValue();\r
234                                     int channel = tuner_and_channel->substringFromIndex(r.location + 1)->intValue();\r
235                                     DebugLog3("tuner: %d, channel: %d", tuner, channel);\r
236 \r
237                                     // lock\r
238                                     EnterCriticalSection(&_cs);\r
239 \r
240                                     // 非ストリーミング中 かつ 非レコーディング中 または チャンネルが同じ 場合\r
241                                     if (!_controller->_tuners[tuner]->isStreaming() && (!_controller->_tuners[tuner]->isRecording() || _controller->_tuners[tuner]->channel() == channel))\r
242                                     {\r
243                                         // ストリーミング開始可能\r
244 \r
245                                         if (_controller->_tuners[tuner]->channel() != channel)\r
246                                         {\r
247                                             _controller->setChannel(tuner, channel);\r
248                                         }\r
249 \r
250                                         SOCKADDR_IN dst_addr;\r
251                                         dst_addr.sin_family = AF_INET;\r
252                                         dst_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);\r
253                                         dst_addr.sin_port = (WORD)udptable->table[i].dwLocalPort;\r
254 \r
255                                         if (_controller->_tuners[tuner]->startStreaming(&dst_addr))\r
256                                         {\r
257                                             // 成功\r
258                                             DebugLog0("auto streaming start: %d", ntohs((WORD)udptable->table[i].dwLocalPort));\r
259 \r
260                                             // 使用中ポートに登録\r
261                                             using_port = _ctrls->dictionaryForKey(KEY_UDP_IN_USE);\r
262                                             if (using_port == NULL)\r
263                                             {\r
264                                                 using_port = Dictionary::dictionaryWithCapacity(0);\r
265                                                 _ctrls->setObject(using_port, KEY_UDP_IN_USE);\r
266                                             }\r
267                                             using_port->setBool(true, port);\r
268                                         }\r
269                                     }\r
270 \r
271                                     // unlock\r
272                                     LeaveCriticalSection(&_cs);\r
273                                 }\r
274                             }\r
275                         }\r
276                     }\r
277                 }\r
278 \r
279                 // バッファ解放\r
280                 free(udptable);\r
281             }\r
282         }\r
283     }\r
284 \r
285 \r
286     //\r
287     // 1/100秒単位が 0 に近くなるように次回T.O.を微調整\r
288     //\r
289 #if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)\r
290     static const __time64_t DELTA_EPOCH_IN_MICROSECS = 11644473600000000Ui64;\r
291 #else\r
292     static const __time64_t DELTA_EPOCH_IN_MICROSECS = 11644473600000000ULL;\r
293 #endif\r
294     // 現在時刻を取得\r
295     FILETIME ft;\r
296     GetSystemTimeAsFileTime(&ft);\r
297 \r
298     // EPOCH秒への変換\r
299     __time64_t now_sec;\r
300     __time64_t now_usec;\r
301     now_sec = ft.dwHighDateTime;\r
302     now_sec <<= 32;\r
303     now_sec |= ft.dwLowDateTime;\r
304     now_sec /= 10;  /*convert into microseconds*/\r
305     now_sec -= DELTA_EPOCH_IN_MICROSECS;\r
306     now_usec = (now_sec % 1000000UL);\r
307     now_sec = now_sec / 1000000UL;\r
308 \r
309     TimeInterval interval = (TimeInterval)now_usec;\r
310     interval = interval / 1000000;\r
311     _timer_periodic->setTimeInterval(1.005 - interval);\r
312 }\r
313 \r
314 \r
315 } // iPTd\r
316 } // ry0\r