mirror of
https://github.com/vdr-projects/vdr.git
synced 2025-03-01 10:50:46 +00:00
- Updated the Russian OSD texts (thanks to Oleg Roitburd). - Fixed handling the 'pointer field' in generating and parsing PAT/PMT (thanks to Frank Schmirler). - Fixed handling modulation types for DVB-S transponders when processing the NIT. - Changed cDvbDevice::GrabImage() to use V4L2 (thanks to Marco Schlüßler). - Added a poll to cDvbDevice::PlayVideo() and cDvbDevice::PlayAudio() to avoid excessive CPU load (this is just a makeshift solution until the FF DVB cards can play TS directly). - The recording format is now Transport Stream. Existing recordings in PES format can still be replayed and edited, but new recordings are done in TS. All code for recording in PES has been removed. The following changes were made to switch to TS recording format: + The index file format has been changed to support file sizes of up to 1TB (previously 2GB), and up to 65535 separate files per recording (previously 255). + The recording file names are now of the form 00001.ts (previously 001.vdr). + The frame rate is now detected by looking at two subsequent PTS values. The "frame duration" (in multiples of 1/90000) is stored in the info.vdr file using the new tag F (thanks to Artur Skawina for helping to get the IndexToHMSF() calculation right). + Several functions now have an additional parameter FramesPerSecond. + Several functions now have an additional parameter IsPesRecording. + The functionality of cFileWriter was moved into cRecorder, and cRemux is now obsolete. This also avoids one level of data copying while recording. + cRemux, cRingBufferLinearPes, cTS2PES and all c*Repacker classes have been removed. + A PAT/PMT is inserted before every independent frame, so that no extra measures need to be taken when editing a recording. + The directory name for a recording has been changed from YYYY-MM-DD-hh[.:]mm.pr.lt.rec (pr=priority, lt=lifetime) to YYYY-MM-DD-hh.mm.ch-ri.rec (ch=channel, ri=resumeId). Priority and Lifetime are now stored in the info.vdr file with the new tags P and L (if no such file exists, the maximum values are assumed by default, which avoids inadvertently deleting a recording if disk space is low). No longer storing Priority and Lifetime in the directory name avoids starting a new recording if one of these is changed in the timer and the recording is re-started for some reason. Instead of Priority and Lifetime, the directory name now contains the channel number from which the recording was made, and the "resume id" of this instance of VDR. This avoids problems if several VDR instances record the same show on different channels, or even on the same channel. The '-' between channel number and resumeId prevents older versions of VDR from "seeing" these recordings, which makes sure they won't even try to replay them, or remove them in case the disk runs full. + The semantics of PlayTs*() have been changed. These functions are now required to return the given Length (which is TS_SIZE) if they have processed the TS packet. + The files "index", "info", "marks" and "resume" within a TS recording directory are now created without the ".vdr" extension. + The "resume" file is no longer a binary file, but contains tagged lines to be able to store additional information, like the selected audio or subtitle track. + cDevice::StillPicture() will now be called with either TS or PES data. + cDvbPlayer::Goto() no longer appends a "sequence end code" to the data. If the output device needs this, it has to take care of it by itself. - Fixed cPatPmtParser::ParsePmt() to reset vpid and vtype when switching from a video to an audio channel (thanks to Reinhard Nissl). - cDvbDevice now uses the FE_CAN_2G_MODULATION flag to determine whether a device can handle DVB-S2. The #define is still there to allow people with older drivers who don't need DVB-S2 to use this version without pathcing.
295 lines
15 KiB
C
295 lines
15 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 2.3 2008/12/20 10:57:50 kls Exp $
|
|
*/
|
|
|
|
#include "nit.h"
|
|
#include <linux/dvb/frontend.h>
|
|
#include "channels.h"
|
|
#include "eitscan.h"
|
|
#include "libsi/section.h"
|
|
#include "libsi/descriptor.h"
|
|
#include "tools.h"
|
|
|
|
cNitFilter::cNitFilter(void)
|
|
{
|
|
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;
|
|
}
|
|
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;
|
|
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' };
|
|
char Polarization = 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 };
|
|
int CodeRate = CodeRates[sd->getFecInner()];
|
|
static int Modulations[] = { QAM_AUTO, QPSK, PSK_8, QAM_16 };
|
|
int Modulation = Modulations[sd->getModulationType()];
|
|
int System = sd->getModulationSystem() ? SYS_DVBS2 : SYS_DVBS;
|
|
static int RollOffs[] = { ROLLOFF_35, ROLLOFF_25, ROLLOFF_20, ROLLOFF_AUTO };
|
|
int RollOff = 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], Polarization), Transponder())) {
|
|
nits[ThisNIT].hasTransponder = true;
|
|
//printf("has transponder %d\n", Transponder());
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
if (Setup.UpdateChannels >= 5) {
|
|
bool found = 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, Polarization), transponder)) {
|
|
for (int n = 0; n < NumFrequencies; n++) {
|
|
if (ISTRANSPONDER(cChannel::Transponder(Frequencies[n], Polarization), transponder)) {
|
|
Frequency = Frequencies[n];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (ISTRANSPONDER(cChannel::Transponder(Frequency, Polarization), Transponder())) // only modify channels if we're actually receiving this transponder
|
|
Channel->SetSatTransponderData(Source, Frequency, Polarization, SymbolRate, CodeRate, Modulation, System, RollOff);
|
|
}
|
|
}
|
|
if (!found) {
|
|
for (int n = 0; n < NumFrequencies; n++) {
|
|
cChannel *Channel = new cChannel;
|
|
Channel->SetId(ts.getOriginalNetworkId(), ts.getTransportStreamId(), 0, 0);
|
|
if (Channel->SetSatTransponderData(Source, Frequencies[n], Polarization, SymbolRate, CodeRate, Modulation, System, RollOff))
|
|
EITScanner.AddTransponder(Channel);
|
|
else
|
|
delete Channel;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case SI::CableDeliverySystemDescriptorTag: {
|
|
SI::CableDeliverySystemDescriptor *sd = (SI::CableDeliverySystemDescriptor *)d;
|
|
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 };
|
|
int CodeRate = CodeRates[sd->getFecInner()];
|
|
static int Modulations[] = { QPSK, QAM_16, QAM_32, QAM_64, QAM_128, QAM_256, QAM_AUTO };
|
|
int Modulation = 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;
|
|
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->SetCableTransponderData(Source, Frequency, Modulation, SymbolRate, CodeRate);
|
|
}
|
|
}
|
|
if (!found) {
|
|
for (int n = 0; n < NumFrequencies; n++) {
|
|
cChannel *Channel = new cChannel;
|
|
Channel->SetId(ts.getOriginalNetworkId(), ts.getTransportStreamId(), 0, 0);
|
|
if (Channel->SetCableTransponderData(Source, Frequencies[n], Modulation, SymbolRate, CodeRate))
|
|
EITScanner.AddTransponder(Channel);
|
|
else
|
|
delete Channel;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case SI::TerrestrialDeliverySystemDescriptorTag: {
|
|
SI::TerrestrialDeliverySystemDescriptor *sd = (SI::TerrestrialDeliverySystemDescriptor *)d;
|
|
int Source = cSource::FromData(cSource::stTerr);
|
|
int Frequency = Frequencies[0] = sd->getFrequency() * 10;
|
|
static int Bandwidths[] = { 8000000, 7000000, 6000000, 0, 0, 0, 0, 0 };
|
|
int Bandwidth = Bandwidths[sd->getBandwidth()];
|
|
static int Constellations[] = { QPSK, QAM_16, QAM_64, QAM_AUTO };
|
|
int Constellation = Constellations[sd->getConstellation()];
|
|
static int Hierarchies[] = { HIERARCHY_NONE, HIERARCHY_1, HIERARCHY_2, HIERARCHY_4, HIERARCHY_AUTO, HIERARCHY_AUTO, HIERARCHY_AUTO, HIERARCHY_AUTO };
|
|
int Hierarchy = 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 };
|
|
int CodeRateHP = CodeRates[sd->getCodeRateHP()];
|
|
int CodeRateLP = CodeRates[sd->getCodeRateLP()];
|
|
static int GuardIntervals[] = { GUARD_INTERVAL_1_32, GUARD_INTERVAL_1_16, GUARD_INTERVAL_1_8, GUARD_INTERVAL_1_4 };
|
|
int GuardInterval = GuardIntervals[sd->getGuardInterval()];
|
|
static int TransmissionModes[] = { TRANSMISSION_MODE_2K, TRANSMISSION_MODE_8K, TRANSMISSION_MODE_AUTO, TRANSMISSION_MODE_AUTO };
|
|
int TransmissionMode = 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;
|
|
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->SetTerrTransponderData(Source, Frequency, Bandwidth, Constellation, Hierarchy, CodeRateHP, CodeRateLP, GuardInterval, TransmissionMode);
|
|
}
|
|
}
|
|
if (!found) {
|
|
for (int n = 0; n < NumFrequencies; n++) {
|
|
cChannel *Channel = new cChannel;
|
|
Channel->SetId(ts.getOriginalNetworkId(), ts.getTransportStreamId(), 0, 0);
|
|
if (Channel->SetTerrTransponderData(Source, Frequencies[n], Bandwidth, Constellation, Hierarchy, CodeRateHP, CodeRateLP, GuardInterval, TransmissionMode))
|
|
EITScanner.AddTransponder(Channel);
|
|
else
|
|
delete Channel;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default: ;
|
|
}
|
|
delete d;
|
|
}
|
|
}
|
|
Channels.Unlock();
|
|
}
|