summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTORS4
-rw-r--r--HISTORY23
-rw-r--r--client/device.c24
-rw-r--r--client/device.h6
-rw-r--r--client/socket.c36
-rw-r--r--client/socket.h5
-rw-r--r--i18n.h2
-rw-r--r--remux/extern.c5
-rw-r--r--server/connection.c8
-rw-r--r--server/connectionVTP.c48
-rw-r--r--server/connectionVTP.h6
-rw-r--r--server/livestreamer.c21
-rw-r--r--server/livestreamer.h1
-rw-r--r--server/menuHTTP.c19
-rw-r--r--server/menuHTTP.h12
-rw-r--r--streamdev-client.c6
-rw-r--r--streamdev-client.h3
-rwxr-xr-xstreamdev/externremux.sh84
18 files changed, 257 insertions, 56 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 54757c3..4261e63 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -132,6 +132,10 @@ Norman Thiel
vel_tins
for reporting that externremux x264 uses value of ABR for VBR
+ for various suggestions to improve externremux.sh
Matthias Prill
for reporting a compiler error with older libstdc++ versions
+
+Timothy D. Lenz
+ for reporting missing support for invisible channel groups in HTTP menu
diff --git a/HISTORY b/HISTORY
index 7624838..e0a4789 100644
--- a/HISTORY
+++ b/HISTORY
@@ -1,6 +1,29 @@
VDR Plugin 'streamdev' Revision History
---------------------------------------
+- VTP no longer uses a static priority value for its server-side receivers.
+ The server stores channel and priority requested with the PROV command and
+ re-uses these values in a subsequent TUNE for the same channel. The new
+ PRIO command is used to update the receiver's priority if necessary.
+- added parameter HEIGHT to externremux.sh
+- fixed syslog messages reporting local instead of remote IP and port
+- log an error if externremux.sh is missing or not executable
+- added dsyslog messages to help troubleshouting channel switch issues
+- VTP command SUSP didn't attach the player to the primary device
+- replacing a connections receiver is now an atomic operation. Solves
+ stuttering audio/video due to lost TS packets when adding/removing PIDs
+- fixed missing support for invisible channel groups (groups without name)
+ in HTTP menu (reported by Timothy D. Lenz)
+- don't quote actual program call in externremux.sh, so you can run th
+ program through e.g. nice or taskset just by extending the variable
+ which holds the program name
+- in externremux.sh each mencoder audio and video codec has a dedicated
+ variable for a default option string now. Still you can override each
+ default option with an URL parameter
+- externremux.sh mencoder now uses scale parameter with negative height
+ instead of -xy for scaling (suggested by vel_tins@vdrportal)
+- added FPS (frames per second) parameter to externremux.sh (suggested by
+ vel_tins@vdrportal)
- don't use std::map.at(). It's not available in older libstdc++ version
(reported by Matthias Prill)
- fixed extremux x264 using value of ABR for VBR (thanks to vel_tins@vdrportal)
diff --git a/client/device.c b/client/device.c
index 581176f..bbdaf6a 100644
--- a/client/device.c
+++ b/client/device.c
@@ -1,5 +1,5 @@
/*
- * $Id: device.c,v 1.18.2.3 2010/06/08 05:56:15 schmirl Exp $
+ * $Id: device.c,v 1.18.2.4 2010/08/18 10:26:18 schmirl Exp $
*/
#include "client/device.h"
@@ -8,6 +8,7 @@
#include "tools/select.h"
+#include <vdr/config.h>
#include <vdr/channels.h>
#include <vdr/ringbuffer.h>
#include <vdr/eit.h>
@@ -32,6 +33,7 @@ cStreamdevDevice::cStreamdevDevice(void) {
m_Device = this;
m_Pids = 0;
+ m_Priority = -1;
m_DvrClosed = true;
}
@@ -108,7 +110,7 @@ bool cStreamdevDevice::ProvidesChannel(const cChannel *Channel, int Priority,
res = prio && ClientSocket.ProvidesChannel(Channel, Priority);
ndr = true;
}
-
+
if (NeedsDetachReceivers)
*NeedsDetachReceivers = ndr;
Dprintf("prov res = %d, ndr = %d\n", res, ndr);
@@ -119,6 +121,9 @@ bool cStreamdevDevice::SetChannelDevice(const cChannel *Channel,
bool LiveView) {
Dprintf("SetChannelDevice Channel: %s, LiveView: %s\n", Channel->Name(),
LiveView ? "true" : "false");
+ LOCK_THREAD;
+
+ m_UpdatePriority = ClientSocket.SupportsPrio();
if (LiveView)
return false;
@@ -139,6 +144,8 @@ bool cStreamdevDevice::SetPid(cPidHandle *Handle, int Type, bool On) {
Handle->used);
LOCK_THREAD;
+ m_UpdatePriority = ClientSocket.SupportsPrio();
+
if (On && !m_TSBuffer) {
Dprintf("SetPid: no data connection -> OpenDvr()");
OpenDvrInt();
@@ -305,3 +312,16 @@ bool cStreamdevDevice::ReInit(void) {
return StreamdevClientSetup.StartClient ? Init() : true;
}
+void cStreamdevDevice::UpdatePriority(void) {
+ if (m_Device) {
+ m_Device->Lock();
+ if (m_Device->m_UpdatePriority && ClientSocket.DataSocket(siLive)) {
+ int Priority = m_Device->Priority();
+ if (m_Device == cDevice::ActualDevice() && Priority < Setup.PrimaryLimit)
+ Priority = Setup.PrimaryLimit;
+ if (m_Device->m_Priority != Priority && ClientSocket.SetPriority(Priority))
+ m_Device->m_Priority = Priority;
+ }
+ m_Device->Unlock();
+ }
+}
diff --git a/client/device.h b/client/device.h
index ada66de..909e015 100644
--- a/client/device.h
+++ b/client/device.h
@@ -1,5 +1,5 @@
/*
- * $Id: device.h,v 1.7.2.1 2008/04/07 15:07:39 schmirl Exp $
+ * $Id: device.h,v 1.7.2.2 2010/08/18 10:26:18 schmirl Exp $
*/
#ifndef VDR_STREAMDEV_DEVICE_H
@@ -15,13 +15,14 @@ class cTBString;
#define CMD_LOCK_OBJ(x) cMutexLock CmdLock((cMutex*)&(x)->m_Mutex)
class cStreamdevDevice: public cDevice {
- friend class cRemoteRecordings;
private:
const cChannel *m_Channel;
cTSBuffer *m_TSBuffer;
cStreamdevFilters *m_Filters;
int m_Pids;
+ int m_Priority;
+ bool m_UpdatePriority;
bool m_DvrClosed;
static cStreamdevDevice *m_Device;
@@ -56,6 +57,7 @@ public:
bool *NeedsDetachReceivers = NULL) const;
virtual bool IsTunedToTransponder(const cChannel *Channel);
+ static void UpdatePriority(void);
static bool Init(void);
static bool ReInit(void);
diff --git a/client/socket.c b/client/socket.c
index e766f7c..f2b5eea 100644
--- a/client/socket.c
+++ b/client/socket.c
@@ -1,5 +1,5 @@
/*
- * $Id: socket.c,v 1.11.2.1 2010/06/08 05:56:15 schmirl Exp $
+ * $Id: socket.c,v 1.11.2.3 2010/08/18 10:26:18 schmirl Exp $
*/
#include <tools/select.h>
@@ -21,6 +21,7 @@ cClientSocket ClientSocket;
cClientSocket::cClientSocket(void)
{
memset(m_DataSockets, 0, sizeof(cTBSocket*) * si_Count);
+ m_Prio = false;
Reset();
}
@@ -143,8 +144,14 @@ bool cClientSocket::CheckConnection(void) {
if(Command("CAPS FILTERS", 220))
Filters = ",FILTERS";
- isyslog("Streamdev: Connected to server %s:%d using capabilities TSPIDS%s",
- RemoteIp().c_str(), RemotePort(), Filters);
+ const char *Prio = "";
+ if(Command("CAPS PRIO", 220)) {
+ Prio = ",PRIO";
+ m_Prio = true;
+ }
+
+ isyslog("Streamdev: Connected to server %s:%d using capabilities TSPIDS%s%s",
+ RemoteIp().c_str(), RemotePort(), Filters, Prio);
return true;
}
@@ -242,7 +249,7 @@ bool cClientSocket::SetChannelDevice(const cChannel *Channel) {
CMD_LOCK;
std::string command = (std::string)"TUNE "
- + (const char*)Channel->GetChannelID().ToString();
+ + (const char*)Channel->GetChannelID().ToString();
if (!Command(command, 220)) {
if (errno == 0)
esyslog("ERROR: Streamdev: Couldn't tune %s:%d to channel %s",
@@ -252,6 +259,21 @@ bool cClientSocket::SetChannelDevice(const cChannel *Channel) {
return true;
}
+bool cClientSocket::SetPriority(int Priority) {
+ if (!CheckConnection()) return false;
+
+ CMD_LOCK;
+
+ std::string command = (std::string)"PRIO " + (const char*)itoa(Priority);
+ if (!Command(command, 220)) {
+ if (errno == 0)
+ esyslog("Streamdev: Failed to update priority on %s:%d", RemoteIp().c_str(),
+ RemotePort());
+ return false;
+ }
+ return true;
+}
+
bool cClientSocket::SetPid(int Pid, bool On) {
if (!CheckConnection()) return false;
@@ -260,8 +282,8 @@ bool cClientSocket::SetPid(int Pid, bool On) {
std::string command = (std::string)(On ? "ADDP " : "DELP ") + (const char*)itoa(Pid);
if (!Command(command, 220)) {
if (errno == 0)
- esyslog("Streamdev: Pid %d not available from %s:%d", Pid, LocalIp().c_str(),
- LocalPort());
+ esyslog("Streamdev: Pid %d not available from %s:%d", Pid, RemoteIp().c_str(),
+ RemotePort());
return false;
}
return true;
@@ -277,7 +299,7 @@ bool cClientSocket::SetFilter(ushort Pid, uchar Tid, uchar Mask, bool On) {
if (!Command(command, 220)) {
if (errno == 0)
esyslog("Streamdev: Filter %hu, %hhu, %hhu not available from %s:%d",
- Pid, Tid, Mask, LocalIp().c_str(), LocalPort());
+ Pid, Tid, Mask, RemoteIp().c_str(), RemotePort());
return false;
}
return true;
diff --git a/client/socket.h b/client/socket.h
index 3b86304..1608bb4 100644
--- a/client/socket.h
+++ b/client/socket.h
@@ -1,5 +1,5 @@
/*
- * $Id: socket.h,v 1.6.2.1 2010/06/08 05:56:15 schmirl Exp $
+ * $Id: socket.h,v 1.6.2.2 2010/08/18 10:26:18 schmirl Exp $
*/
#ifndef VDR_STREAMDEV_CLIENT_CONNECTION_H
@@ -20,6 +20,7 @@ private:
cTBSocket *m_DataSockets[si_Count];
cMutex m_Mutex;
char m_Buffer[BUFSIZ + 1]; // various uses
+ bool m_Prio; // server supports command PRIO
protected:
/* Send Command, and return true if the command results in Expected.
@@ -45,6 +46,8 @@ public:
bool CreateDataConnection(eSocketId Id);
bool CloseDataConnection(eSocketId Id);
bool SetChannelDevice(const cChannel *Channel);
+ bool SupportsPrio() { return m_Prio; }
+ bool SetPriority(int Priority);
bool SetPid(int Pid, bool On);
bool SetFilter(ushort Pid, uchar Tid, uchar Mask, bool On);
bool CloseDvr(void);
diff --git a/i18n.h b/i18n.h
index c2eeceb..9d90f97 100644
--- a/i18n.h
+++ b/i18n.h
@@ -1,5 +1,5 @@
/*
- * $Id: i18n.h,v 1.1 2004/12/30 22:43:58 lordjaxom Exp $
+ * $Id: i18n.h,v 1.1.1.1 2004/12/30 22:43:58 lordjaxom Exp $
*/
#ifndef VDR_STREAMDEV_I18N_H
diff --git a/remux/extern.c b/remux/extern.c
index d66dc0a..cf766d3 100644
--- a/remux/extern.c
+++ b/remux/extern.c
@@ -191,6 +191,11 @@ cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connect
if (setpgid(0, 0) == -1)
esyslog("streamdev-server: externremux setpgid failed: %m");
+ if (access(opt_remux, X_OK) == -1) {
+ esyslog("streamdev-server %s: %m", opt_remux);
+ _exit(-1);
+ }
+
if (execle("/bin/sh", "sh", "-c", opt_remux, NULL, env) == -1) {
esyslog("streamdev-server: externremux script '%s' execution failed: %m", opt_remux);
_exit(-1);
diff --git a/server/connection.c b/server/connection.c
index 6bcbd1e..4096092 100644
--- a/server/connection.c
+++ b/server/connection.c
@@ -1,5 +1,5 @@
/*
- * $Id: connection.c,v 1.10.2.3 2010/07/19 13:50:14 schmirl Exp $
+ * $Id: connection.c,v 1.10.2.4 2010/08/03 10:56:58 schmirl Exp $
*/
#include "server/connection.h"
@@ -254,12 +254,16 @@ cDevice *cServerConnection::GetDevice(const cChannel *Channel, int Priority)
}
Dprintf(" * Found device for live tv: %p (%d)\n", newdev,
newdev ? newdev->CardIndex() + 1 : 0);
- if (newdev == NULL || newdev == device)
+ if (newdev == NULL || newdev == device) {
// no suitable device to continue live TV, giving up...
device = NULL;
+ dsyslog("streamdev: Not providing channel %s at priority %d - live TV not suspended", Channel->Name(), Priority);
+ }
else
newdev->SwitchChannel(current, true);
}
+ else if (!device)
+ dsyslog("streamdev: No device provides channel %s at priority %d", Channel->Name(), Priority);
}
return device;
diff --git a/server/connectionVTP.c b/server/connectionVTP.c
index 91d9265..b98f094 100644
--- a/server/connectionVTP.c
+++ b/server/connectionVTP.c
@@ -1,5 +1,5 @@
/*
- * $Id: connectionVTP.c,v 1.18.2.7 2010/07/19 13:50:14 schmirl Exp $
+ * $Id: connectionVTP.c,v 1.18.2.9 2010/08/18 10:26:19 schmirl Exp $
*/
#include "server/connectionVTP.h"
@@ -726,6 +726,8 @@ cConnectionVTP::cConnectionVTP(void):
m_StreamType(stTSPIDS),
m_FiltersSupport(false),
m_RecPlayer(NULL),
+ m_TuneChannel(NULL),
+ m_TunePriority(0),
m_LSTEHandler(NULL),
m_LSTCHandler(NULL),
m_LSTTHandler(NULL),
@@ -816,6 +818,7 @@ bool cConnectionVTP::Command(char *Cmd)
else if (strcasecmp(Cmd, "READ") == 0) return CmdREAD(param);
else if (strcasecmp(Cmd, "TUNE") == 0) return CmdTUNE(param);
else if (strcasecmp(Cmd, "PLAY") == 0) return CmdPLAY(param);
+ else if (strcasecmp(Cmd, "PRIO") == 0) return CmdPRIO(param);
else if (strcasecmp(Cmd, "ADDP") == 0) return CmdADDP(param);
else if (strcasecmp(Cmd, "DELP") == 0) return CmdDELP(param);
else if (strcasecmp(Cmd, "ADDF") == 0) return CmdADDF(param);
@@ -874,6 +877,11 @@ bool cConnectionVTP::CmdCAPS(char *Opts)
return Respond(220, "Capability \"%s\" accepted", Opts);
}
+ // Command PRIO is known
+ if (strcasecmp(Opts, "PRIO") == 0) {
+ return Respond(220, "Capability \"%s\" accepted", Opts);
+ }
+
return Respond(561, "Capability \"%s\" not known", Opts);
}
@@ -891,9 +899,15 @@ bool cConnectionVTP::CmdPROV(char *Opts)
if ((chan = ChannelFromString(Opts)) == NULL)
return Respond(550, "Undefined channel \"%s\"", Opts);
- return GetDevice(chan, prio) != NULL
- ? Respond(220, "Channel available")
- : Respond(560, "Channel not available");
+ if (GetDevice(chan, prio)) {
+ m_TuneChannel = chan;
+ m_TunePriority = prio;
+ return Respond(220, "Channel available");
+ }
+ else {
+ m_TuneChannel = NULL;
+ return Respond(560, "Channel not available");
+ }
}
bool cConnectionVTP::CmdPORT(char *Opts)
@@ -1047,18 +1061,23 @@ bool cConnectionVTP::CmdTUNE(char *Opts)
{
const cChannel *chan;
cDevice *dev;
+ int prio = m_TunePriority;
if ((chan = ChannelFromString(Opts)) == NULL)
return Respond(550, "Undefined channel \"%s\"", Opts);
- if ((dev = GetDevice(chan, 0)) == NULL)
+ if (chan != m_TuneChannel) {
+ esyslog("streamdev-server TUNE %s: Priority unknown - using 0", Opts);
+ prio = 0;
+ }
+ if ((dev = GetDevice(chan, prio)) == NULL)
return Respond(560, "Channel not available");
if (!dev->SwitchChannel(chan, false))
return Respond(560, "Channel not available");
delete m_LiveStreamer;
- m_LiveStreamer = new cStreamdevLiveStreamer(1, this);
+ m_LiveStreamer = new cStreamdevLiveStreamer(prio, this);
m_LiveStreamer->SetChannel(chan, m_StreamType);
m_LiveStreamer->SetDevice(dev);
if(m_LiveSocket)
@@ -1099,6 +1118,22 @@ bool cConnectionVTP::CmdPLAY(char *Opts)
}
}
+bool cConnectionVTP::CmdPRIO(char *Opts)
+{
+ int prio;
+ char *end;
+
+ prio = strtoul(Opts, &end, 10);
+ if (end == Opts || (*end != '\0' && *end != ' '))
+ return Respond(500, "Use: PRIO Priority");
+
+ if (m_LiveStreamer) {
+ m_LiveStreamer->SetPriority(prio);
+ return Respond(220, "Priority changed to %d", prio);
+ }
+ return Respond(550, "Priority not applicable");
+}
+
bool cConnectionVTP::CmdADDP(char *Opts)
{
int pid;
@@ -1223,6 +1258,7 @@ bool cConnectionVTP::CmdSUSP(void)
else if (StreamdevServerSetup.SuspendMode == smOffer
&& StreamdevServerSetup.AllowSuspend) {
cControl::Launch(new cSuspendCtl);
+ cControl::Attach();
return Respond(220, "Server is suspended");
} else
return Respond(550, "Client may not suspend server");
diff --git a/server/connectionVTP.h b/server/connectionVTP.h
index b938fe6..ee842fe 100644
--- a/server/connectionVTP.h
+++ b/server/connectionVTP.h
@@ -31,6 +31,11 @@ private:
bool m_FiltersSupport;
RecPlayer *m_RecPlayer;
+ // Priority is only known in PROV command
+ // Store in here for later use in TUNE call
+ const cChannel *m_TuneChannel;
+ int m_TunePriority;
+
// Members adopted for SVDRP
cLSTEHandler *m_LSTEHandler;
cLSTCHandler *m_LSTCHandler;
@@ -59,6 +64,7 @@ public:
bool CmdREAD(char *Opts);
bool CmdTUNE(char *Opts);
bool CmdPLAY(char *Opts);
+ bool CmdPRIO(char *Opts);
bool CmdADDP(char *Opts);
bool CmdDELP(char *Opts);
bool CmdADDF(char *Opts);
diff --git a/server/livestreamer.c b/server/livestreamer.c
index ff4f708..7ec7d9e 100644
--- a/server/livestreamer.c
+++ b/server/livestreamer.c
@@ -297,7 +297,7 @@ void cStreamdevPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, i
if (written != TS_SIZE)
siBuffer.ReportOverflow(TS_SIZE - written);
if (pmtPid != prevPmtPid) {
- m_Streamer->SetPids(pmtPid);
+ m_Streamer->SetPid(pmtPid, true);
Add(pmtPid, 0x02);
pmtVersion = -1;
}
@@ -442,21 +442,28 @@ bool cStreamdevLiveStreamer::SetPids(int Pid, const int *Pids1, const int *Pids2
return true;
}
+void cStreamdevLiveStreamer::SetPriority(int Priority)
+{
+ m_Priority = Priority;
+ StartReceiver();
+}
+
void cStreamdevLiveStreamer::StartReceiver(void)
{
- DELETENULL(m_Receiver);
- if (m_NumPids > 0) {
+ if (m_Device != NULL && m_NumPids > 0 && IsRunning()) {
Dprintf("Creating Receiver to respect changed pids\n");
+ cReceiver *current = m_Receiver;
#if VDRVERSNUM < 10500
m_Receiver = new cStreamdevLiveReceiver(this, m_Channel->Ca(), m_Priority, m_Pids);
#else
m_Receiver = new cStreamdevLiveReceiver(this, m_Channel->GetChannelID(), m_Priority, m_Pids);
#endif
- if (IsRunning() && m_Device != NULL) {
- Dprintf("Attaching new receiver\n");
- Attach();
- }
+ cThreadLock ThreadLock(m_Device);
+ Attach();
+ delete current;
}
+ else
+ DELETENULL(m_Receiver);
}
bool cStreamdevLiveStreamer::SetChannel(const cChannel *Channel, eStreamType StreamType, const int* Apid, const int *Dpid)
diff --git a/server/livestreamer.h b/server/livestreamer.h
index 283a395..71feb4c 100644
--- a/server/livestreamer.h
+++ b/server/livestreamer.h
@@ -38,6 +38,7 @@ public:
bool SetPid(int Pid, bool On);
bool SetPids(int Pid, const int *Pids1 = NULL, const int *Pids2 = NULL, const int *Pids3 = NULL);
bool SetChannel(const cChannel *Channel, eStreamType StreamType, const int* Apid = NULL, const int* Dpid = NULL);
+ void SetPriority(int Priority);
virtual int Put(const uchar *Data, int Count);
virtual uchar *Get(int &Count);
diff --git a/server/menuHTTP.c b/server/menuHTTP.c
index 47df3a8..1d30aa1 100644
--- a/server/menuHTTP.c
+++ b/server/menuHTTP.c
@@ -2,7 +2,7 @@
#include "server/menuHTTP.h"
//**************************** cChannelIterator **************
-cChannelIterator::cChannelIterator(cChannel *First): channel(First)
+cChannelIterator::cChannelIterator(const cChannel *First): channel(First)
{}
const cChannel* cChannelIterator::Next()
@@ -19,7 +19,7 @@ cListAll::cListAll(): cChannelIterator(Channels.First())
const cChannel* cListAll::NextChannel(const cChannel *Channel)
{
if (Channel)
- Channel = Channels.Next(Channel);
+ Channel = SkipFakeGroups(Channels.Next(Channel));
return Channel;
}
@@ -46,14 +46,19 @@ const cChannel* cListGroups::NextChannel(const cChannel *Channel)
}
//
// ********************* cListGroup ****************
-cListGroup::cListGroup(const cChannel *Group): cChannelIterator((Group && Group->GroupSep() && Channels.Next(Group) && !Channels.Next(Group)->GroupSep()) ? Channels.Next(Group) : NULL)
+cListGroup::cListGroup(const cChannel *Group): cChannelIterator(GetNextChannelInGroup(Group))
{}
-const cChannel* cListGroup::NextChannel(const cChannel *Channel)
+const cChannel* cListGroup::GetNextChannelInGroup(const cChannel *Channel)
{
if (Channel)
- Channel = Channels.Next(Channel);
- return (Channel && !Channel->GroupSep()) ? Channel : NULL;
+ Channel = SkipFakeGroups(Channels.Next(Channel));
+ return Channel && !Channel->GroupSep() ? Channel : NULL;
+}
+
+const cChannel* cListGroup::NextChannel(const cChannel *Channel)
+{
+ return GetNextChannelInGroup(Channel);
}
//
// ********************* cListTree ****************
@@ -68,7 +73,7 @@ const cChannel* cListTree::NextChannel(const cChannel *Channel)
if (currentGroup == selectedGroup)
{
if (Channel)
- Channel = Channels.Next(Channel);
+ Channel = SkipFakeGroups(Channels.Next(Channel));
if (Channel && Channel->GroupSep())
currentGroup = Channel;
}
diff --git a/server/menuHTTP.h b/server/menuHTTP.h
index ecded86..d0f4211 100644
--- a/server/menuHTTP.h
+++ b/server/menuHTTP.h
@@ -13,9 +13,10 @@ class cChannelIterator
const cChannel *channel;
protected:
virtual const cChannel* NextChannel(const cChannel *Channel) = 0;
+ static inline const cChannel* SkipFakeGroups(const cChannel *Channel);
public:
const cChannel* Next();
- cChannelIterator(cChannel *First);
+ cChannelIterator(const cChannel *First);
virtual ~cChannelIterator() {};
};
@@ -48,6 +49,8 @@ class cListGroups: public cChannelIterator
class cListGroup: public cChannelIterator
{
+ private:
+ static const cChannel* GetNextChannelInGroup(const cChannel *Channel);
protected:
virtual const cChannel* NextChannel(const cChannel *Channel);
public:
@@ -155,4 +158,11 @@ class cM3uChannelList: public cChannelList
virtual ~cM3uChannelList();
};
+inline const cChannel* cChannelIterator::SkipFakeGroups(const cChannel* Group)
+{
+ while (Group && Group->GroupSep() && !*Group->Name())
+ Group = Channels.Next(Group);
+ return Group;
+}
+
#endif
diff --git a/streamdev-client.c b/streamdev-client.c
index 87afdf3..c720186 100644
--- a/streamdev-client.c
+++ b/streamdev-client.c
@@ -3,7 +3,7 @@
*
* See the README file for copyright information and how to reach the author.
*
- * $Id: streamdev-client.c,v 1.5.2.1 2010/06/08 05:56:14 schmirl Exp $
+ * $Id: streamdev-client.c,v 1.5.2.2 2010/08/18 10:26:18 schmirl Exp $
*/
#include "streamdev-client.h"
@@ -56,4 +56,8 @@ bool cPluginStreamdevClient::SetupParse(const char *Name, const char *Value) {
return StreamdevClientSetup.SetupParse(Name, Value);
}
+void cPluginStreamdevClient::MainThreadHook(void) {
+ cStreamdevDevice::UpdatePriority();
+}
+
VDRPLUGINCREATOR(cPluginStreamdevClient); // Don't touch this!
diff --git a/streamdev-client.h b/streamdev-client.h
index a95dbbf..fe1c9fb 100644
--- a/streamdev-client.h
+++ b/streamdev-client.h
@@ -1,5 +1,5 @@
/*
- * $Id: streamdev-client.h,v 1.1.1.1.2.1 2010/06/08 05:56:14 schmirl Exp $
+ * $Id: streamdev-client.h,v 1.1.1.1.2.2 2010/08/18 10:26:18 schmirl Exp $
*/
#ifndef VDR_STREAMDEVCLIENT_H
@@ -23,6 +23,7 @@ public:
virtual cOsdObject *MainMenuAction(void);
virtual cMenuSetupPage *SetupMenu(void);
virtual bool SetupParse(const char *Name, const char *Value);
+ virtual void MainThreadHook(void);
};
#endif // VDR_STREAMDEVCLIENT_H
diff --git a/streamdev/externremux.sh b/streamdev/externremux.sh
index f64777a..27cca5d 100755
--- a/streamdev/externremux.sh
+++ b/streamdev/externremux.sh
@@ -16,6 +16,8 @@
# VBR video bitrate (kbit)
# VOPTS custom video options
# WIDTH scale video to width
+# HEIGHT scale video to height
+# FPS output frames per second
# AC audio codec
# ABR audio bitrate (kbit)
# AOPTS custom audio options
@@ -42,14 +44,22 @@ ABR_MONO=64
###
# mencoder binary
MENCODER=mencoder
+### video part
# Default video codec (e.g. lavc/x264/copy)
MENCODER_VC=lavc
+# Default video options if lavc is used (-ovc lavc -lavcopts ...)
+MENCODER_LAVC_VOPTS=vcodec=mpeg4
+# Default video options if x264 is used (-ovc x264 -x264encopts ...)
+MENCODER_X264_VOPTS=threads=auto
+### audio part
# Default audio codec (e.g. lavc/mp3lame/faac/copy)
MENCODER_AC=mp3lame
-# Default video codec if lavc is used (-ovc lavc -lavcopts vcodec=)
-MENCODER_LAVC_VC=mpeg4
-# Default audio codec if lavc is used (-oac lavc -lavcopts acodec=)
-MENCODER_LAVC_AC=mp2
+# Default audio options if lavc is used (-oac lavc -lavcopts ...)
+MENCODER_LAVC_AOPTS=acodec=mp2
+# Default audio options if mp3lame is used (-oac mp3lame -lameopts ...)
+MENCODER_LAME_AOPTS=
+# Default audio options if faac is used (-oac faac -faacopts ...)
+MENCODER_FAAC_AOPTS=
###
### MENCODER CONFIG END
@@ -63,6 +73,8 @@ OGG_SPEED=1
OGG_VQUALITY=0
# audioquality - higher value gives better quality but is slower (0..10)
OGG_AQUALITY=0
+# aspect ratio used for scaling if only one of HEIGHT/WIDTH given (16/9 or 4/3)
+OGG_ASPECT='4 / 3'
###
### OGG CONFIG END
@@ -70,7 +82,19 @@ OGG_AQUALITY=0
function hasOpt { echo "$1" | grep -q "\b${2}\b"; }
-function isNumeric() { echo "$@" | grep -q '^[0-9]\{1,\}$'; }
+# $1: concatenation of already set option=value pairs
+# $2-$n: option=value pairs to be echod if the option is not present in $1
+function addOpts
+{
+ local opts="$1"
+ shift
+ while [ $# -gt 0 ]; do
+ hasOpt "$opts" ${1%%=*}= || echo $1
+ shift
+ done
+}
+
+function isNumeric() { echo "$@" | grep -q '^-\?[0-9]\{1,\}$'; }
function remux_cat
{
@@ -87,20 +111,31 @@ function remux_mencoder
# Assemble video options
VC=${REMUX_PARAM_VC:-$MENCODER_VC}
VOPTS=${REMUX_PARAM_VOPTS}
- WIDTH=${REMUX_PARAM_WIDTH:-$WIDTH}
+ FPS=${REMUX_PARAM_FPS:-$FPS}
+
+ # if only one of HEIGHT/WIDTH given:
+ # have mencoder calculate other value depending on actual aspect ratio
+ if [ "$HEIGHT" -a -z "$WIDTH" ]; then
+ WIDTH=-3
+ elif [ "$WIDTH" -a -z "$HEIGHT" ]; then
+ HEIGHT=-3
+ fi
+
case "$VC" in
lavc)
LAVCOPTS=(
${VOPTS}
- $(hasOpt "$VOPTS" vcodec || echo "vcodec=$MENCODER_LAVC_VC")
+ $(IFS=$IFS:; addOpts "$VOPTS" $MENCODER_LAVC_VOPTS)
${VBR:+vbitrate=$VBR}
)
[ ${#LAVCOPTS[*]} -gt 0 ] && VOPTS=$(IFS=:; echo -lavcopts "${LAVCOPTS[*]}")
;;
x264)
+ isNumeric "$HEIGHT" && [ $HEIGHT -lt 0 -a $HEIGHT -gt -8 ] && ((HEIGHT-=8))
+ isNumeric "$WIDTH" && [ $WIDTH -lt 0 -a $WIDTH -gt -8 ] && ((WIDTH-=8))
X264OPTS=(
${VOPTS}
- $(hasOpt "$VOPTS" threads || echo "threads=auto")
+ $(IFS=$IFS:; addOpts "$VOPTS" $MENCODER_X264_VOPTS)
${VBR:+bitrate=$VBR}
)
[ ${#X264OPTS[*]} -gt 0 ] && VOPTS=$(IFS=:; echo -x264encopts "${X264OPTS[*]}")
@@ -121,7 +156,7 @@ function remux_mencoder
LAVCOPTS=(
${LAVCOPTS[*]}
${AOPTS}
- $(hasOpt "$AOPTS" acodec || echo "acodec=$MENCODER_LAVC_AC")
+ $(IFS=$IFS:; addOpts "$AOPTS" $MENCODER_LAVC_AOPTS)
${ABR:+abitrate=$ABR}
)
@@ -133,6 +168,7 @@ function remux_mencoder
LAMEOPTS=(
${AOPTS}
$(isNumeric "${ABR}" && [ "${ABR}" -lt "$ABR_MONO" ] && ! hasOpt "${AOPTS}" mode ] && echo 'mode=3')
+ $(IFS=$IFS:; addOpts "$AOPTS" $MENCODER_LAME_AOPTS)
${ABR:+preset=$ABR}
)
[ ${#LAMEOPTS[*]} -gt 0 ] && AOPTS=$(IFS=:; echo -lameopts "${LAMEOPTS[*]}")
@@ -140,6 +176,7 @@ function remux_mencoder
faac)
FAACOPTS=(
${AOPTS}
+ $(IFS=$IFS:; addOpts "$AOPTS" $MENCODER_FAAC_AOPTS)
${ABR:+br=$ABR}
)
[ ${#FAACOPTS[*]} -gt 0 ] && AOPTS=$(IFS=:; echo -faacopts "${FAACOPTS[*]}")
@@ -155,15 +192,17 @@ function remux_mencoder
startReply
exec 3<&0
- echo "$MENCODER" \
+ echo $MENCODER \
-ovc $VC $VOPTS \
-oac $AC $AOPTS \
- ${WIDTH:+-vf scale -zoom -xy $WIDTH} \
+ ${WIDTH:+-vf scale=$WIDTH:$HEIGHT -zoom} \
+ ${FPS:+-ofps $FPS} \
-o "$FIFO" -- - >&2
- "$MENCODER" \
+ $MENCODER \
-ovc $VC $VOPTS \
-oac $AC $AOPTS \
- ${WIDTH:+-vf scale -zoom -xy $WIDTH} \
+ ${WIDTH:+-vf scale=$WIDTH:$HEIGHT -zoom} \
+ ${FPS:+-ofps $FPS} \
-o "$FIFO" -- - 0<&3 >/dev/null &
}
@@ -171,7 +210,15 @@ function remux_ogg
{
VOPTS=${REMUX_PARAM_VOPTS//[:=]/ }
AOPTS=${REMUX_PARAM_AOPTS//[:=]/ }
- WIDTH=${REMUX_PARAM_WIDTH:-$WIDTH}
+
+ # if only one of HEIGHT/WIDTH given:
+ # calculate other value depending on configured aspect ratio
+ # trim to multiple of 8
+ if [ "$HEIGHT" -a -z "$WIDTH" ]; then
+ WIDTH=$((HEIGHT * $OGG_ASPECT / 8 * 8))
+ elif [ "$WIDTH" -a -z "$HEIGHT" ]; then
+ HEIGHT=$(($WIDTH * $( echo $OGG_ASPECT | sed 's#^\([0-9]\+\) */ *\([0-9]\+\)$#\2 / \1#') / 8 * 8))
+ fi
OGGOPTS=(
${VOPTS}
@@ -187,14 +234,14 @@ function remux_ogg
startReply
exec 3<&0
- echo "$OGG" --format ts \
+ echo $OGG --format ts \
${OGGOPTS[*]} \
- ${WIDTH:+--width $WIDTH --height $(($WIDTH * 3 / 4 / 8 * 8))} \
+ ${WIDTH:+--width $WIDTH --height $HEIGHT} \
--title "VDR Streamdev: ${REMUX_CHANNEL_NAME}" \
--output "$FIFO" -- - 0<&3 >&2
- "$OGG" --format ts \
+ $OGG --format ts \
${OGGOPTS[*]} \
- ${WIDTH:+--width $WIDTH --height $(($WIDTH * 3 / 4 / 8 * 8))} \
+ ${WIDTH:+--width $WIDTH --height $HEIGHT} \
--title "VDR Streamdev: ${REMUX_CHANNEL_NAME}" \
--output "$FIFO" -- - 0<&3 >/dev/null &
}
@@ -254,6 +301,7 @@ esac
ABR=${REMUX_PARAM_ABR:-$ABR}
VBR=${REMUX_PARAM_VBR:-$VBR}
WIDTH=${REMUX_PARAM_WIDTH:-$WIDTH}
+HEIGHT=${REMUX_PARAM_HEIGHT:-$HEIGHT}
PROG=${REMUX_PARAM_PROG:-$PROG}
case "$PROG" in