public List <int> PrepareTeletext() { var pages = new List <int>(); var i = 1; while (i <= _dataBuffer.Length - 6) { var dataUnitId = _dataBuffer[i++]; var dataUnitLen = _dataBuffer[i++]; if (dataUnitId == (int)Teletext.DataUnitT.DataUnitEbuTeletextNonSubtitle || dataUnitId == (int)Teletext.DataUnitT.DataUnitEbuTeletextSubtitle) { if (dataUnitLen == 44) // teletext payload has always size 44 bytes { // reverse endianness (via lookup table), ETS 300 706, chapter 7.1 for (var j = 0; j < dataUnitLen; j++) { _dataBuffer[i + j] = TeletextHamming.Reverse8[_dataBuffer[i + j]]; } var pageNumber = Teletext.GetPageNumber(new Teletext.TeletextPacketPayload(_dataBuffer, i)); if (!pages.Contains(pageNumber) && pageNumber > 0) { pages.Add(pageNumber); } } } i += dataUnitLen; } return(pages); }
public Dictionary <int, Paragraph> GetTeletext(TeletextRunSettings teletextRunSettings, int pageNumber, int pageNumberBcd) { var timestamp = PresentationTimestamp.HasValue ? PresentationTimestamp.Value / 90 : 40; // do not allow timestamp to go back - treat lower timestamp as a reset/overflow var lastTimestamp = teletextRunSettings.GetLastTimestamp(pageNumber); teletextRunSettings.SetLastTimestamp(pageNumber, timestamp); timestamp += teletextRunSettings.GetAddTimestamp(pageNumber); if (lastTimestamp > 0 && lastTimestamp > timestamp) { teletextRunSettings.SetAddTimestamp(pageNumber, lastTimestamp); } // offset all time codes if first timestamp in ts file is > 1 sec timestamp = teletextRunSettings.SubtractStartMs(timestamp); if (timestamp < 40) { timestamp = 40; // Teletext.cs will subtract 40 ms (1 frame @25 fps) and this value must not be below 0 } var teletextPages = new Dictionary <int, Paragraph>(); var i = 1; while (i <= _dataBuffer.Length - 6) { var dataUnitId = _dataBuffer[i++]; var dataUnitLen = _dataBuffer[i++]; if (dataUnitId == (int)Teletext.DataUnitT.DataUnitEbuTeletextNonSubtitle || dataUnitId == (int)Teletext.DataUnitT.DataUnitEbuTeletextSubtitle) { if (dataUnitLen == 44) // teletext payload has always size 44 bytes { Teletext.ProcessTelxPacket((Teletext.DataUnitT)dataUnitId, new Teletext.TeletextPacketPayload(_dataBuffer, i), timestamp, teletextRunSettings, pageNumberBcd, pageNumber); } } i += dataUnitLen; } if (teletextRunSettings.PageNumberAndParagraph.ContainsKey(pageNumber) && teletextRunSettings.PageNumberAndParagraph[pageNumber] != null) { if (teletextPages.ContainsKey(pageNumber)) { teletextPages[pageNumber] = teletextRunSettings.PageNumberAndParagraph[pageNumber]; } else { teletextPages.Add(pageNumber, teletextRunSettings.PageNumberAndParagraph[pageNumber]); } } teletextRunSettings.PageNumberAndParagraph.Clear(); return(teletextPages); }
public Dictionary <int, Paragraph> GetTeletext(int packetId, TeletextRunSettings teletextRunSettings, int pageNumber, int pageNumberBcd, ulong?firstMs) { var lastTimestamp = PresentationTimestamp.HasValue ? PresentationTimestamp.Value / 90 : 40; if (firstMs.HasValue && lastTimestamp >= firstMs) { lastTimestamp -= firstMs.Value; } if (lastTimestamp < 40) { lastTimestamp = 40; // Teletext.cs will subtract 40 ms (1 frame @25 fps) and this value must not be below 0 } var teletextPages = new Dictionary <int, Paragraph>(); var i = 1; while (i <= _dataBuffer.Length - 6) { var dataUnitId = _dataBuffer[i++]; var dataUnitLen = _dataBuffer[i++]; if (dataUnitId == (int)Teletext.DataUnitT.DataUnitEbuTeletextNonSubtitle || dataUnitId == (int)Teletext.DataUnitT.DataUnitEbuTeletextSubtitle) { if (dataUnitLen == 44) // teletext payload has always size 44 bytes { Teletext.ProcessTelxPacket((Teletext.DataUnitT)dataUnitId, new Teletext.TeletextPacketPayload(_dataBuffer, i), lastTimestamp, teletextRunSettings, pageNumberBcd, pageNumber); } } i += dataUnitLen; } if (teletextRunSettings.PageNumberAndParagraph.ContainsKey(pageNumber) && teletextRunSettings.PageNumberAndParagraph[pageNumber] != null) { if (teletextPages.ContainsKey(pageNumber)) { teletextPages[pageNumber] = teletextRunSettings.PageNumberAndParagraph[pageNumber]; } else { teletextPages.Add(pageNumber, teletextRunSettings.PageNumberAndParagraph[pageNumber]); } } teletextRunSettings.PageNumberAndParagraph.Clear(); return(teletextPages); }
/// <summary> /// Can be used with e.g. MemoryStream or FileStream /// </summary> /// <param name="ms">Input stream</param> /// <param name="callback">Optional callback event to follow progress</param> public void Parse(Stream ms, LoadTransportStreamCallback callback) { IsM2TransportStream = false; NumberOfNullPackets = 0; TotalNumberOfPackets = 0; TotalNumberOfPrivateStream1 = 0; TotalNumberOfPrivateStream1Continuation0 = 0; SubtitlePacketIds = new List <int>(); SubtitlePackets = new List <Packet>(); ms.Position = 0; const int packetLength = 188; IsM2TransportStream = IsM3TransportStream(ms); var packetBuffer = new byte[packetLength]; var m2TsTimeCodeBuffer = new byte[4]; long position = 0; SubtitlesLookup = new SortedDictionary <int, List <DvbSubPes> >(); TeletextSubtitlesLookup = new SortedDictionary <int, SortedDictionary <int, List <Paragraph> > >(); var teletextPesList = new Dictionary <int, List <DvbSubPes> >(); var teletextPages = new Dictionary <int, List <int> >(); ulong?firstMs = null; ulong?firstVideoMs = null; // check for Topfield .rec file ms.Seek(position, SeekOrigin.Begin); ms.Read(m2TsTimeCodeBuffer, 0, 3); if (m2TsTimeCodeBuffer[0] == 0x54 && m2TsTimeCodeBuffer[1] == 0x46 && m2TsTimeCodeBuffer[2] == 0x72) { position = 3760; } long transportStreamLength = ms.Length; ms.Seek(position, SeekOrigin.Begin); while (position < transportStreamLength) { if (IsM2TransportStream) { ms.Read(m2TsTimeCodeBuffer, 0, m2TsTimeCodeBuffer.Length); position += m2TsTimeCodeBuffer.Length; } var bytesRead = ms.Read(packetBuffer, 0, packetLength); if (bytesRead < packetLength) { break; // incomplete packet at end-of-file } if (packetBuffer[0] == Packet.SynchronizationByte) { var packet = new Packet(packetBuffer); if (packet.IsNullPacket) { NumberOfNullPackets++; } else if (!firstVideoMs.HasValue && packet.IsVideoStream) { if (packet.Payload != null && packet.Payload.Length > 10) { int presentationTimestampDecodeTimestampFlags = packet.Payload[7] >> 6; if (presentationTimestampDecodeTimestampFlags == Helper.B00000010 || presentationTimestampDecodeTimestampFlags == Helper.B00000011) { firstVideoMs = (ulong)packet.Payload[9 + 4] >> 1; firstVideoMs += (ulong)packet.Payload[9 + 3] << 7; firstVideoMs += (ulong)(packet.Payload[9 + 2] & Helper.B11111110) << 14; firstVideoMs += (ulong)packet.Payload[9 + 1] << 22; firstVideoMs += (ulong)(packet.Payload[9 + 0] & Helper.B00001110) << 29; firstVideoMs = firstVideoMs / 90; } } } else if (packet.IsPrivateStream1 || SubtitlePacketIds.Contains(packet.PacketId)) { TotalNumberOfPrivateStream1++; if (!SubtitlePacketIds.Contains(packet.PacketId)) { SubtitlePacketIds.Add(packet.PacketId); } if (packet.PayloadUnitStartIndicator) { firstMs = ProcessPackages(packet.PacketId, teletextPages, teletextPesList, firstMs); SubtitlePackets.RemoveAll(p => p.PacketId == packet.PacketId); } SubtitlePackets.Add(packet); if (packet.ContinuityCounter == 0) { TotalNumberOfPrivateStream1Continuation0++; } } TotalNumberOfPackets++; position += packetLength; if (TotalNumberOfPackets % 100000 == 0) { callback?.Invoke(position, transportStreamLength); } } else { // sync byte not found - search for it (will be very slow!) if (IsM2TransportStream) { position -= m2TsTimeCodeBuffer.Length; } position++; ms.Seek(position, SeekOrigin.Begin); } } foreach (var pid in SubtitlePackets.GroupBy(p => p.PacketId)) { firstMs = ProcessPackages(pid.Key, teletextPages, teletextPesList, firstMs); } SubtitlePackets.Clear(); callback?.Invoke(transportStreamLength, transportStreamLength); foreach (var packetId in teletextPesList.Keys) // teletext from PES packets { foreach (var page in teletextPages[packetId].OrderBy(p => p)) { var pageBcd = Teletext.DecToBec(page); Teletext.InitializeStaticFields(packetId, pageBcd); var teletextRunSettings = new TeletextRunSettings(); foreach (var pes in teletextPesList[packetId]) { var textDictionary = pes.GetTeletext(packetId, teletextRunSettings, page, pageBcd, firstMs); foreach (var dic in textDictionary) { if (!string.IsNullOrEmpty(dic.Value.Text)) { if (TeletextSubtitlesLookup.ContainsKey(packetId)) { var innerDic = TeletextSubtitlesLookup[packetId]; if (innerDic.ContainsKey(dic.Key)) { innerDic[dic.Key].Add(dic.Value); } else { innerDic.Add(dic.Key, new List <Paragraph> { dic.Value }); } } else { TeletextSubtitlesLookup.Add(packetId, new SortedDictionary <int, List <Paragraph> > { { dic.Key, new List <Paragraph> { dic.Value } } }); } } } } } } if (IsM2TransportStream) // m2ts blu-ray images from PES packets { DvbSubtitlesLookup = new SortedDictionary <int, List <TransportStreamSubtitle> >(); foreach (int pid in SubtitlesLookup.Keys) { var bdMs = new MemoryStream(); var list = SubtitlesLookup[pid]; var currentList = new List <DvbSubPes>(); var sb = new StringBuilder(); var subList = new List <TransportStreamSubtitle>(); var offset = (long)(firstVideoMs ?? 0); // when to use firstMs ? for (var index = 0; index < list.Count; index++) { var item = list[index]; item.WriteToStream(bdMs); currentList.Add(item); if (item.DataIdentifier == 0x80) { bdMs.Position = 0; var bdList = BluRaySupParser.ParseBluRaySup(bdMs, sb, true); if (bdList.Count > 0) { var startMs = currentList.First().PresentationTimestampToMilliseconds(); var endMs = index + 1 < list.Count ? list[index + 1].PresentationTimestampToMilliseconds() : startMs + (ulong)Configuration.Settings.General.NewEmptyDefaultMs; startMs = (ulong)((long)startMs - offset); endMs = (ulong)((long)endMs - offset); subList.Add(new TransportStreamSubtitle(bdList[0], startMs, endMs)); } bdMs.Dispose(); bdMs = new MemoryStream(); currentList.Clear(); } } if (subList.Count > 0) { DvbSubtitlesLookup.Add(pid, subList); } } SubtitlePacketIds.Clear(); foreach (int key in DvbSubtitlesLookup.Keys) { SubtitlePacketIds.Add(key); } return; } SubtitlePacketIds.Clear(); foreach (int key in SubtitlesLookup.Keys) { SubtitlePacketIds.Add(key); } SubtitlePacketIds.Sort(); // Merge packets and set start/end time DvbSubtitlesLookup = new SortedDictionary <int, List <TransportStreamSubtitle> >(); foreach (int pid in SubtitlePacketIds) { var subtitles = new List <TransportStreamSubtitle>(); var list = ParseAndRemoveEmpty(GetSubtitlePesPackets(pid)); var offset = (long)(firstVideoMs ?? 0); // when to use firstMs ? for (int i = 0; i < list.Count; i++) { var pes = list[i]; pes.ParseSegments(); if (pes.ObjectDataList.Count > 0) { var sub = new TransportStreamSubtitle { StartMilliseconds = pes.PresentationTimestampToMilliseconds(), Pes = pes }; if (i + 1 < list.Count && list[i + 1].PresentationTimestampToMilliseconds() > 25) { sub.EndMilliseconds = list[i + 1].PresentationTimestampToMilliseconds() - 25; } if (sub.EndMilliseconds < sub.StartMilliseconds || sub.EndMilliseconds - sub.StartMilliseconds > (ulong)Configuration.Settings.General.SubtitleMaximumDisplayMilliseconds) { sub.EndMilliseconds = sub.StartMilliseconds + (ulong)Configuration.Settings.General.SubtitleMaximumDisplayMilliseconds; } if (offset <= (long)sub.StartMilliseconds || offset < 0) { sub.StartMilliseconds = (ulong)((long)sub.StartMilliseconds - offset); sub.EndMilliseconds = (ulong)((long)sub.EndMilliseconds - offset); } else { if (subtitles.Count > 0) { offset = (long)sub.StartMilliseconds - (long)subtitles[subtitles.Count - 1].EndMilliseconds + 1000; sub.StartMilliseconds = (ulong)((long)sub.StartMilliseconds - offset); sub.EndMilliseconds = (ulong)((long)sub.EndMilliseconds - offset); } } subtitles.Add(sub); } } DvbSubtitlesLookup.Add(pid, subtitles); } SubtitlePacketIds.Clear(); foreach (int key in DvbSubtitlesLookup.Keys) { if (DvbSubtitlesLookup[key].Count > 0) { SubtitlePacketIds.Add(key); } } SubtitlePacketIds.Sort(); }