How do I simultaneously read and write to a stream for playing media file in Silverlight 4 MediaStreamSource?

1.4k views Asked by At

BACKGROUND

I have a media file that I am progressively downloading to my Silverlight 4 application, using WebClient.OpenReadAsync/OpenReadCompleted, and Stream.BeginRead/AsyncCallback. The goal is to play the file in a MediaElement by calling the SetSource method, passing in an instance of our custom MediaStreamSource, so that the file can start playing before the entire contents of the file have been downloaded. The media file is using custom encoding/decoding, which is why we are using a custom MediaStreamSource. Our MediaStreamSource is built to accept a Stream and begin parsing track information and play back in a MediaElement. I have confirmed I am progressively downloading the file contents. Here is a summary of the download code:

public void SetSource(string sourceUrl)
{
    var uriBuilder = new UriBuilder(sourceUrl);
    WebClient webClient = new WebClient();

    // AllowReadStreamBuffering = false allows us to get the stream
    // before it's finished writing to it.
    webClient.AllowReadStreamBuffering = false;
    webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(webClient_OpenReadCompleted);
    webClient.OpenReadAsync(uriBuilder.Uri);
}

void webClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
    _inboundVideoStream = e.Result;
    BeginReadingFromStream();
}

private void BeginReadingFromStream()
{
    if (_inboundVideoStream.CanRead)
    {
        _chunk = new byte[_chunkSize];
        _inboundVideoStream.BeginRead(_chunk, 0, _chunk.Length, new AsyncCallback(BeginReadCallback), _inboundVideoStream);
    }
}

private void BeginReadCallback(IAsyncResult asyncResult)
{
    Stream stream = asyncResult.AsyncState as Stream;
    int bytesRead = stream.EndRead(asyncResult);
    _totalBytesRead += bytesRead;

    if (_playableStream == null)
        _playableStream = new MemoryStream();

    _playableStream.Write(_chunk, 0, _chunk.Length);

    if (!_initializedMediaStream && _playableStream.Length >= _minimumToStartPlayback)
    {
        _initializedMediaStream = true;

        // Problem: we can't hand the stream source a stream that's still being written to
        // It's Position is at the end.  Can I read and write from the same stream or is there another way
        MP4MediaStreamSource streamSource = new MP4MediaStreamSource(_playableStream);

        this.Dispatcher.BeginInvoke(() =>
        {
            mediaElement1.SetSource(streamSource);
        });
    }

    if (_totalBytesRead < _fileSize)
    {
        ReadFromDownloadStream();
    }
    else
    {
        // Finished downloading
    }
}

I've tried both writing/reading to a MemoryStream simultaneously, as listed above, as well as writing to an IsolatedStorageFile and reading from that file as I'm writing to it. So far I can't find a way to make either approach work.

QUESTION:

Is there a way to read and write to the same stream? Or is there a standard way to implement this with a stream and MediaStreamSource?

Thanks

1

There are 1 answers

2
Christian Resma Helle On

The way I did it in my MediaStreamSource implementation is to have 2 streams in it: one for reading and one for writing.

I dispose and re-create the reading stream using the buffer of the writing stream every time GetSampleAsync() is called. Another way to do it I guess is to use a negative offset when creating a MediaStreamSample to pass to ReportGetSampleCompleted() since the position of the stream will always be at the end, but you have to make sure that the position is at the end otherwise this will not work, to keep it simple I just used 2 streams