Xamarin-Plugins icon indicating copy to clipboard operation
Xamarin-Plugins copied to clipboard

Play audio from an HTTP source

Open ThaineNorris opened this issue 7 years ago • 10 comments

Hello,

Thank you for this perfect tool! It is exactly the simple player I was looking for, and implemented in Xamarin.Forms! This has saved me so much time.

I am very new to Xamarin.Forms. I see how you open a file to get a stream, but how would one get a valid stream from a URL, say from a podcast MP3 hosted in the cloud?

Thank you for your help, Thaine Norris

ThaineNorris avatar Aug 15 '17 23:08 ThaineNorris

Thanks @ThaineNorris - I'll add this to my ToDo list.

I'm just testing some Android fixes and adding macOS support - and then I'll add support for web audio sources.

Cheers!

adrianstevens avatar Sep 30 '17 22:09 adrianstevens

@adrianstevens Thanks for sharing this plugin! Do you know if being able to stream audio from a URL will be an option in the near future? Or do you have any suggestions for how to implement it myself? Thanks!

LyndiLeighWK avatar Jul 10 '18 20:07 LyndiLeighWK

My "fix" was to convert the URL resource into a stream, and load that stream:

ISimpleAudioPlayer player = CrossSimpleAudioPlayer.Current;
string url = "some-url-string";
WebClient wc = new WebClient();
Stream fileStream = wc.OpenRead(url);
player.Load(fileStream);
player.Play();

My only problem is, I'm not sure how to detect an Http Error (say, if the resource is unavailable, or if there is some server error). I happen to be quite new to C#. Let me know if you guys have any suggestions!

markguindi1 avatar Jul 26 '18 17:07 markguindi1

Thanks for the suggestion @markguindi1! That helped us get us on the right track.

This is what we ended up using:

SimpleAudioPlayer player = CrossSimpleAudioPlayer.Current;
string url = "url-audio-string";
HttpClient httpClient = new HttpClient();
Stream fileStream = await httpClient.GetStreamAsync(url);
player.Load(fileStream);

As far as detecting Http Errors, before loading the audio, we do a quick Http request to the audio file to check the status code. If it returns 200, we'll load the audio player. Here's an example:

try
            {
                var httpClient = new HttpClient();
                var request = new HttpRequestMessage(HttpMethod.Head, new Uri(audioFile));
                var response = await httpClient.SendAsync(request);

                request.Dispose();

                if (response.StatusCode != HttpStatusCode.OK)
                    GATracker.DefaultTracker.TrackException($"{(int)response.StatusCode} {response.StatusCode}\n{audioFile}", false);

                return (response.StatusCode == HttpStatusCode.OK);
            }
            catch (Exception e)
            {
                GATracker.DefaultTracker.TrackException($"{e.Message}\n{e.InnerException.Message}\n{e.StackTrace}", false);
                return false;
            }

LyndiLeighWK avatar Oct 31 '18 17:10 LyndiLeighWK

Thank you for this lib, I hope I can contribute sometime soon.

Very interesting approach @markguindi1 and @LyndiLeighWK.

I know that for android and iOS there are native os APIs that can do that for us like MediaPlayer for android and AVFoundation for iOS. maybe this would be the way to go for implementing this on this lib :)

I haven't looked deep into the lib code, you are probably already using them, we would just have to make the streaming apis available.

DanielCauser avatar Dec 19 '18 21:12 DanielCauser

Thanks @LyndiLeighWK and @DanielCauser. I've looked into those libs that @DanielCauser mentioned, but didn't end up using them as they didn't seem to fit my specific use case (as far as I remember from when I was working on my project).

And @LyndiLeighWK I've thought of your idea, to make a quick HTTP request before loading the audio, but it didn't work for me, as the server I was connecting to was a bit unreliable (don't ask). So making an HTTP request before loading the audio would be of little use to me, as the initial request may succeed but loading the audio (the 2nd request) may fail, or vice versa. I was looking for a way to check the HTTP status of the request that actually produced the stream, but I couldn't find a way to access it.

markguindi1 avatar Jan 18 '19 01:01 markguindi1

What is the status of playing audio from an HTTP source ?

bodeg avatar Aug 12 '19 03:08 bodeg

Thanks for the suggestion @markguindi1! That helped us get us on the right track.

This is what we ended up using:

SimpleAudioPlayer player = CrossSimpleAudioPlayer.Current;
string url = "url-audio-string";
HttpClient httpClient = new HttpClient();
Stream fileStream = await httpClient.GetStreamAsync(url);
player.Load(fileStream);

As far as detecting Http Errors, before loading the audio, we do a quick Http request to the audio file to check the status code. If it returns 200, we'll load the audio player. Here's an example:

try
            {
                var httpClient = new HttpClient();
                var request = new HttpRequestMessage(HttpMethod.Head, new Uri(audioFile));
                var response = await httpClient.SendAsync(request);

                request.Dispose();

                if (response.StatusCode != HttpStatusCode.OK)
                    GATracker.DefaultTracker.TrackException($"{(int)response.StatusCode} {response.StatusCode}\n{audioFile}", false);

                return (response.StatusCode == HttpStatusCode.OK);
            }
            catch (Exception e)
            {
                GATracker.DefaultTracker.TrackException($"{e.Message}\n{e.InnerException.Message}\n{e.StackTrace}", false);
                return false;
            }

This is working for me! Thanks. However, in my case, android was throwing Android.OS.NetworkOnMainThreadException, which i fixed by using

await Task.Run(async () =>
           {
               await PlayAudio();
           });

async Task PlayAudio()
       {
           player = CrossSimpleAudioPlayer.Current;
           string url = "some audio url here";
           using var httpClient = new HttpClient();
           var fileStream = await httpClient.GetStreamAsync(url);
           player.Load(fileStream);
           player.Play();
           player.PlaybackEnded += this.Player_PlaybackEnded;

       }

softsan avatar Jan 01 '20 15:01 softsan

Dear Contributors,

Please confirm if there is a way by which we can stream mp3 from internet URL i.e. we should not require the complete download of mp3 prior to starting playback. This is for the use case of large MP3 files and slower internet. Something similar to buffered playback offered by Portals.

Sorry if I am asking for too much.

Regards, Dhiraj.

dhiraj-shetty avatar Apr 14 '20 02:04 dhiraj-shetty

Hi,

Please suggest me an option to stream url file without downloading everything in the starting itself, Is there any way to implement this case myself using this plugin?

Jack-Paul avatar May 09 '21 11:05 Jack-Paul