mirror of
https://projects.vdr-developer.org/git/vdr-plugin-streamdev.git
synced 2023-10-10 19:16:51 +02:00
Snapshot 2010-09-15
This commit is contained in:
parent
e0a00f90ae
commit
2ec54f7505
16
CONTRIBUTORS
16
CONTRIBUTORS
@ -38,6 +38,7 @@ Rolf Ahrenberg
|
||||
for suggesting to include the charset in HTTP replies
|
||||
for requesting replacement of asprintf calls
|
||||
for suggesting to change the URL path from EXTERN to EXT
|
||||
for suggesting increased thread priorities for cStreamdevWriter/Streamer
|
||||
|
||||
Rantanen Teemu
|
||||
for providing vdr-incompletesections.diff
|
||||
@ -153,7 +154,20 @@ carel
|
||||
for helping to find a way to cleanly shutdown externremux with mencoder
|
||||
|
||||
wolfi.m
|
||||
for reporting a typo in externermux quality parameter value
|
||||
for reporting a typo in externremux quality parameter value
|
||||
|
||||
Norman Thiel
|
||||
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
38
HISTORY
@ -1,6 +1,44 @@
|
||||
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
|
||||
|
||||
- fixed wrong URL path in m3u playlists (reported by Norman Thiel)
|
||||
|
10
Makefile
10
Makefile
@ -1,7 +1,7 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
@ -35,7 +35,8 @@ TSPLAYVERSNUM = $(shell grep 'define TSPLAY_PATCH_VERSION ' $(VDRDIR)/device.h |
|
||||
|
||||
ifeq ($(shell test $(APIVERSNUM) -ge 10713; echo $$?),0)
|
||||
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
|
||||
CFLAGS += -fPIC
|
||||
CXXFLAGS += -fPIC
|
||||
@ -43,13 +44,14 @@ else
|
||||
CFLAGS += -fPIC
|
||||
CXXFLAGS += -fPIC
|
||||
endif
|
||||
endif
|
||||
|
||||
-include $(VDRDIR)/Make.config
|
||||
|
||||
### export all vars for sub-makes, using absolute paths
|
||||
|
||||
VDRDIR := $(abspath $(VDRDIR))
|
||||
LIBDIR := $(abspath $(LIBDIR))
|
||||
VDRDIR := $(shell cd $(VDRDIR) >/dev/null 2>&1 && pwd)
|
||||
LIBDIR := $(shell cd $(LIBDIR) >/dev/null 2>&1 && pwd)
|
||||
export
|
||||
unexport PLUGIN
|
||||
|
||||
|
@ -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"
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -118,6 +120,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;
|
||||
@ -140,6 +145,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();
|
||||
@ -301,3 +308,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();
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
@ -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;
|
||||
@ -59,6 +60,7 @@ public:
|
||||
#endif
|
||||
virtual bool IsTunedToTransponder(const cChannel *Channel);
|
||||
|
||||
static void UpdatePriority(void);
|
||||
static bool Init(void);
|
||||
static bool ReInit(void);
|
||||
|
||||
|
@ -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>
|
||||
@ -20,6 +20,7 @@ cClientSocket ClientSocket;
|
||||
cClientSocket::cClientSocket(void)
|
||||
{
|
||||
memset(m_DataSockets, 0, sizeof(cTBSocket*) * si_Count);
|
||||
m_Prio = false;
|
||||
Reset();
|
||||
}
|
||||
|
||||
@ -142,8 +143,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;
|
||||
}
|
||||
|
||||
@ -251,6 +258,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;
|
||||
|
||||
@ -259,8 +281,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;
|
||||
@ -276,7 +298,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;
|
||||
|
@ -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
|
||||
@ -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);
|
||||
|
@ -3,7 +3,7 @@
|
||||
*
|
||||
* 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"
|
||||
@ -52,4 +52,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!
|
||||
|
@ -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
|
||||
@ -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
|
||||
|
2
common.c
2
common.c
@ -10,7 +10,7 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
const char *VERSION = "0.5.0";
|
||||
const char *VERSION = "0.5.0-CVS";
|
||||
|
||||
const char cMenuEditIpItem::IpCharacters[] = "0123456789.";
|
||||
|
||||
|
@ -1,12 +1,16 @@
|
||||
#
|
||||
# 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):
|
||||
|
||||
OBJS = ctools.o remux.o ringbuffy.o transform.o
|
||||
|
||||
### Disable attribute warn_unused_result
|
||||
|
||||
DEFINES += -U_FORTIFY_SOURCE
|
||||
|
||||
### The main target:
|
||||
|
||||
.PHONY: clean
|
||||
|
@ -151,7 +151,10 @@ cTSExt::cTSExt(cRingBufferLinear *ResultBuffer, const cServerConnection *Connect
|
||||
|
||||
// look for section parameters: /path;param1=value1;param2=value2/
|
||||
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_first_not_of(';', begin);
|
||||
end = path.find_first_of(";/", begin);
|
||||
@ -188,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);
|
||||
|
@ -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"
|
||||
@ -8,6 +8,7 @@
|
||||
#include "common.h"
|
||||
|
||||
#include <vdr/tools.h>
|
||||
#include <vdr/transfer.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
@ -185,74 +186,171 @@ bool cServerConnection::Respond(const char *Message, bool Last, ...)
|
||||
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 *device = NULL;
|
||||
|
||||
/*Dprintf("+ Statistics:\n");
|
||||
Dprintf("+ Current Channel: %d\n", cDevice::CurrentChannel());
|
||||
Dprintf("+ Current Device: %d\n", cDevice::ActualDevice()->CardIndex());
|
||||
Dprintf("+ Transfer Mode: %s\n", cDevice::ActualDevice()
|
||||
== cDevice::PrimaryDevice() ? "false" : "true");
|
||||
Dprintf("+ Replaying: %s\n", cDevice::PrimaryDevice()->Replaying() ? "true"
|
||||
: "false");*/
|
||||
|
||||
Dprintf(" * GetDevice(const cChannel*, int)\n");
|
||||
Dprintf(" * -------------------------------\n");
|
||||
|
||||
device = cDevice::GetDevice(Channel, Priority, false);
|
||||
|
||||
Dprintf(" * Found following device: %p (%d)\n", device,
|
||||
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 (!device || (device == cDevice::ActualDevice()
|
||||
&& !cSuspendCtl::IsActive()
|
||||
&& StreamdevServerSetup.SuspendMode != smAlways)) {
|
||||
// mustn't switch actual device
|
||||
// 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");
|
||||
|
||||
// turn off the streams of this connection
|
||||
Detach();
|
||||
device = cDevice::GetDevice(Channel, Priority, false);
|
||||
// This call may detach receivers of the device it returns
|
||||
cDevice *device = cDevice::GetDevice(Channel, Priority, false);
|
||||
|
||||
if (device && device == cDevice::ActualDevice()
|
||||
&& !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;
|
||||
}
|
||||
|
||||
if (!device) {
|
||||
// can't switch - continue the current stream
|
||||
Attach();
|
||||
Dprintf(" * Found following device: %p (%d)\n", device,
|
||||
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()
|
||||
dsyslog("streamdev: GetDevice failed for channel %s at priority %d", Channel->Name(), Priority);
|
||||
}
|
||||
return device;
|
||||
}
|
||||
|
||||
bool cServerConnection::ProvidesChannel(const cChannel *Channel, int Priority)
|
||||
{
|
||||
const cChannel *current = Channels.GetByNumber(cDevice::CurrentChannel());
|
||||
|
||||
cDevice *device = CheckDevice(Channel, Priority, false);
|
||||
if (!device || (device == cDevice::ActualDevice()
|
||||
&& !cSuspendCtl::IsActive()
|
||||
&& StreamdevServerSetup.SuspendMode != smAlways
|
||||
&& current != NULL
|
||||
&& !TRANSPONDER(Channel, current))) {
|
||||
// mustn't switch actual device
|
||||
// maybe a device would be free if THIS connection did turn off its streams?
|
||||
Detach();
|
||||
device = CheckDevice(Channel, Priority, false);
|
||||
Attach();
|
||||
if (device && device == cDevice::ActualDevice()
|
||||
&& !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
|
||||
cDevice *newdev = NULL;
|
||||
for (int i = 0; i < cDevice::NumDevices(); ++i) {
|
||||
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...
|
||||
cDevice *newdev = CheckDevice(current, 0, true, device);
|
||||
if (!newdev) {
|
||||
device = NULL;
|
||||
else
|
||||
newdev->SwitchChannel(current, true);
|
||||
dsyslog("streamdev: Not providing channel %s at priority %d - live TV not suspended", Channel->Name(), Priority);
|
||||
}
|
||||
}
|
||||
|
||||
else if (!device)
|
||||
dsyslog("streamdev: No device provides channel %s at priority %d", Channel->Name(), Priority);
|
||||
}
|
||||
return device;
|
||||
}
|
||||
|
@ -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
|
||||
@ -35,6 +35,11 @@ private:
|
||||
|
||||
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:
|
||||
/* Will be called when a command terminated by a newline has been
|
||||
received */
|
||||
@ -91,10 +96,14 @@ public:
|
||||
/* Will make the socket close after sending all queued output data */
|
||||
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 *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 Detach(void) = 0;
|
||||
|
@ -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>
|
||||
@ -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)
|
||||
return Respond("%s", true, m_ChannelList->HttpHeader().c_str());
|
||||
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) {
|
||||
device->SwitchChannel(m_Channel, false);
|
||||
m_LiveStreamer = new cStreamdevLiveStreamer(0, this);
|
||||
@ -176,13 +183,12 @@ bool cConnectionHTTP::ProcessRequest(void)
|
||||
return Respond("HTTP/1.0 404 not found")
|
||||
&& 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();
|
||||
if (m_ChannelList)
|
||||
return Respond("%s", true, m_ChannelList->HttpHeader().c_str());
|
||||
else if (m_Channel != NULL) {
|
||||
cDevice *device = GetDevice(m_Channel, 0);
|
||||
if (device != NULL) {
|
||||
if (ProvidesChannel(m_Channel, 0)) {
|
||||
if (m_StreamType == stEXT) {
|
||||
// TODO
|
||||
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 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;
|
||||
cChannelIterator *iterator = NULL;
|
||||
|
@ -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>
|
||||
@ -25,7 +25,9 @@ cConnectionIGMP::~cConnectionIGMP()
|
||||
bool cConnectionIGMP::Start(cChannel *Channel, in_addr_t Dst)
|
||||
{
|
||||
if (Channel != NULL) {
|
||||
cDevice *device = GetDevice(Channel, 0);
|
||||
cDevice *device = NULL;
|
||||
if (ProvidesChannel(Channel, 0))
|
||||
device = GetDevice(Channel, 0);
|
||||
if (device != NULL) {
|
||||
device->SwitchChannel(Channel, false);
|
||||
struct in_addr ip;
|
||||
|
@ -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"
|
||||
@ -746,6 +746,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),
|
||||
@ -836,6 +838,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);
|
||||
@ -894,6 +897,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);
|
||||
}
|
||||
|
||||
@ -911,9 +919,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 (ProvidesChannel(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)
|
||||
@ -1067,18 +1081,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)
|
||||
@ -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)
|
||||
{
|
||||
int pid;
|
||||
@ -1243,6 +1278,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");
|
||||
|
@ -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);
|
||||
|
@ -289,7 +289,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;
|
||||
}
|
||||
@ -434,17 +434,24 @@ 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;
|
||||
m_Receiver = new cStreamdevLiveReceiver(this, m_Channel->GetChannelID(), m_Priority, m_Pids);
|
||||
if (IsRunning() && m_Device != NULL) {
|
||||
Dprintf("Attaching new receiver\n");
|
||||
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)
|
||||
@ -631,12 +638,10 @@ void cStreamdevFilterStreamer::SetDevice(cDevice *Device)
|
||||
{
|
||||
Dprintf("cStreamdevFilterStreamer::SetDevice()\n");
|
||||
LOCK_THREAD;
|
||||
if(Device != m_Device) {
|
||||
Detach();
|
||||
m_Device = Device;
|
||||
//m_Channel = NULL;
|
||||
Attach();
|
||||
}
|
||||
}
|
||||
|
||||
bool cStreamdevFilterStreamer::SetFilter(u_short Pid, u_char Tid, u_char Mask, bool On)
|
||||
|
@ -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);
|
||||
|
@ -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::GetNextChannelInGroup(const cChannel *Channel)
|
||||
{
|
||||
if (Channel)
|
||||
Channel = SkipFakeGroups(Channels.Next(Channel));
|
||||
return Channel && !Channel->GroupSep() ? Channel : NULL;
|
||||
}
|
||||
|
||||
const cChannel* cListGroup::NextChannel(const cChannel *Channel)
|
||||
{
|
||||
if (Channel)
|
||||
Channel = Channels.Next(Channel);
|
||||
return (Channel && !Channel->GroupSep()) ? Channel : NULL;
|
||||
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;
|
||||
}
|
||||
|
@ -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:
|
||||
@ -140,4 +143,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
|
||||
|
@ -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>
|
||||
@ -46,6 +46,9 @@ void cStreamdevWriter::Action(void)
|
||||
uchar *block = NULL;
|
||||
int count, offset = 0;
|
||||
|
||||
#if APIVERSNUM >= 10705
|
||||
SetPriority(-3);
|
||||
#endif
|
||||
sel.Clear();
|
||||
sel.Add(*m_Socket, true);
|
||||
while (Running()) {
|
||||
@ -149,6 +152,9 @@ void cStreamdevStreamer::Stop(void)
|
||||
|
||||
void cStreamdevStreamer::Action(void)
|
||||
{
|
||||
#if APIVERSNUM >= 10705
|
||||
SetPriority(-3);
|
||||
#endif
|
||||
while (Running()) {
|
||||
int got;
|
||||
uchar *block = m_RingBuffer->Get(got);
|
||||
|
@ -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,21 +111,32 @@ 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")
|
||||
${VBR:+bitrate=$ABR}
|
||||
$(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
|
||||
|
Loading…
Reference in New Issue
Block a user