Example #1
0
 public void OnModulationMode(AtscTransmissionMedium transmissionMedium, byte index, TransmissionSystem transmissionSystem,
                              BinaryConvolutionCodeRate innerCodingMode, bool isSplitBitstreamMode, ModulationType modulationFormat, int symbolRate)
 {
     if (transmissionMedium != AtscTransmissionMedium.Cable)
     {
         return;
     }
     _modulationModes[index] = modulationFormat;
 }
Example #2
0
 public void OnCarrierDefinition(AtscTransmissionMedium transmissionMedium, byte index, int carrierFrequency)
 {
     if (transmissionMedium != AtscTransmissionMedium.Cable)
     {
         return;
     }
     if (carrierFrequency > 1750)
     {
         // Convert from centre frequency to the analog video carrier frequency.
         // This is a BDA convention.
         PhysicalChannel channel = new PhysicalChannel();
         channel.Frequency          = carrierFrequency - 1750;
         channel.Channel            = ATSCChannel.GetPhysicalChannelFromFrequency(carrierFrequency);
         _carrierFrequencies[index] = channel;
     }
 }
Example #3
0
        public void OnSourceName(AtscTransmissionMedium transmissionMedium, bool applicationType, int sourceId, string name)
        {
            if (transmissionMedium != AtscTransmissionMedium.Cable || applicationType)
            {
                return;
            }
            ATSCChannel channel = null;

            if (_channels.TryGetValue(sourceId, out channel))
            {
                channel.Name      = name;
                channel.NetworkId = sourceId;
                _sourcesWithoutNames.Remove(sourceId);
                if (_sourcesWithoutNames.Count == 0)
                {
                    Log.Log.Info("DRI CC: all sources now have names, assuming NTT is complete");
                    OnTableComplete(MgtTableType.NttSns);
                }
            }
        }
Example #4
0
        public void OnSvctChannelDetail(AtscTransmissionMedium transmissionMedium, int vctId, int virtualChannelNumber, bool applicationVirtualChannel,
                                        int bitstreamSelect, int pathSelect, ChannelType channelType, int sourceId, byte cdsReference, int programNumber, byte mmsReference)
        {
            if (transmissionMedium != AtscTransmissionMedium.Cable || applicationVirtualChannel || programNumber == 0 || sourceId == 0)
            {
                // Not tunable/supported.
                return;
            }

            ATSCChannel channel = null;

            if (_channels.TryGetValue(sourceId, out channel))
            {
                Log.Log.Info("DRI CC: received repeated S-VCT channel detail for source 0x{0:x}", sourceId);
                return;
            }

            channel = new ATSCChannel();
            _channels.Add(sourceId, channel);

            channel.LogicalChannelNumber = virtualChannelNumber;
            channel.IsTv            = true;
            channel.IsRadio         = false;
            channel.FreeToAir       = false;
            channel.Frequency       = _carrierFrequencies[cdsReference].Frequency;
            channel.MajorChannel    = virtualChannelNumber;
            channel.MinorChannel    = 0;
            channel.ModulationType  = _modulationModes[mmsReference];
            channel.NetworkId       = sourceId;
            channel.PhysicalChannel = _carrierFrequencies[cdsReference].Channel;
            channel.PmtPid          = 0; // TV Server will automatically lookup the correct PID from the PAT
            channel.Provider        = "Cable";
            channel.ServiceId       = programNumber;
            channel.TransportId     = 0; // We don't have these details.
            _sourcesWithoutNames.Add(sourceId);
        }
Example #5
0
        public void Decode(byte[] section)
        {
            if (OnTableComplete == null)
            {
                return;
            }
            if (section.Length < 14)
            {
                Log.Log.Error("NTT: invalid section size {0}, expected at least 14 bytes", section.Length);
                return;
            }

            byte tableId = section[2];

            if (tableId != 0xc3)
            {
                return;
            }
            int sectionLength = ((section[3] & 0x0f) << 8) + section[4];

            if (section.Length != 2 + sectionLength + 3) // 2 for section length bytes, 3 for table ID and PID
            {
                Log.Log.Error("NTT: invalid section length = {0}, byte count = {1}", sectionLength, section.Length);
                return;
            }
            int    protocolVersion = (section[5] & 0x1f);
            string isoLangCode     = System.Text.Encoding.ASCII.GetString(section, 6, 3);
            AtscTransmissionMedium transmissionMedium = (AtscTransmissionMedium)(section[9] >> 4);
            TableSubtype           tableSubtype       = (TableSubtype)(section[9] & 0x0f);

            Log.Log.Debug("NTT: section length = {0}, protocol version = {1}, ISO language code = {2}, transmission medium = {3}, table subtype = {4}",
                          sectionLength, protocolVersion, isoLangCode, transmissionMedium, tableSubtype);

            int pointer      = 10;
            int endOfSection = section.Length - 4;

            try
            {
                switch (tableSubtype)
                {
                case TableSubtype.TransponderName:
                    DecodeTransponderName(section, endOfSection, ref pointer);
                    break;

                case TableSubtype.SatelliteText:
                    DecodeSatelliteText(section, endOfSection, ref pointer);
                    break;

                case TableSubtype.RatingsText:
                    DecodeRatingsText(section, endOfSection, ref pointer);
                    break;

                case TableSubtype.RatingSystem:
                    DecodeRatingSystem(section, endOfSection, ref pointer);
                    break;

                case TableSubtype.CurrencySystem:
                    DecodeCurrencySystem(section, endOfSection, ref pointer);
                    break;

                case TableSubtype.SourceName:
                    DecodeSourceName(section, endOfSection, ref pointer, transmissionMedium);
                    break;

                case TableSubtype.MapName:
                    DecodeMapName(section, endOfSection, ref pointer);
                    break;

                default:
                    Log.Log.Error("NTT: unsupported table subtype {0}", tableSubtype);
                    return;
                }
            }
            catch (Exception ex)
            {
                Log.Log.Error(ex.Message);
                return;
            }

            while (pointer + 1 < endOfSection)
            {
                byte tag    = section[pointer++];
                byte length = section[pointer++];
                Log.Log.Debug("NTT: descriptor, tag = 0x{0:x}, length = {1}", tag, length);
                if (pointer + length > endOfSection)
                {
                    Log.Log.Error("NTT: invalid descriptor length {0}, pointer = {1}, end of section = {2}", length, pointer, endOfSection);
                    return;
                }

                if (tag == 0x93) // revision detection descriptor
                {
                    DecodeRevisionDetectionDescriptor(section, pointer, length, (int)tableSubtype);
                }

                pointer += length;
            }

            if (pointer != endOfSection)
            {
                Log.Log.Error("NTT: corruption detected at end of section, pointer = {0}, end of section = {1}", pointer, endOfSection);
                return;
            }

            if (tableSubtype == TableSubtype.SourceName &&
                _currentVersions[(int)TableSubtype.SourceName] != -1 &&
                _unseenSections[(int)TableSubtype.SourceName].Count == 0 &&
                OnTableComplete != null)
            {
                OnTableComplete(MgtTableType.NttSns);
                OnTableComplete = null;
                OnSourceName    = null;
            }
        }
Example #6
0
 public void OnCarrierDefinition(AtscTransmissionMedium transmissionMedium, byte index, int carrierFrequency)
 {
   if (transmissionMedium != AtscTransmissionMedium.Cable)
   {
     return;
   }
   if (carrierFrequency > 1750)
   {
     // Convert from centre frequency to the analog video carrier frequency.
     // This is a BDA convention.
     PhysicalChannel channel = new PhysicalChannel();
     channel.Frequency = carrierFrequency - 1750;
     channel.Channel = ATSCChannel.GetPhysicalChannelFromFrequency(carrierFrequency);
     _carrierFrequencies[index] = channel;
   }
 }
        private void DecodeModulationMode(byte[] section, int endOfSection, ref int pointer, ref byte firstIndex, AtscTransmissionMedium transmissionMedium)
        {
            if (pointer + 6 > endOfSection)
            {
                throw new Exception(string.Format("NIT: corruption detected at modulation mode, pointer = {0}, end of section = {1}", pointer, endOfSection));
            }
            TransmissionSystem        transmissionSystem = (TransmissionSystem)(section[pointer] >> 4);
            BinaryConvolutionCodeRate innerCodingMode    = BinaryConvolutionCodeRate.RateNotDefined;

            switch (section[pointer] & 0x0f)
            {
            case 0:
                innerCodingMode = BinaryConvolutionCodeRate.Rate5_11;
                break;

            case 1:
                innerCodingMode = BinaryConvolutionCodeRate.Rate1_2;
                break;

            case 3:
                innerCodingMode = BinaryConvolutionCodeRate.Rate3_5;
                break;

            case 5:
                innerCodingMode = BinaryConvolutionCodeRate.Rate2_3;
                break;

            case 7:
                innerCodingMode = BinaryConvolutionCodeRate.Rate3_4;
                break;

            case 8:
                innerCodingMode = BinaryConvolutionCodeRate.Rate4_5;
                break;

            case 9:
                innerCodingMode = BinaryConvolutionCodeRate.Rate5_6;
                break;

            case 11:
                innerCodingMode = BinaryConvolutionCodeRate.Rate7_8;
                break;

            case 15:
                // concatenated coding not used
                innerCodingMode = BinaryConvolutionCodeRate.RateNotSet;
                break;
            }
            pointer++;

            bool           isSplitBitstreamMode = ((section[pointer] & 0x80) != 0);
            ModulationType modulationFormat     = ModulationType.ModNotSet;

            switch (section[pointer] & 0x1f)
            {
            case 1:
                modulationFormat = ModulationType.ModQpsk;
                break;

            case 2:
                modulationFormat = ModulationType.ModBpsk;
                break;

            case 3:
                modulationFormat = ModulationType.ModOqpsk;
                break;

            case 4:
                modulationFormat = ModulationType.Mod8Vsb;
                break;

            case 5:
                modulationFormat = ModulationType.Mod16Vsb;
                break;

            case 6:
                modulationFormat = ModulationType.Mod16Qam;
                break;

            case 7:
                modulationFormat = ModulationType.Mod32Qam;
                break;

            case 8:
                modulationFormat = ModulationType.Mod64Qam;
                break;

            case 9:
                modulationFormat = ModulationType.Mod80Qam;
                break;

            case 10:
                modulationFormat = ModulationType.Mod96Qam;
                break;

            case 11:
                modulationFormat = ModulationType.Mod112Qam;
                break;

            case 12:
                modulationFormat = ModulationType.Mod128Qam;
                break;

            case 13:
                modulationFormat = ModulationType.Mod160Qam;
                break;

            case 14:
                modulationFormat = ModulationType.Mod192Qam;
                break;

            case 15:
                modulationFormat = ModulationType.Mod224Qam;
                break;

            case 16:
                modulationFormat = ModulationType.Mod256Qam;
                break;

            case 17:
                modulationFormat = ModulationType.Mod320Qam;
                break;

            case 18:
                modulationFormat = ModulationType.Mod384Qam;
                break;

            case 19:
                modulationFormat = ModulationType.Mod448Qam;
                break;

            case 20:
                modulationFormat = ModulationType.Mod512Qam;
                break;

            case 21:
                modulationFormat = ModulationType.Mod640Qam;
                break;

            case 22:
                modulationFormat = ModulationType.Mod768Qam;
                break;

            case 23:
                modulationFormat = ModulationType.Mod896Qam;
                break;

            case 24:
                modulationFormat = ModulationType.Mod1024Qam;
                break;
            }
            pointer++;

            // s/s
            int symbolRate = ((section[pointer] & 0x0f) << 24) + (section[pointer + 1] << 16) + (section[pointer + 2] << 8) + section[pointer + 3];

            pointer += 4;
            Log.Log.Debug("NIT: modulation mode, transmission system = {0}, inner coding mode = {1}, is split bitstream mode = {2}, modulation format = {3}, symbol rate = {4} s/s",
                          transmissionSystem, innerCodingMode, isSplitBitstreamMode, modulationFormat, symbolRate);

            if (OnModulationMode != null)
            {
                OnModulationMode(transmissionMedium, firstIndex++, transmissionSystem, innerCodingMode, isSplitBitstreamMode, modulationFormat, symbolRate);
            }
        }
        private void DecodeCarrierDefinition(byte[] section, int endOfSection, ref int pointer, ref byte firstIndex, AtscTransmissionMedium transmissionMedium)
        {
            if (pointer + 5 > endOfSection)
            {
                throw new Exception(string.Format("NIT: corruption detected at carrier definition, pointer = {0}, end of section = {1}", pointer, endOfSection));
            }
            byte numberOfCarriers = section[pointer++];
            int  spacingUnit      = 10; // kHz

            if ((section[pointer] & 0x80) != 0)
            {
                spacingUnit = 125;                                                                          // kHz
            }
            int frequencySpacing = spacingUnit * (((section[pointer] & 0x3f) << 8) + section[pointer + 1]); // kHz

            pointer += 2;

            int frequencyUnit = 10; // kHz

            if ((section[pointer] & 0x80) != 0)
            {
                frequencyUnit = 125;                                                                               // kHz
            }
            int firstCarrierFrequency = frequencyUnit * (((section[pointer] & 0x7f) << 8) + section[pointer + 1]); // kHz

            pointer += 2;
            Log.Log.Debug("NIT: carrier definition, number of carriers = {0}, spacing unit = {1} kHz, frequency spacing = {2} kHz, frequency unit = {3} kHz, first carrier frequency = {4} kHz",
                          numberOfCarriers, spacingUnit, frequencySpacing, frequencyUnit, firstCarrierFrequency);

            if (OnCarrierDefinition != null)
            {
                int carrierFrequency = firstCarrierFrequency;
                for (byte f = 0; f < numberOfCarriers; f++)
                {
                    OnCarrierDefinition(transmissionMedium, firstIndex++, carrierFrequency);
                    carrierFrequency += frequencySpacing;
                }
            }
        }
        public void Decode(byte[] section)
        {
            if (OnTableComplete == null)
            {
                return;
            }
            if (section.Length < 13)
            {
                Log.Log.Error("NIT: invalid section size {0}, expected at least 13 bytes", section.Length);
                return;
            }

            byte tableId = section[2];

            if (tableId != 0xc2)
            {
                return;
            }
            int sectionLength = ((section[3] & 0xf) << 8) + section[4];

            if (section.Length != 2 + sectionLength + 3) // 2 for section length bytes, 3 for table ID and PID
            {
                Log.Log.Error("NIT: invalid section length = {0}, byte count = {1}", sectionLength, section.Length);
                return;
            }
            int  protocolVersion = (section[5] & 0x1f);
            byte firstIndex      = section[6];
            byte numberOfRecords = section[7];
            AtscTransmissionMedium transmissionMedium = (AtscTransmissionMedium)(section[8] >> 4);
            TableSubtype           tableSubtype       = (TableSubtype)(section[8] & 0x0f);

            if ((tableSubtype != TableSubtype.CarrierDefinition || OnCarrierDefinition == null) &&
                (tableSubtype != TableSubtype.ModulationMode || OnModulationMode == null))
            {
                return;
            }

            int pointer      = 9;
            int endOfSection = section.Length - 4;

            byte satelliteId = 0;

            if (tableSubtype == TableSubtype.TransponderData)
            {
                if (pointer >= endOfSection)
                {
                    Log.Log.Error("NIT: invalid section length at satellite ID, pointer = {0}, end of section = {1}", pointer, endOfSection);
                    return;
                }
                satelliteId = section[pointer++];
            }
            Log.Log.Debug("NIT: section length = {0}, protocol version = {1}, first index = {2}, number of records = {3}, transmission medium = {4}, table subtype = {5}, satellite ID = {6}",
                          sectionLength, protocolVersion, firstIndex, numberOfRecords, transmissionMedium, tableSubtype, satelliteId);

            for (byte i = 0; i < numberOfRecords; i++)
            {
                try
                {
                    switch (tableSubtype)
                    {
                    case TableSubtype.CarrierDefinition:
                        DecodeCarrierDefinition(section, endOfSection, ref pointer, ref firstIndex, transmissionMedium);
                        break;

                    case TableSubtype.ModulationMode:
                        DecodeModulationMode(section, endOfSection, ref pointer, ref firstIndex, transmissionMedium);
                        break;

                    case TableSubtype.SatelliteInformation:
                        DecodeSatelliteInformation(section, endOfSection, ref pointer);
                        break;

                    case TableSubtype.TransponderData:
                        DecodeTransponderData(section, endOfSection, ref pointer);
                        break;

                    default:
                        Log.Log.Error("NIT: unsupported table subtype {0}", tableSubtype);
                        return;
                    }
                }
                catch (Exception ex)
                {
                    Log.Log.Error(ex.Message);
                    return;
                }

                // table descriptors
                if (pointer >= endOfSection)
                {
                    Log.Log.Error("NIT: invalid section length at table descriptor count, pointer = {0}, end of section = {1}, loop = {2}", pointer, endOfSection, i);
                    return;
                }
                byte descriptorCount = section[pointer++];
                for (byte d = 0; d < descriptorCount; d++)
                {
                    if (pointer + 2 > endOfSection)
                    {
                        Log.Log.Error("NIT: detected table descriptor count {0} is invalid, pointer = {1}, end of section = {2}, loop = {3}, inner loop = {4}", descriptorCount, pointer, endOfSection, i, d);
                        return;
                    }
                    byte tag    = section[pointer++];
                    byte length = section[pointer++];
                    Log.Log.Debug("NIT: table descriptor, tag = 0x{0:x}, length = {1}", tag, length);
                    if (pointer + length > endOfSection)
                    {
                        Log.Log.Error("NIT: invalid table descriptor length {0}, pointer = {1}, end of section = {2}, loop = {3}, inner loop = {4}", length, pointer, endOfSection, i, d);
                        return;
                    }
                    pointer += length;
                }
            }

            while (pointer + 1 < endOfSection)
            {
                byte tag    = section[pointer++];
                byte length = section[pointer++];
                Log.Log.Debug("NIT: descriptor, tag = 0x{0:x}, length = {1}", tag, length);
                if (pointer + length > endOfSection)
                {
                    Log.Log.Error("NIT: invalid descriptor length {0}, pointer = {1}, end of section = {2}", length, pointer, endOfSection);
                    return;
                }

                if (tag == 0x93)
                {
                    DecodeRevisionDetectionDescriptor(section, pointer, length, (int)tableSubtype);
                }

                pointer += length;
            }

            if (pointer != endOfSection)
            {
                Log.Log.Error("NIT: corruption detected at end of section, pointer = {0}, end of section = {1}", pointer, endOfSection);
                return;
            }

            if (tableSubtype == TableSubtype.CarrierDefinition &&
                (
                    _currentVersions[(int)TableSubtype.CarrierDefinition] == -1 ||
                    _unseenSections[(int)TableSubtype.CarrierDefinition].Count == 0
                ) &&
                OnCarrierDefinition != null)
            {
                OnCarrierDefinition = null;
                if (OnModulationMode == null && OnTableComplete != null)
                {
                    OnTableComplete(MgtTableType.NitCds);
                    OnTableComplete = null;
                }
            }
            else if (tableSubtype == TableSubtype.ModulationMode &&
                     (
                         _currentVersions[(int)TableSubtype.ModulationMode] == -1 ||
                         _unseenSections[(int)TableSubtype.ModulationMode].Count == 0
                     ) &&
                     OnModulationMode != null)
            {
                OnModulationMode = null;
                if (OnCarrierDefinition == null && OnTableComplete != null)
                {
                    OnTableComplete(MgtTableType.NitMms);
                    OnTableComplete = null;
                }
            }
        }
Example #10
0
    private void DecodeModulationMode(byte[] section, int endOfSection, ref int pointer, ref byte firstIndex, AtscTransmissionMedium transmissionMedium)
    {
      if (pointer + 6 > endOfSection)
      {
        throw new Exception(string.Format("NIT: corruption detected at modulation mode, pointer = {0}, end of section = {1}", pointer, endOfSection));
      }
      TransmissionSystem transmissionSystem = (TransmissionSystem)(section[pointer] >> 4);
      BinaryConvolutionCodeRate innerCodingMode = BinaryConvolutionCodeRate.RateNotDefined;
      switch (section[pointer] & 0x0f)
      {
        case 0:
          innerCodingMode = BinaryConvolutionCodeRate.Rate5_11;
          break;
        case 1:
          innerCodingMode = BinaryConvolutionCodeRate.Rate1_2;
          break;
        case 3:
          innerCodingMode = BinaryConvolutionCodeRate.Rate3_5;
          break;
        case 5:
          innerCodingMode = BinaryConvolutionCodeRate.Rate2_3;
          break;
        case 7:
          innerCodingMode = BinaryConvolutionCodeRate.Rate3_4;
          break;
        case 8:
          innerCodingMode = BinaryConvolutionCodeRate.Rate4_5;
          break;
        case 9:
          innerCodingMode = BinaryConvolutionCodeRate.Rate5_6;
          break;
        case 11:
          innerCodingMode = BinaryConvolutionCodeRate.Rate7_8;
          break;
        case 15:
          // concatenated coding not used
          innerCodingMode = BinaryConvolutionCodeRate.RateNotSet;
          break;
      }
      pointer++;

      bool isSplitBitstreamMode = ((section[pointer] & 0x80) != 0);
      ModulationType modulationFormat = ModulationType.ModNotSet;
      switch (section[pointer] & 0x1f)
      {
        case 1:
          modulationFormat = ModulationType.ModQpsk;
          break;
        case 2:
          modulationFormat = ModulationType.ModBpsk;
          break;
        case 3:
          modulationFormat = ModulationType.ModOqpsk;
          break;
        case 4:
          modulationFormat = ModulationType.Mod8Vsb;
          break;
        case 5:
          modulationFormat = ModulationType.Mod16Vsb;
          break;
        case 6:
          modulationFormat = ModulationType.Mod16Qam;
          break;
        case 7:
          modulationFormat = ModulationType.Mod32Qam;
          break;
        case 8:
          modulationFormat = ModulationType.Mod64Qam;
          break;
        case 9:
          modulationFormat = ModulationType.Mod80Qam;
          break;
        case 10:
          modulationFormat = ModulationType.Mod96Qam;
          break;
        case 11:
          modulationFormat = ModulationType.Mod112Qam;
          break;
        case 12:
          modulationFormat = ModulationType.Mod128Qam;
          break;
        case 13:
          modulationFormat = ModulationType.Mod160Qam;
          break;
        case 14:
          modulationFormat = ModulationType.Mod192Qam;
          break;
        case 15:
          modulationFormat = ModulationType.Mod224Qam;
          break;
        case 16:
          modulationFormat = ModulationType.Mod256Qam;
          break;
        case 17:
          modulationFormat = ModulationType.Mod320Qam;
          break;
        case 18:
          modulationFormat = ModulationType.Mod384Qam;
          break;
        case 19:
          modulationFormat = ModulationType.Mod448Qam;
          break;
        case 20:
          modulationFormat = ModulationType.Mod512Qam;
          break;
        case 21:
          modulationFormat = ModulationType.Mod640Qam;
          break;
        case 22:
          modulationFormat = ModulationType.Mod768Qam;
          break;
        case 23:
          modulationFormat = ModulationType.Mod896Qam;
          break;
        case 24:
          modulationFormat = ModulationType.Mod1024Qam;
          break;
      }
      pointer++;

      // s/s
      int symbolRate = ((section[pointer] & 0x0f) << 24) + (section[pointer + 1] << 16) + (section[pointer + 2] << 8) + section[pointer + 3];
      pointer += 4;
      Log.Log.Debug("NIT: modulation mode, transmission system = {0}, inner coding mode = {1}, is split bitstream mode = {2}, modulation format = {3}, symbol rate = {4} s/s",
        transmissionSystem, innerCodingMode, isSplitBitstreamMode, modulationFormat, symbolRate);

      if (OnModulationMode != null)
      {
        OnModulationMode(transmissionMedium, firstIndex++, transmissionSystem, innerCodingMode, isSplitBitstreamMode, modulationFormat, symbolRate);
      }
    }
Example #11
0
    private void DecodeCarrierDefinition(byte[] section, int endOfSection, ref int pointer, ref byte firstIndex, AtscTransmissionMedium transmissionMedium)
    {
      if (pointer + 5 > endOfSection)
      {
        throw new Exception(string.Format("NIT: corruption detected at carrier definition, pointer = {0}, end of section = {1}", pointer, endOfSection));
      }
      byte numberOfCarriers = section[pointer++];
      int spacingUnit = 10;   // kHz
      if ((section[pointer] & 0x80) != 0)
      {
        spacingUnit = 125;    // kHz
      }
      int frequencySpacing = spacingUnit * (((section[pointer] & 0x3f) << 8) + section[pointer + 1]);   // kHz
      pointer += 2;

      int frequencyUnit = 10; // kHz
      if ((section[pointer] & 0x80) != 0)
      {
        frequencyUnit = 125;  // kHz
      }
      int firstCarrierFrequency = frequencyUnit * (((section[pointer] & 0x7f) << 8) + section[pointer + 1]);  // kHz
      pointer += 2;
      Log.Log.Debug("NIT: carrier definition, number of carriers = {0}, spacing unit = {1} kHz, frequency spacing = {2} kHz, frequency unit = {3} kHz, first carrier frequency = {4} kHz",
        numberOfCarriers, spacingUnit, frequencySpacing, frequencyUnit, firstCarrierFrequency);

      if (OnCarrierDefinition != null)
      {
        int carrierFrequency = firstCarrierFrequency;
        for (byte f = 0; f < numberOfCarriers; f++)
        {
          OnCarrierDefinition(transmissionMedium, firstIndex++, carrierFrequency);
          carrierFrequency += frequencySpacing;
        }
      }
    }
Example #12
0
        public void Decode(byte[] section)
        {
            if (OnTableComplete == null)
            {
                return;
            }
            if (section.Length < 13)
            {
                Log.Log.Error("S-VCT: invalid section size {0}, expected at least 13 bytes", section.Length);
                return;
            }

            byte tableId = section[2];

            if (tableId != 0xc4)
            {
                return;
            }
            int sectionLength = ((section[3] & 0x0f) << 8) + section[4];

            if (section.Length != 2 + sectionLength + 3) // 2 for section length bytes, 3 for table ID and PID
            {
                Log.Log.Error("S-VCT: invalid section length = {0}, byte count = {1}", sectionLength, section.Length);
                return;
            }
            byte protocolVersion = (byte)(section[5] & 0x1f);
            AtscTransmissionMedium transmissionMedium = (AtscTransmissionMedium)(section[6] >> 4);
            TableSubtype           tableSubtype       = (TableSubtype)(section[6] & 0x0f);
            int vctId = (section[7] << 8) + section[8];

            Log.Log.Debug("S-VCT: section length = {0}, protocol version = {1}, transmission medium = {2}, table subtype = {3}, VCT ID = 0x{4:x}",
                          sectionLength, protocolVersion, transmissionMedium, tableSubtype, vctId);

            int pointer      = 9;
            int endOfSection = section.Length - 4;

            try
            {
                switch (tableSubtype)
                {
                case TableSubtype.DefinedChannelMap:
                    DecodeDefinedChannelMap(section, endOfSection, ref pointer);
                    break;

                case TableSubtype.VirtualChannelMap:
                    DecodeVirtualChannelMap(section, endOfSection, ref pointer, transmissionMedium, vctId);
                    break;

                case TableSubtype.InverseChannelMap:
                    DecodeInverseChannelMap(section, endOfSection, ref pointer);
                    break;

                default:
                    Log.Log.Error("S-VCT: unsupported table subtype {0}", tableSubtype);
                    return;
                }
            }
            catch (Exception ex)
            {
                Log.Log.Error(ex.Message);
                return;
            }

            while (pointer + 1 < endOfSection)
            {
                byte tag    = section[pointer++];
                byte length = section[pointer++];
                Log.Log.Debug("S-VCT: descriptor, tag = 0x{0:x}, length = {1}", tag, length);
                if (pointer + length > endOfSection)
                {
                    Log.Log.Error("S-VCT: invalid descriptor length {0}, pointer = {1}, end of section = {2}", length, pointer, endOfSection);
                    return;
                }

                if (tag == 0x93) // revision detection descriptor
                {
                    DecodeRevisionDetectionDescriptor(section, pointer, length, (int)tableSubtype);
                }

                pointer += length;
            }

            if (pointer != endOfSection)
            {
                Log.Log.Error("S-VCT: corruption detected at end of section, pointer = {0}, end of section = {1}", pointer, endOfSection);
                return;
            }

            // Two methods for detecting S-VCT VCM completion:
            // 1. Revision detection descriptors.
            // 2. DCM channel count equals VCM channel count.
            if (
                (
                    tableSubtype == TableSubtype.VirtualChannelMap &&
                    _currentVersions[(int)TableSubtype.VirtualChannelMap] != -1 &&
                    _unseenSections[(int)TableSubtype.VirtualChannelMap].Count == 0 &&
                    OnTableComplete != null
                ) ||
                (
                    (tableSubtype == TableSubtype.DefinedChannelMap || tableSubtype == TableSubtype.VirtualChannelMap) &&
                    _currentVersions[(int)TableSubtype.VirtualChannelMap] == -1 &&
                    _channelDefinitions.Count == _definedChannels.Count &&
                    OnTableComplete != null
                )
                )
            {
                OnTableComplete(MgtTableType.SvctVcm);
                OnTableComplete = null;
                OnChannelDetail = null;
            }
        }
Example #13
0
        private void DecodeSourceName(byte[] section, int endOfSection, ref int pointer, AtscTransmissionMedium transmissionMedium)
        {
            if (pointer >= endOfSection)
            {
                throw new Exception(string.Format("NTT: corruption detected at source name, pointer = {0}, end of section = {1}", pointer, endOfSection));
            }

            byte numberOfSntRecords = section[pointer++];

            for (byte i = 0; i < numberOfSntRecords; i++)
            {
                if (pointer + 5 > endOfSection)
                {
                    throw new Exception(string.Format("NTT: detected source name table number of SNT records {0} is invalid, pointer = {1}, end of section = {2}, loop = {3}", numberOfSntRecords, pointer, endOfSection, i));
                }
                bool applicationType = ((section[pointer++] & 0x80) != 0);
                int  sourceId        = (section[pointer] << 8) + section[pointer + 1];
                pointer += 2;
                byte nameLength = section[pointer++];
                if (pointer + nameLength > endOfSection)
                {
                    throw new Exception(string.Format("NTT: invalid source name table string length {0}, pointer = {1}, end of section = {2}, loop = {3}", nameLength, pointer, endOfSection, i));
                }
                string sourceName = DecodeMultilingualText(section, pointer + nameLength, ref pointer);
                Log.Log.Debug("NTT: source name, source ID = 0x{0:x}, name = {1}, application type = {2}", sourceId, sourceName, applicationType);
                if (OnSourceName != null)
                {
                    OnSourceName(transmissionMedium, applicationType, sourceId, sourceName);
                }

                // table descriptors
                if (pointer >= endOfSection)
                {
                    throw new Exception(string.Format("NTT: invalid section length at source name table descriptor count, pointer = {0}, end of section = {1}, loop = {2}", pointer, endOfSection, i));
                }
                byte descriptorCount = section[pointer++];
                for (byte d = 0; d < descriptorCount; d++)
                {
                    if (pointer + 2 > endOfSection)
                    {
                        throw new Exception(string.Format("NTT: detected source name table descriptor count {0} is invalid, pointer = {1}, end of section = {2}, loop = {3}, inner loop = {4}", descriptorCount, pointer, endOfSection, i, d));
                    }
                    byte tag    = section[pointer++];
                    byte length = section[pointer++];
                    Log.Log.Debug("NTT: source name table descriptor, tag = 0x{0:x}, length = {1}", tag, length);
                    if (pointer + length > endOfSection)
                    {
                        throw new Exception(string.Format("NTT: invalid source name table descriptor length {0}, pointer = {1}, end of section = {2}, loop = {3}, inner loop = {4}", length, pointer, endOfSection, i, d));
                    }
                    pointer += length;
                }
            }
        }
Example #14
0
    private void DecodeSourceName(byte[] section, int endOfSection, ref int pointer, AtscTransmissionMedium transmissionMedium)
    {
      if (pointer >= endOfSection)
      {
        throw new Exception(string.Format("NTT: corruption detected at source name, pointer = {0}, end of section = {1}", pointer, endOfSection));
      }

      byte numberOfSntRecords = section[pointer++];
      for (byte i = 0; i < numberOfSntRecords; i++)
      {
        if (pointer + 5 > endOfSection)
        {
          throw new Exception(string.Format("NTT: detected source name table number of SNT records {0} is invalid, pointer = {1}, end of section = {2}, loop = {3}", numberOfSntRecords, pointer, endOfSection, i));
        }
        bool applicationType = ((section[pointer++] & 0x80) != 0);
        int sourceId = (section[pointer] << 8) + section[pointer + 1];
        pointer += 2;
        byte nameLength = section[pointer++];
        if (pointer + nameLength > endOfSection)
        {
          throw new Exception(string.Format("NTT: invalid source name table string length {0}, pointer = {1}, end of section = {2}, loop = {3}", nameLength, pointer, endOfSection, i));
        }
        string sourceName = DecodeMultilingualText(section, pointer + nameLength, ref pointer);
        Log.Log.Debug("NTT: source name, source ID = 0x{0:x}, name = {1}, application type = {2}", sourceId, sourceName, applicationType);
        if (OnSourceName != null)
        {
          OnSourceName(transmissionMedium, applicationType, sourceId, sourceName);
        }

        // table descriptors
        if (pointer >= endOfSection)
        {
          throw new Exception(string.Format("NTT: invalid section length at source name table descriptor count, pointer = {0}, end of section = {1}, loop = {2}", pointer, endOfSection, i));
        }
        byte descriptorCount = section[pointer++];
        for (byte d = 0; d < descriptorCount; d++)
        {
          if (pointer + 2 > endOfSection)
          {
            throw new Exception(string.Format("NTT: detected source name table descriptor count {0} is invalid, pointer = {1}, end of section = {2}, loop = {3}, inner loop = {4}", descriptorCount, pointer, endOfSection, i, d));
          }
          byte tag = section[pointer++];
          byte length = section[pointer++];
          Log.Log.Debug("NTT: source name table descriptor, tag = 0x{0:x}, length = {1}", tag, length);
          if (pointer + length > endOfSection)
          {
            throw new Exception(string.Format("NTT: invalid source name table descriptor length {0}, pointer = {1}, end of section = {2}, loop = {3}, inner loop = {4}", length, pointer, endOfSection, i, d));
          }
          pointer += length;
        }
      }
    }
Example #15
0
 public void OnSourceName(AtscTransmissionMedium transmissionMedium, bool applicationType, int sourceId, string name)
 {
   if (transmissionMedium != AtscTransmissionMedium.Cable || applicationType)
   {
     return;
   }
   ATSCChannel channel = null;
   if (_channels.TryGetValue(sourceId, out channel))
   {
     channel.Name = name;
     channel.NetworkId = sourceId;
     _sourcesWithoutNames.Remove(sourceId);
     if (_sourcesWithoutNames.Count == 0)
     {
       Log.Log.Info("DRI CC: all sources now have names, assuming NTT is complete");
       OnTableComplete(MgtTableType.NttSns);
     }
   }
 }
Example #16
0
    public void OnSvctChannelDetail(AtscTransmissionMedium transmissionMedium, int vctId, int virtualChannelNumber, bool applicationVirtualChannel,
      int bitstreamSelect, int pathSelect, ChannelType channelType, int sourceId, byte cdsReference, int programNumber, byte mmsReference)
    {
      if (transmissionMedium != AtscTransmissionMedium.Cable || applicationVirtualChannel || programNumber == 0 || sourceId == 0)
      {
        // Not tunable/supported.
        return;
      }

      ATSCChannel channel = null;
      if (_channels.TryGetValue(sourceId, out channel))
      {
        Log.Log.Info("DRI CC: received repeated S-VCT channel detail for source 0x{0:x}", sourceId);
        return;
      }

      channel = new ATSCChannel();
      _channels.Add(sourceId, channel);

      channel.LogicalChannelNumber = virtualChannelNumber;
      channel.IsTv = true;
      channel.IsRadio = false;
      channel.FreeToAir = false;
      channel.Frequency = _carrierFrequencies[cdsReference].Frequency;
      channel.MajorChannel = virtualChannelNumber;
      channel.MinorChannel = 0;
      channel.ModulationType = _modulationModes[mmsReference];
      channel.NetworkId = sourceId;
      channel.PhysicalChannel = _carrierFrequencies[cdsReference].Channel;
      channel.PmtPid = 0;     // TV Server will automatically lookup the correct PID from the PAT
      channel.Provider = "Cable";
      channel.ServiceId = programNumber;
      channel.TransportId = 0;  // We don't have these details.
      _sourcesWithoutNames.Add(sourceId);
    }
Example #17
0
 public void OnModulationMode(AtscTransmissionMedium transmissionMedium, byte index, TransmissionSystem transmissionSystem,
   BinaryConvolutionCodeRate innerCodingMode, bool isSplitBitstreamMode, ModulationType modulationFormat, int symbolRate)
 {
   if (transmissionMedium != AtscTransmissionMedium.Cable)
   {
     return;
   }
   _modulationModes[index] = modulationFormat;
 }
Example #18
0
    private void DecodeVirtualChannelMap(byte[] section, int endOfSection, ref int pointer, AtscTransmissionMedium transmissionMedium, int vctId)
    {
      // Virtual channel formats depend on transmission medium.
      if (pointer + 7 > endOfSection)
      {
        throw new Exception(string.Format("S-VCT: corruption detected at virtual channel map, pointer = {0}, end of section = {1}", pointer, endOfSection));
      }

      bool freqSpecIncluded = ((section[pointer] & 0x80) != 0);
      bool symbolRateIncluded = ((section[pointer] & 0x40) != 0);
      bool descriptorsIncluded = ((section[pointer++] & 0x20) != 0);
      bool splice = ((section[pointer++] & 0x80) != 0);
      uint activationTime = 0;
      for (byte b = 0; b < 4; b++)
      {
        activationTime = activationTime << 8;
        activationTime = section[pointer++];
      }
      byte numberOfVcRecords = section[pointer++];
      Log.Log.Debug("S-VCT: virtual channel map, transmission medium = {0}, freq. spec. included = {1}, symbol rate included = {2}, descriptors included = {3}, splice = {4}, activation time = {5}, number of VC records = {6}",
        transmissionMedium, freqSpecIncluded, symbolRateIncluded, descriptorsIncluded, splice, activationTime, numberOfVcRecords);

      for (byte i = 0; i < numberOfVcRecords; i++)
      {
        if (pointer + 9 > endOfSection)
        {
          throw new Exception(string.Format("S-VCT: detected number of virtual channel records {0} is invalid, pointer = {1}, end of section = {2}, loop = {3}", numberOfVcRecords, pointer, endOfSection, i));
        }

        int virtualChannelNumber = ((section[pointer] & 0x0f) << 8) + section[pointer + 1];
        pointer += 2;
        _channelDefinitions.Add(virtualChannelNumber);
        bool applicationVirtualChannel = ((section[pointer] & 0x80) != 0);
        int bitstreamSelect = ((section[pointer] & 0x40) >> 6);   // broadcast reserved
        int pathSelect = ((section[pointer] & 0x20) >> 5);        // satellite, SMATV, broadcast reserved
        TransportType transportType = (TransportType)((section[pointer] & 0x10) >> 4);
        ChannelType channelType = (ChannelType)(section[pointer++] & 0x0f);
        int sourceId = (section[pointer] << 8) + section[pointer + 1];
        pointer += 2;
        Log.Log.Debug("S-VCT: virtual channel number = {0}, application virtual channel = {1}, bitstream select = {2}, path select = {3}, transport type = {4}, channel type = {5}, source ID = 0x{6:x}",
          virtualChannelNumber, applicationVirtualChannel, bitstreamSelect, pathSelect, transportType, channelType, sourceId);

        if (channelType == ChannelType.NvodAccess)
        {
          int nvodChannelBase = ((section[pointer] & 0x0f) << 8) + section[pointer + 1];
          pointer += 2;
          if (transmissionMedium == AtscTransmissionMedium.Smatv)
          {
            pointer += 3;
          }
          else if (transmissionMedium != AtscTransmissionMedium.OverTheAir)
          {
            pointer += 2;
          }
          Log.Log.Debug("S-VCT: NVOD channel base = 0x{0:x}", nvodChannelBase);
        }
        else
        {
          switch (transmissionMedium)
          {
            case AtscTransmissionMedium.Satellite:
              if (transportType == TransportType.Mpeg2)
              {
                byte satellite = section[pointer++];
                int transponder = (section[pointer++] & 0x3f);
                int programNumber = (section[pointer] << 8) + section[pointer + 1];
                pointer += 2;
                Log.Log.Debug("S-VCT: satellite = {0}, transponder = {1}, program number = 0x{2:x}", satellite, transponder, programNumber);
              }
              else
              {
                byte satellite = section[pointer++];
                int transponder = (section[pointer++] & 0x3f);
                pointer += 2;
                Log.Log.Debug("S-VCT: satellite = {0}, transponder = {1}", satellite, transponder);
              }
              break;
            case AtscTransmissionMedium.Smatv:
              if (transportType == TransportType.Mpeg2)
              {
                byte cdsReference = section[pointer++];
                int programNumber = (section[pointer] << 8) + section[pointer + 1];
                pointer += 2;
                byte mmsReference = section[pointer++];
                pointer++;
                Log.Log.Debug("S-VCT: CDS reference = {0}, program number = 0x{1:x}, MMS reference = {2}", cdsReference, programNumber, mmsReference);
              }
              else
              {
                byte cdsReference = section[pointer++];
                bool scrambled = ((section[pointer] & 0x80) != 0);
                VideoStandard videoStandard = (VideoStandard)(section[pointer++] & 0x0f);
                bool isWideBandwidthVideo = ((section[pointer] & 0x80) != 0);
                WaveformStandard waveformStandard = (WaveformStandard)(section[pointer++] & 0x1f);
                bool isWideBandwidthAudio = ((section[pointer] & 0x80) != 0);
                bool isCompandedAudio = ((section[pointer] & 0x40) != 0);
                MatrixMode matrixMode = (MatrixMode)((section[pointer] >> 4) & 0x03);
                int subcarrier2Offset = 10 * (((section[pointer] & 0x0f) << 6) + (section[pointer + 1] >> 2));  // kHz
                pointer++;
                int subcarrier1Offset = 10 * (((section[pointer] & 0x03) << 8) + section[pointer + 1]);
                pointer += 2;
                Log.Log.Debug("S-VCT: CDS reference = {0}, scrambled = {1}, video standard = {2}, is WB video = {3}, waveform standard = {4}, is WB audio = {5}, is companded audio = {6}, matrix mode = {7}, subcarrier 2 offset = {8} kHz, subcarrier 1 offset = {9} kHz",
                  cdsReference, scrambled, videoStandard, isWideBandwidthVideo, waveformStandard, isWideBandwidthAudio,
                  isCompandedAudio, matrixMode, subcarrier2Offset, subcarrier1Offset);
              }
              break;
            case AtscTransmissionMedium.OverTheAir:
              if (transportType == TransportType.Mpeg2)
              {
                int programNumber = (section[pointer] << 8) + section[pointer + 1];
                pointer += 2;
                Log.Log.Debug("S-VCT: program number = 0x{0:x}", programNumber);
              }
              else
              {
                bool scrambled = ((section[pointer] & 0x80) != 0);
                VideoStandard videoStandard = (VideoStandard)(section[pointer++] & 0x0f);
                pointer++;
                Log.Log.Debug("S-VCT: scrambled = {0}, video standard = {1}", scrambled, videoStandard);
              }
              break;
            case AtscTransmissionMedium.Cable:
            case AtscTransmissionMedium.Mmds:
              if (transportType == TransportType.Mpeg2)
              {
                byte cdsReference = section[pointer++];
                int programNumber = (section[pointer] << 8) + section[pointer + 1];
                pointer += 2;
                byte mmsReference = section[pointer++];
                Log.Log.Debug("S-VCT: CDS reference = {0}, program number = 0x{1:x}, MMS reference = {2}", cdsReference, programNumber, mmsReference);
                if (OnChannelDetail != null)
                {
                  OnChannelDetail(transmissionMedium, vctId, virtualChannelNumber, applicationVirtualChannel, bitstreamSelect,
                    pathSelect, channelType, sourceId, cdsReference, programNumber, mmsReference);
                }
              }
              else
              {
                byte cdsReference = section[pointer++];
                bool scrambled = ((section[pointer] & 0x80) != 0);
                VideoStandard videoStandard = (VideoStandard)(section[pointer++] & 0x0f);
                pointer += 2;
                Log.Log.Debug("S-VCT: CDS reference = {0}, scrambled = {1}, video standard = {2}", cdsReference, scrambled, videoStandard);
              }
              break;
            default:
              throw new Exception(string.Format("S-VCT: unsupported transmission medium {0}", transmissionMedium));
          }
        }

        if (freqSpecIncluded || transmissionMedium == AtscTransmissionMedium.OverTheAir)
        {
          int frequencyUnit = 10; // kHz
          if ((section[pointer] & 0x80) != 0)
          {
            frequencyUnit = 125;  // kHz
          }
          int carrierFrequency = frequencyUnit * (((section[pointer] & 0x7f) << 8) + section[pointer + 1]);  // kHz
          pointer += 2;
          Log.Log.Debug("S-VCT: frequency, unit = {0} kHz, carrier = {1} kHz", frequencyUnit, carrierFrequency);
        }
        if (symbolRateIncluded && transmissionMedium != AtscTransmissionMedium.OverTheAir)
        {
          // s/s
          int symbolRate = ((section[pointer] & 0x0f) << 24) + (section[pointer + 1] << 16) + (section[pointer + 2] << 8) + section[pointer + 3];
          pointer += 4;
          Log.Log.Debug("S-VCT: symbol rate = {0} s/s", symbolRate);
        }
        if (descriptorsIncluded)
        {
          if (pointer >= endOfSection)
          {
            throw new Exception(string.Format("S-VCT: invalid section length at virtual channel map descriptor count, pointer = {0}, end of section = {1}, loop = {2}", pointer, endOfSection, i));
          }
          byte descriptorCount = section[pointer++];
          for (byte d = 0; d < descriptorCount; d++)
          {
            if (pointer + 2 > endOfSection)
            {
              throw new Exception(string.Format("S-VCT: detected virtual channel map descriptor count {0} is invalid, pointer = {1}, end of section = {2}, loop = {3}, inner loop = {4}", descriptorCount, pointer, endOfSection, i, d));
            }
            byte tag = section[pointer++];
            byte length = section[pointer++];
            Log.Log.Debug("S-VCT: virtual channel map descriptor, tag = 0x{0:x}, length = {1}", tag, length);
            if (pointer + length > endOfSection)
            {
              throw new Exception(string.Format("S-VCT: invalid virtual channel map descriptor length {0}, pointer = {1}, end of section = {2}, loop = {3}, inner loop = {4}", length, pointer, endOfSection, i, d));
            }
            pointer += length;
          }
        }
      }
    }
Example #19
0
        private void DecodeVirtualChannelMap(byte[] section, int endOfSection, ref int pointer, AtscTransmissionMedium transmissionMedium, int vctId)
        {
            // Virtual channel formats depend on transmission medium.
            if (pointer + 7 > endOfSection)
            {
                throw new Exception(string.Format("S-VCT: corruption detected at virtual channel map, pointer = {0}, end of section = {1}", pointer, endOfSection));
            }

            bool freqSpecIncluded    = ((section[pointer] & 0x80) != 0);
            bool symbolRateIncluded  = ((section[pointer] & 0x40) != 0);
            bool descriptorsIncluded = ((section[pointer++] & 0x20) != 0);
            bool splice         = ((section[pointer++] & 0x80) != 0);
            uint activationTime = 0;

            for (byte b = 0; b < 4; b++)
            {
                activationTime = activationTime << 8;
                activationTime = section[pointer++];
            }
            byte numberOfVcRecords = section[pointer++];

            Log.Log.Debug("S-VCT: virtual channel map, transmission medium = {0}, freq. spec. included = {1}, symbol rate included = {2}, descriptors included = {3}, splice = {4}, activation time = {5}, number of VC records = {6}",
                          transmissionMedium, freqSpecIncluded, symbolRateIncluded, descriptorsIncluded, splice, activationTime, numberOfVcRecords);

            for (byte i = 0; i < numberOfVcRecords; i++)
            {
                if (pointer + 9 > endOfSection)
                {
                    throw new Exception(string.Format("S-VCT: detected number of virtual channel records {0} is invalid, pointer = {1}, end of section = {2}, loop = {3}", numberOfVcRecords, pointer, endOfSection, i));
                }

                int virtualChannelNumber = ((section[pointer] & 0x0f) << 8) + section[pointer + 1];
                pointer += 2;
                _channelDefinitions.Add(virtualChannelNumber);
                bool          applicationVirtualChannel = ((section[pointer] & 0x80) != 0);
                int           bitstreamSelect           = ((section[pointer] & 0x40) >> 6); // broadcast reserved
                int           pathSelect    = ((section[pointer] & 0x20) >> 5);             // satellite, SMATV, broadcast reserved
                TransportType transportType = (TransportType)((section[pointer] & 0x10) >> 4);
                ChannelType   channelType   = (ChannelType)(section[pointer++] & 0x0f);
                int           sourceId      = (section[pointer] << 8) + section[pointer + 1];
                pointer += 2;
                Log.Log.Debug("S-VCT: virtual channel number = {0}, application virtual channel = {1}, bitstream select = {2}, path select = {3}, transport type = {4}, channel type = {5}, source ID = 0x{6:x}",
                              virtualChannelNumber, applicationVirtualChannel, bitstreamSelect, pathSelect, transportType, channelType, sourceId);

                if (channelType == ChannelType.NvodAccess)
                {
                    int nvodChannelBase = ((section[pointer] & 0x0f) << 8) + section[pointer + 1];
                    pointer += 2;
                    if (transmissionMedium == AtscTransmissionMedium.Smatv)
                    {
                        pointer += 3;
                    }
                    else if (transmissionMedium != AtscTransmissionMedium.OverTheAir)
                    {
                        pointer += 2;
                    }
                    Log.Log.Debug("S-VCT: NVOD channel base = 0x{0:x}", nvodChannelBase);
                }
                else
                {
                    switch (transmissionMedium)
                    {
                    case AtscTransmissionMedium.Satellite:
                        if (transportType == TransportType.Mpeg2)
                        {
                            byte satellite     = section[pointer++];
                            int  transponder   = (section[pointer++] & 0x3f);
                            int  programNumber = (section[pointer] << 8) + section[pointer + 1];
                            pointer += 2;
                            Log.Log.Debug("S-VCT: satellite = {0}, transponder = {1}, program number = 0x{2:x}", satellite, transponder, programNumber);
                        }
                        else
                        {
                            byte satellite   = section[pointer++];
                            int  transponder = (section[pointer++] & 0x3f);
                            pointer += 2;
                            Log.Log.Debug("S-VCT: satellite = {0}, transponder = {1}", satellite, transponder);
                        }
                        break;

                    case AtscTransmissionMedium.Smatv:
                        if (transportType == TransportType.Mpeg2)
                        {
                            byte cdsReference  = section[pointer++];
                            int  programNumber = (section[pointer] << 8) + section[pointer + 1];
                            pointer += 2;
                            byte mmsReference = section[pointer++];
                            pointer++;
                            Log.Log.Debug("S-VCT: CDS reference = {0}, program number = 0x{1:x}, MMS reference = {2}", cdsReference, programNumber, mmsReference);
                        }
                        else
                        {
                            byte             cdsReference         = section[pointer++];
                            bool             scrambled            = ((section[pointer] & 0x80) != 0);
                            VideoStandard    videoStandard        = (VideoStandard)(section[pointer++] & 0x0f);
                            bool             isWideBandwidthVideo = ((section[pointer] & 0x80) != 0);
                            WaveformStandard waveformStandard     = (WaveformStandard)(section[pointer++] & 0x1f);
                            bool             isWideBandwidthAudio = ((section[pointer] & 0x80) != 0);
                            bool             isCompandedAudio     = ((section[pointer] & 0x40) != 0);
                            MatrixMode       matrixMode           = (MatrixMode)((section[pointer] >> 4) & 0x03);
                            int subcarrier2Offset = 10 * (((section[pointer] & 0x0f) << 6) + (section[pointer + 1] >> 2)); // kHz
                            pointer++;
                            int subcarrier1Offset = 10 * (((section[pointer] & 0x03) << 8) + section[pointer + 1]);
                            pointer += 2;
                            Log.Log.Debug("S-VCT: CDS reference = {0}, scrambled = {1}, video standard = {2}, is WB video = {3}, waveform standard = {4}, is WB audio = {5}, is companded audio = {6}, matrix mode = {7}, subcarrier 2 offset = {8} kHz, subcarrier 1 offset = {9} kHz",
                                          cdsReference, scrambled, videoStandard, isWideBandwidthVideo, waveformStandard, isWideBandwidthAudio,
                                          isCompandedAudio, matrixMode, subcarrier2Offset, subcarrier1Offset);
                        }
                        break;

                    case AtscTransmissionMedium.OverTheAir:
                        if (transportType == TransportType.Mpeg2)
                        {
                            int programNumber = (section[pointer] << 8) + section[pointer + 1];
                            pointer += 2;
                            Log.Log.Debug("S-VCT: program number = 0x{0:x}", programNumber);
                        }
                        else
                        {
                            bool          scrambled     = ((section[pointer] & 0x80) != 0);
                            VideoStandard videoStandard = (VideoStandard)(section[pointer++] & 0x0f);
                            pointer++;
                            Log.Log.Debug("S-VCT: scrambled = {0}, video standard = {1}", scrambled, videoStandard);
                        }
                        break;

                    case AtscTransmissionMedium.Cable:
                    case AtscTransmissionMedium.Mmds:
                        if (transportType == TransportType.Mpeg2)
                        {
                            byte cdsReference  = section[pointer++];
                            int  programNumber = (section[pointer] << 8) + section[pointer + 1];
                            pointer += 2;
                            byte mmsReference = section[pointer++];
                            Log.Log.Debug("S-VCT: CDS reference = {0}, program number = 0x{1:x}, MMS reference = {2}", cdsReference, programNumber, mmsReference);
                            if (OnChannelDetail != null)
                            {
                                OnChannelDetail(transmissionMedium, vctId, virtualChannelNumber, applicationVirtualChannel, bitstreamSelect,
                                                pathSelect, channelType, sourceId, cdsReference, programNumber, mmsReference);
                            }
                        }
                        else
                        {
                            byte          cdsReference  = section[pointer++];
                            bool          scrambled     = ((section[pointer] & 0x80) != 0);
                            VideoStandard videoStandard = (VideoStandard)(section[pointer++] & 0x0f);
                            pointer += 2;
                            Log.Log.Debug("S-VCT: CDS reference = {0}, scrambled = {1}, video standard = {2}", cdsReference, scrambled, videoStandard);
                        }
                        break;

                    default:
                        throw new Exception(string.Format("S-VCT: unsupported transmission medium {0}", transmissionMedium));
                    }
                }

                if (freqSpecIncluded || transmissionMedium == AtscTransmissionMedium.OverTheAir)
                {
                    int frequencyUnit = 10; // kHz
                    if ((section[pointer] & 0x80) != 0)
                    {
                        frequencyUnit = 125;                                                                          // kHz
                    }
                    int carrierFrequency = frequencyUnit * (((section[pointer] & 0x7f) << 8) + section[pointer + 1]); // kHz
                    pointer += 2;
                    Log.Log.Debug("S-VCT: frequency, unit = {0} kHz, carrier = {1} kHz", frequencyUnit, carrierFrequency);
                }
                if (symbolRateIncluded && transmissionMedium != AtscTransmissionMedium.OverTheAir)
                {
                    // s/s
                    int symbolRate = ((section[pointer] & 0x0f) << 24) + (section[pointer + 1] << 16) + (section[pointer + 2] << 8) + section[pointer + 3];
                    pointer += 4;
                    Log.Log.Debug("S-VCT: symbol rate = {0} s/s", symbolRate);
                }
                if (descriptorsIncluded)
                {
                    if (pointer >= endOfSection)
                    {
                        throw new Exception(string.Format("S-VCT: invalid section length at virtual channel map descriptor count, pointer = {0}, end of section = {1}, loop = {2}", pointer, endOfSection, i));
                    }
                    byte descriptorCount = section[pointer++];
                    for (byte d = 0; d < descriptorCount; d++)
                    {
                        if (pointer + 2 > endOfSection)
                        {
                            throw new Exception(string.Format("S-VCT: detected virtual channel map descriptor count {0} is invalid, pointer = {1}, end of section = {2}, loop = {3}, inner loop = {4}", descriptorCount, pointer, endOfSection, i, d));
                        }
                        byte tag    = section[pointer++];
                        byte length = section[pointer++];
                        Log.Log.Debug("S-VCT: virtual channel map descriptor, tag = 0x{0:x}, length = {1}", tag, length);
                        if (pointer + length > endOfSection)
                        {
                            throw new Exception(string.Format("S-VCT: invalid virtual channel map descriptor length {0}, pointer = {1}, end of section = {2}, loop = {3}, inner loop = {4}", length, pointer, endOfSection, i, d));
                        }
                        pointer += length;
                    }
                }
            }
        }