I am building a AVQeueue Player from a set of encoded videos on an s3 bucket, playing videos in sequence using AVPlayerActionAtItemEndAdvance;
videos are all encoded the same resolution and codec
They are all tested good to play
On some devices this solution below works fine
The issue below is noticed both on IOS 5.x and 6.x
Been trying to check all done in the main thread. (most likely the issue)
Issue: on the simulator and on some devices (noticed on 4s and 5) after an x amount of videos (x being unpredictable at times but usually 3, 4) a video plays only with sound and kills the queue and does not throw "playerItemDidReachEnd" notification. App does not crash or freeze.
No Errors received from "AVPlayerItemStatusFailed" or "AVPlayerItemStatusUnknown"
If anyone has experience with this problem please share.
Code:
-(void)addObservers {
dispatch_async(dispatch_get_main_queue(),
^{
for(int i = 0; i < [self.videodata count]; i++) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[self.videodata objectAtIndex:i]];
[[self.videodata objectAtIndex:i] addObserver:self forKeyPath:#"status" options:NSKeyValueObservingOptionNew context:nil];
[[self.videodata objectAtIndex:i] addObserver:self forKeyPath:#"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];
[[self.videodata objectAtIndex:i] addObserver:self forKeyPath:#"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];
}
});
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *) change context:(void *)context
{
if ([object isKindOfClass:[AVPlayerItem class]])
{
AVPlayerItem *item = (AVPlayerItem *)object;
if ([keyPath isEqualToString:#"status"])
{ //yes->check it...
switch(item.status)
{
case AVPlayerItemStatusFailed:
{
[TestFlight passCheckpoint:#"AV FAILED"];
NSError *error = [item error];
NSLog(#"playback error is %#", error);
break;
}
case AVPlayerItemStatusReadyToPlay:
NSLog(#"player item status is ready to play");
break;
case AVPlayerItemStatusUnknown:
{
[TestFlight passCheckpoint:#"AV STATUS UNKNOWN"];
break;
}
}
}
else if (item == [self.queuePlayer currentItem] && [keyPath isEqualToString:#"playbackBufferEmpty"])
{
if (item.playbackBufferEmpty)
{
[loadingView show:YES];
}
}
else if(item == [self.queuePlayer currentItem] && [keyPath isEqualToString:#"playbackLikelyToKeepUp"])
{
[self.queuePlayer play];
[loadingView hide:YES];
}
}
}
- (void)playerItemDidReachEnd:(NSNotification *)notification {
NSLog(#"ended video %i " , (int)self.currentItem);
AVPlayerItem *p = [notification object];
[p seekToTime:kCMTimeZero];
}
Related
I am trying to show MBProgressHud to MPMoviePlayerController, for that I am observing notifications for load states of MPMoviePlayer, but somehow Method observing notification never observes notifications for load states other then MPMovieLoadStatePlayable. I show MBProgressHud when video starts streaming but it does not work after it plays and then to pause to download video, Due to this I am unable to engage user while video is loading, If anyone has a better method please mention it or if there is any problem in the following code then let me know.
-(void)movieLoadStateDidChange:(NSNotification*)notification{
MPMoviePlayerController *player = [notification object];
if ((player.loadState & MPMovieLoadStatePlayable) == MPMovieLoadStatePlayable) {
NSLog(#"Load state Playable");
[MBProgressHUD hideAllHUDsForView:self.view animated:YES];
}else if ((player.loadState & MPMovieLoadStatePlaythroughOK) == MPMovieLoadStatePlaythroughOK){
NSLog(#"Load state Playing");
[MBProgressHUD hideAllHUDsForView:self.view animated:YES];
}else if ((player.loadState & MPMovieLoadStateStalled) == MPMovieLoadStateStalled){
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
NSLog(#"Load state stalled");
}else if ((player.loadState & MPMovieLoadStateUnknown) == MPMovieLoadStateUnknown){
NSLog(#"Load State unknown");
}
}
Ok .. i got your problem and the problem is that you are not getting notification for load states except MPMovieLoadStatePlayable. so here what you can do is...like below...
write down below notifiations in viewdidload
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(moviePlayerPlaybackDidFinish:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(moviePlayerPlaybackStateDidChange:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(moviePlayerLoadStateDidChange:) name:MPMoviePlayerLoadStateDidChangeNotification object:nil];
After defining it in ViewDidLoad , implement those functions like below....
- (void) moviePlayerPlaybackDidFinish:(NSNotification *)notification
{
//your code....
MPMovieFinishReason finishReason = [notification.userInfo[MPMoviePlayerPlaybackDidFinishReasonUserInfoKey] integerValue];
NSError *error = notification.userInfo[XCDMoviePlayerPlaybackDidFinishErrorUserInfoKey];
NSString *reason = #"Unknown";
switch (finishReason)
{
case MPMovieFinishReasonPlaybackEnded:
reason = #"Playback Ended";
break;
case MPMovieFinishReasonPlaybackError:
reason = #"Playback Error";
break;
case MPMovieFinishReasonUserExited:
reason = #"User Exited";
break;
}
NSLog(#"Finish Reason: %#%#", reason, error ? [#"\n" stringByAppendingString:[error description]] : #"");
}
- (void) moviePlayerPlaybackStateDidChange:(NSNotification *)notification
{
MPMoviePlayerController *moviePlayerController = notification.object;
NSString *playbackState = #"Unknown";
switch (moviePlayerController.playbackState)
{
case MPMoviePlaybackStateStopped:
playbackState = #"Stopped";
break;
case MPMoviePlaybackStatePlaying:
playbackState = #"Playing";
break;
case MPMoviePlaybackStatePaused:
playbackState = #"Paused";
break;
case MPMoviePlaybackStateInterrupted:
playbackState = #"Interrupted";
break;
case MPMoviePlaybackStateSeekingForward:
playbackState = #"Seeking Forward";
break;
case MPMoviePlaybackStateSeekingBackward:
playbackState = #"Seeking Backward";
break;
}
NSLog(#"Playback State: %#", playbackState);
}
- (void) moviePlayerLoadStateDidChange:(NSNotification *)notification
{
MPMoviePlayerController *moviePlayerController = notification.object;
NSMutableString *loadState = [NSMutableString new];
MPMovieLoadState state = moviePlayerController.loadState;
if (state & MPMovieLoadStatePlayable)
[loadState appendString:#" | Playable"];
if (state & MPMovieLoadStatePlaythroughOK)
[loadState appendString:#" | Playthrough OK"];
if (state & MPMovieLoadStateStalled)
[loadState appendString:#" | Stalled"];
NSLog(#"Load State: %#", loadState.length > 0 ? [loadState substringFromIndex:3] : #"N/A");
}
let me know it is working or not!!!
Happy Coding!!!!
I'm building a Game Center game in iOS6 and am continuously running into problems with it. The current one has me stumped - Every single time my game tries to authenticate the local player it fails. Every time it is triggering my "disable game center" function and it's driving me crazy.
- (void) disableGameCenter
{
// Write something to disable gamecenter.
// gameCenterAvailable = FALSE;
}
-(void)showAuthenticationDialogWhenReasonable:(UIViewController *)viewController
{
// Pause Tasks Here
// [[[(AppDelegate *)[[UIApplication sharedApplication] delegate] window] rootViewController] presentViewController:viewController animated:YES completion:nil];
}
- (void) authenticateLocalPlayer
{
localPlayer = [GKLocalPlayer localPlayer];
__weak GKLocalPlayer* weakLocalPlayer = localPlayer;
weakLocalPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error)
{
if (viewController != nil)
{
[self showAuthenticationDialogWhenReasonable: viewController];
}
else if (weakLocalPlayer.isAuthenticated)
{
self.localPlayer = weakLocalPlayer;
}
else
{
[self disableGameCenter];
}
};
}
Forgive me if I'm wrong, but I don't think you are running the authenticate method.
Try this method instead:
- (void) authenticateLocalPlayer
{
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
[localPlayer authenticateWithCompletionHandler:^(NSError *error) {
if (localPlayer.isAuthenticated)
{
NSLog(#"Authenticated");
}
else
{
NSLog(#"Not Authenticated");
}
}];
}
I'm trying to get ASIReachability to work in my app which works when the connection isn't there but when the connection exists it give the following error:
2013-04-08 12:26:20.501 Your Llanelli Companion[1576:207] -[NSConcreteNotification isReachable]: unrecognized selector sent to instance 0x7d84d30
I can't seem to remedy this and it's beginning to bug me.
.m file:
- (void) CheckIfAInternetConnectionExists
{
Reachability *reach = [[Reachability reachabilityWithHostName:#"http://176.31.101.181:8020/listen.pls"]retain ];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reachabilityChanged:)
name:kReachabilityChangedNotification
object:nil];
[reach startNotifier];
}
- (void) reachabilityChanged:(Reachability *)reach {
if ([reach isReachable ]) {
NSLog(#"connection");
ServerFound = YES;
[self PrepareStream];
} else{
NSLog(#"no connection");
ServerFound = NO;
[self PrepareStream];
}
}
If someone can figure this out for me I'd be very grateful.
When you receive the notification, the parameter to your reachabilityChanged: method will be the notification itself. To get the Reachability object, you'll need to get it from the notification by sending -[NSNotification object] to the notification.
So something like this should work:
- (void) reachabilityChanged:(NSNotification *)note {
Reachability *reach = [note object];
if ([reach isReachable ]) {
NSLog(#"connection");
ServerFound = YES;
} else{
NSLog(#"no connection");
ServerFound = NO;
}
[self PrepareStream];
}
How do I know who is calling me?
Identify the number or even the contact in my list case I have.
I can identify if have or not a call, with this code.
void (^ctCallStateMuda)(NSNotification *) = ^(NSNotification * notification) {
NSString *callInfo = [[notification userInfo] objectForKey:#"callState"];
if ([callInfo isEqualToString:CTCallStateIncoming]) {
NSLog(#">>>>>> chegando");
} else if ([callInfo isEqualToString:CTCallStateConnected]) {
NSLog(#">>> atendendo <<<");
} else if ([callInfo isEqualToString:CTCallStateDisconnected]) {
NSLog(#"desconectado >>>>>>");
} else if ([callInfo isEqualToString:CTCallStateConnected]) {
NSLog(#"discando");
} else {
NSLog(#"nada");
}
};
CTCallCenter *callCenter;
callCenter = [[CTCallCenter alloc] init];
callCenter.callEventHandler = ^(CTCall* aCallIncomming) {
NSDictionary *dict = [NSDictionary dictionaryWithObject:aCallIncomming.callState
forKey:#"callState"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"CTCallStateDidChange"
object:self
userInfo:dict];
};
[[NSNotificationCenter defaultCenter] addObserverForName:#"CTCallStateDidChange"
object:nil
queue:nil
usingBlock:ctCallStateMuda];
You don't have access to this information in the public SDK (a jailbroken iPhone is another matter). Apple prohibit apps from having access to any information relating to call history. The code you've posted above is so your app can detect when the user is receiving a phone call and adapt their interface accordingly, but that's it.
Is it possible to force an audio file to finish playing before proceeding with code? In the below example, I'd like an audio file to play for as many times as I have items in the array. current it finishes the loop only playing once. I'm using the AVAudioPlayer within the AVFoundation framework.
for (int i = 0; i<[Array count]; i++) {
if ([Array objectAtIndex:i] == #"Red") {
NSLog(#"red");
[self.player play];
}
if ([Array objectAtIndex:i] == #"Blue") {
NSLog(#"blue");
[self.player play];
}
if ([Array objectAtIndex:i] == #"Green") {
NSLog(#"green");
[self.player play];
}
if ([Array objectAtIndex:i] == #"Yellow") {
NSLog(#"yellow");
[self.player play];
}
}
I also use the method audioPlayerDidFinishPlaying to test if it's finishing five times, but it only reaches this method once.
-(void) audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL) completed{
if ((completed) == YES){
NSLog(#"audio finish");
return;
}
}
Is it possible to hold the for loop in place while the audio is playing?
in theory the console should read: color->audio finish->color->audio finish->repe
Maybe you can tell the AudioPlayer to play when you receive the finishedPlaying notification like:
int arrayIndex = 0
NSArray* array; //initialized somewhere..
-(void)myFunction
{
if(arrayIndex < [array length])
{
NSArray* colors = [NSArray arrayWithObjects:#"Red",#"Blue",#"Green",#"Yellow",nil];
if( [colors indexOf:[array objectAtIndex:arrayIndex]] != NSNotFound )
{
NSLog( [array objectAtIndex:arrayIndex] );
[self.player play];
}
arrayIndex++;
}else{
NSLog(#"audio finish");
}
}
-(void) audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL) completed{
if ((completed) == YES){
[self myFunction];
}
}
-(void)start_it_up
{
arrayIndex = 0;
[self myFunction];
}