views:

816

answers:

3

Hi,

My question is about writing a video file to the hard drive that is being downloaded from the network and playing it at the same time using Windows Media Player. The file is pretty big and will take awhile to download. It is necessary to download it rather than just stream it directly to Windows Media Player.

What happens is that, while I can write to the video file and read it at the same time from my own test code, it cannot be done using Windows Media Player (at least I haven't figured it out). I know it is possible to do because Amazon Unbox downloads does it. Unbox lets you play WMVs while it is downloading them. And Unbox is written in .NET so...

I've read the "C# file read/write fileshare doesn’t appear to work" question and answers for opening a file with the FileShare flags. But it's not working for me. Process Monitor says that Media Player is opening the file with Fileshare flags, but it errors out.

In the buffering thread I have this code for reading the file from the network and writing it to a file (no error handling or other stuff to make it more readable):

// the download thread
void StartStreaming(Stream webStream, int bufferFullByteCount)
{
 int bytesRead;
 var buffer = new byte[4096];
 var fileStream = new FileStream(MediaFile.FullName, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
 var writer = new BinaryWriter(fileStream);
 var totalBytesRead = 0;
 do
 {
  bytesRead = webStream.Read(buffer, 0, 4096);
  if (bytesRead != 0)
  {
   writer.Write(buffer, 0, (int)bytesRead);
   writer.Flush();
   totalBytesRead += bytesRead;
  }
  if (totalBytesRead >= bufferFullByteCount)
  {
   // fire an event to a different thread to tell 
   // Windows Media Player to start playing
   OnBufferingComplete(this, new BufferingCompleteEventArgs(this, MediaFile));
  }
 } while (bytesRead != 0);
}

This seems to work fine. The file writes to the disk and has the correct permissions.

But then heres the event handler in the other thread for playing back the video

 // the playback thread

private void OnBufferingComplete(object sender, BufferingCompleteEventArgs e)
{
 axWindowsMediaPlayer1.URL = e.MediaFile.FullName;
}

Windows Media Player indicates that its opening the file and then just stops with an error that the file can't be opened "already opened in another process."

I have tried everything I can think of. What am I missing? If the Amazon guys can do this then so can I, right?

edit: this code works with mplayer, VLC, and Media Player Classic; just not Windows Media Player or Windows Media Center player. IOW, the only players I need them to work with. ugh!

edit2: I went so far as to use MiniHttp to stream the video to Windows Media Player to see if that would "fool" WMP into playing a video that is being download. Nothing doing. While WMP did open the file it waited until the mpeg file was completely copied before it started playing. How does it know?

edit3: After some digging I discovered the problem. I am working with MPEG2 files. The problem is not necessarily with Windows Media Player, but with the Microsoft MPEG2 DirectShow Splitter that WMP uses to open the MPEG2 files that I am trying to play and download at the same time. The Splitter opens the files in non-Shared mode. Not so with WMV files. WMP opens them in shared mode and everything works as expected.

A: 

Updated 16 March after comment by @darin:

You're specifying FileShare.ReadWrite when you're writing the file, which theoretically allows another process to open it for writing too.

Try altering your code to only request FileShare.Read:

var fileStream 
    = new FileStream(
        MediaFile.FullName, 
        FileMode.Create, 
        FileAccess.Write, 
        FileShare.Read); // Instead of ReadWrite

To quote MSDN:

FileShare.Read: Allows subsequent opening of the file for reading.

FileShare.ReadWrite: Allows subsequent opening of the file for reading or writing.

Bevan
@Bevan, specifying FileShare.Write when creating the file means that media player will never be able to open the file for reading.
Darin Dimitrov
@darin - thanks for the heads up. FileShare didn't mean what I thought it did. I've updated my answer to suit my improved understanding.
Bevan
A: 

When you open the file for writing you shouldn't use FileShare.ReadWrite. You should instead use FileShare.Read:

var fileStream = new FileStream(
    MediaFile.FullName, FileMode.Create, FileAccess.Write, FileShare.Read);

When mplayer.exe process open the file for reading it will first try to open it with FileShare.Read which will fail but it then will reattempt with FileShare.ReadWrite which will succeed.

Darin Dimitrov
Unfortunately this works for mplayer.exe, VLC and mplayerc.exe, but not for Windows Media Player. Wish I knew what magic the Unbox guys were doine.
CLaRGe
+2  A: 

I've decided to answer my own question in case someone else runs into this rare situation.

The short answer is this: Windows Media Player (as of this writing) will allow a file to be downloaded and play at the same time as long as that functionality is supported by CODECS involved in rendering the file.

To quote from the last edit to the question:

After some digging I discovered the problem. I am working with MPEG2 files. The problem is not necessarily with Windows Media Player, but with the Microsoft MPEG2 DirectShow Splitter that WMP uses to open the MPEG2 files that I am trying to play and download at the same time. The Splitter opens the files in non-Shared mode. Not so with WMV files. WMP opens them in shared mode and everything works as expected.

CLaRGe