vdr-plugin-iptv/device.c

463 lines
13 KiB
C

/*
* device.c: IPTV plugin for the Video Disk Recorder
*
* See the README file for copyright information and how to reach the author.
*
*/
#include "config.h"
#include "source.h"
#include "device.h"
#define IPTV_MAX_DEVICES MAXDEVICES
static cIptvDevice * IptvDevicesS[IPTV_MAX_DEVICES] = { NULL };
cIptvDevice::cIptvDevice(unsigned int indexP)
: deviceIndexM(indexP),
dvrFdM(-1),
isPacketDeliveredM(false),
isOpenDvrM(false),
sidScanEnabledM(false),
pidScanEnabledM(false),
channelM()
{
unsigned int bufsize = (unsigned int)IPTV_BUFFER_SIZE;
bufsize -= (bufsize % TS_SIZE);
info("Creating IPTV device %d (CardIndex=%d)", deviceIndexM, CardIndex());
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());
}
pUdpProtocolM = new cIptvProtocolUdp();
pCurlProtocolM = new cIptvProtocolCurl();
pHttpProtocolM = new cIptvProtocolHttp();
pFileProtocolM = new cIptvProtocolFile();
pExtProtocolM = new cIptvProtocolExt();
pPidScannerM = new cPidScanner();
// Start section handler for iptv device
pIptvSectionM = new cIptvSectionFilterHandler(deviceIndexM, bufsize + 1);
StartSectionHandler();
// Sid scanner must be created after the section handler
AttachFilter(pSidScannerM = new cSidScanner());
// Check if dvr fifo exists
struct stat sb;
cString filename = cString::sprintf(IPTV_DVR_FILENAME, deviceIndexM);
stat(filename, &sb);
if (S_ISFIFO(sb.st_mode)) {
dvrFdM = open(filename, O_RDWR | O_NONBLOCK);
if (dvrFdM >= 0)
info("IPTV device %d redirecting input stream to '%s'", deviceIndexM, *filename);
}
}
cIptvDevice::~cIptvDevice()
{
debug1("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
// 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
if (dvrFdM >= 0) {
int fd = dvrFdM;
dvrFdM = -1;
close(fd);
}
}
bool cIptvDevice::Initialize(unsigned int deviceCountP)
{
debug1("%s (%u)", __PRETTY_FUNCTION__, deviceCountP);
new cIptvSourceParam(IPTV_SOURCE_CHARACTER, "IPTV");
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;
return true;
}
void cIptvDevice::Shutdown(void)
{
debug1("%s", __PRETTY_FUNCTION__);
for (int i = 0; i < IPTV_MAX_DEVICES; ++i) {
if (IptvDevicesS[i])
IptvDevicesS[i]->CloseDvr();
}
}
unsigned int cIptvDevice::Count(void)
{
unsigned int count = 0;
debug1("%s", __PRETTY_FUNCTION__);
for (unsigned int i = 0; i < IPTV_MAX_DEVICES; ++i) {
if (IptvDevicesS[i] != NULL)
count++;
}
return count;
}
cIptvDevice *cIptvDevice::GetIptvDevice(int cardIndexP)
{
debug16("%s (%d)", __PRETTY_FUNCTION__, cardIndexP);
for (unsigned int i = 0; i < IPTV_MAX_DEVICES; ++i) {
if (IptvDevicesS[i] && (IptvDevicesS[i]->CardIndex() == cardIndexP)) {
debug16("%s (%d) Found", __PRETTY_FUNCTION__, cardIndexP);
return IptvDevicesS[i];
}
}
return NULL;
}
cString cIptvDevice::GetGeneralInformation(void)
{
debug16("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
LOCK_CHANNELS_READ;
return cString::sprintf("IPTV device: %d\nCardIndex: %d\nStream: %s\nStream bitrate: %s\n%sChannel: %s",
deviceIndexM, CardIndex(),
pIptvStreamerM ? *pIptvStreamerM->GetInformation() : "",
pIptvStreamerM ? *pIptvStreamerM->GetStreamerStatistic() : "",
*GetBufferStatistic(),
*Channels->GetByNumber(cDevice::CurrentChannel())->ToText());
}
cString cIptvDevice::GetPidsInformation(void)
{
debug16("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
return GetPidStatistic();
}
cString cIptvDevice::GetFiltersInformation(void)
{
debug16("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
return cString::sprintf("Active section filters:\n%s", pIptvSectionM ? *pIptvSectionM->GetInformation() : "");
}
cString cIptvDevice::GetInformation(unsigned int pageP)
{
// generate information string
cString s;
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;
case IPTV_DEVICE_INFO_PROTOCOL:
s = pIptvStreamerM ? *pIptvStreamerM->GetInformation() : "";
break;
case IPTV_DEVICE_INFO_BITRATE:
s = pIptvStreamerM ? *pIptvStreamerM->GetStreamerStatistic() : "";
break;
default:
s = cString::sprintf("%s%s%s",
*GetGeneralInformation(),
*GetPidsInformation(),
*GetFiltersInformation());
break;
}
return s;
}
cString cIptvDevice::DeviceType(void) const
{
debug16("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
return "IPTV";
}
cString cIptvDevice::DeviceName(void) const
{
debug1("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
return cString::sprintf("IPTV %d", deviceIndexM);
}
int cIptvDevice::SignalStrength(void) const
{
debug1("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
return (100);
}
int cIptvDevice::SignalQuality(void) const
{
debug1("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
return (100);
}
bool cIptvDevice::ProvidesSource(int sourceP) const
{
debug1("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
return (cSource::IsType(sourceP, IPTV_SOURCE_CHARACTER));
}
bool cIptvDevice::ProvidesTransponder(const cChannel *channelP) const
{
debug1("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
return (ProvidesSource(channelP->Source()));
}
bool cIptvDevice::ProvidesChannel(const cChannel *channelP, int priorityP, bool *needsDetachReceiversP) const
{
bool result = false;
bool hasPriority = (priorityP == IDLEPRIORITY) || (priorityP > this->Priority());
bool needsDetachReceivers = false;
debug1("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
if (channelP && ProvidesTransponder(channelP)) {
result = hasPriority;
if (Receiving()) {
if (channelP->GetChannelID() == channelM.GetChannelID())
result = true;
else
needsDetachReceivers = Receiving();
}
}
if (needsDetachReceiversP)
*needsDetachReceiversP = needsDetachReceivers;
return result;
}
bool cIptvDevice::ProvidesEIT(void) const
{
return false;
}
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);
}
bool cIptvDevice::SetChannelDevice(const cChannel *channelP, bool liveViewP)
{
cIptvProtocolIf *protocol;
cIptvTransponderParameters itp(channelP->Parameters());
debug1("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
if (isempty(itp.Address())) {
error("Unrecognized IPTV address: %s", channelP->Parameters());
return false;
}
switch (itp.Protocol()) {
case cIptvTransponderParameters::eProtocolUDP:
protocol = pUdpProtocolM;
break;
case cIptvTransponderParameters::eProtocolCURL:
protocol = pCurlProtocolM;
break;
case cIptvTransponderParameters::eProtocolHTTP:
protocol = pHttpProtocolM;
break;
case cIptvTransponderParameters::eProtocolFILE:
protocol = pFileProtocolM;
break;
case cIptvTransponderParameters::eProtocolEXT:
protocol = pExtProtocolM;
break;
default:
error("Unrecognized IPTV protocol: %s", channelP->Parameters());
return false;
break;
}
sidScanEnabledM = itp.SidScan() ? true : false;
pidScanEnabledM = itp.PidScan() ? true : false;
if (pIptvStreamerM && pIptvStreamerM->SetSource(itp.Address(), itp.Parameter(), deviceIndexM, protocol)) {
channelM = *channelP;
if (sidScanEnabledM && pSidScannerM && IptvConfig.GetSectionFiltering())
pSidScannerM->SetChannel(channelM.GetChannelID());
if (pidScanEnabledM && pPidScannerM)
pPidScannerM->SetChannel(channelM.GetChannelID());
}
return true;
}
bool cIptvDevice::SetPid(cPidHandle *handleP, int typeP, bool onP)
{
debug1("%s (%d, %d, %d) [device %d]", __PRETTY_FUNCTION__, handleP ? handleP->pid : -1, typeP, onP, deviceIndexM);
if (pIptvStreamerM && handleP)
return pIptvStreamerM->SetPid(handleP->pid, typeP, onP);
return true;
}
int cIptvDevice::OpenFilter(u_short pidP, u_char tidP, u_char maskP)
{
debug16("%s (%d, %d, %d) [device %d]", __PRETTY_FUNCTION__, pidP, tidP, maskP, deviceIndexM);
if (pIptvSectionM && IptvConfig.GetSectionFiltering()) {
if (pIptvStreamerM)
pIptvStreamerM->SetPid(pidP, ptOther, true);
return pIptvSectionM->Open(pidP, tidP, maskP);
}
return -1;
}
void cIptvDevice::CloseFilter(int handleP)
{
debug16("%s (%d) [device %d]", __PRETTY_FUNCTION__, handleP, deviceIndexM);
if (pIptvSectionM) {
if (pIptvStreamerM)
pIptvStreamerM->SetPid(pIptvSectionM->GetPid(handleP), ptOther, false);
pIptvSectionM->Close(handleP);
}
}
bool cIptvDevice::OpenDvr(void)
{
debug1("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
isPacketDeliveredM = false;
tsBufferM->Clear();
if (pIptvStreamerM)
pIptvStreamerM->Open();
if (sidScanEnabledM && pSidScannerM && IptvConfig.GetSectionFiltering())
pSidScannerM->Open();
if (pidScanEnabledM && pPidScannerM)
pPidScannerM->Open();
isOpenDvrM = true;
return true;
}
void cIptvDevice::CloseDvr(void)
{
debug1("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
if (pidScanEnabledM && pPidScannerM)
pPidScannerM->Close();
if (sidScanEnabledM && pSidScannerM)
pSidScannerM->Close();
if (pIptvStreamerM)
pIptvStreamerM->Close();
isOpenDvrM = false;
}
bool cIptvDevice::HasLock(int timeoutMsP) const
{
debug16("%s (%d) [device %d]", __PRETTY_FUNCTION__, timeoutMsP, deviceIndexM);
return (pIptvStreamerM && pIptvStreamerM->Active());
}
bool cIptvDevice::HasInternalCam(void)
{
debug16("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
return false;
}
void cIptvDevice::WriteData(uchar *bufferP, int lengthP)
{
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)
{
debug16("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
if (tsBufferM)
return (unsigned int)tsBufferM->Free();
return 0;
}
uchar *cIptvDevice::GetData(int *availableP)
{
debug16("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
if (isOpenDvrM && tsBufferM) {
int count = 0;
if (isPacketDeliveredM)
SkipData(TS_SIZE);
uchar *p = tsBufferM->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;
}
}
tsBufferM->Del(count);
info("Skipped %d bytes to sync on TS packet", count);
return NULL;
}
isPacketDeliveredM = true;
if (availableP)
*availableP = count;
// Update pid statistics
AddPidStatistic(ts_pid(p), payload(p));
return p;
}
}
return NULL;
}
void cIptvDevice::SkipData(int countP)
{
debug16("%s (%d) [device %d]]", __PRETTY_FUNCTION__, countP, deviceIndexM);
tsBufferM->Del(countP);
isPacketDeliveredM = false;
// Update buffer statistics
AddBufferStatistic(countP, tsBufferM->Available());
}
bool cIptvDevice::GetTSPacket(uchar *&dataP)
{
debug16("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
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;
}
}
dataP = GetData();
return true;
}
// Reduce cpu load by preventing busylooping
cCondWait::SleepMs(10);
dataP = NULL;
return true;
}