/// <summary> /// Erzeugt eine neue Hintergrundaufgabe zum Auslesen von SI Tabellen. /// </summary> /// <param name="device">Das zu verwendende Gerät.</param> /// <param name="stream">Die Datenstromkennung, die überwacht werden soll.</param> /// <returns>Die neue Aufgabe.</returns> public static CancellableTask <TTableType[]> GetTableAsync <TTableType>(this Hardware device, ushort stream) where TTableType : Table { // Validate if (device == null) { throw new ArgumentException("no hardware to use", "device"); } // Create the task return (CancellableTask <TTableType[]> .Run(cancel => { // Termination synchronisation var sync = new object(); var done = false; // Parsing state var tables = default(TTableType[]); var expectedVersion = 0; var collectedParts = 0; // Create parser var parser = TableParser.Create(( TTableType table ) => { // Disabled if (done) { return; } // Check version if (tables != null) { if (table.Version != expectedVersion) { tables = null; } } // Discard on count mismatch if (tables != null) { if (tables.Length != (table.LastSection + 1)) { tables = null; } } // Discard on overrun if (tables != null) { if (table.CurrentSection >= tables.Length) { tables = null; } } // Discard on duplicates if (tables != null) { if (tables[table.CurrentSection] != null) { tables = null; } } // Create once if (tables == null) { // Create in full size tables = new TTableType[table.LastSection + 1]; expectedVersion = table.Version; collectedParts = 0; } // Add it tables[table.CurrentSection] = table; // Mark as done if (++collectedParts >= tables.Length) { lock (sync) { done = true; // Our wake up call Monitor.Pulse(sync); } } }); // Register with cleanup var tableType = Table.GetIsExtendedTable <TTableType>() ? StreamTypes.ExtendedTable : StreamTypes.StandardTable; var registration = device.AddConsumer(stream, tableType, parser.AddPayload); try { // Start receiving data device.SetConsumerState(registration, true); // Wait for end and fully process the cancellation token using (cancel.Register(() => { lock (sync) Monitor.Pulse(sync); })) lock (sync) while (!done) { if (cancel.IsCancellationRequested) { return null; } else { Monitor.Wait(sync); } } } finally { // Cleanup device.SetConsumerState(registration, null); } // Report return tables; })); }
/// <summary> /// Erzeugt eine neue Verwaltung. /// </summary> /// <param name="hardware">Das Gerät, auf dem die zugehörige Quellgruppe gerade aktiv ist.</param> /// <param name="profile">Optional das Geräteprofil mit der zugehörigen Senderliste.</param> /// <param name="source">Die Quelle, die zu betrachten ist.</param> /// <param name="selection">Die zu betrachtenden Datenströme.</param> /// <exception cref="ArgumentNullException">Ein Parameter wurde nicht angegeben.</exception> public SourceStreamsManager( Hardware hardware, Profile profile, SourceIdentifier source, StreamSelection selection ) { // Validate if (null == hardware) throw new ArgumentNullException( "hardware" ); if (null == source) throw new ArgumentNullException( "source" ); if (null == selection) throw new ArgumentNullException( "selection" ); // Remember all StreamSelection = selection; Hardware = hardware; Profile = profile; Source = source; }
/// <summary> /// Beginnt die Aufzeichnung in einen <i>Transport Stream</i> - optional als /// Datei. /// </summary> /// <param name="nextPID">Die erste Datenstromkennung (PID), die in der Aufzeichnungsdatei verwendet werden darf.</param> /// <param name="recreate">Gesetzt, wenn ein Neustart aufgrund veränderter Nutzdatenströme erforderlich wurde.</param> /// <exception cref="ArgumentException">Eine Aufzeichnung der angegebenen Quelle ist nicht möglich.</exception> private void CreateStream(short nextPID, bool recreate) { // Try to get the full name var filePath = m_OriginalPath; if (filePath != null) { if (m_FileCount > 0) { // Split off the parts var name = Path.GetFileNameWithoutExtension(filePath); var dir = Path.GetDirectoryName(filePath); var ext = Path.GetExtension(filePath); // Construct new name filePath = Path.Combine(dir, $"{name} - {m_FileCount}{ext}"); } } // Try to decrypt if (m_Decrypting = m_OriginalSettings.IsEncrypted) { try { // Process Hardware.Decrypt(Source); } catch { // Ignore any error m_Decrypting = false; } } // Type of the video EPG.StreamTypes?videoType; // Video first if (m_OriginalSettings.VideoStream == 0) { videoType = null; } else { videoType = (m_OriginalSettings.VideoType == VideoTypes.H264) ? EPG.StreamTypes.H264 : EPG.StreamTypes.Video13818; } // Get the buffer size var bufferSize = (FileBufferSizeChooser == null) ? null : FileBufferSizeChooser(videoType); // Create the new stream m_TransportStream = new Manager(filePath, nextPID, bufferSize.GetValueOrDefault(Manager.DefaultBufferSize)); // Attach PCR sink m_TransportStream.OnWritingPCR = m_WritePCRSink; // Report of the actually selected streams StreamSelection result = new StreamSelection(); // Cleanup on any error try { // Video first if (videoType.HasValue) { AddConsumer(m_OriginalSettings.VideoStream, StreamTypes.Video, m_TransportStream.AddVideo((byte)videoType.Value)); } // Select audio ProcessAudioSelection(AudioTypes.MP2, result.MP2Tracks, StreamSelection.MP2Tracks); ProcessAudioSelection(AudioTypes.AC3, result.AC3Tracks, StreamSelection.AC3Tracks); // Videotext if (StreamSelection.Videotext) { if (0 != m_OriginalSettings.TextStream) { // Register AddConsumer(m_OriginalSettings.TextStream, StreamTypes.VideoText, m_TransportStream.AddTeleText()); // Remember result.Videotext = true; } } // Subtitle streams var subtitles = new Dictionary <ushort, List <EPG.SubtitleInfo> >(); // Preset mode result.SubTitles.LanguageMode = LanguageModes.All; // All audio // Subtitles foreach (var subtitle in m_OriginalSettings.Subtitles) { // Check for primary if (StreamSelection.SubTitles.LanguageMode == LanguageModes.Primary) { // Attach to the list AddSubtitleInformation(subtitle, subtitles); // Copy over result.SubTitles.LanguageMode = LanguageModes.Primary; // Remember result.SubTitles.Languages.Add(subtitle.Language); // Done break; } // Standard selection if (StreamSelection.SubTitles.Contains(subtitle.Language)) { // Attach to the list AddSubtitleInformation(subtitle, subtitles); // Remember result.SubTitles.Languages.Add(subtitle.Language); } else { // At least one is excluded result.SubTitles.LanguageMode = LanguageModes.Selection; } } // Clear flag if no audio is used if (LanguageModes.All == result.SubTitles.LanguageMode) { if (result.SubTitles.Languages.Count < 1) { result.SubTitles.LanguageMode = LanguageModes.Selection; } } // Process all subtitles foreach (var current in subtitles) { AddConsumer(current.Key, StreamTypes.SubTitle, m_TransportStream.AddSubtitles(current.Value.ToArray())); } // See if program guide is requested bool epg = StreamSelection.ProgramGuide; // May want to disable if (epg) { if ((Hardware.Profile != null) && Hardware.Profile.DisableProgramGuide) { epg = false; } else if (Profile != null) { if (Profile.GetFilter(Source).DisableProgramGuide) { epg = false; } } } // EPG if (epg) { // Activate dispatch m_TransportStream.SetEPGMapping(Source.Network, Source.TransportStream, Source.Service); // Start it Hardware.AddProgramGuideConsumer(DispatchEPG); // Remember result.ProgramGuide = true; } // Counter int started = 0; try { // Start all foreach (var consumer in m_Consumers) { try { // Forward Hardware.SetConsumerState(consumer, true); // Count it ++started; } catch (OutOfConsumersException) { // Translate throw new OutOfConsumersException(m_Consumers.Count, started) { RequestedSelection = result }; } } } catch { // Detach EPG Hardware.RemoveProgramGuideConsumer(DispatchEPG); // Cleanup on all errors foreach (var consumer in m_Consumers) { try { // Remove Hardware.SetConsumerState(consumer, null); } catch { // Ignore any error } } // Forward throw; } // Remember path if (filePath != null) { m_AllFiles.Add(new FileStreamInformation { FilePath = filePath, VideoType = m_OriginalSettings.VideoType }); } // Remember the time LastActivationTime = DateTime.UtcNow; } catch { // Simply forget m_TransportStream.Dispose(); m_TransportStream = null; // Forward throw; } // Get the next free PID NextStreamIdentifier = m_TransportStream.NextPID; // Remember the streams we use ActiveSelection = result; // Attach to clients var createNotify = OnCreatedStream; // Report if (createNotify != null) { createNotify(this); } }
/// <summary> /// Startet das Auslesen einer SI-Tabelle. /// </summary> /// <typeparam name="TTableType">Die Art der Tabelle.</typeparam> /// <param name="device">Das zu verwendende, bereits aktivierte Gerät.</param> /// <returns>Die Steuerung des Auslesevorgangs.</returns> public static CancellableTask <TTableType[]> GetTableAsync <TTableType>(this Hardware device) where TTableType : WellKnownTable { return(device.GetTableAsync <TTableType>(WellKnownTable.GetWellKnownStream <TTableType>())); }
/// <summary> /// Meldet einen Datenstrom zum Datenempfang an. /// </summary> /// <param name="pid">Die gewünschte Datestromkennung (PID).</param> /// <param name="type">Die Art der Nutzdaten im Datenstrom.</param> /// <param name="stream">Der Empfänger der Daten.</param> private void AddConsumer(ushort pid, StreamTypes type, StreamBase stream) => m_Consumers.Add(Hardware.AddConsumer(pid, type, stream.AddPayload));
/// <summary> /// Startet das Auslesen der aktuellen Daten zur aktiven Quelle im Hintergrund. /// </summary> /// <returns>Eine Steuerinstanz zum asynchronen Zugriff auf die aktuellen Daten.</returns> public CancellableTask <SourceInformation> GetCurrentInformationAsync() => Hardware.GetSourceInformationAsync(Source, Profile);
/// <summary> /// Meldet eine Analyseinstanz auf einem Datenstrom von SI Tabellen an. /// </summary> /// <param name="provider">Die zu aktuelle Hardware Abstraktion.</param> /// <param name="stream">Die eindeutige Nummer (PID) des Datenstroms in der aktuellen <see cref="SourceGroup"/>.</param> /// <param name="parser">Die zu verwendene Analyseeinheit.</param> /// <returns>Die eindeutige Kennung des neu angemeldeten Verbrauchers.</returns> /// <exception cref="ArgumentNullException">Die Hardware Abstraktion und / oder die Analyseinstanz /// sind nicht gesetzt.</exception> public static Guid AddConsumer(this Hardware provider, ushort stream, TableParser parser) { // Forward return(provider.AddConsumer(stream, parser, StreamTypes.StandardTable)); }
/// <summary> /// Wandelt einen zusammengehörigen Block von SI NIT Tabellen in Informationen über einen /// Ursprung um. /// </summary> /// <param name="tables">Die Rohinformationen als SI NIT Tabellen.</param> /// <param name="provider">Das zugehörige DVB.NET Gerät, das noch auf den Ursprung eingestellt ist.</param> /// <returns>Die gewünschten Informationen.</returns> public static TerrestrialLocationInformation ToLocationInformation(this NIT[] tables, Hardware <TerrestrialProfile, TerrestrialLocation, TerrestrialGroup> provider) { // Forward return(CreateLocationInformation <TerrestrialGroup, TerrestrialLocationInformation>(new TerrestrialLocation(), tables)); }
/// <summary> /// Wandelt einen zusammengehörigen Block von SI NIT Tabellen in Informationen über einen /// Ursprung um. /// </summary> /// <param name="tables">Die Rohinformationen als SI NIT Tabellen.</param> /// <param name="provider">Das zugehörige DVB.NET Gerät, das noch auf den Ursprung eingestellt ist.</param> /// <returns>Die gewünschten Informationen.</returns> public static CableLocationInformation ToLocationInformation(this NIT[] tables, Hardware <CableProfile, CableLocation, CableGroup> provider) { // Forward return(CreateLocationInformation <CableGroup, CableLocationInformation>(new CableLocation(), tables)); }
/// <summary> /// Wandelt einen zusammengehörigen Block von SI NIT Tabellen in Informationen über einen /// Ursprung um. /// </summary> /// <param name="tables">Die Rohinformationen als SI NIT Tabellen.</param> /// <param name="provider">Das zugehörige DVB.NET Gerät, das noch auf den Ursprung eingestellt ist.</param> /// <returns>Die gewünschten Informationen.</returns> public static SatelliteLocationInformation ToLocationInformation(this NIT[] tables, Hardware <SatelliteProfile, SatelliteLocation, SatelliteGroup> provider) { // Forward if (null == provider) { return(CreateLocationInformation <SatelliteGroup, SatelliteLocationInformation>(new SatelliteLocation(), tables)); } else { return(CreateLocationInformation <SatelliteGroup, SatelliteLocationInformation>(SatelliteLocation.Parse(provider.CurrentLocation.ToString()), tables)); } }
/// <summary> /// Erzeugt einen Aufzeichnungskontext für eine Quelle. /// </summary> /// <param name="source">Die Informationen zur Quelle.</param> /// <param name="hardware">Das zu verwendende Gerät.</param> /// <param name="profile">Opetional ein Geräteprofil mit der zugehörigen Senderliste.</param> /// <param name="streams">Die gewünschten Aufzeichnungsparameter.</param> /// <returns>Eine Kontrollinstanz für die Aufzeichnung. Diese muss mittels <see cref="IDisposable.Dispose"/> /// freigegeben werden.</returns> public static SourceStreamsManager Open(this SourceIdentifier source, Hardware hardware, Profile profile, StreamSelection streams) { // Forward return(new SourceStreamsManager(hardware, profile, source, streams)); }