diff --git a/device.c b/device.c index d806484..e86adb8 100644 --- a/device.c +++ b/device.c @@ -469,6 +469,13 @@ int cSatipDevice::GetCISlot(void) return slot; } +cString cSatipDevice::GetTnrParameterString(void) +{ + if (channelM.Ca()) + return GetTnrUrlParameters(&channelM); + return NULL; +} + bool cSatipDevice::IsIdle(void) { return !Receiving(); diff --git a/device.h b/device.h index ad18e2f..983e24d 100644 --- a/device.h +++ b/device.h @@ -110,6 +110,7 @@ public: virtual int GetId(void); virtual int GetPmtPid(void); virtual int GetCISlot(void); + virtual cString GetTnrParameterString(void); virtual bool IsIdle(void); }; diff --git a/deviceif.h b/deviceif.h index b67bfe1..9fe1636 100644 --- a/deviceif.h +++ b/deviceif.h @@ -16,6 +16,7 @@ public: virtual int GetId(void) = 0; virtual int GetPmtPid(void) = 0; virtual int GetCISlot(void) = 0; + virtual cString GetTnrParameterString(void) = 0; virtual bool IsIdle(void) = 0; private: diff --git a/param.c b/param.c index c9c4529..5104559 100644 --- a/param.c +++ b/param.c @@ -194,3 +194,313 @@ cString GetTransponderUrlParameters(const cChannel *channelP) } return NULL; } + +cString GetTnrUrlParameters(const cChannel *channelP) +{ + if (channelP) { + cDvbTransponderParameters dtp(channelP->Parameters()); + eTrackType track = cDevice::PrimaryDevice()->GetCurrentAudioTrack(); + + // TunerType: Byte; + // 0 = cable, 1 = satellite, 2 = terrestrial, 3 = atsc, 4 = iptv, 5 = stream (URL, DVBViewer GE) + int TunerType = 0; + if (channelP->IsCable()) + TunerType = 0; + else if (channelP->IsSat()) + TunerType = 1; + else if (channelP->IsTerr()) + TunerType = 2; + else if (channelP->IsAtsc()) + TunerType = 3; + + // Frequency: DWord; + // DVB-S: MHz if < 1000000, kHz if >= 1000000 + // DVB-T/C, ATSC: kHz + // IPTV: IP address Byte3.Byte2.Byte1.Byte0 + int Frequency = channelP->Frequency() / 1000; + + // Symbolrate: DWord; + // DVB S/C: in kSym/s + // DVB-T, ATSC: 0 + // IPTV: Port + int Symbolrate = (channelP->IsSat() || channelP->IsCable()) ? channelP->Srate() : 0; + + // LNB_LOF: Word; + // DVB-S: Local oscillator frequency of the LNB + // DVB-T/C, ATSC: 0 + // IPTV: Byte0 and Byte1 of Source IP + int LNB_LOF = channelP->IsSat() ? Setup.LnbSLOF : 0; + + // Tone: Byte; + // 0 = off, 1 = 22 khz + int Tone = (channelP->Frequency() < Setup.LnbSLOF) ? 0 : 1; + + // Polarity: Byte; + // DVB-S polarity: 0 = horizontal, 1 = vertical, 2 = circular left, 3 = circular right + // DVB-C modulation: 0 = Auto, 1 = 16QAM, 2 = 32QAM, 3 = 64QAM, 4 = 128QAM, 5 = 256 QAM + // DVB-T bandwidth: 0 = 6 MHz, 1 = 7 MHz, 2 = 8 MHz + // IPTV: Byte3 of SourceIP + int Polarity = 0; + if (channelP->IsSat()) { + switch (tolower(dtp.Polarization())) { + case 'h': + Polarity = 0; + break; + case 'v': + Polarity = 1; + break; + case 'l': + Polarity = 2; + break; + case 'r': + Polarity = 3; + break; + default: + break; + } + } + else if (channelP->IsCable()) { + switch (dtp.Modulation()) { + case 999: + Polarity = 0; + break; + case 16: + Polarity = 1; + break; + case 32: + Polarity = 2; + break; + case 64: + Polarity = 3; + break; + case 128: + Polarity = 4; + break; + case 256: + Polarity = 5; + break; + default: + break; + } + } + else if (channelP->IsTerr()) { + switch (dtp.Bandwidth()) { + case 6: + Polarity = 0; + break; + case 7: + Polarity = 1; + break; + case 8: + Polarity = 2; + break; + default: + break; + } + } + + // DiSEqC: Byte; + // 0 = None + // 1 = Pos A (mostly translated to PosA/OptA) + // 2 = Pos B (mostly translated to PosB/OptA) + // 3 = PosA/OptA + // 4 = PosB/OptA + // 5 = PosA/OptB + // 6 = PosB/OptB + // 7 = Preset Position (DiSEqC 1.2, see DiSEqCExt) + // 8 = Angular Position (DiSEqC 1.2, see DiSEqCExt) + // 9 = DiSEqC Command Sequence (see DiSEqCExt) + int DiSEqC = 0; + + // FEC: Byte; + // 0 = Auto + // 1 = 1/2 + // 2 = 2/3 + // 3 = 3/4 + // 4 = 5/6 + // 5 = 7/8 + // 6 = 8/9 + // 7 = 3/5 + // 8 = 4/5 + // 9 = 9/10 + // IPTV: Byte2 of SourceIP + // DVB C/T, ATSC: 0 + int FEC = 0; + if (channelP->IsSat()) { + switch (dtp.CoderateH()) { + case 999: + FEC = 0; + break; + case 12: + FEC = 1; + break; + case 23: + FEC = 2; + break; + case 34: + FEC = 3; + break; + case 56: + FEC = 4; + break; + case 78: + FEC = 5; + break; + case 89: + FEC = 6; + break; + case 35: + FEC = 7; + break; + case 45: + FEC = 8; + break; + case 910: + FEC = 9; + break; + default: + break; + } + } + + // Audio_PID: Word; + int Audio_PID = channelP->Apid(0); + if (IS_AUDIO_TRACK(track)) + Audio_PID = channelP->Apid(int(track - ttAudioFirst)); + else if (IS_DOLBY_TRACK(track)) + Audio_PID = channelP->Dpid(int(track - ttDolbyFirst)); + + // Video_PID: Word; + int Video_PID = channelP->Vpid(); + + // PMT_PID: Word; + int PMT_PID = channelP->Ppid(); + + // Service_ID: Word; + int Service_ID = channelP->Sid(); + + // SatModulation: Byte; + // Bit 0..1: satellite modulation. 0 = Auto, 1 = QPSK, 2 = 8PSK, 3 = 16QAM or APSK for DVB-S2 + // Bit 2: modulation system. 0 = DVB-S/T/C, 1 = DVB-S2/T2/C2 + // Bit 3..4: DVB-S2: roll-off. 0 = 0.35, 1 = 0.25, 2 = 0.20, 3 = reserved + // Bit 5..6: spectral inversion, 0 = undefined, 1 = auto, 2 = normal, 3 = inverted + // Bit 7: DVB-S2: pilot symbols, 0 = off, 1 = on + // DVB-T2: DVB-T2 Lite, 0 = off, 1 = on + int SatModulation = 0; + if (channelP->IsSat() && dtp.System()) { + switch (dtp.Modulation()) { + case 999: + SatModulation |= (0 & 0x3) << 0; + break; + case 2: + SatModulation |= (1 & 0x3) << 0; + break; + case 5: + SatModulation |= (2 & 0x3) << 0; + break; + case 6: + SatModulation |= (3 & 0x3) << 0; + break; + default: + break; + } + } + SatModulation |= (dtp.System() & 0x1) << 2; + if (channelP->IsSat() && dtp.System()) { + switch (dtp.RollOff()) { + case 35: + SatModulation |= (0 & 0x3) << 3; + break; + case 25: + SatModulation |= (1 & 0x3) << 3; + break; + case 20: + SatModulation |= (2 & 0x3) << 3; + break; + default: + break; + } + } + switch (dtp.Inversion()) { + case 999: + SatModulation |= (1 & 0x3) << 5; + break; + case 0: + SatModulation |= (2 & 0x3) << 5; + break; + case 1: + SatModulation |= (3 & 0x3) << 5; + break; + default: + break; + } + if (channelP->IsSat() && dtp.System()) { + switch (dtp.Pilot()) { + case 0: + SatModulation |= (0 & 0x1) << 7; + break; + case 1: + SatModulation |= (1 & 0x1) << 7; + break; + default: + break; + } + } + + // DiSEqCExt: Word; + // DiSEqC Extension, meaning depends on DiSEqC + // DiSEqC = 0..6: 0 + // DiSEqC = 7: Preset Position (DiSEqC 1.2) + // DiSEqC = 8: Orbital Position (DiSEqC 1.2, USALS, for calculating motor angle) + // Same format as OrbitalPos above + // DiSEQC = 9: Orbital Position referencing DiSEqC sequence defined in DiSEqC.xml/ini + // Same format as OrbitalPos above + int DiSEqCExt = 0; + + // Flags: Byte; + // Bit 0: 1 = encrypted channel + // Bit 1: reserved, set to 0 + // Bit 2: 1 = channel broadcasts RDS data + // Bit 3: 1 = channel is a video service (even if the Video PID is temporarily = 0) + // Bit 4: 1 = channel is an audio service (even if the Audio PID is temporarily = 0) + // Bit 5: 1 = audio has a different samplerate than 48 KHz + // Bit 6: 1 = bandstacking, internally polarisation is always set to H + // Bit 7: 1 = channel entry is an additional audio track of the preceding + // channel with bit 7 = 0 + int Flags = (channelP->Ca() > 0xFF) ? 1 : 0; + + // ChannelGroup: Byte; + // 0 = Group A, 1 = Group B, 2 = Group C etc. + int ChannelGroup = 0; + + // TransportStream_ID: Word; + int TransportStream_ID = channelP->Tid(); + + // OriginalNetwork_ID: Word; + int OriginalNetwork_ID = channelP->Nid(); + + // Substream: Word; + // DVB-S/C/T, ATSC, IPTV: 0 + // DVB-T2: 0 = PLP_ID not set, 1..256: PLP_ID + 1, 257... reserved + int Substream = (channelP->IsTerr() && dtp.System()) ? dtp.StreamId() - 1 : 0; + + // OrbitalPos: Word; + // DVB-S: orbital position x 10, 0 = undefined, 1..1800 east, 1801..3599 west (1°W = 3599) + // DVB-C: 4000..4999 + // DVB-T: 5000..5999 + // ATSC: 6000..6999 + // IPTV: 7000..7999 + // Stream: 8000..8999 + int OrbitalPos = 0; + if (channelP->IsSat()) { + OrbitalPos = cSource::Position(channelP->Source()); + if (OrbitalPos != 3600) + OrbitalPos += 1800; + } + + return cString::sprintf("%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + TunerType, Frequency, Symbolrate, LNB_LOF, Tone, Polarity, DiSEqC, FEC, Audio_PID, Video_PID, PMT_PID, Service_ID, + SatModulation, DiSEqCExt, Flags, ChannelGroup, TransportStream_ID, OriginalNetwork_ID, Substream, OrbitalPos); + } + return NULL; +} diff --git a/param.h b/param.h index ae68578..82de1b3 100644 --- a/param.h +++ b/param.h @@ -11,5 +11,6 @@ #include "common.h" cString GetTransponderUrlParameters(const cChannel *channelP); +cString GetTnrUrlParameters(const cChannel *channelP); #endif // __SATIP_PARAM_H diff --git a/server.c b/server.c index ad2af5c..c936752 100644 --- a/server.c +++ b/server.c @@ -146,11 +146,25 @@ cSatipServer::cSatipServer(const char *addressP, const int portP, const char *mo quirkM |= eSatipQuirkForceLock; quirksM = cString::sprintf("%s%sForceLock", *quirksM, isempty(*quirksM) ? "" : ","); } + // These devices support the X_PMT protocol extension + if (strstr(*descriptionM, "OctopusNet") || // Digital Devices OctopusNet + strstr(*descriptionM, "minisatip") // minisatip server + ) { + quirkM |= eSatipQuirkCiXpmt; + quirksM = cString::sprintf("%s%sCiXpmt", *quirksM, isempty(*quirksM) ? "" : ","); + } + // These devices support the TNR protocol extension + if (strstr(*descriptionM, "DVBViewer") // DVBViewer Media Server + ) { + quirkM |= eSatipQuirkCiTnr; + quirksM = cString::sprintf("%s%sCiTnr", *quirksM, isempty(*quirksM) ? "" : ","); + } debug3("%s description=%s quirks=%s", __PRETTY_FUNCTION__, *descriptionM, *quirksM); } - // These devices support the X_PMT protocol extension + // These devices support external CI if (strstr(*descriptionM, "OctopusNet") || // Digital Devices OctopusNet - strstr(*descriptionM, "minisatip") // minisatip server + strstr(*descriptionM, "minisatip") || // minisatip server + strstr(*descriptionM, "DVBViewer") // DVBViewer Media Server ) { hasCiM = true; } diff --git a/server.h b/server.h index 5afbc4e..1956013 100644 --- a/server.h +++ b/server.h @@ -79,7 +79,9 @@ public: eSatipQuirkPlayPids = 0x02, eSatipQuirkForceLock = 0x04, eSatipQuirkRtpOverTcp = 0x08, - eSatipQuirkMask = 0x0F + eSatipQuirkCiXpmt = 0x10, + eSatipQuirkCiTnr = 0x20, + eSatipQuirkMask = 0xFF }; cSatipServer(const char *addressP, const int portP, const char *modelP, const char *filtersP, const char *descriptionP); virtual ~cSatipServer(); diff --git a/tuner.c b/tuner.c index d49791a..bef87d4 100644 --- a/tuner.c +++ b/tuner.c @@ -25,6 +25,7 @@ cSatipTuner::cSatipTuner(cSatipDeviceIf &deviceP, unsigned int packetLenP) rtcpM(*this), streamAddrM(""), streamParamM(""), + tnrParamM(""), streamPortM(SATIP_DEFAULT_RTSP_PORT), currentServerM(NULL, deviceP.GetId(), 0), nextServerM(NULL, deviceP.GetId(), 0), @@ -210,6 +211,7 @@ bool cSatipTuner::Connect(void) if (!isempty(*streamAddrM)) { cString connectionUri = GetBaseUrl(*streamAddrM, streamPortM); + tnrParamM = ""; // Just retune if (streamIdM >= 0) { cString uri = cString::sprintf("%sstream=%d?%s", *connectionUri, streamIdM, *streamParamM); @@ -445,20 +447,30 @@ bool cSatipTuner::UpdatePids(bool forceP) uri = cString::sprintf("%s%sdelpids=%s", *uri, addPidsM.Size() ? "&" : "?", *delPidsM.ListPids()); } if (useci) { - // CI extension parameters: - // - x_pmt : specifies the PMT of the service you want the CI to decode - // - x_ci : specfies which CI slot (1..n) to use - // value 0 releases the CI slot - // CI slot released automatically if the stream is released, - // but not when used retuning to another channel - int pid = deviceM->GetPmtPid(); - if ((pid > 0) && (pid != pmtPidM)) { - int slot = deviceM->GetCISlot(); - uri = cString::sprintf("%s&x_pmt=%d", *uri, pid); - if (slot > 0) - uri = cString::sprintf("%s&x_ci=%d", *uri, slot); + if (currentServerM.IsQuirk(cSatipServer::eSatipQuirkCiXpmt)) { + // CI extension parameters: + // - x_pmt : specifies the PMT of the service you want the CI to decode + // - x_ci : specfies which CI slot (1..n) to use + // value 0 releases the CI slot + // CI slot released automatically if the stream is released, + // but not when used retuning to another channel + int pid = deviceM->GetPmtPid(); + if ((pid > 0) && (pid != pmtPidM)) { + int slot = deviceM->GetCISlot(); + uri = cString::sprintf("%s&x_pmt=%d", *uri, pid); + if (slot > 0) + uri = cString::sprintf("%s&x_ci=%d", *uri, slot); + } + pmtPidM = pid; + } + else if (currentServerM.IsQuirk(cSatipServer::eSatipQuirkCiTnr)) { + // CI extension parameters: + // - tnr : specifies a channel config entry + cString param = deviceM->GetTnrParameterString(); + if (!isempty(*param) && strcmp(*tnrParamM, *param) != 0) + uri = cString::sprintf("%s&tnr=%s", *uri, *param); + tnrParamM = param; } - pmtPidM = pid; } pidUpdateCacheM.Set(ePidUpdateIntervalMs); if (!rtspM.Play(*uri)) diff --git a/tuner.h b/tuner.h index 6c7c952..b70b1ac 100644 --- a/tuner.h +++ b/tuner.h @@ -99,6 +99,7 @@ private: cSatipRtcp rtcpM; cString streamAddrM; cString streamParamM; + cString tnrParamM; int streamPortM; cSatipTunerServer currentServerM; cSatipTunerServer nextServerM;