Asynchronous MPMoviePlayerController load has no duration value - iphone

I assume that the initWithContentURL: method is not asynchronous. I added the following methods to do the load in the background and then assign the temporary player to my variable.
-(void)createPlayer {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
MPMoviePlayerController *temp = [[MPMoviePlayerController alloc] initWithContentURL:[NSURL URLWithString:#"URLString"]];
[self performSelectorOnMainThread:#selector(passPlayer:) withObject:temp waitUntilDone:YES];
[temp release];
[pool release];
}
-(void)passPlayer:(MPMoviePlayerController*)temp {
player = [temp retain];
player.movieSourceType = MPMovieSourceTypeStreaming;
//player.view.hidden = YES;
//[self.view addSubview:player.view];
[player prepareToPlay];
}
The problem is when I print out the duration of the player I get this:
double duration = player.duration;
NSLog(#"duration: %f, duration);
console output: duration: nan
I have added the MPMoviePlayerControllerNotifications to be notified when there is a duration value available, but even then the value is still 'nan'. If I do the load on the main thread the duration has a value, but I don't want to use the main thread so my UI doesn't lock up.
Any ideas?

First of all, don't create UI objects on a non-main thread. All UI operations must be done on the main thread, or odd things will occur. Such as, perhaps, the movie never loading and NAN being returned for the duration.
Second, initWithContentURL: probably is asynchronous or there wouldn't be notifications to let you know when the movie was actually loaded. Just create the controller as you would normally, subscribe to the appropriate notifications to know when the status is updated, and then check the status immediately just in case the movie happened to be a local file or already cached so the load was able to happen synchronously.

This happens because the movie player has not been able to calculate the duration. If you subscribe to the MPMovieDurationAvailableNotification notification, you'll be notified if and when a value has been determined.
MPMovieDurationAvailableNotification
This notification is posted when the
duration of a movie object is
determined. The object of the
notification is the
MPMoviePlayerController object itself.
There is no userInfo dictionary. The
duration value is reflected in the
duration property of the movie player
controller.
Source: Apple documentation

From the official doc :
Discussion
If the duration of the movie is not known, the value in this property is 0.0. If the duration is subsequently determined, this property is updated and a MPMovieDurationAvailableNotification notification is posted.
So use NSNotificationCenter to register the MPMovieDurationAvailableNotification notification.

Related

Variables falling out of scope

I have a subclass of AVAudioPlayer and within that subclass I have a method for stopping the current player and (for reasons I won't explain) manually calling audioPlayerDidFinishPlaying like this:
// Handles stopping the player and calling audioPlayerDidFinishPlaying
- (void) stopPlayerForTimedRepeat {
// Stop the player
[self stop];
// Manually call the audio player callback
EditPlayListViewController *playlistController = [[EditPlayListViewController alloc] init];
[playlistController audioPlayerDidFinishPlaying:self successfully:YES];
[playlistController release];
}
However, when I call audioPlayerDidFinishPlaying manually like this, all of my variables in the original EditPlaylistViewController fall out of scope.
How do I avoid this so that I still have access to all of my original variables?
I figured out a better way to do this without manually calling audioPlayerDidFinishPlaying so that all the variables will still be within scope.
// Handles stopping the player and calling audioPlayerDidFinishPlaying
- (void) stopPlayerForTimedRepeat {
// Fast forward the call to the end, which will also call audioPlayerDidFinishPlaying
[self setCurrentTime:[self duration]];
}
The variable scope loses because you are creating a new object EditPlayListViewController *playlistController = [[EditPlayListViewController alloc] init]; in the method after stopping it.
Please declare the player in your .h file (like this EditPlayListViewController *playlistController; and allocate it in your viewDidLoad method (like playlistController = [[EditPlayListViewController alloc] init];.
and change your method like this,
- (void) stopPlayerForTimedRepeat
{
// Stop the player
[self stop];
// Manually call the audio player callback
[playlistController audioPlayerDidFinishPlaying:self successfully:YES];
[playlistController release];
}
Hope,it'll work for you, if not please tell me.

How to perform operations when playing sound in iPhone?

I play a MP3 in my iPhone app using AVAudioPlayer; i need to perform some operations at certain times (say 30th seconds, 1 minute); is there a way to invoke callback functions based on mp3 playing time?
I believe the best solution is to start an NSTimer as you start the AVAudioPlayer playing. You could set the timer to fire every half second or so. Then each time your timer fires, look at the currentTime property on your audio player.
In order to do something at certain intervals, I'd suggest you kept an instance variable for the playback time from last time your timer callback was called. Then if you had passed the critical point between last callback and this, do your action.
So, in pseudocode, the timer callback:
Get the currentTime of your AVAudioPlayer
Check to see if currentTime is greater than criticalPoint
If yes, check to see if lastCurrentTime is less than criticalPoint
If yes to that too, do your action.
Set lastCurrentTime to currentTime
If you're able to use AVPlayer instead of AVAudioPlayer, you can set boundary or periodic time observers:
// File URL or URL of a media library item
AVPlayer *player = [[AVPlayer alloc] initWithURL:url];
CMTime time = CMTimeMakeWithSeconds(30.0, 600);
NSArray *times = [NSArray arrayWithObject:[NSValue valueWithCMTime:time]];
id playerObserver = [player addBoundaryTimeObserverForTimes:times queue:NULL usingBlock:^{
NSLog(#"Playback time is 30 seconds");
}];
[player play];
// remove the observer when you're done with the player:
[player removeTimeObserver:playerObserver];
AVPlayer documentation:
http://developer.apple.com/library/ios/#documentation/AVFoundation/Reference/AVPlayer_Class/Reference/Reference.html
I found this link describing a property property which seems to indicate you can get the current playback time.
If the sound is playing, currentTime is the offset of the current
playback position, measured in seconds from the start of the sound. If
the sound is not playing, currentTime is the offset of where playing
starts upon calling the play method, measured in seconds from the
start of the sound.
By setting this property you can seek to a specific point in a sound
file or implement audio fast-forward and rewind functions.
To check the time and perform your action you can simply query it:
if (avAudioPlayerObject.currentTime == 30.0) //You may need a more broad check. Double may not be able to exactly represent 30.0s
{
//Do Something
}
with multithreading your goal is simple, just do like this :
1 : in your main thread create a variable for storing time passed
2 : create new thread like "checkthread" that check each 30-20 sec(as you need)
3 : if the time passed is what you want do the callback
Yes Sure you can ...it's tricky i hope it works for you but it works for me ..
1- you play your mp3 file.
2- [self performSelector:#selector(Operation:) withObject:Object afterDelay:30];
then the function
-(void)Operation:(id)sender;
called; so you fired function after 30 second of mp3 file .. you can make many of function based on time you want..
3- there is other solution using timers
[NSTimer scheduledTimerWithTimeInterval:0 target:self selector:#selector(CheckTime:) userInfo:nil repeats:YES];
it will fire function called Check Time
-(void)CheckTime:(id)sender{
if (avAudioPlayerObject.currentTime == 30.0)
{
//Do Something
//Fire and function call such
[self performSelector:#selector(Operation:) withObject:Object]
}
}
then you can change time interval you want and repeats is for you to control repeat this action every 5 seconds or not..
Hope that helpful..
Thanks
i think ,you want to play different sound-files after 30sec then use this code :
1) all sound-files put in Array and then retrieve from document directory
2)then try this:
-(IBAction)play_sound
{
BackgroundPlayer=[[AVAudioPlayer alloc]initWithContentsOfURL:[NSURL fileURLWithPath:[[NSBundle mainBundle]pathForResource:[Arr_tone_selected objectAtIndex:j]ofType:#"mp3"]]error:NULL];
BackgroundPlayer.delegate=self;
[BackgroundPlayer play];
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
[BackgroundPlayer stop];
j++;
[self performSelector:#selector(play_sound) withObject:Object afterDelay:30];
}

Should I stop current AVPlayer instance when playing music from another URL?

I just created AVPlayer and it plays music well. I have two questions
How to play another music from another URL (should I stop current player?)
How to show current time of the song in UISlider (actually is it a method that called when the song is playing?)
Use -[AVPlayer replaceCurrentItemWithPlayerItem] to replace the current playing item reusing the player instance. You can create an item with an URL or with an asset.
In order to know when a given item finishes playing use the notification AVPlayerItemDidPlayToEndTimeNotification.
Use -[AVPlayer addPeriodicTimeObserverForInterval] to perform some action periodically while the player is playing. See this example:
[self.player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100)
queue:nil
usingBlock:^(CMTime time) {
<# your code will be called each 1/10th second #>
}];
1) If you used - (id)initWithURL:(NSURL *)URL then you should stop player with pause, dealloc it and create new instance.
AVPlayer *player = [AVPlayer alloc] initWithURL:[NSURL URLWithString:#"http:/someurl.com"]];
[player play];
[player pause];
[player release];
player = [AVPlayer alloc] initWithURL:[NSURL URLWithString:#"http:/someurl2.com"]];
[player pause];
[player release];
If you used playerWithURL, then just call the same line again.
2). The easiest is the get duration of the current item https://stackoverflow.com/a/3999238/619434 and then update the UISlider with that value. You can use NSTimer to periodically check the duration.
self.player.currentItem.asset.duration

Loading Multiple Movies in iPhone MPMoviePlayerController

-(void)initAndPlayMovie:(NSURL *)movieURL
{
// Initialize a movie player object with the specified URL
MPMoviePlayerController *mp = [[MPMoviePlayerController alloc] initWithContentURL:movieURL];
if (mp)
{
self.moviePlayer = mp;
[mp release];
[self.moviePlayer play];
}
}
Here, in above code, We can pass just one movie URL. Isn't it possible to pass multiple urls to it?
So, Movie Player will load second url after playing first one.
Is it possible? How can we do that?
Right now, when I try to pass other url, after finishing first one.
- (void) moviePlayBackDidFinish:(NSNotification*)notification
{
[self initAndPlayMovie:secondURL];
}
The Device First change its orientation while loading and after loading Device again come back to landscape mode.
How to resolve this problem?
You might want to change the orientation by changing the statusBar orientation before you start playing videos and change it back after you are done with all.
[[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationLandscapeRight animated:YES];
You should be able to call setContentURL just as the first movie is about to close to change to another movie. Check endPlaybackTime and fire off your method to invoke setContentURL one second prior to the movie ending.

How to get iphone movie player handler (UIWindow handler)

I am trying to play a video like this
self.webView = [[UIWebView alloc] initWithFrame: CGRectMake(0.0, 0.0, 1.0, 1.0)];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: urlToMovie];
[self.webView loadRequest: request];
[request release];
could anyone tell me how can i get moviePlayerWindow (UIWindow ) handler if i am playing with the help of safari.
Is there any way to get movie notifications? like did finish loading, finish playing etc.
thank you for your responses :)
NSArray *windows = [[UIApplication sharedApplication] windows];
if ([windows count] > 1) { // Locate the movie player window
UIWindow * moviePlayerWindow = [[UIApplication sharedApplication] keyWindow];
// ... and now you can add views and play around with the window directly
}
There is only one notification you can get at.
http://www.iphonedevsdk.com/forum/iphone-sdk-development/26322-all-mpmovieplayercontroller-notifications.html
Skip the UIWebView and use MPMoviePlayerController directly, which provides the following notifications:
MPMoviePlayerContentPreloadDidFinishNotification
Notifies observers that the movie is now in memory and ready to play. The affected movie player is stored in the object parameter of the notification. If an error occurred during loading, the userInfo dictionary of this notification contains a key with the name “error” whose value is the NSError object describing the problem.
MPMoviePlayerPlaybackDidFinishNotification
Notifies observers that the movie finished playing. The affected movie player is stored in the object parameter of the notification.
MPMoviePlayerScalingModeDidChangeNotification
Notifies observers that the scaling mode property of the player changed. The affected movie player is stored in the object parameter of the notification.
User actions may also cause the media player to send this notification.