I am using MPMoviePlayerController to play a live streaming m3u8 video for older devices (3.1.2). This worked fine until this morning. I tried changing the scalingMode to resolve another issue, and now the player does not work at all. I went back to older backups that worked, and they don't work, either.
While debugging, control goes into [mMPPlayer play] and never returns. This also locks up my app.
Has something changed with MPMoviePlayerController, or did I break something in XCode?
My app was scheduled to start moving to production today, so I'm really in a bind, here. :(
Here's the warning that I get:
Warning: MPMoviePlayerController may not support file of type m3u8
And here's my code:
MyViewController.h:
#import <UIKit/UIKit.h>
#import <MediaPlayer/MediaPlayer.h>
#interface WatchNowViewController : UIViewController {
MPMoviePlayerController *mMPPlayer;
}
#property (nonatomic, retain) MPMoviePlayerController *mMPPlayer;
#end
MyViewController.m:
mMPPlayer = [[MPMoviePlayerController alloc] initWithContentURL:[NSURL URLWithString:#"http://www.mysite.com/myVideo.m3u8"]];
mMPPlayer.scalingMode=MPMovieScalingModeFill;
mMPPlayer.backgroundColor=[UIColor blackColor];
[mMPPlayer play];
NSLog("Control never returns to here");
Happened to us as well. Not sure what went wrong. The encrypted streams just stopped playing in 3.2. Try the m3u8 url in the iPad safari and check if it plays there. If it doesn't play in iPad safari as well, try an unencrypted stream. As per my experience, an unencrypted stream played in 3.2 without issues.
Related
Has anyone else noticed that playableDuration property of MPMoviePlayerController class always returns 0 in iOS 5. This used to work fine in previous versions of iOS. I use it to set the value of a progress bar.
Here is piece of code that used to work under 4.x SDK just fine (i.e., the playableDuration attribute returned the correct non-zero value while buffering the stream), but under SDK 5.x it always returns zero.
- (void) updateMeter {
NSLog(#"playableDuration = %f", streamPlayer.playableDuration);
}
- (void)viewDidLoad
{
[super viewDidLoad];
streamPlayer = [[MPMoviePlayerController alloc]
initWithContentURL:[NSURL URLWithString:#"http://99.198.118.250:8158/"]];
NSTimer *updateBarTimer = [NSTimer scheduledTimerWithTimeInterval:0.5
target:self selector:#selector(updateMeter)
userInfo:nil repeats:YES];
streamPlayer.controlStyle = MPMovieControlStyleEmbedded;
[streamPlayer play];
}
Use your exact code but replace the url with: http://devimages.apple.com/iphone/samples/bipbop/gear1/prog_index.m3u8
For me your code failed unless I pointed the player to a segmented .m3u8 file.
I did some tests with a .mp4 movie and an .mp3 audio file locally on my computer and both worked fine as well.
I'm speculating here but I believe it's probable that while streaming media, the MPMoviePlayerController is using the .m3u8 file to deduce player item data on the fly? That's my guess anyway. What's curious is that if this is the case, why does it work at all for your url? Which leads me to my next comment...
You would probably have better results using AVFoundation rather than the MediaPlayer framework. I switched to it in my own work as well. It's less "prepackaged" but simply provides much more control.
I made a little test app to try to isolate this issue, but it exhibits the same behavior: the applicationMusicPlayer stops playing immediately when the app enters the background. I don't know what I'm doing wrong, or if it's an Apple bug. It seems so simple that if it were an Apple bug others would have encountered it and posted on it.
I've set the info.plist UIBackgroundModes key to Audio
I've verified that the app is not terminating
I've tested on 4.1 beta 3 with the same results
I've searched the web for similar complaints. People report other MPMediaPlayerController issues/bugs, but more complex e.g. involving interaction with AVAudio.
Any/all suggestions appreciated.
Here's the core of my test app:
MPTest.h
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <MediaPlayer/MediaPlayer.h>
#interface MPTestViewController : UIViewController <MPMediaPickerControllerDelegate> {
MPMusicPlayerController *MPPlayer;
}
#property (nonatomic, retain) MPMusicPlayerController *MPPlayer;
#end
MPTest.m
#import "MPTestViewController.h"
#implementation MPTestViewController
#synthesize MPPlayer;
- (void)viewDidLoad {
[super viewDidLoad];
// get the application music player
self.MPPlayer = [MPMusicPlayerController applicationMusicPlayer];
// break to allow application didFinishLaunching to complete
[self performSelector:#selector(presentMPPicker) withObject:nil afterDelay:0.01];
}
- (void)presentMPPicker {
// present the picker in a modal view controller
MPMediaPickerController *picker = [[MPMediaPickerController alloc] initWithMediaTypes:MPMediaTypeAnyAudio];
picker.delegate = self;
[self presentModalViewController:picker animated:YES];
[picker release];
}
// delegate called after user picks a media item
- (void)mediaPicker:(MPMediaPickerController *)mediaPicker didPickMediaItems:(MPMediaItemCollection *)mediaItemCollection {
[self dismissModalViewControllerAnimated:YES];
// tell the player what to play
[MPPlayer setQueueWithItemCollection:mediaItemCollection];
// start playing
[MPPlayer play];
}
#end
MPMusicPlayerController does not support background audio. You will need to use something like AVAudioPlayer or AVPlayer (AVPlayer allows you to use iPod libary items via the AssetURL).
The reason is that MPMusicPlayerController uses the iPod application to play audio, thus your application is not actually playing anything.
Please see this thread on the Apple Developer Forums for more information: https://devforums.apple.com/thread/47905?start=0&tstart=120
Did you set the appropriate audio session type for being a media player? The OS uses session types to make decisions among competing uses for the audio channels.
I have a soundboard
it's just a screen with about 8 buttons.
each individual button will have its own sound which will be played upon button press
There are a couple of ways I could play the sound, such as using SystemSound or AVAudioPlayer
system sound so far seems have the quickest response times, avaudioplayer is quite slow, it cant keep up if the user taps on the buttons really fast, I created an audio player for each sound which was quite messy.
this is how I'm playing the sounds at the moment
the .h file
#interface MainScreenViewController : UIViewController <AVAudioPlayerDelegate, UITabBarControllerDelegate> {
AVAudioPlayer *player;
CFURLRef keNURL;
SystemSoundID keNObject;
//KE LOUD
CFURLRef keLURL;
SystemSoundID keLObject;
//GE NORMAL
CFURLRef geNURL;
SystemSoundID geNObject;
//GE LOUD
CFURLRef geLURL;
SystemSoundID geLObject;
//NA NORMAL
CFURLRef naNURL;
SystemSoundID naNObject;
//NA LOUD
CFURLRef naLURL;
SystemSoundID naLObject;
//RA
CFURLRef raURL;
SystemSoundID raObject;
//DAGGA CLICK
CFURLRef daCURL;
SystemSoundID daCObject;
//TILLI CLICK
CFURLRef tiCURL;
SystemSoundID tiCObject;
}
#property (nonatomic, retain) AVAudioPlayer *player;
#property (readwrite) CFURLRef keNURL;
#property (readonly) SystemSoundID keNObject;
#property (readwrite) CFURLRef keLURL;
#property (readonly) SystemSoundID keLObject;
#property (readwrite) CFURLRef geNURL;
#property (readonly) SystemSoundID geNObject;
#property (readwrite) CFURLRef geLURL;
#property (readonly) SystemSoundID geLObject;
#property (readwrite) CFURLRef naNURL;
#property (readonly) SystemSoundID naNObject;
#property (readwrite) CFURLRef naLURL;
#property (readonly) SystemSoundID naLObject;
#property (readwrite) CFURLRef raURL;
#property (readonly) SystemSoundID raObject;
#property (readwrite) CFURLRef daCURL;
#property (readonly) SystemSoundID daCObject;
#property (readwrite) CFURLRef tiCURL;
#property (readonly) SystemSoundID tiCObject;
}
then the actions that play the individual sounds
then the .m file, after importing the .h file and the right frame works and synthesizing all the variables, write the code for the action
and this is what's in the individual actions.
-(IBAction)geSound{
AudioServicesPlaySystemSound (self.geNObject);
}
I just wanted to know if systemsound is the way forward for me to create a soundboard.
thanks especially when the user will tap on the board really fast alternating between beats.
.
if not what is the best way to play the sound which responds really well?
Speaking from experience, AVAudioPlayer works quite well at playing multiple sounds at the same time or very quickly one after the other. The best way to use it is to just create one method that you feed in an NSString to play some sound with the name held in that NSString... in that way, you will create a new player for each sound file that you play. Be careful about releasing the allocated players though unless you know you are finished with them.
Unless you have very large sound files which might take a short second to buffer (you'll have to figure out for yourself if you can live with any latency or not), I've never had any issues with it being slow. If it's slow, you're probably doing something against Apple's recommendations in regards to decoding certain files (i.e. for multiple sounds at once, Apple recommends the CAF format since it is hardware decoded versus software decoded): http://developer.apple.com/iphone/library/documentation/AVFoundation/Reference/AVAudioPlayerClassReference/Reference/Reference.html
Update
Here is how I encode my files with a shell script. Put this in a file called batchit.sh in it's own directory. Then place whatever .WAV files you want to encode as a .CAF file in that directory. Open up the Terminal on your Mac, cd to that directory and type sh batchit.sh and let it do it's thing. It will batch convert all the files... Here's the code:
for f in *; do
if [ "$f" != "batchit.sh" ]
then
/usr/bin/afconvert -f caff -d ima4 $f
echo "$f converted"
fi
done
Notice I didn't do anything in the audioPlayerDidFinishPlaying method which is a delegate method of AVAudioPlayer. You will need to add code to properly release the players after each is finished playing (hey, I can't do all the work for you :) so that all the memory is allocated correctly. Otherwise you will eventually run out if you indefinitely keep creating players without releasing them. Since it seems like you're having a rough day.. see below for a big hint.
That's all the help I can give though. You gotta learn & earn the rest
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
// do something here if you want.. you know like maybe release the old players by uncommenting the line below :)
//[player release];
}
22/03/2014 - Update: Got rid of my overly excited post (which was written a long time ago) and updated to portray a more appropriate answer.
I had to achieve fine control over audio so opted to use OpenAL.
WOW is one way to sum it up. As soon as I took it upon myself to use OpenAL which required a bit of extra leg work, getting all the required methods implemented and setting everything up; I found myself with practically no noticeable latency at all. I was absolutely pleased with the excellent results.
I was able to achieve both fine control and no latency at all. I remember jumping in joy at the time when I had it working.
Here are the resources I used that helped ease the endeavor of implementing OpenAL for the first time:
OpenAL on the iPhone
It was because of this link this video tutorial that I was able to create an excellent sound manager singleton. This allowed me to play all the sounds that I needed from any class that I wanted with little sound management, the singleton took care of everything for me.
I humbly suggest everyone to use OpenAL if you require fine control over your audio, and more importantly if you require a low-latency on-demand audio, specially for games when you need to make sure sound is played there and then when you require and expect it to, an audio based application for example.
Just going to add for the benefit of others, even though this is 2 years after the issue.
I've found that if they are short sounds, you would be ok (ish) on your application load. buffering all your tracks by running an instance of prepare to play for every sound (in a for loop) (then releasing - just enough for your processor to remember where the file is)
This takes away the initial loading time next time you come round to it and lets the sounds start more promptly
you could create AVAudioPlayer for each sound and then just pause/play it.
Have you tried playing back each sound in its own thread? You could try a basic NSOperation
I have been fighting some code for about a week, and am hoping that someone else may have experienced this problem and can point me in the right direction.
I am using the MPMoviePlayerViewController to play a video on the iPad. The primary problem is that it works FLAWLESSLY on the iPad Simulator, but will not play at all on the iPad. I have tried re-encoding the video to make sure that isn't an issue. The video I'm using is currently a 480x360 video encoded with H.264 Basline 3.0 with AAC/LC audio. The video plays fine on the iPhone, and also does play through Safari on the iPad. The video actually loads, and you can scrub through the video with the scrubber bar and see that it is there. The frames actually display, but just will not play. If you click play, it just immediately stops. Even when I have mp.moviePlayer.shouldAutoplay=YES set, you can see the player attempt to play, but only for a split second (maybe 1 frame?).
I have tried just adding view with the following code:
in .h
------
MPMoviePlayerViewController *vidViewController;
#property (readwrite, retain) MPMoviePlayerViewController *vidViewController;
in .m
------
MPMoviePlayerViewController *mp=[[MPMoviePlayerViewController alloc] initWithContentURL:[NSURL URLWithString:videoURL]];
[mp shouldAutorotateToInterfaceOrientation:YES];
mp.moviePlayer.scalingMode=MPMovieScalingModeAspectFit;
mp.moviePlayer.shouldAutoplay=YES;
mp.moviePlayer.controlStyle=MPMovieControlStyleFullscreen;
[videoURL release];
self.vidViewController = mp;
[mp release];
[self.view addSubview:vidViewController.view];
float w = self.view.frame.size.width;
float h = w * 0.75;
self.vidViewController.view.frame = CGRectMake(0, 0, w, h);
I have also just tried to do a:
[self presentMoviePlayerViewControllerAnimated:self.vidViewController];
which I actually can not get to orient properly...always shows up in Portrait and almost completely off the screen on the bottom, and the app is only intended to run in either of the Landscape views...
If anybody needs more info, just let me know. I'm about at my wits end on this. ANY help will be GREATLY appreciated.
I see 2 problems here.
First you are not setting the frame of the view. Try adding
mp.view.frame = self.view.frame;
This is what fixed it for me.
Next the shouldAutorotateToInterfaceOrientation does not need to be called. This method simply tells you if the view supports an orientation. Check the Apple docs.
You may want to try shutting down the iPad (powering off), remove and re-install the app.
Just add a line after you add the movie player to the view:
[self.vidViewController.moviePlayer play];
See if it helps.
Thanks,
Madhup
I have built an app that plays lots of sounds the easy way:
AudioServicesPlaySystemSound(someSoundID);
When I use the device volume buttons to increase or decrease the volume, the volume I actually change is the phone's ringer volume. So if you decrease it and exit the app then your phone will ring quietly...
Is there an easy way to override this and actually change my application's volume?
I have found the solution to what I thought would be a common problem. So here is how your app can have its own volume, and not mess with the user's ringer volume, even if you are only playing sounds as System Sounds.
You have to import the AVFoundation framework and in an object that stays loaded the whole time your app runs (or view, or the app delegate) you have initialize a AVAudioPlayer, give it a file to play and set it to "prepareToPlay" it...
This is what i did in my main View (that is used to load other views as subviews):
in the header file:
#import <AVFoundation/AVFoundation.h>
#interface MainViewController : UIViewController {
AVAudioPlayer *volumeOverridePlayer;
}
#property (nonatomic, retain) AVAudioPlayer *volumeOverridePlayer;
In the implementation file:
#synthesize volumeOverridePlayer;
- (void)viewDidLoad
{
[super viewDidLoad];
volumeOverridePlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL URLWithString:[[NSBundle mainBundle] pathForResource:#"something" ofType:#"caf"]] error:nil];
[volumeOverridePlayer prepareToPlay];
//...
}
Just leave the player prepared to play your file and enjoy your own volume control without having to play the sounds through it!
And of course, remember to release it in dealloc.
From what I can see in Apple's documentation, there are two issues here. 1. Application and the OS/hardware volume can only be controlled by the hardware/user. 2. You are playing a system sound. I believe the system sound is governed by the OS, so you're out of luck here, unless you are playing actual sound files. If you can use sound files, then take a look here.
Good luck!