iOS7 AVQueueplayer background loop (loop queued music in background) - ios

This is my first post and I have a problem that I am completely stumped on. I have a AVQueueplayer with multiple mp3's posted on a server.
The app plays and loops while the app is in the foreground. However, when I press the home button, queueplayer stops looping.
Here is the code I have so far
-(void)playselectedsong{
NSError *sessionError = nil;
[[AVAudioSession sharedInstance] setDelegate:self];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:&sessionError];
UInt32 doChangeDefaultRoute = 1;
AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryDefaultToSpeaker, sizeof(doChangeDefaultRoute), &doChangeDefaultRoute);
NSString* musicURL= [NSString stringWithFormat:#"%#%#", getMusicURL, [musicArray objectAtIndex:0]];
NSString* musicURL2= [NSString stringWithFormat:#"%#%#", getMusicURL, [musicArray objectAtIndex:1]];
NSArray *queue = #[[AVPlayerItem playerItemWithURL:[NSURL URLWithString:musicURL]], [AVPlayerItem playerItemWithURL:[NSURL URLWithString:musicURL2]]];
AVQueuePlayer *qplayer = [[AVQueuePlayer alloc] initWithItems:queue];
self.queuePlayer=qplayer;
[qplayer play];
qplayer.actionAtItemEnd = AVPlayerActionAtItemEndAdvance;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[queue lastObject]];
}
- (void)playerItemDidReachEnd:(NSNotification *)notification {
// code here to play next sound file
NSLog(#"playeritemdidreachend");
[self playselectedsong ];
}

Adjust your project for playing audio in background (see here)
Start playing via
dispatch_async(dispatch_get_main_queue(), ^{
[avqueueplayerinstance play];
});//end block`

Related

MPMoviePlayerController automatic pause at second X

I have to automatic stop the player at second X and I don't know which kind of notification I have to used.
After, when user taps anywhere on the screen the player continue to run the video.
- (IBAction)playMovie:(id)sender {
NSString *filepath = [[NSBundle mainBundle] pathForResource:#"movie" ofType:#"m4v"];
NSURL *fileURL = [NSURL fileURLWithPath:filepath];
_moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:fileURL];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(moviePlayBackDidFinish:) name:MPMoviePlayerPlaybackDidFinishNotification object:_moviePlayer];
//here I don't know which notification I have to used
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(moviePlayBackPause:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:_moviePlayer];
_moviePlayer.controlStyle = MPMovieControlStyleDefault;
_moviePlayer.initialPlaybackTime = 2.5;
_moviePlayer.shouldAutoplay = YES;
[self.view addSubview:_moviePlayer.view];
[_moviePlayer setFullscreen:YES animated:NO];
[_moviePlayer play];
}
I tried to catch the movie frame and analyzed it to see if is time to pause the video.
- (void) moviePlayBackPause:(NSNotification*)notification{
// check if it's time to pause the video
if([_moviePlayer currentPlaybackTime] == 6.0){
[_moviePlayer pause];
}
}
Which type of notification I have to use to catch the current time of the video?
Thanks in advance for all your responses!
Kind regards!
You can use a NSTimer to stop your video.
Should exist a better way to do it, but with timer you can do it as well.
[_moviePlayer play];
[NSTimer scheduledTimerWithTimeInterval:6 //Time in seconds
target:self
selector:#selector(moviePlayBackPause) //Method called when the timer is completed.
userInfo:nil
repeats:NO];
}
}
- (void) moviePlayBackPause {
[_moviePlayer pause];
}

handleWatchKitExtensionRequest play background audio on iphone

I'm experimenting with an WatchKit app that will trigger an audio alert on the iphone. I've got it setup to do the trigger, repeating every 5 seconds. On the iphone, I have the logic that will play the short audio wav file in the background. It is designed to work even if the app is minimized and play the audio. I've tested that stand-alone on the iphone, to work fine.
The step that is not working is the trigger on the watch to tell the parent Iphone app to play the audio. It mostly works, except that the 2 second audio alert is clipped at perhaps .5 seconds. It plays a chirp of the first part and is cut off. Each time I trigger it, it will do this clipped chirp of the entire wav sound.
The reply from the parent app comes across okay, and I'm wondering if as soon as that reply comes back all background processing is cut off entirely.
I have background-mode for Audio enabled in the application.
How can I get the watch to trigger the app to play the entire audio alert in the background?
// WatchApp InterfaceController
- (void)pingIphone {
[WKInterfaceController openParentApplication:#{#"requestString":#"findphone"} reply:^(NSDictionary *replyInfo, NSError *error) {
NSLog(#"\nReply info: %#\nError: %#",replyInfo, error);
}];
}
// Iphone AppDelegate
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void(^)(NSDictionary *replyInfo))reply {
NSString * request = [userInfo objectForKey:#"requestString"];
NSDictionary * replyContent = #{#"state":(application.applicationState == UIApplicationStateBackground ? #"back" : #"not back")};
if ([request isEqualToString:#"findphone"]){
ViewController *vc = [[ViewController alloc] init];
[vc pingIphone];
}
reply(replyContent);
}
// Iphone ViewController
#property (strong, nonatomic) AVQueuePlayer *player;
- (void)viewDidLoad {
[super viewDidLoad];
// get device's default audio level on start
AudioSessionInitialize(NULL, NULL, NULL, NULL);
AudioSessionSetActive(true);
Float32 volume;
UInt32 dataSize = sizeof(Float32);
AudioSessionGetProperty (
kAudioSessionProperty_CurrentHardwareOutputVolume,
&dataSize,
&volume
);
[[AVAudioSession sharedInstance] setActive:YES error:nil];
[[AVAudioSession sharedInstance] addObserver:self forKeyPath:#"outputVolume" options:NSKeyValueObservingOptionNew context:nil];
}
(void)pingIphone {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
if([userDefaults boolForKey:#"vibrate_bool"] == YES){
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);
}
for(int i = 0; i < 5; i++){
[_player insertItem:[AVPlayerItem playerItemWithURL:[[NSBundle mainBundle] URLForResource:#"alarm" withExtension:#"wav"]] afterItem:nil];
}
//NSLog(#"volume: %f",[userDefaults floatForKey:#"volume"]);
if (!(_player.rate > 0 && !_player.error)) {
[self startAudio];
}
}
- (void)startAudio {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[[AVAudioSession sharedInstance] setDelegate: self];
NSError *setCategoryError = nil;
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error: &setCategoryError];
NSString* path = [[NSBundle mainBundle] pathForResource:#"alarm" ofType:#"wav"];
_player = [[AVQueuePlayer alloc] init];
for(int i = 0; i < 5; i++){
[_player insertItem:[AVPlayerItem playerItemWithURL:[NSURL fileURLWithPath:path]] afterItem:nil];
}
_player.volume = [userDefaults floatForKey:#"volume"];
[_player play];
if (setCategoryError)
NSLog(#"Error setting category! %#", setCategoryError);
}
It sounds like your app is being killed off by the OS. Check out this answer for some advice: https://stackoverflow.com/a/29848521/3704092
And yes, if you don't use a background task, processing ends once you call reply().

Local video only plays when i play a web video before - AVFoundation

Im trying to play a local video using AVFoundation. I don't know whats going on here that only if I play a video on the web and then play the local video it works. If I just play the local video it gives me the error:
Item cannot be played
he assets tracks were loaded, but could not be made playable.
And Here's my code:
- (void)setURL:(NSURL*)URL
{
if (mURL != URL)
{
[mURL release];
mURL = [URL copy];
NSLog(#"Im here %#",mURL);
}
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:mURL options:nil];
NSArray *requestedKeys = [NSArray arrayWithObjects:kTracksKey, kPlayableKey, nil];
/* Tells the asset to load the values of any of the specified keys that are not already loaded. */
[asset loadValuesAsynchronouslyForKeys:requestedKeys completionHandler:
^{
dispatch_async( dispatch_get_main_queue(),
^{
/* IMPORTANT: Must dispatch to main queue in order to operate on the AVPlayer and AVPlayerItem. */
[self prepareToPlayAsset:asset withKeys:requestedKeys];
});
}];
}
- (void)prepareToPlayAsset:(AVURLAsset *)asset withKeys:(NSArray *)requestedKeys
{
if (!asset.playable)
{
NSString *localizedDescription = NSLocalizedString(#"Item cannot be played", #"Item cannot be played description");
NSString *localizedFailureReason = NSLocalizedString(#"The assets tracks were loaded, but could not be made playable.", #"Item cannot be played failure reason");
NSDictionary *errorDict = [NSDictionary dictionaryWithObjectsAndKeys:
localizedDescription, NSLocalizedDescriptionKey,
localizedFailureReason, NSLocalizedFailureReasonErrorKey,
nil];
NSError *assetCannotBePlayedError = [NSError errorWithDomain:#"StitchedStreamPlayer" code:0 userInfo:errorDict];
[self assetFailedToPrepareForPlayback:assetCannotBePlayedError];
return;
}
if (self.mPlayerItem)
{
[self.mPlayerItem removeObserver:self forKeyPath:kStatusKey];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:AVPlayerItemDidPlayToEndTimeNotification
object:self.mPlayerItem];
}
self.mPlayerItem = [AVPlayerItem playerItemWithAsset:asset];
[self.mPlayerItem addObserver:self
forKeyPath:kStatusKey
options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
context:AVPlayerDemoPlaybackViewControllerStatusObservationContext];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:self.mPlayerItem];
seekToZeroBeforePlay = NO;
if (!self.mPlayer)
{
[self setPlayer:[AVPlayer playerWithPlayerItem:self.mPlayerItem]];
[self.player addObserver:self
forKeyPath:kCurrentItemKey
options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
context:AVPlayerDemoPlaybackViewControllerCurrentItemObservationContext];
[self.player addObserver:self
forKeyPath:kRateKey
options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
context:AVPlayerDemoPlaybackViewControllerRateObservationContext];
}
if (self.player.currentItem != self.mPlayerItem)
{
[self.mPlayer replaceCurrentItemWithPlayerItem:self.mPlayerItem];
[self syncPlayPauseButtons];
}
[self.mScrubber setValue:0.0];
[self play:nil];
loading.hidden = YES;
}
NSLog gives the local URL that im using:
Im here file:///var/mobile/Containers/Data/Application/E806EC2A-2C5D-4B86-AD05-D9FD29E8FDDD/Documents/downloads/VVV%20-%20One%20Direction,%20Band%20Aid%2030,%20Mark%20Ronson,%20Bruno%20Mars,%20OneRepublic,%20DSCVR%20Ones%20To%20Watch%202015.m4v
Thanks in Advance!!
//You can use MPMoviePlayerController for Playing a Video.
Add MediaPlayer.framework (#import )
MPMoviePlayerController *movieController = [[MPMoviePlayerController alloc] init];
movieController.controlStyle = MPMovieControlStyleDefault;//MPMovieControlStyleNone;
[movieController setContentURL:#"Your local Url Path"];
[movieController.view setFrame:CGRectMake (0,0,214,186)];
[self.view addSubview:movieController.view];
[movieController.view bringSubviewToFront:self.view];
[movieController prepareToPlay];
[movieController play];

Instance of class AVPlayer was deallocated

I have a UIView that responds to a single tap gesture by playing an audio file. It does this by using the AVPlayer class. It all works well with one exception. If a user is currently listening to an audio file, and they tap the same UIView again, I want the audio file to return to the beginning and start playing again from the start. However, when this occurs, I get the following console output:
An instance 0x10ad89dd0 of class AVPlayer was deallocated while key
value observers were still registered with it. Observation info was
leaked, and may even become mistakenly attached to some other object.
The first three lines in my play method are my attempt to deal with this, but they don't solve anything. The audio does restart, but the controls (time played, sliders, etc) all go crazy. I see a couple of other posts on this, I'm still stuck. Can anyone see what I need to do to clear this problem?
- (void) playAudio : (UITapGestureRecognizer *)recognizer
{
// remove any existing observers to prevent memory leaks
[self.audioPlayer pause];
self.audioPlayer = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:self.audioPlayer];
unsigned long buttonPressed = [self.buttonsArray indexOfObject:recognizer.view];
Sessions *session = self.sessionsList[buttonPressed];
self.mediaFile = session.media_file;
self.totalSecondsToPlay = [session.play_seconds integerValue];
[self resetAVControls];
NSString *urlString = [NSString stringWithFormat:#"%#%#", AUDIO_URL, self.mediaFile];
AVPlayer *player = [[AVPlayer alloc]initWithURL:[NSURL URLWithString:urlString]];
self.audioPlayer = player;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[self.audioPlayer currentItem]];
[self.audioPlayer addObserver:self forKeyPath:#"status" options:0 context:nil];
self.isPlaying = YES;
timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(updateProgress)
userInfo:nil
repeats:YES];
}
You've registered KeyValueObservers for self.audioPlayer, but you haven't removed them when you are setting the value to nil. So before doing self.audioPlayer = nil; unsubscribe to KVO by using [self.audioPlayer removeObserver:self forKeyPath:#"status" context:nil]
Check this if you want to know more about KVO
Each time when your set your playerview to nil, before call removeObserver.
Example
[self removeObserverForStatusPlay];
playerView = nil;
And before Init
[self removeObserverForStatusPlay];
playerView = [[AVPlayer alloc] initWithURL:url];
When removeObserverForStatusPlay method is
- (void)removeObserverForStatusPlay
{
#try {
[playerView removeObserver:self forKeyPath:#"status"];
} #catch(id anException) {
NSLog(#"excepcion remove observer == %#. Remove previously or never added observer.",anException);
//do nothing, obviously it wasn't attached because an exception was thrown
}
}

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

Resources