public ParsedContent Read(Stream stream)
 {
     var res = new ParsedContent();
       var bodies = new List<Element>();
       var info = new AtomCollection(Channel.ChannelInfo.Extra);
       var processed = false;
       var eos = false;
       while (!eos) {
     var start_pos = stream.Position;
     try {
       var elt = Element.ReadHeader(stream);
       if (ebml.MaxIDLength  <elt.ID.Length ||
       ebml.MaxSizeLength<elt.Size.Length) {
     throw new BadDataException();
       }
       switch (state) {
       case ReaderState.EBML:
     if (elt.ID.BinaryEquals(Elements.EBML)) {
       elt.ReadBody(stream);
       ebml = new EBML(elt);
       state = ReaderState.Segment;
     }
     else {
       throw new BadDataException();
     }
     break;
       case ReaderState.Segment:
     if (elt.ID.BinaryEquals(Elements.Segment)) {
       segment = new Segment(elt);
       state = ReaderState.EndOfHeader;
     }
     else if (elt.ID.BinaryEquals(Elements.EBML)) {
       stream.Position = elt.Position;
       state = ReaderState.EBML;
       continue;
     }
     else if (elt.ID.BinaryEquals(Elements.Void) ||
              elt.ID.BinaryEquals(Elements.CRC32)) {
       elt.ReadBody(stream);
     }
     else {
       throw new BadDataException();
     }
     break;
       case ReaderState.EndOfHeader:
     if (elt.ID.BinaryEquals(Elements.Segment)) {
       stream.Position = elt.Position;
       state = ReaderState.Segment;
       continue;
     }
     else if (elt.ID.BinaryEquals(Elements.EBML)) {
       stream.Position = elt.Position;
       state = ReaderState.EBML;
       continue;
     }
     else if (elt.ID.BinaryEquals(Elements.Cluster)) {
       stream.Position = elt.Position;
       state = ReaderState.Cluster;
       clusters.Clear();
       MemoryStream header;
       using (header=new MemoryStream()) {
         ebml.Element.Write(header);
         segment.Element.Write(header);
         foreach (var c in segment.HeaderElements) {
           c.Write(header);
         }
       }
       streamIndex = Channel.GenerateStreamID();
       streamOrigin = DateTime.Now;
       res.ContentHeader = new Content(streamIndex, TimeSpan.Zero, 0, header.ToArray());
       if (ebml.DocType=="webm") {
         info.SetChanInfoType("WEBM");
         info.SetChanInfoStreamType("video/webm");
         info.SetChanInfoStreamExt(".webm");
       }
       else {
         info.SetChanInfoType("MKV");
         info.SetChanInfoStreamType("video/x-matroska");
         info.SetChanInfoStreamExt(".mkv");
       }
       res.ChannelInfo = new ChannelInfo(info);
     }
     else {
       elt.ReadBody(stream);
       segment.AddHeader(elt);
     }
     break;
       case ReaderState.Cluster:
     if (elt.ID.BinaryEquals(Elements.Segment)) {
       stream.Position = elt.Position;
       state = ReaderState.Segment;
       continue;
     }
     else if (elt.ID.BinaryEquals(Elements.EBML)) {
       stream.Position = elt.Position;
       state = ReaderState.EBML;
       continue;
     }
     else if (elt.ID.BinaryEquals(Elements.Cluster)) {
       if (clusters.Count>0) {
         var timespan = clusters.Sum(c => c.Timespan);
         if (timespan>=30.0) {
           var sz = clusters.Sum(c => c.Timespan>0 ? c.BlockSize : 0);
           var kbps = (int)((sz*8/timespan+900) / 1000.0);
           info.SetChanInfoBitrate(kbps);
           res.ChannelInfo = new ChannelInfo(info);
           while (clusters.Count>1) clusters.RemoveFirst();
         }
       }
       var cluster = new Cluster(elt);
       clusters.AddLast(cluster);
       bodies.Add(elt);
       state = ReaderState.Timecode;
     }
     else if (elt.ID.BinaryEquals(Elements.Void) ||
              elt.ID.BinaryEquals(Elements.CRC32)) {
       elt.ReadBody(stream);
       bodies.Add(elt);
     }
     else {
       throw new BadDataException();
     }
     break;
       case ReaderState.Timecode:
     if (elt.ID.BinaryEquals(Elements.Segment)) {
       stream.Position = elt.Position;
       state = ReaderState.Segment;
       continue;
     }
     else if (elt.ID.BinaryEquals(Elements.EBML)) {
       stream.Position = elt.Position;
       state = ReaderState.EBML;
       continue;
     }
     else if (elt.ID.BinaryEquals(Elements.Cluster)) {
       stream.Position = elt.Position;
       state = ReaderState.Cluster;
       continue;
     }
     else if (elt.ID.BinaryEquals(Elements.Timecode)) {
       elt.ReadBody(stream);
       if (clusters.Last!=null) {
         clusters.Last.Value.Timecode =
           Element.ReadUInt(new MemoryStream(elt.Data), elt.Data.Length)*(segment.TimecodeScale/1000000000.0);
         if (clusters.Count>1) {
           clusters.Last.Previous.Value.Timespan = clusters.Last.Value.Timecode - clusters.Last.Previous.Value.Timecode;
         }
       }
       bodies.Add(elt);
       state = ReaderState.Block;
     }
     else if (elt.ID.BinaryEquals(Elements.SimpleBlock) ||
              elt.ID.BinaryEquals(Elements.BlockGroup)) {
       stream.Position = elt.Position;
       state = ReaderState.Block;
       continue;
     }
     else {
       elt.ReadBody(stream);
       bodies.Add(elt);
     }
     break;
       case ReaderState.Block:
     if (elt.ID.BinaryEquals(Elements.Segment)) {
       stream.Position = elt.Position;
       state = ReaderState.Segment;
       continue;
     }
     else if (elt.ID.BinaryEquals(Elements.EBML)) {
       stream.Position = elt.Position;
       state = ReaderState.EBML;
       continue;
     }
     else if (elt.ID.BinaryEquals(Elements.Cluster)) {
       stream.Position = elt.Position;
       state = ReaderState.Cluster;
       continue;
     }
     else if ((elt.ID.BinaryEquals(Elements.SimpleBlock) ||
               elt.ID.BinaryEquals(Elements.BlockGroup)) &&
              (clusters.Last.Value.BlockID==null ||
               elt.ID.BinaryEquals(clusters.Last.Value.BlockID))) {
       elt.ReadBody(stream);
       clusters.Last.Value.BlockSize += elt.Size.Value;
       clusters.Last.Value.BlockID    = elt.ID.Binary;
       bodies.Add(elt);
     }
     else if (clusters.Last.Value.BlockID==null) {
       elt.ReadBody(stream);
       bodies.Add(elt);
     }
     else {
       stream.Position = elt.Position;
       state = ReaderState.Cluster;
       continue;
     }
     break;
       }
       processed = true;
     }
     catch (EndOfStreamException) {
       if (!processed) throw;
       stream.Position = start_pos;
       eos = true;
     }
     catch (BadDataException) {
       stream.Position = start_pos+1;
     }
       }
       if (res.ContentHeader!=null) {
     position =
       res.ContentHeader.Position +
       res.ContentHeader.Data.Length;
       }
       if (bodies.Count>0) {
     res.Contents = new List<Content>();
     foreach (var body in bodies) {
       MemoryStream s;
       using (s=new MemoryStream()) {
     body.Write(s);
       }
       var data = s.ToArray();
       res.Contents.Add(new Content(streamIndex, DateTime.Now-streamOrigin, position, data));
       position += data.Length;
     }
       }
       return res;
 }
    public async Task ReadAsync(IContentSink sink, Stream stream, CancellationToken cancel_token)
    {
      var state = ReaderState.EBML;
      var position = 0L;
      var stream_index = -1;
      var stream_origin = DateTime.Now;
      var timecode_scale = 1000000.0;
      var ebml     = new EBML();
      var clusters = new LinkedList<Cluster>();
      var headers  = new List<Element>();

      var eos = false;
      while (!eos) {
        try {
          var elt = await Element.ReadHeaderAsync(stream, cancel_token);
          if (ebml.MaxIDLength  <elt.ID.Length ||
              ebml.MaxSizeLength<elt.Size.Length) {
            throw new BadDataException();
          }
        parse_retry:
          switch (state) {
          case ReaderState.EBML:
            if (elt.ID.BinaryEquals(Elements.EBML)) {
              await elt.ReadBodyAsync(stream, cancel_token);
              headers.Clear();
              headers.Add(elt);
              ebml = new EBML(elt);
              state = ReaderState.Segment;
            }
            else {
              throw new BadDataException();
            }
            break;
          case ReaderState.Segment:
            if (elt.ID.BinaryEquals(Elements.Segment)) {
              headers.Add(elt);
              state = ReaderState.EndOfHeader;
            }
            else if (elt.ID.BinaryEquals(Elements.EBML)) {
              state = ReaderState.EBML;
              goto parse_retry;
            }
            else if (elt.ID.BinaryEquals(Elements.Void) ||
                     elt.ID.BinaryEquals(Elements.CRC32)) {
              await elt.ReadBodyAsync(stream, cancel_token);
              headers.Add(elt);
            }
            else {
              throw new BadDataException();
            }
            break;
          case ReaderState.EndOfHeader:
            if (elt.ID.BinaryEquals(Elements.Segment)) {
              state = ReaderState.Segment;
              goto parse_retry;
            }
            else if (elt.ID.BinaryEquals(Elements.EBML)) {
              state = ReaderState.EBML;
              goto parse_retry;
            }
            else if (elt.ID.BinaryEquals(Elements.Cluster)) {
              clusters.Clear();
              MemoryStream header;
              using (header=new MemoryStream()) {
                foreach (var c in headers) {
                  c.Write(header);
                }
              }
              headers.Clear();

              stream_index  = Channel.GenerateStreamID();
              stream_origin = DateTime.Now;
              position = 0;
              sink.OnContentHeader(
                new Content(stream_index, TimeSpan.Zero, 0, header.ToArray())
              );
              position += header.ToArray().LongLength;
              var info = new AtomCollection();
              if (ebml.DocType=="webm") {
                info.SetChanInfoType("WEBM");
                info.SetChanInfoStreamType("video/webm");
                info.SetChanInfoStreamExt(".webm");
              }
              else {
                info.SetChanInfoType("MKV");
                info.SetChanInfoStreamType("video/x-matroska");
                info.SetChanInfoStreamExt(".mkv");
              }
              sink.OnChannelInfo(new ChannelInfo(info));

              state = ReaderState.Cluster;
              goto parse_retry;
            }
            else if (elt.ID.BinaryEquals(Elements.Info)) {
              await elt.ReadBodyAsync(stream, cancel_token);
              var s = new MemoryStream(elt.Data);
              while (s.Position<s.Length) {
                var child = Element.ReadHeader(s);
                if (child.ID.BinaryEquals(Elements.TimecodeScale)) {
                  timecode_scale = Element.ReadUInt(s, child.Size.Value) * 1.0;
                }
                else {
                  child.ReadBody(s);
                }
              }
              headers.Add(elt);
            }
            else {
              await elt.ReadBodyAsync(stream, cancel_token);
              headers.Add(elt);
            }
            break;
          case ReaderState.Cluster:
            if (elt.ID.BinaryEquals(Elements.Segment)) {
              state = ReaderState.Segment;
              goto parse_retry;
            }
            else if (elt.ID.BinaryEquals(Elements.EBML)) {
              state = ReaderState.EBML;
              goto parse_retry;
            }
            else if (elt.ID.BinaryEquals(Elements.Cluster)) {
              if (clusters.Count>0) {
                var timespan = clusters.Sum(c => c.Timespan);
                if (timespan>=30.0) {
                  var sz = clusters.Sum(c => c.Timespan>0 ? c.BlockSize : 0);
                  var kbps = (int)((sz*8/timespan+900) / 1000.0);
                  var info = new AtomCollection();
                  info.SetChanInfoBitrate(kbps);
                  sink.OnChannelInfo(new ChannelInfo(info));
                  while (clusters.Count>1) clusters.RemoveFirst();
                }
              }
              var cluster = new Cluster(elt);
              clusters.AddLast(cluster);
              sink.OnContent(
                new Content(stream_index, DateTime.Now-stream_origin, position, elt.ToArray())
              );
              position += elt.ByteSize;
              state = ReaderState.Timecode;
            }
            else if (elt.ID.BinaryEquals(Elements.Void) ||
                     elt.ID.BinaryEquals(Elements.CRC32)) {
              await elt.ReadBodyAsync(stream, cancel_token);
              sink.OnContent(
                new Content(stream_index, DateTime.Now-stream_origin, position, elt.ToArray())
              );
              position += elt.ByteSize;
            }
            else {
              throw new BadDataException();
            }
            break;
          case ReaderState.Timecode:
            if (elt.ID.BinaryEquals(Elements.Segment)) {
              state = ReaderState.Segment;
              goto parse_retry;
            }
            else if (elt.ID.BinaryEquals(Elements.EBML)) {
              state = ReaderState.EBML;
              goto parse_retry;
            }
            else if (elt.ID.BinaryEquals(Elements.Cluster)) {
              state = ReaderState.Cluster;
              goto parse_retry;
            }
            else if (elt.ID.BinaryEquals(Elements.SimpleBlock) ||
                     elt.ID.BinaryEquals(Elements.BlockGroup)) {
              state = ReaderState.Block;
              goto parse_retry;
            }
            else if (elt.ID.BinaryEquals(Elements.Timecode)) {
              await elt.ReadBodyAsync(stream, cancel_token);
              if (clusters.Last!=null) {
                clusters.Last.Value.Timecode =
                  Element.ReadUInt(new MemoryStream(elt.Data), elt.Data.Length)*(timecode_scale/1000000000.0);
                if (clusters.Count>1) {
                  clusters.Last.Previous.Value.Timespan = clusters.Last.Value.Timecode - clusters.Last.Previous.Value.Timecode;
                }
              }
              sink.OnContent(
                new Content(stream_index, DateTime.Now-stream_origin, position, elt.ToArray())
              );
              position += elt.ByteSize;
              state = ReaderState.Block;
            }
            else {
              await elt.ReadBodyAsync(stream, cancel_token);
              sink.OnContent(
                new Content(stream_index, DateTime.Now-stream_origin, position, elt.ToArray())
              );
              position += elt.ByteSize;
            }
            break;
          case ReaderState.Block:
            if (elt.ID.BinaryEquals(Elements.Segment)) {
              state = ReaderState.Segment;
              goto parse_retry;
            }
            else if (elt.ID.BinaryEquals(Elements.EBML)) {
              state = ReaderState.EBML;
              goto parse_retry;
            }
            else if (elt.ID.BinaryEquals(Elements.Cluster)) {
              state = ReaderState.Cluster;
              goto parse_retry;
            }
            else if ((elt.ID.BinaryEquals(Elements.SimpleBlock) ||
                      elt.ID.BinaryEquals(Elements.BlockGroup)) &&
                     (clusters.Last.Value.BlockID==null ||
                      elt.ID.BinaryEquals(clusters.Last.Value.BlockID))) {
              await elt.ReadBodyAsync(stream, cancel_token);
              clusters.Last.Value.BlockSize += elt.Size.Value;
              clusters.Last.Value.BlockID    = elt.ID.Binary;
              sink.OnContent(
                new Content(stream_index, DateTime.Now-stream_origin, position, elt.ToArray())
              );
              position += elt.ByteSize;
            }
            else if (clusters.Last.Value.BlockID==null) {
              await elt.ReadBodyAsync(stream, cancel_token);
              sink.OnContent(
                new Content(stream_index, DateTime.Now-stream_origin, position, elt.ToArray())
              );
              position += elt.ByteSize;
            }
            else {
              state = ReaderState.Cluster;
              goto parse_retry;
            }
            break;
          }
        }
        catch (EndOfStreamException) {
          eos = true;
        }
        catch (BadDataException) {
        }
      }

    }