/// <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();
        }
Example #2
0
        public void LoadSubtitleFromM2ts(Subtitle subtitle, Stream ms)
        {
            var subtitlePackets = new List<Packet>();
            const int packetLength = 188;
            bool isM2TransportStream = DetectFormat(ms);
            var packetBuffer = new byte[packetLength];
            var m2TsTimeCodeBuffer = new byte[4];
            long position = 0;
            ms.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] & Helper.B00111111);
                    // should m2ts time code be used in any way?
                    var msecs = (ulong)Math.Round((tc) / 27.0); // 27 or 90?
                    var tc2 = new TimeCode(msecs);
                    System.Diagnostics.Debug.WriteLine(tc2);
                    position += m2TsTimeCodeBuffer.Length;
                }

                ms.Read(packetBuffer, 0, packetLength);
                byte syncByte = packetBuffer[0];
                if (syncByte == Packet.SynchronizationByte)
                {
                    var packet = new Packet(packetBuffer);
                    if (packet.PacketId == TextSubtitleStreamPid)
                    {
                        subtitlePackets.Add(packet);
                    }
                    position += packetLength;
                }
                else
                {
                    position++;
                }
            }

            //TODO: merge ts packets

            PresentationSegments = new List<DialogPresentationSegment>();
            foreach (var item in subtitlePackets)
            {
                if (item.Payload != null && item.Payload.Length > 10 && VobSub.VobSubParser.IsPrivateStream2(item.Payload, 0))
                {
                    if (item.Payload[6] == SegmentTypeDialogPresentation)
                    {
                        var dps = new DialogPresentationSegment(item.Payload);
                        PresentationSegments.Add(dps);
                        subtitle.Paragraphs.Add(new Paragraph(dps.Text.Trim(), dps.StartPtsMilliseconds, dps.EndPtsMilliseconds));
                    }
                    else if (item.Payload[6] == SegmentTypeDialogStyle)
                    {
                        StyleSegment = new DialogStyleSegment(item.Payload);
                    }
                }
            }

            subtitle.Renumber();
        }