Search code examples
htmlangularaudio.net-coremp3

HTML5 audio seek is not working properly. Throws Response Content-Length mismatch Exception


I'm trying to stream audio file to Angular application where is html5 audio element and src set to my api end point (example. /audio/234). My backend is implemented with .NET Core 2.0. I have implemented already this kind of streaming: .NET Core| MVC pass audio file to html5 player. Enable seeking

Seek works if I don't seek to end of file immediately when audio starts playing. I use audio element's autoplay attribute to start playing immediately audio element has enough data. So in my situation audio element has not yet all the data when I seek so it make new GET to my API. In that situation in my backend log there is this Exception:

fail: Microsoft.AspNetCore.Server.Kestrel[13] [1] Connection id "0HL9V370HAF39", Request id "0HL9V370HAF39:00000001": An unhandled exception was thrown by the application. [1] System.InvalidOperationException: Response Content-Length mismatch: too few bytes written (0 of 6126919).

Here is my audio controller GET method.

byte[] audioArray = new byte[0];
//Here I load audio file from cloud
long fSize = audioArray.Length;
long startbyte = 0;
long endbyte = fSize - 1;
int statusCode = 200;
var rangeRequest = Request.Headers["Range"].ToString();
_logger.LogWarning(rangeRequest);
if (rangeRequest != "")
{
    string[] range = Request.Headers["Range"].ToString().Split(new char[] { '=', '-' });
    startbyte = Convert.ToInt64(range[1]);
    if (range.Length > 2 && range[2] != "") endbyte = Convert.ToInt64(range[2]);
    if (startbyte != 0 || endbyte != fSize - 1 || range.Length > 2 && range[2] == "")
    { statusCode = 206; }
}

_logger.LogWarning(startbyte.ToString());
long desSize = endbyte - startbyte + 1;
_logger.LogWarning(desSize.ToString());
_logger.LogWarning(fSize.ToString());
Response.StatusCode = statusCode;
Response.ContentType = "audio/mp3";
Response.Headers.Add("Content-Accept", Response.ContentType);
Response.Headers.Add("Content-Length", desSize.ToString());
Response.Headers.Add("Content-Range", string.Format("bytes {0}-{1}/{2}", startbyte, endbyte, fSize));
Response.Headers.Add("Accept-Ranges", "bytes");
Response.Headers.Remove("Cache-Control");
var stream = new MemoryStream(audioArray, (int)startbyte, (int)desSize);

return new FileStreamResult(stream, Response.ContentType)
{
    FileDownloadName = track.Name
};

Am I missing some Header or what?

I didn't get this exception with .NET Core 1.1 but I'm not sure is it just coincident and/or bad testing. But if anybody has information is there something changed in .NET Core related to streaming I will appreciate that info.


Solution

  • Now when I research more I found this: https://learn.microsoft.com/en-us/aspnet/core/aspnetcore-2.0 look Enhanced HTTP header support- heading. It says this

    If an application visitor requests content with a Range Request header, ASP.NET will recognize that and handle that header. If the requested content can be partially delivered, ASP.NET will appropriately skip and return just the requested set of bytes. You do not need to write any special handlers into your methods to adapt or handle this feature; it is automatically handled for you.

    So all I need is some clean up when I move to .NET Core 1.1 to 2.0 because there is already handler for those headers.

    byte[] audioArray = new byte[0];
    //Here I get my MP3 file from cloud
    var stream = new MemoryStream(audioArray);
    return new FileStreamResult(stream, "audio/mp3")
    {
        FileDownloadName = track.Name
    };