Snapshot 2010-09-15

This commit is contained in:
Frank Schmirler 2010-12-02 09:57:17 +01:00
parent e0a00f90ae
commit 2ec54f7505
24 changed files with 495 additions and 144 deletions

View File

@ -38,6 +38,7 @@ Rolf Ahrenberg
for suggesting to include the charset in HTTP replies for suggesting to include the charset in HTTP replies
for requesting replacement of asprintf calls for requesting replacement of asprintf calls
for suggesting to change the URL path from EXTERN to EXT for suggesting to change the URL path from EXTERN to EXT
for suggesting increased thread priorities for cStreamdevWriter/Streamer
Rantanen Teemu Rantanen Teemu
for providing vdr-incompletesections.diff for providing vdr-incompletesections.diff
@ -153,7 +154,20 @@ carel
for helping to find a way to cleanly shutdown externremux with mencoder for helping to find a way to cleanly shutdown externremux with mencoder
wolfi.m wolfi.m
for reporting a typo in externermux quality parameter value for reporting a typo in externremux quality parameter value
Norman Thiel Norman Thiel
for reporting a wrong URL path in m3u playlists for reporting a wrong URL path in m3u playlists
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
Rainer Blickle
for reporting that channel switches may interrupt live TV on the server

38
HISTORY
View File

@ -1,6 +1,44 @@
VDR Plugin 'streamdev' Revision History 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
- fixed regression of the GetDevice(...) change. Filter streaming to clients
with a recent VDR version no longer worked.
- log an error if externremux.sh is missing or not executable
- since VDR 1.5.0 cDevice::GetDevice(...) is no longer a query only method.
It detaches all receivers of the device it returns. So it is no longer
suitable for testing the availability of a device. Added a copy of VDR's
cDevice::GetDevice(...) without the detach receivers part as a workaround
until a better solution is available
- added dsyslog messages to help troubleshouting channel switch issues
- VTP command SUSP didn't attach the player to the primary device
- fixed incompatibilities with older make versions
- replacing a connections receiver is now an atomic operation. Solves
stuttering audio/video due to lost TS packets when adding/removing PIDs
- disabled attribute warn_unused_result in libdvbmpeg
- slightly increased thread priorities of cStreamdevWriter/Streamer
(suggested by Rolf Ahrenberg)
- 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 the
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)
2010-07-20: Version 0.5.0b 2010-07-20: Version 0.5.0b
- fixed wrong URL path in m3u playlists (reported by Norman Thiel) - fixed wrong URL path in m3u playlists (reported by Norman Thiel)

View File

@ -1,7 +1,7 @@
# #
# Makefile for a Video Disk Recorder plugin # Makefile for a Video Disk Recorder plugin
# #
# $Id: Makefile,v 1.22 2010/07/19 13:49:24 schmirl Exp $ # $Id: Makefile,v 1.23 2010/08/02 10:36:59 schmirl Exp $
# The main source file name. # The main source file name.
# #
@ -35,7 +35,8 @@ TSPLAYVERSNUM = $(shell grep 'define TSPLAY_PATCH_VERSION ' $(VDRDIR)/device.h |
ifeq ($(shell test $(APIVERSNUM) -ge 10713; echo $$?),0) ifeq ($(shell test $(APIVERSNUM) -ge 10713; echo $$?),0)
include $(VDRDIR)/Make.global include $(VDRDIR)/Make.global
else ifeq ($(shell test $(APIVERSNUM) -ge 10704 -o -n "$(TSPLAYVERSNUM)" ; echo $$?),0) else
ifeq ($(shell test $(APIVERSNUM) -ge 10704 -o -n "$(TSPLAYVERSNUM)" ; echo $$?),0)
DEFINES += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE DEFINES += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE
CFLAGS += -fPIC CFLAGS += -fPIC
CXXFLAGS += -fPIC CXXFLAGS += -fPIC
@ -43,13 +44,14 @@ else
CFLAGS += -fPIC CFLAGS += -fPIC
CXXFLAGS += -fPIC CXXFLAGS += -fPIC
endif endif
endif
-include $(VDRDIR)/Make.config -include $(VDRDIR)/Make.config
### export all vars for sub-makes, using absolute paths ### export all vars for sub-makes, using absolute paths
VDRDIR := $(abspath $(VDRDIR)) VDRDIR := $(shell cd $(VDRDIR) >/dev/null 2>&1 && pwd)
LIBDIR := $(abspath $(LIBDIR)) LIBDIR := $(shell cd $(LIBDIR) >/dev/null 2>&1 && pwd)
export export
unexport PLUGIN unexport PLUGIN

View File

@ -1,5 +1,5 @@
/* /*
* $Id: device.c,v 1.26 2010/06/08 05:55:17 schmirl Exp $ * $Id: device.c,v 1.27 2010/08/18 10:26:55 schmirl Exp $
*/ */
#include "client/device.h" #include "client/device.h"
@ -8,6 +8,7 @@
#include "tools/select.h" #include "tools/select.h"
#include <vdr/config.h>
#include <vdr/channels.h> #include <vdr/channels.h>
#include <vdr/ringbuffer.h> #include <vdr/ringbuffer.h>
#include <vdr/eit.h> #include <vdr/eit.h>
@ -32,6 +33,7 @@ cStreamdevDevice::cStreamdevDevice(void) {
m_Device = this; m_Device = this;
m_Pids = 0; m_Pids = 0;
m_Priority = -1;
m_DvrClosed = true; m_DvrClosed = true;
} }
@ -118,6 +120,9 @@ bool cStreamdevDevice::SetChannelDevice(const cChannel *Channel,
bool LiveView) { bool LiveView) {
Dprintf("SetChannelDevice Channel: %s, LiveView: %s\n", Channel->Name(), Dprintf("SetChannelDevice Channel: %s, LiveView: %s\n", Channel->Name(),
LiveView ? "true" : "false"); LiveView ? "true" : "false");
LOCK_THREAD;
m_UpdatePriority = ClientSocket.SupportsPrio();
if (LiveView) if (LiveView)
return false; return false;
@ -140,6 +145,8 @@ bool cStreamdevDevice::SetPid(cPidHandle *Handle, int Type, bool On) {
Handle->used); Handle->used);
LOCK_THREAD; LOCK_THREAD;
m_UpdatePriority = ClientSocket.SupportsPrio();
if (On && !m_TSBuffer) { if (On && !m_TSBuffer) {
Dprintf("SetPid: no data connection -> OpenDvr()"); Dprintf("SetPid: no data connection -> OpenDvr()");
OpenDvrInt(); OpenDvrInt();
@ -301,3 +308,16 @@ bool cStreamdevDevice::ReInit(void) {
return StreamdevClientSetup.StartClient ? Init() : true; 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();
}
}

View File

@ -1,5 +1,5 @@
/* /*
* $Id: device.h,v 1.9 2009/06/23 10:26:54 schmirl Exp $ * $Id: device.h,v 1.10 2010/08/18 10:26:55 schmirl Exp $
*/ */
#ifndef VDR_STREAMDEV_DEVICE_H #ifndef VDR_STREAMDEV_DEVICE_H
@ -15,13 +15,14 @@ class cTBString;
#define CMD_LOCK_OBJ(x) cMutexLock CmdLock((cMutex*)&(x)->m_Mutex) #define CMD_LOCK_OBJ(x) cMutexLock CmdLock((cMutex*)&(x)->m_Mutex)
class cStreamdevDevice: public cDevice { class cStreamdevDevice: public cDevice {
friend class cRemoteRecordings;
private: private:
const cChannel *m_Channel; const cChannel *m_Channel;
cTSBuffer *m_TSBuffer; cTSBuffer *m_TSBuffer;
cStreamdevFilters *m_Filters; cStreamdevFilters *m_Filters;
int m_Pids; int m_Pids;
int m_Priority;
bool m_UpdatePriority;
bool m_DvrClosed; bool m_DvrClosed;
static cStreamdevDevice *m_Device; static cStreamdevDevice *m_Device;
@ -59,6 +60,7 @@ public:
#endif #endif
virtual bool IsTunedToTransponder(const cChannel *Channel); virtual bool IsTunedToTransponder(const cChannel *Channel);
static void UpdatePriority(void);
static bool Init(void); static bool Init(void);
static bool ReInit(void); static bool ReInit(void);

View File

@ -1,5 +1,5 @@
/* /*
* $Id: socket.c,v 1.13 2010/06/08 05:55:17 schmirl Exp $ * $Id: socket.c,v 1.15 2010/08/18 10:26:55 schmirl Exp $
*/ */
#include <tools/select.h> #include <tools/select.h>
@ -20,6 +20,7 @@ cClientSocket ClientSocket;
cClientSocket::cClientSocket(void) cClientSocket::cClientSocket(void)
{ {
memset(m_DataSockets, 0, sizeof(cTBSocket*) * si_Count); memset(m_DataSockets, 0, sizeof(cTBSocket*) * si_Count);
m_Prio = false;
Reset(); Reset();
} }
@ -142,8 +143,14 @@ bool cClientSocket::CheckConnection(void) {
if(Command("CAPS FILTERS", 220)) if(Command("CAPS FILTERS", 220))
Filters = ",FILTERS"; Filters = ",FILTERS";
isyslog("Streamdev: Connected to server %s:%d using capabilities TSPIDS%s", const char *Prio = "";
RemoteIp().c_str(), RemotePort(), Filters); 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; return true;
} }
@ -241,7 +248,7 @@ bool cClientSocket::SetChannelDevice(const cChannel *Channel) {
CMD_LOCK; CMD_LOCK;
std::string command = (std::string)"TUNE " std::string command = (std::string)"TUNE "
+ (const char*)Channel->GetChannelID().ToString(); + (const char*)Channel->GetChannelID().ToString();
if (!Command(command, 220)) { if (!Command(command, 220)) {
if (errno == 0) if (errno == 0)
esyslog("ERROR: Streamdev: Couldn't tune %s:%d to channel %s", esyslog("ERROR: Streamdev: Couldn't tune %s:%d to channel %s",
@ -251,6 +258,21 @@ bool cClientSocket::SetChannelDevice(const cChannel *Channel) {
return true; 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) { bool cClientSocket::SetPid(int Pid, bool On) {
if (!CheckConnection()) return false; if (!CheckConnection()) return false;
@ -259,8 +281,8 @@ bool cClientSocket::SetPid(int Pid, bool On) {
std::string command = (std::string)(On ? "ADDP " : "DELP ") + (const char*)itoa(Pid); std::string command = (std::string)(On ? "ADDP " : "DELP ") + (const char*)itoa(Pid);
if (!Command(command, 220)) { if (!Command(command, 220)) {
if (errno == 0) if (errno == 0)
esyslog("Streamdev: Pid %d not available from %s:%d", Pid, LocalIp().c_str(), esyslog("Streamdev: Pid %d not available from %s:%d", Pid, RemoteIp().c_str(),
LocalPort()); RemotePort());
return false; return false;
} }
return true; return true;
@ -276,7 +298,7 @@ bool cClientSocket::SetFilter(ushort Pid, uchar Tid, uchar Mask, bool On) {
if (!Command(command, 220)) { if (!Command(command, 220)) {
if (errno == 0) if (errno == 0)
esyslog("Streamdev: Filter %hu, %hhu, %hhu not available from %s:%d", 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 false;
} }
return true; return true;

View File

@ -1,5 +1,5 @@
/* /*
* $Id: socket.h,v 1.7 2010/06/08 05:55:17 schmirl Exp $ * $Id: socket.h,v 1.8 2010/08/18 10:26:55 schmirl Exp $
*/ */
#ifndef VDR_STREAMDEV_CLIENT_CONNECTION_H #ifndef VDR_STREAMDEV_CLIENT_CONNECTION_H
@ -20,6 +20,7 @@ private:
cTBSocket *m_DataSockets[si_Count]; cTBSocket *m_DataSockets[si_Count];
cMutex m_Mutex; cMutex m_Mutex;
char m_Buffer[BUFSIZ + 1]; // various uses char m_Buffer[BUFSIZ + 1]; // various uses
bool m_Prio; // server supports command PRIO
protected: protected:
/* Send Command, and return true if the command results in Expected. /* Send Command, and return true if the command results in Expected.
@ -45,6 +46,8 @@ public:
bool CreateDataConnection(eSocketId Id); bool CreateDataConnection(eSocketId Id);
bool CloseDataConnection(eSocketId Id); bool CloseDataConnection(eSocketId Id);
bool SetChannelDevice(const cChannel *Channel); bool SetChannelDevice(const cChannel *Channel);
bool SupportsPrio() { return m_Prio; }
bool SetPriority(int Priority);
bool SetPid(int Pid, bool On); bool SetPid(int Pid, bool On);
bool SetFilter(ushort Pid, uchar Tid, uchar Mask, bool On); bool SetFilter(ushort Pid, uchar Tid, uchar Mask, bool On);
bool CloseDvr(void); bool CloseDvr(void);

View File

@ -3,7 +3,7 @@
* *
* See the README file for copyright information and how to reach the author. * See the README file for copyright information and how to reach the author.
* *
* $Id: streamdev-client.c,v 1.2 2010/07/19 13:49:25 schmirl Exp $ * $Id: streamdev-client.c,v 1.3 2010/08/18 10:26:56 schmirl Exp $
*/ */
#include "streamdev-client.h" #include "streamdev-client.h"
@ -52,4 +52,8 @@ bool cPluginStreamdevClient::SetupParse(const char *Name, const char *Value) {
return StreamdevClientSetup.SetupParse(Name, Value); return StreamdevClientSetup.SetupParse(Name, Value);
} }
void cPluginStreamdevClient::MainThreadHook(void) {
cStreamdevDevice::UpdatePriority();
}
VDRPLUGINCREATOR(cPluginStreamdevClient); // Don't touch this! VDRPLUGINCREATOR(cPluginStreamdevClient); // Don't touch this!

View File

@ -1,5 +1,5 @@
/* /*
* $Id: streamdev-client.h,v 1.2 2010/07/19 13:49:25 schmirl Exp $ * $Id: streamdev-client.h,v 1.3 2010/08/18 10:26:56 schmirl Exp $
*/ */
#ifndef VDR_STREAMDEVCLIENT_H #ifndef VDR_STREAMDEVCLIENT_H
@ -23,6 +23,7 @@ public:
virtual cOsdObject *MainMenuAction(void); virtual cOsdObject *MainMenuAction(void);
virtual cMenuSetupPage *SetupMenu(void); virtual cMenuSetupPage *SetupMenu(void);
virtual bool SetupParse(const char *Name, const char *Value); virtual bool SetupParse(const char *Name, const char *Value);
virtual void MainThreadHook(void);
}; };
#endif // VDR_STREAMDEVCLIENT_H #endif // VDR_STREAMDEVCLIENT_H

View File

@ -10,7 +10,7 @@
using namespace std; using namespace std;
const char *VERSION = "0.5.0"; const char *VERSION = "0.5.0-CVS";
const char cMenuEditIpItem::IpCharacters[] = "0123456789."; const char cMenuEditIpItem::IpCharacters[] = "0123456789.";

View File

@ -1,12 +1,16 @@
# #
# Makefile for a Video Disk Recorder plugin # Makefile for a Video Disk Recorder plugin
# #
# $Id: Makefile,v 1.4 2010/07/19 13:49:26 schmirl Exp $ # $Id: Makefile,v 1.5 2010/07/30 10:49:28 schmirl Exp $
### The object files (add further files here): ### The object files (add further files here):
OBJS = ctools.o remux.o ringbuffy.o transform.o OBJS = ctools.o remux.o ringbuffy.o transform.o
### Disable attribute warn_unused_result
DEFINES += -U_FORTIFY_SOURCE
### The main target: ### The main target:
.PHONY: clean .PHONY: clean

View File

@ -151,7 +151,10 @@ cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connect
// look for section parameters: /path;param1=value1;param2=value2/ // look for section parameters: /path;param1=value1;param2=value2/
std::string::size_type begin, end; std::string::size_type begin, end;
std::string path = Connection->Headers().at("PATH_INFO"); const static std::string PATH_INFO("PATH_INFO");
tStrStrMap::const_iterator it_pathinfo = Connection->Headers().find(PATH_INFO);
const std::string& path = it_pathinfo == Connection->Headers().end() ? "/" : it_pathinfo->second;
begin = path.find(';', 0); begin = path.find(';', 0);
begin = path.find_first_not_of(';', begin); begin = path.find_first_not_of(';', begin);
end = path.find_first_of(";/", begin); end = path.find_first_of(";/", begin);
@ -188,6 +191,11 @@ cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connect
if (setpgid(0, 0) == -1) if (setpgid(0, 0) == -1)
esyslog("streamdev-server: externremux setpgid failed: %m"); 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) { if (execle("/bin/sh", "sh", "-c", opt_remux, NULL, env) == -1) {
esyslog("streamdev-server: externremux script '%s' execution failed: %m", opt_remux); esyslog("streamdev-server: externremux script '%s' execution failed: %m", opt_remux);
_exit(-1); _exit(-1);

View File

@ -1,5 +1,5 @@
/* /*
* $Id: connection.c,v 1.14 2010/07/19 13:49:31 schmirl Exp $ * $Id: connection.c,v 1.16 2010/08/03 10:51:53 schmirl Exp $
*/ */
#include "server/connection.h" #include "server/connection.h"
@ -8,6 +8,7 @@
#include "common.h" #include "common.h"
#include <vdr/tools.h> #include <vdr/tools.h>
#include <vdr/transfer.h>
#include <string.h> #include <string.h>
#include <stdarg.h> #include <stdarg.h>
#include <errno.h> #include <errno.h>
@ -185,74 +186,171 @@ bool cServerConnection::Respond(const char *Message, bool Last, ...)
return true; return true;
} }
#if APIVERSNUM >= 10700
static int GetClippedNumProvidedSystems(int AvailableBits, cDevice *Device)
{
int MaxNumProvidedSystems = 1 << AvailableBits;
int NumProvidedSystems = Device->NumProvidedSystems();
if (NumProvidedSystems > MaxNumProvidedSystems) {
esyslog("ERROR: device %d supports %d modulation systems but cDevice::GetDevice() currently only supports %d delivery systems which should be fixed", Device->CardIndex() + 1, NumProvidedSystems, MaxNumProvidedSystems);
NumProvidedSystems = MaxNumProvidedSystems;
}
else if (NumProvidedSystems <= 0) {
esyslog("ERROR: device %d reported an invalid number (%d) of supported delivery systems - assuming 1", Device->CardIndex() + 1, NumProvidedSystems);
NumProvidedSystems = 1;
}
return NumProvidedSystems;
}
#endif
/*
* copy of cDevice::GetDevice(...) but without side effects (not detaching receivers)
*/
cDevice* cServerConnection::CheckDevice(const cChannel *Channel, int Priority, bool LiveView, const cDevice *AvoidDevice)
{
//cDevice *AvoidDevice = avoidDevice;
//avoidDevice = NULL;
// Collect the current priorities of all CAM slots that can decrypt the channel:
int NumCamSlots = CamSlots.Count();
int SlotPriority[NumCamSlots];
int NumUsableSlots = 0;
if (Channel->Ca() >= CA_ENCRYPTED_MIN) {
for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) {
SlotPriority[CamSlot->Index()] = MAXPRIORITY + 1; // assumes it can't be used
if (CamSlot->ModuleStatus() == msReady) {
if (CamSlot->ProvidesCa(Channel->Caids())) {
if (!ChannelCamRelations.CamChecked(Channel->GetChannelID(), CamSlot->SlotNumber())) {
SlotPriority[CamSlot->Index()] = CamSlot->Priority();
NumUsableSlots++;
}
}
}
}
if (!NumUsableSlots)
return NULL; // no CAM is able to decrypt this channel
}
bool NeedsDetachReceivers = false;
cDevice *d = NULL;
//cCamSlot *s = NULL;
uint32_t Impact = 0xFFFFFFFF; // we're looking for a device with the least impact
for (int j = 0; j < NumCamSlots || !NumUsableSlots; j++) {
if (NumUsableSlots && SlotPriority[j] > MAXPRIORITY)
continue; // there is no CAM available in this slot
for (int i = 0; i < cDevice::NumDevices(); i++) {
cDevice *device = cDevice::GetDevice(i);
if (device == AvoidDevice)
continue; // we've been asked to skip this device
if (Channel->Ca() && Channel->Ca() <= CA_DVB_MAX && Channel->Ca() != device->CardIndex() + 1)
continue; // a specific card was requested, but not this one
if (NumUsableSlots && !CamSlots.Get(j)->Assign(device, true))
continue; // CAM slot can't be used with this device
bool ndr;
if (device->ProvidesChannel(Channel, Priority, &ndr)) { // this device is basicly able to do the job
if (NumUsableSlots && device->CamSlot() && device->CamSlot() != CamSlots.Get(j))
ndr = true; // using a different CAM slot requires detaching receivers
// Put together an integer number that reflects the "impact" using
// this device would have on the overall system. Each condition is represented
// by one bit in the number (or several bits, if the condition is actually
// a numeric value). The sequence in which the conditions are listed corresponds
// to their individual severity, where the one listed first will make the most
// difference, because it results in the most significant bit of the result.
uint32_t imp = 0;
imp <<= 1; imp |= LiveView ? !device->IsPrimaryDevice() || ndr : 0; // prefer the primary device for live viewing if we don't need to detach existing receivers
imp <<= 1; imp |= !device->Receiving() && (device != cTransferControl::ReceiverDevice() || device->IsPrimaryDevice()) || ndr; // use receiving devices if we don't need to detach existing receivers, but avoid primary device in local transfer mode
imp <<= 1; imp |= device->Receiving(); // avoid devices that are receiving
#if APIVERSNUM >= 10700
imp <<= 2; imp |= GetClippedNumProvidedSystems(2, device) - 1; // avoid cards which support multiple delivery systems
#endif
imp <<= 1; imp |= device == cTransferControl::ReceiverDevice(); // avoid the Transfer Mode receiver device
imp <<= 8; imp |= min(max(device->Priority() + MAXPRIORITY, 0), 0xFF); // use the device with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
imp <<= 8; imp |= min(max((NumUsableSlots ? SlotPriority[j] : 0) + MAXPRIORITY, 0), 0xFF); // use the CAM slot with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
imp <<= 1; imp |= ndr; // avoid devices if we need to detach existing receivers
imp <<= 1; imp |= device->IsPrimaryDevice(); // avoid the primary device
imp <<= 1; imp |= NumUsableSlots ? 0 : device->HasCi(); // avoid cards with Common Interface for FTA channels
imp <<= 1; imp |= device->HasDecoder(); // avoid full featured cards
imp <<= 1; imp |= NumUsableSlots ? !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), j + 1) : 0; // prefer CAMs that are known to decrypt this channel
if (imp < Impact) {
// This device has less impact than any previous one, so we take it.
Impact = imp;
d = device;
NeedsDetachReceivers = ndr;
}
}
}
if (!NumUsableSlots)
break; // no CAM necessary, so just one loop over the devices
}
return d;
}
cDevice *cServerConnection::GetDevice(const cChannel *Channel, int Priority) cDevice *cServerConnection::GetDevice(const cChannel *Channel, int Priority)
{ {
cDevice *device = NULL; const cChannel *current = Channels.GetByNumber(cDevice::CurrentChannel());
/*Dprintf("+ Statistics:\n"); // turn off the streams of this connection
Dprintf("+ Current Channel: %d\n", cDevice::CurrentChannel()); Detach();
Dprintf("+ Current Device: %d\n", cDevice::ActualDevice()->CardIndex()); // This call may detach receivers of the device it returns
Dprintf("+ Transfer Mode: %s\n", cDevice::ActualDevice() cDevice *device = cDevice::GetDevice(Channel, Priority, false);
== cDevice::PrimaryDevice() ? "false" : "true");
Dprintf("+ Replaying: %s\n", cDevice::PrimaryDevice()->Replaying() ? "true"
: "false");*/
Dprintf(" * GetDevice(const cChannel*, int)\n"); if (device && device == cDevice::ActualDevice()
Dprintf(" * -------------------------------\n"); && !cSuspendCtl::IsActive()
&& StreamdevServerSetup.SuspendMode != smAlways
&& current != NULL
&& !TRANSPONDER(Channel, current)) {
// now we would have to switch away live tv...let's see if live tv
// can be handled by another device
#if VDRVERSNUM >= 10516
cDevice::SetAvoidDevice(device);
cDevice *newdev = cDevice::GetDevice(current, 0, true);
#else
cDevice *newdev = CheckDevice(current, 0, true, device);
#endif
if (newdev)
newdev->SwitchChannel(current, true);
else
device = NULL;
}
device = cDevice::GetDevice(Channel, Priority, false); if (!device) {
// can't switch - continue the current stream
Attach();
dsyslog("streamdev: GetDevice failed for channel %s at priority %d", Channel->Name(), Priority);
}
return device;
}
Dprintf(" * Found following device: %p (%d)\n", device, bool cServerConnection::ProvidesChannel(const cChannel *Channel, int Priority)
device ? device->CardIndex() + 1 : 0); {
if (device == cDevice::ActualDevice()) const cChannel *current = Channels.GetByNumber(cDevice::CurrentChannel());
Dprintf(" * is actual device\n");
if (!cSuspendCtl::IsActive() && StreamdevServerSetup.SuspendMode != smAlways)
Dprintf(" * NOT suspended\n");
cDevice *device = CheckDevice(Channel, Priority, false);
if (!device || (device == cDevice::ActualDevice() if (!device || (device == cDevice::ActualDevice()
&& !cSuspendCtl::IsActive() && !cSuspendCtl::IsActive()
&& StreamdevServerSetup.SuspendMode != smAlways)) { && StreamdevServerSetup.SuspendMode != smAlways
&& current != NULL
&& !TRANSPONDER(Channel, current))) {
// mustn't switch actual device // mustn't switch actual device
// maybe a device would be free if THIS connection did turn off its streams? // maybe a device would be free if THIS connection did turn off its streams?
Dprintf(" * trying again...\n");
const cChannel *current = Channels.GetByNumber(cDevice::CurrentChannel());
isyslog("streamdev-server: Detaching current receiver");
Detach(); Detach();
device = cDevice::GetDevice(Channel, Priority, false); device = CheckDevice(Channel, Priority, false);
Attach(); Attach();
Dprintf(" * Found following device: %p (%d)\n", device, if (device && device == cDevice::ActualDevice()
device ? device->CardIndex() + 1 : 0);
if (device == cDevice::ActualDevice())
Dprintf(" * is actual device\n");
if (!cSuspendCtl::IsActive()
&& StreamdevServerSetup.SuspendMode != smAlways)
Dprintf(" * NOT suspended\n");
if (current && !TRANSPONDER(Channel, current))
Dprintf(" * NOT same transponder\n");
if (device && (device == cDevice::ActualDevice()
&& !cSuspendCtl::IsActive() && !cSuspendCtl::IsActive()
&& StreamdevServerSetup.SuspendMode != smAlways && StreamdevServerSetup.SuspendMode != smAlways
&& current != NULL && current != NULL
&& !TRANSPONDER(Channel, current))) { && !TRANSPONDER(Channel, current)) {
// now we would have to switch away live tv...let's see if live tv // now we would have to switch away live tv...let's see if live tv
// can be handled by another device // can be handled by another device
cDevice *newdev = NULL; cDevice *newdev = CheckDevice(current, 0, true, device);
for (int i = 0; i < cDevice::NumDevices(); ++i) { if (!newdev) {
cDevice *dev = cDevice::GetDevice(i);
if (dev->ProvidesChannel(current, 0) && dev != device) {
newdev = dev;
break;
}
}
Dprintf(" * Found device for live tv: %p (%d)\n", newdev,
newdev ? newdev->CardIndex() + 1 : 0);
if (newdev == NULL || newdev == device)
// no suitable device to continue live TV, giving up...
device = NULL; device = NULL;
else dsyslog("streamdev: Not providing channel %s at priority %d - live TV not suspended", Channel->Name(), Priority);
newdev->SwitchChannel(current, true); }
} }
else if (!device)
dsyslog("streamdev: No device provides channel %s at priority %d", Channel->Name(), Priority);
} }
return device; return device;
} }

View File

@ -1,5 +1,5 @@
/* /*
* $Id: connection.h,v 1.9 2010/07/19 13:49:31 schmirl Exp $ * $Id: connection.h,v 1.10 2010/08/03 10:46:41 schmirl Exp $
*/ */
#ifndef VDR_STREAMDEV_SERVER_CONNECTION_H #ifndef VDR_STREAMDEV_SERVER_CONNECTION_H
@ -35,6 +35,11 @@ private:
tStrStrMap m_Headers; tStrStrMap m_Headers;
/* Check if a device would be available for transfering the given
channel. This call has no side effects except for temporarily
detaching this connection's receivers. */
cDevice *CheckDevice(const cChannel *Channel, int Priority, bool LiveView, const cDevice *AvoidDevice = NULL);
protected: protected:
/* Will be called when a command terminated by a newline has been /* Will be called when a command terminated by a newline has been
received */ received */
@ -91,10 +96,14 @@ public:
/* Will make the socket close after sending all queued output data */ /* Will make the socket close after sending all queued output data */
void DeferClose(void) { m_DeferClose = true; } void DeferClose(void) { m_DeferClose = true; }
/* Will retrieve an unused device for transmitting data. Use the returned /* Will retrieve an unused device for transmitting data. Receivers have
already been attached from the device if necessary. Use the returned
cDevice in a following call to StartTransfer */ cDevice in a following call to StartTransfer */
cDevice *GetDevice(const cChannel *Channel, int Priority); cDevice *GetDevice(const cChannel *Channel, int Priority);
/* Test if a call to GetDevice would return a usable device. */
bool ProvidesChannel(const cChannel *Channel, int Priority);
virtual void Flushed(void) {} virtual void Flushed(void) {}
virtual void Detach(void) = 0; virtual void Detach(void) = 0;

View File

@ -1,5 +1,5 @@
/* /*
* $Id: connectionHTTP.c,v 1.19 2010/07/20 12:26:29 schmirl Exp $ * $Id: connectionHTTP.c,v 1.21 2010/08/03 10:46:41 schmirl Exp $
*/ */
#include <ctype.h> #include <ctype.h>
@ -140,11 +140,18 @@ bool cConnectionHTTP::ProcessRequest(void)
} }
} }
if (Headers().at(REQUEST_METHOD).compare("GET") == 0 && ProcessURI(Headers().at(PATH_INFO))) { tStrStrMap::const_iterator it_method = Headers().find(REQUEST_METHOD);
tStrStrMap::const_iterator it_pathinfo = Headers().find(PATH_INFO);
if (it_method == Headers().end() || it_pathinfo == Headers().end()) {
// should never happen
esyslog("streamdev-server connectionHTTP: Missing method or pathinfo");
} else if (it_method->second.compare("GET") == 0 && ProcessURI(it_pathinfo->second)) {
if (m_ChannelList) if (m_ChannelList)
return Respond("%s", true, m_ChannelList->HttpHeader().c_str()); return Respond("%s", true, m_ChannelList->HttpHeader().c_str());
else if (m_Channel != NULL) { else if (m_Channel != NULL) {
cDevice *device = GetDevice(m_Channel, 0); cDevice *device = NULL;
if (ProvidesChannel(m_Channel, 0))
device = GetDevice(m_Channel, 0);
if (device != NULL) { if (device != NULL) {
device->SwitchChannel(m_Channel, false); device->SwitchChannel(m_Channel, false);
m_LiveStreamer = new cStreamdevLiveStreamer(0, this); m_LiveStreamer = new cStreamdevLiveStreamer(0, this);
@ -176,13 +183,12 @@ bool cConnectionHTTP::ProcessRequest(void)
return Respond("HTTP/1.0 404 not found") return Respond("HTTP/1.0 404 not found")
&& Respond(""); && Respond("");
} }
} else if (Headers().at(REQUEST_METHOD).compare("HEAD") == 0 && ProcessURI(Headers().at(PATH_INFO))) { } else if (it_method->second.compare("HEAD") == 0 && ProcessURI(it_pathinfo->second)) {
DeferClose(); DeferClose();
if (m_ChannelList) if (m_ChannelList)
return Respond("%s", true, m_ChannelList->HttpHeader().c_str()); return Respond("%s", true, m_ChannelList->HttpHeader().c_str());
else if (m_Channel != NULL) { else if (m_Channel != NULL) {
cDevice *device = GetDevice(m_Channel, 0); if (ProvidesChannel(m_Channel, 0)) {
if (device != NULL) {
if (m_StreamType == stEXT) { if (m_StreamType == stEXT) {
// TODO // TODO
return Respond("HTTP/1.0 200 OK") return Respond("HTTP/1.0 200 OK")
@ -249,7 +255,8 @@ cChannelList* cConnectionHTTP::ChannelListFromString(const std::string& Path, co
const static std::string QUERY_STRING("QUERY_STRING"); const static std::string QUERY_STRING("QUERY_STRING");
const static std::string HOST("HTTP_HOST"); const static std::string HOST("HTTP_HOST");
const std::string query = Headers().at(QUERY_STRING); tStrStrMap::const_iterator it_query = Headers().find(QUERY_STRING);
const std::string& query = it_query == Headers().end() ? "" : it_query->second;
std::string groupTarget; std::string groupTarget;
cChannelIterator *iterator = NULL; cChannelIterator *iterator = NULL;

View File

@ -1,5 +1,5 @@
/* /*
* $Id: connectionIGMP.c,v 1.2 2010/07/19 13:49:31 schmirl Exp $ * $Id: connectionIGMP.c,v 1.3 2010/08/03 10:46:41 schmirl Exp $
*/ */
#include <ctype.h> #include <ctype.h>
@ -25,7 +25,9 @@ cConnectionIGMP::~cConnectionIGMP()
bool cConnectionIGMP::Start(cChannel *Channel, in_addr_t Dst) bool cConnectionIGMP::Start(cChannel *Channel, in_addr_t Dst)
{ {
if (Channel != NULL) { if (Channel != NULL) {
cDevice *device = GetDevice(Channel, 0); cDevice *device = NULL;
if (ProvidesChannel(Channel, 0))
device = GetDevice(Channel, 0);
if (device != NULL) { if (device != NULL) {
device->SwitchChannel(Channel, false); device->SwitchChannel(Channel, false);
struct in_addr ip; struct in_addr ip;

View File

@ -1,5 +1,5 @@
/* /*
* $Id: connectionVTP.c,v 1.28 2010/07/19 13:49:31 schmirl Exp $ * $Id: connectionVTP.c,v 1.31 2010/08/18 10:26:54 schmirl Exp $
*/ */
#include "server/connectionVTP.h" #include "server/connectionVTP.h"
@ -746,6 +746,8 @@ cConnectionVTP::cConnectionVTP(void):
m_StreamType(stTSPIDS), m_StreamType(stTSPIDS),
m_FiltersSupport(false), m_FiltersSupport(false),
m_RecPlayer(NULL), m_RecPlayer(NULL),
m_TuneChannel(NULL),
m_TunePriority(0),
m_LSTEHandler(NULL), m_LSTEHandler(NULL),
m_LSTCHandler(NULL), m_LSTCHandler(NULL),
m_LSTTHandler(NULL), m_LSTTHandler(NULL),
@ -836,6 +838,7 @@ bool cConnectionVTP::Command(char *Cmd)
else if (strcasecmp(Cmd, "READ") == 0) return CmdREAD(param); else if (strcasecmp(Cmd, "READ") == 0) return CmdREAD(param);
else if (strcasecmp(Cmd, "TUNE") == 0) return CmdTUNE(param); else if (strcasecmp(Cmd, "TUNE") == 0) return CmdTUNE(param);
else if (strcasecmp(Cmd, "PLAY") == 0) return CmdPLAY(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, "ADDP") == 0) return CmdADDP(param);
else if (strcasecmp(Cmd, "DELP") == 0) return CmdDELP(param); else if (strcasecmp(Cmd, "DELP") == 0) return CmdDELP(param);
else if (strcasecmp(Cmd, "ADDF") == 0) return CmdADDF(param); else if (strcasecmp(Cmd, "ADDF") == 0) return CmdADDF(param);
@ -894,6 +897,11 @@ bool cConnectionVTP::CmdCAPS(char *Opts)
return Respond(220, "Capability \"%s\" accepted", 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); return Respond(561, "Capability \"%s\" not known", Opts);
} }
@ -911,9 +919,15 @@ bool cConnectionVTP::CmdPROV(char *Opts)
if ((chan = ChannelFromString(Opts)) == NULL) if ((chan = ChannelFromString(Opts)) == NULL)
return Respond(550, "Undefined channel \"%s\"", Opts); return Respond(550, "Undefined channel \"%s\"", Opts);
return GetDevice(chan, prio) != NULL if (ProvidesChannel(chan, prio)) {
? Respond(220, "Channel available") m_TuneChannel = chan;
: Respond(560, "Channel not available"); m_TunePriority = prio;
return Respond(220, "Channel available");
}
else {
m_TuneChannel = NULL;
return Respond(560, "Channel not available");
}
} }
bool cConnectionVTP::CmdPORT(char *Opts) bool cConnectionVTP::CmdPORT(char *Opts)
@ -1067,18 +1081,23 @@ bool cConnectionVTP::CmdTUNE(char *Opts)
{ {
const cChannel *chan; const cChannel *chan;
cDevice *dev; cDevice *dev;
int prio = m_TunePriority;
if ((chan = ChannelFromString(Opts)) == NULL) if ((chan = ChannelFromString(Opts)) == NULL)
return Respond(550, "Undefined channel \"%s\"", Opts); 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"); return Respond(560, "Channel not available");
if (!dev->SwitchChannel(chan, false)) if (!dev->SwitchChannel(chan, false))
return Respond(560, "Channel not available"); return Respond(560, "Channel not available");
delete m_LiveStreamer; delete m_LiveStreamer;
m_LiveStreamer = new cStreamdevLiveStreamer(1, this); m_LiveStreamer = new cStreamdevLiveStreamer(prio, this);
m_LiveStreamer->SetChannel(chan, m_StreamType); m_LiveStreamer->SetChannel(chan, m_StreamType);
m_LiveStreamer->SetDevice(dev); m_LiveStreamer->SetDevice(dev);
if(m_LiveSocket) if(m_LiveSocket)
@ -1119,6 +1138,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) bool cConnectionVTP::CmdADDP(char *Opts)
{ {
int pid; int pid;
@ -1243,6 +1278,7 @@ bool cConnectionVTP::CmdSUSP(void)
else if (StreamdevServerSetup.SuspendMode == smOffer else if (StreamdevServerSetup.SuspendMode == smOffer
&& StreamdevServerSetup.AllowSuspend) { && StreamdevServerSetup.AllowSuspend) {
cControl::Launch(new cSuspendCtl); cControl::Launch(new cSuspendCtl);
cControl::Attach();
return Respond(220, "Server is suspended"); return Respond(220, "Server is suspended");
} else } else
return Respond(550, "Client may not suspend server"); return Respond(550, "Client may not suspend server");

View File

@ -31,6 +31,11 @@ private:
bool m_FiltersSupport; bool m_FiltersSupport;
RecPlayer *m_RecPlayer; 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 // Members adopted for SVDRP
cLSTEHandler *m_LSTEHandler; cLSTEHandler *m_LSTEHandler;
cLSTCHandler *m_LSTCHandler; cLSTCHandler *m_LSTCHandler;
@ -59,6 +64,7 @@ public:
bool CmdREAD(char *Opts); bool CmdREAD(char *Opts);
bool CmdTUNE(char *Opts); bool CmdTUNE(char *Opts);
bool CmdPLAY(char *Opts); bool CmdPLAY(char *Opts);
bool CmdPRIO(char *Opts);
bool CmdADDP(char *Opts); bool CmdADDP(char *Opts);
bool CmdDELP(char *Opts); bool CmdDELP(char *Opts);
bool CmdADDF(char *Opts); bool CmdADDF(char *Opts);

View File

@ -289,7 +289,7 @@ void cStreamdevPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, i
if (written != TS_SIZE) if (written != TS_SIZE)
siBuffer.ReportOverflow(TS_SIZE - written); siBuffer.ReportOverflow(TS_SIZE - written);
if (pmtPid != prevPmtPid) { if (pmtPid != prevPmtPid) {
m_Streamer->SetPids(pmtPid); m_Streamer->SetPid(pmtPid, true);
Add(pmtPid, 0x02); Add(pmtPid, 0x02);
pmtVersion = -1; pmtVersion = -1;
} }
@ -434,17 +434,24 @@ bool cStreamdevLiveStreamer::SetPids(int Pid, const int *Pids1, const int *Pids2
return true; return true;
} }
void cStreamdevLiveStreamer::SetPriority(int Priority)
{
m_Priority = Priority;
StartReceiver();
}
void cStreamdevLiveStreamer::StartReceiver(void) void cStreamdevLiveStreamer::StartReceiver(void)
{ {
DELETENULL(m_Receiver); if (m_Device != NULL && m_NumPids > 0 && IsRunning()) {
if (m_NumPids > 0) {
Dprintf("Creating Receiver to respect changed pids\n"); Dprintf("Creating Receiver to respect changed pids\n");
cReceiver *current = m_Receiver;
m_Receiver = new cStreamdevLiveReceiver(this, m_Channel->GetChannelID(), m_Priority, m_Pids); m_Receiver = new cStreamdevLiveReceiver(this, m_Channel->GetChannelID(), m_Priority, m_Pids);
if (IsRunning() && m_Device != NULL) { cThreadLock ThreadLock(m_Device);
Dprintf("Attaching new receiver\n"); Attach();
Attach(); delete current;
}
} }
else
DELETENULL(m_Receiver);
} }
bool cStreamdevLiveStreamer::SetChannel(const cChannel *Channel, eStreamType StreamType, const int* Apid, const int *Dpid) bool cStreamdevLiveStreamer::SetChannel(const cChannel *Channel, eStreamType StreamType, const int* Apid, const int *Dpid)
@ -631,12 +638,10 @@ void cStreamdevFilterStreamer::SetDevice(cDevice *Device)
{ {
Dprintf("cStreamdevFilterStreamer::SetDevice()\n"); Dprintf("cStreamdevFilterStreamer::SetDevice()\n");
LOCK_THREAD; LOCK_THREAD;
if(Device != m_Device) { Detach();
Detach(); m_Device = Device;
m_Device = Device; //m_Channel = NULL;
//m_Channel = NULL; Attach();
Attach();
}
} }
bool cStreamdevFilterStreamer::SetFilter(u_short Pid, u_char Tid, u_char Mask, bool On) bool cStreamdevFilterStreamer::SetFilter(u_short Pid, u_char Tid, u_char Mask, bool On)

View File

@ -38,6 +38,7 @@ public:
bool SetPid(int Pid, bool On); bool SetPid(int Pid, bool On);
bool SetPids(int Pid, const int *Pids1 = NULL, const int *Pids2 = NULL, const int *Pids3 = NULL); 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); 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 int Put(const uchar *Data, int Count);
virtual uchar *Get(int &Count); virtual uchar *Get(int &Count);

View File

@ -2,7 +2,7 @@
#include "server/menuHTTP.h" #include "server/menuHTTP.h"
//**************************** cChannelIterator ************** //**************************** cChannelIterator **************
cChannelIterator::cChannelIterator(cChannel *First): channel(First) cChannelIterator::cChannelIterator(const cChannel *First): channel(First)
{} {}
const cChannel* cChannelIterator::Next() const cChannel* cChannelIterator::Next()
@ -19,7 +19,7 @@ cListAll::cListAll(): cChannelIterator(Channels.First())
const cChannel* cListAll::NextChannel(const cChannel *Channel) const cChannel* cListAll::NextChannel(const cChannel *Channel)
{ {
if (Channel) if (Channel)
Channel = Channels.Next(Channel); Channel = SkipFakeGroups(Channels.Next(Channel));
return Channel; return Channel;
} }
@ -46,14 +46,19 @@ const cChannel* cListGroups::NextChannel(const cChannel *Channel)
} }
// //
// ********************* cListGroup **************** // ********************* 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::GetNextChannelInGroup(const cChannel *Channel)
{
if (Channel)
Channel = SkipFakeGroups(Channels.Next(Channel));
return Channel && !Channel->GroupSep() ? Channel : NULL;
}
const cChannel* cListGroup::NextChannel(const cChannel *Channel) const cChannel* cListGroup::NextChannel(const cChannel *Channel)
{ {
if (Channel) return GetNextChannelInGroup(Channel);
Channel = Channels.Next(Channel);
return (Channel && !Channel->GroupSep()) ? Channel : NULL;
} }
// //
// ********************* cListTree **************** // ********************* cListTree ****************
@ -68,7 +73,7 @@ const cChannel* cListTree::NextChannel(const cChannel *Channel)
if (currentGroup == selectedGroup) if (currentGroup == selectedGroup)
{ {
if (Channel) if (Channel)
Channel = Channels.Next(Channel); Channel = SkipFakeGroups(Channels.Next(Channel));
if (Channel && Channel->GroupSep()) if (Channel && Channel->GroupSep())
currentGroup = Channel; currentGroup = Channel;
} }

View File

@ -13,9 +13,10 @@ class cChannelIterator
const cChannel *channel; const cChannel *channel;
protected: protected:
virtual const cChannel* NextChannel(const cChannel *Channel) = 0; virtual const cChannel* NextChannel(const cChannel *Channel) = 0;
static inline const cChannel* SkipFakeGroups(const cChannel *Channel);
public: public:
const cChannel* Next(); const cChannel* Next();
cChannelIterator(cChannel *First); cChannelIterator(const cChannel *First);
virtual ~cChannelIterator() {}; virtual ~cChannelIterator() {};
}; };
@ -48,6 +49,8 @@ class cListGroups: public cChannelIterator
class cListGroup: public cChannelIterator class cListGroup: public cChannelIterator
{ {
private:
static const cChannel* GetNextChannelInGroup(const cChannel *Channel);
protected: protected:
virtual const cChannel* NextChannel(const cChannel *Channel); virtual const cChannel* NextChannel(const cChannel *Channel);
public: public:
@ -140,4 +143,11 @@ class cM3uChannelList: public cChannelList
virtual ~cM3uChannelList(); virtual ~cM3uChannelList();
}; };
inline const cChannel* cChannelIterator::SkipFakeGroups(const cChannel* Group)
{
while (Group && Group->GroupSep() && !*Group->Name())
Group = Channels.Next(Group);
return Group;
}
#endif #endif

View File

@ -1,5 +1,5 @@
/* /*
* $Id: streamer.c,v 1.20 2010/07/19 13:49:32 schmirl Exp $ * $Id: streamer.c,v 1.21 2010/07/30 10:01:11 schmirl Exp $
*/ */
#include <vdr/ringbuffer.h> #include <vdr/ringbuffer.h>
@ -46,6 +46,9 @@ void cStreamdevWriter::Action(void)
uchar *block = NULL; uchar *block = NULL;
int count, offset = 0; int count, offset = 0;
#if APIVERSNUM >= 10705
SetPriority(-3);
#endif
sel.Clear(); sel.Clear();
sel.Add(*m_Socket, true); sel.Add(*m_Socket, true);
while (Running()) { while (Running()) {
@ -149,6 +152,9 @@ void cStreamdevStreamer::Stop(void)
void cStreamdevStreamer::Action(void) void cStreamdevStreamer::Action(void)
{ {
#if APIVERSNUM >= 10705
SetPriority(-3);
#endif
while (Running()) { while (Running()) {
int got; int got;
uchar *block = m_RingBuffer->Get(got); uchar *block = m_RingBuffer->Get(got);

View File

@ -16,6 +16,8 @@
# VBR video bitrate (kbit) # VBR video bitrate (kbit)
# VOPTS custom video options # VOPTS custom video options
# WIDTH scale video to width # WIDTH scale video to width
# HEIGHT scale video to height
# FPS output frames per second
# AC audio codec # AC audio codec
# ABR audio bitrate (kbit) # ABR audio bitrate (kbit)
# AOPTS custom audio options # AOPTS custom audio options
@ -42,14 +44,22 @@ ABR_MONO=64
### ###
# mencoder binary # mencoder binary
MENCODER=mencoder MENCODER=mencoder
### video part
# Default video codec (e.g. lavc/x264/copy) # Default video codec (e.g. lavc/x264/copy)
MENCODER_VC=lavc 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) # Default audio codec (e.g. lavc/mp3lame/faac/copy)
MENCODER_AC=mp3lame MENCODER_AC=mp3lame
# Default video codec if lavc is used (-ovc lavc -lavcopts vcodec=) # Default audio options if lavc is used (-oac lavc -lavcopts ...)
MENCODER_LAVC_VC=mpeg4 MENCODER_LAVC_AOPTS=acodec=mp2
# Default audio codec if lavc is used (-oac lavc -lavcopts acodec=) # Default audio options if mp3lame is used (-oac mp3lame -lameopts ...)
MENCODER_LAVC_AC=mp2 MENCODER_LAME_AOPTS=
# Default audio options if faac is used (-oac faac -faacopts ...)
MENCODER_FAAC_AOPTS=
### ###
### MENCODER CONFIG END ### MENCODER CONFIG END
@ -63,6 +73,8 @@ OGG_SPEED=1
OGG_VQUALITY=0 OGG_VQUALITY=0
# audioquality - higher value gives better quality but is slower (0..10) # audioquality - higher value gives better quality but is slower (0..10)
OGG_AQUALITY=0 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 ### OGG CONFIG END
@ -70,7 +82,19 @@ OGG_AQUALITY=0
function hasOpt { echo "$1" | grep -q "\b${2}\b"; } 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 function remux_cat
{ {
@ -87,21 +111,32 @@ function remux_mencoder
# Assemble video options # Assemble video options
VC=${REMUX_PARAM_VC:-$MENCODER_VC} VC=${REMUX_PARAM_VC:-$MENCODER_VC}
VOPTS=${REMUX_PARAM_VOPTS} 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 case "$VC" in
lavc) lavc)
LAVCOPTS=( LAVCOPTS=(
${VOPTS} ${VOPTS}
$(hasOpt "$VOPTS" vcodec || echo "vcodec=$MENCODER_LAVC_VC") $(IFS=$IFS:; addOpts "$VOPTS" $MENCODER_LAVC_VOPTS)
${VBR:+vbitrate=$VBR} ${VBR:+vbitrate=$VBR}
) )
[ ${#LAVCOPTS[*]} -gt 0 ] && VOPTS=$(IFS=:; echo -lavcopts "${LAVCOPTS[*]}") [ ${#LAVCOPTS[*]} -gt 0 ] && VOPTS=$(IFS=:; echo -lavcopts "${LAVCOPTS[*]}")
;; ;;
x264) x264)
isNumeric "$HEIGHT" && [ $HEIGHT -lt 0 -a $HEIGHT -gt -8 ] && ((HEIGHT-=8))
isNumeric "$WIDTH" && [ $WIDTH -lt 0 -a $WIDTH -gt -8 ] && ((WIDTH-=8))
X264OPTS=( X264OPTS=(
${VOPTS} ${VOPTS}
$(hasOpt "$VOPTS" threads || echo "threads=auto") $(IFS=$IFS:; addOpts "$VOPTS" $MENCODER_X264_VOPTS)
${VBR:+bitrate=$ABR} ${VBR:+bitrate=$VBR}
) )
[ ${#X264OPTS[*]} -gt 0 ] && VOPTS=$(IFS=:; echo -x264encopts "${X264OPTS[*]}") [ ${#X264OPTS[*]} -gt 0 ] && VOPTS=$(IFS=:; echo -x264encopts "${X264OPTS[*]}")
;; ;;
@ -121,7 +156,7 @@ function remux_mencoder
LAVCOPTS=( LAVCOPTS=(
${LAVCOPTS[*]} ${LAVCOPTS[*]}
${AOPTS} ${AOPTS}
$(hasOpt "$AOPTS" acodec || echo "acodec=$MENCODER_LAVC_AC") $(IFS=$IFS:; addOpts "$AOPTS" $MENCODER_LAVC_AOPTS)
${ABR:+abitrate=$ABR} ${ABR:+abitrate=$ABR}
) )
@ -133,6 +168,7 @@ function remux_mencoder
LAMEOPTS=( LAMEOPTS=(
${AOPTS} ${AOPTS}
$(isNumeric "${ABR}" && [ "${ABR}" -lt "$ABR_MONO" ] && ! hasOpt "${AOPTS}" mode ] && echo 'mode=3') $(isNumeric "${ABR}" && [ "${ABR}" -lt "$ABR_MONO" ] && ! hasOpt "${AOPTS}" mode ] && echo 'mode=3')
$(IFS=$IFS:; addOpts "$AOPTS" $MENCODER_LAME_AOPTS)
${ABR:+preset=$ABR} ${ABR:+preset=$ABR}
) )
[ ${#LAMEOPTS[*]} -gt 0 ] && AOPTS=$(IFS=:; echo -lameopts "${LAMEOPTS[*]}") [ ${#LAMEOPTS[*]} -gt 0 ] && AOPTS=$(IFS=:; echo -lameopts "${LAMEOPTS[*]}")
@ -140,6 +176,7 @@ function remux_mencoder
faac) faac)
FAACOPTS=( FAACOPTS=(
${AOPTS} ${AOPTS}
$(IFS=$IFS:; addOpts "$AOPTS" $MENCODER_FAAC_AOPTS)
${ABR:+br=$ABR} ${ABR:+br=$ABR}
) )
[ ${#FAACOPTS[*]} -gt 0 ] && AOPTS=$(IFS=:; echo -faacopts "${FAACOPTS[*]}") [ ${#FAACOPTS[*]} -gt 0 ] && AOPTS=$(IFS=:; echo -faacopts "${FAACOPTS[*]}")
@ -155,15 +192,17 @@ function remux_mencoder
startReply startReply
exec 3<&0 exec 3<&0
echo "$MENCODER" \ echo $MENCODER \
-ovc $VC $VOPTS \ -ovc $VC $VOPTS \
-oac $AC $AOPTS \ -oac $AC $AOPTS \
${WIDTH:+-vf scale -zoom -xy $WIDTH} \ ${WIDTH:+-vf scale=$WIDTH:$HEIGHT -zoom} \
${FPS:+-ofps $FPS} \
-o "$FIFO" -- - >&2 -o "$FIFO" -- - >&2
"$MENCODER" \ $MENCODER \
-ovc $VC $VOPTS \ -ovc $VC $VOPTS \
-oac $AC $AOPTS \ -oac $AC $AOPTS \
${WIDTH:+-vf scale -zoom -xy $WIDTH} \ ${WIDTH:+-vf scale=$WIDTH:$HEIGHT -zoom} \
${FPS:+-ofps $FPS} \
-o "$FIFO" -- - 0<&3 >/dev/null & -o "$FIFO" -- - 0<&3 >/dev/null &
} }
@ -171,7 +210,15 @@ function remux_ogg
{ {
VOPTS=${REMUX_PARAM_VOPTS//[:=]/ } VOPTS=${REMUX_PARAM_VOPTS//[:=]/ }
AOPTS=${REMUX_PARAM_AOPTS//[:=]/ } 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=( OGGOPTS=(
${VOPTS} ${VOPTS}
@ -187,14 +234,14 @@ function remux_ogg
startReply startReply
exec 3<&0 exec 3<&0
echo "$OGG" --format ts \ echo $OGG --format ts \
${OGGOPTS[*]} \ ${OGGOPTS[*]} \
${WIDTH:+--width $WIDTH --height $(($WIDTH * 3 / 4 / 8 * 8))} \ ${WIDTH:+--width $WIDTH --height $HEIGHT} \
--title "VDR Streamdev: ${REMUX_CHANNEL_NAME}" \ --title "VDR Streamdev: ${REMUX_CHANNEL_NAME}" \
--output "$FIFO" -- - 0<&3 >&2 --output "$FIFO" -- - 0<&3 >&2
"$OGG" --format ts \ $OGG --format ts \
${OGGOPTS[*]} \ ${OGGOPTS[*]} \
${WIDTH:+--width $WIDTH --height $(($WIDTH * 3 / 4 / 8 * 8))} \ ${WIDTH:+--width $WIDTH --height $HEIGHT} \
--title "VDR Streamdev: ${REMUX_CHANNEL_NAME}" \ --title "VDR Streamdev: ${REMUX_CHANNEL_NAME}" \
--output "$FIFO" -- - 0<&3 >/dev/null & --output "$FIFO" -- - 0<&3 >/dev/null &
} }
@ -254,6 +301,7 @@ esac
ABR=${REMUX_PARAM_ABR:-$ABR} ABR=${REMUX_PARAM_ABR:-$ABR}
VBR=${REMUX_PARAM_VBR:-$VBR} VBR=${REMUX_PARAM_VBR:-$VBR}
WIDTH=${REMUX_PARAM_WIDTH:-$WIDTH} WIDTH=${REMUX_PARAM_WIDTH:-$WIDTH}
HEIGHT=${REMUX_PARAM_HEIGHT:-$HEIGHT}
PROG=${REMUX_PARAM_PROG:-$PROG} PROG=${REMUX_PARAM_PROG:-$PROG}
case "$PROG" in case "$PROG" in