play audio from internet using AVAudioPlayer - ios

I am implementing AVAudioPlayer to play audio and it works perfectly well while playing files locally stored in the PC.
But when i give the url of some audio file over the internet, it fails sadly.
Here's what the code looks like:
NSString *url = [[NSString alloc] init];
url = #"http://files.website.net/audio/files/audioFile.mp3";
NSURL *fileURL = [[NSURL alloc] initWithString: url];
AVAudioPlayer *newPlayer =[[AVAudioPlayer alloc] initWithContentsOfURL: fileURL error: nil];
Could anybody please point out the problem and what could be done?
Thanks!

Use AVPlayer to stream audio/video based on http url's. It will work fine. AVAudioPlayer is for local files. Here's the code
NSURL *url = [NSURL URLWithString:url];
self.avAsset = [AVURLAsset URLAssetWithURL:url options:nil];
self.playerItem = [AVPlayerItem playerItemWithAsset:avAsset];
self.audioPlayer = [AVPlayer playerWithPlayerItem:playerItem];
[self.audioPlayer play];

This is what the Apple docs say:
The AVAudioPlayer class does not provide support for streaming audio based on HTTP URL's. The URL used with initWithContentsOfURL: must be a File URL (file://). That is, a local path.

I tried other method initWithData on AVAudioPlayer instead of initWithContentsOfURL. First try to get MP3 file into NSData and then play this data.
Look at my code here.

Use AVPlayer and monitor its status to start playback.
Here is a workable example, hope it would be helpful.
#implementation AudioStream
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
if (context == PlayerStatusContext) {
AVPlayer *thePlayer = (AVPlayer *)object;
switch ([thePlayer status]) {
case AVPlayerStatusReadyToPlay:
NSLog(#"player status ready to play");
[thePlayer play];
break;
case AVPlayerStatusFailed:
NSLog(#"player status failed");
break;
default:
break;
}
return;
} else if (context == ItemStatusContext) {
AVPlayerItem *thePlayerItem = (AVPlayerItem *)object;
switch ([thePlayerItem status]) {
case AVPlayerItemStatusReadyToPlay:
NSLog(#"player item ready to play");
break;
case AVPlayerItemStatusFailed:
NSLog(#"player item failed");
break;
default:
break;
}
return;
}
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
- (void)playAudioStream
{
NSURL *audioUrl = [NSURL URLWithString:#"your_stream_url"];
AVURLAsset *audioAsset = [AVURLAsset assetWithURL:audioUrl];
AVPlayerItem *audioPlayerItem = [AVPlayerItem playerItemWithAsset:audioAsset];
[audioPlayerItem addObserver:self forKeyPath:#"status" options:0 context:ItemStatusContext];
self.player = [AVPlayer playerWithPlayerItem:audioPlayerItem];
[self.player addObserver:self forKeyPath:#"status" options:0 context:PlayerStatusContext];
}
#end

Related

iOS - AVPlayer not streaming audio for some URL

I have tried play audio from streaming. It is working fine with some URL but for some with some URL (http://listen.radionomy.com/almightyradiohindi) it fails even player status comes = AVPlayerStatusReadyToPlay. If I put same URL which is not working in browser then there it also does not work. But I think player should not show status as = AVPlayerStatusReadyToPlay. I am facing problem to manage it. For example how to I know for which streaming URL AVPlayer will work or not ?
My code :
-(void)viewDidLoad {
NSURL *url = [[NSURL alloc] initWithString:urlString];
AVAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil];
AVPlayerItem *anItem = [AVPlayerItem playerItemWithAsset:asset];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
avPlayer = [AVPlayer playerWithPlayerItem:anItem];
[avPlayer addObserver:self forKeyPath:#"status" options:0 context:nil];
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (object == avPlayer && [keyPath isEqualToString:#"status"]) {
if (avPlayer.status == AVPlayerStatusFailed) {
NSLog(#"\n\n **** AVPlayer Failed ***\n\n");
}
else if (avPlayer.status == AVPlayerStatusReadyToPlay) {
NSLog(#"\n\n **** AVPlayer Ready to Play ****\n\n");
[avPlayer play];
}
else if (avPlayer.status == AVPlayerItemStatusUnknown) {
NSLog(#"\n\n **** AVPlayer Unknown \n\n ****");
}
}
}
when I open your audio url(http://listen.radionomy.com/almightyradiohindi) in browser it will change to (http://streaming.radionomy.com/AlmightyRadioHindi?lang=en-US%2cen%3bq%3d0.8%2cfa%3bq%3d0.6)
if you can get the exact url and url-encode it the problem will be fixed
url encoding in swift 3.0:
var originalString = "test/test"
var escapedString = originalString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
print(escapedString!)

AVPlayerViewController not playing video and showing Quicktime logo

I am trying to play video with new AVPlayerViewController introduced in XCode 6.
To play video i have done this setup.
Using local mp4 file to play video
Extended AVPlayerViewController
Player setup code :
-(void)setupPlayer
{
NSString* filePath = [[NSBundle mainBundle] pathForResource:#"exodus_trailer" ofType:#"mp4"];
NSLog(#"File Path : %#",filePath);
AVAsset *avAsset = [AVAsset assetWithURL:[NSURL URLWithString:filePath]];
AVPlayerItem *avPlayerItem =[[AVPlayerItem alloc]initWithAsset:avAsset];
self.player = [[AVPlayer alloc]initWithPlayerItem:avPlayerItem];
[self.player addObserver:self forKeyPath:#"status" options:0 context:nil];
[self.player play];
}
KVO handling :
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (object == self.player && [keyPath isEqualToString:#"status"])
{
if (self.player.status == AVPlayerStatusFailed)
{
NSLog(#"AVPlayer Failed");
}
else if (self.player.status == AVPlayerStatusReadyToPlay)
{
NSLog(#"AVPlayerStatusReadyToPlay");
[self.player play];
}
else if (self.player.status == AVPlayerItemStatusUnknown)
{
NSLog(#"AVPlayer Unknown");
}
}
}
Issue :
KVO log is printing AVPlayerStatusReadyToPlay and file path seems ok. Previously view was at least showing player with all default controls, but now without any changes it started to show Quick time logo without any control. What is meaning of showing this logo ? what i am doing wrong here ?
Screenshot :
Here is exactly like what you wanted to do. The problem with your code is that you are not using file path, use this to load the file path NSURL *url = [NSURL fileURLWithPath:urlString]
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *urlString = [[NSBundle mainBundle] pathForResource:#"testfile" ofType:#"mp4"];
NSURL *url = [NSURL fileURLWithPath:urlString];
self.player = [[AVPlayer alloc] initWithURL:url];
[self.player addObserver:self forKeyPath:#"status"
options:NSKeyValueObservingOptionNew
context:NULL];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (object == self.player) {
AVPlayerStatus status = [change[NSKeyValueChangeNewKey] integerValue];
if (status == AVPlayerStatusReadyToPlay) {
[self.player play];
[self.player removeObserver:self forKeyPath:#"status"];
}
}
else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
#end

avplayer is not playing the URL

I am using avplayer for play audio url, but it is not working, I don't know where i am wrong
NSString *radioURL = #"https://www.example.com";
radioPlayer = [AVPlayer playerWithURL:[NSURL URLWithString:radioURL]] ;
// [radioPlayer seekToTime:kCMTimeZero];
NSLog(#"radio player %#",radioPlayer.currentItem);
[radioPlayer play];
Any help would be appreciated.
I strongly recommended the code below to play radio streaming: please take a look also AVPlayer_Class
-(void)Play{
NSString *radioURL = #"https://www.example.com"; //this url must valid
AVPlayer *player = [[AVPlayer alloc]initWithURL:[NSURL URLWithString:radioURL]];
self.songPlayer = player; //self.songPlayer is a globle object of avplayer
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[songPlayer currentItem]];
[self.songPlayer addObserver:self forKeyPath:#"status" options:0 context:nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (object == songPlayer && [keyPath isEqualToString:#"status"]) {
if (songPlayer.status == AVPlayerStatusFailed) {
NSLog(#"AVPlayer Failed");
} else if (songPlayer.status == AVPlayerStatusReadyToPlay) {
NSLog(#"AVPlayerStatusReadyToPlay");
[self.songPlayer play];
} else if (songPlayer.status == AVPlayerItemStatusUnknown) {
NSLog(#"AVPlayer Unknown");
}
}
}
- (void)playerItemDidReachEnd:(NSNotification *)notification {
// code here to play next sound file
}
Ref link is - Streaming mp3 audio with AVPlayer
I had the same issue and just realized that I wasn't retaining the player (using ARC)! So it gets deallocated and stop playing immediately after start.
You need to make sure that you have a strong property radioPlayer and use self.radioPlayer instead of radioPlayer.
You are doing good but your method need to correct on one place.Check using the below method:
NSURL *audioURL = [NSURL URLWithString:radioURL];
NSData *audioData = [NSData dataWithContentsOfURL:audioURL];
self.audioPlayer = [[AVAudioPlayer alloc] initWithData:audioData error:nil];
self.audioPlayer.numberOfLoops = -1;
self.audioPlayer.delegate = self;
[self.audioPlayer prepareToPlay];
self.audioPlayer.volume=1.0;
[self.audioPlayer autorelease];
[self.audioPlayer Play];
and add proper delegate methods for AVAudioPlayer.
Make Sure URL is AddingPercentEscapes.
NSURL *audioURL = // Your Url;
NSString *encodedString = [#"Your Url" stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *myURL = [[NSURL alloc] initWithString:encodedString]
AVPlayer *player = [AVPlayer playerWithURL:myURL];
[player prepareToPlay];
player play];
Did you forgot to set the attribute App Transport Security Settings -> Allow Arbitrary Loads (Yes)?

Playing videos in AVPlayer

My app currently plays 2 videos at the same time, functionally, but I all the SO answers i've seen utilizes lots of Key-Value code. Is it bad/wrong if I just do the bare minimum listed below?
viewdidload
AVURLAsset *asset = [AVURLAsset assetWithURL:[NSURL URLWithString:firstVideo.videoURL]];
AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:asset];
self.player1 = [AVPlayer playerWithPlayerItem:item];
[topPlayer setMovieToPlayer:self.player1];
AVURLAsset *asset2 = [AVURLAsset assetWithURL:[NSURL URLWithString:secondVideo.videoURL]];
AVPlayerItem *item2 = [AVPlayerItem playerItemWithAsset:asset2];
self.player2 = [AVPlayer playerWithPlayerItem:item2];
[bottomPlayer setMovieToPlayer:self.player2];
((AVPlayerLayer *)[self.topPlayer layer]).videoGravity = AVLayerVideoGravityResizeAspectFill;
((AVPlayerLayer *)[self.bottomPlayer layer]).videoGravity = AVLayerVideoGravityResizeAspectFill;
[self.player1 play];
[self.player2 play];
The above code is all I use to play a video, and it works fine. Occasionally there is a ~1s delay, how can I wait until both the videos are ready to play, then play them both?
use KVO to observer the player's status, and play videos when both's status is ready.
[yourPlayerItem addObserver:self forKeyPath:#"status" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil];
and in kvo:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:#"status"]) {
AVPlayerStatus status = [change[NSKeyValueChangeNewKey] integerValue];
switch (status) {
case AVPlayerStatusUnknown:
//do something
break;
case AVPlayerStatusReadyToPlay:
{
//check which avplayer is ready, by check the parametric object isEqual:yourPlayerItem
//use a bool value to record the ready status. after two bools are YES, then play the video
}
break;
case AVPlayerStatusFailed:
{
AVPlayerItem *playerItem = (AVPlayerItem *)object;
[self assetFailedToPrepareForPlayback:playerItem.error];
}
break;
}
}
}

Multiple audio files playing at once in iOS App

I have an iOS app which works like this:
First view
3 buttons that link to different views, each has its own audio file within
Imagine user opens one view, and starts to play the audio file. When they navigate around the app, the audio file still plays which is fine, Im happy with that.
However, if user then opens the second audio file and presses play, it plays on top of the first one so 2 audios are playing together.
How do I tell the code that if another play button is pressed it should stop the first audio file that is playing??
I am using AVFoundation framework to do this
Code is as follows:
NSString *stringPath = [[NSBundle mainBundle]pathForResource:#"01" ofType:#"MP3"];
NSURL *url = [NSURL fileURLWithPath:stringPath];
NSError *error;
avPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:url error:&error];
[avPlayer setNumberOfLoops:2];
[avPlayer setVolume:self.sliderVolumeOutlet.value];
[NSTimer scheduledTimerWithTimeInterval:.1 target:self selector:#selector(updateMyProgress) userInfo:nil repeats:YES];
-(void)updateMyProgress
{
float progress = [avPlayer currentTime]/[avPlayer duration];
self.myProgressView.progress = progress;
}
- (IBAction)sliderVolumeAction:(id)sender {
UISlider *mySlider = sender;
[avPlayer setVolume:mySlider.value];
}
- (IBAction)stopButton:(id)sender {
[avPlayer stop];
[avPlayer setCurrentTime:0];
}
- (IBAction)pauseButton:(id)sender {
[avPlayer pause];
}
- (IBAction)playButton:(id)sender {
[avPlayer play];
}
- (IBAction)backButtonEvent:(id)sender {
[self dismissViewControllerAnimated:YES completion:NULL];
}
You can check whether it is playing the previous track or not by if ([AVPlayer rate] != 0.0)
If it is playing then you can either add the next track into queue or just use this function to replace current track with the new one - (void)replaceCurrentItemWithPlayerItem:(AVPlayerItem *)item
Perhaps creating some sort of playSound function would be better suited for this. Something like this:
- (void)playSoundWithFileName:(NSString *)fileName andExtension:(NSString *)extension {
NSString *stringPath = [[NSBundle mainBundle]pathForResource:fileName ofType:extension];
NSURL *url = [NSURL fileURLWithPath:stringPath];
NSError *error;
if (avPlayer) {
[avPlayer stop];
avPlayer = nil;
}
avPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:url error:&error];
if (error)
NSLog(#"Error");
else {
[avPlayer setNumberOfLoops:2];
[avPlayer setVolume:self.sliderVolumeOutlet.value];
[avPlayer play];
}
}
Use:
[self playSoundWithFileName:#"01" andExtension:#"MP3"];
[NSTimer scheduledTimerWithTimeInterval:.1 target:self selector:#selector(updateMyProgress) userInfo:nil repeats:YES];
and call -(void)playSoundWithFileName whenever you want to play another sound.
Or if you were using the AVPlayer class:
- (void)playSoundWithFileName:(NSString *)fileName andExtension:(NSString *)extension {
NSString *stringPath = [[NSBundle mainBundle]pathForResource:fileName ofType:extension];
NSURL *url = [NSURL fileURLWithPath:stringPath];
AVPlayerItem *item = [AVPlayerItem playerItemWithURL:url];
if (avPlayer)
[avPlayer replaceCurrentItemWithPlayerItem:item];
else
avPlayer = [AVPlayer playerWithPlayerItem:item];
[avPlayer setVolume:self.sliderVolumeOutlet.value];
[avPlayer play];
}
Though the latter doesn't allow you set a number of loops.

Resources