vdr/nit.c
Klaus Schmidinger 3e4e4454fc Version 2.1.6
VDR developer version 2.1.6 is now available at

       ftp://ftp.tvdr.de/vdr/Developer/vdr-2.1.6.tar.bz2

A 'diff' against the previous version is available at

       ftp://ftp.tvdr.de/vdr/Developer/vdr-2.1.5-2.1.6.diff

MD5 checksums:

79519dac59166fabc2029b916bd61d00  vdr-2.1.6.tar.bz2
99f2f7094a242462696c0da5e52bb4c3  vdr-2.1.5-2.1.6.diff

WARNING:
========

This is a *developer* version. Even though *I* use it in my productive
environment, I strongly recommend that you only use it under controlled
conditions and for testing and debugging.

From the HISTORY file:
- Revoked "Fixed some compiler warnings with Clang 3.4.1" from ci.c, because this
  did not compile with older versions of gcc (thanks to Sören Moch).
- Fixed keeping the current position in the Recordings menu if a recording was
  deleted in a sub folder.
- Fixed handling transfer mode on full featured DVB cards for encrypted channels
  that have no audio pid (reported by Christian Winkler).
- Fixed a possible endless loop in cH264Parser::GetGolombUe(), which caused recordings
  on some HD channels to get stuck and resulted in buffer overflows.
- Fixed handling PAT packets when detecting frames, so that they can be properly
  taken into account when regenerating the index of a recording.
- Fixed adding new source types in case they are already registered (reported by Rolf
  Ahrenberg).
- Removed an unnecessary assignment from cMenuRecordings::~cMenuRecordings().
- The Recordings menu now remembers the last recording the cursor was positioned on,
  independent of the last replayed recording. When a replay ends, however, the cursor
  will initially be positioned to the last replayed recording again when the menu
  is opened.
- Updated the Finnish OSD texts (thanks to Antti Hartikainen).
- Fixed drawing the live indicator in the LCARS skin in case there are no devices.
- When checking for obsolete channels, those with an RID that is not 0 are now
  ignored (suggested by Oliver Endriss).
- The SDT is now only parsed *after* the NIT has been read, and it explicitly uses
  the source value derived from the NIT. This should prevent new channels from being
  created with the wrong source.
- Added a log message in case a receiver is detached from its device because the
  assigned CAM can't decrypt the channel.
- Refactored setup parameter handling for output devices:
  + The function cDevice::GetVideoSystem() has been deprecated and will be removed
    in a future version. In order to check whether a particular plugin needs to be
    modified if this function is removed, you can comment out the line
    #define DEPRECATED_VIDEOSYSTEM
    in device.h.
  + Handling the "video (display) format" (things like 16:9, 4:3, pan&scan, letterbox
    etc) shall now be done by the individual output devices, because the types and
    numbers of parameters are too device specific. The Setup/DVB parameters
    "Video format" and "Video display format" are still there for now and can be used
    by SD devices. HD devices, however, shall not use these parameters (any more),
    but rather implement their own setup menu with the necessary parameters for
    controlling output.
  + The dvbhdffdevice plugin has been modified accordingly.
  + Made it clear that cDevice::SetDigitalAudioDevice() merely tells the output device
    that the current audio track is Dolby Digital. This function was only used by the
    original "full featured" DVB cards - do not use it for new developments!
    If an output device has several ways of replaying audio (like HDMI or analog jack)
    it shall implement the proper options in its plugin's SetupMenu() function.
- Added support for "Pilot", "T2-System-Id" and "SISO/MISO" parameters (thanks to
  Rolf Ahrenberg).
- Now initializing the isOnVideoDirectoryFileSystem member of cRecording when
  scanning the video directory, so that it won't cause a delay when opening the menu
  on a system with a large number of recordings.
- Now resetting the isOnVideoDirectoryFileSystem member of a cRecording to -1 after
  renaming it, so that it will be re-checked upon the next call to
  IsOnVideoDirectoryFileSystem().
- Added support for systemd (thanks to Christopher Reimer). To activate this you
  need to add "SDNOTIFY=1" to the 'make' call.
2014-03-16 16:20:07 +01:00

367 lines
20 KiB
C

/*
* nit.c: NIT section filter
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: nit.c 3.3 2014/03/16 10:38:31 kls Exp $
*/
#include "nit.h"
#include <linux/dvb/frontend.h>
#include "channels.h"
#include "dvbdevice.h"
#include "eitscan.h"
#include "libsi/section.h"
#include "libsi/descriptor.h"
#include "tools.h"
#define DVB_SYSTEM_1 0 // see also dvbdevice.c
#define DVB_SYSTEM_2 1
cNitFilter::cNitFilter(cSdtFilter *SdtFilter)
{
sdtFilter = SdtFilter;
numNits = 0;
networkId = 0;
Set(0x10, 0x40); // NIT
}
void cNitFilter::SetStatus(bool On)
{
cFilter::SetStatus(On);
numNits = 0;
networkId = 0;
sectionSyncer.Reset();
}
void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
{
SI::NIT nit(Data, false);
if (!nit.CheckCRCAndParse())
return;
// Some broadcasters send more than one NIT, with no apparent way of telling which
// one is the right one to use. This is an attempt to find the NIT that contains
// the transponder it was transmitted on and use only that one:
int ThisNIT = -1;
if (!networkId) {
for (int i = 0; i < numNits; i++) {
if (nits[i].networkId == nit.getNetworkId()) {
if (nit.getSectionNumber() == 0) {
// all NITs have passed by
for (int j = 0; j < numNits; j++) {
if (nits[j].hasTransponder) {
networkId = nits[j].networkId;
//printf("taking NIT with network ID %d\n", networkId);
//XXX what if more than one NIT contains this transponder???
break;
}
}
if (!networkId) {
//printf("none of the NITs contains transponder %d\n", Transponder());
return;
}
}
else {
ThisNIT = i;
break;
}
}
}
if (!networkId && ThisNIT < 0 && numNits < MAXNITS) {
if (nit.getSectionNumber() == 0) {
*nits[numNits].name = 0;
SI::Descriptor *d;
for (SI::Loop::Iterator it; (d = nit.commonDescriptors.getNext(it)); ) {
switch (d->getDescriptorTag()) {
case SI::NetworkNameDescriptorTag: {
SI::NetworkNameDescriptor *nnd = (SI::NetworkNameDescriptor *)d;
nnd->name.getText(nits[numNits].name, MAXNETWORKNAME);
}
break;
default: ;
}
delete d;
}
nits[numNits].networkId = nit.getNetworkId();
nits[numNits].hasTransponder = false;
//printf("NIT[%d] %5d '%s'\n", numNits, nits[numNits].networkId, nits[numNits].name);
ThisNIT = numNits;
numNits++;
}
}
}
else if (networkId != nit.getNetworkId())
return; // ignore all other NITs
else if (!sectionSyncer.Sync(nit.getVersionNumber(), nit.getSectionNumber(), nit.getLastSectionNumber()))
return;
if (!Channels.Lock(true, 10))
return;
SI::NIT::TransportStream ts;
for (SI::Loop::Iterator it; nit.transportStreamLoop.getNext(ts, it); ) {
SI::Descriptor *d;
SI::Loop::Iterator it2;
SI::FrequencyListDescriptor *fld = (SI::FrequencyListDescriptor *)ts.transportStreamDescriptors.getNext(it2, SI::FrequencyListDescriptorTag);
int NumFrequencies = fld ? fld->frequencies.getCount() + 1 : 1;
int Frequencies[NumFrequencies];
if (fld) {
int ct = fld->getCodingType();
if (ct > 0) {
int n = 1;
for (SI::Loop::Iterator it3; fld->frequencies.hasNext(it3); ) {
int f = fld->frequencies.getNext(it3);
switch (ct) {
case 1: f = BCD2INT(f) / 100; break;
case 2: f = BCD2INT(f) / 10; break;
case 3: f = f * 10; break;
default: ;
}
Frequencies[n++] = f;
}
}
else
NumFrequencies = 1;
}
delete fld;
for (SI::Loop::Iterator it2; (d = ts.transportStreamDescriptors.getNext(it2)); ) {
switch (d->getDescriptorTag()) {
case SI::SatelliteDeliverySystemDescriptorTag: {
SI::SatelliteDeliverySystemDescriptor *sd = (SI::SatelliteDeliverySystemDescriptor *)d;
cDvbTransponderParameters dtp;
int Source = cSource::FromData(cSource::stSat, BCD2INT(sd->getOrbitalPosition()), sd->getWestEastFlag());
int Frequency = Frequencies[0] = BCD2INT(sd->getFrequency()) / 100;
static char Polarizations[] = { 'H', 'V', 'L', 'R' };
dtp.SetPolarization(Polarizations[sd->getPolarization()]);
static int CodeRates[] = { FEC_NONE, FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8, FEC_8_9, FEC_3_5, FEC_4_5, FEC_9_10, FEC_AUTO, FEC_AUTO, FEC_AUTO, FEC_AUTO, FEC_AUTO, FEC_NONE };
dtp.SetCoderateH(CodeRates[sd->getFecInner()]);
static int Modulations[] = { QAM_AUTO, QPSK, PSK_8, QAM_16 };
dtp.SetModulation(Modulations[sd->getModulationType()]);
dtp.SetSystem(sd->getModulationSystem() ? DVB_SYSTEM_2 : DVB_SYSTEM_1);
static int RollOffs[] = { ROLLOFF_35, ROLLOFF_25, ROLLOFF_20, ROLLOFF_AUTO };
dtp.SetRollOff(sd->getModulationSystem() ? RollOffs[sd->getRollOff()] : ROLLOFF_AUTO);
int SymbolRate = BCD2INT(sd->getSymbolRate()) / 10;
if (ThisNIT >= 0) {
for (int n = 0; n < NumFrequencies; n++) {
if (ISTRANSPONDER(cChannel::Transponder(Frequencies[n], dtp.Polarization()), Transponder())) {
nits[ThisNIT].hasTransponder = true;
//printf("has transponder %d\n", Transponder());
break;
}
}
break;
}
if (Setup.UpdateChannels >= 5) {
bool found = false;
bool forceTransponderUpdate = false;
for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) {
if (!Channel->GroupSep() && Channel->Source() == Source && Channel->Nid() == ts.getOriginalNetworkId() && Channel->Tid() == ts.getTransportStreamId()) {
int transponder = Channel->Transponder();
found = true;
if (!ISTRANSPONDER(cChannel::Transponder(Frequency, dtp.Polarization()), transponder)) {
for (int n = 0; n < NumFrequencies; n++) {
if (ISTRANSPONDER(cChannel::Transponder(Frequencies[n], dtp.Polarization()), transponder)) {
Frequency = Frequencies[n];
break;
}
}
}
if (ISTRANSPONDER(cChannel::Transponder(Frequency, dtp.Polarization()), Transponder())) // only modify channels if we're actually receiving this transponder
Channel->SetTransponderData(Source, Frequency, SymbolRate, dtp.ToString('S'));
else if (Channel->Srate() != SymbolRate || strcmp(Channel->Parameters(), dtp.ToString('S')))
forceTransponderUpdate = true; // get us receiving this transponder
}
}
if (!found || forceTransponderUpdate) {
for (int n = 0; n < NumFrequencies; n++) {
cChannel *Channel = new cChannel;
Channel->SetId(ts.getOriginalNetworkId(), ts.getTransportStreamId(), 0, 0);
if (Channel->SetTransponderData(Source, Frequencies[n], SymbolRate, dtp.ToString('S')))
EITScanner.AddTransponder(Channel);
else
delete Channel;
}
}
}
sdtFilter->Trigger(Source);
}
break;
case SI::S2SatelliteDeliverySystemDescriptorTag: {
if (Setup.UpdateChannels >= 5) {
for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) {
if (!Channel->GroupSep() && cSource::IsSat(Channel->Source()) && Channel->Nid() == ts.getOriginalNetworkId() && Channel->Tid() == ts.getTransportStreamId()) {
SI::S2SatelliteDeliverySystemDescriptor *sd = (SI::S2SatelliteDeliverySystemDescriptor *)d;
cDvbTransponderParameters dtp(Channel->Parameters());
dtp.SetSystem(DVB_SYSTEM_2);
dtp.SetStreamId(sd->getInputStreamIdentifier());
Channel->SetTransponderData(Channel->Source(), Channel->Frequency(), Channel->Srate(), dtp.ToString('S'));
break;
}
}
}
}
break;
case SI::CableDeliverySystemDescriptorTag: {
SI::CableDeliverySystemDescriptor *sd = (SI::CableDeliverySystemDescriptor *)d;
cDvbTransponderParameters dtp;
int Source = cSource::FromData(cSource::stCable);
int Frequency = Frequencies[0] = BCD2INT(sd->getFrequency()) / 10;
//XXX FEC_outer???
static int CodeRates[] = { FEC_NONE, FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8, FEC_8_9, FEC_3_5, FEC_4_5, FEC_9_10, FEC_AUTO, FEC_AUTO, FEC_AUTO, FEC_AUTO, FEC_AUTO, FEC_NONE };
dtp.SetCoderateH(CodeRates[sd->getFecInner()]);
static int Modulations[] = { QPSK, QAM_16, QAM_32, QAM_64, QAM_128, QAM_256, QAM_AUTO };
dtp.SetModulation(Modulations[min(sd->getModulation(), 6)]);
int SymbolRate = BCD2INT(sd->getSymbolRate()) / 10;
if (ThisNIT >= 0) {
for (int n = 0; n < NumFrequencies; n++) {
if (ISTRANSPONDER(Frequencies[n] / 1000, Transponder())) {
nits[ThisNIT].hasTransponder = true;
//printf("has transponder %d\n", Transponder());
break;
}
}
break;
}
if (Setup.UpdateChannels >= 5) {
bool found = false;
bool forceTransponderUpdate = false;
for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) {
if (!Channel->GroupSep() && Channel->Source() == Source && Channel->Nid() == ts.getOriginalNetworkId() && Channel->Tid() == ts.getTransportStreamId()) {
int transponder = Channel->Transponder();
found = true;
if (!ISTRANSPONDER(Frequency / 1000, transponder)) {
for (int n = 0; n < NumFrequencies; n++) {
if (ISTRANSPONDER(Frequencies[n] / 1000, transponder)) {
Frequency = Frequencies[n];
break;
}
}
}
if (ISTRANSPONDER(Frequency / 1000, Transponder())) // only modify channels if we're actually receiving this transponder
Channel->SetTransponderData(Source, Frequency, SymbolRate, dtp.ToString('C'));
else if (Channel->Srate() != SymbolRate || strcmp(Channel->Parameters(), dtp.ToString('C')))
forceTransponderUpdate = true; // get us receiving this transponder
}
}
if (!found || forceTransponderUpdate) {
for (int n = 0; n < NumFrequencies; n++) {
cChannel *Channel = new cChannel;
Channel->SetId(ts.getOriginalNetworkId(), ts.getTransportStreamId(), 0, 0);
if (Channel->SetTransponderData(Source, Frequencies[n], SymbolRate, dtp.ToString('C')))
EITScanner.AddTransponder(Channel);
else
delete Channel;
}
}
}
sdtFilter->Trigger(Source);
}
break;
case SI::TerrestrialDeliverySystemDescriptorTag: {
SI::TerrestrialDeliverySystemDescriptor *sd = (SI::TerrestrialDeliverySystemDescriptor *)d;
cDvbTransponderParameters dtp;
int Source = cSource::FromData(cSource::stTerr);
int Frequency = Frequencies[0] = sd->getFrequency() * 10;
static int Bandwidths[] = { 8000000, 7000000, 6000000, 5000000, 0, 0, 0, 0 };
dtp.SetBandwidth(Bandwidths[sd->getBandwidth()]);
static int Constellations[] = { QPSK, QAM_16, QAM_64, QAM_AUTO };
dtp.SetModulation(Constellations[sd->getConstellation()]);
dtp.SetSystem(DVB_SYSTEM_1);
static int Hierarchies[] = { HIERARCHY_NONE, HIERARCHY_1, HIERARCHY_2, HIERARCHY_4, HIERARCHY_AUTO, HIERARCHY_AUTO, HIERARCHY_AUTO, HIERARCHY_AUTO };
dtp.SetHierarchy(Hierarchies[sd->getHierarchy()]);
static int CodeRates[] = { FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8, FEC_AUTO, FEC_AUTO, FEC_AUTO };
dtp.SetCoderateH(CodeRates[sd->getCodeRateHP()]);
dtp.SetCoderateL(CodeRates[sd->getCodeRateLP()]);
static int GuardIntervals[] = { GUARD_INTERVAL_1_32, GUARD_INTERVAL_1_16, GUARD_INTERVAL_1_8, GUARD_INTERVAL_1_4 };
dtp.SetGuard(GuardIntervals[sd->getGuardInterval()]);
static int TransmissionModes[] = { TRANSMISSION_MODE_2K, TRANSMISSION_MODE_8K, TRANSMISSION_MODE_4K, TRANSMISSION_MODE_AUTO };
dtp.SetTransmission(TransmissionModes[sd->getTransmissionMode()]);
if (ThisNIT >= 0) {
for (int n = 0; n < NumFrequencies; n++) {
if (ISTRANSPONDER(Frequencies[n] / 1000000, Transponder())) {
nits[ThisNIT].hasTransponder = true;
//printf("has transponder %d\n", Transponder());
break;
}
}
break;
}
if (Setup.UpdateChannels >= 5) {
bool found = false;
bool forceTransponderUpdate = false;
for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) {
if (!Channel->GroupSep() && Channel->Source() == Source && Channel->Nid() == ts.getOriginalNetworkId() && Channel->Tid() == ts.getTransportStreamId()) {
int transponder = Channel->Transponder();
found = true;
if (!ISTRANSPONDER(Frequency / 1000000, transponder)) {
for (int n = 0; n < NumFrequencies; n++) {
if (ISTRANSPONDER(Frequencies[n] / 1000000, transponder)) {
Frequency = Frequencies[n];
break;
}
}
}
if (ISTRANSPONDER(Frequency / 1000000, Transponder())) // only modify channels if we're actually receiving this transponder
Channel->SetTransponderData(Source, Frequency, 0, dtp.ToString('T'));
else if (strcmp(Channel->Parameters(), dtp.ToString('T')))
forceTransponderUpdate = true; // get us receiving this transponder
}
}
if (!found || forceTransponderUpdate) {
for (int n = 0; n < NumFrequencies; n++) {
cChannel *Channel = new cChannel;
Channel->SetId(ts.getOriginalNetworkId(), ts.getTransportStreamId(), 0, 0);
if (Channel->SetTransponderData(Source, Frequencies[n], 0, dtp.ToString('T')))
EITScanner.AddTransponder(Channel);
else
delete Channel;
}
}
}
sdtFilter->Trigger(Source);
}
break;
case SI::ExtensionDescriptorTag: {
SI::ExtensionDescriptor *sd = (SI::ExtensionDescriptor *)d;
switch (sd->getExtensionDescriptorTag()) {
case SI::T2DeliverySystemDescriptorTag: {
if (Setup.UpdateChannels >= 5) {
for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) {
int Source = cSource::FromData(cSource::stTerr);
if (!Channel->GroupSep() && Channel->Source() == Source && Channel->Nid() == ts.getOriginalNetworkId() && Channel->Tid() == ts.getTransportStreamId()) {
SI::T2DeliverySystemDescriptor *td = (SI::T2DeliverySystemDescriptor *)d;
int Frequency = Channel->Frequency();
int SymbolRate = Channel->Srate();
cDvbTransponderParameters dtp(Channel->Parameters());
dtp.SetSystem(DVB_SYSTEM_2);
dtp.SetStreamId(td->getPlpId());
dtp.SetT2SystemId(td->getT2SystemId());
if (td->getExtendedDataFlag()) {
dtp.SetSisoMiso(td->getSisoMiso());
static int T2Bandwidths[] = { 8000000, 7000000, 6000000, 5000000, 10000000, 1712000, 0, 0 };
dtp.SetBandwidth(T2Bandwidths[td->getBandwidth()]);
static int T2GuardIntervals[] = { GUARD_INTERVAL_1_32, GUARD_INTERVAL_1_16, GUARD_INTERVAL_1_8, GUARD_INTERVAL_1_4, GUARD_INTERVAL_1_128, GUARD_INTERVAL_19_128, GUARD_INTERVAL_19_256, 0 };
dtp.SetGuard(T2GuardIntervals[td->getGuardInterval()]);
static int T2TransmissionModes[] = { TRANSMISSION_MODE_2K, TRANSMISSION_MODE_8K, TRANSMISSION_MODE_4K, TRANSMISSION_MODE_1K, TRANSMISSION_MODE_16K, TRANSMISSION_MODE_32K, TRANSMISSION_MODE_AUTO, TRANSMISSION_MODE_AUTO };
dtp.SetTransmission(T2TransmissionModes[td->getTransmissionMode()]);
//TODO add parsing of frequencies
}
Channel->SetTransponderData(Source, Frequency, SymbolRate, dtp.ToString('T'));
}
}
}
}
break;
default: ;
}
}
break;
default: ;
}
delete d;
}
}
Channels.Unlock();
}