public void BluRaySupWriteAndReadTwoBitmaps() { var fileName = Guid.NewGuid() + ".sup"; using (var binarySubtitleFile = new FileStream(fileName, FileMode.Create)) { var brSub = new BluRaySupPicture { StartTime = 0, EndTime = 1000, Width = 1080, Height = 720 }; var buffer = BluRaySupPicture.CreateSupFrame(brSub, new Bitmap(100, 20), 10, 25, 10, ContentAlignment.BottomCenter); binarySubtitleFile.Write(buffer, 0, buffer.Length); brSub.StartTime = 2000; brSub.EndTime = 3000; buffer = BluRaySupPicture.CreateSupFrame(brSub, new Bitmap(100, 20), 10, 25, 10, ContentAlignment.BottomCenter); binarySubtitleFile.Write(buffer, 0, buffer.Length); } var log = new StringBuilder(); var subtitles = BluRaySupParser.ParseBluRaySup(fileName, log); Assert.AreEqual(2, subtitles.Count); }
private static void ConvertBluRaySubtitle(string fileName, string toFormat, string offset, Encoding targetEncoding, string outputFolder, int count, ref int converted, ref int errors, IList <SubtitleFormat> formats, bool overwrite, string pacCodePage, double?targetFrameRate, bool removeTextForHi, bool fixCommonErrors, bool redoCasing) { SubtitleFormat format = Utilities.GetSubtitleFormatByFriendlyName(toFormat) ?? new SubRip(); var log = new StringBuilder(); Console.WriteLine("Loading subtitles from file \"{0}\"", fileName); var bluRaySubtitles = BluRaySupParser.ParseBluRaySup(fileName, log); Subtitle sub; using (var vobSubOcr = new VobSubOcr()) { Console.WriteLine("Using OCR to extract subtitles"); vobSubOcr.FileName = Path.GetFileName(fileName); vobSubOcr.InitializeBatch(bluRaySubtitles, Configuration.Settings.VobSubOcr, fileName); sub = vobSubOcr.SubtitleFromOcr; Console.WriteLine("Extracted subtitles from file \"{0}\"", fileName); } if (sub != null) { Console.WriteLine("Converted subtitle"); BatchConvertSave(toFormat, offset, targetEncoding, outputFolder, count, ref converted, ref errors, formats, fileName, sub, format, overwrite, pacCodePage, targetFrameRate, removeTextForHi, fixCommonErrors, redoCasing); } }
private void OpenBinSubtitle(string fileName) { numericUpDownScreenWidth.Value = 1920; numericUpDownScreenHeight.Value = 1080; comboBoxFrameRate.Items.Clear(); comboBoxFrameRate.Items.Add("23.976"); comboBoxFrameRate.Items.Add("24"); comboBoxFrameRate.Items.Add("25"); comboBoxFrameRate.Items.Add("29.97"); comboBoxFrameRate.Items.Add("30"); comboBoxFrameRate.Items.Add("50"); comboBoxFrameRate.Items.Add("59.94"); comboBoxFrameRate.Items.Add("60"); comboBoxFrameRate.SelectedIndex = 2; if (FileUtil.IsBluRaySup(fileName)) { var log = new StringBuilder(); _bluRaySubtitles = BluRaySupParser.ParseBluRaySup(fileName, log); _subtitle = new Subtitle(); _extra = new List <Extra>(); bool first = true; foreach (var s in _bluRaySubtitles) { if (first) { var bmp = s.GetBitmap(); if (bmp != null && bmp.Width > 1 && bmp.Height > 1) { SetFrameRate(s.FramesPerSecondType); SetResolution(s.Size); first = false; } } _subtitle.Paragraphs.Add(new Paragraph { StartTime = new TimeCode(s.StartTime / 90.0), EndTime = new TimeCode(s.EndTime / 90.0) }); var pos = s.GetPosition(); _extra.Add(new Extra { Forced = s.IsForced, X = pos.Left, Y = pos.Top }); } _subtitle.Renumber(); subtitleListView1.Fill(_subtitle); } if (_subtitle != null) { subtitleListView1.SelectIndexAndEnsureVisible(_subtitle.GetParagraphOrDefault(0)); } }
private static List <PcsData> LoadBluRaySubFromMatroska(MatroskaTrackInfo matroskaSubtitleInfo, MatroskaFile matroska) { var sub = matroska.GetSubtitle(matroskaSubtitleInfo.TrackNumber, null); var subtitles = new List <BluRaySupParser.PcsData>(); var log = new StringBuilder(); var clusterStream = new MemoryStream(); var lastPalettes = new Dictionary <int, List <PaletteInfo> >(); var bitmapObjects = new Dictionary <int, List <OdsData> >(); foreach (var p in sub) { var buffer = p.GetData(matroskaSubtitleInfo); if (buffer != null && buffer.Length > 2) { clusterStream.Write(buffer, 0, buffer.Length); if (ContainsBluRayStartSegment(buffer)) { if (subtitles.Count > 0 && subtitles[subtitles.Count - 1].StartTime == subtitles[subtitles.Count - 1].EndTime) { subtitles[subtitles.Count - 1].EndTime = (long)((p.Start - 1) * 90.0); } clusterStream.Position = 0; var list = BluRaySupParser.ParseBluRaySup(clusterStream, log, true, lastPalettes, bitmapObjects); foreach (var sup in list) { sup.StartTime = (long)((p.Start - 1) * 90.0); sup.EndTime = (long)((p.End - 1) * 90.0); subtitles.Add(sup); // fix overlapping if (subtitles.Count > 1 && sub[subtitles.Count - 2].End > sub[subtitles.Count - 1].Start) { subtitles[subtitles.Count - 2].EndTime = subtitles[subtitles.Count - 1].StartTime - 1; } } clusterStream = new MemoryStream(); } } else if (subtitles.Count > 0) { var lastSub = subtitles[subtitles.Count - 1]; if (lastSub.StartTime == lastSub.EndTime) { lastSub.EndTime = (long)((p.Start - 1) * 90.0); if (lastSub.EndTime - lastSub.StartTime > 1000000) { lastSub.EndTime = lastSub.StartTime; } } } } return(subtitles); }
public void BluRaySupReadMultiImagePic() { string fileName = Path.Combine(Directory.GetCurrentDirectory(), "sample_BDSUP_multi_image.sup"); var log = new StringBuilder(); var subtitles = BluRaySupParser.ParseBluRaySup(fileName, log); Assert.AreEqual(2, subtitles.Count); Assert.AreEqual(2, subtitles[0].PcsObjects.Count); using (var bmp = subtitles[0].GetBitmap()) { Assert.AreEqual(784, bmp.Width); Assert.AreEqual(911, bmp.Height); } }
private static bool ContainsBluRayStartSegment(byte[] buffer) { const int epochStart = 0x80; var position = 0; while (position + 3 <= buffer.Length) { var segmentType = buffer[position]; if (segmentType == epochStart) { return(true); } var length = BluRaySupParser.BigEndianInt16(buffer, position + 1) + 3; position += length; } return(false); }
public static List <SubtitleEvent> ReadSup(string inputFile) { List <SubtitleEvent> events = new List <SubtitleEvent>(); StringBuilder logger = new StringBuilder(); var result = BluRaySupParser.ParseBluRaySup(inputFile, logger); int i = 0; foreach (var line in result) { var bitmap = BluRaySupParser.SupDecoder.DecodeImage(line.PcsObjects[0], line.BitmapObjects[0], line.PaletteInfos); var image_data = ImageUtil.ConvertToPngBytes(bitmap); events.Add(new SubtitleEvent() { StartTime = TimeSpan.FromMilliseconds(line.StartTime / 90.0), EndTime = TimeSpan.FromMilliseconds(line.EndTime / 90.0), MimeType = "image/png", Image = image_data, Origin = line.PcsObjects[0].Origin }); i++; } return(events); }
/// <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 { if (!teletextPages.ContainsKey(packetId)) { continue; } foreach (var page in teletextPages[packetId].OrderBy(p => p)) { var pageBcd = Teletext.DecToBec(page); Teletext.InitializeStaticFields(packetId, pageBcd); var teletextRunSettings = new TeletextRunSettings(firstMs); foreach (var pes in teletextPesList[packetId]) { var textDictionary = pes.GetTeletext(teletextRunSettings, page, pageBcd); AddToTeletextDictionary(textDictionary, packetId); } var lastTextDictionary = Teletext.ProcessTelxPacketPendingLeftovers(teletextRunSettings, page); AddToTeletextDictionary(lastTextDictionary, packetId); } } if (Configuration.Settings.SubtitleSettings.TeletextItalicFix) { FixTeletextItalics(TeletextSubtitlesLookup); } foreach (var id in TeletextSubtitlesLookup.Keys) { SubtitlePacketIds = SubtitlePacketIds.Where(p => p != id).ToList(); SubtitlesLookup.Remove(id); } DvbSubtitlesLookup = new SortedDictionary <int, List <TransportStreamSubtitle> >(); if (IsM2TransportStream) // m2ts blu-ray images from PES packets { 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 = SubtitlePacketIds.Where(p => p != pid).ToList(); } } } SubtitlePacketIds.Clear(); foreach (int key in SubtitlesLookup.Keys) { SubtitlePacketIds.Add(key); } SubtitlePacketIds.Sort(); // Merge packets and set start/end time foreach (int pid in SubtitlePacketIds.Where(p => !DvbSubtitlesLookup.ContainsKey(p))) { 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); } } if (subtitles.Count > 0) { DvbSubtitlesLookup.Add(pid, subtitles); } } SubtitlePacketIds.Clear(); foreach (int key in DvbSubtitlesLookup.Keys) { if (DvbSubtitlesLookup[key].Count > 0 && !SubtitlePacketIds.Contains(key)) { SubtitlePacketIds.Add(key); } } SubtitlePacketIds.Sort(); }
/// <summary> /// Can be used with e.g. MemoryStream or FileStream /// </summary> /// <param name="ms">Input stream</param> public void Parse(Stream ms, LoadTransportStreamCallback callback) { bool firstVideoPtsFound = false; IsM2TransportStream = false; NumberOfNullPackets = 0; TotalNumberOfPackets = 0; TotalNumberOfPrivateStream1 = 0; TotalNumberOfPrivateStream1Continuation0 = 0; SubtitlePacketIds = new List <int>(); SubtitlePackets = new List <Packet>(); // ProgramAssociationTables = new List<Packet>(); ms.Position = 0; const int packetLength = 188; DetectFormat(ms); var packetBuffer = new byte[packetLength]; var m2TsTimeCodeBuffer = new byte[4]; long position = 0; // 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; while (position < transportStreamLength) { ms.Seek(position, SeekOrigin.Begin); if (IsM2TransportStream) { ms.Read(m2TsTimeCodeBuffer, 0, m2TsTimeCodeBuffer.Length); //var tc = (m2tsTimeCodeBuffer[0]<< 24) | (m2tsTimeCodeBuffer[1] << 16) | (m2tsTimeCodeBuffer[2] << 8) | (m2tsTimeCodeBuffer[3]); position += m2TsTimeCodeBuffer.Length; } ms.Read(packetBuffer, 0, packetLength); byte syncByte = packetBuffer[0]; if (syncByte == Packet.SynchronizationByte) { var packet = new Packet(packetBuffer); if (packet.IsNullPacket) { NumberOfNullPackets++; } else if (!firstVideoPtsFound && packet.IsVideoStream) { if (packet.Payload != null && packet.Payload.Length > 10) { int presentationTimestampDecodeTimestampFlags = packet.Payload[7] >> 6; if (presentationTimestampDecodeTimestampFlags == Helper.B00000010 || presentationTimestampDecodeTimestampFlags == Helper.B00000011) { FirstVideoPts = (ulong)packet.Payload[9 + 4] >> 1; FirstVideoPts += (ulong)packet.Payload[9 + 3] << 7; FirstVideoPts += (ulong)(packet.Payload[9 + 2] & Helper.B11111110) << 14; FirstVideoPts += (ulong)packet.Payload[9 + 1] << 22; FirstVideoPts += (ulong)(packet.Payload[9 + 0] & Helper.B00001110) << 29; firstVideoPtsFound = true; } } } else if (packet.IsProgramAssociationTable) { //var sb = new StringBuilder(); //sb.AppendLine("PacketNo: " + TotalNumberOfPackets + 1); //sb.AppendLine("PacketId: " + packet.PacketId); //sb.AppendLine(); //sb.AppendLine("TransportErrorIndicator: " + packet.TransportErrorIndicator); //sb.AppendLine("PayloadUnitStartIndicator: " + packet.PayloadUnitStartIndicator); //sb.AppendLine("TransportPriority: " + packet.TransportPriority); //sb.AppendLine("ScramblingControl: " + packet.ScramblingControl); //sb.AppendLine("AdaptationFieldExist: " + packet.AdaptationFieldControl); //sb.AppendLine("ContinuityCounter: " + packet.ContinuityCounter); //sb.AppendLine(); //if (packet.AdaptationField != null) //{ //sb.AppendLine("AdaptationFieldLength: " + packet.AdaptationField.Length); //sb.AppendLine("DiscontinuityIndicator: " + packet.AdaptationField.DiscontinuityIndicator); //sb.AppendLine("RandomAccessIndicator: " + packet.AdaptationField.RandomAccessIndicator); //sb.AppendLine("ElementaryStreamPriorityIndicator: " + packet.AdaptationField.ElementaryStreamPriorityIndicator); //sb.AppendLine("PcrFlag: " + packet.AdaptationField.PcrFlag); //sb.AppendLine("OpcrFlag: " + packet.AdaptationField.OpcrFlag); //sb.AppendLine("SplicingPointFlag: " + packet.AdaptationField.SplicingPointFlag); //sb.AppendLine("TransportPrivateDataFlag: " + packet.AdaptationField.TransportPrivateDataFlag); //sb.AppendLine("AdaptationFieldExtensionFlag: " + packet.AdaptationField.AdaptationFieldExtensionFlag); //sb.AppendLine(); //} //sb.AppendLine("TableId: " + packet.ProgramAssociationTable.TableId); //sb.AppendLine("SectionLength: " + packet.ProgramAssociationTable.SectionLength); //sb.AppendLine("TransportStreamId: " + packet.ProgramAssociationTable.TransportStreamId); //sb.AppendLine("VersionNumber: " + packet.ProgramAssociationTable.VersionNumber); //sb.AppendLine("CurrentNextIndicator: " + packet.ProgramAssociationTable.CurrentNextIndicator); //sb.AppendLine("SectionNumber: " + packet.ProgramAssociationTable.SectionNumber); //sb.AppendLine("LastSectionNumber: " + packet.ProgramAssociationTable.LastSectionNumber); //ProgramAssociationTables.Add(packet); } else if (packet.IsPrivateStream1 || SubtitlePacketIds.Contains(packet.PacketId)) { TotalNumberOfPrivateStream1++; SubtitlePackets.Add(packet); if (!SubtitlePacketIds.Contains(packet.PacketId)) { SubtitlePacketIds.Add(packet.PacketId); } if (packet.ContinuityCounter == 0) { TotalNumberOfPrivateStream1Continuation0++; //int pesExtensionlength = 0; //if (12 + packet.AdaptionFieldLength < packetBuffer.Length) // pesExtensionlength = 0xFF & packetBuffer[12 + packet.AdaptionFieldLength]; //int pesOffset = 13 + packet.AdaptionFieldLength + pesExtensionlength; //bool isTeletext = (pesExtensionlength == 0x24 && (0xFF & packetBuffer[pesOffset]) >> 4 == 1); //// workaround uk freesat teletext //if (!isTeletext) // isTeletext = (pesExtensionlength == 0x24 && (0xFF & packetBuffer[pesOffset]) == 0x99); //if (!isTeletext) //{ //} } if (callback != null) { callback.Invoke(ms.Position, transportStreamLength); } } TotalNumberOfPackets++; position += packetLength; } else { position++; } } if (IsM2TransportStream) { DvbSubtitlesLookup = new Dictionary <int, List <TransportStreamSubtitle> >(); SubtitlesLookup = new Dictionary <int, List <DvbSubPes> >(); foreach (int pid in SubtitlePacketIds) { var bdMs = new MemoryStream(); var list = MakeSubtitlePesPackets(pid); var startMsList = new List <ulong>(); var endMsList = new List <ulong>(); foreach (var item in list) { item.WriteToStream(bdMs); if (item.DataIdentifier == 0x16) { if (startMsList.Count <= endMsList.Count) { startMsList.Add(item.PresentationTimestampToMilliseconds()); } else { endMsList.Add(item.PresentationTimestampToMilliseconds()); } } //else if (item.DataBuffer[0] == 0x80) //{ // TODO: Load bd sub after 0x80, so we can be sure to get correct time code??? // endMsList.Add(item.PresentationTimestampToMilliseconds() / 90); //} } SubtitlesLookup.Add(pid, list); bdMs.Position = 0; var sb = new StringBuilder(); var bdList = BluRaySupParser.ParseBluRaySup(bdMs, sb, true); if (bdList.Count > 0) { var subList = new List <TransportStreamSubtitle>(); for (int k = 0; k < bdList.Count; k++) { var bdSup = bdList[k]; ulong startMs = 0; if (k < startMsList.Count) { startMs = startMsList[k]; } ulong endMs = 0; if (k < endMsList.Count) { endMs = endMsList[k]; } subList.Add(new TransportStreamSubtitle(bdSup, startMs, endMs, (ulong)((FirstVideoPts + 45) / 90.0))); } DvbSubtitlesLookup.Add(pid, subList); } } SubtitlePacketIds.Clear(); foreach (int key in DvbSubtitlesLookup.Keys) { SubtitlePacketIds.Add(key); } SubtitlePacketIds.Sort(); return; } // check for SubPictureStreamId = 32 SubtitlesLookup = new Dictionary <int, List <DvbSubPes> >(); foreach (int pid in SubtitlePacketIds) { var list = MakeSubtitlePesPackets(pid); bool hasImageSubtitles = false; foreach (var item in list) { if (item.IsDvbSubpicture) { hasImageSubtitles = true; break; } } if (hasImageSubtitles) { SubtitlesLookup.Add(pid, list); } } SubtitlePacketIds.Clear(); foreach (int key in SubtitlesLookup.Keys) { SubtitlePacketIds.Add(key); } SubtitlePacketIds.Sort(); // Merge packets and set start/end time DvbSubtitlesLookup = new Dictionary <int, List <TransportStreamSubtitle> >(); var firstVideoMs = (ulong)((FirstVideoPts + 45) / 90.0); foreach (int pid in SubtitlePacketIds) { var subtitles = new List <TransportStreamSubtitle>(); var list = GetSubtitlePesPackets(pid); if (list != null) { for (int i = 0; i < list.Count; i++) { var pes = list[i]; pes.ParseSegments(); if (pes.ObjectDataList.Count > 0) { var sub = new TransportStreamSubtitle(); sub.StartMilliseconds = pes.PresentationTimestampToMilliseconds(); sub.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 + 3500; } subtitles.Add(sub); if (sub.StartMilliseconds < firstVideoMs) { firstVideoMs = sub.StartMilliseconds; } } } } foreach (var s in subtitles) { s.OffsetMilliseconds = firstVideoMs; } DvbSubtitlesLookup.Add(pid, subtitles); } SubtitlePacketIds.Clear(); foreach (int key in DvbSubtitlesLookup.Keys) { if (DvbSubtitlesLookup[key].Count > 0) { SubtitlePacketIds.Add(key); } } SubtitlePacketIds.Sort(); }
public List <BluRaySupParser.PcsData> LoadSubtitles(string supFileName) { var log = new StringBuilder(); return(BluRaySupParser.ParseBluRaySup(supFileName, log)); }
public static List <SubtitleEvent> ReadMatroskaBluraySup(string inputFile, int tracknumber) { List <SubtitleEvent> events = new List <SubtitleEvent>(); var matroska = new MatroskaFile(inputFile); var matroskaSubtitleInfo = matroska.GetTracks()[tracknumber]; var sub = matroska.GetSubtitle(matroskaSubtitleInfo.TrackNumber, (p, t) => Console.WriteLine("Progress: {0} out of {1}", p, t)); var subtitles = new List <BluRaySupParser.PcsData>(); var log = new StringBuilder(); var clusterStream = new MemoryStream(); var lastPalettes = new Dictionary <int, List <PaletteInfo> >(); var lastBitmapObjects = new Dictionary <int, List <BluRaySupParser.OdsData> >(); foreach (var p in sub) { byte[] buffer = p.GetData(matroskaSubtitleInfo); if (buffer != null && buffer.Length > 2) { clusterStream.Write(buffer, 0, buffer.Length); if (ContainsBluRayStartSegment(buffer)) { if (subtitles.Count > 0 && subtitles[subtitles.Count - 1].StartTime == subtitles[subtitles.Count - 1].EndTime) { subtitles[subtitles.Count - 1].EndTime = (long)((p.Start - 1) * 90.0); } clusterStream.Position = 0; var list = BluRaySupParser.ParseBluRaySup(clusterStream, log, true, lastPalettes, lastBitmapObjects); foreach (var sup in list) { sup.StartTime = (long)((p.Start - 1) * 90.0); sup.EndTime = (long)((p.End - 1) * 90.0); subtitles.Add(sup); // fix overlapping if (subtitles.Count > 1 && sub[subtitles.Count - 2].End > sub[subtitles.Count - 1].Start) { subtitles[subtitles.Count - 2].EndTime = subtitles[subtitles.Count - 1].StartTime - 1; } } clusterStream = new MemoryStream(); } } else if (subtitles.Count > 0) { var lastSub = subtitles[subtitles.Count - 1]; if (lastSub.StartTime == lastSub.EndTime) { lastSub.EndTime = (long)((p.Start - 1) * 90.0); if (lastSub.EndTime - lastSub.StartTime > 1000000) { lastSub.EndTime = lastSub.StartTime; } } } } clusterStream.Dispose(); int i = 0; foreach (var line in subtitles) { var bitmap = BluRaySupParser.SupDecoder.DecodeImage(line.PcsObjects[0], line.BitmapObjects[0], line.PaletteInfos); var image_data = ImageUtil.ConvertToPngBytes(bitmap); events.Add(new SubtitleEvent() { StartTime = TimeSpan.FromMilliseconds(line.StartTime / 90.0), EndTime = TimeSpan.FromMilliseconds(line.EndTime / 90.0), MimeType = "image/png", Image = image_data, Origin = line.PcsObjects[0].Origin }); i++; } return(events); }
/// <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) { bool firstVideoPtsFound = false; 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 Dictionary <int, List <DvbSubPes> >(); // 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; while (position < transportStreamLength) { ms.Seek(position, SeekOrigin.Begin); if (IsM2TransportStream) { ms.Read(m2TsTimeCodeBuffer, 0, m2TsTimeCodeBuffer.Length); position += m2TsTimeCodeBuffer.Length; } ms.Read(packetBuffer, 0, packetLength); byte syncByte = packetBuffer[0]; if (syncByte == Packet.SynchronizationByte) { var packet = new Packet(packetBuffer); if (packet.IsNullPacket) { NumberOfNullPackets++; } else if (!firstVideoPtsFound && packet.IsVideoStream) { if (packet.Payload != null && packet.Payload.Length > 10) { int presentationTimestampDecodeTimestampFlags = packet.Payload[7] >> 6; if (presentationTimestampDecodeTimestampFlags == Helper.B00000010 || presentationTimestampDecodeTimestampFlags == Helper.B00000011) { FirstVideoPts = (ulong)packet.Payload[9 + 4] >> 1; FirstVideoPts += (ulong)packet.Payload[9 + 3] << 7; FirstVideoPts += (ulong)(packet.Payload[9 + 2] & Helper.B11111110) << 14; FirstVideoPts += (ulong)packet.Payload[9 + 1] << 22; FirstVideoPts += (ulong)(packet.Payload[9 + 0] & Helper.B00001110) << 29; firstVideoPtsFound = true; } } } else if (packet.IsProgramAssociationTable) { } else if (packet.IsPrivateStream1 || SubtitlePacketIds.Contains(packet.PacketId)) { TotalNumberOfPrivateStream1++; if (!SubtitlePacketIds.Contains(packet.PacketId)) { SubtitlePacketIds.Add(packet.PacketId); } if (!IsM2TransportStream && packet.PayloadUnitStartIndicator) { var list = MakeSubtitlePesPackets(packet.PacketId, SubtitlePackets); bool hasImageSubtitles = false; foreach (var item in list) { if (item.IsDvbSubpicture) { hasImageSubtitles = true; break; } } if (hasImageSubtitles) { if (SubtitlesLookup.ContainsKey(packet.PacketId)) { SubtitlesLookup[packet.PacketId].AddRange(list); } else { SubtitlesLookup.Add(packet.PacketId, list); } } SubtitlePackets.RemoveAll(p => p.PacketId == packet.PacketId); } SubtitlePackets.Add(packet); if (packet.ContinuityCounter == 0) { TotalNumberOfPrivateStream1Continuation0++; //int pesExtensionlength = 0; //if (12 + packet.AdaptionFieldLength < packetBuffer.Length) // pesExtensionlength = 0xFF & packetBuffer[12 + packet.AdaptionFieldLength]; //int pesOffset = 13 + packet.AdaptionFieldLength + pesExtensionlength; //bool isTeletext = (pesExtensionlength == 0x24 && (0xFF & packetBuffer[pesOffset]) >> 4 == 1); //// workaround uk freesat teletext //if (!isTeletext) // isTeletext = (pesExtensionlength == 0x24 && (0xFF & packetBuffer[pesOffset]) == 0x99); //if (!isTeletext) //{ //} } } TotalNumberOfPackets++; position += packetLength; if (TotalNumberOfPackets % 100000 == 0) { callback.Invoke(ms.Position, transportStreamLength); } } else { position++; } } if (IsM2TransportStream) { DvbSubtitlesLookup = new Dictionary <int, List <TransportStreamSubtitle> >(); SubtitlesLookup = new Dictionary <int, List <DvbSubPes> >(); foreach (int pid in SubtitlePacketIds) { var bdMs = new MemoryStream(); var list = MakeSubtitlePesPackets(pid, SubtitlePackets); var currentList = new List <DvbSubPes>(); var sb = new StringBuilder(); var subList = new List <TransportStreamSubtitle>(); 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; subList.Add(new TransportStreamSubtitle(bdList[0], startMs, endMs, (ulong)(FirstVideoPts / 90.0))); } bdMs = new MemoryStream(); currentList.Clear(); } } SubtitlesLookup.Add(pid, list); DvbSubtitlesLookup.Add(pid, subList); } SubtitlePacketIds.Clear(); foreach (int key in DvbSubtitlesLookup.Keys) { SubtitlePacketIds.Add(key); } SubtitlePacketIds.Sort(); return; } // check for SubPictureStreamId = 32 foreach (int pid in SubtitlePacketIds) { var list = MakeSubtitlePesPackets(pid, SubtitlePackets); bool hasImageSubtitles = false; foreach (var item in list) { if (item.IsDvbSubpicture) { hasImageSubtitles = true; break; } } if (hasImageSubtitles) { if (SubtitlesLookup.ContainsKey(pid)) { SubtitlesLookup[pid].AddRange(list); } else { SubtitlesLookup.Add(pid, list); } } SubtitlePackets.RemoveAll(p => p.PacketId == pid); } SubtitlePacketIds.Clear(); foreach (int key in SubtitlesLookup.Keys) { SubtitlePacketIds.Add(key); } SubtitlePacketIds.Sort(); // Merge packets and set start/end time DvbSubtitlesLookup = new Dictionary <int, List <TransportStreamSubtitle> >(); var firstVideoMs = (ulong)(FirstVideoPts / 90.0); foreach (int pid in SubtitlePacketIds) { var subtitles = new List <TransportStreamSubtitle>(); var list = ParseAndRemoveEmpty(GetSubtitlePesPackets(pid)); 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; } subtitles.Add(sub); if (sub.StartMilliseconds < firstVideoMs) { firstVideoMs = sub.StartMilliseconds; } } } foreach (var s in subtitles) { s.OffsetMilliseconds = firstVideoMs; } DvbSubtitlesLookup.Add(pid, subtitles); } SubtitlePacketIds.Clear(); foreach (int key in DvbSubtitlesLookup.Keys) { if (DvbSubtitlesLookup[key].Count > 0) { SubtitlePacketIds.Add(key); } } SubtitlePacketIds.Sort(); }
private void importTimeCodesToolStripMenuItem_Click(object sender, EventArgs e) { if (_subtitle.Paragraphs.Count < 1) { return; } openFileDialog1.Title = Configuration.Settings.Language.General.OpenSubtitle; openFileDialog1.FileName = string.Empty; openFileDialog1.Filter = UiUtil.SubtitleExtensionFilter.Value; if (openFileDialog1.ShowDialog(this) == DialogResult.OK) { var timeCodeSubtitle = new Subtitle(); SubtitleFormat format = null; if (openFileDialog1.FileName.EndsWith(".sup", StringComparison.OrdinalIgnoreCase) && FileUtil.IsBluRaySup(openFileDialog1.FileName)) { var log = new StringBuilder(); var subtitles = BluRaySupParser.ParseBluRaySup(openFileDialog1.FileName, log); if (subtitles.Count > 0) { foreach (var sup in subtitles) { timeCodeSubtitle.Paragraphs.Add(new Paragraph(sup.StartTimeCode, sup.EndTimeCode, string.Empty)); } format = new SubRip(); // just to set format to something } } if (format == null) { format = timeCodeSubtitle.LoadSubtitle(openFileDialog1.FileName, out var encoding, null); } if (format == null) { var formats = SubtitleFormat.GetBinaryFormats(true).Union(SubtitleFormat.GetTextOtherFormats()).Union(new SubtitleFormat[] { new TimeCodesOnly1(), new TimeCodesOnly2() }).ToArray(); format = SubtitleFormat.LoadSubtitleFromFile(formats, openFileDialog1.FileName, timeCodeSubtitle); } if (format == null) { return; } if (timeCodeSubtitle.Paragraphs.Count != _subtitle.Paragraphs.Count) { var text = string.Format(Configuration.Settings.Language.Main.ImportTimeCodesDifferentNumberOfLinesWarning, timeCodeSubtitle.Paragraphs.Count, _subtitle.Paragraphs.Count); if (MessageBox.Show(this, text, Text, MessageBoxButtons.YesNoCancel) != DialogResult.Yes) { return; } } int count = 0; for (int i = 0; i < timeCodeSubtitle.Paragraphs.Count; i++) { var existing = _subtitle.GetParagraphOrDefault(i); var newTimeCode = timeCodeSubtitle.GetParagraphOrDefault(i); if (existing == null || newTimeCode == null) { break; } existing.StartTime.TotalMilliseconds = newTimeCode.StartTime.TotalMilliseconds; existing.EndTime.TotalMilliseconds = newTimeCode.EndTime.TotalMilliseconds; count++; } MessageBox.Show(string.Format(Configuration.Settings.Language.Main.TimeCodeImportedFromXY, Path.GetFileName(openFileDialog1.FileName), count)); var idx = subtitleListView1.SelectedItems[0].Index; subtitleListView1.Fill(_subtitle); subtitleListView1.SelectIndexAndEnsureVisible(_subtitle.GetParagraphOrDefault(idx)); } }