{\r
int result = -1;\r
\r
-#ifdef OBJC_MEMORY_CHECK\r
+#ifdef RAYM_MEMORY_CHECK\r
DebugLog0("");\r
- DebugLog0("Application::main() global_objc_count_ = %d", Raym::global_objc_count_);\r
+ DebugLog0("Application::main() global_raym_count_ = %d", Raym::global_raym_count_);\r
#endif\r
\r
// ARP生成\r
// ARP解放\r
pool->release();\r
\r
-#ifdef OBJC_MEMORY_CHECK\r
- DebugLog0("Application::main() global_objc_count_ = %d", Raym::global_objc_count_);\r
+#ifdef RAYM_MEMORY_CHECK\r
+ DebugLog0("Application::main() global_raym_count_ = %d", Raym::global_raym_count_);\r
#endif\r
\r
return result;\r
DebugLog2("Array::count()");\r
\r
UInteger result = 0;\r
- OBJC_LOCK(this);\r
+ RaymLock(this);\r
result = (UInteger)_array.size();\r
- OBJC_UNLOCK(this);\r
+ RaymUnlock(this);\r
\r
return result;\r
}\r
\r
if (object != NULL)\r
{\r
- OBJC_LOCK(this);\r
+ RaymLock(this);\r
object->retain();\r
_array.push_back(object);\r
- OBJC_UNLOCK(this);\r
+ RaymUnlock(this);\r
}\r
}\r
\r
\r
if (array != NULL)\r
{\r
- OBJC_LOCK(this);\r
+ RaymLock(this);\r
for (UInteger i = 0; i < array->count(); ++i)\r
{\r
Object *object = array->objectAtIndex(i);\r
object->retain();\r
_array.push_back(object);\r
}\r
- OBJC_UNLOCK(this);\r
+ RaymUnlock(this);\r
}\r
}\r
\r
DebugLog2("Array::objectAtIndex(index)");\r
\r
Object *result = NULL;\r
- OBJC_LOCK(this);\r
+ RaymLock(this);\r
if (index < _array.size())\r
{\r
result = _array.at(index);\r
}\r
- OBJC_UNLOCK(this);\r
+ RaymUnlock(this);\r
\r
return result;\r
}\r
{\r
DebugLog2("Array::insertObject(object,index)");\r
\r
- OBJC_LOCK(this);\r
+ RaymLock(this);\r
if (object != NULL)\r
{\r
std::vector<Object *>::iterator it;\r
++idx;\r
}\r
}\r
- OBJC_UNLOCK(this);\r
+ RaymUnlock(this);\r
}\r
\r
void Array::removeObject(Object *object)\r
{\r
DebugLog2("Array::removeObject(object)");\r
\r
- OBJC_LOCK(this);\r
+ RaymLock(this);\r
std::vector<Object *>::iterator it;\r
for (it = _array.begin(); it != _array.end(); ++it)\r
{\r
break;\r
}\r
}\r
- OBJC_UNLOCK(this);\r
+ RaymUnlock(this);\r
}\r
\r
void Array::removeObjectAtIndex(UInteger index)\r
{\r
DebugLog2("Array::removeObjectAtIndex(index)");\r
\r
- OBJC_LOCK(this);\r
+ RaymLock(this);\r
std::vector<Object *>::iterator it;\r
UInteger idx = 0;\r
for (it = _array.begin(); it != _array.end(); ++it)\r
}\r
++idx;\r
}\r
- OBJC_UNLOCK(this);\r
+ RaymUnlock(this);\r
}\r
\r
void Array::removeAllObjects()\r
{\r
DebugLog2("Array::removeAllObjects()");\r
\r
- OBJC_LOCK(this);\r
+ RaymLock(this);\r
for (unsigned int i = 0; i < _array.size(); ++i)\r
{\r
Object *obj = _array.at(i);\r
obj->release();\r
}\r
_array.clear();\r
- OBJC_UNLOCK(this);\r
+ RaymUnlock(this);\r
}\r
\r
Array *Array::sortedArrayUsingFunction(Integer (*function)(Object *, Object *, void *), void *context)\r
\r
Array *result = Array::arrayWithCapacity(0);\r
\r
- OBJC_LOCK(this);\r
+ RaymLock(this);\r
\r
for (unsigned int i = 0; i < _array.size(); ++i)\r
{\r
}\r
}\r
\r
- OBJC_UNLOCK(this);\r
+ RaymUnlock(this);\r
\r
return result;\r
}\r
if (object != NULL)
{
- OBJC_LOCK(this);
+ RaymLock(this);
_objects.push_back(object);
- OBJC_UNLOCK(this);
+ RaymUnlock(this);
}
}
\r
if ((object != NULL) && (forKey != NULL))\r
{\r
- OBJC_LOCK(this);\r
+ RaymLock(this);\r
removeObjectForKey(forKey);\r
KeyAndValue *kv = KeyAndValue::keyAndValue(forKey, object);\r
if (kv != NULL)\r
{\r
_dict.push_back(kv);\r
}\r
- OBJC_UNLOCK(this);\r
+ RaymUnlock(this);\r
}\r
}\r
\r
Object *result = NULL;\r
if (key != NULL)\r
{\r
- OBJC_LOCK(this);\r
+ RaymLock(this);\r
std::vector<Object *>::iterator it;\r
for (it = _dict.begin(); it != _dict.end(); ++it)\r
{\r
break;\r
}\r
}\r
- OBJC_UNLOCK(this);\r
+ RaymUnlock(this);\r
}\r
return result;\r
}\r
\r
if (key != NULL)\r
{\r
- OBJC_LOCK(this);\r
+ RaymLock(this);\r
std::vector<Object *>::iterator it;\r
for (it = _dict.begin(); it != _dict.end(); ++it)\r
{\r
break;\r
}\r
}\r
- OBJC_UNLOCK(this);\r
+ RaymUnlock(this);\r
}\r
}\r
\r
DebugLog2("Dictionary::allKeys()");\r
\r
Array *result = Array::alloc()->initWithCapacity(0)->autorelease();\r
- OBJC_LOCK(this);\r
+ RaymLock(this);\r
\r
std::vector<Object *>::iterator it;\r
for (it = _dict.begin(); it != _dict.end(); ++it)\r
result->addObject(kv->key());\r
}\r
\r
- OBJC_UNLOCK(this);\r
+ RaymUnlock(this);\r
\r
return result;\r
}\r
DebugLog2("Dictionary::count()");\r
\r
UInteger result = 0;\r
- OBJC_LOCK(this);\r
+ RaymLock(this);\r
result = _dict.size();\r
- OBJC_UNLOCK(this);\r
+ RaymUnlock(this);\r
return result;\r
}\r
\r
{\r
DebugLog2("Dictionary::writeToFile(path,atomically)");\r
\r
- OBJC_LOCK(this);\r
+ RaymLock(this);\r
\r
const char *outfile = path->cString();\r
int fd = -1;\r
close(fd);\r
#endif\r
\r
- OBJC_UNLOCK(this);\r
+ RaymUnlock(this);\r
\r
return true;\r
}\r
{\r
DebugLog2("Dictionary::toString()");\r
\r
- OBJC_LOCK(this);\r
+ RaymLock(this);\r
\r
std::string result = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";\r
result += "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n";\r
result += toString(0);\r
result += "</plist>\n";\r
\r
- OBJC_UNLOCK(this);\r
+ RaymUnlock(this);\r
\r
return result;\r
}\r
void Lock::lock()
{
- OBJC_LOCK(this);
+ RaymLock(this);
}
void Lock::unlock()
{
- OBJC_UNLOCK(this);
+ RaymUnlock(this);
}
const char *Lock::className()
namespace Raym
{
-#ifdef OBJC_MEMORY_CHECK
-int global_objc_count_ = 0;
-int global_objc_init_count_ = 0;
-int global_objc_retain_count_ = 0;
-int global_objc_autorelease_count_ = 0;
-int global_objc_release_count_ = 0;
+#ifdef RAYM_MEMORY_CHECK
+int global_raym_count_ = 0;
+int global_raym_init_count_ = 0;
+int global_raym_retain_count_ = 0;
+int global_raym_autorelease_count_ = 0;
+int global_raym_release_count_ = 0;
DEFINE_STATIC_MUTEX(global_lock_);
#endif
{
DebugLog2("Object::Object()");
- OBJC_LOCK_CREATE;
+ RAYM_LOCK_CREATE;
_retainCount = 1;
-#ifdef OBJC_MEMORY_CHECK
+#ifdef RAYM_MEMORY_CHECK
global_lock_.lock();
- ++global_objc_count_;
-// DebugLog0("C:0x%08x:%d", this, global_objc_count_);
+ ++global_raym_count_;
+// DebugLog0("C:0x%08x:%d", this, global_raym_count_);
global_lock_.unlock();
#endif
}
Object::~Object()
{
- OBJC_LOCK_DESTROY;
+ RAYM_LOCK_DESTROY;
-#ifdef OBJC_MEMORY_CHECK
+#ifdef RAYM_MEMORY_CHECK
global_lock_.lock();
-// DebugLog0("D:0x%08x:%d", this, global_objc_count_);
- --global_objc_count_;
+// DebugLog0("D:0x%08x:%d", this, global_raym_count_);
+ --global_raym_count_;
global_lock_.unlock();
#endif
{
DebugLog2("Object::init()");
-#ifdef OBJC_MEMORY_CHECK
+#ifdef RAYM_MEMORY_CHECK
global_lock_.lock();
- ++global_objc_init_count_;
+ ++global_raym_init_count_;
global_lock_.unlock();
#endif
{
DebugLog2("Object::retain()");
- OBJC_LOCK(this);
+ RaymLock(this);
++_retainCount;
- OBJC_UNLOCK(this);
+ RaymUnlock(this);
-#ifdef OBJC_MEMORY_CHECK
+#ifdef RAYM_MEMORY_CHECK
global_lock_.lock();
- ++global_objc_retain_count_;
+ ++global_raym_retain_count_;
global_lock_.unlock();
#endif
{
DebugLog2("Object::autorelease()");
-#ifdef OBJC_MEMORY_CHECK
+#ifdef RAYM_MEMORY_CHECK
global_lock_.lock();
- ++global_objc_autorelease_count_;
+ ++global_raym_autorelease_count_;
global_lock_.unlock();
#endif
{
DebugLog2("Object::autorelease()");
-#ifdef OBJC_MEMORY_CHECK
+#ifdef RAYM_MEMORY_CHECK
global_lock_.lock();
- ++global_objc_autorelease_count_;
+ ++global_raym_autorelease_count_;
global_lock_.unlock();
#endif
{
DebugLog2("Object::release()");
-#ifdef OBJC_MEMORY_CHECK
+#ifdef RAYM_MEMORY_CHECK
global_lock_.lock();
- ++global_objc_release_count_;
+ ++global_raym_release_count_;
global_lock_.unlock();
#endif
- OBJC_LOCK(this);
+ RaymLock(this);
if (_retainCount > 0)
{
--_retainCount;
if (_retainCount == 0)
{
- OBJC_UNLOCK(this);
+ RaymUnlock(this);
delete this;
return;
}
DebugLog0("object is already released. (0x%016lx)", this);
abort();
}
- OBJC_UNLOCK(this);
+ RaymUnlock(this);
}
String *Object::description()
\r
#pragma once\r
\r
-#define OBJC_MEMORY_CHECK\r
+#define RAYM_MEMORY_CHECK\r
\r
#ifdef _WIN32\r
#include <windows.h>\r
\r
#ifdef _WIN32\r
\r
-#define OBJC_LOCK_CREATE InitializeCriticalSection(&_cs)\r
-#define OBJC_LOCK_DESTROY DeleteCriticalSection(&_cs)\r
-/*\r
-#define OBJC_LOCK(OBJ) \\r
- DebugLog4("ECS_B:[0x%08X:0x%08X] %s() at %d\n", (unsigned int)(OBJ), GetCurrentThreadId(), __FUNCTION__, __LINE__); \\r
- EnterCriticalSection(&(OBJ)->_cs); \\r
- DebugLog4("ECS_A:[0x%08X:0x%08X] %s() at %d\n", (unsigned int)(OBJ), GetCurrentThreadId(), __FUNCTION__, __LINE__);\r
-\r
-#define OBJC_UNLOCK(OBJ) \\r
- DebugLog4("LCS_B:[0x%08X:0x%08X] %s() at %d\n", (unsigned int)(OBJ), GetCurrentThreadId(), __FUNCTION__, __LINE__); \\r
- LeaveCriticalSection(&(OBJ)->_cs); \\r
- DebugLog4("LCS_A:[0x%08X:0x%08X] %s() at %d\n", (unsigned int)(OBJ), GetCurrentThreadId(), __FUNCTION__, __LINE__);\r
-*/\r
+#define RAYM_LOCK_CREATE InitializeCriticalSection(&_cs)\r
+#define RAYM_LOCK_DESTROY DeleteCriticalSection(&_cs)\r
\r
#define DEFINE_STATIC_MUTEX(variable) \\r
class STATIC_MUTEX_variable \\r
\r
#else\r
\r
-#define OBJC_LOCK_CREATE { \\r
+#define RAYM_LOCK_CREATE { \\r
pthread_mutexattr_t attr; \\r
pthread_mutexattr_init(&attr); \\r
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \\r
pthread_mutex_init(&_lock, &attr); \\r
}\r
-#define OBJC_LOCK_DESTROY pthread_mutex_destroy(&_lock)\r
-#define OBJC_LOCK pthread_mutex_lock(&_lock)\r
-#define OBJC_UNLOCK pthread_mutex_unlock(&_lock)\r
+#define RAYM_LOCK_DESTROY pthread_mutex_destroy(&_lock)\r
+#define RAYM_LOCK pthread_mutex_lock(&_lock)\r
+#define RAYM_UNLOCK pthread_mutex_unlock(&_lock)\r
\r
#endif\r
\r
namespace Raym\r
{\r
\r
-#ifdef OBJC_MEMORY_CHECK\r
-extern int global_objc_count_;\r
-extern int global_objc_init_count_;\r
-extern int global_objc_retain_count_;\r
-extern int global_objc_autorelease_count_;\r
-extern int global_objc_release_count_;\r
+#ifdef RAYM_MEMORY_CHECK\r
+extern int global_raym_count_;\r
+extern int global_raym_init_count_;\r
+extern int global_raym_retain_count_;\r
+extern int global_raym_autorelease_count_;\r
+extern int global_raym_release_count_;\r
#endif\r
\r
typedef unsigned int uint;\r
// virtual const char *className() = 0;\r
virtual const char *className();\r
\r
- friend void OBJC_LOCK(Object *);\r
- friend void OBJC_UNLOCK(Object *);\r
+ friend void RaymLock(Object *);\r
+ friend void RaymUnlock(Object *);\r
};\r
\r
-inline void OBJC_LOCK(Object *obj)\r
+inline void RaymLock(Object *obj)\r
{\r
#ifdef _WIN32\r
EnterCriticalSection(&(obj->_cs));\r
#endif\r
}\r
\r
-inline void OBJC_UNLOCK(Object *obj)\r
+inline void RaymUnlock(Object *obj)\r
{\r
#ifdef _WIN32\r
LeaveCriticalSection(&(obj->_cs));\r
#endif\r
}\r
\r
+#define RaymCriticalSection(OBJ, BLOCK) RaymLock(OBJ); BLOCK; RaymUnlock(OBJ);\r
+\r
} // Raym\r
\r
#pragma once
#include <Raym/Object.h>
-#include <Raym/Object.h>
#include <Raym/AutoreleasePool.h>
#include <Raym/String.h>
#include <Raym/Dictionary.h>
#include <Raym/InputStream.h>
#include <Raym/FileInputStream.h>
#include <Raym/RunningApplication.h>
-
#include <Raym/Application.h>
namespace Raym
namespace iPTd\r
{\r
\r
-static const char * PLIST_PREFIX = "com.gmail.tim.and.pom";\r
-\r
// プロパティデフォルト値\r
static const char * DEF_NAME = "iPTd_R2";\r
static const char * DEF_HOSTNAME = "localhost";\r
static const TimeInterval DEF_COLLECT_EPG_LIMIT_S = 10.5;\r
static const TimeInterval DEF_COLLECT_EPG_LIMIT_T = 20.5;\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
+const char *Controller::_plist_prefix = "com.gmail.tim.and.pom";\r
\r
#ifndef _WIN32\r
#pragma mark '\r
_status = NULL;\r
_epgs_path = NULL;\r
_epgs = NULL;\r
- _store_path = NULL;\r
\r
_timer_restart = NULL;\r
_timer_periodic = NULL;\r
DebugLog2("_system_path: %s\n", _system_path->cString());\r
\r
// プロパティファイルのパス設定\r
- _props_path = String::alloc()->initWithFormat("%s%s.iptd.plist", _system_path->cString(), PLIST_PREFIX);\r
+ _props_path = String::alloc()->initWithFormat("%s%s.iptd.plist", _system_path->cString(), _plist_prefix);\r
if (_props_path == NULL)\r
{\r
DebugLog0("error: set property file path.\n");\r
}\r
\r
// ステータスファイルのパス設定\r
- _status_path = String::alloc()->initWithFormat("%s%s.iptd.status.plist", _system_path->cString(), PLIST_PREFIX);\r
+ _status_path = String::alloc()->initWithFormat("%s%s.iptd.status.plist", _system_path->cString(), _plist_prefix);\r
if (_status_path == NULL)\r
{\r
DebugLog0("error: set status file path.\n");\r
updated = true;\r
}\r
\r
+ // 録画データ格納先の確認\r
+ if (_props->stringForKey(KEY_STORE_PATH) == NULL)\r
+ {\r
+ // プロパティに未設定の場合\r
+ // <Public Directory>/Videos を設定\r
+ const char *public_dir = GetPublicDirectory();\r
+ if (public_dir == NULL)\r
+ {\r
+ DebugLog0("error: GetPublicDirectory().");\r
+ result = -1;\r
+ break;\r
+ }\r
+ _props->setString(String::alloc()->initWithFormat("%s\\Videos", public_dir), KEY_STORE_PATH);\r
+\r
+ // 更新フラグ\r
+ updated = true;\r
+ }\r
+\r
+ // 実際にディレクトリが存在しているか確認\r
+ FileManager *fm = FileManager::defaultManager();\r
+ bool isDir = false;\r
+ if (!fm->fileExistsAtPath(_props->stringForKey(KEY_STORE_PATH), &isDir))\r
+ {\r
+ isDir = false;\r
+ }\r
+ if (!isDir)\r
+ {\r
+ DebugLog0("error: \"%s\" is not exists.", _props->stringForKey(KEY_STORE_PATH)->cString());\r
+ result = -1;\r
+ break;\r
+ }\r
+\r
//\r
- _reservation = Reservation::alloc()->initWithController(this, String::stringWithFormat("%s%s.iptd.reservations.plist", _system_path->cString(), PLIST_PREFIX));\r
+ _reservation = Reservation::alloc()->initWithController(this);\r
+ if (_reservation == NULL)\r
+ {\r
+ result = -1;\r
+ break;\r
+ }\r
\r
//\r
_streaming = Streaming::alloc()->initWithController(this);\r
+ if (_streaming == NULL)\r
+ {\r
+ result = -1;\r
+ break;\r
+ }\r
\r
// httpdのルートパス\r
String *rootPath = _system_path->stringByAppendingPathComponent("html");\r
- FileManager *fm = FileManager::defaultManager();\r
- bool isDir = false;\r
+ isDir = false;\r
if (!fm->fileExistsAtPath(rootPath, &isDir))\r
{\r
isDir = false;\r
}\r
\r
//\r
- _httpd = HTTPDaemon::alloc()->initWithController(this, _props->integerForKey(KEY_HTTP_PORT), rootPath);\r
+ _httpd = HTTPD::alloc()->initWithController(this, _props->integerForKey(KEY_HTTP_PORT), rootPath);\r
+ if (_httpd == NULL)\r
+ {\r
+ result = -1;\r
+ break;\r
+ }\r
\r
\r
// プロパティファイルを保存\r
RELEASE(_status);\r
RELEASE(_epgs_path);\r
RELEASE(_epgs);\r
- RELEASE(_store_path);\r
\r
RELEASE(_reservation);\r
RELEASE(_streaming);\r
class Controller : public Raym::Application,\r
public Raym::TimerDelegate\r
{\r
-private:\r
-// CRITICAL_SECTION _cs;\r
+protected:\r
+ Controller();\r
+ ~Controller();\r
\r
+public:\r
+ static const char * _plist_prefix;\r
Raym::String * _system_path; // システムパス(実行ファイルが配置されているディレクトリ)\r
Raym::String * _props_path; // プロパティファイルのパス\r
Raym::Dictionary * _props; // プロパティ\r
Raym::Dictionary * _status; // ステータス\r
Raym::String * _epgs_path; // 番組データファイルのパス\r
Raym::Dictionary * _epgs; // 番組データ\r
- Raym::String * _store_path; // 録画データ格納先\r
int _idle_count; // アイドルカウンタ\r
\r
// 非同期処理用タイマ\r
\r
Reservation * _reservation; // 予約録画制御\r
Streaming * _streaming; // ストリーミング制御\r
- HTTPDaemon * _httpd; // HTTP制御\r
+ HTTPD * _httpd; // HTTP制御\r
\r
bool _initialized; // 初期化済み\r
HMODULE _multi2_dll;\r
bool _cancel_epg_collect; // EPG収集キャンセル\r
\r
\r
-public:\r
int _tunerCount;\r
ry0::device::Tuner * _tuners[ry0::device::MAX_TUNERS];\r
\r
-protected:\r
- Controller();\r
- ~Controller();\r
\r
-public:\r
static Controller *alloc();\r
int restart();\r
\r
#include "ry0/iPTd/Controller.h"\r
\r
using namespace Raym;\r
+using namespace NET;\r
\r
namespace ry0\r
{\r
namespace iPTd\r
{\r
\r
-HTTPDaemon::HTTPDaemon()\r
+HTTPD::HTTPD()\r
{\r
_controller = NULL;\r
_httpd = NULL;\r
_path = NULL;\r
}\r
\r
-HTTPDaemon::~HTTPDaemon()\r
+HTTPD::~HTTPD()\r
{\r
RELEASE(_httpd);\r
RELEASE(_path);\r
_controller = NULL;\r
}\r
\r
-HTTPDaemon *HTTPDaemon::alloc()\r
+HTTPD *HTTPD::alloc()\r
{\r
- return new HTTPDaemon();\r
+ return new HTTPD();\r
}\r
\r
-HTTPDaemon *HTTPDaemon::initWithController(Controller *controller, int port, String *path)\r
+HTTPD *HTTPD::initWithController(Controller *controller, int port, String *path)\r
{\r
_controller = controller;\r
_port = port;\r
return this;\r
}\r
\r
-bool HTTPDaemon::start()\r
+bool HTTPD::start()\r
{\r
if (_httpd == NULL)\r
{\r
- _httpd = NET::HTTPDaemon::alloc()->initWithPort(_port, 10);\r
+ _httpd = HTTPDaemon::alloc()->initWithPort(_port, 10);\r
_httpd->setRootPath(_path);\r
_httpd->setDelegate(this);\r
}\r
return _httpd->start();\r
}\r
\r
-void HTTPDaemon::stop()\r
+void HTTPD::stop()\r
{\r
_httpd->stop();\r
}\r
\r
-NET::HTTPResponse *HTTPDaemon::request(NET::HTTPRequest *request, struct sockaddr_in *client)\r
+HTTPResponse *HTTPD::request(HTTPRequest *request, SOCKADDR_IN *client)\r
{\r
DebugLog2("%s\n", __FUNCTION__);\r
\r
// return NULL;\r
}\r
\r
- NET::HTTPResponse *response = NULL;\r
+ HTTPResponse *response = NULL;\r
\r
if (request->method()->isEqualToString("GET") ||\r
request->method()->isEqualToString("HEAD"))\r
// URI\r
String *uri = request->URI();\r
DebugLog0("request: %s\n", uri->cString());\r
+ if (uri->isMatch("^/config.xml$"))\r
+ {\r
+ RaymCriticalSection(_controller,\r
+ {\r
+ response = responseWithDictionary(request, _controller->_props);\r
+ });\r
+ }\r
+ else if (uri->isMatch("^/status.xml$"))\r
+ {\r
+ RaymCriticalSection(_controller,\r
+ {\r
+ response = responseWithDictionary(request, _controller->_status);\r
+ });\r
+ }\r
+ //\r
+ // tuner control\r
+ //\r
+ else if (uri->isMatch("^/[0-9]{3}/"))\r
+ {\r
+ // String::substringWithRange() の実装は後回しなので。。\r
+ std::string s = uri->cString();\r
+ int tuner = atoi(s.substr(1, 3).c_str());\r
+ if ((0 <= tuner) && (tuner < _controller->_tunerCount))\r
+ {\r
+ response = requestTunerControl(request, client, tuner);\r
+ }\r
+ }\r
}\r
\r
-\r
return response;\r
}\r
\r
+HTTPResponse *HTTPD::requestTunerControl(HTTPRequest *request, SOCKADDR_IN *client, int tuner)\r
+{\r
+ DebugLog0("%s\n", __FUNCTION__);\r
+\r
+ HTTPResponse *result = NULL;\r
+\r
+ // lock\r
+ RaymLock(_controller);\r
+\r
+ // URI取得\r
+ String *uri = request->URI();\r
+ while (uri != NULL)\r
+ {\r
+ // CGIリクエストとして解析\r
+ Dictionary *cgi = request->parseAsCGI();\r
+ if (cgi != NULL)\r
+ {\r
+ uri = cgi->stringForKey(HTTPRequest::KEY_CGI);\r
+ if (uri == NULL)\r
+ {\r
+ break;\r
+ }\r
+ }\r
+\r
+ //\r
+ // チャンネル設定\r
+ // /ttt/channel=nnn\r
+ //\r
+ 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
+ {\r
+ break;\r
+ }\r
+ int channel = ch->intValue();\r
+ DebugLog2("set channel:%d(%s)\n", channel, ch->cString());\r
+ if (_controller->setChannel(tuner, channel))\r
+ {\r
+ // success\r
+ DebugLog2("success.\n");\r
+ result = responseForSuccess(request);\r
+ }\r
+ else\r
+ {\r
+ // failed\r
+ DebugLog2("failed.\n");\r
+ result = responseForFailed(request);\r
+ }\r
+ }\r
+\r
+ //\r
+ // 録画開始(最大23:59まで)\r
+ // /ttt/recording=on?hour=hh&min=mm[&channel=nnn]\r
+ //\r
+ else if (uri->isMatch("^/[0-9]{3}/recording=on$") && (cgi != NULL))\r
+ {\r
+ // パラメータがあるか\r
+ Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
+ if (params == NULL)\r
+ {\r
+ break;\r
+ }\r
+\r
+ // パラメータ数は2〜3か\r
+ if ((params->count() != 2) && (params->count() != 3))\r
+ {\r
+ break;\r
+ }\r
+\r
+ // パラメータのチェック\r
+ String *p_hour = NULL;\r
+ String *p_min = NULL;\r
+ String *p_channel = NULL;\r
+\r
+ struct {\r
+ const char *name;\r
+ String **variable;\r
+ const char *regex;\r
+ }\r
+ cgi[] =\r
+ {\r
+ {"hour", &p_hour, "^[0-2][0-9]$"},\r
+ {"min", &p_min, "^[0-5][0-9]$"},\r
+ {"channel", &p_channel, "^[0-9]{3}$"},\r
+ {NULL, NULL, NULL}\r
+ };\r
+\r
+ for (uint i = 0; cgi[i].name != NULL; ++i)\r
+ {\r
+ *(cgi[i].variable) = NULL;\r
+ for (uint j = 0; j < params->count(); ++j)\r
+ {\r
+ Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
+ String *value = param->stringForKey(cgi[i].name);\r
+ if ((value != NULL) && value->isMatch(cgi[i].regex))\r
+ {\r
+ *(cgi[i].variable) = value;\r
+ }\r
+ }\r
+ }\r
+\r
+ // パラメータは有効か\r
+ if ((p_hour == NULL) || (p_min == NULL))\r
+ {\r
+ break;\r
+ }\r
+\r
+ // チャンネル設定\r
+ int channel = 0;\r
+ if (p_channel != NULL)\r
+ {\r
+ channel = p_channel->intValue();\r
+ }\r
+ else\r
+ {\r
+ channel = _controller->_tuners[tuner]->channel();\r
+ }\r
+\r
+ if (channel >= 0)\r
+ {\r
+ // recording on\r
+ int hour = p_hour->intValue();\r
+ int min = p_min->intValue();\r
+ if (hour < 24)\r
+ {\r
+ // EPG生成\r
+ Dictionary *epg = Dictionary::dictionaryWithCapacity(0);\r
+ while (true)\r
+ {\r
+ time_t now;\r
+ time(&now);\r
+ now += 1; // margin\r
+ TM tm;\r
+ if (localtime_s(&tm, &now) != 0)\r
+ {\r
+ epg = NULL;\r
+ break;\r
+ }\r
+ TM end;\r
+ end = tm;\r
+ end.tm_hour += hour;\r
+ end.tm_min += min;\r
+ end.tm_sec += 1; // margin\r
+ if (mktime(&end) == -1)\r
+ {\r
+ epg = NULL;\r
+ break;\r
+ }\r
+\r
+ char tmp[16];\r
+\r
+ // Date\r
+ sprintf_s(tmp, sizeof(tmp), "%04d/%02d/%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);\r
+ epg->setString(tmp, KEY_EPG_DATE);\r
+\r
+ // Start\r
+ sprintf_s(tmp, sizeof(tmp), "%02d:%02d:%02d", tm.tm_hour, tm.tm_min, tm.tm_sec);\r
+ epg->setString(tmp, KEY_EPG_START);\r
+\r
+ // End\r
+ sprintf_s(tmp, sizeof(tmp), "%02d:%02d:%02d", end.tm_hour, end.tm_min, end.tm_sec);\r
+ epg->setString(tmp, KEY_EPG_END);\r
+\r
+ // Channel\r
+ sprintf_s(tmp, sizeof(tmp), "%d", channel);\r
+ epg->setString(tmp, KEY_EPG_CHANNEL);\r
+\r
+ // 繰り返し\r
+ epg->setString("off", KEY_EPG_REPEAT); \r
+\r
+ // Status\r
+ epg->setString("ready", KEY_EPG_STATUS);\r
+\r
+ break;\r
+ }\r
+ \r
+ if (epg != NULL)\r
+ {\r
+ // 録画開始&結果生成\r
+ if (_controller->_reservation->reserve(tuner, epg))\r
+ {\r
+ result = responseForSuccess(request);\r
+ }\r
+ else\r
+ {\r
+ result = responseForFailed(request);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // 録画停止\r
+ // /ttt/recording=off\r
+ //\r
+ else if (uri->isMatch("^/[0-9]{3}/recording=off$") && (cgi == NULL))\r
+ {\r
+ // recording off\r
+ DebugLog2("recording off: %s\n", uri->cString());\r
+ if (_controller->_reservation->cancel(tuner, -1))\r
+ {\r
+ // success\r
+ DebugLog2("success.\n");\r
+ result = responseForSuccess(request);\r
+ }\r
+ else\r
+ {\r
+ // failed\r
+ DebugLog2("failed.\n");\r
+ result = responseForFailed(request);\r
+ }\r
+ }\r
+\r
+ //\r
+ // ストリーミング開始\r
+ // /ttt/streaming=on?udp=nnnnn(&host=aaaaaa)\r
+ //\r
+ else if (uri->isMatch("^/[0-9]{3}/streaming=on$") && (cgi != NULL))\r
+ {\r
+ // パラメータがあるか\r
+ Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
+ if (params == NULL)\r
+ {\r
+ break;\r
+ }\r
+\r
+ // パラメータ数は1〜2か\r
+ if ((params->count() != 1) && (params->count() != 2))\r
+ {\r
+ break;\r
+ }\r
+\r
+ // パラメータのチェック\r
+ String *p_udp = NULL;\r
+ String *p_host = NULL;\r
+\r
+ struct {\r
+ const char *name;\r
+ String **variable;\r
+ const char *regex;\r
+ }\r
+ cgi[] =\r
+ {\r
+ {"udp", &p_udp, "^[0-9]{1,5}$"},\r
+ {"host", &p_host, "^.+$"},\r
+ {NULL, NULL, NULL}\r
+ };\r
+\r
+ for (uint i = 0; cgi[i].name != NULL; ++i)\r
+ {\r
+ *(cgi[i].variable) = NULL;\r
+ for (uint j = 0; j < params->count(); ++j)\r
+ {\r
+ Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
+ String *value = param->stringForKey(cgi[i].name);\r
+ if ((value != NULL) && value->isMatch(cgi[i].regex))\r
+ {\r
+ *(cgi[i].variable) = value;\r
+ }\r
+ }\r
+ }\r
+\r
+ // パラメータチェック\r
+ if (p_udp == NULL)\r
+ {\r
+ break;\r
+ }\r
+\r
+ SOCKADDR_IN dst_addr;\r
+ \r
+ if (p_host != NULL)\r
+ {\r
+ #if 0\r
+ std::string host = udpstr.substr(idx + 5);\r
+ udpstr = udpstr.substr(0, idx - 1);\r
+ DebugLog2("udp: %s\n", udpstr.c_str());\r
+ DebugLog2("host: %s\n", host.c_str());\r
+ struct hostent *ent = gethostbyname(host.c_str());\r
+ #endif\r
+ }\r
+ else\r
+ {\r
+ memcpy(&dst_addr, client, sizeof(SOCKADDR_IN));\r
+ }\r
+ dst_addr.sin_port = htons(p_udp->intValue());\r
+ \r
+ if (_controller->_tuners[tuner]->startStreaming(&dst_addr))\r
+ {\r
+ // success\r
+ DebugLog2("success.\n");\r
+ result = responseForSuccess(request);\r
+ }\r
+ else\r
+ {\r
+ // failed\r
+ DebugLog2("failed.\n");\r
+ result = responseForFailed(request);\r
+ }\r
+ }\r
+\r
+ //\r
+ // ストリーミング停止\r
+ // /ttt/streaming=off(?host=aaaa)\r
+ //\r
+ else if (uri->isMatch("^/[0-9]{3}/streaming=off$"))\r
+ {\r
+ // パラメータ\r
+ String *p_host = NULL;\r
+\r
+ // パラメータがあるか\r
+ if (cgi != NULL)\r
+ {\r
+ Array *params = cgi->arrayForKey(HTTPRequest::KEY_PARAMS);\r
+ if (params == NULL)\r
+ {\r
+ break;\r
+ }\r
+\r
+ // パラメータ数は0〜1か\r
+ if ((params->count() != 0) && (params->count() != 1))\r
+ {\r
+ break;\r
+ }\r
+\r
+ struct {\r
+ const char *name;\r
+ String **variable;\r
+ const char *regex;\r
+ }\r
+ cgi[] =\r
+ {\r
+ {"host", &p_host, "^.+$"},\r
+ {NULL, NULL, NULL}\r
+ };\r
+\r
+ for (uint i = 0; cgi[i].name != NULL; ++i)\r
+ {\r
+ *(cgi[i].variable) = NULL;\r
+ for (uint j = 0; j < params->count(); ++j)\r
+ {\r
+ Dictionary *param = (Dictionary *)params->objectAtIndex(j);\r
+ String *value = param->stringForKey(cgi[i].name);\r
+ if ((value != NULL) && value->isMatch(cgi[i].regex))\r
+ {\r
+ *(cgi[i].variable) = value;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ SOCKADDR_IN dst_addr;\r
+ if (p_host != NULL)\r
+ {\r
+ }\r
+ else\r
+ {\r
+ }\r
+\r
+ _controller->_tuners[tuner]->stopStreaming();\r
+ \r
+ // success\r
+ DebugLog2("success.\n");\r
+ result = responseForSuccess(request);\r
+ }\r
+\r
+ //\r
+ // HLS制御\r
+ //\r
+ else if (uri->isMatch("^/[0-9]{3}/[0-9]{3}/streaming(-[^\\.]+)?.m3u8$") && (cgi == NULL))\r
+ {\r
+ DebugLog0("uri: %s", uri->cString());\r
+/*\r
+ // URIからチャンネル番号を抽出\r
+ // Range実装したい...\r
+ int ch = uri->substringFromIndex(5)->substringToIndex(3)->intValue();\r
+ DebugLog0("ch: %d", ch);\r
+\r
+ // presetが指定されている場合、presetを抽出\r
+ String *preset = NULL;\r
+ if (uri->isMatch("streaming-"))\r
+ {\r
+ preset = uri->substringFromIndex(19);\r
+ preset = preset->substringToIndex(preset->length() - 5);\r
+ DebugLog0("opt: preset: %s", preset->cString());\r
+ }\r
+ else\r
+ {\r
+ // なければ "default"\r
+ preset = NSString::stringWithUTF8String(KEY_DEFAULT);\r
+ }\r
+\r
+ // チャンネル/presetが有効か確認\r
+ if (isChannelEnabled(tuner, ch) &&\r
+ (_props->dictionaryForKey(KEY_PRESETS) != NULL) &&\r
+ (_props->dictionaryForKey(KEY_PRESETS)->objectForKey(preset) != NULL))\r
+ {\r
+ // \r
+ result = responseForHLSControl(request, client, tuner, ch, preset);\r
+ }\r
+ else\r
+ {\r
+ result = responseForFailed(request);\r
+ }\r
+ DebugLog0("hls req. done");\r
+*/\r
+ }\r
+ else if (uri->isMatch("^/[0-9]{3}/[0-9]{3}/streaming-[0-9]+.ts$") && (cgi == NULL))\r
+ {\r
+ // 分割されたTS\r
+ DebugLog0("uri: %s", uri->cString());\r
+ }\r
+\r
+ break;\r
+ }\r
+\r
+ // unlock\r
+ RaymUnlock(_controller);\r
+\r
+ return result;\r
+}\r
+\r
+// positive response by XML\r
+HTTPResponse *HTTPD::responseForSuccess(HTTPRequest *request)\r
+{\r
+ Dictionary *dict = Dictionary::dictionaryWithCapacity(0);\r
+ dict->setString("Success", KEY_RESULT);\r
+ return responseWithDictionary(request, dict);\r
+}\r
+\r
+// negative response by XML\r
+HTTPResponse *HTTPD::responseForFailed(HTTPRequest *request)\r
+{\r
+ Dictionary *dict = Dictionary::dictionaryWithCapacity(0);\r
+ dict->setString("Failed", KEY_RESULT);\r
+ return responseWithDictionary(request, dict);\r
+}\r
+\r
+HTTPResponse *HTTPD::responseWithDictionary(HTTPRequest *request, Dictionary *dictionary)\r
+{\r
+ HTTPResponse *result = NULL;\r
+ if ((request != NULL) && (dictionary != NULL))\r
+ {\r
+ std::string xml = dictionary->toString();\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("application/xml", "Content-Type");\r
+ // Connection\r
+ // Tranfer-Encoding\r
+ // Content-Length\r
+ header->setFieldBodyWithName(String::stringWithFormat("%I64u", xml.length()), "Content-Length");\r
+\r
+ // body\r
+ InternetTextMessageBody *body = InternetTextMessageBody::alloc()->initWithString(String::stringWithUTF8String(xml.c_str()));\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::alloc()->init();\r
+ result->autorelease();\r
+ result->setVersion(request->version());\r
+ result->setReason(NET::HTTPDaemon::reasonForStatus(200));\r
+ result->setStatus(200);\r
+ result->setMessage(message);\r
+ RELEASE(message);\r
+ }\r
+ }\r
+ return result;\r
+}\r
+\r
} // iPTd\r
} // ry0\r
\r
class Controller;\r
\r
-class HTTPDaemon : public Raym::Object,\r
- public NET::HTTPDaemonDelegate\r
+class HTTPD : public Raym::Object,\r
+ public NET::HTTPDaemonDelegate\r
{\r
private:\r
Controller * _controller;\r
Raym::String * _path;\r
\r
protected:\r
- HTTPDaemon();\r
- ~HTTPDaemon();\r
+ HTTPD();\r
+ ~HTTPD();\r
\r
public:\r
- static HTTPDaemon *alloc();\r
- HTTPDaemon *initWithController(Controller *controller, int port, Raym::String *path);\r
+ static HTTPD *alloc();\r
+ HTTPD *initWithController(Controller *controller, int port, Raym::String *path);\r
\r
bool start();\r
void stop();\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
+\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
};\r
\r
\r
*/\r
\r
#include <time.h>\r
+#include <fcntl.h>\r
+#include <io.h>\r
+#include <direct.h>\r
+\r
\r
#define DBG_LEVEL 3\r
#include "Raym/Log.h"\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
\r
Reservation::Reservation()\r
{\r
_controller = NULL;\r
- _path = NULL;\r
+ _reservations_path = NULL;\r
_reservations = NULL;\r
_reservation_seq_id = -1;\r
}\r
Reservation::~Reservation()\r
{\r
_controller = NULL;\r
- RELEASE(_path);\r
+ RELEASE(_reservations_path);\r
RELEASE(_reservations);\r
}\r
\r
return new Reservation();\r
}\r
\r
-Reservation *Reservation::initWithController(Controller *controller, String *path)\r
+Reservation *Reservation::initWithController(Controller *controller)\r
{\r
_controller = controller;\r
- _path = path->retain();\r
\r
- _reservations = Dictionary::alloc()->initWithContentsOfFile(_path);\r
+ _reservations_path = String::alloc()->initWithFormat("%s%s.iptd.reservations.plist", _controller->_system_path->cString(), Controller::_plist_prefix);\r
+ if (_reservations_path == NULL)\r
+ {\r
+ release();\r
+ return NULL;\r
+ }\r
+\r
+ _reservations = Dictionary::alloc()->initWithContentsOfFile(_reservations_path);\r
if (_reservations == NULL)\r
{\r
- DebugLog1("because \"%s\" is not exists, created.", _path->cString());\r
+ DebugLog1("because \"%s\" is not exists, created.", _reservations_path->cString());\r
_reservations = Dictionary::alloc()->initWithCapacity(0);\r
+ if (_reservations == NULL)\r
+ {\r
+ release();\r
+ return NULL;\r
+ }\r
_reservation_seq_id = 1;\r
}\r
else\r
{\r
- DebugLog1("reservations file: \"%s\"", _path->cString());\r
+ DebugLog1("reservations file: \"%s\"", _reservations_path->cString());\r
\r
// 予約情報シーケンスID\r
_reservation_seq_id = _reservations->integerForKey(KEY_EPG_LAST_RESV_ID);\r
}\r
\r
+ // 周期タイマ起動\r
+ _timer_periodic = Timer::alloc()->initWithTimeInterval(1.0, this, NULL, true);\r
+ if (_timer_periodic == NULL)\r
+ {\r
+ release();\r
+ return NULL;\r
+ }\r
+\r
+ _timer_periodic->fire();\r
+\r
return this;\r
}\r
\r
return true;\r
}\r
\r
+//\r
+// 録画予約:チューナ/EPG指定\r
+//\r
+bool Reservation::reserve(int tuner, Dictionary *in_epg)\r
+{\r
+ DebugLog2("Reservation::reserve(tuner, epg)");\r
+\r
+ bool result = false;\r
+\r
+ // lock\r
+ RaymLock(_controller);\r
+\r
+ while ((0 <= tuner) && (tuner < _controller->_tunerCount) && (in_epg != NULL))\r
+ {\r
+ Dictionary *epg = Dictionary::dictionaryWithDictionary(in_epg);\r
+ if (epg == NULL)\r
+ {\r
+ DebugLog3("Dictionary::dictionaryWithDictionary() ng.");\r
+ break;\r
+ }\r
+\r
+ Array *array = _reservations->arrayForKey(_controller->_tuners[tuner]->name());\r
+ if (array == NULL)\r
+ {\r
+ array = Array::arrayWithCapacity(0);\r
+ _reservations->setObject(array, _controller->_tuners[tuner]->name());\r
+ }\r
+\r
+ time_t epg_start;\r
+ time_t epg_end;\r
+ Controller::getTimeWithEPG(epg, &epg_start, &epg_end);\r
+ DebugLog2("epg start: %ld, end: %ld\n", epg_start, epg_end);\r
+\r
+ time_t pre_start = 0;\r
+ time_t pre_end = 0;\r
+ pre_end = time(NULL);\r
+ DebugLog2("pre_end: %ld", pre_end);\r
+\r
+ for (uint i = 0; i < array->count(); ++i)\r
+ {\r
+ Dictionary *cur = (Dictionary *)array->objectAtIndex(i);\r
+ time_t cur_start;\r
+ time_t cur_end;\r
+ Controller::getTimeWithEPG(cur, &cur_start, &cur_end);\r
+ DebugLog2("cur start: %ld, end: %ld\n", cur_start, cur_end);\r
+ if ((pre_end <= epg_start) && (epg_end <= cur_start))\r
+ {\r
+ DebugLog2("insert: %d\n", i);\r
+ array->insertObject(epg, i);\r
+ result = true;\r
+ break;\r
+ }\r
+ pre_start = cur_start;\r
+ pre_end = cur_end;\r
+ }\r
+\r
+ if (!result)\r
+ {\r
+ if (pre_end <= epg_start)\r
+ {\r
+ DebugLog2("add\n");\r
+ array->addObject(epg);\r
+ result = true;\r
+ }\r
+ else\r
+ {\r
+ DebugLog2("no add\n");\r
+ }\r
+ }\r
+ if (result)\r
+ {\r
+ epg->setInteger(_reservation_seq_id, KEY_EPG_RESV_ID);\r
+ _reservation_seq_id = (_reservation_seq_id + 1) % 1000000;\r
+ _reservations->setInteger(_reservation_seq_id, KEY_EPG_LAST_RESV_ID);\r
+\r
+ //\r
+ _reservations->writeToFile(_reservations_path, true);\r
+ }\r
+\r
+ break;\r
+ }\r
+\r
+ // unlock\r
+ RaymUnlock(_controller);\r
+\r
+ return result;\r
+}\r
+\r
+//\r
+// tuner: 0 - (_tunerCount - 1) cancel current\r
+// tuner: -1 cancel reserve_id\r
+//\r
+bool Reservation::cancel(int tuner, int reserve_id)\r
+{\r
+ bool result = false;\r
+\r
+ // lock\r
+ RaymLock(_controller);\r
+\r
+ //\r
+ if ((0 <= tuner) && (tuner < _controller->_tunerCount))\r
+ {\r
+ Array *array = _reservations->arrayForKey(_controller->_tuners[tuner]->name());\r
+ if (array != NULL)\r
+ {\r
+ if (array->count() > 0)\r
+ {\r
+ Dictionary *epg = (Dictionary *)array->objectAtIndex(0);\r
+ String *status = epg->stringForKey(KEY_EPG_STATUS);\r
+ if (status != NULL)\r
+ {\r
+ if (status->isEqualToString("running"))\r
+ {\r
+ epg->setString("stop", KEY_EPG_STATUS);\r
+ result = true;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ else if ((tuner < 0) && (0 <= reserve_id) && (reserve_id < 1000000))\r
+ {\r
+ for (int i = 0; i < _controller->_tunerCount; ++i)\r
+ {\r
+ Array *array = _reservations->arrayForKey(_controller->_tuners[i]->name());\r
+ if (array != NULL)\r
+ {\r
+ for (uint j = 0; j < array->count(); ++j)\r
+ {\r
+ Dictionary *epg = (Dictionary *)array->objectAtIndex(j);\r
+ if (reserve_id == epg->integerForKey(KEY_EPG_RESV_ID))\r
+ {\r
+ String *status = epg->stringForKey(KEY_EPG_STATUS);\r
+ if ((status != NULL) && status->isEqualToString("running"))\r
+ {\r
+ epg->setString("stop", KEY_EPG_STATUS);\r
+ }\r
+ else\r
+ {\r
+ array->removeObjectAtIndex(j);\r
+ }\r
+ result = true;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ if (result)\r
+ {\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (result)\r
+ {\r
+ _reservations->writeToFile(_reservations_path, true);\r
+ }\r
+\r
+ // unlock\r
+ RaymUnlock(_controller);\r
+\r
+ return result;\r
+}\r
+\r
+std::string Reservation::createVideoPath(int tuner)\r
+{\r
+ DebugLog2("Reservation::createVideoPath()");\r
+\r
+ std::string result = "";\r
+\r
+ while (true)\r
+ {\r
+ time_t now;\r
+ time(&now);\r
+ TM tm;\r
+ if (localtime_s(&tm, &now) != 0)\r
+ {\r
+ break;\r
+ }\r
+\r
+ result = _controller->_props->stringForKey(KEY_STORE_PATH)->cString();\r
+ DebugLog2("result: %s\n", result.c_str());\r
+\r
+ char tmp[128];\r
+ if (sprintf_s(tmp, sizeof(tmp), "\\%04d", tm.tm_year + 1900) < 0)\r
+ {\r
+ DebugLog0("sprintf_s() error: year\n");\r
+ result = "";\r
+ break;\r
+ }\r
+ result += tmp;\r
+ DebugLog2("result: %s\n", result.c_str());\r
+\r
+ STAT stat;\r
+ if (_stat(result.c_str(), &stat) != 0)\r
+ {\r
+ if (_mkdir(result.c_str()) != 0)\r
+ {\r
+ DebugLog0("_mkdir() error: year\n");\r
+ result = "";\r
+ break;\r
+ }\r
+ _stat(result.c_str(), &stat);\r
+ }\r
+ if ((stat.st_mode & _S_IFDIR) != _S_IFDIR)\r
+ {\r
+ DebugLog0("%s is not directory.\n", result.c_str());\r
+ result = "";\r
+ break;\r
+ }\r
+\r
+ if (sprintf_s(tmp, sizeof(tmp), "\\%02d", tm.tm_mon + 1) < 0)\r
+ {\r
+ DebugLog0("sprintf_s() error: month\n");\r
+ result = "";\r
+ break;\r
+ }\r
+ result += tmp;\r
+ DebugLog2("result: %s\n", result.c_str());\r
+\r
+ if (_stat(result.c_str(), &stat) != 0)\r
+ {\r
+ if (_mkdir(result.c_str()) != 0)\r
+ {\r
+ DebugLog0("_mkdir() error: month\n");\r
+ result = "";\r
+ break;\r
+ }\r
+ _stat(result.c_str(), &stat);\r
+ }\r
+ if ((stat.st_mode & _S_IFDIR) != _S_IFDIR)\r
+ {\r
+ DebugLog0("%s is not directory.", result.c_str());\r
+ result = "";\r
+ break;\r
+ }\r
+\r
+ if (sprintf_s(tmp, sizeof(tmp),\r
+ "\\%04d%02d%02d_%02d%02d%02d_%03d_%s.ts",\r
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,\r
+ _controller->_tuners[tuner]->channel(), _controller->_tuners[tuner]->name()) < 0)\r
+ {\r
+ DebugLog0("sprintf_s() error: filename");\r
+ result = "";\r
+ break;\r
+ }\r
+ result += tmp;\r
+ DebugLog2("result: %s\n", result.c_str());\r
+\r
+ break;\r
+ }\r
+\r
+ return result;\r
+}\r
+\r
+void Reservation::timerExpired(Timer *timer, void *userInfo)\r
+{\r
+ bool need_update = false;\r
+\r
+#ifdef RAYM_MEMORY_CHECK\r
+ DebugLog0("global_objc_count_ = %d", Raym::global_raym_count_);\r
+#endif\r
+\r
+ // lock\r
+ RaymLock(_controller);\r
+\r
+ // 現在時刻取得\r
+ time_t now = time(NULL);\r
+\r
+ DebugLog2("periodic: %d", now);\r
+\r
+ //\r
+ for (int tuner = 0; tuner < _controller->_tunerCount; ++tuner)\r
+ {\r
+ Array *array = _reservations->arrayForKey(_controller->_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
+ Controller::getTimeWithEPG(epg, &start, &end);\r
+ \r
+ //\r
+ // 録画停止要否チェック\r
+ //\r
+ bool stop_need = false;\r
+ while (true)\r
+ {\r
+ String *status = epg->stringForKey(KEY_EPG_STATUS);\r
+ if (status != NULL)\r
+ {\r
+ if (status->isEqualToString("stop"))\r
+ {\r
+ stop_need = true;\r
+ break;\r
+ }\r
+ if (!status->isEqualToString("running"))\r
+ {\r
+ break;\r
+ }\r
+ }\r
+ if (end + OFFSET_OF_END_TIME <= now)\r
+ {\r
+ stop_need = true;\r
+ }\r
+ break;\r
+ }\r
+ if (stop_need)\r
+ {\r
+ DebugLog2("I try stop\n");\r
+ int fd = _controller->_tuners[tuner]->stopRecording();\r
+ if (fd < 0)\r
+ {\r
+ DebugLog1("stopRecording() error.\n");\r
+ }\r
+ else\r
+ {\r
+ DebugLog2("stopRecording() ok\n");\r
+ DebugLog0("stop recording of \"%s\"", _controller->_tuners[tuner]->name());\r
+ _close(fd);\r
+ }\r
+ array->removeObject(epg);\r
+ \r
+ if (array->count() > 0)\r
+ {\r
+ epg = (Dictionary *)array->objectAtIndex(0);\r
+ }\r
+ else\r
+ {\r
+ epg = NULL;\r
+ }\r
+ need_update = true;\r
+ }\r
+\r
+ if (epg == NULL)\r
+ {\r
+ // next tuner\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // 録画開始要否チェック\r
+ //\r
+ bool start_need = false;\r
+ start = end = 0;\r
+ Controller::getTimeWithEPG(epg, &start, &end);\r
+ if ((start != 0) && (end != 0))\r
+ {\r
+ String *status = epg->stringForKey(KEY_EPG_STATUS);\r
+ if ((status == NULL) || !(status->isEqualToString("running")))\r
+ {\r
+ if (end + OFFSET_OF_END_TIME <= now)\r
+ {\r
+ // 既に終了時間が経過しているので削除する\r
+ array->removeObject(epg);\r
+ }\r
+ else if (start + OFFSET_OF_START_TIME <= now)\r
+ {\r
+ start_need = true;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (start_need)\r
+ {\r
+ DebugLog2("I need start.\n");\r
+ String *ch = epg->stringForKey(KEY_EPG_CHANNEL);\r
+ if (ch != NULL)\r
+ {\r
+ int channel = atoi(ch->cString());\r
+ DebugLog2("channel: %d\n", channel);\r
+ std::string videopath = createVideoPath(tuner);\r
+ if (videopath != "")\r
+ {\r
+ DebugLog2("videopath: %s\n", videopath.c_str());\r
+ int fd = -1;\r
+ if (_sopen_s(&fd, videopath.c_str(),\r
+ (_O_CREAT | _O_EXCL | _O_WRONLY | _O_BINARY | _O_TRUNC), _SH_DENYRW, (_S_IREAD | _S_IWRITE)) == 0)\r
+ {\r
+ DebugLog2("open ok.\n");\r
+ bool startResult = true;\r
+ if (_controller->_tuners[tuner]->channel() != channel)\r
+ {\r
+ if (!_controller->setChannel(tuner, channel))\r
+ {\r
+ DebugLog3("setChannel() ng.");\r
+ startResult = false;\r
+ }\r
+ }\r
+\r
+ if (startResult)\r
+ {\r
+ if (_controller->_tuners[tuner]->startRecording(fd))\r
+ {\r
+ DebugLog2("startRecording() ok.");\r
+ DebugLog0("start recording of \"%s\" to %s.", _controller->_tuners[tuner]->name(), videopath.c_str());\r
+ }\r
+ else\r
+ {\r
+ DebugLog3("Tuner::startRecording() failed.");\r
+ startResult = false;\r
+ }\r
+ }\r
+\r
+ if (startResult)\r
+ {\r
+ epg->setString("running", KEY_EPG_STATUS);\r
+ }\r
+ else\r
+ {\r
+ _close(fd);\r
+ }\r
+ }\r
+ else\r
+ {\r
+ DebugLog0("open ng. 0x%08x\n", errno);\r
+ }\r
+ }\r
+ else\r
+ {\r
+ DebugLog0("Can't create videopath.\n");\r
+ }\r
+ }\r
+ else\r
+ {\r
+ DebugLog0("error.\n");\r
+ }\r
+ }\r
+ }\r
+\r
+ if (need_update)\r
+ {\r
+ //\r
+ _reservations->writeToFile(_reservations_path, true);\r
+ }\r
+\r
+#if 0\r
+ // EPG収集時刻を取得\r
+ NSString *collect_str = _props->stringForKey(KEY_COLLECT_EPG_TIME);\r
+ if (collect_str != NULL)\r
+ {\r
+ // 秒に変換\r
+ time_t collect_time = 0;\r
+ getTimeWithString(collect_str, &collect_time);\r
+\r
+ // 現在時刻と比較\r
+ if ((collect_time <= now) && (now < collect_time + 1))\r
+ {\r
+ // タイマが起動中か確認\r
+ if ((_timer_epg_s == NULL) || !_timer_epg_s->valid())\r
+ {\r
+ // EPG収集用タイマ起動(ISDB-S)\r
+ RELEASE(_timer_epg_s);\r
+ _timer_epg_s = NSTimer::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
+ // タイマが起動中か確認\r
+ if ((_timer_epg_t == NULL) || !_timer_epg_t->valid())\r
+ {\r
+ // EPG収集用タイマ起動(ISDB-T)\r
+ RELEASE(_timer_epg_t);\r
+ _timer_epg_t = NSTimer::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
+ }\r
+#endif\r
+\r
+ // unlock\r
+ RaymUnlock(_controller);\r
+\r
+ //\r
+ // 1/100秒単位が 0 に近くなるように次回T.O.を微調整\r
+ // ただし、windowsは精度が低いので期待しないことw\r
+ //\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
+#ifdef RAYM_MEMORY_CHECK\r
+ DebugLog0("global_objc_count_ = %d", Raym::global_raym_count_);\r
+#endif\r
+}\r
+\r
} // iPTd\r
} // ry0\r
\r
class Controller;\r
\r
-class Reservation : public Raym::Object\r
+class Reservation : public Raym::Object,\r
+ public Raym::TimerDelegate\r
+\r
{\r
private:\r
Controller * _controller;\r
- Raym::String * _path;\r
+ Raym::String * _reservations_path;\r
Raym::Dictionary * _reservations; // 予約情報\r
int _reservation_seq_id; // 予約情報シーケンスID\r
+ Raym::Timer * _timer_periodic;\r
\r
\r
protected:\r
\r
public:\r
static Reservation *alloc();\r
- Reservation *initWithController(Controller *controller, Raym::String *path);\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 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
+\r
+ std::string createVideoPath(int tuner);\r
+\r
+ // タイマ満了IF (from Timer)\r
+ void timerExpired(Raym::Timer *timer, void *userInfo);\r
};\r
\r
\r
\r
// 制御情報\r
_ctrls = Dictionary::alloc()->initWithCapacity(0);\r
+ if (_ctrls == NULL)\r
+ {\r
+ release();\r
+ return NULL;\r
+ }\r
\r
// 周期タイマ起動\r
_timer_periodic = Timer::alloc()->initWithTimeInterval(1.0, this, NULL, true);\r
+ if (_timer_periodic == NULL)\r
+ {\r
+ release();\r
+ return NULL;\r
+ }\r
+\r
_timer_periodic->fire();\r
\r
return this;\r