iOS - Detect movie is finished - ios

I am using MPMoviePlayerViewController to play a movie , I create a method which should detects when movie is finished then run a method :
- (void)movieFinishedWithSelector:(SEL)selectors {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(selectors)
name:MPMoviePlayerPlaybackDidFinishNotification
object:[player moviePlayer]];
}
and use this method like this , but does not work .
[self movieFinishedWithSelector:#selector(finished)];
Am I missing something ?

The selectors parameter is already a selector. Don't use #selector:
- (void)movieFinishedWithSelector:(SEL)selector {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:selector
name:MPMoviePlayerPlaybackDidFinishNotification
object:[player moviePlayer]];
}

create a notication when you load the movie
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(myMovieFinished:) name:MPMoviePlayerPlaybackDidFinishNotification object:movieController];
When finished the myMovieFinished will be called
-(void)myMovieFinished:(NSNotification *)aNotification
{
NSLog(#"%#",aNotification.userInfo);
int reason = [[[aNotification userInfo]valueForKey:MPMoviePlayerPlaybackDidFinishNotification]intValue];
if (reason==MPMovieFinishReasonPlaybackEnded) {
NSLog(#"Movie finished playing");
}
else if (reason==MPMovieFinishReasonUserExited)
{
NSLog(#"Movie finished because user exited");
}
else if (reason==MPMovieFinishReasonPlaybackError)
{
NSLog(#"movie finished playback error");
}
movieController=[aNotification object];
[[NSNotificationCenter defaultCenter]removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:movieController ];
}

how did you define the selector? It should be:
- (void)movieDidFinish:(NSNotification*)notification

Related

iOS control center music controls stop working in iOS 11 update (remoteControlReceivedWithEvent is not called iOS 11)

I’m having an issue with iOS control center music controls Before the iOS 11 update, the Play Pause button was enabled and worked normally, as is expected.
However, in iOS 11 it stopped working. After a research, I found that in IOS 11 the remoteControlReceivedWithEvent is never being called, but, in older iOS versions such as iOS 9 it is being called normally
I set my events on AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Enable background audio listening
// https://developer.apple.com/library/ios/qa/qa1668/_index.html
NSError *error = nil;
if (![[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error]) {
NSLog(#"%#", error);
}
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
}
- (void)remoteControlReceivedWithEvent:(UIEvent *)receivedEvent {
if (receivedEvent.type == UIEventTypeRemoteControl) {
switch (receivedEvent.subtype) {
case UIEventSubtypeRemoteControlPlay:
[[NSNotificationCenter defaultCenter] postNotificationName:kCYCAppDelegatePlayPauseNotificationName object:nil];
break;
case UIEventSubtypeRemoteControlPause:
[[NSNotificationCenter defaultCenter] postNotificationName:kCYCAppDelegatePlayPauseNotificationName object:nil];
break;
case UIEventSubtypeRemoteControlTogglePlayPause:
[[NSNotificationCenter defaultCenter] postNotificationName:kCYCAppDelegatePlayPauseNotificationName object:nil];
break;
default:
break;
}
}
}
also I subscribe to remote events in another class to control play/pause buttons
- (void)subscribeToRemoteControlEvents {
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"7.1")) {
// Disables the forward/backward buttons and only shows the play button.
// You can't just enable the command, you must subscribe for this to activate, so
// the subscription in this case doesn't do anything.
[MPRemoteCommandCenter sharedCommandCenter].togglePlayPauseCommand.enabled = YES;
[[MPRemoteCommandCenter sharedCommandCenter].togglePlayPauseCommand addTarget:self action:#selector(ignore_removeCommandCenterFired)];
}
[[NSNotificationCenter defaultCenter] addObserver:self forName:kCYCAppDelegatePlayPauseNotificationName object:nil queue:nil usingBlock:^(NSNotification *note, CYCCastManager *observer) {
if (observer.isCastPlaying) {
[observer pause];
}
else {
[observer play:NO];
}
}];
}
- (void)unsubscribeFromRemoteControlEvents {
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"7.1")) {
[[MPRemoteCommandCenter sharedCommandCenter].togglePlayPauseCommand removeTarget:self action:#selector(ignore_removeCommandCenterFired)];
}
}
I want known why is not working anymore I did check in documentation for changes in the API, but I don't see changes
Note: I check the following links with no luck
iOS - UIEventTypeRemoteControl events not received
https://forums.developer.apple.com/thread/84204
Unable to receive remoteControlReceivedWithEvent - objective c - ios
remoteControlReceivedWithEvent in AVAudio is not being called
remoteControlReceivedWithEvent not Called in appDelegate
Finally I fix the issue by using remoteCommandCenter and play and pause buttons instead of tooglePlayPauseCommand
if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"11.0")){
//NOTE: this is the only way that I find to make this work on IOS 11 its seems to be that togglePlayPauseCommand is not working anymore
MPRemoteCommandCenter* commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
[commandCenter.playCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
[[NSNotificationCenter defaultCenter] postNotificationName:kCYCAppDelegatePlayNotificationName object:nil];
return MPRemoteCommandHandlerStatusSuccess;
}];
[commandCenter.pauseCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
[[NSNotificationCenter defaultCenter] postNotificationName:kCYCAppDelegatePauseNotificationName object:nil];
return MPRemoteCommandHandlerStatusSuccess;
}];
[[NSNotificationCenter defaultCenter] addObserver:self forName:kCYCAppDelegatePlayNotificationName object:nil queue:nil usingBlock:^(NSNotification *note, CYCCastManager *observer) {
if (!observer.isCastPlaying) {
[observer play:NO];
}
}];
[[NSNotificationCenter defaultCenter] addObserver:self forName:kCYCAppDelegatePauseNotificationName object:nil queue:nil usingBlock:^(NSNotification *note, CYCCastManager *observer) {
if (observer.isCastPlaying) {
[observer pause];
}
}];
}
Just modified by Juan's answer.
if(#available(iOS 11, *)) {
MPRemoteCommandCenter* commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
[commandCenter.playCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"RemotePlayCommandNotification" object:nil];
return MPRemoteCommandHandlerStatusSuccess;
}];
[commandCenter.pauseCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"RemotePauseCommandNotification" object:nil];
return MPRemoteCommandHandlerStatusSuccess;
}];
[[NSNotificationCenter defaultCenter] addObserverForName:#"RemotePlayCommandNotification" object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
NSLog(#"Clicked the play button.");
}];
[[NSNotificationCenter defaultCenter] addObserverForName:#"RemotePauseCommandNotification" object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
NSLog(#"Clicked the pause button.");
}];
}

In objective-c how to play multiple video url one after another with out inbetween buffering

In my project I have to play multiple video one after another from server and I have done the following code. My video is playing well but the problem is that while one video is finished I am playing the next video in the -(void)aMoviePlaybackComplete method a black screen is appearing for 1 or 2 seconds. I dont want that and also want to play the video without buffering. Is there any better way to do this? If possible please kindly share valuable code or related links.
Thanks in advance.
-(void)viewDidAppear:(BOOL)animated
{
NSURL * myUrl = [NSURL URLWithString:[arrVideoLink objectAtIndex:0]];
moviePlayer = [[MPMoviePlayerController alloc]initWithContentURL:myUrl];
moviePlayer.controlStyle=MPMovieControlStyleNone;
[vwVideoview addSubview:moviePlayer.view];
moviePlayer.view.frame = CGRectMake(0, 0, vwVideoview.frame.size.width, vwVideoview.frame.size.height);
moviePlayer.view.layer.zPosition = 1;
[moviePlayer prepareToPlay];
[moviePlayer play];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerPlaybackDidFinishNotification
object:moviePlayer];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(aMoviePlaybackComplete:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:moviePlayer];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(loadStateChanged:)
name:MPMoviePlayerPlaybackStateDidChangeNotification
object:moviePlayer];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(nowPlayingMovie:)
name:MPMoviePlayerNowPlayingMovieDidChangeNotification
object:moviePlayer];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(doneButtonClick:)
name:MPMoviePlayerWillExitFullscreenNotification
object:moviePlayer];
}
- (void)aMoviePlaybackComplete:(NSNotification*)notification
{
if (urlIndex+1<=arrVideoLink.count-1)
{
NSURL * myUrl = [NSURL URLWithString:[arrVideoLink objectAtIndex:urlIndex+1]];
moviePlayer = [[MPMoviePlayerController alloc]initWithContentURL:myUrl];
moviePlayer.controlStyle = MPMovieControlStyleNone;
[vwVideoview addSubview:moviePlayer.view];
moviePlayer.view.frame = CGRectMake(0, 0, vwVideoview.frame.size.width, vwVideoview.frame.size.height);
moviePlayer.view.layer.zPosition = 1;
[moviePlayer prepareToPlay];
[moviePlayer play];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(aMoviePlaybackComplete:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:moviePlayer];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(loadStateChanged:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:moviePlayer];
urlIndex++;
}
}else
{
[[NSNotificationCenter defaultCenter]removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:moviePlayer];
[[NSNotificationCenter defaultCenter]removeObserver:self name:MPMoviePlayerPlaybackStateDidChangeNotification object:moviePlayer];
[player.view removeFromSuperview];
}
}

NSNotificationCenter notification fired twice on swipe

I am building an app with AVPlayer that will play songs from an api.
When a song ends, next song will be played. For this I am using the following code:
- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playbackFinished:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[_audioPlayer currentItem]];
}
-(void)playbackFinished:(NSNotification *)notification {
// flagSkip = NO;
NSLog(#"## %# ", NSStringFromSelector(_cmd));
// if(flagSkip == NO)
[[DataSingleton sharedMySingleton] nextTrack];
// else
// flagSkip = NO;
}
On Swipe gesture, next song will be played.
For this, am removing the notification observer and adding it again as follows:
- (IBAction)skipButtonPressed:(id)sender
{
[[DataSingleton sharedMySingleton] nextTrack];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:[_audioPlayer currentItem]];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playbackFinished:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[_audioPlayer currentItem]];
}else{
//
}
}
But when I swipe, sometimes the notification method gets called.
Where am I getting wrong? How do I solve this?
Edit to include [[DataSingleton sharedMySingleton] nextTrack]
-(void)nextTrack{
NSDictionary *prevTrackInfo;
if (currentIndex == -1){
// We're at the start of a refilled list, so previousTrack should be
// the only thing in the cache dir. Clean it up.
dispatch_queue_t removeFilesQueue;
NSLog(#"## %# removeFilesQueue", NSStringFromSelector(_cmd));
removeFilesQueue = dispatch_queue_create("com.zombieprocess.removeFilesQueue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(removeFilesQueue, ^{
// Code goes here
NSError *error = nil;
[fileMgr removeItemAtPath:[self getFeedBandsCacheDir] error:&error];
[fileMgr createDirectoryAtPath:[self getFeedBandsCacheDir] withIntermediateDirectories:YES attributes:nil error:nil];
});
//dispatch_release(removeFilesQueue);
} else{
if (trackInfo){
prevTrackInfo = trackInfo;
}else{
NSLog(#"nextTrack, attempting to store prevTrackInfo, no trackInfo for currentIndex: %d", currentIndex);
}
}
currentIndex += 1;
// We should not have this, but just in case
if (currentIndex >= self.feedEntries.count) {
// We are at the end. Get the feed again.
NSLog(#"## %# currentIndex >= self.feedEntries.count", NSStringFromSelector(_cmd));
if (self.feedEntries.count == 0) {
[self loadFeed];
return;
}
currentIndex = 0; // This will loop it back to the beginning
// [self loadFeed];
// return;
}
trackInfo = [self.feedEntries objectAtIndex:currentIndex];
[self dispatchPlayNotification];
if (prevTrackInfo && [self isTrackCached:prevTrackInfo] && prevTrackInfo != trackInfo && feedEntries.count > [self numberOfSongsToCache]){
NSLog(#"nextTrack, deleting cached copy: %#", [[prevTrackInfo objectForKey:#"file_url"] lastPathComponent]);
[self deleteCachedTrack:prevTrackInfo completionBlock:^{
[self fillDownloadQueue];
}];
} else {
[self fillDownloadQueue];
}
}
I guess you've already switched the next song. So you can't remove notification observer with [_audioPlayer currentItem], because it is already changed.
Update 1.
Try to replace 1st and 2nd lines:
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:[_audioPlayer currentItem]];
[[DataSingleton sharedMySingleton] nextTrack];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(playbackFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:[_audioPlayer currentItem]];
Instead of removing the notification listener, I would read the current state on the player inside the method (void)playbackFinished:(NSNotification *)notification.
For example:
- (IBAction)skipButtonPressed:(id)sender
{
didSkipSong = YES;
[[DataSingleton sharedMySingleton] nextTrack];
}
And in -(void)playbackFinished:(NSNotification *)notification
-(void)playbackFinished:(NSNotification *)notification {
if(didSkipSong){
didSkipSong = NO;
return;
}
[[DataSingleton sharedMySingleton] nextTrack];
}

NSNotificationCenter removing wrong observers?

Is there any corner case behaviors for removeObserver:name:object:? In the following block of code, my observer isn't being registered properly:
- (void)setPlayerItem:(AVPlayerItem *)playerItem {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:nil
object:playerItem];
[playerItem addObserver:self
forKeyPath:kStatus
options:0
context:(__bridge void*)self];
[playerItem addObserver:self
forKeyPath:kPlaybackBufferEmpty
options:0
context:(__bridge void*)self]; // For adding a buffering activity indicator
id temp = playerItem_;
playerItem_ = [playerItem retain];
[[NSNotificationCenter defaultCenter] removeObserver:self name:nil object:temp];
[temp removeObserver:self forKeyPath:kPlaybackBufferEmpty];
[temp removeObserver:self forKeyPath:kStatus];
[temp release];
}
However, if I change the order around to:
- (void)setPlayerItem:(AVPlayerItem *)playerItem {
[playerItem addObserver:self
forKeyPath:kStatus
options:0
context:(__bridge void*)self];
[playerItem addObserver:self
forKeyPath:kPlaybackBufferEmpty
options:0
context:(__bridge void*)self]; // For adding a buffering activity indicator
id temp = playerItem_;
playerItem_ = [playerItem retain];
[[NSNotificationCenter defaultCenter] removeObserver:self name:nil object:temp];
[temp removeObserver:self forKeyPath:kPlaybackBufferEmpty];
[temp removeObserver:self forKeyPath:kStatus];
[temp release];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:nil
object:playerItem];
}
All the notifications post just fine. This leads me to believe something strange is happening when I call:
[[NSNotificationCenter defaultCenter] removeObserver:self name:nil object:temp];
Am I missing something really obvious here? I'm on iOS 6 with no ARC.
Found the answer. Turns out it has to do with passing in nil for the observer name.
Calling [[NSNotificationCenter defaultCenter] removeObserver:self name:nil object:temp];
will remove self from observing any notifications posted by temp.
However, in the corner case that temp is nil, this line of code will remove self as an observer all together.
Name shouldn't be nil. Did you try giving your observer a name?
#Lee is correct that the name should not be nil but it also should not be the name of the observer. Rather it should be the name of the notification that you are registering to observe. e.g., UIDeviceOrientationDidChangeNotification.
Add the name of the notification that you want to observe in that param and also pass it as the name param when you remove observer

MPMoviePlayerController Wont Disappear

I have been trying all day but for some reason on IOS6 im having so many issues with MPMoviePlayerController, it seemed people have had issues but not similar enough to mine:
The movie loads up and plays fine, when it completes the movie. I can loop the movie. The issue arises when I try to close and remove the movie like so:
If I add the controls to the Movie itself and hit the "Done" button, the movie sort of just pauses & never closes!
Any tips/ideas?
_moviePlayerController = [[MPMoviePlayerController alloc] initWithContentURL:fileURL];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlaybackComplete:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:_moviePlayerController];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlaybackLoaded:)
name:MPMoviePlayerLoadStateDidChangeNotification
object:_moviePlayerController];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlaybackComplete:)
name:kMovieOverlayViewTapped
object:nil];
[self.view addSubview:_moviePlayerController.view];
_moviePlayerController.fullscreen = YES;
-
Now on Playback loaded:
- (void)moviePlaybackLoaded:(NSNotification *)notification
{
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerLoadStateDidChangeNotification
object:_moviePlayerController];
_moviePlayerController.movieSourceType = MPMovieSourceTypeFile;
_moviePlayerController.controlStyle = MPMovieControlStyleDefault;
[_moviePlayerController prepareToPlay];
[_moviePlayerController.view setFrame:CGRectMake(38, 100, 250, 163)];
[_moviePlayerController play];
}
now to remove the video:
- (void)moviePlaybackComplete:(NSNotification *)notification
{
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerPlaybackDidFinishNotification
object:_moviePlayerController];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerDidExitFullscreenNotification
object:_moviePlayerController];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:kMovieOverlayViewTapped
object:nil];
[_moviePlayerController stop];
[_moviePlayerController.view removeFromSuperview];
_moviePlayerController = nil;

Resources