vdr-plugin-iptv/device.c

463 lines
13 KiB
C
Raw Permalink Normal View History

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-15 17:38:38 +02:00
#include "config.h"
2010-03-04 16:34:21 +01:00
#include "source.h"
2007-09-12 19:28:59 +02:00
#include "device.h"
#define IPTV_MAX_DEVICES MAXDEVICES
2007-09-12 19:28:59 +02:00
2013-02-23 14:31:11 +01:00
static cIptvDevice * IptvDevicesS[IPTV_MAX_DEVICES] = { NULL };
2007-09-12 19:28:59 +02:00
2013-02-23 14:31:11 +01:00
cIptvDevice::cIptvDevice(unsigned int indexP)
: deviceIndexM(indexP),
dvrFdM(-1),
isPacketDeliveredM(false),
isOpenDvrM(false),
sidScanEnabledM(false),
pidScanEnabledM(false),
channelM()
2007-09-12 19:28:59 +02:00
{
unsigned int bufsize = (unsigned int)IPTV_BUFFER_SIZE;
2009-02-26 15:04:12 +01:00
bufsize -= (bufsize % TS_SIZE);
2015-03-08 13:33:18 +01:00
info("Creating IPTV device %d (CardIndex=%d)", deviceIndexM, CardIndex());
2013-02-23 14:31:11 +01:00
tsBufferM = new cRingBufferLinear(bufsize + 1, TS_SIZE, false,
*cString::sprintf("IPTV TS %d", deviceIndexM));
if (tsBufferM) {
tsBufferM->SetTimeouts(100, 100);
tsBufferM->SetIoThrottle();
pIptvStreamerM = new cIptvStreamer(*this, tsBufferM->Free());
}
2013-02-23 14:31:11 +01:00
pUdpProtocolM = new cIptvProtocolUdp();
pCurlProtocolM = new cIptvProtocolCurl();
pHttpProtocolM = new cIptvProtocolHttp();
pFileProtocolM = new cIptvProtocolFile();
pExtProtocolM = new cIptvProtocolExt();
pPidScannerM = new cPidScanner();
2007-10-01 20:14:57 +02:00
// Start section handler for iptv device
pIptvSectionM = new cIptvSectionFilterHandler(deviceIndexM, bufsize + 1);
2007-09-20 23:15:08 +02:00
StartSectionHandler();
2007-10-01 20:14:57 +02:00
// Sid scanner must be created after the section handler
2013-02-25 23:34:45 +01:00
AttachFilter(pSidScannerM = new cSidScanner());
// Check if dvr fifo exists
struct stat sb;
2013-02-23 14:31:11 +01:00
cString filename = cString::sprintf(IPTV_DVR_FILENAME, deviceIndexM);
stat(filename, &sb);
if (S_ISFIFO(sb.st_mode)) {
2013-02-23 14:31:11 +01:00
dvrFdM = open(filename, O_RDWR | O_NONBLOCK);
if (dvrFdM >= 0)
2015-03-08 13:33:18 +01:00
info("IPTV device %d redirecting input stream to '%s'", deviceIndexM, *filename);
}
2007-09-12 19:28:59 +02:00
}
cIptvDevice::~cIptvDevice()
{
2015-03-08 13:33:18 +01:00
debug1("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
2013-02-25 23:34:45 +01:00
// Stop section handler of iptv device
StopSectionHandler();
DELETE_POINTER(pIptvSectionM);
DELETE_POINTER(pSidScannerM);
DELETE_POINTER(pPidScannerM);
DELETE_POINTER(pIptvStreamerM);
DELETE_POINTER(pExtProtocolM);
DELETE_POINTER(pFileProtocolM);
DELETE_POINTER(pHttpProtocolM);
DELETE_POINTER(pCurlProtocolM);
DELETE_POINTER(pUdpProtocolM);
DELETE_POINTER(tsBufferM);
// Close dvr fifo
2013-02-23 14:31:11 +01:00
if (dvrFdM >= 0) {
int fd = dvrFdM;
dvrFdM = -1;
close(fd);
}
2007-09-12 19:28:59 +02:00
}
2013-02-23 14:31:11 +01:00
bool cIptvDevice::Initialize(unsigned int deviceCountP)
2007-09-12 19:28:59 +02:00
{
2015-03-08 13:33:18 +01:00
debug1("%s (%u)", __PRETTY_FUNCTION__, deviceCountP);
2010-03-04 16:34:21 +01:00
new cIptvSourceParam(IPTV_SOURCE_CHARACTER, "IPTV");
2013-02-23 14:31:11 +01:00
if (deviceCountP > IPTV_MAX_DEVICES)
deviceCountP = IPTV_MAX_DEVICES;
for (unsigned int i = 0; i < deviceCountP; ++i)
IptvDevicesS[i] = new cIptvDevice(i);
for (unsigned int i = deviceCountP; i < IPTV_MAX_DEVICES; ++i)
IptvDevicesS[i] = NULL;
2007-09-12 19:28:59 +02:00
return true;
}
void cIptvDevice::Shutdown(void)
{
2015-03-08 13:33:18 +01:00
debug1("%s", __PRETTY_FUNCTION__);
for (int i = 0; i < IPTV_MAX_DEVICES; ++i) {
if (IptvDevicesS[i])
IptvDevicesS[i]->CloseDvr();
}
}
2007-09-12 19:28:59 +02:00
unsigned int cIptvDevice::Count(void)
{
unsigned int count = 0;
2015-03-08 13:33:18 +01:00
debug1("%s", __PRETTY_FUNCTION__);
2007-09-12 19:28:59 +02:00
for (unsigned int i = 0; i < IPTV_MAX_DEVICES; ++i) {
2013-02-23 14:31:11 +01:00
if (IptvDevicesS[i] != NULL)
2007-09-12 19:28:59 +02:00
count++;
}
return count;
}
2013-02-23 14:31:11 +01:00
cIptvDevice *cIptvDevice::GetIptvDevice(int cardIndexP)
2007-09-12 19:28:59 +02:00
{
2015-03-08 13:33:18 +01:00
debug16("%s (%d)", __PRETTY_FUNCTION__, cardIndexP);
for (unsigned int i = 0; i < IPTV_MAX_DEVICES; ++i) {
if (IptvDevicesS[i] && (IptvDevicesS[i]->CardIndex() == cardIndexP)) {
2015-03-08 13:33:18 +01:00
debug16("%s (%d) Found", __PRETTY_FUNCTION__, cardIndexP);
2013-02-23 14:31:11 +01:00
return IptvDevicesS[i];
}
}
2007-09-12 19:28:59 +02:00
return NULL;
}
cString cIptvDevice::GetGeneralInformation(void)
{
2015-03-08 13:33:18 +01:00
debug16("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
2015-09-19 16:20:56 +02:00
LOCK_CHANNELS_READ;
2012-04-01 21:46:08 +02:00
return cString::sprintf("IPTV device: %d\nCardIndex: %d\nStream: %s\nStream bitrate: %s\n%sChannel: %s",
2013-02-23 14:31:11 +01:00
deviceIndexM, CardIndex(),
pIptvStreamerM ? *pIptvStreamerM->GetInformation() : "",
pIptvStreamerM ? *pIptvStreamerM->GetStreamerStatistic() : "",
*GetBufferStatistic(),
2015-09-19 16:20:56 +02:00
*Channels->GetByNumber(cDevice::CurrentChannel())->ToText());
}
cString cIptvDevice::GetPidsInformation(void)
{
2015-03-08 13:33:18 +01:00
debug16("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
return GetPidStatistic();
}
cString cIptvDevice::GetFiltersInformation(void)
{
2015-03-08 13:33:18 +01:00
debug16("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
return cString::sprintf("Active section filters:\n%s", pIptvSectionM ? *pIptvSectionM->GetInformation() : "");
}
2013-02-23 14:31:11 +01:00
cString cIptvDevice::GetInformation(unsigned int pageP)
{
2007-10-08 00:54:09 +02:00
// generate information string
cString s;
2013-02-23 14:31:11 +01:00
switch (pageP) {
case IPTV_DEVICE_INFO_GENERAL:
s = GetGeneralInformation();
break;
case IPTV_DEVICE_INFO_PIDS:
s = GetPidsInformation();
break;
case IPTV_DEVICE_INFO_FILTERS:
s = GetFiltersInformation();
break;
2012-04-01 21:46:08 +02:00
case IPTV_DEVICE_INFO_PROTOCOL:
2013-02-23 14:31:11 +01:00
s = pIptvStreamerM ? *pIptvStreamerM->GetInformation() : "";
2012-04-01 21:46:08 +02:00
break;
case IPTV_DEVICE_INFO_BITRATE:
2013-02-23 14:31:11 +01:00
s = pIptvStreamerM ? *pIptvStreamerM->GetStreamerStatistic() : "";
2012-04-01 21:46:08 +02:00
break;
default:
s = cString::sprintf("%s%s%s",
*GetGeneralInformation(),
*GetPidsInformation(),
*GetFiltersInformation());
break;
}
return s;
}
cString cIptvDevice::DeviceType(void) const
{
2015-03-08 13:33:18 +01:00
debug16("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
return "IPTV";
}
2012-03-25 15:42:06 +02:00
cString cIptvDevice::DeviceName(void) const
{
2015-03-08 13:33:18 +01:00
debug1("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
2013-02-23 14:31:11 +01:00
return cString::sprintf("IPTV %d", deviceIndexM);
2012-03-25 15:42:06 +02:00
}
2011-06-19 21:07:01 +02:00
int cIptvDevice::SignalStrength(void) const
{
2015-03-08 13:33:18 +01:00
debug1("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
2011-06-19 21:07:01 +02:00
return (100);
}
int cIptvDevice::SignalQuality(void) const
{
2015-03-08 13:33:18 +01:00
debug1("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
2011-06-19 21:07:01 +02:00
return (100);
}
2013-02-23 14:31:11 +01:00
bool cIptvDevice::ProvidesSource(int sourceP) const
2007-09-12 19:28:59 +02:00
{
2015-03-08 13:33:18 +01:00
debug1("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
2013-02-23 14:31:11 +01:00
return (cSource::IsType(sourceP, IPTV_SOURCE_CHARACTER));
2007-09-12 19:28:59 +02:00
}
2013-02-23 14:31:11 +01:00
bool cIptvDevice::ProvidesTransponder(const cChannel *channelP) const
2007-09-12 19:28:59 +02:00
{
2015-03-08 13:33:18 +01:00
debug1("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
2013-02-23 14:31:11 +01:00
return (ProvidesSource(channelP->Source()));
2007-09-12 19:28:59 +02:00
}
2013-02-23 14:31:11 +01:00
bool cIptvDevice::ProvidesChannel(const cChannel *channelP, int priorityP, bool *needsDetachReceiversP) const
2007-09-12 19:28:59 +02:00
{
bool result = false;
2013-02-23 14:31:11 +01:00
bool hasPriority = (priorityP == IDLEPRIORITY) || (priorityP > this->Priority());
2012-06-02 14:37:05 +02:00
bool needsDetachReceivers = false;
2007-09-12 19:28:59 +02:00
2015-03-08 13:33:18 +01:00
debug1("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
2012-06-02 14:37:05 +02:00
2013-02-23 14:31:11 +01:00
if (channelP && ProvidesTransponder(channelP)) {
2012-06-02 14:37:05 +02:00
result = hasPriority;
if (Receiving()) {
if (channelP->GetChannelID() == channelM.GetChannelID())
2012-06-02 14:37:05 +02:00
result = true;
else
needsDetachReceivers = Receiving();
}
}
2013-02-23 14:31:11 +01:00
if (needsDetachReceiversP)
*needsDetachReceiversP = needsDetachReceivers;
2007-09-12 19:28:59 +02:00
return result;
}
2011-09-04 16:58:18 +02:00
bool cIptvDevice::ProvidesEIT(void) const
{
return false;
}
2008-01-28 22:36:32 +01:00
int cIptvDevice::NumProvidedSystems(void) const
{
return 1;
}
const cChannel *cIptvDevice::GetCurrentlyTunedTransponder(void) const
{
return &channelM;
}
bool cIptvDevice::IsTunedToTransponder(const cChannel *channelP) const
{
return channelP ? (channelP->GetChannelID() == channelM.GetChannelID()) : false;
}
bool cIptvDevice::MaySwitchTransponder(const cChannel *channelP) const
{
return cDevice::MaySwitchTransponder(channelP);
}
2013-02-23 14:31:11 +01:00
bool cIptvDevice::SetChannelDevice(const cChannel *channelP, bool liveViewP)
2007-09-12 19:28:59 +02:00
{
2007-09-14 17:44:25 +02:00
cIptvProtocolIf *protocol;
2013-02-23 14:31:11 +01:00
cIptvTransponderParameters itp(channelP->Parameters());
2007-09-12 19:28:59 +02:00
2015-03-08 13:33:18 +01:00
debug1("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
2011-06-15 16:49:15 +02:00
2010-03-04 16:34:21 +01:00
if (isempty(itp.Address())) {
2013-02-23 14:31:11 +01:00
error("Unrecognized IPTV address: %s", channelP->Parameters());
return false;
}
2010-03-04 16:34:21 +01:00
switch (itp.Protocol()) {
case cIptvTransponderParameters::eProtocolUDP:
2013-02-23 14:31:11 +01:00
protocol = pUdpProtocolM;
2010-03-04 16:34:21 +01:00
break;
case cIptvTransponderParameters::eProtocolCURL:
2013-02-23 14:31:11 +01:00
protocol = pCurlProtocolM;
break;
2010-03-04 16:34:21 +01:00
case cIptvTransponderParameters::eProtocolHTTP:
2013-02-23 14:31:11 +01:00
protocol = pHttpProtocolM;
2010-03-04 16:34:21 +01:00
break;
case cIptvTransponderParameters::eProtocolFILE:
2013-02-23 14:31:11 +01:00
protocol = pFileProtocolM;
2010-03-04 16:34:21 +01:00
break;
case cIptvTransponderParameters::eProtocolEXT:
2013-02-23 14:31:11 +01:00
protocol = pExtProtocolM;
2010-03-04 16:34:21 +01:00
break;
default:
2013-02-23 14:31:11 +01:00
error("Unrecognized IPTV protocol: %s", channelP->Parameters());
2010-03-04 16:34:21 +01:00
return false;
break;
}
2013-02-23 14:31:11 +01:00
sidScanEnabledM = itp.SidScan() ? true : false;
pidScanEnabledM = itp.PidScan() ? true : false;
2014-02-09 18:22:02 +01:00
if (pIptvStreamerM && pIptvStreamerM->SetSource(itp.Address(), itp.Parameter(), deviceIndexM, protocol)) {
channelM = *channelP;
2013-02-23 14:31:11 +01:00
if (sidScanEnabledM && pSidScannerM && IptvConfig.GetSectionFiltering())
pSidScannerM->SetChannel(channelM.GetChannelID());
2013-02-23 14:31:11 +01:00
if (pidScanEnabledM && pPidScannerM)
pPidScannerM->SetChannel(channelM.GetChannelID());
}
2007-09-12 19:28:59 +02:00
return true;
}
2013-02-23 14:31:11 +01:00
bool cIptvDevice::SetPid(cPidHandle *handleP, int typeP, bool onP)
2007-09-12 19:28:59 +02:00
{
2015-03-08 13:33:18 +01:00
debug1("%s (%d, %d, %d) [device %d]", __PRETTY_FUNCTION__, handleP ? handleP->pid : -1, typeP, onP, deviceIndexM);
2014-02-09 18:22:02 +01:00
if (pIptvStreamerM && handleP)
return pIptvStreamerM->SetPid(handleP->pid, typeP, onP);
2007-09-12 19:28:59 +02:00
return true;
}
2013-02-23 14:31:11 +01:00
int cIptvDevice::OpenFilter(u_short pidP, u_char tidP, u_char maskP)
{
2015-03-08 13:33:18 +01:00
debug16("%s (%d, %d, %d) [device %d]", __PRETTY_FUNCTION__, pidP, tidP, maskP, deviceIndexM);
2014-02-09 18:22:02 +01:00
if (pIptvSectionM && IptvConfig.GetSectionFiltering()) {
if (pIptvStreamerM)
pIptvStreamerM->SetPid(pidP, ptOther, true);
return pIptvSectionM->Open(pidP, tidP, maskP);
2014-02-09 18:22:02 +01:00
}
return -1;
}
2013-02-23 14:31:11 +01:00
void cIptvDevice::CloseFilter(int handleP)
{
2015-03-08 13:33:18 +01:00
debug16("%s (%d) [device %d]", __PRETTY_FUNCTION__, handleP, deviceIndexM);
2014-02-09 18:22:02 +01:00
if (pIptvSectionM) {
if (pIptvStreamerM)
pIptvStreamerM->SetPid(pIptvSectionM->GetPid(handleP), ptOther, false);
pIptvSectionM->Close(handleP);
2014-02-09 18:22:02 +01:00
}
}
2007-09-12 19:28:59 +02:00
bool cIptvDevice::OpenDvr(void)
{
2015-03-08 13:33:18 +01:00
debug1("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
2013-02-23 14:31:11 +01:00
isPacketDeliveredM = false;
tsBufferM->Clear();
if (pIptvStreamerM)
pIptvStreamerM->Open();
if (sidScanEnabledM && pSidScannerM && IptvConfig.GetSectionFiltering())
pSidScannerM->Open();
if (pidScanEnabledM && pPidScannerM)
pPidScannerM->Open();
2013-02-23 14:31:11 +01:00
isOpenDvrM = true;
2007-09-12 19:28:59 +02:00
return true;
}
void cIptvDevice::CloseDvr(void)
{
2015-03-08 13:33:18 +01:00
debug1("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
if (pidScanEnabledM && pPidScannerM)
pPidScannerM->Close();
2014-01-07 12:17:30 +01:00
if (sidScanEnabledM && pSidScannerM)
2013-02-23 14:31:11 +01:00
pSidScannerM->Close();
if (pIptvStreamerM)
pIptvStreamerM->Close();
isOpenDvrM = false;
2007-09-12 19:28:59 +02:00
}
2013-02-23 14:31:11 +01:00
bool cIptvDevice::HasLock(int timeoutMsP) const
{
2015-03-08 13:33:18 +01:00
debug16("%s (%d) [device %d]", __PRETTY_FUNCTION__, timeoutMsP, deviceIndexM);
return (pIptvStreamerM && pIptvStreamerM->Active());
2007-09-21 23:50:52 +02:00
}
bool cIptvDevice::HasInternalCam(void)
{
2015-03-08 13:33:18 +01:00
debug16("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
2014-02-06 23:16:37 +01:00
return false;
}
void cIptvDevice::WriteData(uchar *bufferP, int lengthP)
{
2015-03-08 13:33:18 +01:00
debug16("%s (, %d) [device %d]", __PRETTY_FUNCTION__, lengthP, deviceIndexM);
int len;
// Send data to dvr fifo
if (dvrFdM >= 0)
len = write(dvrFdM, bufferP, lengthP);
// Fill up TS buffer
if (tsBufferM) {
len = tsBufferM->Put(bufferP, lengthP);
if (len != lengthP)
tsBufferM->ReportOverflow(lengthP - len);
}
// Filter the sections
if (pIptvSectionM && IptvConfig.GetSectionFiltering())
pIptvSectionM->Write(bufferP, lengthP);
}
unsigned int cIptvDevice::CheckData(void)
{
2015-03-08 13:33:18 +01:00
debug16("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
if (tsBufferM)
return (unsigned int)tsBufferM->Free();
return 0;
}
2014-01-28 16:10:11 +01:00
uchar *cIptvDevice::GetData(int *availableP)
2007-09-12 19:28:59 +02:00
{
2015-03-08 13:33:18 +01:00
debug16("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
if (isOpenDvrM && tsBufferM) {
2014-01-28 16:10:11 +01:00
int count = 0;
if (isPacketDeliveredM)
SkipData(TS_SIZE);
uchar *p = tsBufferM->Get(count);
if (p && count >= TS_SIZE) {
2007-09-21 23:50:52 +02:00
if (*p != TS_SYNC_BYTE) {
2014-01-28 16:10:11 +01:00
for (int i = 1; i < count; i++) {
2007-09-21 23:50:52 +02:00
if (p[i] == TS_SYNC_BYTE) {
2014-01-28 16:10:11 +01:00
count = i;
2007-09-21 23:50:52 +02:00
break;
2007-09-19 20:02:38 +02:00
}
2007-09-21 23:50:52 +02:00
}
2014-01-28 16:10:11 +01:00
tsBufferM->Del(count);
info("Skipped %d bytes to sync on TS packet", count);
2014-01-28 16:10:11 +01:00
return NULL;
2007-09-21 23:50:52 +02:00
}
2013-02-23 14:31:11 +01:00
isPacketDeliveredM = true;
2014-01-28 16:10:11 +01:00
if (availableP)
*availableP = count;
// Update pid statistics
AddPidStatistic(ts_pid(p), payload(p));
2014-01-28 16:10:11 +01:00
return p;
}
}
return NULL;
}
void cIptvDevice::SkipData(int countP)
{
2015-03-08 13:33:18 +01:00
debug16("%s (%d) [device %d]]", __PRETTY_FUNCTION__, countP, deviceIndexM);
2014-01-28 16:10:11 +01:00
tsBufferM->Del(countP);
isPacketDeliveredM = false;
// Update buffer statistics
AddBufferStatistic(countP, tsBufferM->Available());
}
bool cIptvDevice::GetTSPacket(uchar *&dataP)
{
2015-03-08 13:33:18 +01:00
debug16("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
2014-01-28 16:10:11 +01:00
if (tsBufferM) {
if (cCamSlot *cs = CamSlot()) {
if (cs->WantsTsData()) {
int available;
dataP = GetData(&available);
if (dataP) {
dataP = cs->Decrypt(dataP, available);
SkipData(available);
}
return true;
}
2007-09-21 23:50:52 +02:00
}
2014-01-28 16:10:11 +01:00
dataP = GetData();
return true;
2007-09-12 19:28:59 +02:00
}
2007-10-27 01:48:30 +02:00
// Reduce cpu load by preventing busylooping
2009-02-26 15:04:12 +01:00
cCondWait::SleepMs(10);
2014-01-28 16:10:11 +01:00
dataP = NULL;
2007-09-12 19:28:59 +02:00
return true;
}