From 526dd66a84f3593c0673b6edd39970d27a2c3434 Mon Sep 17 00:00:00 2001 From: osx86_pt1 Date: Sun, 6 Mar 2016 23:16:42 +0900 Subject: [PATCH] udp streaming --- src/ry0/iPTd/HTTPDaemon.cpp | 45 +++++++ src/ry0/iPTd/HTTPDaemon.h | 33 +++++ src/ry0/iPTd/Streaming.cpp | 316 ++++++++++++++++++++++++++++++++++++++++++++ src/ry0/iPTd/Streaming.h | 41 ++++++ 4 files changed, 435 insertions(+) create mode 100644 src/ry0/iPTd/HTTPDaemon.cpp create mode 100644 src/ry0/iPTd/HTTPDaemon.h create mode 100644 src/ry0/iPTd/Streaming.cpp create mode 100644 src/ry0/iPTd/Streaming.h diff --git a/src/ry0/iPTd/HTTPDaemon.cpp b/src/ry0/iPTd/HTTPDaemon.cpp new file mode 100644 index 0000000..a0ddae9 --- /dev/null +++ b/src/ry0/iPTd/HTTPDaemon.cpp @@ -0,0 +1,45 @@ +/** + * @file HTTPDaemon.cpp + * + */ + +#include + +#define DBG_LEVEL 3 +#include "Raym/Log.h" + +#include "keys.h" +#include "ry0/iPTd/HTTPDaemon.h" +#include "ry0/iPTd/Controller.h" + +using namespace Raym; + +namespace ry0 +{ +namespace iPTd +{ + +HTTPDaemon::HTTPDaemon() +{ + _controller = NULL; +} + +HTTPDaemon::~HTTPDaemon() +{ + _controller = NULL; +} + +HTTPDaemon *HTTPDaemon::alloc() +{ + return new HTTPDaemon(); +} + +HTTPDaemon *HTTPDaemon::initWithController(Controller *controller) +{ + _controller = controller; + + return this; +} + +} // iPTd +} // ry0 diff --git a/src/ry0/iPTd/HTTPDaemon.h b/src/ry0/iPTd/HTTPDaemon.h new file mode 100644 index 0000000..7614ec4 --- /dev/null +++ b/src/ry0/iPTd/HTTPDaemon.h @@ -0,0 +1,33 @@ +/** + * @file HTTPDaemon.h + * + */ + +#pragma once + +#include + +namespace ry0 +{ +namespace iPTd +{ + +class Controller; + +class HTTPDaemon : public Raym::Object +{ +private: + Controller * _controller; + +protected: + HTTPDaemon(); + ~HTTPDaemon(); + +public: + static HTTPDaemon *alloc(); + HTTPDaemon *initWithController(Controller *controller); +}; + + +} // iPTd +} // ry0 diff --git a/src/ry0/iPTd/Streaming.cpp b/src/ry0/iPTd/Streaming.cpp new file mode 100644 index 0000000..ea53a97 --- /dev/null +++ b/src/ry0/iPTd/Streaming.cpp @@ -0,0 +1,316 @@ +/** + * @file Streaming.cpp + * + */ + +#include +#include +#include + +#define DBG_LEVEL 3 +#include "Raym/Log.h" + +#include "keys.h" +#include "ry0/iPTd/Streaming.h" +#include "ry0/iPTd/Controller.h" + +using namespace Raym; + +namespace ry0 +{ +namespace iPTd +{ + +Streaming::Streaming() +{ + DebugLog2("%s", __FUNCTION__); + + _controller = NULL; + _ctrls = NULL; + _timer_periodic = NULL; +} + +Streaming::~Streaming() +{ + // タイマ停止 + if ((_timer_periodic != NULL) && _timer_periodic->valid()) + { + _timer_periodic->invalidate(); + } + + RELEASE(_ctrls); + RELEASE(_timer_periodic); + _controller = NULL; + + DebugLog2("%s", __FUNCTION__); +} + +Streaming *Streaming::alloc() +{ + return new Streaming(); +} + +Streaming *Streaming::initWithController(Controller *controller) +{ + _controller = controller; + + // 制御情報 + _ctrls = Dictionary::alloc()->initWithCapacity(0); + + // 周期タイマ起動 + _timer_periodic = Timer::alloc()->initWithTimeInterval(1.0, this, NULL, true); + _timer_periodic->fire(); + + return this; +} + +void Streaming::mapping(int tuner, int channel, int port) +{ + Dictionary *udp_to_tuner_channel = _ctrls->dictionaryForKey(KEY_MAPPING_UDP_TO_TUNER_CHANNEL); + if (udp_to_tuner_channel == NULL) + { + udp_to_tuner_channel = Dictionary::dictionaryWithCapacity(0); + _ctrls->setObject(udp_to_tuner_channel, KEY_MAPPING_UDP_TO_TUNER_CHANNEL); + } + + Dictionary *tuner_channel_to_udp = _ctrls->dictionaryForKey(KEY_MAPPING_TUNER_CHANNEL_TO_UDP); + if (tuner_channel_to_udp == NULL) + { + tuner_channel_to_udp = Dictionary::dictionaryWithCapacity(0); + _ctrls->setObject(tuner_channel_to_udp, KEY_MAPPING_TUNER_CHANNEL_TO_UDP); + } + + char port_str[10]; + sprintf_s(port_str, "%d", port); + char tuner_and_channel[10]; + sprintf_s(tuner_and_channel, "%d,%d", tuner, channel); + + udp_to_tuner_channel->setString(tuner_and_channel, port_str); + tuner_channel_to_udp->setString(port_str, tuner_and_channel); +} + +void Streaming::timerExpired(Timer *timer, void *userInfo) +{ + DebugLog2("%s", __FUNCTION__); + + // + // UDPポート監視 + // + + // マッピング(UDPPort:tuner,ch)情報取得 + Dictionary *mapping = NULL; + if ((_ctrls != NULL) && ((mapping = _ctrls->dictionaryForKey(KEY_MAPPING_UDP_TO_TUNER_CHANNEL)) != NULL)) + { + // マッピング情報取得OK + + // 使用中のUDPの情報を取得 + // どれだけ使用中なのか不明なので、まずは必要サイズを調べる + DWORD size = 0; + if (GetExtendedUdpTable(NULL, &size, true, AF_INET, UDP_TABLE_OWNER_PID, 0) == ERROR_INSUFFICIENT_BUFFER) + { + // ERROR_INSUFFICIENT_BUFFER の場合、必要なバッファサイズが size に格納される + + // バッファ確保 + PMIB_UDPTABLE_OWNER_PID udptable = (PMIB_UDPTABLE_OWNER_PID)malloc(size); + if (udptable != NULL) + { + // バッファ確保OK + + // UDP情報取得 + if (GetExtendedUdpTable(udptable, &size, true, AF_INET, UDP_TABLE_OWNER_PID, 0) == NO_ERROR) + { + // 取得OK + DebugLog3("udptable->dwNumEntries: %d", udptable->dwNumEntries); + + // 停止要否確認 + Dictionary *using_port = _ctrls->dictionaryForKey(KEY_UDP_IN_USE); + if (using_port != NULL) + { + // key = 使用中ポート + Array *using_ports = using_port->allKeys(); + if (using_ports != NULL) + { + // 使用中ポートでループ + for (uint i = 0; i < using_ports->count(); ++i) + { + // 停止要否フラグ + bool stop_need = true; + + // 使用中のUDP情報でループ + for (uint j = 0; j < udptable->dwNumEntries; ++j) + { + if (((String *)using_ports->objectAtIndex(i))->intValue() == ntohs((WORD)udptable->table[j].dwLocalPort)) + { + // 使用中なので停止不要 + stop_need = false; + break; + } + } + + // 停止要否 + if (stop_need) + { + // マッピング情報を取得 + String *tuner_and_channel = mapping->stringForKey((String *)using_ports->objectAtIndex(i)); + if (tuner_and_channel != NULL) + { + // チューナとチャンネルに分割 + Range r = tuner_and_channel->rangeOfString(","); + if (r.location != NotFound) + { + int tuner = tuner_and_channel->substringToIndex(r.location)->intValue(); + int channel = tuner_and_channel->substringFromIndex(r.location + 1)->intValue(); + DebugLog3("tuner: %d, channel: %d", tuner, channel); + + DebugLog0("auto streaming stop: %s", ((String *)using_ports->objectAtIndex(i))->cString()); + + _controller->_tuners[tuner]->stopStreaming(); + using_port->removeObjectForKey((String *)using_ports->objectAtIndex(i)); + } + } + } + } + } + } + + + // 起動要否確認 + for (uint i = 0; i < udptable->dwNumEntries; ++i) + { + // ポート番号を文字列に変換して + char port[10]; + sprintf_s(port, "%d", ntohs((WORD)udptable->table[i].dwLocalPort)); + DebugLog3("port = %s", port); + + // マッピング情報を取得 + String *tuner_and_channel = mapping->stringForKey(port); + if (tuner_and_channel != NULL) + { + // 取得OK: 監視対象ポートが使用されている + + // 使用アプリを調べる + bool auto_streaming = false; + char exec_path[MAX_PATH]; + memset(exec_path, 0, sizeof(exec_path)); + + // プロセスハンドル取得 + size_t returnValue; + HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, udptable->table[i].dwOwningPid); + if (hProcess != NULL) + { + TCHAR exec[MAX_PATH]; + memset(exec, 0, sizeof(exec)); + DWORD len = sizeof(exec) - 1; + + // イメージ取得 + if (QueryFullProcessImageName(hProcess, 0, exec, &len)) + { + // ワイド -> マルチ 変換 + if (wcstombs_s(&returnValue, exec_path, sizeof(exec_path), exec, _TRUNCATE) == 0) + { + // 成功 + + // とりあえず、、、現状は "ffmpeg.exe" / "vlc.exe" / "Kodi.exe" があったら auto_streaming を true にする + if ((strstr(exec_path, "ffmpeg.exe") != NULL) || + (strstr(exec_path, "vlc.exe") != NULL) || + (strstr(exec_path, "Kodi.exe") != NULL)) + { + auto_streaming = true; + } + } + } + + // プロセスハンドル解放 + CloseHandle(hProcess); + } + + if (auto_streaming) + { + // チューナとチャンネルに分割 + Range r = tuner_and_channel->rangeOfString(","); + if (r.location != NotFound) + { + int tuner = tuner_and_channel->substringToIndex(r.location)->intValue(); + int channel = tuner_and_channel->substringFromIndex(r.location + 1)->intValue(); + DebugLog3("tuner: %d, channel: %d", tuner, channel); + + // lock + EnterCriticalSection(&_cs); + + // 非ストリーミング中 かつ 非レコーディング中 または チャンネルが同じ 場合 + if (!_controller->_tuners[tuner]->isStreaming() && (!_controller->_tuners[tuner]->isRecording() || _controller->_tuners[tuner]->channel() == channel)) + { + // ストリーミング開始可能 + + if (_controller->_tuners[tuner]->channel() != channel) + { + _controller->setChannel(tuner, channel); + } + + SOCKADDR_IN dst_addr; + dst_addr.sin_family = AF_INET; + dst_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + dst_addr.sin_port = (WORD)udptable->table[i].dwLocalPort; + + if (_controller->_tuners[tuner]->startStreaming(&dst_addr)) + { + // 成功 + DebugLog0("auto streaming start: %d", ntohs((WORD)udptable->table[i].dwLocalPort)); + + // 使用中ポートに登録 + using_port = _ctrls->dictionaryForKey(KEY_UDP_IN_USE); + if (using_port == NULL) + { + using_port = Dictionary::dictionaryWithCapacity(0); + _ctrls->setObject(using_port, KEY_UDP_IN_USE); + } + using_port->setBool(true, port); + } + } + + // unlock + LeaveCriticalSection(&_cs); + } + } + } + } + } + + // バッファ解放 + free(udptable); + } + } + } + + + // + // 1/100秒単位が 0 に近くなるように次回T.O.を微調整 + // +#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS) + static const __time64_t DELTA_EPOCH_IN_MICROSECS = 11644473600000000Ui64; +#else + static const __time64_t DELTA_EPOCH_IN_MICROSECS = 11644473600000000ULL; +#endif + // 現在時刻を取得 + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + + // EPOCH秒への変換 + __time64_t now_sec; + __time64_t now_usec; + now_sec = ft.dwHighDateTime; + now_sec <<= 32; + now_sec |= ft.dwLowDateTime; + now_sec /= 10; /*convert into microseconds*/ + now_sec -= DELTA_EPOCH_IN_MICROSECS; + now_usec = (now_sec % 1000000UL); + now_sec = now_sec / 1000000UL; + + TimeInterval interval = (TimeInterval)now_usec; + interval = interval / 1000000; + _timer_periodic->setTimeInterval(1.005 - interval); +} + + +} // iPTd +} // ry0 diff --git a/src/ry0/iPTd/Streaming.h b/src/ry0/iPTd/Streaming.h new file mode 100644 index 0000000..b84968b --- /dev/null +++ b/src/ry0/iPTd/Streaming.h @@ -0,0 +1,41 @@ +/** + * @file Streaming.h + * + */ + +#pragma once + +#include + +namespace ry0 +{ +namespace iPTd +{ + +class Controller; + +class Streaming : public Raym::Object, + public Raym::TimerDelegate +{ +private: + Controller * _controller; + Raym::Dictionary * _ctrls; // 制御情報 + Raym::Timer * _timer_periodic; + +protected: + Streaming(); + ~Streaming(); + +public: + static Streaming *alloc(); + Streaming *initWithController(Controller *controller); + + void mapping(int tuner, int channel, int port); + + // タイマ満了IF (from Timer) + void timerExpired(Raym::Timer *timer, void *userInfo); +}; + + +} // iPTd +} // ry0 -- 2.11.0