Playing back Wave files in Silverlight


I’ve been working on Silverlight for a couple of years now, and finally decided to start talking about it in my blog. With the recent beta, we’ve added raw A/V support to Silverlight 3, and that means not only that people will be able to write their own decoders for Silverlight, but also that they can now play back PCM audio through managed code. The easiest way to get PCM data is simply to use a Wave file. Wave files are a container format for multiple audio encodings, so not all files will play back in Silverlight, but the PCM encoded ones will. To help out, I’ve published a sample MediaStreamSource implementation on Code Gallery (http://code.msdn.microsoft.com/wavmss).

The easiest way to try out the code is to open a local wave file from your drive using the OpenFileDialog (C:\Windows\Media has a bunch of little sound bites).

private void Button_Click(object sender, RoutedEventArgs e) 
{
    OpenFileDialog ofd = new OpenFileDialog(); 

    if (ofd.ShowDialog().Value)
    {
        Stream s = ofd.File.OpenRead();
        WaveMediaStreamSource wavMss = new WaveMediaStreamSource(s);
        try
        {
            me.SetSource(wavMss);
        }
        catch (InvalidOperationException)
        {
            // This file is not valid
        }
    }
} 

This snippet will play the wave file right away. Remember that to get the FileOpenDialog, you need to hook the handler to a user initiated action. An easy way for this, is to put a MediaElement and a button on a page.

<UserControl x:Class="SampleWave.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="400" Height="300">
    <Grid x:Name="LayoutRoot" Background="White">
        <MediaElement Name="me"/>
        <Button Name="b" Width="200" Height="20" Content="Open" Click="Button_Click"/>
    </Grid>
</UserControl>

That’s it, you can now play wave files.


Comments (45)

  1. AlexandreMutel says:

    Hi Gilles,

    This is a great and simple sample to explain the new RAW audio pipeline in SL3.

    I’m very excited about this feature as it opens a wide range of new realtime audio-visual experience!

    Although, the main issue right now with the RAW audio is the high audio latency and then doesn’t allow to make realtime audio performance, as it is reported here : http://silverlight.net/forums/p/82270/192635.aspx

    It seems that the team working on the MediaStreamSource is aware of this problem, hope that a correction will be possible for the final release of SL3!

  2. gillesk says:

    Thanks for the feedback. We’re aware of the latency issue and should address it for the release. I don’t know how low we’ll be able to go (with lower the latencywe need to get the data from the app mre frequently).

  3. mliebster says:

    Thanks for posting the code for this!

    We currently are converting our Windows .WAV files to WMA on the server on demand and then playing them via SL2.

    Unfortunately some of our audio files are in a Dialogic PCM muLaw format. So these go through 2 conversions to be played – first be converted to wav and then converted to WMA.

    This will simplify things so much!

  4. garyhalb says:

    Hi Gilles,

    Thanks for this great example. I’m utilising it to try and play a wav file in a seamless loop. But one thing that does not seem to work is the ability to set the Position on the MediaElement back to 0 – it seems to do nothing. I’ve tried it with the MediaEnded event, and also with a MarkerReached event, but setting the Position seems to do nothing.

    Is this a bug, or is there a better way of doing this?

    Thanks,

    Gary

  5. gillesk says:

    There is an easy way to do this. There are issues with doing a seamless loop by setting the position of the MediaElement. But since you’re controlling the data that gets played, the easiest way to achieve a continuous loop is to keep feeding data from the beginning when you reach the end of the wave file. If you take a look at WaveMediaStreamSource.cs there’s a block of code that is commented out line 175.

                   /* Uncomment to loop forever

                   // If there are no more bytes in the chunk, start again from the beginning

                   if (this.wavParser.BytesRemainingInChunk == 0)

                   {

                       this.wavParser.MoveToStartOfChunk();

                       this.currentPosition = this.startPosition;

                   }

                   */

    If you uncomment this block, you will automatically use the wave file as a circular buffer that will loop forever. You might want to change the duration reported when you open the media to seem infinite in this case.

  6. good4you says:

    Hi,

    Thanks for this great library to play this audio type.

    What i see, it’s working only for the PCM wav files, what if the not a PCM wav file, how i can play it , is it possible?

    Thanks

  7. gillesk says:

    Silverlight natively only accepts PCM, MP3, WMA and AAC as audio format. If you need to playback a WAV file that is not encoded in one of those formats, then you would need to write or obtain a managed decoder for that format and connect it to your MediaStreamSource.

  8. kyrisu says:

    Hi,

    Your implementation is great and it almost solved my problems :) Almost because I need to be able to open the stream before it's buffered entirely. Can you point me in the right direction?

    How to enable audio playback before the file is buffered entirely on the local machine? (or at least get the progress, so user have some visual feedback from the page)

  9. gillesk says:

    You should be able to enable playback before the file is downloaded by using the async model. As you call BeginGetResponse, you should be able to start getting the stream before it is fully downloaded. As long as your MediaStreamSource implementation doesn't read data past the point of download then you can still play the stream as your downloading it. If you don't have enough data available then you can call ReportSampleProgress to tell the media pipeline that it should start buffering until you get enough data to keep playing.

  10. Matt says:

    Hi,

    This code is incredible!  I've been attempting to modify it for ulaw wav files, but have not yet succeeded.  Is it possible for this wrapper to work with ulaw wave files??  Thanks!

  11. gillesk says:

    Thanks for the compliments. You should be able to use uLaw as long as you have a decoder available. Since the Silverlight pipeline only accepts PCM, WMA, MP3 and AAC audio, you'll need to decode the file into PCM. First open the file, since it's a WAV it will have the same format as my sample, the format will be uLaw though, when you detect this, you should then feed the compressed data to a uLaw decoder and then feed the output of the decoder to the pipeline. You would initialize the pipeline with the format that the uLaw decoder outputs (it should be PCM).

    I haven't played with uLaw in particular, but I have tried with other formats and it's fairly simply to connect the different pieces together.

  12. Harry says:

    I am having some trouble getting this working correctly I am using the Memorystream object to get the data from a wcf service. The error i get is :

    at MS.Internal.XcpImports.CheckHResult(UInt32 hr)

      at MS.Internal.XcpImports.MediaElement_SetMediaStreamSource(MediaElement mediaElement)

      at System.Windows.Controls.MediaElement.SetSource(MediaStreamSource mediaStreamSource)

    Any help would be appreciated,

    Thanks

  13. gillesk says:

    Can you explain a bit how you connect the response from the WCF service to your MediaStreamSource? Do any other the methods of the MediaStreamSource get called? This looks like an error where Silverlight cannot properly connect your MediaStreamSource to the MediaElement.

  14. michael says:

    Awesome Post! I am new to silverlight and this project has helped me beyond expression. I have a question though and would be very grateful for a response.

    How do I dynamically load an audio file to the WaveMediaStreamSource class? I have tried WebRequest and HTTPWebRequest with no luck as I have received thread and object state errors. I'm new to the WPF world and would appreciate any direction.

    Many thanks in advance.

  15. Erick says:

    Hi,

    Great blog!!!!

    Is any way to make the property MediaElement.Position work? I need to set the position where the media starts or change it during playing

    Thanks in advance.

  16. gillesk says:

    Michael,

    You should be able to set the stream from a WebRequest or an HttpWebRequest. When you get the EndResponse callback, you might need to get the stream and then post back to the UI thread to set it in your MediaStreamSource.

  17. gillesk says:

    Erick,

    The MediaElement position should work fine with the sample. If you want to set the position before starting playback, you should set your MediaElement AutoPlay property to false. Setup your MediaStreamSource and when you receive the MediaOpened event, you can set the position to where you want it to start and then call play. To change position during playback, you can just change the position property.

    The following code in my MediaStreamSource sample will get called when the position is changed and the right data will be returned:

           /// <summary>

           /// Called when asked to seek to a new position

           /// </summary>

           /// <param name="seekToTime">the time to seek to</param>

           protected override void SeekAsync(long seekToTime)

           {

               if (seekToTime > this.wavParser.Duration)

               {

                   throw new InvalidOperationException("The seek position is beyond the length of the stream");

               }

               this.currentPosition = this.wavParser.WaveFormatEx.BufferSizeFromAudioDuration(seekToTime) + this.startPosition;

               this.currentTimeStamp = seekToTime;

               ReportSeekCompleted(seekToTime);

           }

    After the seek has been done, the then Sample request will be for the seeked position.

    I hope this helps your scenario.

  18. DG says:

    Hi,

    I have a very urgent reuirement in my project; Playing WAV file in Silverlight application (Silverlight 4 + MVVM Framework + WCF). It's should working like a Media Player. That have a PLAY, PAUSE and STOP BUTTON as well as Seek Bar.I can not convert the file to MP3 or WMV. Is there any way to accomplish the requirement?

    Thanks in advance for your help.

  19. gillesk says:

    Hi DG,

    This should be easily achievable. For a media player functionality, there are a lot of templates and examples that you can use. I don't have any specific links though. For the playback of the wave file. It would be very easy. If you take the code for WavMSS (included in the post) and set it to the source of your MediaElement then you will have wave playback as long as the wave is in PCM format. This would behave just like if you were using an MP3 file (pause, seek, stop) everything should just work.

  20. DG says:

    Hi gillesk,

    Thank you for your valueable inputs. Here is my Code

    <MediaElement Name="mediaElement1" />

    <TextBlock x:Name="txtDuration" Text="" FontFamily="Verdana" FontSize="26"></TextBlock>

    <Button x:Name="btnOpen" Click="btnOpen_Click" Height="50" Content="Open Media" Width="95"></Button>

    <Button x:Name="btnPlay" Content="Play Media" Click="btnPlay_Click" Height="50" Width="95"></Button>

    <Button x:Name="btnStop" Content="Stop Media" Click="btnStop_Click" Height="50" Width="95"></Button>

    Code Behind

    WaveMediaStreamSource wavMss;

    private void btnOpen_Click(object sender, RoutedEventArgs e)

    {

    OpenFileDialog ofd = new OpenFileDialog();

    if (ofd.ShowDialog().Value)

    {

    Stream s = ofd.File.OpenRead();

    wavMss = new WaveMediaStreamSource(s);

    WavParser wp = new WavParser(s);

    try

    {

    mediaElement1.SetSource(wavMss);

    mediaElement1.AutoPlay = false;

    // HOW DO I GET TOTAL DURATION OF MEDIA???????????

    txtDuration.Text = "Total Duration : " + wp.Duration.ToString();

    }

    catch (Exception ex)

    {

    Console.WriteLine(ex.Message);

    }

    }

    }

    private void btnStop_Click(object sender, RoutedEventArgs e)

    {

    mediaElement1.Stop();

    }

    private void btnPlay_Click(object sender, RoutedEventArgs e)

    {

    mediaElement1.Play();

    }

    It's a very simple code. I want the Total Duration of WAV file. So that it willl be the Maximum value for slider control; which actually works as SEEK control.

    <Button x:Name="btnOpen" Click="btnOpen_Click" Height="50" Content="Open Media" Width="95"></Button>

    <Button x:Name="btnPlay" Content="Play Media" Click="btnPlay_Click" Height="50" Width="95"></Button>

    <Button x:Name="btnStop" Content="Stop Media" Click="btnStop_Click" Height="50" Width="95"></Button>

    Code Behind

    WaveMediaStreamSource wavMss;

    private void btnOpen_Click(object sender, RoutedEventArgs e)

    {

    OpenFileDialog ofd = new OpenFileDialog();

    if (ofd.ShowDialog().Value)

    {

    Stream s = ofd.File.OpenRead();

    wavMss = new WaveMediaStreamSource(s);

    WavParser wp = new WavParser(s);

    try

    {

    mediaElement1.SetSource(wavMss);

    mediaElement1.AutoPlay = false;

    // HOW DO I GET TOTAL DURATION OF MEDIA???????????

    txtDuration.Text = "Total Duration : " + wp.Duration.ToString();

    }

    catch (Exception ex)

    {

    Console.WriteLine(ex.Message);

    }

    }

    }

    private void btnStop_Click(object sender, RoutedEventArgs e)

    {

    mediaElement1.Stop();

    }

    private void btnPlay_Click(object sender, RoutedEventArgs e)

    {

    mediaElement1.Play();

    }

    It's a very simple code. I want the Total Duration of WAV file. So that it willl be the Maximum value for slider control; which actually works as SEEK control.

    Best Regards,

    DG

  21. gillesk says:

    Hi DG,

    To get the duration, you need to query it off the MediaElement after you get the MediaOpened event has fired. You don't even have to worry about the MediaStreamSource for this.

    Subscribe to the event in the MediaElement:

    <MediaElement Name="mediaElement1"  MediaOpened="ME_MediaOpened/>

    Add the EventHandler

    private void ME_MediaOpened( object sender, RoutedEventArgs e )

    {

         txtDuration.Text = "Total Duration : " + mediaElement1.NaturalDuration;

    }

  22. DG says:

    Hi gillesk,

    Thank you very much.

    Now, I'm trying to implement it.

  23. abdus samadh says:

    hi gillesk your work is price worthy…..thanks alot…..em new to silverlight…when i tried to follow this article i got an error on wavemediastreamsource….how can i refer this in my project….thank u

  24. Catalpa says:

    What would be the causes of a DRM Exception when setting the source to the MediaElement? I've had great success with your wav parser in the past, but the same basic code is now throwing this error. Only difference is the name of the file to load is coming from a wcf service instead of an object in the movie, but I've validated the URL is valid coming from the service.

    System.Windows.Media.DrmException was unhandled by user code

     Message=3220 3220 An error has occurred.

     StackTrace:

          at MS.Internal.XcpImports.CheckHResult(UInt32 hr)

          at MS.Internal.XcpImports.MediaElement_SetMediaStreamSource(MediaElement mediaElement)

          at System.Windows.Controls.MediaElement.SetSource(MediaStreamSource mediaStreamSource)

  25. Fabio says:

    Hi,

    i'm using your great software to play wav file on SIlverlight 4 client. I've a problem with short audio : the MediaElement plays no audio if the buffer is less than 32K. No error or exception is shown running the code,  do you know if there's a size limit of the buffer to play?

    Thanks

  26. Rami says:

    WaveMediaStreamSource , which namespace this class belongs?

  27. gillesk says:

    Hi Rami,

    WaveMediaStreamSource is not part of the silverlight framework. You can download it in source form from the link at the beginning of the post.

    Thank you.

  28. Vikas says:

    Hi Giles,

    How can I load the 8-bit A-Law Encoded Waveformat file(s) in the silverlight using MediaStreamSource class.

    Your help on this would be greatly appreciated.

    Cheers

    Vikas

  29. Yogesh says:

    Great work Giles!!!

    However, I was wondering, how can i extract the samples while the wave file is playing. I need to extract the samples and boost the volume (using a multiplier) of the wave file. i.e. without using the MediaElement.Volume.

    Thanks and best regards,

    Yogesh

  30. gillesk says:

    Yes, that would be really easy. In the mediastreamsource, as you are getting the samples, you can modify them in any way you like. If you look at the code, the data is handed back to the MediaElement in GetSampleAsync. before giving the data, you can increase the volume, perform some noise cancellation or do any kind of processing that you would like. I had a sample that would gather some of the samples and do a visualization as the sounds were playing.

  31. Yogesh says:

    Exactly Gilles, that's the idea. i.e. to boost the volume in GetSampleAsync. However, i m struggling with reading the samples, perhaps in a byte array, just after "this.wavParser.ProcessDataFromChunk()". Once, i m able to retrieve the sample then i can perform my calculation on it.

    Any help would be greatly appreciated.

    Thanks and best regards,

    Yogesh

  32. gillesk says:

    You can do a couple of things. You can process the data ahead of time, when you get the stream from your source, you can read the data from the stream and process it, or you can do it in GetSampleAsync.

    Here's a sample of what it could look like for GetSampleAsync if you wanted to increase the volume by 20%. We read the data into a byte buffer, process it based on the sample size and then wrap it into a MemoryStream to pass back to the MediaElement.

           protected override void GetSampleAsync(MediaStreamType mediaStreamType)

           {

               // Start with one second of data, rounded up to the nearest block.

               uint bufferSize = (uint)AlignUp(

                   this.wavParser.WaveFormatEx.AvgBytesPerSec,

                   this.wavParser.WaveFormatEx.BlockAlign);

               // Figure out how much data we have left in the chunk compared to the

               // data that we need.

               bufferSize = Math.Min(bufferSize, (uint)this.wavParser.BytesRemainingInChunk);

               if (bufferSize > 0)

               {

                   this.wavParser.ProcessDataFromChunk(bufferSize);

                   // We have a sample, copy it to a byte buffer

                   byte[] sampleData = new byte[bufferSize];

                   // Read the data from the stream

                   this.stream.Read(out sampleData, 0, bufferSize);

                   if (this.wavParser.WaveFormatEx.BitsPerSample == 8)

                   {

                       // 8-bit are unsigned make them sign process and make them

                       // unsigned again

                       foreach(byte b in sampleData)

                       {

                           int signed = (int) b – 128;

                           // Increase the volume by 20%

                           signed *= 1.2;

                           // Move back to unsigned

                           signed += 128;

                           // Clamp back to a byte

                           b = (byte) Math.Min(byte.MinValue, Math.Max(signed, byte.MaxValue));

                       }

                   }

                   else if (this.wavParser.WaveFormatEx.BitsPerSample == 16)

                   {

                       // 16-bit are signed

                       for(int i = 0; i < sampleData.Length; i += 2)

                       {

                           // Need to verify endianess for this

                           int signed = sampleData[i] << 8 + sampleData[i+1];

                           // Increase the volume by 20%

                           signed *= 1.2;

                           // Clamp back to a byte

                           signed = Math.Min(short.MinValue, Math.Max(signed, short.MaxValue));

                           sampleData[i] = (byte) signed >> 8;

                           sampleData[i+1] = (byte) signed;

                       }

                   }

                   // We have processed the data

                   MemoryStream m = new MemoryStream(sampleData);

                   // Send out the next sample

                   MediaStreamSample sample = new MediaStreamSample(

                       this.audioDesc,

                       this.m,

                       0,

                       sampleData.Length,

                       this.currentTimeStamp,

                       this.emptySampleDict);

                   // Move our timestamp and position forward

                   this.currentTimeStamp += this.wavParser.WaveFormatEx.AudioDurationFromBufferSize(bufferSize);

                   this.currentPosition += bufferSize;

                   /* Uncomment to loop forever

                   // If there are no more bytes in the chunk, start again from the beginning

                   if (this.wavParser.BytesRemainingInChunk == 0)

                   {

                       this.wavParser.MoveToStartOfChunk();

                       this.currentPosition = this.startPosition;

                   }

                   */

                   ReportGetSampleCompleted(sample);

               }

               else

               {

                   // Report EOS

                   ReportGetSampleCompleted(new MediaStreamSample(this.audioDesc, null, 0, 0, 0, this.emptySampleDict));

               }

           }

  33. Yogesh says:

    Thanks a lot Gilles!! This definitely is the way forward. I did copy the Stream to a MemoryStream, but didn't take the wave files bits per sample into account. That's why all my calculation was going wrong.

    Now I need to find the RMS on the peaks.. Will keep you posted on the outcome.

    Thanks again!! :)

    Best regards,

    Yogesh

  34. greatJL says:

    Hi, if im usin silverlight to make a audio recorder, and thing is , it doesnt playback, the methods i have have found seem mostly revolve around  

    "wavemediastreamsource waveMss =new wavemediastreamsource(audiosink.audiodata); mediaelement.setsource(waveMss);"

    However it does not seem to be working for me

  35. Dor Cohen says:

    I used WAVmss library to play wave files on silverlight media element and it works great! thanks!

    It plays the first time I load the file into the element, but when I try and play it a second time it doesn't play. The playback code could not be simpler:

    private void Play()

    {

       mediaElement.Stop();

       mediaElement.Position = TimeSpan.Zero;

       mediaElement.Play();

    }

    I have debugged it. The Position property does get set to zero and when I break in after clicking it again its position has moved to the end.

    I read your answer about playing in loop (blogs.msdn.com/…/playing-back-wave-files-in-silverlight.aspx) but it doesn't fit my needs. I don't want a loopless play if my file.

    I want the user can hear the file again after it reached the end, can you help me?

  36. gillesk says:

    You should not have to call Stop. You should be able to simply set the position, that will issue a seek on your MSS and you will start providing samples from the requested position.

  37. Dor Cohen says:

    Maybe I'm missing something, here is my code where I'm opening wave file and play it successfully for the first time, Here is the file reading and setting as source in my media element:

               OpenFileDialog openFileDialog = new OpenFileDialog();

               MemoryStream audioSource = new MemoryStream();

               if (openFileDialog.ShowDialog() == true)

               {

                   using (FileStream fileStream = openFileDialog.File.OpenRead())

                   {

                       audioSource.SetLength(fileStream.Length);

                       fileStream.Read(audioSource.GetBuffer(), 0, (int)fileStream.Length);

                   }

               }

               WaveMSS.WaveMediaStreamSource audioStreamSource = new WaveMSS.WaveMediaStreamSource(audioSource);

               mediaElement1.SetSource(audioStreamSource);

    And when the first play over (I know it since I'm getting _MediaEnded event) I can't play this video again.

    I tried to set the position of the MediaElement but failed to play it again:

               mediaElement1.Position = TimeSpan.FromSeconds(0);

               mediaElement1.Play();

    Instead it sets up the position to the end and pops up again the _MediaEnded event.

    What can I do?

  38. gillesk says:

    Ok, I found the issue. The implementation of SeekAsync, the position in the stream was changed but the remaining count in the chunk was not. In WaveMediaStreamSource.cs, look for the SeekAsync method and change the code to this (add the MoveToChunkOffest line):

               this.currentPosition = this.wavParser.WaveFormatEx.BufferSizeFromAudioDuration( seekToTime ) + this.startPosition;

               this.wavParser.MoveToChunkOffset( ( uint ) this.wavParser.WaveFormatEx.BufferSizeFromAudioDuration( seekToTime ) );

               this.currentTimeStamp = seekToTime;

               ReportSeekCompleted( seekToTime );

  39. Dor Cohen says:

    Thanks! It works now!

  40. George Birbilis says:

    At ClipFlair Studio (http://studio.clipflair.net) I was having an issue with MediaElement not thowing an event when WAV playback was finished, wonder if this last correction posted above by Gilles (the MoveToChunkOffset) fixes that issue too

  41. Asif Iqbal says:

    hello Gilles..Great post..can you please tell can  silvelight MediaPlayer   play.mov videos.?

  42. Asif Iqbal says:

    Hello Gilles.

    I have a silverlight project in which media element is used to stream videos from url.

    url with .mp4 extension is playing but with .mov extension are not working. Here is the sample links : video.ch9.ms/…/DEV-B385_mid.mp4

    video.ch9.ms/…/DEV-B385_mid.mov

    Below is small piece of code for MainPage.xaml.cs of silverlight player

    private void LoadVideo()

       {

           var uri = "video.ch9.ms/…/DEV-B385_mid.mov";

           mPlayer.Playlist.AutoLoad = true;

           mPlayer.Playlist.AutoPlay = true;

           mPlayer.Playlist.StretchMode = Stretch.Uniform;

           if (mPlayer.Playlist.Items.Count > 0)

           {

               mPlayer.Playlist.Items[0].MediaSource = new Uri(uri);

           }

           else

           {

               PlaylistItem newPlaylistItem = new PlaylistItem

               {

                   MediaSource = new Uri(uri)

               };

               mPlayer.Playlist.Items.Add(newPlaylistItem);

           }

           HtmlPage.Document.SetProperty("title", "MyMedia");

           tracker.LogVideoStart();

       }

  43. George Birbilis says:

    MediaElement can't play .MOV, however some newer .MOV files are just .MP4 files, so you could try renaming to .MP4

  44. George Birbilis says:

    btw, there is a big issue on MacOS-X that I'm facing at ClipFlair Studio (http://studio.clipflair.net). Silverlight seems to record there at 24-bit by default, while 24-bit playback isn't supported in either MacOS-X or Windows with Silverlight.

  45. George Birbilis (zoomicon.com) says:

    I finally found out why I wasn't getting MediaEnded events for WAVs in Silverlight when using your WaveMediaSource (it was happening with Mp3MediaSource from Managed Media Helpers that I just added to ClipFlair Studio's AudioRecorder component)

    Seems MediaElement doesn't always raise MediaOpened/MediaEnded events if it is not in the visual tree (e.g. you created it in code and didn't add it to the tree). Not sure why it does that, probably a bug

    zoomicon.wordpress.com/…/gotcha-mediaelement-must-be-in-visual-tree-for-mediaopened-mediaended-to-be-fired

    another strange thing I see (at least with MP3) is that it is a bit faster to use AutoPlay, compared to use MediaOpened event and Play – zoomicon.wordpress.com/…/gotcha-mediaelement-autoplay-faster-than-doing-play-at-mediaopened