Compensating for different devices ability to handle large audio files.

Situation

Recently I completed a project to allow downloading of audio recordings of Council Meetings.  the council meetings are rpesented to the City government office as MP3 players - and a support officer uploads them.  These MP3 files are large - in the order of 50+MB, depending on teh length of the meeting.

Recently after live testing it has been reported that the download function on iOS devices "loops" after 5 minutes and returns to the beginning of the stream.  

Original context

Initially, the files were set up to download on demand:

        [AllowAnonymous]
        public FilePathResult Download(String filename)
        {
            String myfile = Path.Combine(HttpContext.Server.MapPath("~/Content/audiofiles/"), filename);
            return File(myfile, System.Net.Mime.MediaTypeNames.Application.Octet, filename);
        }

On initial testing all devices played my test track, and also played the larger files.  There lies a moral here in testing larger files - one should listen to the full thing, and not assume that because it starts it also finishes..

Solution pt1

After experiementing with a number of different methods of downloading the file I attempted to use the HTML5 audio tag which uses the browser as a media player.

<audio controls preload="auto"><source src="@ViewBag.location" type="audio/mp3"></audio>

This led to a second problem - on testing with an Android device the Streaming method would not work.

Solution pt2

The second part of the solution involved identifying the browsers which worked with each listening method.
Mac and PC browsers with Chrome / Firefox/ Safari (mac) and Internet explorer (PC) worked great with both methods

iOS devices would stream content using the audio element, but displayed a consistent error on downloading the file.

Android devices would download the large files well, but not stream it. 

Windows phones seemed happy to do both equally well.

My controller code
 [AllowAnonymous]
        public ActionResult Index()
        {
            DirectoryInfo path = new DirectoryInfo(Server.MapPath("~/Content/audiofiles"));
            List<audiofile> files = new List<audiofile>();
            foreach (FileInfo file in path.GetFiles("*.mp3"))
            {
                audiofile afile = new audiofile(file.Name);
                if (DateTime.Now.Subtract(afile.Date) < new TimeSpan(60, 0, 0, 0))
                {
                    files.Add(afile);
                }
            }
            files.Sort((x, y) => y.Date.CompareTo(x.Date));
            ViewBag.Filelist = files;
            
            ViewBag.showDownload = showDowload(Request);
            ViewBag.showStream = showStream(Request);
            return View();
        }

The showDownload and showStream functions were reasonably straightforward;

 private bool showDowload(HttpRequestBase request)
        {
            bool answer = true;
            String Manufacturer = request.Browser.MobileDeviceManufacturer;
            if (Manufacturer.Contains("Apple"))
            {
                answer = false;
            }
            return answer;
        }
 
private bool showStream(HttpRequestBase request)
        {
            bool answer = true;
            if (request.Browser.Browser.Contains("Mozilla"))
            {
                answer = false;
            }
            return answer;
        }
Whilst not the most efficient code - it is Readable, and that is sometimes what counts more. The Broswer.MobileDeviceManufacturer gives an answer of Unknown for all OSX devices - which gives a ready indication that one is dealing with an iOS device,  The android device was harder to pin down as it didn't even identify as a mobile device.  The Mozilla Browser does not pick up other mozilla user-agents used in testing.

The structure of these is to allow for future problems to be rectified.  ie if there is a future version of a browser that cannot download the files it can be added to the relevant private function switching the answer to false for that browser.

View  code


@{
    List<CAudio.Models.audiofile> audiolist = ViewBag.Filelist;
    bool showDownload = ViewBag.showDownload;
    bool showStream = ViewBag.showStream;
}


<table>
    <tr>
        <th>Date</th>
        <th>Meeting</th>
        @if (showDownload)
        {
            <th>Download link</th>
        }

        @if (showStream)
        {
            <th>Stream / Play</th>
        }

    </tr>
    @foreach (var afile in audiolist)
    {

        var link = afile.getFilename();

        <tr>
            <td>@afile.Date.ToLongDateString()</td>
            <td>@afile.Meeting</td>
            @if (showDownload)
            {
                <td>
                    @Html.ActionLink("Download", "Download", new { filename = link }, new { @class = "btn btn-primary", @role = "button", @onclick = String.Format("ga('send', 'event', 'councilAudio', 'download', '{0}');", link) })

                </td>
            }
            @if(showStream)
            {
            <td>
                @Html.ActionLink("Stream", "Play", new { filename = link }, new { @class = "btn btn-primary", @role = "button", @onclick = String.Format("ga('send', 'event', 'councilAudio', 'Stream', '{0}');", link) })
            </td>
            }
        </tr>
    }
</table>

This results in only showing the appropriate buttons to download or stream the audio to the correct devices.
iOS screen Capture


PC Screen Capture
Android devices only show teh download button.  all processing occurs at the server.



If anyone happens to come across any other devices which exhibit problem behaviviours with downloading, or streaming please let me know.

Comments

Popular Posts