/// <summary> /// Read the cues from the root cue node. /// </summary> public static Cue[] GetCues(MatroskaSegment segment, Stream reader) { MatroskaTree cues = segment.GetChildFromSeek(reader, MatroskaTree.Segment_Cues); if (cues == null) { return(new Cue[0]); } segment.Position(reader); long offset = reader.Position; MatroskaTree[] sources = cues.GetChildren(reader, MatroskaTree.Segment_Cues_CuePoint); Cue[] results = new Cue[sources.Length]; for (int i = 0; i < sources.Length; ++i) { long time = sources[i].GetChildValue(reader, MatroskaTree.Segment_Cues_CuePoint_CueTime); MatroskaTree position = sources[i].GetChild(reader, MatroskaTree.Segment_Cues_CuePoint_CueTrackPositions); results[i] = new Cue( time, position.GetChildValue(reader, MatroskaTree.Segment_Cues_CuePoint_CueTrackPositions_CueTrack), offset + position.GetChildValue(reader, MatroskaTree.Segment_Cues_CuePoint_CueTrackPositions_CueClusterPosition) ); } return(results); }
/// <summary> /// Fetch all child instances of a tag. /// </summary> public MatroskaTree[] GetChildren(Stream reader, int tag) { int tags = 0; for (int i = 0, c = children.Count; i < c; ++i) { if (children[i].Tag == tag) { ++tags; } } reader.Position = nextTag; while (reader.Position < end) { MatroskaTree subtree = new MatroskaTree(reader); children.Add(subtree); if (subtree.Tag == tag) { ++tags; } } nextTag = end; MatroskaTree[] result = new MatroskaTree[tags]; for (int i = 0, c = children.Count; i < c; ++i) { if (children[i].Tag == tag) { result[^ tags] = children[i];
/// <summary> /// Reads the segment with its seek header. /// </summary> public MatroskaSegment(Stream reader) : base(reader) { MatroskaTree seekHead = GetChild(reader, Segment_SeekHead); if (seekHead == null) { reader.Position = end; return; } MatroskaTree[] seekInputs = seekHead.GetChildren(reader, Segment_SeekHead_Seek); for (int i = 0; i < seekInputs.Length; ++i) { seeks[(int)seekInputs[i].GetChildValue(reader, Segment_SeekHead_Seek_SeekID)] = seekInputs[i].GetChildValue(reader, Segment_SeekHead_Seek_SeekPosition); } reader.Position = end; }
/// <summary> /// Fetch the first child of a tag if it exists. /// </summary> public MatroskaTree GetChild(Stream reader, int tag) { for (int i = 0, c = children.Count; i < c; ++i) { if (children[i].Tag == tag) { return(children[i]); } } reader.Position = nextTag; while (nextTag < end) { MatroskaTree subtree = new MatroskaTree(reader); children.Add(subtree); if (subtree.Tag == tag) { return(subtree); } nextTag = reader.Position; } return(null); }
/// <summary> /// All blocks of the cluster, in order. /// </summary> public IReadOnlyList <Block> GetBlocks(Stream reader) { if (blocks != null) { return(blocks); } MemoryStream stream = new MemoryStream(source.GetRawData(reader)); blocks = new List <Block>(); long end = stream.Length - 2; // Safety barrier, a byte might remain, but a 2 byte child is impossible while (stream.Position < end) { MatroskaTree child = new MatroskaTree(stream); long continueFrom = stream.Position; // Block groups (Matroska v1) if (child.Tag == MatroskaTree.Segment_Cluster_BlockGroup) { MatroskaTree[] blocksHere = child.GetChildren(stream, MatroskaTree.Segment_Cluster_BlockGroup_Block); for (int i = 0; i < blocksHere.Length; ++i) { blocks.Add(new Block(stream, blocksHere[i])); } } // Simple blocks (Matroska v2) else if (child.Tag == MatroskaTree.Segment_Cluster_SimpleBlock) { blocks.Add(new Block(stream, child)); } stream.Position = continueFrom; } return(blocks); }
/// <summary> /// Parse metadata from a cluster. /// </summary> public Cluster(Stream reader, MatroskaTree source) { this.source = source; TimeStamp = source.GetChildValue(reader, MatroskaTree.Segment_Cluster_Timestamp); }
/// <summary> /// Parse the metadata of a data block. /// </summary> public Block(Stream reader, MatroskaTree source) { source.Position(reader); this.reader = reader; long start = reader.Position; Track = VarInt.ReadValue(reader); TimeStamp = reader.ReadInt16BE(); flags = (byte)reader.ReadByte(); Lacing lacing = LacingType; if (lacing != Lacing.None) { int x = reader.ReadByte(); frameCount = x + 1; frameSizes = new int[frameCount]; firstFrame = reader.Position; switch (lacing) { case Lacing.Xiph: byte frame = 0; long totalSize = 0; while (frame < frameCount - 1) { byte value; int sum = 0; do { value = (byte)reader.ReadByte(); sum += value; } while (value == 255); totalSize += sum; frameSizes[frame++] = sum; } frameSizes[frame] = (int)(source.Length - (reader.Position - start) - totalSize); break; case Lacing.FixedSize: int step = (int)((source.Length - (firstFrame - start)) / frameCount); for (byte i = 0; i < frameCount; ++i) { frameSizes[i] = step; } break; case Lacing.EBML: frameSizes[0] = (int)VarInt.ReadValue(reader); long last = -frameSizes[0]; for (int i = 1; i < frameCount - 1; ++i) { frameSizes[i] = frameSizes[i - 1] + (int)VarInt.ReadSignedValue(reader); last -= frameSizes[i]; } frameSizes[frameCount - 1] = (int)(last + source.Length - (reader.Position - start)); break; default: break; } } else { firstFrame = reader.Position; frameCount = 1; frameSizes = new int[1] { (int)(source.Length - (firstFrame - start)) }; } }
/// <summary> /// Get a specific child by its order of the same kind of children. /// </summary> public MatroskaTree GetChild(Stream reader, int tag, int index) { if (childIndices == null) { childIndices = new Dictionary <int, List <int> >(); } List <int> indices; if (childIndices.ContainsKey(tag)) { indices = childIndices[tag]; } else { indices = childIndices[tag] = new List <int>(); } int c = indices.Count; if (index < indices.Count) { return(children[indices[index]]); } int lastChild = 0; if (c != 0) { lastChild = indices[c - 1] + 1; } for (int i = lastChild, childCount = children.Count; i < childCount; ++i) { if (children[i].Tag == tag) { indices.Add(i); if (c++ == index) { return(children[i]); } } } int tagIndex = children.Count; reader.Position = nextTag; while (nextTag < end) { MatroskaTree subtree = TryCreate(reader, end); if (subtree == null) { nextTag = end; return(null); } children.Add(subtree); if (subtree.Tag == tag) { indices.Add(tagIndex); if (c++ == index) { nextTag = reader.Position; return(subtree); } } ++tagIndex; } nextTag = reader.Position; return(null); }