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 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
View File

@ -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)

View File

@ -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

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"
@ -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();
}
}

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
@ -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);

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>
@ -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;

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
@ -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);

View File

@ -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!

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
@ -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

View File

@ -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.";

View File

@ -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

View File

@ -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);

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"
@ -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;
}

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
@ -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;

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>
@ -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;

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>
@ -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;

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"
@ -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");

View File

@ -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);

View File

@ -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,13 +638,11 @@ 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)
{

View File

@ -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);

View File

@ -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;
}

View File

@ -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

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>
@ -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);

View File

@ -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