2004-12-30 23:43:55 +01:00
|
|
|
/*
|
|
|
|
* streamdev.c: A plugin for the Video Disk Recorder
|
|
|
|
*
|
|
|
|
* See the README file for copyright information and how to reach the author.
|
|
|
|
*
|
2010-12-02 09:57:17 +01:00
|
|
|
* $Id: streamdev-server.c,v 1.2 2010/07/19 13:49:32 schmirl Exp $
|
2004-12-30 23:43:55 +01:00
|
|
|
*/
|
|
|
|
|
2007-02-19 13:08:16 +01:00
|
|
|
#include <getopt.h>
|
2008-10-14 13:05:46 +02:00
|
|
|
#include <vdr/tools.h>
|
2004-12-30 23:43:55 +01:00
|
|
|
#include "streamdev-server.h"
|
2011-11-28 16:23:57 +01:00
|
|
|
#include "server/menu.h"
|
2004-12-30 23:43:55 +01:00
|
|
|
#include "server/setup.h"
|
|
|
|
#include "server/server.h"
|
2013-11-02 16:58:17 +01:00
|
|
|
#include "server/suspend.h"
|
2004-12-30 23:43:55 +01:00
|
|
|
|
2012-03-05 10:18:38 +01:00
|
|
|
#if !defined(APIVERSNUM) || APIVERSNUM < 10725
|
|
|
|
#error "VDR-1.7.25 or greater required to compile server! Use 'make client' to compile client only."
|
2008-04-07 16:27:27 +02:00
|
|
|
#endif
|
|
|
|
|
2014-06-07 00:24:27 +02:00
|
|
|
cList<cMainThreadHookSubscriber> cMainThreadHookSubscriber::m_Subscribers;
|
|
|
|
cMutex cMainThreadHookSubscriber::m_Mutex;
|
|
|
|
|
2016-03-21 00:28:02 +01:00
|
|
|
#if APIVERSNUM >= 20300
|
|
|
|
cList<cMainThreadHookSubscriber>& cMainThreadHookSubscriber::Subscribers(cMutexLock& Lock)
|
|
|
|
#else
|
2014-06-07 00:24:27 +02:00
|
|
|
const cList<cMainThreadHookSubscriber>& cMainThreadHookSubscriber::Subscribers(cMutexLock& Lock)
|
2016-03-21 00:28:02 +01:00
|
|
|
#endif
|
2014-06-07 00:24:27 +02:00
|
|
|
{
|
|
|
|
Lock.Lock(&m_Mutex);
|
|
|
|
return m_Subscribers;
|
|
|
|
}
|
|
|
|
|
|
|
|
cMainThreadHookSubscriber::cMainThreadHookSubscriber()
|
|
|
|
{
|
|
|
|
m_Mutex.Lock();
|
|
|
|
m_Subscribers.Add(this);
|
|
|
|
m_Mutex.Unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
cMainThreadHookSubscriber::~cMainThreadHookSubscriber()
|
|
|
|
{
|
|
|
|
m_Mutex.Lock();
|
|
|
|
m_Subscribers.Del(this, false);
|
|
|
|
m_Mutex.Unlock();
|
|
|
|
}
|
|
|
|
|
2008-04-08 16:18:15 +02:00
|
|
|
const char *cPluginStreamdevServer::DESCRIPTION = trNOOP("VDR Streaming Server");
|
2004-12-30 23:43:55 +01:00
|
|
|
|
2005-05-09 22:22:29 +02:00
|
|
|
cPluginStreamdevServer::cPluginStreamdevServer(void)
|
|
|
|
{
|
2013-11-02 16:58:17 +01:00
|
|
|
m_Suspend = false;
|
2004-12-30 23:43:55 +01:00
|
|
|
}
|
|
|
|
|
2005-05-09 22:22:29 +02:00
|
|
|
cPluginStreamdevServer::~cPluginStreamdevServer()
|
|
|
|
{
|
2008-10-14 13:05:46 +02:00
|
|
|
free(opt_auth);
|
2008-04-29 09:00:53 +02:00
|
|
|
free(opt_remux);
|
2004-12-30 23:43:55 +01:00
|
|
|
}
|
|
|
|
|
2005-05-09 22:22:29 +02:00
|
|
|
const char *cPluginStreamdevServer::Description(void)
|
|
|
|
{
|
2004-12-30 23:43:55 +01:00
|
|
|
return tr(DESCRIPTION);
|
|
|
|
}
|
|
|
|
|
2007-02-19 13:08:16 +01:00
|
|
|
const char *cPluginStreamdevServer::CommandLineHelp(void)
|
|
|
|
{
|
|
|
|
// return a string that describes all known command line options.
|
2008-10-14 13:05:46 +02:00
|
|
|
return
|
|
|
|
" -a <LOGIN:PASSWORD>, --auth=<LOGIN:PASSWORD> Credentials for HTTP authentication.\n"
|
|
|
|
" -r <CMD>, --remux=<CMD> Define an external command for remuxing.\n"
|
|
|
|
;
|
2007-02-19 13:08:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool cPluginStreamdevServer::ProcessArgs(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
// implement command line argument processing here if applicable.
|
|
|
|
static const struct option long_options[] = {
|
2008-10-14 13:05:46 +02:00
|
|
|
{ "auth", required_argument, NULL, 'a' },
|
2007-02-19 13:08:16 +01:00
|
|
|
{ "remux", required_argument, NULL, 'r' },
|
|
|
|
{ NULL, 0, NULL, 0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
int c;
|
2008-10-14 13:05:46 +02:00
|
|
|
while((c = getopt_long(argc, argv, "a:r:", long_options, NULL)) != -1) {
|
2007-02-19 13:08:16 +01:00
|
|
|
switch (c) {
|
2008-10-14 13:05:46 +02:00
|
|
|
case 'a':
|
|
|
|
{
|
|
|
|
if (opt_auth)
|
|
|
|
free(opt_auth);
|
|
|
|
int l = strlen(optarg);
|
|
|
|
cBase64Encoder Base64((uchar*) optarg, l, l * 4 / 3 + 3);
|
|
|
|
const char *s = Base64.NextLine();
|
|
|
|
if (s)
|
|
|
|
opt_auth = strdup(s);
|
|
|
|
}
|
|
|
|
break;
|
2007-02-19 13:08:16 +01:00
|
|
|
case 'r':
|
2008-04-29 09:00:53 +02:00
|
|
|
if (opt_remux)
|
|
|
|
free(opt_remux);
|
|
|
|
opt_remux = strdup(optarg);
|
2007-02-19 13:08:16 +01:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2005-05-09 22:22:29 +02:00
|
|
|
bool cPluginStreamdevServer::Start(void)
|
|
|
|
{
|
2008-04-08 16:18:15 +02:00
|
|
|
I18nRegister(PLUGIN_NAME_I18N);
|
2005-05-09 22:22:29 +02:00
|
|
|
if (!StreamdevHosts.Load(STREAMDEVHOSTSPATH, true, true)) {
|
|
|
|
esyslog("streamdev-server: error while loading %s", STREAMDEVHOSTSPATH);
|
2007-04-16 13:01:02 +02:00
|
|
|
fprintf(stderr, "streamdev-server: error while loading %s\n", STREAMDEVHOSTSPATH);
|
2005-05-09 22:22:29 +02:00
|
|
|
if (access(STREAMDEVHOSTSPATH, F_OK) != 0) {
|
2004-12-30 23:43:55 +01:00
|
|
|
fprintf(stderr, " Please install streamdevhosts.conf into the path "
|
|
|
|
"printed above. Without it\n"
|
2005-05-09 22:22:29 +02:00
|
|
|
" no client will be able to access your streaming-"
|
|
|
|
"server. An example can be\n"
|
|
|
|
" found together with this plugin's sources.\n");
|
|
|
|
}
|
2004-12-30 23:43:55 +01:00
|
|
|
return false;
|
|
|
|
}
|
2008-04-29 09:00:53 +02:00
|
|
|
if (!opt_remux)
|
|
|
|
opt_remux = strdup(DEFAULT_EXTERNREMUX);
|
2004-12-30 23:43:55 +01:00
|
|
|
|
2005-05-09 22:22:29 +02:00
|
|
|
cStreamdevServer::Initialize();
|
2004-12-30 23:43:55 +01:00
|
|
|
|
2013-11-02 16:58:17 +01:00
|
|
|
m_Suspend = StreamdevServerSetup.StartSuspended == ssAuto ?
|
|
|
|
!cDevice::PrimaryDevice()->HasDecoder() :
|
|
|
|
StreamdevServerSetup.StartSuspended;
|
2005-05-09 22:22:29 +02:00
|
|
|
return true;
|
2004-12-30 23:43:55 +01:00
|
|
|
}
|
|
|
|
|
2005-05-09 22:22:29 +02:00
|
|
|
void cPluginStreamdevServer::Stop(void)
|
|
|
|
{
|
|
|
|
cStreamdevServer::Destruct();
|
|
|
|
}
|
|
|
|
|
2006-07-05 22:36:58 +02:00
|
|
|
cString cPluginStreamdevServer::Active(void)
|
2005-05-09 22:22:29 +02:00
|
|
|
{
|
2006-07-05 22:36:58 +02:00
|
|
|
if (cStreamdevServer::Active())
|
|
|
|
{
|
2006-11-24 12:45:36 +01:00
|
|
|
static const char *Message = NULL;
|
|
|
|
if (!Message) Message = tr("Streaming active");
|
|
|
|
return Message;
|
2006-07-05 22:36:58 +02:00
|
|
|
}
|
|
|
|
return NULL;
|
2004-12-30 23:43:55 +01:00
|
|
|
}
|
|
|
|
|
2005-05-09 22:22:29 +02:00
|
|
|
const char *cPluginStreamdevServer::MainMenuEntry(void)
|
|
|
|
{
|
2011-12-08 13:07:17 +01:00
|
|
|
return !StreamdevServerSetup.HideMenuEntry ? tr("Streamdev Connections") : NULL;
|
2004-12-30 23:43:55 +01:00
|
|
|
}
|
|
|
|
|
2005-05-09 22:22:29 +02:00
|
|
|
cOsdObject *cPluginStreamdevServer::MainMenuAction(void)
|
|
|
|
{
|
2011-11-28 16:23:57 +01:00
|
|
|
return new cStreamdevServerMenu();
|
2004-12-30 23:43:55 +01:00
|
|
|
}
|
|
|
|
|
2011-03-07 21:37:08 +01:00
|
|
|
void cPluginStreamdevServer::MainThreadHook(void)
|
|
|
|
{
|
2013-11-02 16:58:17 +01:00
|
|
|
if (m_Suspend) {
|
|
|
|
cControl::Launch(new cSuspendCtl);
|
|
|
|
m_Suspend = false;
|
|
|
|
}
|
|
|
|
|
2014-06-07 00:24:27 +02:00
|
|
|
cMutexLock lock;
|
2016-03-21 00:28:02 +01:00
|
|
|
#if APIVERSNUM >= 20300
|
|
|
|
cList<cMainThreadHookSubscriber>& subs = cMainThreadHookSubscriber::Subscribers(lock);
|
|
|
|
#else
|
2014-06-07 00:24:27 +02:00
|
|
|
const cList<cMainThreadHookSubscriber>& subs = cMainThreadHookSubscriber::Subscribers(lock);
|
2016-03-21 00:28:02 +01:00
|
|
|
#endif
|
2014-06-07 00:24:27 +02:00
|
|
|
for (cMainThreadHookSubscriber *s = subs.First(); s; s = subs.Next(s))
|
2011-11-28 16:23:57 +01:00
|
|
|
s->MainThreadHook();
|
2011-03-07 21:37:08 +01:00
|
|
|
}
|
|
|
|
|
2005-05-09 22:22:29 +02:00
|
|
|
cMenuSetupPage *cPluginStreamdevServer::SetupMenu(void)
|
|
|
|
{
|
|
|
|
return new cStreamdevServerMenuSetupPage;
|
2004-12-30 23:43:55 +01:00
|
|
|
}
|
|
|
|
|
2005-05-09 22:22:29 +02:00
|
|
|
bool cPluginStreamdevServer::SetupParse(const char *Name, const char *Value)
|
|
|
|
{
|
|
|
|
return StreamdevServerSetup.SetupParse(Name, Value);
|
2004-12-30 23:43:55 +01:00
|
|
|
}
|
|
|
|
|
2014-11-07 23:01:08 +01:00
|
|
|
const char **cPluginStreamdevServer::SVDRPHelpPages(void)
|
|
|
|
{
|
|
|
|
static const char *HelpPages[]=
|
|
|
|
{
|
|
|
|
"LSTC\n"
|
|
|
|
" List connected clients\n",
|
|
|
|
"DISC client_id\n"
|
|
|
|
" Disconnect a client\n",
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
return HelpPages;
|
|
|
|
}
|
|
|
|
|
|
|
|
cString cPluginStreamdevServer::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
|
|
|
|
{
|
|
|
|
|
|
|
|
cString reply = NULL;
|
|
|
|
if (!strcasecmp(Command, "LSTC"))
|
|
|
|
{
|
|
|
|
reply = "";
|
|
|
|
cThreadLock lock;
|
2016-03-21 00:28:02 +01:00
|
|
|
#if APIVERSNUM >= 20300
|
|
|
|
cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
|
|
|
|
#else
|
2014-11-07 23:01:08 +01:00
|
|
|
const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
|
2016-03-21 00:28:02 +01:00
|
|
|
#endif
|
2014-11-07 23:01:08 +01:00
|
|
|
cServerConnection *s = clients.First();
|
|
|
|
if (!s)
|
|
|
|
{
|
|
|
|
reply = "no client connected";
|
|
|
|
ReplyCode = 550;
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
for (; s; s = clients.Next(s))
|
|
|
|
{
|
|
|
|
reply = cString::sprintf("%s%p: %s\n", (const char*) reply, s, (const char *) s->ToText());
|
|
|
|
}
|
|
|
|
ReplyCode = 250;
|
|
|
|
}
|
|
|
|
} else if (!strcasecmp(Command, "DISC"))
|
|
|
|
{
|
|
|
|
void *client = NULL;
|
|
|
|
if (sscanf(Option, "%p", &client) != 1 || !client)
|
|
|
|
{
|
|
|
|
reply = "invalid client handle";
|
|
|
|
ReplyCode = 501;
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
cThreadLock lock;
|
2016-03-21 00:28:02 +01:00
|
|
|
#if APIVERSNUM >= 20300
|
|
|
|
cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
|
|
|
|
#else
|
2014-11-07 23:01:08 +01:00
|
|
|
const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock);
|
2016-03-21 00:28:02 +01:00
|
|
|
#endif
|
2014-11-07 23:01:08 +01:00
|
|
|
cServerConnection *s = clients.First();
|
|
|
|
for (; s && s != client; s = clients.Next(s));
|
|
|
|
|
|
|
|
if (!s)
|
|
|
|
{
|
|
|
|
reply = "client not found";
|
|
|
|
ReplyCode = 501;
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
s->Close();
|
|
|
|
reply = "client disconnected";
|
|
|
|
ReplyCode = 250;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return reply;
|
|
|
|
}
|
|
|
|
|
2014-11-07 23:51:13 +01:00
|
|
|
bool cPluginStreamdevServer::Service(const char *Id, void *Data)
|
|
|
|
{
|
|
|
|
if (strcmp(Id, "StreamdevServer::ClientCount-v1.0") == 0) {
|
|
|
|
if (Data) {
|
|
|
|
int *count = (int *) Data;
|
|
|
|
cThreadLock lock;
|
|
|
|
*count = cStreamdevServer::Clients(lock).Count();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2004-12-30 23:43:55 +01:00
|
|
|
VDRPLUGINCREATOR(cPluginStreamdevServer); // Don't touch this!
|