private void ReadHeader(EBMLreader element) { string doctype = null; ulong i = 0; while (i < element.DataSize) { var child = new EBMLreader(element, element.DataOffset + i); switch ((EBMLID)child.ID) { case EBMLID.EBMLDocType: doctype = child.ReadString(); break; } i += child.Size; } // Check DocType if (string.IsNullOrEmpty(doctype) || doctype != "matroska" && doctype != "webm") { throw new UnsupportedFormatException("DocType is not matroska or webm"); } }
private void ReadCreateSegmentInfo(EBMLreader element, EBMLelement ebml_sinfo) { ulong i = 0; while (i < element.DataSize) { var child = new EBMLreader(element, element.DataOffset + i); switch (child.ID) { case MatroskaID.Duration: duration_unscaled = child.ReadDouble(); if (time_scale > 0) { duration = TimeSpan.FromMilliseconds(duration_unscaled * time_scale / 1000000); } break; case MatroskaID.TimeCodeScale: time_scale = child.ReadULong(); if (duration_unscaled > 0) { duration = TimeSpan.FromMilliseconds(duration_unscaled * time_scale / 1000000); } break; case MatroskaID.Title: break; } i += child.Size; } }
/// <summary> /// Constructs a <see cref="Track" /> parsing from provided /// file data. /// Parsing will be done reading from _file at position references by /// parent element's data section. /// </summary> /// <param name="_file"><see cref="File" /> instance to read from.</param> /// <param name="element">Parent <see cref="EBMLreader" />.</param> public Track(File _file, EBMLreader element) { ulong i = 0; while (i < element.DataSize) { var child = new EBMLreader(_file, element.DataOffset + i); switch (child.ID) { case MatroskaID.TrackNumber: track_number = child.ReadULong(); break; case MatroskaID.TrackUID: _UID = child.ReadULong(); break; case MatroskaID.CodecID: track_codec_id = child.ReadString(); break; case MatroskaID.CodecName: track_codec_name = child.ReadString(); break; case MatroskaID.TrackName: track_name = child.ReadString(); break; case MatroskaID.TrackLanguage: track_language = child.ReadString(); break; case MatroskaID.TrackFlagEnabled: track_enabled = child.ReadBool(); break; case MatroskaID.TrackFlagDefault: track_default = child.ReadBool(); break; case MatroskaID.CodecPrivate: codec_data = child.ReadBytes(); break; default: UnknownElements.Add(child); break; } i += child.Size; } }
/// <summary> /// Construct a <see cref="AudioTrack" /> reading information from /// provided file data. /// Parsing will be done reading from _file at position references by /// parent element's data section. /// </summary> /// <param name="_file"><see cref="File" /> instance to read from.</param> /// <param name="element">Parent <see cref="EBMLreader" />.</param> public AudioTrack(File _file, EBMLreader element) : base(_file, element) { MatroskaID matroska_id; // Here we handle the unknown elements we know, and store the rest foreach (var elem in base.UnknownElements) { matroska_id = elem.ID; switch (matroska_id) { case MatroskaID.TrackAudio: { ulong i = 0; while (i < elem.DataSize) { var child = new EBMLreader(_file, elem.DataOffset + i); matroska_id = child.ID; switch (matroska_id) { case MatroskaID.AudioChannels: channels = child.ReadULong(); break; case MatroskaID.AudioBitDepth: depth = child.ReadULong(); break; case MatroskaID.AudioSamplingFreq: rate = child.ReadDouble(); break; default: UnknownElements.Add(child); break; } i += child.Size; } break; } default: UnknownElements.Add(elem); break; } } }
/// <summary> /// Create a new abstract <see cref="EBMLreader" /> with arbitrary attributes, /// without reading its information on the file. /// </summary> /// <param name="parent">The <see cref="EBMLreader" /> that contains the instance to be described.</param> /// <param name="position">Position in the file.</param> /// <param name="ebmlid">EBML ID of the element</param> /// <param name="size">Total size of the EBML, in bytes</param> public EBMLreader(EBMLreader parent, ulong position, MatroskaID ebmlid, ulong size = 0) { // Keep a reference to the file if (parent != null) { file = parent.file; } Parent = parent; // Initialize attributes offset = position; DataOffset = offset; ebml_id = (uint)ebmlid; DataSize = size; }
/// <summary> /// Reads (and Write, if file Mode is Write) the file with a specified read style. /// </summary> /// <param name="propertiesStyle"> /// A <see cref="ReadStyle" /> value specifying at what level /// of accuracy to read the media properties, or /// <see /// cref="ReadStyle.None" /> /// to ignore the properties. /// </param> private void ReadWrite(ReadStyle propertiesStyle) { var offset = ReadLeadText(); var hasSegment = false; while (offset < (ulong)Length) { EBMLreader element; try { element = new EBMLreader(this, offset); } catch (Exception) { // Sometimes, the file has zero padding at the end if (hasSegment) { break; // Avoid crash } throw; } var ebml_id = (EBMLID)element.ID; var matroska_id = element.ID; switch (ebml_id) { case EBMLID.EBMLHeader: ReadHeader(element); break; } switch (matroska_id) { case MatroskaID.Segment: ReadWriteSegment(element, propertiesStyle); hasSegment = true; break; } offset += element.Size; } }
private void ReadTracks(EBMLreader element) { ulong i = 0; while (i < element.DataSize) { var child = new EBMLreader(element, element.DataOffset + i); switch (child.ID) { case MatroskaID.TrackEntry: ReadTrackEntry(child); break; } i += child.Size; } }
private void ReadAttachments(EBMLreader element, ReadStyle propertiesStyle) { ulong i = 0; while (i < (ulong)(long)element.DataSize) { var child = new EBMLreader(element, element.DataOffset + i); switch (child.ID) { case MatroskaID.AttachedFile: ReadAttachedFile(child, propertiesStyle); break; } i += child.Size; } }
/// <summary> /// Constructs a child <see cref="EBMLreader" /> reading the data from the /// EBML parent at the provided file position. /// </summary> /// <param name="parent">The <see cref="EBMLreader" /> that contains the instance to be created.</param> /// <param name="position">Position in the file to start reading from.</param> public EBMLreader(EBMLreader parent, ulong position) { if (parent == null) { throw new ArgumentNullException("file"); } // Keep a reference to the file file = parent.file; Parent = parent; // Initialize attributes offset = position; DataOffset = position; ebml_id = 0; DataSize = 0; // Actually read the EBML on the file Read(true); }
private void ReadTrackEntry(EBMLreader element) { ulong i = 0; while (i < element.DataSize) { var child = new EBMLreader(element, element.DataOffset + i); switch (child.ID) { case MatroskaID.TrackType: { switch ((TrackType)child.ReadULong()) { case TrackType.Video: { var track = new VideoTrack(this, element); tracks.Add(track); break; } case TrackType.Audio: { var track = new AudioTrack(this, element); tracks.Add(track); break; } } break; } } i += child.Size; } }
private void ReadAttachedFile(EBMLreader element, ReadStyle propertiesStyle) { ulong i = 0; #pragma warning restore 219 while (i < element.DataSize) { var child = new EBMLreader(element, element.DataOffset + i); switch (child.ID) { case MatroskaID.FileName: #pragma warning disable 219 // Assigned, never read _ = child.ReadString(); break; case MatroskaID.FileMimeType: _ = child.ReadString(); break; case MatroskaID.FileDescription: _ = child.ReadString(); break; case MatroskaID.FileData: break; case MatroskaID.FileUID: _ = child.ReadULong(); break; } i += child.Size; } }
/// <summary> /// Constructs a <see cref="VideoTrack" /> parsing from provided /// file data. /// Parsing will be done reading from _file at position references by /// parent element's data section. /// </summary> /// <param name="_file"><see cref="File" /> instance to read from.</param> /// <param name="element">Parent <see cref="EBMLreader" />.</param> public VideoTrack(File _file, EBMLreader element) : base(_file, element) { MatroskaID matroska_id; // Here we handle the unknown elements we know, and store the rest foreach (var elem in base.UnknownElements) { matroska_id = elem.ID; switch (matroska_id) { case MatroskaID.TrackVideo: { ulong i = 0; while (i < elem.DataSize) { var child = new EBMLreader(_file, elem.DataOffset + i); matroska_id = child.ID; switch (matroska_id) { case MatroskaID.VideoDisplayWidth: disp_width = child.ReadULong(); break; case MatroskaID.VideoDisplayHeight: disp_height = child.ReadULong(); break; case MatroskaID.VideoPixelWidth: width = child.ReadULong(); break; case MatroskaID.VideoPixelHeight: height = child.ReadULong(); break; case MatroskaID.VideoFrameRate: framerate = child.ReadDouble(); break; case MatroskaID.VideoFlagInterlaced: interlaced = child.ReadBool(); break; case MatroskaID.VideoAspectRatioType: ratio_type = (VideoAspectRatioType)child.ReadULong(); break; case MatroskaID.VideoColourSpace: fourcc = child.ReadBytes(); break; default: UnknownElements.Add(child); break; } i += child.Size; } break; } case MatroskaID.TrackDefaultDuration: var tmp = elem.ReadULong(); framerate = 1000000000.0 / tmp; break; default: UnknownElements.Add(elem); break; } } }
private bool ReadSeekHead(EBMLreader element, List <EBMLreader> segm_list) { MatroskaID ebml_id = 0; ulong ebml_position = 0; ulong i = 0; while (i < element.DataSize) { var ebml_seek = new EBMLreader(element, element.DataOffset + i); var matroska_id = ebml_seek.ID; if (matroska_id != MatroskaID.Seek) { return(false); // corrupted SeekHead } ulong j = 0; while (j < ebml_seek.DataSize) { var child = new EBMLreader(ebml_seek, ebml_seek.DataOffset + j); matroska_id = child.ID; switch (matroska_id) { case MatroskaID.SeekID: ebml_id = (MatroskaID)child.ReadULong(); break; case MatroskaID.SeekPosition: ebml_position = child.ReadULong() + element.Offset; break; } j += child.Size; } if (ebml_id > 0 && ebml_position > 0) { // Create abstract EBML representation of the segment EBML var ebml = new EBMLreader(element.Parent, ebml_position, ebml_id); // Sort the seek-entries by increasing position order int k; for (k = segm_list.Count - 1; k >= 0; k--) { if (ebml_position > segm_list[k] .Offset) { break; } } segm_list.Insert(k + 1, ebml); // Chained SeekHead recursive read if (ebml_id == MatroskaID.SeekHead) { if (!ebml.Read()) { return(false); // Corrupted } ReadSeekHead(ebml, segm_list); } } i += ebml_seek.Size; } return(true); }
private List <EBMLreader> ReadSegments(EBMLreader element, bool allowSeekHead) { var segm_list = new List <EBMLreader>(10); var foundCluster = false; // find first Cluster ulong i = 0; while (i < element.DataSize) { EBMLreader child; try { child = new EBMLreader(element, element.DataOffset + i); } catch { MarkAsCorrupt("Truncated file or invalid EBML entry"); break; // Corrupted file: quit here and good luck for the rest } var matroska_id = child.ID; var refInSeekHead = false; switch (matroska_id) { case MatroskaID.SeekHead: if (allowSeekHead) { // Take only the first SeekHead into account var ebml_seek = new List <EBMLreader>(10) { child }; if (ReadSeekHead(child, ebml_seek)) { // Always reference the first element if (ebml_seek[0] .Offset > element.DataOffset) { ebml_seek.Insert(0, segm_list[0]); } segm_list = ebml_seek; i = element.DataSize; // Exit the loop: we got what we need } else { MarkAsCorrupt("Invalid Meta Seek"); refInSeekHead = true; } } else { refInSeekHead = true; } break; case MatroskaID.Void: // extend SeekHead space to following void break; case MatroskaID.Cluster: // reference first Cluster only (too many) refInSeekHead = !foundCluster; foundCluster = true; break; // Reference the following elements case MatroskaID.Cues: case MatroskaID.Tracks: case MatroskaID.SegmentInfo: case MatroskaID.Tags: case MatroskaID.Attachments: default: refInSeekHead = true; break; } i += child.Size; if (refInSeekHead || i == 0) { segm_list.Add(child); } } return(segm_list); }
private void ReadWriteSegment(EBMLreader element, ReadStyle propertiesStyle, bool retry = true) { // First make reference of all EBML elements at level 1 (top) in the Segment var segm_list = ReadSegments(element, retry); // Try to get it from SeekHead the first time (way faster) // Now process (read and prepare to write) the referenced elements we care about EBMLelement ebml_sinfo = null; var valid = true; foreach (var child in segm_list) { // the child here may be Abstract if it has been retrieved in the SeekHead, // so child.Read() must be used to retrieve the full EBML header switch (child.ID) { case MatroskaID.SeekHead: valid = child.Read(); break; case MatroskaID.SegmentInfo: if (valid = child.Read()) { ReadCreateSegmentInfo(child, ebml_sinfo); } break; case MatroskaID.Tracks: if ((propertiesStyle & ReadStyle.Average) != 0) { if (valid = child.Read()) { ReadTracks(child); } } break; case MatroskaID.Tags: valid = child.Read(); break; case MatroskaID.Attachments: valid = child.Read(); if (valid) { ReadAttachments(child, propertiesStyle); } break; case MatroskaID.CRC32: // We don't support it valid = child.Read(); break; } if (!valid) { break; } } // Detect invalid SeekHead if (!valid) { if (retry) { MarkAsCorrupt("Invalid Meta Seek"); // Retry it one last time ReadWriteSegment(element, propertiesStyle, false); } else { MarkAsCorrupt("Invalid EBML element Read"); } } }