I am developing an audio streaming app with the old AudioStreamer from Matt and i am trying to make the interruption (when receive a call) by using :
- (void)MyAudioSessionInterruptionListener(void *inClientData, UInt32 inInterruptionState)
{
AudioStreamer *streamer = (AudioStreamer*)inClientData;
if (inInterruptionState == kAudioSessionBeginInterruption)
{
[streamer stop];
NSLog(#"kAudioSessionBeginInterruption");
}
else if (inInterruptionState == kAudioSessionEndInterruption)
{
[self playpause];
NSLog(#"kAudioSessionEndInterruption");
}
}
My problem is I am trying to call the function "playpause" with the [self playpause]; but I get an error playpause undeclared !
How can I declare playpause inside MyAudioSessionInterruptionListener ?
its not [self playPause] it should be [streamer playpause] assuming the AudioStreamer class is the class with the method...The listener method is a static C function outside your class, therefore you cant call a method on self, since self implies you are inside the instance of the class. If the class with the method is not the AudioStreamer then you are going to have to pass that class along as well in the inClientData argument in order to be able to get a hold of it..
Hope that helps
So after testing all posibility the best was using Notification.
Here the code :
void MyAudioSessionInterruptionListener(void *inClientData, UInt32 inInterruptionState)
{
if (inInterruptionState == kAudioSessionBeginInterruption) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"stopstreamer" object:nil];
NSLog(#"kAudioSessionBeginInterruption");
}
else if (inInterruptionState == kAudioSessionEndInterruption) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"TogglePlayPause" object:nil];
NSLog(#"kAudioSessionEndInterruption");
}
}
Related
I am getting EXC_BAD_ACCESS crash that occurs in the AudioToolBox. How to handle interrupts properly?
Please have a look at crashlytics screenshot for more info.
My audio streaming player was crashing when I get a phone call/ faceTime. It was actually an older class with Non ARC. Simply Add an InterruptionNotification Observer for the streaming class, and if the streaming class is playing we need to pause the player instance while interrupt begins.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleInterruptionChangeToState:) name:#"ASAudioSessionInterruptionOccuredNotification" object:nil];
- (void)handleInterruptionChangeToState:(NSNotification *)notification {
AudioQueuePropertyID inInterruptionState = (AudioQueuePropertyID) [notification.object unsignedIntValue];
if (inInterruptionState == kAudioSessionBeginInterruption){
if ([self isPlaying]) {
[self pause];
pausedByInterruption = YES; //a global Bool variable
}
}
else if (inInterruptionState == kAudioSessionEndInterruption){
AudioSessionSetActive( true );
if ([self isPaused] && pausedByInterruption) {
[self pause]; // this is actually resume
pausedByInterruption = NO;
}
}
}
Hope it helps.
I'm in the process of developing an iOS app where I need to track if the user leaves the app (presses the home button to use other apps) while they are 'in game' however the user should be able to lock and un-lock their device without this function being called.
func applicationDidEnterBackground(application: UIApplication) {
if defaults.boolForKey("TimerActive"){
defaults.setBool(true, forKey: "Failed")
}
}
This, unfortunately, is triggered when the user locks their devices as well as when they exit the app.
A little context about the app: The app encourages people to focus on their work and not become distracted by their phones for a preset time period.
Other suggestions of how I can encourage the users to reopen the app upon exit while the timer is still active but not when they lock their devices would be greatly welcomed!
Well, there's no clean way to do this. But there is a hack that you can use. It's not guaranteed to keep working though(I've tested up to iOS 9.3 and I'm pretty sure it works on the iOS 10 betas).
The idea is that there's a system-wide notification for the phone being locked. You can listen to that and coupled with listening to background/foreground events on your app you can determine what's happening.
This is a piece of code for an object that will watch for this stuff. Create it from app delegate or wherever and keep a strong ref for as long as you need it. Give it a delegate it can call for whatever events you want to observer and react to(or put the code right there in checkState). I haven't compiled this so I may have made some errors typing it up. It's derived from code I use in an app, but the original has a lot more stuff I won't post here. It's in objc but it shouldn't be too hard to convert to swift(someone feel free to post a second answer in swift or edit mine, but I don't have the time to do it right now)
#interface LockStateDetector : NSObject {
int _notify_token;
}
#property BOOL deviceIsLocked;
#property BOOL appIsInBackground;
#property NSTimer * checkStateTimer;
#end
#implementation LockStateDetector
- (instancetype)init
{
self = [super init];
if (self) {
[self registerForNotifications];
}
return self;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
notify_cancel(_notify_token);
}
- (void)registerForNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didMoveToBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
__weak__ LockStateDector * wSelf = self;
notify_register_dispatch("com.apple.springboard.lockstate", &_notify_token, dispatch_get_main_queue(), ^(int token) {
__strong__ LockStateDetector sSelf = wSelf;
if (!sSelf) {
return;
}
uint64_t state = UINT64_MAX;
notify_get_state(token, &state);
sSelf.deviceIsLocked = state != 0;
NSLog(#"device lock state changed: %#", #(state));
[sSelf checkState];
});
}
- (void)didBecomeActive
{
self.appIsInBackground = NO;
[self checkState];
}
- (void)didMoveToBackground
{
self.appIsInBackground = YES;
[self checkState];
}
- (void)checkState
{
[self.checkStateTimer invalidate];
self.checkStateTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(_checkState) userInfo:nil repeats:NO];
}
- (void)_checkState
{
[self.checkStateTimer invalidate];
self.checkStateTimer = nil;
if (!self.appIsInBackground) {
return;
}
if (!self.deviceIsLocked) {
// app is in background because device was locked
} else {
// app is in background because user pressed home and switched to something else
}
}
I have a view controller with this method:
- (void)getImages
{
if (self.myEntities != nil) {
if (self.myEntities.count > 0) {
if (self.imagesDownloader == nil) {
self.imagesDownloader = [[ImagesDownloader alloc] initWithListener];
}
}
for (MyEntity *myEntity in self.myEntities) {
if (![myEntity.imageUrl isEqualToString:#""] && (myEntity.imageUrl != nil)) {
[self.imagesDownloader getImageFromServiceUrl:myEntity.imageUrl];
}
}
}
}
And ImagesDownloader is an NSObject subclass like this:
#implementation ImagesDownloader
- (id)initWithListener
{
self = [super init];
if (self) {
[self registerNotifications];
}
return self;
}
- (void)registerNotifications
{
[[NSNotificationCenter defaultCenter] removeObserver:self
name:#"getImageDidFinish"
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(getImageDidFinish:)
name:#"getImageDidFinish"
object:nil];
}
- (void)getImageFromServiceUrl:(NSString *)imageUrl
{
GetImage *getImage = [[GetImage alloc] init];
[getImage queryServiceWithImageUrl:imageUrl];
}
// More instance methods
#end
In turn, GetImage is another NSObject subclass that calls a RESTful web service by using an NSURLConnection object, and then, in its connectionDidFinishLoading: delegate method, it posts the notification the imagesDownloader object is observing via Notification Center:
[[NSNotificationCenter defaultCenter] postNotificationName:#"getImageDidFinish"
object:nil
userInfo:serviceInfoDict];
This is the call where I sometimes get the EXC_BAD_ACCESS error.
The scenario is like this: the view controller is loaded (it is pushed into a navigation stack) and getImages is called. Right now, its self.myEntities has 3 objects, so self.imagesDownloader is initialized and call 3 times to getImageFromServiceUrl. The service is called 3 times as well, and in connectionDidFinishLoading:, the notification is posted the 3 times with no error.
But I then go back and forth the view controller, and it is loaded again and same calls made. But this time, I get the error the 2nd time the notification is going to be posted from connectionDidFinishLoading:. I don't undersatnd why, what could I be missing?
Thanks in advance
As #PhillipMills said, adding this to ImagesDownloader seems to solve the problem:
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
I have this website which plays videos, then my ios app shows this website on an UIWebview. My question is that when ios plays video from this website, which player does ios use? I want to know the answer to this question because I want to get pause notification from the player, so I want to know which kind of notification to use. Thanks
Definitely when iOS plays a video from a website page using native controller, it most probably seems to be a Native iOS player that we can show using MPMoviePlayViewController .
The reason seems to be the VOD or streaming video service must be following Apple's guideline for HLS.
Here is code to listen to StateChange notification
MPMoviePlaybackState
Constants describing the current playback state of the movie player.
enum {
MPMoviePlaybackStateStopped,
MPMoviePlaybackStatePlaying,
MPMoviePlaybackStatePaused,
MPMoviePlaybackStateInterrupted,
MPMoviePlaybackStateSeekingForward,
MPMoviePlaybackStateSeekingBackward
};
typedef NSInteger MPMoviePlaybackState;
Register for the MPMoviePlayerPlaybackStateDidChangeNotification
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(MPMoviePlayerPlaybackStateDidChange:)
name:MPMoviePlayerPlaybackStateDidChangeNotification
object:nil];
//Check in this function MPMoviePlaybackState
(void)MPMoviePlayerPlaybackStateDidChange:(NSNotification *)notification
{
if (player.playbackState == MPMoviePlaybackStatePlaying)
{ //playing
}
if (player.playbackState == MPMoviePlaybackStateStopped)
{ //stopped
}if (player.playbackState == MPMoviePlaybackStatePaused)
{ //paused
}if (player.playbackState == MPMoviePlaybackStateInterrupted)
{ //interrupted
}if (player.playbackState == MPMoviePlaybackStateSeekingForward)
{ //seeking forward
}if (player.playbackState == MPMoviePlaybackStateSeekingBackward)
{ //seeking backward
}
}
Remove notification by
[[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:nil];
Refer :MPMoviePlaybackState
I am trying to detect if the back or forward slip button is pressed in control center, but when I run the following code, it crashes if (receivedEvent.subtype == UIEventSubtypeRemoteControlNextTrack saying unrecognized selector sent to instance 0x170259890. I'm not sure what is wrong and why it isn't working.
- (void)viewDidLoad {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleNowPlayingItemChanged:) name:MPMusicPlayerControllerNowPlayingItemDidChangeNotification object:self.player];
[self.player beginGeneratingPlaybackNotifications];
}
-(void)handleNowPlayingItemChanged :(UIEvent *)receivedEvent {
NSLog(#"worked");
if (receivedEvent.subtype == UIEventSubtypeRemoteControlNextTrack) {
NSLog(#"next track");
}
}
Any time you setup a handler for a notification using NSNotificationCenter, you have to be aware that the method you provide must be setup a specific way. From the docs for addObserver:selector:name::
Selector that specifies the message the receiver sends notificationObserver to notify it of the notification posting. The method specified by notificationSelector must have one and only one argument (an instance of NSNotification).
This means that your handlePlayingItemChanged: method must be:
- (void)handleNowPlayingItemChanged:(NSNotification *)notification {
NSLog(#"worked");
MPMusicPlayerController *player = notification.object;
// get the nowPlayingItem or any other property as needed
}