jho
jho

Reputation: 243

How do I play an mp3 from the file system on Xamarin Forms iOS?

I'm writing an app which plays an mp3 file.

In Android this is straight forward:

  1. Use Xamarin.Essentials.FilePicker to pick the mp3 filepath
  2. Use Android.Media.MediaPlayer to play the mp3 filepath

I tried a similar approach for iOS:

  1. Use Xamarin.Essentials.FilePicker to pick the mp3 filepath (either from icloud or on phone)
  2. Use AVAudioEngine to play the mp3 filepath

This works in the simulator but throws an exception when trying to create an AVAudioFile. (LibVlcSharp also fails when trying to load the filepath).

NSError err;
AVAudioFile file = new AVAudioFile(new NSUrl(audioUri, false), out err);
//err is null after this call throws an exception

Exception: "Could not initialize an instance of the type 'AVFoundation.AVAudioFile': the native 'initForReading:error:' method returned nil.\nIt is possible to ignore this condition by setting ObjCRuntime.Class.ThrowOnInitFailure to false."

(Setting ThrowOnInitFailure = false doesn't help as file is still null).

I suspect it's a permissions issue, so I tried using the MPMediaPickerController:

MPMediaPickerController mp = new MPMediaPickerController();
mp.AllowsPickingMultipleItems = false;
mp.ShowsCloudItems = true;
  UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(mp, true, null);

with MPMediaLibraryAuthorizationStatus set to Authorized and the following set in info.plist:

<key>NSAppleMusicUsageDescription</key>
<string>Select a music file</string>

This code does nothing whatsoever.

How can I get this or the file picker method to work?

UPDATE

I've managed to get the MediaPicker working. Will update on that soon.

My next problem is I get an NSUrl from the MediaPicker. If I pass this directly into the AVAudioEngine, it plays fine.

But if I save the NSUrl to a string for serialization purposes, and then try to recreate it with the following code, the same exception above occurs when trying to create AVAudioFile.

//url1 works fine
NSUrl url1 = mediaItemCollection.Items[0].AssetURL;
AVAudioFile file = new AVAudioFile(url1, out err);
        
//url2 throws the same exception above
string str = mediaItemCollection.Items[0].AssetURL.AbsoluteString;
NSUrl url2 = new NSUrl(str, false) 
AVAudioFile file = new AVAudioFile(url2, out err);

Any ideas?

Upvotes: 0

Views: 203

Answers (2)

jho
jho

Reputation: 243

Finally achieved playback. I will document below the major obstacles I encountered and how I resolved them, in case anyone finds this useful.

Displaying MPMediaPickerController

The code above didn't work because I was trying to launch it from a popup window and thus needed to find the popup's corresponding ViewController instead of the RootViewController I was using above.

This post had the solution: How to find current UIViewController in Xamarin

Serializing a NSUrl

In order to Serialize an MPMediaItem you can't simply use the Url string. You need to use:

MPMediaItem.PersistentID which is a ulong.

To recreate a NSUrl from the ulong, you need to:

  1. Query the media library again for the MPMediaItem with that PersistentID using MPMediaQuery and MPMediaPropertyPredicate
  2. Use the query resultant MPMediaItem's AssetURL property

Playing audio but no sound comes solution

This one drove me crazy but turns out one of the phone's volume settings was preventing the sound emission.

This post and the following code solved the problem: Swift: Playing audio using AVPlayer - Audio is not playing, cannot hear audio

AVAudioSession.SharedInstance().SetCategory(AVAudioSession.CategoryPlayback);
AVAudioSession.SharedInstance().SetActive(true);

Thanks everyone for your help on this. It was much appreciated. Happy coding!

Upvotes: 0

Liqun Shen-MSFT
Liqun Shen-MSFT

Reputation: 8260

You could call NSUrl.StartAccessingSecurityScopedResource Method and check the return value:

NSUrl url = NSUrl.FromString(audioUri);
var securityEnabled = url.StartAccessingSecurityScopedResource();

For more info, you could refer to startAccessingSecurityScopedResource

============= Origin answer======

You may try using AVPlayer.

AVPlayerItem aVPlayerItem = new AVPlayerItem(new NSUrl(audioUri));
AVPlayer aVPlayer = new AVPlayer(aVPlayerItem);
aVPlayer.Play();

also add this in info.plist if you load from a http source

<key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict>

Hope it works.

Upvotes: 0

Related Questions