Unable to play music from using avplayer in iOS? - ios

I am uploading music to server by converting it to NSData. When i upload the file to server then i also save it to document directory.So when i pass the url of any other music file other than i uploaded then it plays music.But when i try to play music file which i uploaded then it does not play the music.Below is the code for that.
Code for play music
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:fileURL];
AVPlayer *player = [AVPlayer playerWithPlayerItem:playerItem];
player = [AVPlayer playerWithURL:fileURL];
[player play];

it may help you
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:fileURL options:nil];
NSArray *keys = [NSArray arrayWithObject:[NSString stringWithFormat:#"%d",self.indexValue]];
[asset loadValuesAsynchronouslyForKeys:keys completionHandler:
^{
dispatch_async( dispatch_get_main_queue(),
^{
/* IMPORTANT: Must dispatch to main queue in order to operate on the AVPlayer and AVPlayerItem. */
if (![[keys objectAtIndex:0] isEqual:[NSString stringWithFormat:#"%d",self.indexValue]])
{
}
// NSLog(#"&______________________________________________NOreturned");
playerItem = [AVPlayerItem playerItemWithAsset:asset];
if (!player)
{
player=nil;
}
player=[AVPlayer playerWithPlayerItem:playerItem];
[player play];//: Playground - noun: a place where people can play
});
}];
}

There is a redundant reinitialization for the player object.
Do not forget to subscribe for notifications, for instance :
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:nil];
As it was said in comments, download via browser the file using its URL and try to play by regular player.
Or create asset, and asks about it's tracks, duration, playable state.
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:_videoURL options:nil];
NSArray *requestedKeys = #[#"tracks",#"playable",#"duration"];
__weak typeof(self) wSelf = self;
[asset loadValuesAsynchronouslyForKeys:requestedKeys completionHandler:
^{
for (NSString *thisKey in requestedKeys) {
NSError *error = nil;
AVKeyValueStatus keyStatus = [asset statusOfValueForKey:thisKey error:&error];
if (keyStatus == AVKeyValueStatusFailed) {
//Failed here
//return
}
}
if (!asset.isPlayable) {
//not playable....
}
}

I advice you use AVAudioPlayer for playing mp3. And I show you my two swift examples.
The first example when AVAudioPlayer plays mp3 file from NSData
var audioPlayer = AVAudioPlayer()
func playFromCore() {
let object = fetchedResultsController.fetchedObjects!.first as! Song
let file = object.file // This is NSData
AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, error: nil)
AVAudioSession.sharedInstance().setActive(true, error: nil)
audioPlayer = AVAudioPlayer(data: file, fileTypeHint: AVFileTypeMPEGLayer3, error: nil)
audioPlayer.prepareToPlay()
audioPlayer.play()
}
The second example when AVAudioPlayer plays NSURL
func playMusic() {
let url = NSBundle.mainBundle().URLForResource("89", withExtension: "mp3")!
AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, error: nil)
AVAudioSession.sharedInstance().setActive(true, error: nil)
audioPlayer = AVAudioPlayer(contentsOfURL: url, error: nil)
audioPlayer.prepareToPlay()
audioPlayer.play()
}

Related

AVPlayer cannot play mp3-format file loaded by AVURLAsset with option AVURLAssetPreferPreciseDurationAndTimingKey set to true on iOS 14

While testing my app on iOS 14, I found some mp3-format files not working properly. There's no error from start to end, but sounds only heard in the very beginning and muted in the following seconds.
Here are sample codes to reproduce.
NSURL *url = [NSURL URLWithString:urlString];
BOOL preferPreciseDuration = YES;
NSDictionary *options = #{AVURLAssetPreferPreciseDurationAndTimingKey: #(preferPreciseDuration)};
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:options];
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:asset];
[playerItem addObserver:self forKeyPath:#"status" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil];
player = [AVPlayer playerWithPlayerItem:playerItem];
[player play];
I have looked into it for some time and found:
This problem only occurs on iOS 14+.
If I omit AVURLAssetPreferPreciseDurationAndTimingKey option, everything works fine.
It seems to be server-related, I have tested two URLs hosting exact same file, but sample1 works while sample1 doesn't.
On the same server, file1 works while file2 doesn't.
I have recently implement audio features in my application it's working in iOS 14+ too.
var player: AVAudioPlayer?
let url = Bundle.main.url(forResource: "zapsplat_emergency_nuclear_power_station_meltdown_alarm_42849", withExtension: "mp3")!
do {
player = try AVAudioPlayer(contentsOf: url)
guard let player = player else { return }
player.prepareToPlay()
player.play()
} catch let error as NSError {
print(error.description)
}

audio is not playing on button click

I want to play a audio on Button click. I have tried this code but audio is not playing:-
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:#"sound" ofType:#"mp3"];
NSURL *soundFileURL = [NSURL fileURLWithPath:soundFilePath];
AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithData:soundFileURL error:nil];
audioPlayer.delegate = self;
audioPlayer.volume=1.0;
[audioPlayer setNumberOfLoops:1];
[audioPlayer play];
you haven't created a player item
//declare in .h file
AVPlayerItem *playerItem;
//then in .m file
player = [AVPlayer playerWithPlayerItem:playerItem];
after that play it
[player play];
First, it appears you are using initWithData to access an audio file pointed to by a URL.
You should instead use initWithContentsOfU‌RL.
Another issue could be due to a common error in implementing the audio player.
If the AVAudioPlayer you are creating is inside a function, it will be released, or deallocated, at the end of the function. This is why the audio does not play.
To fix this problem, make the AVAudioPlayer a property or instance variable in your class so that it does not get immediately deallocated.
For example, a property is declared in your header as
#property(strong, nonatomic) AVAudioPlayer *audioPlayer;
and initialized in your implementation with
self.audioPlayer = [[AVAudioPlayer alloc] initWithData:soundFileURL error:nil];
If this does not solve your problem, then the issue is likely to do with the file pointed to by your URL.
NSString *soundFilePath = [NSString stringWithFormat:#"%#/test.mp3",
[[NSBundle mainBundle] resourcePath]];
NSURL *soundFileURL = [NSURL fileURLWithPath:soundFilePath];
AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFileURL error:nil];
player.numberOfLoops = -1; //Infinite
[player play];
print "soundFileURL" . check whether it is null(in case soundFileURL contains spaces it results/prints null ) and if it not a null value check the file name is spelled exactly correct or not(i.e., here your file name is "sound")
var asyncaAudioPlayer: AVAudioPlayer!
let audioFilePath = NSBundle.mainBundle().pathForResource(fileName, ofType: "mp3")
let audioFileUrl = NSURL.fileURLWithPath(audioFilePath!)
asyncaAudioPlayer = AVAudioPlayer(contentsOfURL: audioFileUrl, fileTypeHint: nil)
audioPlayerArray.append(asyncaAudioPlayer)
asyncaAudioPlayer.prepareToPlay()
asyncaAudioPlayer.play()
Your problem is you set NSURL for initWithData -> Wrong.
in case it sound play multi time, you should prepare your sound Data and keep it as strong ref.
and you should use CGD and global queue instead of main queue.
-(void)onTouchUpInside{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithData:self.soundFileData error:nil];
audioPlayer.delegate = self;
audioPlayer.volume=1.0;
[audioPlayer setNumberOfLoops:1];
if ([audioPlayer prepareToPlay] && [audioPlayer play])
{
NSLog(#"play Success!");
}
else
{
NSLog(#"Failed to play the audio file.");
}
});
}

How to cache audio to local when playing audio with avplayer

I play an audio online with AVPlayer,and want to save the data/stream of audio to local when avplayer finish loading stream.
I implementt it as the following:
let fileUrl = NSURL(string: strUrl)!
let asset = AVURLAsset(URL: fileUrl)
asset.resourceLoader.setDelegate(self, queue:dispatch_queue_create("AVARLDelegateDemo loader", nil))
self.pendingRequests = [AVAssetResourceLoadingRequest]()
asset.loadValuesAsynchronouslyForKeys(["playable"]){
dispatch_async( dispatch_get_main_queue()){
self.prepareToPlayAsset(asset, requestedKeys: ["playable"])
}
}
func resourceLoader(resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool {
.......
return false
}
When url is http/https, it does not call resourceLoader(resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource.... -, when url is customize (eg.:'test'), it call resourceLoader(resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource...
Who know the reason, Does resourceLoader not support http/https?
When player doesn't know how to load a file, it calls resourceLoader(resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource... method. So since it knows how to load URL, it wont call this method, so you have to pass custom URL.
This sample project and tutorial seems to detail what you're looking for.
the link above details setting a custom URL scheme and setting an AVAssetResourceLoaderDelegate to handle the resource as it loads. This is initialised as:
NSURL *url = [NSURL URLWithString:#"customscheme://host/audio.mp3"];
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil];
[asset.resourceLoader setDelegate:self queue:dispatch_get_main_queue()];
AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:asset];
[self addObserversForPlayerItem:item];
self.player = [AVPlayer playerWithPlayerItem:playerItem];
[self addObserversForPlayer];
The implementation of the delegate methods would then look similar to the following:
- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest*)loadingRequest{
NSURL *resourceURL = [loadingRequest.request URL];
if([resourceURL.scheme isEqualToString:#"customscheme"]){
LSFilePlayerResourceLoader *loader = [self resourceLoaderForRequest:loadingRequest];
if(loader==nil){
loader = [[LSFilePlayerResourceLoader alloc] initWithResourceURL:resourceURL session:self.session];
loader.delegate = self;
[self.resourceLoaders setObject:loader forKey:[self keyForResourceLoaderWithURL:resourceURL]];
}
[loader addRequest:loadingRequest];
return YES;
}
return NO;
}
- (void)resourceLoader:(AVAssetResourceLoader *)resourceLoader didCancelLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest{
LSFilePlayerResourceLoader *loader = [self resourceLoaderForRequest:loadingRequest];
[loader removeRequest:loadingRequest];
}
Where LSFilePlayerResourceLoader and LSFilePlayerResourceLoader are custom objects for handling the data being received (detailed in the link).
Here you can find full Swift solution based on native AVPlayerItem with custom loader.

Play multiple audio at one same time

My client wants to create an app in which user can play 4 audio at once and then start start any audio and then record them as one file.
I have not worked on audio/video apps so i want to know that is that possible in ios sdk?
How many audio file can be played at same time?
You can play many musics at the same time using AVAudioSession.
UInt32 allowMixing = true;
AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers, sizeof(allowMixing), &allowMixing);
You can play multiple audio file with this code
NSString *songA = [[NSBundle mainBundle] pathForResource:#"songA" ofType:#"mp3"];
NSError *soundError = nil;
self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:songA] error:&soundError];
if(self.player == nil)
NSLog(#"%#",soundError);
else
{
[self.player setDelegate:self];
[self.player setVolume:0.75];
[self.player play];
}
NSString *songB = [[NSBundle mainBundle] pathForResource:#"songB" ofType:#"mp3"];
soundError = nil;
self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:songB] error:&soundError];
if(self.player == nil)
NSLog(#"%#",soundError);
else
{
[self.player setDelegate:self];
[self.player setVolume:0.25];
[self.player play];
}
To marge more than one audio file you can use ans from this question Combining two .caf files on iPhone

AVQueuePlayer playback without gap and freeze

I use AVQueuePlayer to play a sequence of movies which are loaded from URLs.
I tried to initialize player instance with array of all AVPlayerItems that I need to play.
player = [[AVQueuePlayer queuePlayerWithItems:playerItemsArray]
But in this case AVQueuePlayer loads some initial part of each AVPlayerItem before starting playback. It causes frustrating freeze and application doesn't respond for some seconds.
There is possibility to add only first AVPLayerItem to player's queue, observe its state and add second item in queue only when first will reach end, but in this case there will be a gap between playback of two items caused by initializing and buffering of second AVPlayerItem.
Is there any way to organize gapless playback of several videos without a freeze?
Should I use some other player for this purposes?
Thanks in advance.
The solution is found.
When adding new AVPlayerItem in queue of AVQueuePlayer player will synchronously wait till initial part of player item will be buffered.
So in this case player item should be buffered asynchronously and after that it can be added in the queue. It can be done using [AVURLAsset loadValuesAsynchronouslyForKeys: completionHandler:]
For example:
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:url options:nil];
NSArray *keys = [NSArray arrayWithObject:#"playable"];
[asset loadValuesAsynchronouslyForKeys:keys completionHandler:^()
{
dispatch_async(dispatch_get_main_queue(), ^
{
AVPlayerItem *playerItem = [[[AVPlayerItem alloc] initWithAsset:asset] autorelease];
[player insertItem:playerItem afterItem:nil];
});
}];
Using this solution queue of AVQueuePlayer can be populated with items without any gaps and freezes.
in Swift 2, working here:
func load() {
let player = AVQueuePlayer()
for url in urls {
makeItem(url)
}
}
func makeItem(url: String) {
let avAsset = AVURLAsset(URL: NSURL(string: url)!)
avAsset.loadValuesAsynchronouslyForKeys(["playable", "tracks", "duration"], completionHandler: {
dispatch_async(dispatch_get_main_queue(), {
self.enqueue(avAsset: avAsset)
})
})
}
func enqueue(avAsset: AVURLAsset) {
let item = AVPlayerItem(asset: avAsset)
self.player.insertItem(item, afterItem: nil)
}
Here is solution.
- (void)_makePlayer{
_player = [[AVQueuePlayer alloc] initWithPlayerItem:[AVPlayerItem playerItemWithAsset:[SSMoviePreviewItemMaker generateAVMovieItem]]];
}
+ (AVAsset *)generateAVMovieItem{
NSArray * array = [SSMovieFileManager getAllMovieResourceURL];
AVMutableComposition *composition = [[AVMutableComposition alloc] init];
for (int i = 0; i < array.count; i++) {
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:array[i] options:nil];
[composition insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration)
ofAsset:asset
atTime:composition.duration error:nil];
}
return composition;
}

Resources