// システム状態をチェックしてサスペンド可能かを返す\r
bool Controller::canSuspend()\r
{\r
- return isIdleState(true);\r
+ bool result = false;\r
+ RaymLock(this);\r
+ if (_initialized)\r
+ {\r
+ result = _reservation->canSuspend();\r
+ }\r
+ RaymUnlock(this);\r
+ return result;\r
}\r
\r
bool Controller::canTerminate()\r
{\r
- return isIdleState(false);\r
-}\r
-\r
-bool Controller::isIdleState(bool suspend)\r
-{\r
- DebugLog2("Controller::isIdleState() start.");\r
-\r
bool result = false;\r
-\r
- // lock\r
- EnterCriticalSection(&_cs);\r
-\r
- // 初期化済みか\r
- while (_initialized)\r
+ RaymLock(this);\r
+ if (_initialized)\r
{\r
- // EPG(ISDB-S)収集中か\r
- if (suspend && (_timer_epg_s != NULL) && _timer_epg_s->valid())\r
- {\r
- // サスペンド不可\r
- break;\r
- }\r
-\r
- // EPG(ISDB-T)収集中か\r
- if (suspend && (_timer_epg_t != NULL) && _timer_epg_t->valid())\r
- {\r
- // サスペンド不可\r
- break;\r
- }\r
-\r
- // 予約情報をチェック\r
result = _reservation->canTerminate();\r
- break;\r
}\r
-\r
- // unlock\r
- LeaveCriticalSection(&_cs);\r
-\r
- DebugLog2("Controller::isIdleState() end.");\r
-\r
+ RaymUnlock(this);\r
return result;\r
}\r
\r
{\r
DebugLog2("Controller::systemWillSuspend() start");\r
\r
- EnterCriticalSection(&_cs);\r
- _cancel_epg_collect = true;\r
- LeaveCriticalSection(&_cs);\r
-\r
- // タイマ停止\r
if ((_timer_restart != NULL) && _timer_restart->valid())\r
{\r
_timer_restart->invalidate();\r
}\r
- if ((_timer_epg_s != NULL) && _timer_epg_s->valid())\r
- {\r
- _timer_epg_s->invalidate();\r
- }\r
- if ((_timer_epg_t != NULL) && _timer_epg_t->valid())\r
- {\r
- _timer_epg_t->invalidate();\r
- }\r
- if ((_timer_periodic != NULL) && _timer_periodic->valid())\r
- {\r
- _timer_periodic->invalidate();\r
- }\r
- if ((_timer_periodic_2 != NULL) && _timer_periodic_2->valid())\r
- {\r
- _timer_periodic_2->invalidate();\r
- }\r
+ _reservation->systemWillSuspend();\r
+ _streaming->systemWillSuspend();\r
\r
- // lock\r
- EnterCriticalSection(&_cs);\r
+ RaymLock(this);\r
\r
- // タイマ解放\r
RELEASE(_timer_restart);\r
- RELEASE(_timer_epg_s);\r
- RELEASE(_timer_epg_t);\r
- RELEASE(_timer_periodic);\r
- RELEASE(_timer_periodic_2);\r
\r
- // スケジュール更新\r
-// updateSchedule();\r
+ _reservation->updateSchedule();\r
\r
- // 未初期化に設定\r
_initialized = false;\r
\r
- // チューナ解放\r
- for (int i = 0; i < _tunerCount; ++i)\r
+ for (int i = 0; i < _tuner_count; ++i)\r
{\r
if (_tuners[i] != NULL)\r
{\r
}\r
}\r
\r
- _cancel_epg_collect = false;\r
-\r
DebugLog0("system will suspend...");\r
\r
- // unlock\r
- LeaveCriticalSection(&_cs);\r
+ RaymUnlock(this);\r
\r
#ifdef OBJC_MEMORY_CHECK\r
DebugLog0("global_objc_count_ = %d", Raym::global_objc_count_);\r
#endif\r
-\r
DebugLog2("Controller::systemWillSuspend() end");\r
}\r
\r
\r
if (_timer_restart == NULL)\r
{\r
- // 再開タイマ起動\r
_timer_restart = Timer::alloc()->initWithTimeInterval(1.0, this, (void *)CMD_RESTART, false);\r
_timer_restart->fire();\r
}\r
\r
bool result = false;\r
\r
- if ((0 <= tuner) && (tuner < _tunerCount))\r
+ if ((0 <= tuner) && (tuner < _tuner_count))\r
{\r
- // lock\r
- EnterCriticalSection(&_cs);\r
+ RaymLock(this);\r
\r
Dictionary *tunersInfo = _props->dictionaryForKey(KEY_TUNERS);\r
if (tunersInfo != NULL)\r
}\r
}\r
\r
- // unlock\r
- LeaveCriticalSection(&_cs);\r
+ RaymUnlock(this);\r
}\r
\r
return result;\r
\r
bool result = false;\r
\r
- if ((0 <= tuner) && (tuner < _tunerCount))\r
+ if ((0 <= tuner) && (tuner < _tuner_count))\r
{\r
// lock\r
EnterCriticalSection(&_cs);\r
\r
bool result = false;\r
\r
- if ((0 <= tuner) && (tuner < _tunerCount) && (0 <= channel) && (channel <= Tuner::MAX_CHANNELS_ISDB_T))\r
+ if ((0 <= tuner) && (tuner < _tuner_count) && (0 <= channel) && (channel <= Tuner::MAX_CHANNELS_ISDB_T))\r
{\r
// lock\r
EnterCriticalSection(&_cs);\r
{\r
DebugLog2("Controller::scanChannel(%d)", tuner);\r
\r
- if ((tuner < 0) || (_tunerCount <= tuner))\r
+ if ((tuner < 0) || (_tuner_count <= tuner))\r
{\r
DebugLog3("Invalid tuner: %d", tuner);\r
return;\r
\r
int channel = -1;\r
\r
- if ((0 <= tuner) && (tuner < _tunerCount))\r
+ if ((0 <= tuner) && (tuner < _tuner_count))\r
{\r
// lock\r
EnterCriticalSection(&_cs);\r
\r
bool result = false;\r
\r
- if ((0 <= tuner) && (tuner < _tunerCount))\r
+ if ((0 <= tuner) && (tuner < _tuner_count))\r
{\r
// lock\r
EnterCriticalSection(&_cs);\r
\r
int Controller::restart()\r
{\r
- // lock\r
- EnterCriticalSection(&_cs);\r
+ RaymLock(this);\r
\r
// 未初期化に設定\r
_initialized = false;\r
\r
// チューナ解放\r
- for (int i = 0; i < _tunerCount; ++i)\r
+ for (int i = 0; i < _tuner_count; ++i)\r
{\r
if (_tuners[i] != NULL)\r
{\r
\r
\r
// チューナ初期化\r
- _tunerCount = ry0::device::TunerFactory::scan(_tuners, _multi2_dll);\r
+ _tuner_count = ry0::device::TunerFactory::scan(_tuners, _multi2_dll);\r
\r
// unlock\r
- LeaveCriticalSection(&_cs);\r
+ RaymUnlock(this);\r
\r
// チューナでループ\r
- for (int i = 0; i < _tunerCount; ++i)\r
+ for (int i = 0; i < _tuner_count; ++i)\r
{\r
// チューナが初期化済みか\r
if (!isTunerInitialized(i))\r
DebugLog0(" UDP Port Mapping :");\r
DebugLog0(" tuner, ch -> port");\r
int udpport = _props->integerForKey(KEY_BEGIN_UDP_PORT);\r
- for (int tuner = 0; tuner < _tunerCount; ++tuner)\r
+ for (int tuner = 0; tuner < _tuner_count; ++tuner)\r
{\r
int max_channel = (_tuners[tuner]->type() == Tuner::ISDB_S ? Tuner::MAX_CHANNELS_ISDB_S : Tuner::MAX_CHANNELS_ISDB_T);\r
for (int ch = 0; ch <= max_channel; ++ch)\r
}\r
}\r
\r
-#if 0\r
- // 周期タイマ起動\r
- _timer_periodic = Timer::alloc()->initWithTimeInterval(1.0, this, (void *)CMD_PERIODIC, true);\r
- _timer_periodic->fire();\r
-#endif\r
-\r
- // lock\r
- EnterCriticalSection(&_cs);\r
-\r
- // 初期化済みに設定\r
+ RaymLock(this);\r
_initialized = true;\r
+ RaymUnlock(this);\r
\r
- // unlock\r
- LeaveCriticalSection(&_cs);\r
-\r
- return (_tunerCount > 0);\r
+ return (_tuner_count > 0);\r
}\r
\r
int Controller::start()\r
_props = NULL;\r
_status_path = NULL;\r
_status = NULL;\r
- _epgs_path = NULL;\r
- _epgs = NULL;\r
\r
_timer_restart = NULL;\r
- _timer_periodic = NULL;\r
- _timer_periodic_2 = NULL;\r
- _timer_epg_s = NULL;\r
- _timer_epg_t = NULL;\r
\r
_multi2_dll = NULL;\r
\r
\r
_initialized = false;\r
\r
- _cancel_epg_collect = false;\r
-\r
_reservation = NULL;\r
_streaming = NULL;\r
_httpd = NULL;\r
\r
- _tunerCount = 0;\r
+ _tuner_count = 0;\r
for (int i = 0; i < ry0::device::MAX_TUNERS; ++i)\r
{\r
_tuners[i] = NULL;\r
}\r
\r
\r
- EnterCriticalSection(&_cs);\r
- _cancel_epg_collect = true;\r
- LeaveCriticalSection(&_cs);\r
-\r
- // タイマ停止\r
if ((_timer_restart != NULL) && _timer_restart->valid())\r
{\r
_timer_restart->invalidate();\r
}\r
- if ((_timer_epg_s != NULL) && _timer_epg_s->valid())\r
- {\r
- _timer_epg_s->invalidate();\r
- }\r
- if ((_timer_epg_t != NULL) && _timer_epg_t->valid())\r
- {\r
- _timer_epg_t->invalidate();\r
- }\r
- if ((_timer_periodic != NULL) && _timer_periodic->valid())\r
- {\r
- _timer_periodic->invalidate();\r
- }\r
- if ((_timer_periodic_2 != NULL) && _timer_periodic_2->valid())\r
- {\r
- _timer_periodic_2->invalidate();\r
- }\r
\r
- // チューナ解放\r
- for (int i = 0; i < _tunerCount; ++i)\r
+ _reservation->systemWillSuspend();\r
+ _streaming->systemWillSuspend();\r
+\r
+ for (int i = 0; i < _tuner_count; ++i)\r
{\r
if (_tuners[i] != NULL)\r
{\r
}\r
}\r
\r
- // スケジュールリセット\r
-// resetWakeSchedule();\r
-\r
+ resetWakeSchedule();\r
\r
- // 解放\r
RELEASE(_timer_restart);\r
- RELEASE(_timer_epg_s);\r
- RELEASE(_timer_epg_t);\r
- RELEASE(_timer_periodic);\r
- RELEASE(_timer_periodic_2);\r
RELEASE(_system_path);\r
RELEASE(_props_path);\r
RELEASE(_props);\r
RELEASE(_status_path);\r
RELEASE(_status);\r
- RELEASE(_epgs_path);\r
- RELEASE(_epgs);\r
\r
RELEASE(_reservation);\r
RELEASE(_streaming);\r
FreeLibrary(_multi2_dll);\r
}\r
\r
- // 終了\r
DebugLog0("finished.");\r
\r
return result;\r
#include "ry0/iPTd/HTTPD.h"\r
\r
#define VERSION "1.00"\r
-#define REVISION 3\r
+#define REVISION 11\r
\r
namespace ry0\r
{\r
Raym::Dictionary * _props; // プロパティ\r
Raym::String * _status_path; // ステータスファイルのパス\r
Raym::Dictionary * _status; // ステータス\r
- Raym::String * _epgs_path; // 番組データファイルのパス\r
- Raym::Dictionary * _epgs; // 番組データ\r
int _idle_count; // アイドルカウンタ\r
\r
- // 非同期処理用タイマ\r
Raym::Timer * _timer_restart; // 再開処理用\r
- Raym::Timer * _timer_periodic; // 周期処理用\r
- Raym::Timer * _timer_periodic_2; // 周期処理用2\r
- Raym::Timer * _timer_epg_s; // EPG(ISDB-S)収集用\r
- Raym::Timer * _timer_epg_t; // EPG(ISDB-T)収集用\r
\r
Reservation * _reservation; // 予約録画制御\r
Streaming * _streaming; // ストリーミング制御\r
\r
bool _initialized; // 初期化済み\r
HMODULE _multi2_dll;\r
- bool _cancel_epg_collect; // EPG収集キャンセル\r
\r
-\r
- int _tunerCount;\r
+ int _tuner_count;\r
ry0::device::Tuner * _tuners[ry0::device::MAX_TUNERS];\r
\r
\r
Raym::String *Controller::stationNameForServiceID(Raym::String *service_id);\r
\r
\r
- bool isIdleState(bool suspend);\r
- bool canSuspend();\r
-\r
- // 起動関連用IF (from Application)\r
int start();\r
void systemWillSuspend();\r
void systemResumed();\r
void detectIdle();\r
void detectNonIdle();\r
+ bool canSuspend();\r
bool canTerminate();\r
};\r
\r
HTTPD *HTTPD::initWithController(Controller *controller, int port, String *path)\r
{\r
_controller = controller;\r
- _tuner_count = controller->_tunerCount;\r
+ _tuner_count = controller->_tuner_count;\r
_tuners = controller->_tuners;\r
\r
_port = port;\r
}\r
}\r
\r
+ if (uri->isMatch("^/iptv.m3u8$"))\r
+ {\r
+ return responseForPlaylist(request, client);\r
+ }\r
+\r
//\r
// チャンネル設定\r
// /ttt/channel=nnn\r
//\r
- if (uri->isMatch("^/[0-9]{3}/channel=[0-9]{1,3}$") && (cgi == NULL))\r
+ else if (uri->isMatch("^/[0-9]{3}/channel=[0-9]{1,3}$") && (cgi == NULL))\r
{\r
String *ch = uri->substringFromIndex(13);\r
if (ch == NULL)\r
return result;\r
}\r
\r
+HTTPResponse *HTTPD::responseForPlaylist(HTTPRequest *request, SOCKADDR_IN *client)\r
+{\r
+ HTTPResponse *result = NULL;\r
+ // http://bit.ly/iptv_feb2015\r
+\r
+#if 0\r
+\r
+#EXTM3U\r
+\r
+#EXTINF:-1, [COLOR yellow]Updated 15/04/2015 @ 03:45 [/COLOR] \r
+plugin://plugin.video.youtube/?action=play_video&videoid=eMduu81gNgM\r
+#EXTINF:-1, [COLOR green] --Uk Live Tv--[/COLOR] \r
+plugin://plugin.video.youtube/?action=play_video&videoid=eMduu81gNgM\r
+#EXTINF:-1, [COLOR green] --Free Collection of IPTV sports links--[/COLOR] \r
+plugin://plugin.video.youtube/?action=play_video&videoid=eMduu81gNgM\r
+#EXTINF:-1, [COLOR red] --Links will go down. Please note that it will take time to update--[/COLOR]\r
+plugin://plugin.video.youtube/?action=play_video&videoid=eMduu81gNgM\r
+#EXTINF:-1, [COLOR red] --Please contact me at the husham.com website--[/COLOR]\r
+plugin://plugin.video.youtube/?action=play_video&videoid=eMduu81gNgM\r
+\r
+#EXTINF:-1, Sky sports news\r
+rtmp://89.248.172.159:443/liverepeater playpath=35 swfUrl=http://popeoftheplayers.eu/atdedead.swf pageUrl=http://popeoftheplayers.eu/crichd.php?id=35&width=600&height=450 token=#atd%#$ZH\r
+#EXTINF:-1,Bt sports 1\r
+rtmp://80.82.78.87:443/liverepeater/starsp pageUrl=http://xxxxxxxxxxxxxxxx.xx/rtmpe://strm.dcast.tv:1935/live/asdfadfaa/pageUrl=http://xxxxx.xx/rtmp://80.82.64.90:443/liverepeater/79/pageUrl=http://filotv.pw/rtmpe://strm.ukcast.tv:1935/redirect/FUNKTSN/pageUrl=http://ukcast.tv/rtmp://173.192.81.176:443/liverepeater/stream1/token%ZZri(nKa@#Z/pageUrl=http://hdcast.org/cdn.kingofplayers.com/rtmpe://46.246.29.152:1935/redirect/HDMNBC playpath=41?18?49?33?48?38?11 pageUrl=http://popeoftheplayers.eu/hdcast.org/rtmp://31.220.0.134:1935/live/tsn2/pageUrl=http://www.eucast.tv/rtmp://195.154.236.152:80/liverepeater/141449/pageUrl=http://goodcast.pw/rtmp://77.81.98.134/tv/bt1h28qn?v=pageUrl=http://castok.com/rtmp://89.46.102.70:443/liveedge/bt1pageUrl=http://hqstreams.tv/rtmpe://play.finecast.tv/live/hqbt1page/playpath=42?finecast.tv/rtmpe://cdn.hdcast.org:1935/redirect/swfUrl=http://www.hdcast.org/aplayer/jwplayer.flash.swfpageUrl=http://www.hdcast.org/token=Fo5_n0w?U.rA6l3-70w47ch@#8x12pX@ token=#atd%#$ZH\r
+\r
+#endif\r
+\r
+\r
+ std::string contents;\r
+ contents = "#EXTM3U\r\n";\r
+ contents += "\r\n";\r
+\r
+/*\r
+ Dictionary *tuner_channel_to_udp = _streaming_ctrls->dictionaryForKey(KEY_MAPPING_TUNER_CHANNEL_TO_UDP);\r
+ if (tuner_channel_to_udp != NULL)\r
+ {\r
+ Tuner::Type types[] = {Tuner::ISDB_T, Tuner::ISDB_S};\r
+ for (int t = 0; t < sizeof(types) / sizeof(Tuner::Type); ++t)\r
+ {\r
+ for (int i = 0; i < _tunerCount; ++i)\r
+ {\r
+ if (isTunerInitialized(i) && (_tuners[i]->type() == types[t]))\r
+ {\r
+ uint ch_max = ((types[t] == Tuner::ISDB_T) ? Tuner::MAX_CHANNELS_ISDB_T : Tuner::MAX_CHANNELS_ISDB_S);\r
+ for (uint ch = 0; ch <= ch_max; ++ch)\r
+ {\r
+ if (isChannelEnabled(i, ch))\r
+ {\r
+ char tuner_and_channel[10];\r
+ sprintf_s(tuner_and_channel, "%d,%d", i, ch);\r
+ char *chstr = strchr(tuner_and_channel, ',');\r
+ if (chstr != NULL)\r
+ {\r
+ ++chstr;\r
+ }\r
+\r
+ NSString *udp_str = tuner_channel_to_udp->stringForKey(tuner_and_channel);\r
+ if (udp_str != NULL)\r
+ {\r
+ contents += "#EXTINF:-1 tvg-id=\"";\r
+ contents += tuner_and_channel;\r
+ contents += "\" tvg-logo=\"";\r
+ contents += ((types[t] == Tuner::ISDB_T) ? "t_" : "s_");\r
+ if (chstr != NULL)\r
+ {\r
+ contents += chstr;\r
+ }\r
+ else\r
+ {\r
+ contents += "ffff";\r
+ }\r
+ contents += "\" group-title=\"";\r
+ contents += _tuners[i]->name();\r
+ contents += "\", ";\r
+ NSString *station_name = stationName(types[t], ch);\r
+ if (station_name != NULL)\r
+ {\r
+ contents += station_name->cString();\r
+ }\r
+ else\r
+ {\r
+ contents += tuner_and_channel;\r
+ }\r
+ contents += "\r\n";\r
+ contents += "udp://0.0.0.0:";\r
+ contents += udp_str->cString();\r
+ contents += "\r\n";\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ }\r
+*/\r
+ String *text = String::stringWithUTF8String(contents.c_str());\r
+ if (text != NULL)\r
+ {\r
+ result = responseWithUTF8Text(request, text);\r
+ }\r
+\r
+ return result;\r
+}\r
+\r
// positive response by XML\r
HTTPResponse *HTTPD::responseForSuccess(HTTPRequest *request)\r
{\r
return result;\r
}\r
\r
+HTTPResponse *HTTPD::responseWithUTF8Text(HTTPRequest *request, String *text)\r
+{\r
+ HTTPResponse *result = NULL;\r
+ if ((text != NULL) && (request != NULL))\r
+ {\r
+ // header\r
+ InternetTextMessageHeader *header = InternetTextMessageHeader::alloc()->init();\r
+ // Date\r
+ // Server\r
+ // Content-Encoding\r
+ // Last-Modified\r
+ // Content-Type\r
+ header->setFieldBodyWithName("text/plane; charset=UTF-8", "Content-Type");\r
+ // Connection\r
+ // Tranfer-Encoding\r
+ // Content-Length\r
+ header->setFieldBodyWithName(String::stringWithFormat("%I64u", text->length()), "Content-Length");\r
+\r
+ // body\r
+ InternetTextMessageBody *body = InternetTextMessageBody::alloc()->initWithString(text);\r
+\r
+ // message\r
+ InternetTextMessage *message = InternetTextMessage::alloc()->initWithHeaderAndBody(header, body);\r
+ RELEASE(header);\r
+ RELEASE(body);\r
+ if (message != NULL)\r
+ {\r
+// result = HTTPResponse::response();\r
+ result = HTTPResponse::alloc()->init();\r
+ result->setVersion(request->version());\r
+ result->setReason(NET::HTTPDaemon::reasonForStatus(200));\r
+ result->setStatus(200);\r
+ result->setMessage(message);\r
+ result->autorelease();\r
+ RELEASE(message);\r
+ }\r
+ }\r
+ return result;\r
+}\r
+\r
} // iPTd\r
} // ry0\r
\r
NET::HTTPResponse *request(NET::HTTPRequest *request, struct sockaddr_in *client);\r
NET::HTTPResponse *requestTunerControl(NET::HTTPRequest *request, struct sockaddr_in *client, int tuner);\r
+ NET::HTTPResponse *responseForPlaylist(NET::HTTPRequest *request, struct sockaddr_in *client);\r
\r
\r
static NET::HTTPResponse *responseForSuccess(NET::HTTPRequest *request);\r
static NET::HTTPResponse *responseForFailed(NET::HTTPRequest *request);\r
static NET::HTTPResponse *responseWithDictionary(NET::HTTPRequest *request, Raym::Dictionary *dictionary);\r
+ static NET::HTTPResponse *responseWithUTF8Text(NET::HTTPRequest *request, Raym::String *text);\r
};\r
\r
\r
#include "Raym/Log.h"\r
\r
#include "keys.h"\r
+#include "mpeg2/ts/Analyzer.h"\r
#include "ry0/iPTd/Reservation.h"\r
#include "ry0/iPTd/Controller.h"\r
\r
using namespace Raym;\r
+using namespace ry0::device;\r
\r
namespace ry0\r
{\r
namespace iPTd\r
{\r
\r
-static const time_t OFFSET_OF_START_TIME = -2; // 録画開始時刻の補正(秒単位)\r
-static const time_t OFFSET_OF_END_TIME = -3; // 録画停止時刻の補正(秒単位)\r
-static const time_t OFFSET_OF_WAKEUP = -240; // 起動スケジュールの補正(秒単位) 注:休止するまでの時間(DEF_SUSPEND_TIME)よりも短くすること\r
-static const time_t OFFSET_OF_SUPPRESSION_TIME = -600; // 録画開始前に休止の抑制を開始する時間(秒単位)\r
+static const long long CMD_PERIODIC = 0x0001; // 周期処理\r
+static const long long CMD_COLLECT_EPG_ISDB_S = 0x0002; // 番組情報取得(ISDB-S)\r
+static const long long CMD_COLLECT_EPG_ISDB_T = 0x0003; // 番組情報取得(ISDB-T)\r
+\r
+static const time_t OFFSET_OF_START_TIME = -2; // 録画開始時刻の補正(秒単位)\r
+static const time_t OFFSET_OF_END_TIME = -3; // 録画停止時刻の補正(秒単位)\r
+static const time_t OFFSET_OF_WAKEUP = -240; // 起動スケジュールの補正(秒単位) 注:休止するまでの時間(DEF_SUSPEND_TIME)よりも短くすること\r
+static const time_t OFFSET_OF_SUPPRESSION_TIME = -600; // 録画開始前に休止の抑制を開始する時間(秒単位)\r
+\r
+static const TimeInterval DEF_COLLECT_EPG_DELAY = 1.0;\r
+static const TimeInterval DEF_COLLECT_EPG_RETRY = 60.0;\r
+static const TimeInterval DEF_COLLECT_EPG_LIMIT_S = 10.5;\r
+static const TimeInterval DEF_COLLECT_EPG_LIMIT_T = 20.5;\r
\r
Reservation::Reservation()\r
{\r
_reservations_path = NULL;\r
_reservations = NULL;\r
_reservation_seq_id = -1;\r
+ _epgs_path = NULL;\r
+ _epgs = NULL;\r
+ _timer_periodic = NULL;\r
+ _timer_epg_s = NULL;\r
+ _timer_epg_t = NULL;\r
+ _cancel_epg_collect = false;\r
}\r
\r
Reservation::~Reservation()\r
{\r
RELEASE(_reservations_path);\r
RELEASE(_reservations);\r
+ RELEASE(_epgs_path);\r
+ RELEASE(_epgs);\r
+ RELEASE(_timer_periodic);\r
+ RELEASE(_timer_epg_s);\r
+ RELEASE(_timer_epg_t);\r
}\r
\r
Reservation *Reservation::alloc()\r
Reservation *Reservation::initWithController(Controller *controller)\r
{\r
_controller = controller;\r
- _tuner_count = controller->_tunerCount;\r
+ _tuner_count = controller->_tuner_count;\r
_tuners = controller->_tuners;\r
\r
_reservations_path = String::alloc()->initWithFormat("%s%s.iptd.reservations.plist", _controller->_system_path->cString(), Controller::_plist_prefix);\r
_reservation_seq_id = _reservations->integerForKey(KEY_EPG_LAST_RESV_ID);\r
}\r
\r
- _timer_periodic = Timer::alloc()->initWithTimeInterval(1.0, this, NULL, true);\r
- if (_timer_periodic == NULL)\r
+ _epgs_path = String::alloc()->initWithFormat("%s%s.iptd.epgs.plist", _controller->_system_path->cString(), Controller::_plist_prefix);\r
+ if (_epgs_path == NULL)\r
{\r
release();\r
return NULL;\r
}\r
\r
- _timer_periodic->fire();\r
+ _epgs = Dictionary::alloc()->initWithContentsOfFile(_epgs_path);\r
+ if (_epgs == NULL)\r
+ {\r
+ DebugLog1("because \"%s\" is not exists, created.", _epgs_path->cString());\r
+ _epgs = Dictionary::alloc()->initWithCapacity(0);\r
+ if (_epgs == NULL)\r
+ {\r
+ release();\r
+ return NULL;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ DebugLog1("epgs file: \"%s\"", _epgs_path->cString());\r
+\r
+ // 過去の番組データは削除\r
+ removePastEPGs();\r
+ }\r
\r
return this;\r
}\r
\r
+bool Reservation::canSuspend()\r
+{\r
+ bool result = true;\r
+\r
+ RaymLock(_controller);\r
+ if (((_timer_epg_s != NULL) && _timer_epg_s->valid()) ||\r
+ ((_timer_epg_t != NULL) && _timer_epg_t->valid()))\r
+ {\r
+ result = false;\r
+ }\r
+ RaymUnlock(_controller);\r
+\r
+ if (result)\r
+ {\r
+ result = canTerminate();\r
+ }\r
+\r
+ return result;\r
+}\r
+\r
bool Reservation::canTerminate()\r
{\r
bool result = true;\r
\r
- // 現在時刻取得\r
+ RaymLock(_controller);\r
+\r
time_t now = time(NULL);\r
\r
for (int tuner = 0; tuner < _tuner_count; ++tuner)\r
Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
if ((array == NULL) || (array->count() == 0))\r
{\r
- // next tuner\r
continue;\r
}\r
\r
- //\r
Dictionary *epg = (Dictionary *)array->objectAtIndex(0);\r
time_t start;\r
time_t end;\r
\r
if (start + OFFSET_OF_SUPPRESSION_TIME <= now)\r
{\r
- // サスペンド不可\r
result = false;\r
break;\r
}\r
}\r
\r
- return true;\r
+ RaymUnlock(_controller);\r
+\r
+ return result;\r
+}\r
+\r
+void Reservation::systemWillSuspend()\r
+{\r
+ RaymLock(_controller);\r
+ _cancel_epg_collect = true;\r
+ RaymUnlock(_controller);\r
+\r
+ if ((_timer_epg_s != NULL) && _timer_epg_s->valid())\r
+ {\r
+ _timer_epg_s->invalidate();\r
+ }\r
+ if ((_timer_epg_t != NULL) && _timer_epg_t->valid())\r
+ {\r
+ _timer_epg_t->invalidate();\r
+ }\r
+ if ((_timer_periodic != NULL) && _timer_periodic->valid())\r
+ {\r
+ _timer_periodic->invalidate();\r
+ }\r
+\r
+ RaymLock(_controller);\r
+ _cancel_epg_collect = false;\r
+ RaymUnlock(_controller);\r
+}\r
+\r
+void Reservation::systemResumed()\r
+{\r
+ RaymLock(_controller);\r
+\r
+ _cancel_epg_collect = false;\r
+\r
+ if (_timer_periodic == NULL)\r
+ {\r
+ _timer_periodic = Timer::alloc()->initWithTimeInterval(1.0, this, (void *)CMD_PERIODIC, true);\r
+ if (_timer_periodic != NULL)\r
+ {\r
+ _timer_periodic->fire();\r
+ }\r
+ }\r
+\r
+ if (_timer_epg_s == NULL)\r
+ {\r
+ _timer_epg_s = Timer::alloc()->initWithTimeInterval(DEF_COLLECT_EPG_DELAY, this, (void *)CMD_COLLECT_EPG_ISDB_S, true);\r
+ if (_timer_epg_s != NULL)\r
+ {\r
+ _timer_epg_s->fire();\r
+ }\r
+ }\r
+\r
+ if (_timer_epg_t == NULL)\r
+ {\r
+ _timer_epg_t = Timer::alloc()->initWithTimeInterval(DEF_COLLECT_EPG_DELAY, this, (void *)CMD_COLLECT_EPG_ISDB_T, true);\r
+ if (_timer_epg_t != NULL)\r
+ {\r
+ _timer_epg_t->fire();\r
+ }\r
+ }\r
+\r
+ RaymUnlock(_controller);\r
}\r
\r
//\r
return result;\r
}\r
\r
+void Reservation::updateSchedule()\r
+{\r
+ DebugLog2("Reservation::updateSchedule()");\r
+\r
+ RaymLock(_controller);\r
+\r
+ // レジューム時刻\r
+ time_t resume_time = 0;\r
+\r
+ // EPG収集時刻を取得\r
+ String *collect_epg_time = _controller->_props->stringForKey(KEY_COLLECT_EPG_TIME);\r
+ if (collect_epg_time != NULL)\r
+ {\r
+ Controller::getTimeWithString(collect_epg_time, &resume_time);\r
+ }\r
+\r
+ // 録画予約をチェック\r
+ for (int tuner = 0; tuner < _tuner_count; ++tuner)\r
+ {\r
+ Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
+ if ((array == NULL) || (array->count() == 0))\r
+ {\r
+ // next tuner\r
+ continue;\r
+ }\r
+\r
+ Dictionary *epg = (Dictionary *)array->objectAtIndex(0);\r
+ time_t start = 0;\r
+ time_t end = 0;\r
+ Controller::getTimeWithEPG(epg, &start, &end);\r
+ if ((start != 0) && (end != 0))\r
+ {\r
+ if (start < resume_time)\r
+ {\r
+ resume_time = start;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ TM resume_tm;\r
+ resume_time += OFFSET_OF_WAKEUP; // 起動時刻を 5分前に設定(OFFSET_OF_WAKEUPで調整)\r
+ if (localtime_s(&resume_tm, &resume_time) == 0)\r
+ {\r
+ _controller->resetWakeSchedule();\r
+ if (_controller->setWakeSchedule(resume_tm.tm_year + 1900, resume_tm.tm_mon + 1, resume_tm.tm_mday, resume_tm.tm_hour, resume_tm.tm_min))\r
+ {\r
+ DebugLog0("set wake schedule to \"%04d/%02d/%02d %02d:%02d:00\".",\r
+ resume_tm.tm_year + 1900, resume_tm.tm_mon + 1, resume_tm.tm_mday, resume_tm.tm_hour, resume_tm.tm_min);\r
+ }\r
+ }\r
+\r
+ // unlock\r
+ RaymUnlock(_controller);\r
+}\r
+\r
std::string Reservation::createVideoPath(int tuner)\r
{\r
DebugLog2("Reservation::createVideoPath()");\r
return result;\r
}\r
\r
-void Reservation::timerExpired(Timer *timer, void *userInfo)\r
+void Reservation::removePastEPGs()\r
+{\r
+ DebugLog2("Reservation::removePastEPGs()");\r
+\r
+ time_t now = time(NULL);\r
+\r
+ RaymLock(_controller);\r
+\r
+ Array *keys1 = _epgs->allKeys();\r
+ for (uint i = 0; i < keys1->count(); ++i)\r
+ {\r
+ Dictionary *events = _epgs->dictionaryForKey((String *)keys1->objectAtIndex(i));\r
+ if (events != NULL)\r
+ {\r
+ Array *keys2 = events->allKeys();\r
+ for (uint j = 0; j < keys2->count(); ++j)\r
+ {\r
+ String *key = (String *)keys2->objectAtIndex(j);\r
+ Dictionary *epg = events->dictionaryForKey(key);\r
+ if (epg != NULL)\r
+ {\r
+ time_t start = 0;\r
+ time_t end = 0;\r
+ Controller::getTimeWithEPG(epg, &start, &end);\r
+\r
+ if (now > end)\r
+ {\r
+ events->removeObjectForKey(key);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ RaymUnlock(_controller);\r
+}\r
+\r
+void Reservation::collectEPGsForTuner(int tuner, TimeInterval limit)\r
+{\r
+ DebugLog2("Reservation::collectEPGsForTuner(%d) start.", tuner);\r
+\r
+ // 既にロックされた状態でコールされる前提\r
+ bool locked = false;\r
+ RaymLock(_controller);\r
+ if ((0 <= tuner) && (tuner < _tuner_count))\r
+ {\r
+ locked = _tuners[tuner]->isLocked();\r
+ }\r
+ RaymUnlock(_controller);\r
+ if (!locked)\r
+ {\r
+ DebugLog2("Reservation::collectEPGsForTuner(%d) end(no locked).", tuner);\r
+ return;\r
+ }\r
+\r
+ // \r
+ Array *collected = Array::arrayWithCapacity(0);\r
+\r
+ // 開始時刻取得\r
+ Date *start = Date::date();\r
+\r
+ // 取得開始\r
+ bool done = false;\r
+ while (!done)\r
+ {\r
+ AutoreleasePool *pool = AutoreleasePool::alloc()->init();\r
+\r
+ // EPG取得\r
+ MPEG2::TS::Analyzer an;\r
+ an.setFlag(MPEG2::TS::Analyzer::FLAG_EIT);\r
+ _tuners[tuner]->setListener(&an);\r
+ Array *epgs = an.epgInfos();\r
+ _tuners[tuner]->setListener(NULL);\r
+\r
+ if (epgs != NULL)\r
+ {\r
+ // 取得成功\r
+ collected->addObjectsFromArray(epgs);\r
+ }\r
+\r
+ // リミットチェック\r
+ if (Date::date()->timeIntervalSinceDate(start) >= limit)\r
+ {\r
+ done = true;\r
+ }\r
+\r
+ // キャンセル確認\r
+ RaymLock(_controller);\r
+ if (!done)\r
+ {\r
+ done = _cancel_epg_collect;\r
+ }\r
+ RaymUnlock(_controller);\r
+\r
+ pool->release();\r
+ }\r
+\r
+ // lock\r
+ RaymLock(_controller);\r
+\r
+ for (uint j = 0; j < collected->count(); ++j)\r
+ {\r
+ Dictionary *epg1 = (Dictionary *)collected->objectAtIndex(j);\r
+\r
+ if (epg1->stringForKey(KEY_EPG_TITLE) == NULL)\r
+ {\r
+ // タイトルが無い場合は不要\r
+ continue;\r
+ }\r
+\r
+ // Service ID を Primary Key\r
+ String *key = epg1->stringForKey(KEY_EPG_SERVICE_ID);\r
+ if (key != NULL)\r
+ {\r
+ Dictionary *epgs_of_service = _epgs->dictionaryForKey(key);\r
+ if (epgs_of_service == NULL)\r
+ {\r
+ // 無い場合は作成\r
+ epgs_of_service = Dictionary::dictionaryWithCapacity(0);\r
+ _epgs->setObject(epgs_of_service, key);\r
+ }\r
+ // Event ID を Secondary Key\r
+ key = epg1->stringForKey(KEY_EPG_EVENT_ID);\r
+ if (key != NULL)\r
+ {\r
+ epgs_of_service->setObject(epg1, key);\r
+ }\r
+ }\r
+ }\r
+\r
+ // ファイルへ書き出し\r
+ _epgs->writeToFile(_epgs_path, true);\r
+\r
+ // unlock\r
+ RaymUnlock(_controller);\r
+\r
+ DebugLog2("Reservation::collectEPGsForTuner(%d) end.", tuner);\r
+}\r
+\r
+bool Reservation::collectEPGs(Tuner::Type type)\r
+{\r
+ DebugLog0("Reservation::collectEPGs(%s) start.", type == Tuner::ISDB_S ? "ISDB-S" : "ISDB-T");\r
+\r
+ // 使用するチューナを決定\r
+ int tuner = 0;\r
+ bool locked = false;\r
+ RaymLock(_controller);\r
+ for (int i = _tuner_count - 1; i >= 0; --i)\r
+ {\r
+ if (_controller->isTunerEnabled(i))\r
+ {\r
+ if ((!_tuners[i]->isLocked()) && (_tuners[i]->type() == type))\r
+ {\r
+ if (_tuners[i]->lock())\r
+ {\r
+ tuner = i;\r
+ locked = true;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ RaymUnlock(_controller);\r
+\r
+ // ロック確認\r
+ if (!locked)\r
+ {\r
+ DebugLog0("Controller::collectEPGs(%s) end(Can't locled).", type == Tuner::ISDB_S ? "ISDB-S" : "ISDB-T");\r
+ // ロックできない場合は収集しない\r
+ return false;\r
+ }\r
+\r
+ bool canceled = false;\r
+ DebugLog0("start collect EPG of \"%s(#%d)\".", _tuners[tuner]->name(), tuner);\r
+\r
+ // 現在のチャンネルを保存\r
+ int channel = _tuners[tuner]->channel();\r
+\r
+ // チャンネルを変更しつつEPGを取得\r
+ int max_channel = (type == Tuner::ISDB_S ? Tuner::MAX_CHANNELS_ISDB_S : Tuner::MAX_CHANNELS_ISDB_T);\r
+ for (int ch = 0; ch <= max_channel; ++ch)\r
+ {\r
+ // チャンネルが有効かチェック\r
+ if (_controller->isChannelEnabled(tuner, ch))\r
+ {\r
+ // チャンネル設定\r
+ if (_tuners[tuner]->setChannel(ch))\r
+ {\r
+ // EPG取集\r
+ collectEPGsForTuner(tuner, ((type == Tuner::ISDB_S) ? DEF_COLLECT_EPG_LIMIT_S : DEF_COLLECT_EPG_LIMIT_T));\r
+\r
+ // キャンセル確認\r
+ EnterCriticalSection(&_cs);\r
+ if (_cancel_epg_collect)\r
+ {\r
+ ch = Tuner::MAX_CHANNELS_ISDB_T + 1;\r
+ canceled = true;\r
+ }\r
+ LeaveCriticalSection(&_cs);\r
+\r
+ // キャンセル確認\r
+ // 終了不可 -> 録画待機中/録画中 なので、収集は諦める\r
+ if (!canTerminate())\r
+ {\r
+ ch = Tuner::MAX_CHANNELS_ISDB_T + 1;\r
+ canceled = true;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ // 元のチャンネルに戻す\r
+ _tuners[tuner]->setChannel(channel);\r
+\r
+ // lock\r
+ EnterCriticalSection(&_cs);\r
+\r
+ // チューナをアンロック\r
+ _tuners[tuner]->unlock();\r
+\r
+ // unlock\r
+ LeaveCriticalSection(&_cs);\r
+\r
+ if (canceled)\r
+ {\r
+ DebugLog0("collect EPG of \"%s\" was canceled.", _tuners[tuner]->name());\r
+ }\r
+ else\r
+ {\r
+ DebugLog0("collect EPG of \"%s\" was finished.", _tuners[tuner]->name());\r
+\r
+ // 過去のEPGを削除\r
+ removePastEPGs();\r
+ }\r
+\r
+ DebugLog2("Reservation::collectEPGs(%s) end.", type == Tuner::ISDB_S ? "ISDB-S" : "ISDB-T");\r
+\r
+ return true;\r
+}\r
+\r
+void Reservation::periodic()\r
{\r
bool need_update = false;\r
\r
#endif\r
}\r
\r
+void Reservation::timerExpired(Timer *timer, void *userInfo)\r
+{\r
+ switch ((long long)userInfo)\r
+ {\r
+ case CMD_PERIODIC:\r
+ periodic();\r
+ break;\r
+\r
+ case CMD_COLLECT_EPG_ISDB_S:\r
+ if (collectEPGs(Tuner::ISDB_S))\r
+ {\r
+ _timer_epg_s->setRepeats(false);\r
+ }\r
+ else\r
+ {\r
+ _timer_epg_s->setTimeInterval(DEF_COLLECT_EPG_RETRY);\r
+ }\r
+ break;\r
+\r
+ case CMD_COLLECT_EPG_ISDB_T:\r
+ if (collectEPGs(Tuner::ISDB_T))\r
+ {\r
+ _timer_epg_t->setRepeats(false);\r
+ }\r
+ else\r
+ {\r
+ _timer_epg_t->setTimeInterval(DEF_COLLECT_EPG_RETRY);\r
+ }\r
+ break;\r
+ }\r
+}\r
+\r
} // iPTd\r
} // ry0\r
Raym::String * _reservations_path;\r
Raym::Dictionary * _reservations;\r
int _reservation_seq_id;\r
+ Raym::String * _epgs_path;\r
+ Raym::Dictionary * _epgs;\r
Raym::Timer * _timer_periodic;\r
-\r
+ Raym::Timer * _timer_epg_s;\r
+ Raym::Timer * _timer_epg_t;\r
+ bool _cancel_epg_collect;\r
\r
protected:\r
Reservation();\r
static Reservation *alloc();\r
Reservation *initWithController(Controller *controller);\r
\r
-// void collectEPGsForTuner(int tuner, Foundation::NSTimeInterval limit);\r
-// bool collectEPGs(ry0::device::Tuner::Type type);\r
-// void removePastEPGs();\r
+ bool canSuspend();\r
+ bool canTerminate();\r
+ void systemWillSuspend();\r
+ void systemResumed();\r
+\r
+ void collectEPGsForTuner(int tuner, Raym::TimeInterval limit);\r
+ bool collectEPGs(ry0::device::Tuner::Type type);\r
+ void removePastEPGs();\r
+\r
bool reserve(int service_id, int event_id);\r
bool reserve(Raym::Dictionary *epg);\r
bool reserve(int tuner, Raym::Dictionary *epg);\r
bool cancel(int tuner, int reserve_id);\r
-// void updateKeywordsReservation();\r
-// void updateSchedule();\r
-\r
- bool canTerminate();\r
+ void updateKeywordsReservation();\r
+ void updateSchedule();\r
\r
std::string createVideoPath(int tuner);\r
+ void periodic();\r
\r
- // タイマ満了IF (from Timer)\r
void timerExpired(Raym::Timer *timer, void *userInfo);\r
};\r
\r
Streaming *Streaming::initWithController(Controller *controller)\r
{\r
_controller = controller;\r
- _tuner_count = controller->_tunerCount;\r
+ _tuner_count = controller->_tuner_count;\r
_tuners = controller->_tuners;\r
\r
// 制御情報\r
return this;\r
}\r
\r
+void Streaming::systemWillSuspend()\r
+{\r
+}\r
+\r
+void Streaming::systemResumed()\r
+{\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
static Streaming *alloc();\r
Streaming *initWithController(Controller *controller);\r
\r
+ void systemWillSuspend();\r
+ void systemResumed();\r
+\r
void mapping(int tuner, int channel, int port);\r
\r
void timerExpired(Raym::Timer *timer, void *user_info);\r