コード例 #1
0
ファイル: Ebml.cs プロジェクト: johnnoel/ebmlreader
 public Element(long offset, Document doc)
 {
     this.Offset = offset;
     this.Document = doc;
     this.Parse();
 }
コード例 #2
0
ファイル: Program.cs プロジェクト: johnnoel/ebmlreader
        static void Main(string[] args)
        {
            if (args.Length != 2)
            {
                Console.WriteLine("Usage: ebmlreader <inputfile.mkv> <outputfile.ass>");
                return;
            }

            string inputFilename = args[0];
            string outputFilename = args[1];

            if (!File.Exists(inputFilename))
            {
                Console.WriteLine("Input file \"" + inputFilename + "\" does not exist");
                return;
            }

            StreamWriter subsFile = new StreamWriter(File.Open(outputFilename, FileMode.Create), System.Text.Encoding.UTF8);

            Document doc = new Document(File.Open(inputFilename, FileMode.Open));
            Element segment = doc.TopLevelElements[1];

            // first off find the track (if any) that holds the ASS
            long subsTrackNumber = -1;
            Element[] trackElements = segment.FindElements(
                CreatePath(Matroska.Tracks.ID, Matroska.TrackEntry.ID),
                null);

            if (trackElements.Length == 0)
            {
                Console.WriteLine("Unable to find any tracks (invalid Matroska file?)");
                return;
            }

            foreach (Element trackElement in trackElements)
            {
                Element codecIdElement = trackElement.FindFirstElement(CreatePath(Matroska.TrackEntryCodecId.ID));

                if (codecIdElement.Data.GetAsString() == "S_TEXT/ASS")
                {
                    // store the codec private (ASS header essentially)
                    Element codecPrivateElement = trackElement.FindFirstElement(CreatePath(Matroska.TrackEntryCodecPrivate.ID));
                    if (codecPrivateElement == null)
                    {
                        break;
                    }

                    //subsFile.Write(codecPrivateElement.Data.Get());
                    subsFile.WriteLine(codecPrivateElement.Data.GetAsString());

                    // grab the track number for later processing
                    Element trackNumberElement = trackElement.FindFirstElement(CreatePath(Matroska.TrackNumber.ID));
                    if (trackNumberElement == null)
                    {
                        break;
                    }
                    subsTrackNumber = trackNumberElement.Data.GetAsUnsignedInteger();

                    break;
                }
            }

            if (subsTrackNumber == -1)
            {
                Console.WriteLine("Unable to find subtitle track");
                return;
            }

            // get the timecode scale of the segment
            Element timecodeScaleElement = segment.FindFirstElement(
                CreatePath(Matroska.SegmentInfo.ID, Matroska.TimecodeScale.ID));
            long timecodeScale = timecodeScaleElement.Data.GetAsUnsignedInteger();

            // get the clusters (as we need the timecode from the cluster)
            Element[] clusters = segment.FindElements(CreatePath(Matroska.Cluster.ID), null);

            if (clusters.Length == 0)
            {
                Console.WriteLine("Unable to find any clusters (?!)");
                return;
            }

            foreach (Element clusterElement in clusters)
            {
                // get the timecode (in nanoseconds) for this cluster
                Element timecodeElement = clusterElement.FindFirstElement(CreatePath(Matroska.ClusterTimecode.ID));
                long timecode = timecodeElement.Data.GetAsUnsignedInteger() * timecodeScale;

                // get the block group (because we need the duration)
                Element[] blockGroups = clusterElement.FindElements(CreatePath(Matroska.BlockGroup.ID), null);
                // because lines may not come out in read order
                List<ASS.Line> lines = new List<ASS.Line>();

                foreach (Element blockGroupElement in blockGroups)
                {
                    // and finally get the blocks
                    Element[] blocks = blockGroupElement.FindElements(CreatePath(Matroska.Block.ID), null);

                    foreach (Element blockElement in blocks)
                    {
                        Matroska.Block mblock = new Matroska.Block(blockElement);
                        if (mblock.TrackNumber != subsTrackNumber) // skip over any non-subs track block
                        {
                            continue;
                        }

                        long blockTimecode = mblock.Timecode * timecodeScale;

                        Element blockDurationElement = blockGroupElement.FindFirstElement(CreatePath(Matroska.BlockDuration.ID));
                        long blockDuration = blockDurationElement.Data.GetAsUnsignedInteger() * timecodeScale;

                        // all of the timecodes so far have been stored in nanoseconds (according to timecodeScale)
                        // this converts them into seconds; those d's are important
                        double start = (timecode + blockTimecode) / 1000000000d;
                        double end = (timecode + blockTimecode + blockDuration) / 1000000000d;

                        ASS.Line l = ASS.Line.FromMatroska(mblock.Data.GetAsString());
                        l.Start = ASS.Time.Format(start);
                        l.End = ASS.Time.Format(end);

                        lines.Add(l);
                    }
                }

                lines.Sort((x, y) => x.ReadOrder.CompareTo(y.ReadOrder));
                foreach (ASS.Line l in lines)
                {
                    subsFile.WriteLine(l);
                }
            }

            subsFile.Flush();
            subsFile.Close();
        }