Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly provide data for <audio>?

I am serving binary data for HTML5 audio thorough .aspx page. The file can be played but there is a problem with SeekBar and replaying in Safari (iPad), Chrome and Firefox.
Edit I have simplified (hopefully not too much) the code and full listing is presented (page file name is obviously play.aspx).

<%@ Page Language="C#"  %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
       if ( Request["filename"] != null)
       {
           string FilePath = MapPath(Request["filename"]);
           long fSize = (new System.IO.FileInfo(FilePath)).Length;
           //Headers
           Response.AddHeader("Content-Length",fSize.ToString());
           Response.ContentType = "audio/mp3";
           //Data
           Response.WriteFile(FilePath);
           Response.End();   
       };      
    }
</script>
<body>
    <p>Direct</p>
       <audio controls="controls" preload="none">
         <source src="sound.mp3" type="audio/mp3" />
      </audio>
      <p>Provided</p>
      <audio controls="controls" preload="none">
         <source src="play.aspx?filename=sound.mp3" type="audio/mp3" />
      </audio>
</body>
</html>

So Content-Length and Content-Type headers are supplied. Two <audio> tags enable compare behavior when the file is accessed directly or thorough an .aspx page.

The question is how to provide data to <audio> tag behave correctly on all required browsers (ff, safari, chrome, IE9+).

Problems

Safari (iPad): When the play button is pressed the sound is played but there is "Streaming ..." instead of Seek Bar and playing seem to last forever and sound cannot be replayed.
Firefox(windows): Seek Bar is displayed but shows increasing time until the end and behaves correctly when replayed.
Chrome(windows): Seek bar correct but cannot be replayed.
IE10: OK

I have found similar questions on but without an answer. From observing another page with fiddler it seems that header Accept-Ranges: bytes and Content-Range: bytes 0-8100780/8100781 can help. However provide necessary features seems to be too difficult as just an attempt so I did not try it.

Note I am looking for solution for other connected problem in How to play .m4a with HTML5 audio in IE(9+) and Safari (Pad)?

like image 946
IvanH Avatar asked Aug 20 '13 13:08

IvanH


People also ask

How does audio data work?

In computer the audio signals are stored in the form of digital. ... then each 0 and 1 is stored into a memory as a raw data buffer corresponding to the recorded audio. it can be stored as file on disk and compressed as mp3 with a compression algorithm. with some audio command, you directly listen to the file.


1 Answers

Simple answer: It is necessary to handle a browser range request.

Complete story

When comparing Fiddler captured communication between Firefox and IIS I have noticed that the response status for the direct link is 206 Partial Content while for the indirect link 200 OK.
More careful analysis of request shown that Firefox requests Range: bytes=0- and iPad Range: bytes=0-1 and later on Range: bytes=0-fullsize (Capture Traffic from iOS Device).
Setting Response.StatusCode = 206 was enough to fool Firefox but not Safari. But I have known the key information and rest was simple. It is necessary to honor the range request see: SO: HTML5 video will not loop. The explanation how to do it I have found in Aha! #1 – ASP.NET ASHX Video Streaming for HTML5 Video. So the new code is (only active part is presented rest is the same as in the answer):

    protected void Page_Load(object sender, EventArgs e)
    {
       if ( Request["filename"] != null)
       {
           string FilePath = MapPath(Request["filename"]);
           long fSize = (new System.IO.FileInfo(FilePath)).Length;
           long startbyte = 0;
           long endbyte=fSize-1;
           int statusCode =200;
           if ((Request.Headers["Range"] != null))
           {
               //Get the actual byte range from the range header string, and set the starting byte.
               string[] range = Request.Headers["Range"].Split(new char[] { '=','-'});
               startbyte = Convert.ToInt64(range[1]);
               if (range.Length >2 && range[2]!="")  endbyte =  Convert.ToInt64(range[2]);
               //If the start byte is not equal to zero, that means the user is requesting partial content.
               if (startbyte != 0 || endbyte != fSize - 1 || range.Length > 2 && range[2] == "")
               {statusCode = 206;}//Set the status code of the response to 206 (Partial Content) and add a content range header.                                    
           }
           long desSize = endbyte - startbyte + 1;
           //Headers
           Response.StatusCode = statusCode;
           Response.ContentType = "audio/mp3";
           Response.AddHeader("Content-Length",desSize.ToString()); 
           Response.AddHeader("Content-Range", string.Format("bytes {0}-{1}/{2}", startbyte, endbyte , fSize));
           //Data
           Response.WriteFile(FilePath,startbyte,desSize);
           Response.End(); 
       };      
    }

It is probably convenient to add other headers (Content-Disposition, Accept-Ranges, Access-Control-Allow-Origin) but the intention was present solution as simple as possible.

Lesson taken: If I had not been fixed to <audio> and had looked also for <video>, I would have found the solution more quickly.

like image 176
IvanH Avatar answered Oct 06 '22 01:10

IvanH