Skip to content
This repository has been archived by the owner on Jan 21, 2022. It is now read-only.

Cannot read non-seekable streams #647

Closed
1 task done
jeremyVignelles opened this issue May 21, 2020 · 3 comments
Closed
1 task done

Cannot read non-seekable streams #647

jeremyVignelles opened this issue May 21, 2020 · 3 comments

Comments

@jeremyVignelles
Copy link
Collaborator

I have an issue about Vlc.DotNet.

Generic information

  • Vlc.DotNet version : develop
  • Vlc.DotNet project used : Core
  • libvlc version : 3.0.10 from NuGet
  • .net version : core 3.0 / net47
  • Project language : C#
  • Project build architecture : AnyCPU
  • Operating system : Windows x64

Summary

When using Play(Stream), when Stream is a non-seekable stream like a mp4 in a CryptoStream, the player cannot play:

Repro:

This seems to be because LibVLC does a probing of the media to know what kind of stream it is.

/cc :

  • @graysuit (thanks for the repro project)

  • @mfkl : We have the same issue in LibVLCSharp

  • I confirm that my issue doesn't happen in VLC itself.

@mfkl
Copy link
Contributor

mfkl commented May 21, 2020

@jeremyVignelles
Copy link
Collaborator Author

Found a workaround here : https://github.com/graysuit/vlc_text_encr/pull/1 . Not sure how and if it should be integrated into Vlc.DotNet / LVS

jeremyVignelles added a commit that referenced this issue May 22, 2020
Declared non-seekable streams as non-seekable in callback input

Increased the buffer size to match VLC buffer size

Related to #647
@jeremyVignelles
Copy link
Collaborator Author

The usage of libvlc_media_new_callback in Vlc.DotNet was not correct, and the stream was considered seekable while it was not.

I fixed that with #648 . Now, the message has changed :

[000001c377569c10] mp4 stream error: no moov before mdat and the stream is not seekable

It seems that your mp4 cannot be consumed that way, in non-seekable streams, and I don't think that Vlc.DotNet should make a compatibility layer for that.
Other kind of streams can work that way, as suggested here, but I'm not expert enough in mp4 to know how to encode such streams properly.

If you can't change your stream, you can use the workaround I implemented here : A Stream implementation that destroys and recreate a new stream on seek.
For reference, here is the implementation:

using System;
using System.IO;

namespace vlcplayerCsharp1
{

    /// <summary>
    /// A wrapper that destroys and creates a stream on demand to be able to seek into non-seekable stream...
    /// </summary>
    public class SeekableStreamWrapper : Stream
    {
        private readonly Func<Stream> _createStream;
        private Stream _innerStream;
        private long _position = 0;

        /// <summary>
        /// The constructor
        /// </summary>
        /// <param name="createStream"></param>
        public SeekableStreamWrapper(Func<Stream> createStream)
        {
            this._createStream = createStream;
            this._innerStream = createStream();
        }

        public override bool CanRead => true;

        public override bool CanSeek => true;

        public override bool CanWrite => false;

        public override long Length => this._innerStream.Length;

        public override long Position { get => this._position; set => this.Seek(value, SeekOrigin.Begin); }

        public override void Flush() => this._innerStream.Flush();

        public override int Read(byte[] buffer, int offset, int count)
        {
            var read = this._innerStream.Read(buffer, offset, count);
            this._position += read;
            return read;
        }


        public override long Seek(long offset, SeekOrigin origin)
        {
            if (origin != SeekOrigin.Begin)
            {
                throw new NotSupportedException("Only supports seeking from the beginning");
            }

            long remaining = 0;
            if (offset > this._position)
            {
                remaining = offset - this._position;
            }
            else
            {
                try
                {
                    this._innerStream.Dispose();
                }
                catch (Exception)
                {
                    // Shit happens... On .NET framework, calling Dispose() on a crypto stream without having read anything can throw...
                }
                this._innerStream = this._createStream();
                remaining = offset;
            }

            var buffer = new byte[Math.Min(0x100_000, remaining)];
            while (remaining > 0)
            {
                var read = this._innerStream.Read(buffer, 0, Math.Min(0x100_000, (remaining > int.MaxValue) ? int.MaxValue : (int)remaining));
                remaining -= read;
            }

            this._position = offset;
            return offset;
        }

        public override void SetLength(long value)
        {
            throw new NotSupportedException();
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            throw new NotSupportedException();
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                this._innerStream.Dispose();
            }

            base.Dispose(disposing);
        }
    }
}

jeremyVignelles added a commit that referenced this issue Jun 27, 2020
Declared non-seekable streams as non-seekable in callback input

Increased the buffer size to match VLC buffer size

Related to #647
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants