예제 #1
0
        public async Task ReadAsync(IContentSink sink, Stream stream, CancellationToken cancel_token)
        {
            long pos          = 0;
            var  streamIndex  = Channel.GenerateStreamID();
            var  streamOrigin = DateTime.Now;

            sink.OnContentHeader(new Content(streamIndex, TimeSpan.Zero, pos, new byte[] { }));
            var channel_info = new AtomCollection(Channel.ChannelInfo.Extra);

            channel_info.SetChanInfoType("RAW");
            channel_info.SetChanInfoStreamType("application/octet-stream");
            channel_info.SetChanInfoStreamExt("");
            sink.OnChannelInfo(new ChannelInfo(channel_info));

            bool eof = false;

            do
            {
                var buf = new byte[8192];
                var sz  = await stream.ReadAsync(buf, 0, buf.Length, cancel_token).ConfigureAwait(false);

                if (sz > 0)
                {
                    sink.OnContent(new Content(streamIndex, DateTime.Now - streamOrigin, pos, buf.Take(sz).ToArray()));
                    pos += sz;
                }
                else
                {
                    eof = true;
                }
            } while (!eof);
        }
예제 #2
0
        protected void OnPCPChanPkt(Atom atom)
        {
            var pkt_type = atom.Children.GetChanPktType();
            var pkt_data = atom.Children.GetChanPktData();

            if (pkt_type != null && pkt_data != null)
            {
                if (pkt_type == Atom.PCP_CHAN_PKT_TYPE_HEAD)
                {
                    long pkt_pos = atom.Children.GetChanPktPos() ?? 0;
                    streamIndex  = Channel.GenerateStreamID();
                    streamOrigin = DateTime.Now;
                    var header = new Content(streamIndex, TimeSpan.Zero, pkt_pos, pkt_data);
                    var info   = ResetContentType(lastInfo, header);
                    contentSink.OnContentHeader(header);
                    contentSink.OnChannelInfo(info);
                    lastHeader   = header;
                    lastInfo     = info;
                    lastPosition = pkt_pos;
                }
                else if (pkt_type == Atom.PCP_CHAN_PKT_TYPE_DATA)
                {
                    if (atom.Children.GetChanPktPos() != null)
                    {
                        long pkt_pos = atom.Children.GetChanPktPos().Value;
                        contentSink.OnContent(new Content(streamIndex, DateTime.Now - streamOrigin, pkt_pos, pkt_data));
                        lastPosition = pkt_pos;
                    }
                }
                else if (pkt_type == Atom.PCP_CHAN_PKT_TYPE_META)
                {
                }
            }
        }
예제 #3
0
        public void AddContentSink(IContentSink sink)
        {
            ReplaceCollection(ref contentSinks, orig => {
                var new_collection = new List <IContentSink>(orig);
                new_collection.Add(sink);
                return(new_collection);
            });
            var header = contentHeader;

            if (header != null)
            {
                sink.OnContentHeader(header);
                var channel_info = ChannelInfo;
                if (channel_info != null)
                {
                    sink.OnChannelInfo(channel_info);
                }
                var channel_track = ChannelTrack;
                if (channel_track != null)
                {
                    sink.OnChannelTrack(channel_track);
                }
                var contents = Contents.GetNewerContents(header.Stream, header.Timestamp, header.Position);
                foreach (var content in contents)
                {
                    sink.OnContent(content);
                }
            }
        }
예제 #4
0
        public IDisposable AddContentSink(IContentSink sink, long requestPos)
        {
            ReplaceCollection(ref contentSinks, orig => orig.Add(sink));
            var header = contentHeader;

            if (header != null)
            {
                var channel_info = ChannelInfo;
                if (channel_info != null)
                {
                    sink.OnChannelInfo(channel_info);
                }
                var channel_track = ChannelTrack;
                if (channel_track != null)
                {
                    sink.OnChannelTrack(channel_track);
                }
                sink.OnContentHeader(header);
                var contents = Contents.GetFirstContents(header.Stream, header.Timestamp, header.Position);
                foreach (var content in contents)
                {
                    if (header.Position >= requestPos || content.Position >= requestPos)
                    {
                        sink.OnContent(content);
                    }
                }
            }
            return(new ContentSinkSubscription(this, sink));
        }
예제 #5
0
        public async Task ReadAsync(IContentSink sink, Stream stream, CancellationToken cancel_token)
        {
            int      streamIndex     = -1;
            DateTime streamOrigin    = DateTime.Now;
            long     contentPosition = 0;
            bool     eof             = false;

            do
            {
                ASFChunk chunk = null;
                try {
                    chunk = await ASFChunk.ReadAsync(stream, cancel_token).ConfigureAwait(false);
                }
                catch (EndOfStreamException) {
                    eof = true;
                    continue;
                }
                switch (chunk.KnownType)
                {
                case ASFChunk.ChunkType.Header:
                {
                    var header = ASFHeader.Read(chunk);
                    var info   = new AtomCollection(Channel.ChannelInfo.Extra);
                    info.SetChanInfoBitrate(header.Bitrate);
                    if (header.Streams.Any(type => type == ASFHeader.StreamType.Video))
                    {
                        info.SetChanInfoType("WMV");
                        info.SetChanInfoStreamType("video/x-ms-wmv");
                        info.SetChanInfoStreamExt(".wmv");
                    }
                    else if (header.Streams.Any(type => type == ASFHeader.StreamType.Audio))
                    {
                        info.SetChanInfoType("WMA");
                        info.SetChanInfoStreamType("audio/x-ms-wma");
                        info.SetChanInfoStreamExt(".wma");
                    }
                    else
                    {
                        info.SetChanInfoType("ASF");
                        info.SetChanInfoStreamType("video/x-ms-asf");
                        info.SetChanInfoStreamExt(".asf");
                    }
                    sink.OnChannelInfo(new ChannelInfo(info));
                    streamIndex     = Channel.GenerateStreamID();
                    streamOrigin    = DateTime.Now;
                    contentPosition = 0;
                    var data = chunk.ToByteArray();
                    sink.OnContentHeader(new Content(streamIndex, TimeSpan.Zero, contentPosition, data, PCPChanPacketContinuation.None));
                    contentPosition += data.Length;
                    break;
                }

                case ASFChunk.ChunkType.Data:
                {
                    var data = chunk.ToByteArray();
                    sink.OnContent(
                        new Content(streamIndex, DateTime.Now - streamOrigin, contentPosition, chunk.ToByteArray(), PCPChanPacketContinuation.None)
                        );
                    contentPosition += data.Length;
                }
                break;

                case ASFChunk.ChunkType.Unknown:
                    break;
                }
            } while (!eof);
        }
예제 #6
0
            private async Task ProcessMessagesAsync(IContentSink targetSink, CancellationToken cancellationToken)
            {
                var bufferStream  = new MemoryStream();
                var context       = new FLVToMPEG2TS.Context(bufferStream);
                var contentBuffer = new MemoryStream();
                var fileParser    = new FLVFileParser();
                var msg           = await msgQueue.DequeueAsync(cancellationToken).ConfigureAwait(false);

                while (msg.Type != ContentMessage.MessageType.Stop)
                {
                    switch (msg.Type)
                    {
                    case ContentMessage.MessageType.ChannelInfo:
                        targetSink.OnChannelInfo(msg.ChannelInfo);
                        break;

                    case ContentMessage.MessageType.ChannelTrack:
                        targetSink.OnChannelTrack(msg.ChannelTrack);
                        break;

                    case ContentMessage.MessageType.ContentHeader:
                    {
                        var buffer = contentBuffer;
                        var pos    = buffer.Position;
                        buffer.Seek(0, SeekOrigin.End);
                        buffer.Write(msg.Content.Data.Span);
                        buffer.Position = pos;
                        fileParser.Read(buffer, context);
                        if (buffer.Position != 0)
                        {
                            var new_buf  = new MemoryStream();
                            var trim_pos = buffer.Position;
                            buffer.Close();
                            var buf = buffer.ToArray();
                            new_buf.Write(buf, (int)trim_pos, (int)(buf.Length - trim_pos));
                            new_buf.Position = 0;
                            contentBuffer    = new_buf;
                        }
                        if (bufferStream.Position != 0)
                        {
                            targetSink.OnContentHeader(
                                new Content(
                                    msg.Content.Stream,
                                    msg.Content.Timestamp,
                                    msg.Content.Position,
                                    bufferStream.ToArray(),
                                    msg.Content.ContFlag
                                    )
                                );
                            bufferStream.SetLength(0);
                        }
                    }
                    break;

                    case ContentMessage.MessageType.ContentBody:
                    {
                        var buffer = contentBuffer;
                        var pos    = buffer.Position;
                        buffer.Seek(0, SeekOrigin.End);
                        buffer.Write(msg.Content.Data.Span);
                        buffer.Position = pos;
                        fileParser.Read(buffer, context);
                        if (buffer.Position != 0)
                        {
                            var new_buf  = new MemoryStream();
                            var trim_pos = buffer.Position;
                            buffer.Close();
                            var buf = buffer.ToArray();
                            new_buf.Write(buf, (int)trim_pos, (int)(buf.Length - trim_pos));
                            new_buf.Position = 0;
                            contentBuffer    = new_buf;
                        }
                        if (bufferStream.Position != 0)
                        {
                            targetSink.OnContent(
                                new Content(
                                    msg.Content.Stream,
                                    msg.Content.Timestamp,
                                    msg.Content.Position,
                                    bufferStream.ToArray(),
                                    msg.Content.ContFlag
                                    )
                                );
                            bufferStream.SetLength(0);
                        }
                    }
                    break;
                    }
                    msg = await msgQueue.DequeueAsync(cancellationToken).ConfigureAwait(false);
                }
                targetSink.OnStop(msg.StopReason);
            }
예제 #7
0
 public void OnContentHeader(Content content_header)
 {
     channelContentSink.OnContentHeader(content_header);
 }
예제 #8
0
        public async Task ReadAsync(IContentSink sink, Stream stream, CancellationToken cancel_token)
        {
            int      streamIndex       = -1;
            long     contentPosition   = 0;
            DateTime streamOrigin      = DateTime.Now;
            DateTime latestContentTime = DateTime.Now;

            byte[] bytes188    = new byte[188];
            byte[] latestHead  = new byte[0];
            byte[] contentData = null;

            streamIndex  = Channel.GenerateStreamID();
            streamOrigin = DateTime.Now;
            sink.OnContentHeader(new Content(streamIndex, TimeSpan.Zero, contentPosition, new byte[] {}, PCPChanPacketContinuation.None));

            try
            {
                while (!cancel_token.IsCancellationRequested)
                {
                    bytes188 = await stream.ReadBytesAsync(188, cancel_token).ConfigureAwait(false);

                    TSPacket packet = new TSPacket(bytes188);
                    if (packet.sync_byte != 0x47)
                    {
                        throw new Exception();
                    }
                    if (packet.payload_unit_start_indicator > 0)
                    {
                        if (packet.PID == patID)
                        {
                            pmtID = packet.PMTID;
                            head  = new MemoryStream();
                            if (!addHead(bytes188))
                            {
                                throw new Exception();
                            }
                            continue;
                        }
                        if (packet.PID == pmtID)
                        {
                            var pmt = new ProgramMapTable(packet, bytes188);
                            pcrPID = pmt.PCRPID;
                            if (!addHead(bytes188))
                            {
                                throw new Exception();
                            }
                            head.Close();
                            byte[] newHead = head.ToArray();
                            if (!Enumerable.SequenceEqual(newHead, latestHead))
                            {
                                streamIndex     = Channel.GenerateStreamID();
                                contentPosition = 0;
                                sink.OnContentHeader(new Content(streamIndex, DateTime.Now - streamOrigin, contentPosition, newHead, PCPChanPacketContinuation.None));
                                contentPosition += newHead.Length;
                                latestHead       = newHead;
                            }
                            continue;
                        }
                        if (packet.PID == pcrPID && packet.program_clock_reference > 0.0)
                        {
                            if (packet.program_clock_reference < rateCounter.lastPCR)
                            {
                                rateCounter.lastPCR   = packet.program_clock_reference;
                                rateCounter.byteCount = 0;
                                recvRate = 0.0;
                            }
                            else if (rateCounter.lastPCR + 10.0 < packet.program_clock_reference)
                            {
                                var bitrate = 8 * rateCounter.byteCount / (packet.program_clock_reference - rateCounter.lastPCR);
                                UpdateRecvRate(sink, bitrate);
                                rateCounter.lastPCR   = packet.program_clock_reference;
                                rateCounter.byteCount = 0;
                            }
                        }
                        if ((DateTime.Now - latestContentTime).Milliseconds > 50)
                        {
                            TryParseContent(packet, out contentData);
                            if (contentData != null)
                            {
                                sink.OnContent(new Content(streamIndex, DateTime.Now - streamOrigin, contentPosition, contentData, PCPChanPacketContinuation.None));
                                contentPosition  += contentData.Length;
                                latestContentTime = DateTime.Now;
                            }
                        }
                    }
                    if (!addCache(bytes188))
                    {
                        throw new Exception();
                    }
                    rateCounter.byteCount += 188;
                }
            }
            catch (EndOfStreamException)
            { }
            catch (Exception)
            { }
        }
예제 #9
0
 public void OnContentHeader(Content content_header)
 {
     targetSink.OnContentHeader(content_header);
 }
예제 #10
0
        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).ConfigureAwait(false);

                    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).ConfigureAwait(false);

                            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).ConfigureAwait(false);

                            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(), PCPChanPacketContinuation.None)
                                );
                            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).ConfigureAwait(false);

                            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).ConfigureAwait(false);

                            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(), PCPChanPacketContinuation.None)
                                );
                            position += elt.ByteSize;
                            state     = ReaderState.Timecode;
                        }
                        else if (elt.ID.BinaryEquals(Elements.Void) ||
                                 elt.ID.BinaryEquals(Elements.CRC32))
                        {
                            await elt.ReadBodyAsync(stream, cancel_token).ConfigureAwait(false);

                            sink.OnContent(
                                new Content(stream_index, DateTime.Now - stream_origin, position, elt.ToArray(), PCPChanPacketContinuation.None)
                                );
                            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).ConfigureAwait(false);

                            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(), PCPChanPacketContinuation.None)
                                );
                            position += elt.ByteSize;
                            state     = ReaderState.Block;
                        }
                        else
                        {
                            await elt.ReadBodyAsync(stream, cancel_token).ConfigureAwait(false);

                            sink.OnContent(
                                new Content(stream_index, DateTime.Now - stream_origin, position, elt.ToArray(), PCPChanPacketContinuation.None)
                                );
                            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).ConfigureAwait(false);

                            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(), PCPChanPacketContinuation.None)
                                );
                            position += elt.ByteSize;
                        }
                        else if (clusters.Last.Value.BlockID == null)
                        {
                            await elt.ReadBodyAsync(stream, cancel_token).ConfigureAwait(false);

                            sink.OnContent(
                                new Content(stream_index, DateTime.Now - stream_origin, position, elt.ToArray(), PCPChanPacketContinuation.None)
                                );
                            position += elt.ByteSize;
                        }
                        else
                        {
                            state = ReaderState.Cluster;
                            goto parse_retry;
                        }
                        break;
                    }
                }
                catch (EndOfStreamException) {
                    eos = true;
                }
                catch (BadDataException) {
                }
            }
        }
예제 #11
0
    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) {
        }
      }

    }