/// <summary>
        /// Beginnt die Aufzeichnung in einen <i>Transport Stream</i> - optional als
        /// Datei.
        /// </summary>
        /// <param name="filePath">Optional die Angabe eines Dateinames.</param>
        /// <param name="info">Die aktuelle Konfiguration der Quelle oder <i>null</i>, wenn keine bekannt ist.</param>
        /// <returns>Gesetzt, wenn die Quelle bekannt ist und der Empfang aktiviert wurde.</returns>
        /// <exception cref="InvalidOperationException">Es ist bereits eine Aufzeichnung aktiv.</exception>
        public bool CreateStream(string filePath, SourceInformation info)
        {
            // Validate
            if (m_TransportStream != null)
            {
                throw new InvalidOperationException();
            }

            // Load
            if (info == null)
            {
                info = GetCurrentInformation();
            }

            // Remember settings
            m_OriginalSettings = info;
            m_OriginalPath     = filePath;
            m_FileCount        = 0;

            // Validate
            if (m_OriginalSettings == null)
            {
                return(false);
            }

            // Forward
            CreateStream(NextStreamIdentifier, false);

            // Update signature
            m_CurrentSignature = CreateRecordingSignature(m_OriginalSettings);

            // Activate streaming
            if (!string.IsNullOrEmpty(m_LastStreamTarget))
            {
                StreamingTarget = m_LastStreamTarget;
            }

            // Did it
            return(true);
        }
        /// <summary>
        /// Prüft, ob sich an der Konfiguration der Quelle etwas verändert hat.
        /// </summary>
        /// <param name="information">Die zugehörige aktuelle Konfiguration der Quelle.</param>
        /// <returns>Gesetzt, wenn nun der Empfang aktiv ist.</returns>
        /// <exception cref="ArgumentNullException">Der angegebenen Konfiguration ist keine Quelle zugeordnet.</exception>
        /// <exception cref="ArgumentException">Die Konfiguration gehört zu einer anderen Quelle als der aktuellen.</exception>
        public bool RetestSourceInformation( SourceInformation information )
        {
            // Must get the current source information
            if (information == null)
            {
                // Close any active stream
                CloseStream();

                // Not active
                return false;
            }

            // Validate
            if (information.Source == null)
                throw new ArgumentNullException( "information.Source" );
            if (!information.Source.Equals( Source ))
                throw new ArgumentException( information.Source.ToString(), "information.Source" );

            // Nothing changed
            var signature = CreateRecordingSignature( information );
            if (m_TransportStream != null)
            {
                // No need to switch
                if (signature.Equals( m_CurrentSignature ))
                    return true;

                // Report to see what happened
                if (!string.IsNullOrEmpty( m_CurrentSignature ))
                    try
                    {
                        // Report and ignore
                        EventLog.WriteEntry( "DVB.NET", $"Detected PSI Change: {m_CurrentSignature} => {signature}", EventLogEntryType.Information );
                    }
                    catch
                    {
                    }
            }

            // Get the streaming data
            var streamTarget = StreamingTarget;

            // Stop the current recording
            CloseStream();

            // Update all
            m_OriginalSettings = information;
            m_CurrentSignature = signature;

            // Fire notifications
            if (BeforeRecreateStream != null)
            {
                // Request new stream configuration
                var newSelection = BeforeRecreateStream( this );
                if (newSelection == null)
                    return false;

                // Remember
                StreamSelection = newSelection.Clone();
            }

            // Forward
            CreateStream( NextStreamIdentifier, true );

            // Reactivate streaming
            if (streamTarget != null)
                StreamingTarget = streamTarget;

            // Did it
            return true;
        }
        /// <summary>
        /// Ermittelt anhand der aktuellen Konfiguration, welche Datenströme wie aufgezeichnet
        /// werden. Die gemeldete Zeichenkette hat keine weitere Bedeutung, nur dass sie zum
        /// schnellen Vergleich verwemdet werden kann.
        /// </summary>
        /// <param name="information">Die aktuellen Daten zur zugehörigen Quelle.</param>
        /// <returns>Ein Schlüssel passend zur aktuellen Aufzeichnung.</returns>
        private string CreateRecordingSignature( SourceInformation information )
        {
            // All keys - will be sorted to make sure that lists are ordered
            var flags = new List<int>();

            // Various offset - PIDs are 12 Bit only!
            const int Offset_VideoType = 0x10000;
            const int Offset_VideoPID = 0x20000;
            const int Offset_MP2PID = 0x30000;
            const int Offset_AC3PID = 0x40000;
            const int Offset_TTXPID = 0x50000;
            const int Offset_SUBPID = 0x60000;

            // Video first
            if (0 != information.VideoStream)
            {
                // Get the type                
                var videoType = (information.VideoType == VideoTypes.H264) ? EPG.StreamTypes.H264 : EPG.StreamTypes.Video13818;

                // Register
                flags.Add( Offset_VideoType + (int) videoType );
                flags.Add( Offset_VideoPID + (int) information.VideoStream );
            }

            // All MP2 audio
            foreach (var audio in information.AudioTracks)
                if (AudioTypes.MP2 == audio.AudioType)
                    if (StreamSelection.MP2Tracks.LanguageMode == LanguageModes.Primary)
                    {
                        // Add as is
                        flags.Add( Offset_MP2PID + (int) audio.AudioStream );

                        // Finish
                        break;
                    }
                    else if (StreamSelection.MP2Tracks.Contains( audio.Language ))
                    {
                        // Add as is
                        flags.Add( Offset_MP2PID + (int) audio.AudioStream );
                    }

            // All AC3 audio
            foreach (var audio in information.AudioTracks)
                if (AudioTypes.AC3 == audio.AudioType)
                    if (StreamSelection.AC3Tracks.LanguageMode == LanguageModes.Primary)
                    {
                        // Add as is
                        flags.Add( Offset_AC3PID + (int) audio.AudioStream );

                        // Finish
                        break;
                    }
                    else if (StreamSelection.AC3Tracks.Contains( audio.Language ))
                    {
                        // Add as is
                        flags.Add( Offset_AC3PID + (int) audio.AudioStream );
                    }

            // Videotext
            if (StreamSelection.Videotext)
                if (0 != information.TextStream)
                    flags.Add( Offset_TTXPID + (int) information.TextStream );

            // Subtitle streams
            var subtitles = new Dictionary<ushort, bool>();

            // Subtitles
            foreach (var subtitle in information.Subtitles)
                if (StreamSelection.SubTitles.LanguageMode == LanguageModes.Primary)
                {
                    // Add as is
                    subtitles[subtitle.SubtitleStream] = true;

                    // Finish
                    break;
                }
                else if (StreamSelection.SubTitles.Contains( subtitle.Language ))
                {
                    // Add as is
                    subtitles[subtitle.SubtitleStream] = true;
                }

            // Process all subtitles
            foreach (var subtitleStream in subtitles.Keys)
                flags.Add( Offset_SUBPID + (int) subtitleStream );

            // Create the key
            return (information.IsEncrypted ? "+" : "-") + string.Join( ".", flags.ConvertAll( f => f.ToString( "x5" ) ).ToArray() );
        }
        /// <summary>
        /// Beginnt die Aufzeichnung in einen <i>Transport Stream</i> - optional als 
        /// Datei.
        /// </summary>
        /// <param name="filePath">Optional die Angabe eines Dateinames.</param>
        /// <param name="info">Die aktuelle Konfiguration der Quelle oder <i>null</i>, wenn keine bekannt ist.</param>
        /// <returns>Gesetzt, wenn die Quelle bekannt ist und der Empfang aktiviert wurde.</returns>
        /// <exception cref="InvalidOperationException">Es ist bereits eine Aufzeichnung aktiv.</exception>
        public bool CreateStream( string filePath, SourceInformation info )
        {
            // Validate
            if (m_TransportStream != null)
                throw new InvalidOperationException();

            // Load
            if (info == null)
                info = GetCurrentInformation();

            // Remember settings
            m_OriginalSettings = info;
            m_OriginalPath = filePath;
            m_FileCount = 0;

            // Validate
            if (m_OriginalSettings == null)
                return false;

            // Forward
            CreateStream( NextStreamIdentifier, false );

            // Update signature
            m_CurrentSignature = CreateRecordingSignature( m_OriginalSettings );

            // Activate streaming
            if (!string.IsNullOrEmpty( m_LastStreamTarget ))
                StreamingTarget = m_LastStreamTarget;

            // Did it
            return true;
        }
        /// <summary>
        /// Beginnt mit dem Auslesen der Quelldaten.
        /// </summary>
        /// <param name="source">Die gewünschte Quelle.</param>
        /// <param name="device">Das zu verwendende Gerät.</param>
        /// <param name="profile">Optional das zu berücksichtigende Geräteprofil.</param>
        /// <returns>Die Hintergrundaufgabe zum Auslesen der Quelledaten.</returns>
        public static CancellableTask<SourceInformation> GetSourceInformationAsync( this Hardware device, SourceIdentifier source, Profile profile = null )
        {
            // Validate
            if (device == null)
                throw new ArgumentNullException( "no hardware to use", "device" );
            if (source == null)
                throw new ArgumentException( "no source to get information for", "source" );

            // Attach to tasks
            var patReader = device.AssociationTableReader;
            var groupReader = device.GroupReader;

            // Start 
            return
                CancellableTask<SourceInformation>.Run( cancel =>
                {
                    // Check tasks
                    if (groupReader == null)
                        return null;
                    if (patReader == null)
                        return null;

                    // Wait on tasks
                    if (!groupReader.CancellableWait( cancel ))
                        return null;
                    if (!patReader.CancellableWait( cancel ))
                        return null;

                    // Get the current group information
                    var groupInfo = groupReader.Result;
                    if (groupInfo == null)
                        return null;

                    // See if group exists
                    if (!groupInfo.Sources.Any( source.Equals ))
                        return null;

                    // Find the stream identifier for the service
                    var pmtIdentifier = patReader.Result.FindService( source.Service );
                    if (!pmtIdentifier.HasValue)
                        return null;

                    // Wait for mapping table
                    var pmtReader = device.GetTableAsync<PMT>( pmtIdentifier.Value );
                    if (!pmtReader.CancellableWait( cancel ))
                        return null;

                    // Request table
                    var pmts = pmtReader.Result;
                    if (pmts == null)
                        return null;

                    // Create dummy
                    var currentSettings = new SourceInformation { Source = source, VideoType = VideoTypes.NoVideo };

                    // Process all PMT - actually should be only one
                    foreach (var pmt in pmts)
                    {
                        // Overwrite encryption if CA descriptor is present
                        if (pmt.Table.Descriptors != null)
                            currentSettings.IsEncrypted = pmt.Table.Descriptors.Any( descriptor => EPG.DescriptorTags.CA == descriptor.Tag );

                        // Process the program entries
                        foreach (var program in pmt.Table.ProgramEntries)
                            currentSettings.Update( program );
                    }

                    // Find the related station information
                    var station = groupInfo.Sources.FirstOrDefault( source.Equals ) as Station;
                    if (station != null)
                    {
                        // Take data from there
                        currentSettings.Provider = station.Provider;
                        currentSettings.Name = station.Name;

                        // See if this is a service
                        currentSettings.IsService = station.IsService;

                        // Overwrite encryption if regular service entry exists
                        if (!currentSettings.IsService)
                            currentSettings.IsEncrypted = station.IsEncrypted;
                    }

                    // See if profile is attached
                    if (profile != null)
                    {
                        // Apply the modifier
                        var modifier = profile.GetFilter( currentSettings.Source );
                        if (modifier != null)
                            modifier.ApplyTo( currentSettings );
                    }

                    // Report
                    return currentSettings;
                } );
        }
        /// <summary>
        /// Beginnt mit dem Auslesen der Quelldaten.
        /// </summary>
        /// <param name="source">Die gewünschte Quelle.</param>
        /// <param name="device">Das zu verwendende Gerät.</param>
        /// <param name="profile">Optional das zu berücksichtigende Geräteprofil.</param>
        /// <returns>Die Hintergrundaufgabe zum Auslesen der Quelledaten.</returns>
        public static CancellableTask <SourceInformation> GetSourceInformationAsync(this Hardware device, SourceIdentifier source, Profile profile = null)
        {
            // Validate
            if (device == null)
            {
                throw new ArgumentNullException("no hardware to use", "device");
            }
            if (source == null)
            {
                throw new ArgumentException("no source to get information for", "source");
            }

            // Attach to tasks
            var patReader   = device.AssociationTableReader;
            var groupReader = device.GroupReader;

            // Start
            return
                (CancellableTask <SourceInformation> .Run(cancel =>
            {
                // Check tasks
                if (groupReader == null)
                {
                    return null;
                }
                if (patReader == null)
                {
                    return null;
                }

                // Wait on tasks
                if (!groupReader.CancellableWait(cancel))
                {
                    return null;
                }
                if (!patReader.CancellableWait(cancel))
                {
                    return null;
                }

                // Get the current group information
                var groupInfo = groupReader.Result;
                if (groupInfo == null)
                {
                    return null;
                }

                // See if group exists
                if (!groupInfo.Sources.Any(source.Equals))
                {
                    return null;
                }

                // Find the stream identifier for the service
                var pmtIdentifier = patReader.Result.FindService(source.Service);
                if (!pmtIdentifier.HasValue)
                {
                    return null;
                }

                // Wait for mapping table
                var pmtReader = device.GetTableAsync <PMT>(pmtIdentifier.Value);
                if (!pmtReader.CancellableWait(cancel))
                {
                    return null;
                }

                // Request table
                var pmts = pmtReader.Result;
                if (pmts == null)
                {
                    return null;
                }

                // Create dummy
                var currentSettings = new SourceInformation {
                    Source = source, VideoType = VideoTypes.NoVideo
                };

                // Process all PMT - actually should be only one
                foreach (var pmt in pmts)
                {
                    // Overwrite encryption if CA descriptor is present
                    if (pmt.Table.Descriptors != null)
                    {
                        currentSettings.IsEncrypted = pmt.Table.Descriptors.Any(descriptor => EPG.DescriptorTags.CA == descriptor.Tag);
                    }

                    // Process the program entries
                    foreach (var program in pmt.Table.ProgramEntries)
                    {
                        currentSettings.Update(program);
                    }
                }

                // Find the related station information
                var station = groupInfo.Sources.FirstOrDefault(source.Equals) as Station;
                if (station != null)
                {
                    // Take data from there
                    currentSettings.Provider = station.Provider;
                    currentSettings.Name = station.Name;

                    // See if this is a service
                    currentSettings.IsService = station.IsService;

                    // Overwrite encryption if regular service entry exists
                    if (!currentSettings.IsService)
                    {
                        currentSettings.IsEncrypted = station.IsEncrypted;
                    }
                }

                // See if profile is attached
                if (profile != null)
                {
                    // Apply the modifier
                    var modifier = profile.GetFilter(currentSettings.Source);
                    if (modifier != null)
                    {
                        modifier.ApplyTo(currentSettings);
                    }
                }

                // Report
                return currentSettings;
            }));
        }
예제 #7
0
        /// <summary>
        /// Verändert die Daten einer Quelle gemäß der Konfiguration dieser Instanz.
        /// </summary>
        /// <param name="source">Die zu verändernde Quelle.</param>
        /// <exception cref="ArgumentNullException">Es wurde keine Quelle angegeben.</exception>
        public void ApplyTo( SourceInformation source )
        {
            // Validate
            if (null == source)
                throw new ArgumentNullException( "source" );

            // Blind apply - even if identifier would not match
            if (!string.IsNullOrEmpty( Name ))
                source.Name = Name;
            if (!string.IsNullOrEmpty( Provider ))
                source.Provider = Provider;
            if (IsEncrypted.HasValue)
                source.IsEncrypted = IsEncrypted.Value;
            if (IsService.HasValue)
                source.IsService = IsService.Value;
            if (VideoStream.HasValue)
                source.VideoStream = VideoStream.Value;
            if (VideoType.HasValue)
                source.VideoType = VideoType.Value;
            if (TextStream.HasValue)
                source.TextStream = TextStream.Value;

            // Audio
            if (null != AudioStreams)
            {
                // Wipe out
                source.AudioTracks.Clear();

                // Add all
                foreach (AudioInformation audio in AudioStreams)
                    source.AudioTracks.Add( audio.Clone() );
            }

            // Subtitles
            if (null != SubtitlesStreams)
            {
                // Wipe out
                source.Subtitles.Clear();

                // Add all
                foreach (SubtitleInformation sub in SubtitlesStreams)
                    source.Subtitles.Add( sub.Clone() );
            }
        }
예제 #8
0
        /// <summary>
        /// Verändert die Daten einer Quelle gemäß der Konfiguration dieser Instanz.
        /// </summary>
        /// <param name="source">Die zu verändernde Quelle.</param>
        /// <exception cref="ArgumentNullException">Es wurde keine Quelle angegeben.</exception>
        public void ApplyTo(SourceInformation source)
        {
            // Validate
            if (null == source)
            {
                throw new ArgumentNullException("source");
            }

            // Blind apply - even if identifier would not match
            if (!string.IsNullOrEmpty(Name))
            {
                source.Name = Name;
            }
            if (!string.IsNullOrEmpty(Provider))
            {
                source.Provider = Provider;
            }
            if (IsEncrypted.HasValue)
            {
                source.IsEncrypted = IsEncrypted.Value;
            }
            if (IsService.HasValue)
            {
                source.IsService = IsService.Value;
            }
            if (VideoStream.HasValue)
            {
                source.VideoStream = VideoStream.Value;
            }
            if (VideoType.HasValue)
            {
                source.VideoType = VideoType.Value;
            }
            if (TextStream.HasValue)
            {
                source.TextStream = TextStream.Value;
            }

            // Audio
            if (null != AudioStreams)
            {
                // Wipe out
                source.AudioTracks.Clear();

                // Add all
                foreach (AudioInformation audio in AudioStreams)
                {
                    source.AudioTracks.Add(audio.Clone());
                }
            }

            // Subtitles
            if (null != SubtitlesStreams)
            {
                // Wipe out
                source.Subtitles.Clear();

                // Add all
                foreach (SubtitleInformation sub in SubtitlesStreams)
                {
                    source.Subtitles.Add(sub.Clone());
                }
            }
        }
        /// <summary>
        /// Prüft, ob sich an der Konfiguration der Quelle etwas verändert hat.
        /// </summary>
        /// <param name="information">Die zugehörige aktuelle Konfiguration der Quelle.</param>
        /// <returns>Gesetzt, wenn nun der Empfang aktiv ist.</returns>
        /// <exception cref="ArgumentNullException">Der angegebenen Konfiguration ist keine Quelle zugeordnet.</exception>
        /// <exception cref="ArgumentException">Die Konfiguration gehört zu einer anderen Quelle als der aktuellen.</exception>
        public bool RetestSourceInformation(SourceInformation information)
        {
            // Must get the current source information
            if (information == null)
            {
                // Close any active stream
                CloseStream();

                // Not active
                return(false);
            }

            // Validate
            if (information.Source == null)
            {
                throw new ArgumentNullException("information.Source");
            }
            if (!information.Source.Equals(Source))
            {
                throw new ArgumentException(information.Source.ToString(), "information.Source");
            }

            // Nothing changed
            var signature = CreateRecordingSignature(information);

            if (m_TransportStream != null)
            {
                // No need to switch
                if (signature.Equals(m_CurrentSignature))
                {
                    return(true);
                }

                // Report to see what happened
                if (!string.IsNullOrEmpty(m_CurrentSignature))
                {
                    try
                    {
                        // Report and ignore
                        EventLog.WriteEntry("DVB.NET", $"Detected PSI Change: {m_CurrentSignature} => {signature}", EventLogEntryType.Information);
                    }
                    catch
                    {
                    }
                }
            }

            // Get the streaming data
            var streamTarget = StreamingTarget;

            // Stop the current recording
            CloseStream();

            // Update all
            m_OriginalSettings = information;
            m_CurrentSignature = signature;

            // Fire notifications
            if (BeforeRecreateStream != null)
            {
                // Request new stream configuration
                var newSelection = BeforeRecreateStream(this);
                if (newSelection == null)
                {
                    return(false);
                }

                // Remember
                StreamSelection = newSelection.Clone();
            }

            // Forward
            CreateStream(NextStreamIdentifier, true);

            // Reactivate streaming
            if (streamTarget != null)
            {
                StreamingTarget = streamTarget;
            }

            // Did it
            return(true);
        }
        /// <summary>
        /// Ermittelt anhand der aktuellen Konfiguration, welche Datenströme wie aufgezeichnet
        /// werden. Die gemeldete Zeichenkette hat keine weitere Bedeutung, nur dass sie zum
        /// schnellen Vergleich verwemdet werden kann.
        /// </summary>
        /// <param name="information">Die aktuellen Daten zur zugehörigen Quelle.</param>
        /// <returns>Ein Schlüssel passend zur aktuellen Aufzeichnung.</returns>
        private string CreateRecordingSignature(SourceInformation information)
        {
            // All keys - will be sorted to make sure that lists are ordered
            var flags = new List <int>();

            // Various offset - PIDs are 12 Bit only!
            const int Offset_VideoType = 0x10000;
            const int Offset_VideoPID  = 0x20000;
            const int Offset_MP2PID    = 0x30000;
            const int Offset_AC3PID    = 0x40000;
            const int Offset_TTXPID    = 0x50000;
            const int Offset_SUBPID    = 0x60000;

            // Video first
            if (0 != information.VideoStream)
            {
                // Get the type
                var videoType = (information.VideoType == VideoTypes.H264) ? EPG.StreamTypes.H264 : EPG.StreamTypes.Video13818;

                // Register
                flags.Add(Offset_VideoType + (int)videoType);
                flags.Add(Offset_VideoPID + (int)information.VideoStream);
            }

            // All MP2 audio
            foreach (var audio in information.AudioTracks)
            {
                if (AudioTypes.MP2 == audio.AudioType)
                {
                    if (StreamSelection.MP2Tracks.LanguageMode == LanguageModes.Primary)
                    {
                        // Add as is
                        flags.Add(Offset_MP2PID + (int)audio.AudioStream);

                        // Finish
                        break;
                    }
                    else if (StreamSelection.MP2Tracks.Contains(audio.Language))
                    {
                        // Add as is
                        flags.Add(Offset_MP2PID + (int)audio.AudioStream);
                    }
                }
            }

            // All AC3 audio
            foreach (var audio in information.AudioTracks)
            {
                if (AudioTypes.AC3 == audio.AudioType)
                {
                    if (StreamSelection.AC3Tracks.LanguageMode == LanguageModes.Primary)
                    {
                        // Add as is
                        flags.Add(Offset_AC3PID + (int)audio.AudioStream);

                        // Finish
                        break;
                    }
                    else if (StreamSelection.AC3Tracks.Contains(audio.Language))
                    {
                        // Add as is
                        flags.Add(Offset_AC3PID + (int)audio.AudioStream);
                    }
                }
            }

            // Videotext
            if (StreamSelection.Videotext)
            {
                if (0 != information.TextStream)
                {
                    flags.Add(Offset_TTXPID + (int)information.TextStream);
                }
            }

            // Subtitle streams
            var subtitles = new Dictionary <ushort, bool>();

            // Subtitles
            foreach (var subtitle in information.Subtitles)
            {
                if (StreamSelection.SubTitles.LanguageMode == LanguageModes.Primary)
                {
                    // Add as is
                    subtitles[subtitle.SubtitleStream] = true;

                    // Finish
                    break;
                }
                else if (StreamSelection.SubTitles.Contains(subtitle.Language))
                {
                    // Add as is
                    subtitles[subtitle.SubtitleStream] = true;
                }
            }

            // Process all subtitles
            foreach (var subtitleStream in subtitles.Keys)
            {
                flags.Add(Offset_SUBPID + (int)subtitleStream);
            }

            // Create the key
            return((information.IsEncrypted ? "+" : "-") + string.Join(".", flags.ConvertAll(f => f.ToString("x5")).ToArray()));
        }
예제 #11
0
        /// <summary>
        /// Übernimmt Informationen aus einer SI Programmbeschreibung in die Informationen
        /// eines Senders.
        /// </summary>
        /// <param name="source">Die Daten zu einer Quelle, die vervollständigt werden sollen.</param>
        /// <param name="program">Die SI Programmbeschreibung.</param>
        public static void Update(this SourceInformation source, EPG.ProgramEntry program)
        {
            // MPEG-2 Video
            if (EPG.StreamTypes.Video13818 == program.StreamType)
            {
                // Remember
                source.VideoType   = VideoTypes.MPEG2;
                source.VideoStream = program.ElementaryPID;

                // Done
                return;
            }

            // H.264 Video
            if (EPG.StreamTypes.H264 == program.StreamType)
            {
                // Remember
                source.VideoType   = VideoTypes.H264;
                source.VideoStream = program.ElementaryPID;

                // Done
                return;
            }

            // MP2 Audio
            if ((EPG.StreamTypes.Audio11172 == program.StreamType) || (EPG.StreamTypes.Audio13818 == program.StreamType))
            {
                // Create new entry
                AudioInformation audio = new AudioInformation {
                    AudioType = AudioTypes.MP2, AudioStream = program.ElementaryPID, Language = program.ProgrammeName.Trim()
                };

                // Remember it
                source.AudioTracks.Add(audio);

                // Done
                return;
            }

            // AC3, TTX or DVB subtitles
            if (EPG.StreamTypes.PrivateData != program.StreamType)
            {
                return;
            }

            // Direct processing of descriptor list
            foreach (var descriptor in program.Descriptors)
            {
                // Check for AC3
                var ac3 = descriptor as EPG.Descriptors.AC3;
                if (null != ac3)
                {
                    // Create new entry
                    AudioInformation audio = new AudioInformation {
                        AudioType = AudioTypes.AC3, AudioStream = program.ElementaryPID, Language = program.ProgrammeName.Trim()
                    };

                    // Remember it
                    source.AudioTracks.Add(audio);

                    // Done
                    return;
                }

                // Check for videotext
                var ttx = descriptor as EPG.Descriptors.Teletext;
                if (null != ttx)
                {
                    // Remember
                    source.TextStream = program.ElementaryPID;

                    // Done
                    return;
                }

                // Check for DVB sub-titles
                var sub = descriptor as EPG.Descriptors.Subtitle;
                if (null != sub)
                {
                    // Process all items
                    foreach (var subTitle in sub.Subtitles)
                    {
                        // Create the information
                        var info = new SubtitleInformation
                        {
                            SubtitleStream  = program.ElementaryPID,
                            Language        = subTitle.Language,
                            SubtitleType    = (SubtitleTypes)subTitle.Type,
                            CompositionPage = subTitle.CompositionPage,
                            AncillaryPage   = subTitle.AncillaryPage
                        };

                        // Remember
                        source.Subtitles.Add(info);
                    }

                    // Done
                    return;
                }
            }
        }