2007-09-12 19:28:59 +02:00
|
|
|
/*
|
|
|
|
* device.c: IPTV plugin for the Video Disk Recorder
|
|
|
|
*
|
|
|
|
* See the README file for copyright information and how to reach the author.
|
|
|
|
*
|
2007-09-19 20:02:38 +02:00
|
|
|
* $Id: device.c,v 1.25 2007/09/19 18:02:38 rahrenbe Exp $
|
2007-09-12 19:28:59 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "common.h"
|
2007-09-15 17:38:38 +02:00
|
|
|
#include "config.h"
|
2007-09-12 19:28:59 +02:00
|
|
|
#include "device.h"
|
|
|
|
|
|
|
|
#define IPTV_MAX_DEVICES 8
|
|
|
|
|
|
|
|
cIptvDevice * IptvDevices[IPTV_MAX_DEVICES];
|
|
|
|
|
|
|
|
unsigned int cIptvDevice::deviceCount = 0;
|
|
|
|
|
|
|
|
cIptvDevice::cIptvDevice(unsigned int Index)
|
|
|
|
: deviceIndex(Index),
|
|
|
|
isPacketDelivered(false),
|
|
|
|
isOpenDvr(false),
|
2007-09-14 17:44:25 +02:00
|
|
|
mutex()
|
2007-09-12 19:28:59 +02:00
|
|
|
{
|
2007-09-12 20:55:31 +02:00
|
|
|
debug("cIptvDevice::cIptvDevice(%d)\n", deviceIndex);
|
2007-09-16 15:38:20 +02:00
|
|
|
tsBuffer = new cRingBufferLinear(MEGABYTE(IptvConfig.GetTsBufferSize()),
|
2007-09-15 23:27:00 +02:00
|
|
|
(TS_SIZE * 2), false, "IPTV");
|
2007-09-19 17:14:32 +02:00
|
|
|
memset(&filter, '\0', sizeof(filter));
|
|
|
|
init_trans(&filter);
|
2007-09-15 19:15:37 +02:00
|
|
|
tsBuffer->SetTimeouts(100, 100);
|
2007-09-15 23:27:00 +02:00
|
|
|
// pad prefill to multiple of TS_SIZE
|
2007-09-16 15:38:20 +02:00
|
|
|
tsBufferPrefill = MEGABYTE(IptvConfig.GetTsBufferSize()) *
|
|
|
|
IptvConfig.GetTsBufferPrefillRatio() / 100;
|
2007-09-15 23:27:00 +02:00
|
|
|
tsBufferPrefill -= (tsBufferPrefill % TS_SIZE);
|
2007-09-16 15:38:20 +02:00
|
|
|
//debug("Buffer=%d Prefill=%d\n", MEGABYTE(IptvConfig.GetTsBufferSize()), tsBufferPrefill);
|
2007-09-14 17:44:25 +02:00
|
|
|
pUdpProtocol = new cIptvProtocolUdp();
|
2007-09-16 11:38:00 +02:00
|
|
|
pHttpProtocol = new cIptvProtocolHttp();
|
2007-09-16 14:18:15 +02:00
|
|
|
pFileProtocol = new cIptvProtocolFile();
|
2007-09-12 19:28:59 +02:00
|
|
|
pIptvStreamer = new cIptvStreamer(tsBuffer, &mutex);
|
2007-09-14 17:44:25 +02:00
|
|
|
StartSectionHandler();
|
2007-09-19 17:14:32 +02:00
|
|
|
|
|
|
|
for(int i = 0; i < 32; ++i) {
|
|
|
|
filters[i].active = false;
|
|
|
|
filters[i].fifoDesc = -1;
|
|
|
|
}
|
|
|
|
|
2007-09-12 19:28:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
cIptvDevice::~cIptvDevice()
|
|
|
|
{
|
2007-09-12 20:55:31 +02:00
|
|
|
debug("cIptvDevice::~cIptvDevice(%d)\n", deviceIndex);
|
2007-09-14 17:44:25 +02:00
|
|
|
delete pIptvStreamer;
|
|
|
|
delete pUdpProtocol;
|
2007-09-12 19:28:59 +02:00
|
|
|
delete tsBuffer;
|
2007-09-19 17:14:32 +02:00
|
|
|
|
|
|
|
// Iterate over all filters and clear their settings
|
|
|
|
for (int i = 0; i < 32; ++i) {
|
|
|
|
if (filters[i].active) {
|
|
|
|
close(filters[i].fifoDesc);
|
|
|
|
unlink(filters[i].pipeName);
|
|
|
|
memset(filters[i].pipeName, '\0', sizeof(filters[i].pipeName));
|
|
|
|
filters[i].fifoDesc = -1;
|
|
|
|
filters[i].active = false;
|
|
|
|
clear_trans_filt(&filter, i);
|
|
|
|
}
|
|
|
|
}
|
2007-09-12 19:28:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool cIptvDevice::Initialize(unsigned int DeviceCount)
|
|
|
|
{
|
|
|
|
debug("cIptvDevice::Initialize()\n");
|
|
|
|
if (DeviceCount > IPTV_MAX_DEVICES)
|
|
|
|
DeviceCount = IPTV_MAX_DEVICES;
|
|
|
|
for (unsigned int i = 0; i < DeviceCount; ++i)
|
|
|
|
IptvDevices[i] = new cIptvDevice(i);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int cIptvDevice::Count(void)
|
|
|
|
{
|
|
|
|
unsigned int count = 0;
|
|
|
|
debug("cIptvDevice::Count()\n");
|
|
|
|
for (unsigned int i = 0; i < IPTV_MAX_DEVICES; ++i) {
|
|
|
|
if (IptvDevices[i])
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
cIptvDevice *cIptvDevice::Get(unsigned int DeviceIndex)
|
|
|
|
{
|
|
|
|
debug("cIptvDevice::Get()\n");
|
|
|
|
if ((DeviceIndex > 0) && (DeviceIndex <= IPTV_MAX_DEVICES))
|
2007-09-15 23:27:00 +02:00
|
|
|
return IptvDevices[DeviceIndex - 1];
|
2007-09-12 19:28:59 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2007-09-14 17:44:25 +02:00
|
|
|
cString cIptvDevice::GetChannelSettings(const char *Param, int *IpPort, cIptvProtocolIf* *Protocol)
|
2007-09-12 19:28:59 +02:00
|
|
|
{
|
2007-09-12 20:55:31 +02:00
|
|
|
debug("cIptvDevice::GetChannelSettings(%d)\n", deviceIndex);
|
2007-09-16 15:11:19 +02:00
|
|
|
char *loc = NULL;
|
2007-09-16 15:44:09 +02:00
|
|
|
if (sscanf(Param, "IPTV|UDP|%a[^|]|%u", &loc, IpPort) == 2) {
|
2007-09-16 15:11:19 +02:00
|
|
|
cString addr(loc, true);
|
2007-09-14 17:44:25 +02:00
|
|
|
*Protocol = pUdpProtocol;
|
2007-09-16 15:11:19 +02:00
|
|
|
return addr;
|
2007-09-12 19:28:59 +02:00
|
|
|
}
|
2007-09-16 15:44:09 +02:00
|
|
|
else if (sscanf(Param, "IPTV|HTTP|%a[^|]|%u", &loc, IpPort) == 2) {
|
2007-09-16 15:11:19 +02:00
|
|
|
cString addr(loc, true);
|
2007-09-16 11:38:00 +02:00
|
|
|
*Protocol = pHttpProtocol;
|
2007-09-16 15:11:19 +02:00
|
|
|
return addr;
|
2007-09-16 11:38:00 +02:00
|
|
|
}
|
2007-09-16 15:44:09 +02:00
|
|
|
else if (sscanf(Param, "IPTV|FILE|%a[^|]|%u", &loc, IpPort) == 2) {
|
2007-09-16 15:38:20 +02:00
|
|
|
cString addr(loc, true);
|
|
|
|
*Protocol = pFileProtocol;
|
|
|
|
return addr;
|
|
|
|
}
|
2007-09-12 19:28:59 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cIptvDevice::ProvidesIptv(const char *Param) const
|
|
|
|
{
|
2007-09-12 20:55:31 +02:00
|
|
|
debug("cIptvDevice::ProvidesIptv(%d)\n", deviceIndex);
|
2007-09-12 19:28:59 +02:00
|
|
|
return (strncmp(Param, "IPTV", 4) == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cIptvDevice::ProvidesSource(int Source) const
|
|
|
|
{
|
2007-09-12 20:55:31 +02:00
|
|
|
debug("cIptvDevice::ProvidesSource(%d)\n", deviceIndex);
|
2007-09-18 20:48:10 +02:00
|
|
|
return (cSource::IsPlug(Source));
|
2007-09-12 19:28:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool cIptvDevice::ProvidesTransponder(const cChannel *Channel) const
|
|
|
|
{
|
2007-09-12 20:55:31 +02:00
|
|
|
debug("cIptvDevice::ProvidesTransponder(%d)\n", deviceIndex);
|
2007-09-18 20:48:10 +02:00
|
|
|
return (ProvidesSource(Channel->Source()) && ProvidesIptv(Channel->PluginParam()));
|
2007-09-12 19:28:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool cIptvDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers) const
|
|
|
|
{
|
|
|
|
bool result = false;
|
|
|
|
bool needsDetachReceivers = false;
|
|
|
|
|
2007-09-12 20:55:31 +02:00
|
|
|
debug("cIptvDevice::ProvidesChannel(%d)\n", deviceIndex);
|
2007-09-12 19:28:59 +02:00
|
|
|
if (ProvidesTransponder(Channel))
|
|
|
|
result = true;
|
|
|
|
if (NeedsDetachReceivers)
|
|
|
|
*NeedsDetachReceivers = needsDetachReceivers;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cIptvDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
|
|
|
|
{
|
2007-09-14 17:44:25 +02:00
|
|
|
int port;
|
2007-09-13 20:14:41 +02:00
|
|
|
cString addr;
|
2007-09-14 17:44:25 +02:00
|
|
|
cIptvProtocolIf *protocol;
|
2007-09-12 19:28:59 +02:00
|
|
|
|
2007-09-12 20:55:31 +02:00
|
|
|
debug("cIptvDevice::SetChannelDevice(%d)\n", deviceIndex);
|
2007-09-18 20:48:10 +02:00
|
|
|
addr = GetChannelSettings(Channel->PluginParam(), &port, &protocol);
|
2007-09-14 23:36:05 +02:00
|
|
|
if (isempty(addr)) {
|
2007-09-18 20:48:10 +02:00
|
|
|
error("ERROR: Unrecognized IPTV channel settings: %s", Channel->PluginParam());
|
2007-09-14 23:36:05 +02:00
|
|
|
return false;
|
|
|
|
}
|
2007-09-15 23:27:00 +02:00
|
|
|
// pad prefill to multiple of TS_SIZE
|
2007-09-16 15:38:20 +02:00
|
|
|
tsBufferPrefill = MEGABYTE(IptvConfig.GetTsBufferSize()) *
|
|
|
|
IptvConfig.GetTsBufferPrefillRatio() / 100;
|
2007-09-15 23:27:00 +02:00
|
|
|
tsBufferPrefill -= (tsBufferPrefill % TS_SIZE);
|
2007-09-14 23:36:05 +02:00
|
|
|
pIptvStreamer->Set(addr, port, protocol);
|
2007-09-12 19:28:59 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cIptvDevice::SetPid(cPidHandle *Handle, int Type, bool On)
|
|
|
|
{
|
2007-09-19 20:02:38 +02:00
|
|
|
debug("cIptvDevice::SetPid(%d) Pid=%d Type=%d On=%d\n", deviceIndex, Handle->pid, Type, On);
|
2007-09-12 19:28:59 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2007-09-14 18:10:44 +02:00
|
|
|
int cIptvDevice::OpenFilter(u_short Pid, u_char Tid, u_char Mask)
|
|
|
|
{
|
2007-09-19 17:14:32 +02:00
|
|
|
// Search the next free filter slot
|
2007-09-19 20:02:38 +02:00
|
|
|
for (unsigned int i = 0; i < (sizeof(filters) / sizeof(filters[0])); ++i) {
|
|
|
|
if (!filters[i].active) {
|
|
|
|
debug("cIptvDevice::OpenFilter(): Pid=0x%X Tid=%02X Mask=%02X, filterCount = %d\n", Pid, Tid, Mask, i);
|
|
|
|
uint8_t mask = Mask;
|
|
|
|
uint8_t filt = Tid;
|
|
|
|
int err = set_trans_filt(&filter, i, Pid, &mask, &filt, 0);
|
|
|
|
if (err < 0)
|
|
|
|
error("Cannot set filter %d\n", i);
|
|
|
|
memset(filters[i].pipeName, '\0', sizeof(filters[i].pipeName));
|
|
|
|
snprintf(filters[i].pipeName, sizeof(filters[i].pipeName),
|
|
|
|
"/tmp/iptvPipe.%d", i);
|
|
|
|
err = mknod(filters[i].pipeName, 0644 | S_IFIFO, 0);
|
|
|
|
if (err < 0) {
|
|
|
|
char tmp[64];
|
|
|
|
error("ERROR: mknod(): %s", strerror_r(errno, tmp, sizeof(tmp)));
|
|
|
|
unlink(filters[i].pipeName);
|
|
|
|
}
|
|
|
|
// Create descriptors
|
|
|
|
int fifoDescriptor = open(filters[i].pipeName, O_RDWR | O_NONBLOCK);
|
|
|
|
int returnDescriptor = open(filters[i].pipeName, O_RDONLY | O_NONBLOCK);
|
|
|
|
// Store the write pipe and set active flag
|
|
|
|
filters[i].active = true;
|
|
|
|
filters[i].fifoDesc = fifoDescriptor;
|
|
|
|
return returnDescriptor;
|
|
|
|
}
|
|
|
|
}
|
2007-09-19 17:14:32 +02:00
|
|
|
// No free filter slot found
|
|
|
|
return -1;
|
2007-09-14 18:10:44 +02:00
|
|
|
}
|
|
|
|
|
2007-09-12 19:28:59 +02:00
|
|
|
bool cIptvDevice::OpenDvr(void)
|
|
|
|
{
|
2007-09-12 20:55:31 +02:00
|
|
|
debug("cIptvDevice::OpenDvr(%d)\n", deviceIndex);
|
2007-09-12 19:28:59 +02:00
|
|
|
mutex.Lock();
|
|
|
|
isPacketDelivered = false;
|
|
|
|
tsBuffer->Clear();
|
|
|
|
mutex.Unlock();
|
2007-09-14 17:44:25 +02:00
|
|
|
pIptvStreamer->Open();
|
2007-09-12 19:28:59 +02:00
|
|
|
isOpenDvr = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cIptvDevice::CloseDvr(void)
|
|
|
|
{
|
2007-09-12 20:55:31 +02:00
|
|
|
debug("cIptvDevice::CloseDvr(%d)\n", deviceIndex);
|
2007-09-14 17:44:25 +02:00
|
|
|
pIptvStreamer->Close();
|
2007-09-12 19:28:59 +02:00
|
|
|
isOpenDvr = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cIptvDevice::GetTSPacket(uchar *&Data)
|
|
|
|
{
|
|
|
|
int Count = 0;
|
2007-09-12 20:55:31 +02:00
|
|
|
//debug("cIptvDevice::GetTSPacket(%d)\n", deviceIndex);
|
2007-09-15 23:27:00 +02:00
|
|
|
if (tsBufferPrefill && tsBuffer->Available() < tsBufferPrefill)
|
|
|
|
return false;
|
|
|
|
else
|
|
|
|
tsBufferPrefill = 0;
|
2007-09-12 19:28:59 +02:00
|
|
|
if (isPacketDelivered) {
|
|
|
|
tsBuffer->Del(TS_SIZE);
|
|
|
|
isPacketDelivered = false;
|
|
|
|
}
|
|
|
|
uchar *p = tsBuffer->Get(Count);
|
|
|
|
if (p && Count >= TS_SIZE) {
|
|
|
|
if (*p != TS_SYNC_BYTE) {
|
|
|
|
for (int i = 1; i < Count; i++) {
|
|
|
|
if (p[i] == TS_SYNC_BYTE) {
|
|
|
|
Count = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tsBuffer->Del(Count);
|
|
|
|
error("ERROR: skipped %d bytes to sync on TS packet\n", Count);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
isPacketDelivered = true;
|
|
|
|
Data = p;
|
2007-09-19 17:14:32 +02:00
|
|
|
memcpy(filter.packet, p, sizeof(filter.packet));
|
2007-09-19 20:02:38 +02:00
|
|
|
trans_filt(p, TS_SIZE, &filter);
|
|
|
|
for (unsigned int i = 0; i < (sizeof(filters) / sizeof(filters[0])); ++i) {
|
|
|
|
if (filters[i].active) {
|
|
|
|
section *filtered = get_filt_sec(&filter, i);
|
|
|
|
if (filtered->found) {
|
|
|
|
// Select on the fifo emptyness. Using null timeout to return
|
|
|
|
// immediately
|
|
|
|
struct timeval tv;
|
|
|
|
tv.tv_sec = 0;
|
|
|
|
tv.tv_usec = 0;
|
|
|
|
fd_set rfds;
|
|
|
|
FD_ZERO(&rfds);
|
|
|
|
FD_SET(filters[i].fifoDesc, &rfds);
|
|
|
|
int retval = select(filters[i].fifoDesc + 1, &rfds, NULL, NULL,
|
|
|
|
&tv);
|
|
|
|
// Check if error
|
|
|
|
if (retval < 0) {
|
|
|
|
char tmp[64];
|
|
|
|
error("ERROR: select(): %s", strerror_r(errno, tmp, sizeof(tmp)));
|
|
|
|
// VDR has probably closed the filter file descriptor, so
|
|
|
|
// clear the filter
|
|
|
|
close(filters[i].fifoDesc);
|
|
|
|
unlink(filters[i].pipeName);
|
|
|
|
memset(filters[i].pipeName, '\0', sizeof(filters[i].pipeName));
|
|
|
|
filters[i].fifoDesc = -1;
|
|
|
|
filters[i].active = false;
|
|
|
|
clear_trans_filt(&filter, i);
|
|
|
|
}
|
|
|
|
// There is no data in the fifo, more can be written
|
|
|
|
if (!retval) {
|
|
|
|
int err = write(filters[i].fifoDesc, filtered->payload,
|
|
|
|
filtered->length+3);
|
|
|
|
if (err < 0) {
|
|
|
|
char tmp[64];
|
|
|
|
error("ERROR: write(): %s", strerror_r(errno, tmp,
|
|
|
|
sizeof(tmp)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2007-09-12 19:28:59 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
Data = NULL;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|