OSDN Git Service

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