NSNotificationCenter notification fired twice on swipe - ios

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];
}

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.");
}];
}

class AVPlayerItem was deallocated while key value observers were still registered

I'm getting the following error, but the strange thing is that it only happens when I do a scroll on my tableview using the pagination, loading a second set of posts, and than I try to move out to new view controller,
if I load only the first page of posts, and move out to another view controller, there is no crash:
Terminating app due to uncaught exception 'NSInternalInconsistencyException',
reason: 'An instance 0x170212330 of class AVPlayerItem was deallocated while key
value observers were still registered with it.
Current observation info: <NSKeyValueObservationInfo 0x174820360> (
<NSKeyValueObservance 0x174244290: Observer: 0x101c76c00, Key path:
playbackBufferEmpty, Options: <New: YES, Old: NO, Prior: NO> Context: 0x0,
Property: 0x174243ea0>
<NSKeyValueObservance 0x1754583c0: Observer: 0x101c76c00, Key path:
playbackLikelyToKeepUp, Options: <New: YES, Old: NO, Prior: NO> Context: 0x0,
Property: 0x174243f60>
I have a player on my cell, here is my code for view controller:
- (UITableViewCell *)tableView:(UITableView *)tableViewSelected cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *identifier = #"postCell";
PostCell* updateCell = [tableViewSelected dequeueReusableCellWithIdentifier:identifier];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(#"ADD VIDEO PLAYER and PLAY VIDEO");
NSString* videoString = [NSString stringWithFormat:#"%#%#%#",UrlBase,PostVideos,post.video];
NSString* expandedPath = [videoString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *videoURL = [NSURL URLWithString:expandedPath];
NSLog(#"URL : %#",videoURL);
updateCell.videoItem = [AVPlayerItem playerItemWithURL:videoURL];
[updateCell.videoItem addObserver:updateCell forKeyPath:#"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];
[updateCell.videoItem addObserver:updateCell forKeyPath:#"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];
updateCell.videoPlayer = [AVPlayer playerWithPlayerItem:updateCell.videoItem];
updateCell.avLayer = [AVPlayerLayer playerLayerWithPlayer:updateCell.videoPlayer];
updateCell.videoPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
[[NSNotificationCenter defaultCenter] addObserver:updateCell selector:#selector(itemDidBufferPlaying:) name:AVPlayerItemPlaybackStalledNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:updateCell selector:#selector(itemDidFinishPlaying:) name:AVPlayerItemDidPlayToEndTimeNotification object:[updateCell.videoPlayer currentItem]];
updateCell.avLayer.frame = updateCell.picture.bounds;
[updateCell.videoView.layer addSublayer:updateCell.avLayer];
[updateCell.avLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
if(indexPath.row==0){
[updateCell.videoPlayer play];
}
});
});
return updateCell;
}
And the only difference I can see from adding a second page is:
When doing regular table view load I do:
[_tableView reloadData];
When loading a second page on the same table view I do:
[_tableView beginUpdates];
[_tableView insertRowsAtIndexPaths:[[GlobalSingleton sharedInstance] indexPathsToInsert] withRowAnimation:UITableViewRowAnimationTop];
[_tableView endUpdates];
Than on my Cell I do:
- (void)prepareForReuse{
[self removePlayer];
}
- (void) dealloc {
[self removePlayer];
}
-(void)removePlayer{
#try{
[self.videoItem removeObserver:self forKeyPath:#"playbackBufferEmpty"];
[self.videoItem removeObserver:self forKeyPath:#"playbackLikelyToKeepUp"];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemPlaybackStalledNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
NSLog(#"remove Observer!");
// [avLayer.player pause];
}
#catch (NSException * e) {
NSLog(#"Exception: %#", e);
}
#finally {
NSLog(#"finally");
[self.avLayer removeFromSuperlayer];
self.playIV.hidden = YES;
self.videoActivity.hidden = YES;
self.videoView.hidden = YES;
self.videoItem = nil;
self.avLayer = nil;
self.videoPlayer = nil;
}
}
Is it possible that insertRowsAtIndexPaths is causing the cells to never be dealloc? I can't find how its possible to have the observers registered.
Somehow when using the insertRowsAtIndexPaths, the prepareForReuse is not been called, so I resolved it by unregistering the observers on the cellForRowAtIndexPath before adding a new videoItem to the Cell:
- (UITableViewCell *)tableView:(UITableView *)tableViewSelected cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *identifier = #"postCell";
PostCell* updateCell = [tableViewSelected dequeueReusableCellWithIdentifier:identifier];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
dispatch_sync(dispatch_get_main_queue(), ^{
NSString* videoString = [NSString stringWithFormat:#"%#%#%#",UrlBase,PostVideos,post.video];
NSString* expandedPath = [videoString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *videoURL = [NSURL URLWithString:expandedPath];
/** SOLUTION STARTS HERE **/
#try{
[self.videoItem removeObserver:self forKeyPath:#"playbackBufferEmpty"];
[self.videoItem removeObserver:self forKeyPath:#"playbackLikelyToKeepUp"];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemPlaybackStalledNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
}
#catch (NSException * e) {
NSLog(#"Exception: %#", e);
}
#finally {
NSLog(#"finally");
}
/** SOLUTION ENDS HERE **/
updateCell.videoItem = [AVPlayerItem playerItemWithURL:videoURL];
[updateCell.videoItem addObserver:updateCell forKeyPath:#"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];
[updateCell.videoItem addObserver:updateCell forKeyPath:#"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];
updateCell.videoPlayer = [AVPlayer playerWithPlayerItem:updateCell.videoItem];
updateCell.avLayer = [AVPlayerLayer playerLayerWithPlayer:updateCell.videoPlayer];
updateCell.videoPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
[[NSNotificationCenter defaultCenter] addObserver:updateCell selector:#selector(itemDidBufferPlaying:) name:AVPlayerItemPlaybackStalledNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:updateCell selector:#selector(itemDidFinishPlaying:) name:AVPlayerItemDidPlayToEndTimeNotification object:[updateCell.videoPlayer currentItem]];
updateCell.avLayer.frame = updateCell.picture.bounds;
[updateCell.videoView.layer addSublayer:updateCell.avLayer];
[updateCell.avLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
if(indexPath.row==0){
[updateCell.videoPlayer play];
}
});
});
return updateCell;
}

Play video from url in objective c with buffering

Hi everyone I am working on an application in which I have a url of video and I have to play a video from that url. I have done this job from this code
- (IBAction)btnPlayVideo:(id)sender
{
NSString *fileName = #"Server Address/Vdieo.flv";
NSURL *streamURL = [NSURL URLWithString:fileName];
mPlayerVC = [[MPMoviePlayerViewController alloc] initWithContentURL:streamURL];
[self.view addSubview:mPlayerVC.view];
//play movie
MPMoviePlayerController *player = [mPlayerVC moviePlayer];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willEnterFullscreen:) name:MPMoviePlayerWillEnterFullscreenNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(playbackFinished:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil];
player.view.frame = self.view.frame;
[player setFullscreen:YES animated:YES];
[self.view addSubview:player.view];
[player prepareToPlay];
[player play];
}
//============Other Methods====================
- (void)willEnterFullscreen:(NSNotification*)notification {
NSLog(#"willEnterFullscreen");
}
- (void)enteredFullscreen:(NSNotification*)notification {
NSLog(#"enteredFullscreen");
}
- (void)willExitFullscreen:(NSNotification*)notification {
NSLog(#"willExitFullscreen");
}
- (void)exitedFullscreen:(NSNotification*)notification {
NSLog(#"exitedFullscreen");
[mPlayerVC.view removeFromSuperview];
mPlayerVC = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)playbackFinished:(NSNotification*)notification {
NSNumber* reason = [[notification userInfo] objectForKey:MPMoviePlayerPlaybackDidFinishReasonUserInfoKey];
switch ([reason intValue]) {
case MPMovieFinishReasonPlaybackEnded:
NSLog(#"playbackFinished. Reason: Playback Ended");
break;
case MPMovieFinishReasonPlaybackError:
NSLog(#"playbackFinished. Reason: Playback Error");
break;
case MPMovieFinishReasonUserExited:
NSLog(#"playbackFinished. Reason: User Exited");
NSLog(#"exitedFullscreen");
[[NSNotificationCenter defaultCenter] removeObserver:self];
break;
default:
break;
}
[mPlayerVC.view removeFromSuperview];
mPlayerVC = nil;
}
My problem is that when this code run video player open and start loading and it takes too much time to run a video. Can anybody guide me how to run video in fast way from internet?
There's nothing in the code that suggests that lag is anything other than the time it takes to make the request and sufficiently buffer. The most common technique to improve UE is to start loading as early as possible, even before the user requests playback.
If this is possible, the code should be reorganized as follows:
// hang on to the movie player
#property(nonatomic,retain) MPMoviePlayerController *mp;
// call this as soon as its possible to know the user might want to see the video
- (void)primeVideo {
NSString *fileName = #"Server Address/Vdieo.flv";
NSURL *streamURL = [NSURL URLWithString:fileName];
MPMoviePlayerController *mp = [[MPMoviePlayerViewController alloc] initWithContentURL:streamURL];
// do this also in dealloc
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willEnterFullscreen:) name:MPMoviePlayerWillEnterFullscreenNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(playbackFinished:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil];
mp.shouldAutoplay = NO;
[mp prepareToPlay];
self.mp = mp;
}
That does as much prep as possible without changing the UI. The rest of your code from the button action is left, tweaked a little...
- (IBAction)btnPlayVideo:(id)sender {
// if there's any way that the user can request playback before
// you've called primeVideo, check for that here. But hopefully you
// can call primeVideo before user even sees the play button
if (!self.mp) [self primeVideo];
self.mp.view.frame = self.view.frame;
[self.mp setFullscreen:YES animated:YES];
[self.view addSubview:self.mp.view];
MPMovieLoadState state = [self.mp loadState];
if (state & MPMovieLoadStatePlaythroughOK) [self.mp play];
else self.mp.shouldAutoplay = YES;
}

AVQueuePlayer AVPlayerItemDidPlayToEndTimeNotification fails to call

Im using AVQueuePlayer to loop through an array of AVPlayerItems.
The way I'm looping it, I listen to the AVPlayerItemDidPlayToEndTimeNotification and every time its called, I add the current object to the end of the queue.
heres the code:
viewWillAppear
{
...
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[_queuePlayer currentItem]];
[_queuePlayer play];
}
- (void)playerItemDidReachEnd:(NSNotification *)notification {
AVPlayerItem *p = [notification object];
AVPlayerItem *fakeNew = [[AVPlayerItem alloc] initWithAsset:p.asset];
if (_queuePlayer.items.count == 1)
{
[p seekToTime:kCMTimeZero];
[_queuePlayer play];
}
else
{
[_queuePlayer insertItem:fakeNew afterItem:[_queuePlayer.items lastObject]];
}
NSLog(#"array of items to play:%lu", (unsigned long)_queuePlayer.items.count);
}
The problem is, that the method is called only for the first video that plays, after that, the method stops getting called, so if for example i have 2 movies in the array, it would play them both+the first one again, any idea why is this happening?
More Info:
also tried to make a new player every time and set it to layer. failed to send the notification more than once just the same
- (void)playerItemDidReachEnd:(NSNotification *)notification {
AVPlayerItem *p = [notification object];
[self.playList removeObjectAtIndex:0];
[self.playList addObject:p];
AVPlayer *newPlayer = [[AVPlayer alloc] initWithPlayerItem:[self.playList objectAtIndex:0]];
_player = newPlayer;
self.AVPlayerLayerView.layer.player = self.player;
[_player play];
}
After a lot of messing around, apparently for whatever reason, the view unregistered as observer every time, I just removed and added observer after every notification:
- (void)playerItemDidReachEnd:(NSNotification *)notification {
AVPlayerItem *p = [notification object];
AVPlayerItem *fakeNewItem = [[AVPlayerItem alloc] initWithAsset:p.asset];
[self.playList removeObjectAtIndex:0];
[self.playList addObject:fakeNewItem];
AVPlayer *newPlayer = [[AVPlayer alloc] initWithPlayerItem:[self.playList objectAtIndex:0]];
_player = newPlayer;
self.AVPlayerLayerView.layer.player = self.player;
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[_player currentItem]];
[_player play];
}
For a clean approach to resolve this issue. I approached with the next piece of code instead
The first is you have to add the code necessary to receive a feedback from the AVPlayer when the reproduction time changes.
- (void)addPeriodicTimeObserverForReproductionChanges {
#weakify(self);
[self.player
addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(kBQDefaultTimeIntervalReproductionChanges, NSEC_PER_SEC)
queue:self.eventsQueue
usingBlock:^(CMTime time) {
#strongify(self);
[self dispatchBlockOnMainQueue:^{
if ([self.delegate respondsToSelector:#selector(playerItemController:didChangeReproductionTime:)])
[self.delegate playerItemController:self
didChangeReproductionTime:time];
[self checkForItemPlayToEnd];
}];
}];
}
- (void)checkForItemPlayToEnd
{
CMTime durationScaled = CMTimeConvertScale(self.duration,self.player.currentTime.timescale, kCMTimeRoundingMethod_Default);
if (CMTIME_COMPARE_INLINE(durationScaled, ==, self.player.currentTime)) {
[self playerDidFinishReproducingItem];
}
}

iOS - Detect movie is finished

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

Resources