Using Audio Queue for my iOS App, I have some problem with a test on iOS6.1, though it has worked fine on iOS6.
The problem is AudioQueueStop and AudioQueueDispose don't return immediately, or sometimes they crash.
Like this:
if (_audioQueue)
{
auto err = AudioQueueStop(_audioQueue, true); // Some delay before return
for (int i = 0; i < kNumberAudioQueueBuffers; i++) {
AudioQueueFreeBuffer(_audioQueue, _audioQueueBuffer[i]);
}
err = AudioQueueDispose(_audioQueue, true); // This also has delay
_audioQueue = nil;
}
This isn't called on main thread but another thread, but other stuffs such as AudioQueueNewoutput and AudioQueueStart also called on that thread.
Actually I tried to run a simple app which uses AudioQueue as a test and in that case it worked fine (on both of iOS6 and 6.1). So other parts in my codes might affect but I couldn't figure out.
Is there anyone who had similar problems and hopefully fixed?
I'm seeing similar issues with AudioQueueSetProperty() and kAudioQueueProperty_MagicCookie. My app crashes every single time this is called, when it worked fine on iOS 6.0 and earlier. I'm thinking Apple messed up the audio queue implementation in 6.1.
Related
I have a watch app with complications. Updating the complication on a watch face did work for a long time, but stopped recently, maybe due to a watchOS update.
The reason is that the activeComplications property of the CLKComplicationServer.sharedInstance() is nil, although my complication placeholder is shown on the watch face (device & simulator).
The code could not be simpler:
final class ComplicationController: NSObject, CLKComplicationDataSource {
// …
func updateComplications() {
//…
let complicationServer = CLKComplicationServer.sharedInstance()
if let activeComplications = complicationServer.activeComplications {
for complication in activeComplications {
complicationServer.reloadTimeline(for: complication)
}
}
//…
}
//…
}
If I stop at a breakpoint at the if let instruction, complicationServer has the following values:
And the following lldb command outputs nil:
What could be the reason?
My bad: I solved the problem 4 years ago, but forgot the solution during refactoring of the app.
Actually I don’t know if this is a solution, a workaround or a hack:
I suspect that the CLKComplicationServer or its CLKComplicationDataSource, i.e. the ComplicationController, is not correctly initialized if ComplicationController.shared is executed anywhere in the code. If not, the ComplicationController is correctly initialized by the CLKComplicationServer.
Therefore, one cannot call any function in the ComplicationController, e.g. to update complications. Instead one can send a notification to the ComplicationController that executes the requested function. Of course, one has to ensure that the ComplicationController is already initialized and registered to receive such a notification before it is posted.
If so, CLKComplicationServer.sharedInstance().activeComplications is no longer nil, and the complication update works.
Environment
OS-X 10.10
xcode 6.4
C++/Obj-C OS-X application
Use-case
Capture video using CoreMediaIO, capture source is iPod5
Capturing machine is OS-X Yosemite
Capture feed consists of Video and Audio samples
Problem description
While video capture is working fine, when video samples are received, there is an accumulating memory leak, when no video samples are received ( only audio ), there is no leak ( memory consumption stops growing )
I am mixing Cocoa thread and POSIX threads, I have made sure to have [NSThread isMultiThreaded] set to YES ( by creating an empty NSThread )
"Obj-C Auto Ref Counting" is set to YES in the project properties
The following is a short code-snap of the code causing the leak:
OSStatus status = 0;
#autoreleasepool {
m_udid = udid;
if (0 != (status = Utils::CoreMediaIO::FindDeviceByUDID(m_udid, m_devId)))
return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
if (0 != (status = Utils::CoreMediaIO::GetStreamByIndex(m_devId, kCMIODevicePropertyScopeInput, 0, m_strmID)))
return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
status = Utils::CoreMediaIO::SetPtopertyData(m_devId, kCMIODevicePropertyExcludeNonDALAccess, 1U);
status = Utils::CoreMediaIO::SetPtopertyData<int>(m_devId, kCMIODevicePropertyDeviceMaster, getpid());// Exclusive access for the calling process
// Results in an infinitely accumulating memory leak
status = CMIOStreamCopyBufferQueue(m_strmID, [](CMIOStreamID streamID, void* token, void* refCon) {
#autoreleasepool {
CMSampleBufferRef sampleBuffer;
while(0 != (sampleBuffer = (CMSampleBufferRef)CMSimpleQueueDequeue(m_queueRef))) {
CFRelease(sampleBuffer);
sampleBuffer = 0;
}
}
}, this, &m_queueRef);
if(noErr != status)
return E_FAIL;
if(noErr != (status = CMIODeviceStartStream(m_devId, m_strmID)))
return E_FAIL;
}
Having sample de-queuing done in the main thread ( using 'dispatch_async(dispatch_get_main_queue(), ^{' ) didn't have any affect...
Is there anything wrong with the above code snap? might this be an OS Bug?
Reference link: https://forums.developer.apple.com/message/46752#46752
AN UPDATE
The QuickTime player support using an iOS device as a capture source ( mirroring it's A/V to the mac machine ), having a preview session running for a while reproduce the above mentioned problem w/ the OS provided QuickTime player, this strongly indicate an OS Bug, bellow is a screen-shot showing the QT player taking 140Mb of RAM after running for ~2hours ( where it starts around 20Mb ), by the end of the day it has grown to ~760Mb...
APPLE Please have this Fixed, I have standing customers commitments...
I am developing a network monitor app that runs in background as a service. Is it possible to get a notification/call when the screen is turned on or off?
It exists in Android by using the following code:
private void registerScreenOnOffReceiver()
{
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
registerReceiver(screenOnOffReceiver, filter);
}
screenOnOffReceiver is then called when screen is turned on/off. Is there a similar solution for iOS?
Edit:
The best I've found so far is UIApplicationProtectedDataWillBecomeUnavailable ( Detect if iPhone screen is on/off ) but it require the user to enable Data Protection (password protection) on the device.
You can use Darwin notifications, to listen for the events. I'm not 100% sure, but it looks to me, from running on a jailbroken iOS 5.0.1 iPhone 4, that one of these events might be what you need:
com.apple.iokit.hid.displayStatus
com.apple.springboard.hasBlankedScreen
com.apple.springboard.lockstate
Update: also, the following notification is posted when the phone locks (but not when it unlocks):
com.apple.springboard.lockcomplete
To use this, register for the event like this (this registers for just one event, but if that doesn't work for you, try the others):
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
NULL, // observer
displayStatusChanged, // callback
CFSTR("com.apple.iokit.hid.displayStatus"), // event name
NULL, // object
CFNotificationSuspensionBehaviorDeliverImmediately);
where displayStatusChanged is your event callback:
static void displayStatusChanged(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) {
NSLog(#"event received!");
// you might try inspecting the `userInfo` dictionary, to see
// if it contains any useful info
if (userInfo != nil) {
CFShow(userInfo);
}
}
If you really want this code to run in the background as a service, and you're jailbroken, I would recommend looking into iOS Launch Daemons. As opposed to an app that you simply let run in the background, a launch daemon can start automatically after a reboot, and you don't have to worry about iOS rules for apps running tasks in the background.
Let us know how this works!
Using the lower-level notify API you can query the lockstate when a notification is received:
#import <notify.h>
int notify_token;
notify_register_dispatch("com.apple.springboard.lockstate", ¬ify_token, dispatch_get_main_queue(), ^(int token) {
uint64_t state = UINT64_MAX;
notify_get_state(token, &state);
NSLog(#"com.apple.springboard.lockstate = %llu", state);
});
Of course your app will have to start a UIBackgroundTask in order to get the notifications, which limits the usefulness of this technique due to the limited runtime allowed by iOS.
While iPhone screen is locked appdelegate method
"- (void)applicationWillResignActive:(UIApplication *)application"
will be called you can check that. Hope it may help you.
I'm playing game sounds using OpenAL, and bg music using standard AV. Recently i've found that after the
incoming call all openal sounds don't work while bg music is still playing. If I force stop app and start again
sounds appear again. Do smbd happen to know what's happening to openal during/after the incoming call?
Ok, it seems I've found a solution.
I'm using obj-c sound manager, so I just added beginInterruption and endInterruption delegate methods of AVAudioSession (and AVAudioPlayer) to my class.
beginInterruption looks like:
alcMakeContextCurrent(NULL);
and endInterruption looks something like:
NSError * audioSessionError = NULL;
[audioSession setCategory:soundCategory error:&audioSessionError];
if (audioSessionError)
{
Log(#"ERROR - SoundManager: Unable to set the audio session category");
return;
}
// Set the audio session state to true and report any errors
audioSessionError = NULL;
[audioSession setActive:YES error:&audioSessionError];
if (audioSessionError)
{
Log(#"ERROR - SoundManager: Unable to set the audio session state to YES with error %d.", (int) result);
return;
}
//music players handling
bool plays = false;
if (musicPlayer[currentPlayer] != nil)
plays = [musicPlayer[currentPlayer] isPlaying];
if (musicPlayer[currentPlayer] != nil && !plays)
[musicPlayer[currentPlayer] play];
alcMakeContextCurrent(context);
Yes, this works if you're using only openAL sounds. But to play long tracks you should use AVAudioPlayer.
But here's the Apple magic again! If you play music along with OpenAL sounds something odd happens.
Cancel the incoming call and AVAudioSessionDelegate::endInterruption with AVAudioPlayerDelegate::audioPlayerEndInterruption will never called. Only beginInterruption, not the end.
Even AppDelegate::applicationWillEnterForeground will not be called, and app just don't know that we've returned.
But the good news is that you can call your endInterruption in AppDelegate::applicationDidBecomeActive method, and openAL context will be restored. And this works!
- (void)applicationDidBecomeActive:(UIApplication *)application
{
if (MySoundMngr != nil)
{
[MySoundMngr endInterruption];
}
// Restart any tasks that were paused and so on....
}
I had a hard time figuring this out so wanted to add my answer here. This is all specifically in Xamarin, but I suspect it applies generally and is similar to #Tertium's answer
You can prevent iOS from interrupting your audio in some situations (e.g., getting a phone call but declining it), using AVAudioSession.SharedInstance().SetPrefersNoInterruptionsFromSystemAlerts(true, out NSError err);
You will still be interrupted in some situations (e.g., you accept a phone call). To catch these you must AVAudioSession.Notifications.ObserveInteruption(myAudioInterruptionHandler); when you launch your app.
Inside this handler, you can determine if you're shutting down or coming back like so:
void myAudioInterruptionHandler(object sender, AVAudioSessionInterruptionEventArgs args) {
args.Notification.UserInfo.TryGetValue(
new NSString("AVAudioSessionInterruptionTypeKey"),
out NSObject typeKey
);
bool isBeginningInterruption = (typeKey.ToString() == "1");
// ...
}
Interruption Begins
When the interruption begins, stop whatever audio is playing (you'll need to handle this on your own based on your app, but probably by calling AL.SourceStop on everything).
Then, critically,
ContextHandle audioContextHandle = Alc.GetCurrentContext();
Alc.MakeContextCurrent(ContextHandle.Zero);
If you don't do this right away, iOS will fry your ALC context and you are doomed. Note that if you have a handler for AudioRouteChanged this is too late, you must do it in the AudioInterruption handler.
Interruption Ends
When you're coming back from the interruption, first reboot your iOS audio session:
AVAudioSession.SharedInstance().SetActive(true);
You may also need to reset your preferred input (I think this step is optional if you always use the default input) AVAudioSession.SharedInstance().SetPreferredInput(Input, out NSError err)
Then restore your context
Alc.MakeContextCurrent(audioContextHandle);
For debugging purposes, I'd like to access console printouts at runtime in a way similar to the Console app current on the App Store (that can be found here).
I did some searching of the docs and I can't find anything that's provided by Apple, but I feel like I'm missing something important. Any insight?
Thanks.
You can do so using <asl.h>. Here is an example that I threw together to create an array of console messages.
-(NSArray*)console
{
NSMutableArray *consoleLog = [NSMutableArray array];
aslclient client = asl_open(NULL, NULL, ASL_OPT_STDERR);
aslmsg query = asl_new(ASL_TYPE_QUERY);
asl_set_query(query, ASL_KEY_MSG, NULL, ASL_QUERY_OP_NOT_EQUAL);
aslresponse response = asl_search(client, query);
asl_free(query);
aslmsg message;
while((message = asl_next(response)) != NULL)
{
const char *msg = asl_get(message, ASL_KEY_MSG);
[consoleLog addObject:[NSString stringWithCString:msg encoding:NSUTF8StringEncoding]];
}
if (message != NULL) {
asl_free(message);
}
asl_free(response);
asl_close(client);
return consoleLog;
}
If your device is attached to Xcode, you can see console output (NSLogs and such) in the debug area:
If you're running the app and connecting to Xcode later, I believe you can get console logs in the Organizer.
Edit: to access the log file at runtime, you should try /var/log/system.log — but even better I recommend using a custom debug function, which would write to the system log and/or a text view in your app. (Check out NSLogv, which will be useful when writing a wrapper function.) This also has the advantage of letting you disable all debug logs from one place (just change your debug function).