Why does MPMusicPlayerController call its now playing item changed selector method twice? - iphone

I am writing a music playlist editing application for the iPhone.
I am subscribing to the MPMusicPlayerController
MPMusicPlayerControllerNowPlayingItemDidChangeNotificationand I am
getting a problem where the #selector method for this notification
is being called more than once when the nowPlayingItem: changes.
I have divided up each playlist into their own MPMediaItemCollection objets, and when one playlist is finished I load the next one, by calling -pause on the player, passing a new MPMediaItemCollectionto the music player controller via its -setQueueWithItemCollection: method. I then explicitly set the nowPlayingItem to an item of item of my own and finally I call -play.
[musicPlayer pause];
[musicPlayer setQueueWithItemCollection:[MPMediaItemCollection collectionWithItems:[selectedPrefs mediaItems]]];
musicPlayer.nowPlayingItem = [selectedPrefs.playbackItems objectAtIndex:selectedPrefs.nowPlayingIndex];
[musicPlayer play];
When I do this the #selector is called twice for the now playing item changing. I suspect this is because -setQueueWithItemCollection:automatically changes the now playing item to point to the first media item in the new collection, and then I am changing this item again before calling -play.
Many thanks.

Related

Requesting a call back or notification once avaudioplayer completes it's playback

I am working in audio player module where it takes one audio file and playing. I am using AvAudioPlayer to play my audio file, it's working as expected and I am getting call backs once finished the audio playback. I have implemented looping by numberOfLoops property with negative value so far it's good. In a case I need call back once the current audio finishes playing first time. Is there any possible way to get call back or observer to notify me my audio played first time.
My idea is that we can achieve this by running a timer. What is the best approach?
Generic:
Each time completion of audio play back expecting a notification even it has infinite loop also.
first make numberofloops=0
use this delegate method so this method will call,
- (void) audioPlayerDidFinishPlaying: (AVAudioPlayer *) appSoundPlayer successfully: (BOOL) flag
then in that method u can make numberofloops= -1 so that u can get first notification and after loop continues. and [player play]..
and if u write numberofloops= 0 in audioPlayerDidFinishPlaying and play then each time audioPlayerDidFinishPlaying will call like loop..and you can get notification for each..

Playing subsequent movies in one MPMoviePlayerViewController

I am having a difficulty with the MPMoviePlayViewController.
I insatiate the controller, assign the url and show the player using:
[self presentMoviePlayerViewControllerAnimated:[appDelegate movieController]];
Then when the movie finished I dismiss it:
[[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:nil];
[self dismissMoviePlayerViewControllerAnimated];
The movie player is alloc in my AppDelegate.
There are some other listeners on MPMoviePlayerPlaybackDidFinishNotification. This is for the case where there might e.g. only be audio and it shows a play/pause button and counters in table cells.
The problem is that when I load a second movie in the same MPMovieViewController, it appears fine but the controls are not working correctly. They are work as long as they are visible, but as soon as they disappear there is no may of getting them back and therefore to dismiss the movie player.Sometimes closing and opening the App works, but sometimes it doesn't and I need to 'kill' the App in order to be able to start again.
Is there a way to play subsequent video's in the MoviePlayer while the controls still work?
Any suggestions how to 'reset' the Player in a way that I can prevent the other listeners from given a DEALLOC as they are listening for the action?
you dont need to dismiss the player you just need to set the new url... or maybe I missunderstood your problem...
Try adding this to your setup movie player controller
moviePlayerController.view.userInteractionEnabled = YES;

Tracking the position of a half played media item on iPhone/iPad

I would like to track the last position of a media item (song or podcast). I can access the total duration using the MPMediaItemPropertyPlaybackDuration property of the MPMediaItem. I am interested in way to store and track the last played position.
Does every player do this on their own ? Would I get it for free if I just play entities with the MediaPlayer ?
I have not hooked media player to play items yet, I would like to understand if I need to implement my own bookmarking method.
[audioPlayer currentPlaybackTime]
Will give you the current playback time. I have a resume feature on a podcast player and I simply watch for interruptions with:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(playPlaybackStateChanged:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:self.audioPlayer];
and then inside that callback function, check the state:
if(self.audioPlayer.playbackState == MPMoviePlaybackStateInterrupted)
and record the playback time on interruption...

iPhone SDK: AVAudioRecorder will not record after calling [AVPlayer play]

I have a view controller that uses AVPlayer. this view controller can load a modal view controler where the user can record audio using AVAudioRecorder.
this is what happens:
if the user plays the composition in the fist controller with [AVPLayer play] the AVAudioRecorder will not record in the modal view controller. there are no errors but the current time returned by AVAudioRecorder is 0.0;
if the user dismisses the modal dialog and reloads it. AVAudioRecorder works fine.
this can be repeated over and over
AVAudioRecorder does not work the first time it is invoked after a [AVPlayer play] call
I have been fighting with this for days and just have reorganized my code related to both AVPlayer and AVAudioRecorder and it's still acting weird.
Any help or pointer is much appreciated
Thanks in advance
Jean-Pierre
I've been having the same problem too, and this is the solution I found.
When recording, write this line code after AVAudioRecord is initialized:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryRecord error:NULL];
Then invoke record method.
How are you setting the AVAudioSession's category, i.e., play or record? Try something like
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
I've been having the same problem. I use AVPlayer to play compositions (previous recordings I've used AVAudioRecord for). However, I found that once I've used AVPlayer I could no longer use AVAudioRecorder. After some searching, I discovered that so long as AVPlayer is instantiated in memory and has been played at least once (which is usually what you do immediately after instantiating it) AVAudioRecorder will not record. However, once AVPlayer is dealloc'd, AVAudioRecorder is then free to record again. It appears that AVPlayer holds on to some kind of connection that AVAudioRecorder needs, and it's greedy...it won't let it go until you pry it from it's cold dead hands.
This is the solution I've found. Some people claim that instantiating AVPlayer takes too much time to keep breaking down and setting back up. However, this is not true. Instantiating AVPlayer is actually quite trivial. So also is instantiating AVPlayerItem. What isn't trivial is loading up AVAsset (or any of it's subclasses). You really only want to do that once. They key is to use this sequence:
Load up AVAsset (for example, if you're loading from a file, use AVURLAsset directly or add it to a AVMutableComposition and use that) and keep a reference to it. Don't let it go until you're done with it. Loading it is what takes all the time.
Once you're ready to play: instantiate AVPlayerItem with your asset, then AVPlayer with the AVPlayerItem and play it. Don't keep a reference to AVPlayerItem, AVPlayer will keep a reference to it and you can't reuse it with another player anyway.
Once it's done playing, immediately destroy AVPlayer...release it, set its var to nil, whatever you need to do. **
Now you can record. AVPlayer doesn't exist, so AVAudioRecorder is free to do its thing.
When you're ready to play again, re-instantiate AVPlayerItem with the asset you've already loaded & AVPlayer. Again, this is trivial. The asset has already been loaded so there shouldn't be a delay.
** Note that destroying AVPlayer may take more than just releasing it and setting its var to nil. Most likely, you've also added a periodic time observer to keep track of the play progress. When you do this, you receive back an opaque object you're supposed to hold on to. If you don't remove this item from the player AND release it/set it to nil, AVPlayer will not dealloc. It appears that Apple creates an intentional retain cycle you must break manually. So before you destroy AVPlayer you need to (example):
[_player removeTimeObserver:_playerObserver];
[_playerObserver release]; //Only if you're not using ARC
_playerObserver = nil;
As a side note, you may also have set up NSNotifications (I use one to determine when the player has completed playing)...don't forget to remove those as well.
If you need use AVPlayer and AVAudioRecorder at the same time do following:
set audio category to "play and record" (as described above or with C-based Audio Session Function)
"record" method must be invoked after invocation of "play" method with some delay. (I set 0.5 sec)
If you don't provide this, playback will start, but recording will not start.
shouldn't it be [AVAudioRecorder record]; instead of play?
I've had the same issue: AVAudioRecorder did not start recording.
My example is a bit different: I have a tabbar based app with several AVAudioPlayers in the different views. For the sake of the example, lets say that the first loaded view has 3 AVAudioPlayers, while the second view has one AVAudioPlayer and one AVAudioRecorder. They are all initiated in the viewDidLoad method.
The recorder was not working in second view. When I hit my record button, the view slightly shaked and the recording did not begin.
Strangely, the simulator worked fine and did not show the same symptoms... (anyone has an idea why?)
After some research and reading this thread I have realized that I have not set the players in the first view to nil, so I guess they were still in the memory preventing the recorder to start. Once I have set them to nil in the viewDidDisappear method, it was working alright again.
here is what helped me:
-(void)viewDidDisappear:(BOOL)animated {
firstPlayer = nil;
secondPlayer = nil;
thirdPlayer = nil;
}
ViewDidUnload did not help because the View, and therefore the variablbes in the class itself were not unloaded when switching views.
Hope this may be useful for others as well.
#Aaron Hayman explanation is accurate but the solution for me was very simple. Just implement the didFinishPlaying method of the player delegate and release the player there.
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
self.playerWord = nil;
}

How to know when the MPMoviePlayerController has been paused in iPhone?

I want to add an overlay view for my video when the video is paused by the user. Is there any way to get the pause notification from MPMoviePlayerController?
According to Apple Doc, there should be ways to do this but I can't find which notification should I use for this purpose.
Quote:
In addition to being notified when
playback finishes, interested clients
can be notified in the following
situations:
-When the movie player begins playing, is paused, or begins seeking forward
...
For more information, see the Notifications section in this reference.
I assume you know about delegates and protocols as a means of receiving callbacks?
There's another global mechanism called notifications too.
You can do this via
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playbackStateChanged)
name:MPMoviePlayerPlaybackStateDidChangeNotification object:nil];
Then, within playbackStateChanged, you can fetch the playbackState
- (void) playbackStateChanged {
_player.playbackState; // reading the playback
}
The step of reading playbackstate directly from the player is specified in the docs
To get the current playback state, get the value of the playbackState property of the movie player object.