iOS: What is the proper way to implement live audio streaming? - ios

I'm developing an iOS application for a news website, the website has a live audio news channel, i have tried using AVAudioPlayer and did the following:
.h file:
#interface ViewController : UIViewController <AVAudioPlayerDelegate>
#property (strong, nonatomic) AVAudioPlayer *audioPlayer;
.m file (viewDidLoad):
- (void)viewDidLoad {
[super viewDidLoad];
NSURL *url = [NSURL fileURLWithPath:#"http://198.178.123.23:8662/stream/1/;listen.mp3"];
NSError *error;
_audioPlayer = [[AVAudioPlayer alloc]
initWithContentsOfURL:url
error:&error];
if (error)
{
NSLog(#"Error in audioPlayer: %#",
[error localizedDescription]);
} else {
_audioPlayer.delegate = self;
[_audioPlayer prepareToPlay];
}
}
But the application is giving me the following error:
2015-05-25 15:03:12.353 AudioPlayer[2043:421434] Error in audioPlayer: The operation couldn’t be completed. (OSStatus error 2003334207.)
I don't know what's wrong with this code.
Can anyone tell me where's the error in the code ?
Many thanks.

I think there is a better way to play the audio link in the default MPMoviePlayer with audio url. Rest of the things will be managed by default player.
EDIT :
NSURL *audioPath = [NSURL URLWithString:#"Your Audio URL"];
MPMoviePlayerViewController *mpViewController = [[MPMoviePlayerViewController alloc] initWithContentURL:audioPath];
// Remove the movie player view controller from the "playback did finish" notification observers
[[NSNotificationCenter defaultCenter] removeObserver:mpViewController
name:MPMoviePlayerPlaybackDidFinishNotification
object:mpViewController.moviePlayer];
// Register this class as an observer instead
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(movieFinishedCallback:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:mpViewController.moviePlayer];
// Set the modal transition style of your choice
mpViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
// Present the movie player view controller
[self presentViewController:mpViewController animated:YES completion:nil];
// Start playback
[mpViewController.moviePlayer prepareToPlay];
[mpViewController.moviePlayer play];

You're getting an error because AVAudioPlayer doesn't support HTTP (meaning that you can't play media from the internet), so what you can do is use AVPlayer it's pretty much similar, here's an example :
- (void)viewDidLoad {
[super viewDidLoad];
NSString *urlAddress = #"http://198.178.123.23:8662/stream/1/;listen.mp3";
urlStream = [NSURL URLWithString:urlAddress];
self.audioPlayer = [AVPlayer playerWithURL:urlStream];
NSError *error;
if (error)
{
NSLog(#"Error in audioPlayer: %#",
[error localizedDescription]);
} else {
[_audioPlayer prepareForInterfaceBuilder];
}
}
Hope my answer helped you :)

Related

Stop AvAudioPlayer in other View iOS

I would like to stop the audio with AvAudioPlayer but when another view dissapear, There are two views:
The problem is that when view2 disappear, the audio doesn't stop... How I should do?
View1.h
#property (nonatomic, retain) AVAudioPlayer *audioPlayer;
- (IBAction)playAudio:(id)sender;
- (IBAction)stopAudio:(id)sender;
View1.m
- (void)viewDidLoad
{
[super viewDidLoad];
[self playSound];
}
- (void) playSound
{
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]
pathForResource:#"simpsonsTheme"
ofType:#"mp3"]];
NSError *error;
self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
//[self.audioPlayer play];
[self playAudio:audioPlayer];
}
- (IBAction)playAudio:(id)sender {
[audioPlayer play];
}
- (IBAction)stopAudio:(id)sender {
[audioPlayer stop];
}
And View2.m
-(void)viewWillDisappear:(BOOL)animated{
view1 = (View1 *)[[UIApplication sharedApplication] delegate];
[view1 stopAudio:view1.audioPlayer];
}
In the View2 I import the View1 to do that.
Thanks
Your app delegate is not a view controller. Maybe you want [[UIApplication sharedApplication] delegate].window.rootViewController.
As you've seen, that's fragile. Instead of having pointers between your viewcontrollers, you could use have looser coupling and use a "stop" NSNotification. It may even be useful elsewhere in your application.
Define
NSString *StopPlayingVideoNotification = #"StopPlayingVideo";
So both views can see it, so put extern NSString *StopPlayingVideoNotification; in one of your header files.
In View1.m's init:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(stopAudio:) name:StopPlayingVideoNotification object:nil];
Add a dealloc to View1:
-(void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:StopPlayingVideoNotification object:nil];
}
In View2.m
-(void)viewWillDisappear:(BOOL)animated{
[[NSNotificationCenter defaultCenter] postNotificationName:StopPlayingVideoNotification object:self];
}

playPrerollAdWithCompletionHandler issue

I configure a MPMoviewPlayerController to display iAd before I play a video. So basicaly my code looks like this
player = [MPMoviePlayerController new];
player.contentURL = videoURL;
[player playPrerollAdWithCompletionHandler:^(NSError *error) {
NSLog(#"error playing ad %#", error.userInfo);
[player play];
}];
[self.contentView addSubview:player.view];
[self.contentView layoutIfNeeded];
And in AppDelegate
[MPMoviePlayerController preparePrerollAds];
But the problem is that i receive the sound, but player does not display a video. Also I don't get any error in completion handler… Any help is very appreciated!
For me it only works when the player is set to fullscreen.
[parent.view addSubview: player.view];
[parent.view bringSubviewToFront:player.view];
[player setFullscreen:YES animated:YES];
I was having the same problem (I could hear the audio, but no video played), and I got a response on the Apple Developer Forums.
It appears you have to make sure there's only one reference to the movie player controller. This is the code I used, which now works:
#interface UIViewController ()
#property (strong, nonatomic) MPMoviePlayerController *moviePlayer;
#end
MPMoviePlayerController *moviePlayer = [[MPMoviePlayerController alloc] init];
moviePlayer.contentURL = movieURL;
if (moviePlayer)
{
self.moviePlayer = moviePlayer;
moviePlayer = nil;
[self.moviePlayer prepareToPlay];
[self.view addSubview:self.moviePlayer.view];
}
Then later, I use:
- (void)playVideo
{
[self.moviePlayer playPrerollAdWithCompletionHandler:^(NSError *error) {
if (!error)
{
[self.moviePlayer play];
}
}];
}
I hope that helps.

Can't play video file using MPMoviePlayerController

The video file is http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4
I can't play it after copying it into the bundle of an app, then running this:
What's wrong?
-(void)showvideo {
NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:#selector(mPMoviePlayerLoadStateDidChangeNotification) name:MPMoviePlayerLoadStateDidChangeNotification object:nil];
[center addObserver:self selector:#selector(readytoplay) name:MPMoviePlayerReadyForDisplayDidChangeNotification object:nil];
NSBundle* b = NSBundle.mainBundle;
NSString* res = [b resourcePath];
NSString* file;
// file = #"sr.m4v";
// file = #"sample_iPod.m4v";
file = #"big_buck_bunny.mp4";
// file = #"xxx.mp4";
NSString* path = [res stringByAppendingPathComponent:file];
NSFileManager* manager = [NSFileManager defaultManager];
if ([manager fileExistsAtPath:path]) {
NSLog(#"file exists!");
}
else {
NSLog(#"file does not exist!");
}
NSURL* url = [NSURL URLWithString:path];
NSLog(#"url: %#", url);
self.movieplayer = [[MPMoviePlayerController alloc] initWithContentURL:url];
// self.movieplayer = [MPMoviePlayerController new];
self.movieplayer.movieSourceType = MPMovieSourceTypeFile;
NSLog(#"self.movieplayer: %#, loadState: %d", self.movieplayer, self.movieplayer.loadState);
[self.movieplayer prepareToPlay];
NSLog(#"self.movieplayer: %#, loadState: %d", self.movieplayer, self.movieplayer.loadState);
}
-(void)readytoplay {
NSLog(#"readytoplay");
[self.movieplayer.view setFrame:self.view.bounds]; // player's frame must match parent's
[self.view addSubview:self.movieplayer.view];
NSLog(#"self.movieplayer: %#", self.movieplayer);
self.movieplayer.fullscreen = YES;
NSLog(#"self.movieplayer: %#", self.movieplayer);
[self.movieplayer play];
}
-(void)mPMoviePlayerLoadStateDidChangeNotification {
NSLog(#"mPMoviePlayerLoadStateDidChangeNotification %#, loadState: %d", self.movieplayer, self.movieplayer.loadState);
}
Movieplayer property:
#property (nonatomic, strong) MPMoviePlayerController* movieplayer;
First of all, you can't modify (append, add, delete) application's bundle on runtime. Secondly, NSURL for local files has to be initialised with fileURLWithPath method. Try to correct this issues, and if it doesn't play, try to observe MPMoviePlayerController's loadState changes, and when its loadState is ready to play, then try to play the file. Hope this helps.
EDIT
Sorry, my bad, you are not changing app's bundle on the runtime, just go for Second part. And, you can get the url for the file by calling just [[NSBundle mainBundle] URLForResource:#"file_name" withExtension:#"extension"];
your URL is wrong. Thats y it didnt work out. Use [NSURL fileURLWithPath:path] for url. It should work.

AVAudio player singleton crashes on a isPlaying check

I am using a singleton for AVAudioPlayer, it works great.
I wish to add a check before initializing the player and stop the player if it already playing something.
I added this code :
SoundPlayer *player = [SoundPlayer sharedInstance];
if([player isPlaying]){
[player stop];
}
But it gives me a EXE_BAD_ACCESS on the if([player isPlaying]) line.
2 Questions:
If this is a singleton and I am getting back the same player then why doesn't it stopes by itself?
Why am I getting the error?
HERE IS THE FULL CODE
#import "SoundPlayer.h"
#implementation SoundPlayer
#synthesize helpMode;
static SoundPlayer *sharedInstance = nil;
+ (SoundPlayer *)sharedInstance {
if (sharedInstance == nil) {
sharedInstance = [[super allocWithZone:NULL] init];
}
return sharedInstance;
}
+(BOOL)playSound:(NSURL*)url{
NSError *error;
SoundPlayer *player = [SoundPlayer sharedInstance];
if([player isPlaying]){
[player stop];
}
player = [[SoundPlayer sharedInstance] initWithContentsOfURL:url error:&error];
[player play];
if (player == nil){
NSLog(#"error %# \n for file %#",[error description],[url path]);
return NO;
}else {
if (![player helpMode]) {
[player play];
}else{
NSLog(#"help mode");
}
}
return YES;
}
-(void)stopSound{
if ([self isPlaying]) {
[self stop];
}
}
#end
Im not sure if this is whats causing the error, but figured I would post my response to the discussion here to make my point clearer since Ill have more room and code formatting.
No you dont have to override all the methods, i was just asking to make sure I understood everything right.
The other piece of what im saying is that just like in stopSound{}, you should be using self not
SoundPlayer *player = [SoundPlayer sharedInstance];
so change your code to this, run it and see if its fixed, post a comment below to let me know the outcome.
-(BOOL)playSound:(NSURL*)url{
NSError *error;
// SoundPlayer *player = [SoundPlayer sharedInstance];
if([self isPlaying]){
[self stop];
}
//not sure if this should be self or super (i think super, try both)
//if you overrided the init then definitely self
//[self initWithContentsOfURL:url error:&error];
[super initWithContentsOfURL:url error:&error];
[self play];
if (self == nil){
NSLog(#"error %# \n for file %#",[error description],[url path]);
return NO;
}else {
if (![self helpMode]) {
[self play];
}else{
NSLog(#"help mode");
}
}
return YES;
}
Really all your doing is creating a pointer to self with you create a player object since it is a singleton. With that said Im not sure why that would make it crash other then the fact that you "should" be using self instead.
+ (SoundPlayer *)sharedInstance {
if (sharedInstance == nil) {
sharedInstance = [[super allocWithZone:NULL] init];
}
return sharedInstance;
}
see here that AVAudioPlayer only has 2 init methods and neither are just init hence you are not completely initializing your super class. You need to override initWithContentsOfURL:url method and initialize this object and its super with the url parameter. Just make sure you put the initWithContentsOfURL:url in both the .h and .m. Also idk if it matters but try just alloc not allocWithZone.

AVAudioPlayer delegate wont get called in a class method?

i am using the AVAudioPlayer and setting its delegate but its delegate is not getting called
+ (void) playflip
{
NSString *path;
path = [[NSBundle mainBundle] pathForResource:#"flip" ofType:#"mp3"];
AVAudioPlayer *flip;
flip = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:Nil];
flip.delegate = self;
[flip play];
}
My class where i am implementing is the sound class
#interface SoundClass : NSObject <AVAudioPlayerDelegate>
I am calling this delegate
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
NSLog(#"delegate called");
[player release];
player = nil;
}
It looks like maybe your flip object is going out of scope, because the rest of your code looks fine. Here's what I do:
// controller.h
#interface SoundClass : NSObject <AVAudioPlayerDelegate> {}
// #property(nonatomic,retain) NSMutableDictionary *sounds;
// I have lots of sounds, pre-loaded in a dictionary so that I can reference by name
// With one player, you can just use:
#property(nonatomic,retain) AVAudioPlayer *player;
Then allocate and load the sound in your .m
player = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:Nil];
[player prepareToPlay];
player.delegate = self;
[player play];
Now you should get your DidFinishPlaying notification.

Resources