How to automatically pair camera and microphone in QTKit - macos

I am programming a video capture application using QTKit. It is set up so that users must select a webcam from the list obtained with [QTCaptureDevice inputDevicesWithMediaType: QTMediaTypeVideo]. I want the user to be able to choose a camera, and have the corresponding microphone automatically selected, but I don't see a way to accomplish this within QTKit.
My application needs to run on OSX 10.6 to 10.8, so I can't use AVFoundation, which arrived in 10.7. QuickTime is deprecated at this point.
So the question is: On Mac OS 10.6 to 10.8, how can I automatically match a webcam camera with it's embedded microphone.
Thanks

The documented way of doing this is found in the QTCaptureDevice Class Reference,
using method attributeForKey with a key of QTCaptureDeviceLinkeDevicesAttribute, which may be called like so.
QTCaptureDevice* device = [QTCaptureDevice deviceWithUniqueID:deviceUniqueID];
QTCaptureDevice* sibling = Nil;
NSArray* linkedDevices = [device attributeForKey: QTCaptureDeviceLinkeDevicesAttribute
NSUInteger linkedCount = [linkedDevices count];
for (NSUInteger i = 0; i < linkedCount; i++)
{
sibling = [linkedDevicesobjectAtIndex: i];
.
.
.
}
However, I have not seen this work, the returned array is always Nil. Additionally This Apple Mailing List Archive suggests that it may only work for Apple iSight devices :(.
Finally, An additional sample may be found here: Apple QTRecorder Sample

Related

AppleScriptingBridge for Music app not returning sources

With the advent of Catalina OSX, iTunes has been removed from OSX, instead a Music app has been introduced, apparently similar but exclusively for music content with podcast etc.
Following code written in Objective C does not return any sources from the music, iTunes object.
Similar code worked for iTunes. but is not working for new Music app of apple.
Please advise, any experts.
MusicApplication* music = [SBApplication applicationWithBundleIdentifier:#"com.apple.music"];
NSArray *sources = [music sources];
// sources are empty, zero length array always
for (MusicSource *source in sources)
{
SBElementArray *userPlaylists = [source userPlaylists];
for(MusicUserPlaylist* playList in userPlaylists)
{ }
}

Register for global file drag events in Cocoa

I'm trying to be notified when a OS X user is dragging any file in OS X, not only in my app.
My current approach was using addGlobalMonitorForEventsMatchingMask:handler: on NSEvent, as follows:
[NSEvent addGlobalMonitorForEventsMatchingMask:NSLeftMouseDraggedMask handler:^(NSEvent* event) {
NSPasteboard* pb = [NSPasteboard pasteboardWithName:NSDragPboard];
NSLog(#"%#", [pb propertyListForType:NSFilenamesPboardType]);
}];
This works partially - the handler is being called when I start dragging a file from my desktop or Finder, however it also is being called when I perform every other operation that contains a left-mouse-drag, e.g. moving a window. The issue is that the NSDragPboard still seems to contain the latest dragged file URL e.g. when I let off the file and start moving a window, which makes it hard to distinguish between these operations.
TL;DR - I am interested in file drag operations system-wide. I do not need any information about the dragged file itself, just the information that a file drag operation has been started or stopped. I would appreciate any hint to a possible solution for this question.
After having talked to Apple DTS, this is most likely a bug. I have filed rdar://25892115 for this issue. There currently seems to be no way to solve my original question with the given API.
To solve my problem, I am now using the Accessibility API to figure out if the item below the cursor is a file (kAXFilenameAttribute is not NULL).
NSPasteboard* pb = [NSPasteboard pasteboardWithName:NSDragPboard];
NSArray* filenames = [pb propertyListForType:NSFilenamesPboardType];
NSInteger changeCount = pb.changeCount;
//when moving a window. the changeCount is not changed, use it to distinguish
if (filenames.count > 0 && self.lastChangeCount != changeCount){
self.lastChangeCount = changeCount;
//your code here
}

Change the output device for AVAudioPlayer on OSX

I need to play audio from AVAudioPlayer to a particular audio device such as a USB headset. By default the audio will get played out to the system default device setting. With QTKit, I was able to use SetMovieAudioContext() to do such a task but do not see equivalent in AVFoundation. How is it possible to change this on OSX?
Use NSSound instead:
Get list of audio devices with CoreAudio functions. Then get the devices' unique identifiers:
//...
address.mSelector = kAudioDevicePropertyDeviceUID;
CFStringRef uid = NULL;
UInt32 size = sizeof(uid);
AudioObjectGetPropertyData(deviceId, &address, 0, NULL, &size, &uid);
//...
Initialize sound item, set playbackDeviceIdentifier property value.
Opened a DTS case with Apple and they said it was not possible (as i need to support 10.8). If you only need to support 10.9 and above AVPlayerAudioDeviceSupport should work.

Notification of active document change on OS X?

I'm using NSWorkspace's NSWorkspaceDidActivateApplicationNotification to detect when the active application changes. I get NSRunningApplication from the userInfo key of the notification.
I need to get a notification when the active document changes. I can get the active document by using the accessibility framework's NSAccessibilityDocumentAttribute key through AXUIElementCopyAttributeValue().
I need a more accurate way of detecting when the document changes other than polling. Some applications use multiple windows, while others use a single window with multiple tabs. With tabbed applications the window returns the currently viewed document.
I don't have to use the accessibility framework. AppleScript (scripting bridge) seems to also be able to get a window's document, but the accessibility framework seems to work with more applications.
I only care about the active document, of the active window, of the active application. What currently has focus on the system.
I've been testing with applications like Sublime Text 2, and Xcode. Sublime returns the currently selected tab, where Xcode returns the active project.
I was actually trying to achieve exactly the same thing and I think I've found a solution for it.
What I did was using CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, kCGNullWindowID);
That will give you a list of all the active windows, including windows that you probably don't care about.
I only care about windows that have kCGWindowLayer = 0; so I filtered the windows that are on layer 0.
Here's how I did it:
CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, kCGNullWindowID);
NSMutableArray *data = [(__bridge NSArray *) windowList mutableCopy];
NSMutableArray *filteredData = [[NSMutableArray alloc] initWithCapacity:10];
for (NSMutableDictionary *theDict in data) {
id layer = [theDict objectForKey:(id)kCGWindowLayer];
if ([layer intValue] == 0) {
[filteredData addObject:theDict];
}
}
NSLog(#"window: %#", filteredData);
This might be the most elegant solution, so if anyone else has a better idea, please share. Also you should have a look at Apple's demo app Son of Grab.

Change the wallpaper on all desktops in OS X 10.7 Lion?

I would like to change the wallpaper of all desktops (formerly "spaces") on a screen. As of OS X 10.6 there is a category to NSWorkspace which allows the setting of the wallpaper, however, when I use this function only the wallpaper of the current desktop gets changed and all the other desktops remain unchanged.
I then looked at the desktop preferences plist and wrote a class that modifies it to reflect the changes I want (basically set a new image file path). After the new file was saved I sent the com.apple.desktop "BackgroundChanged" notification - Google if you don't know what I am talking about, this was how people changed wallpapers in pre 10.6 days. At first this didn't produce any result, so instead of "nil" as userInfo dictionary I sent the exact same userInfo dictionary along as Apple does when you change the wallpaper in your settings (subscribe to the notification in an app and change the wallpaper in the settings app and you will see what it looks like). Luck helped me here, when I sent the notification this way for some reason the Dock crashed and when it reloaded, it loaded the settings from the preferences file thus displaying my changes.
This works on 10.7.1, however, I would a) rather not have the bad user experience of the dock crashing and reloading, and b) use a path that is more or less guaranteed to work in future releases as well. Exploiting a bug doesn't seem like a stable path.
Any other ideas on how to change the wallpaper of all desktops? I am also unsure whether the current behaviour of the NSWorkspace wallpaper category is intended or a bug, however, judging from the behaviour of the wallpaper preferences pane it seems that the former is the case.
There is no api for setting the same wallpaper to all screens or all spaces, NSWorkspace setDesktopImageURL it is implemented as such that it only sets the wallpaper for the current space on the current screen, this is how System Preferences does it too.
Besides the volatile method of manually modifying the ~/Library/Preferences/com.apple.desktop.plist (format could change) and using notifications to reload it (crashes you experienced) what you can do is set the wallpaper to spaces as the user switches to it , e.g. look for NSWorkspaceActiveSpaceDidChangeNotification (if your application is not always running you could tell the user to switch to all spaces he wants the wallpaper to apply to) , arguably these methods are not ideal but at least they are not volatile.
-(void)setWallpaper
{
NSWorkspace *sws = [NSWorkspace sharedWorkspace];
NSURL *image = [NSURL fileURLWithPath:#"/Library/Desktop Pictures/Andromeda Galaxy.jpg"];
NSError *err = nil;
for (NSScreen *screen in [NSScreen screens]) {
NSDictionary *opt = [sws desktopImageOptionsForScreen:screen];
[sws setDesktopImageURL:image forScreen:screen options:opt error:&err];
if (err) {
NSLog(#"%#",[err localizedDescription]);
}else{
NSNumber *scr = [[screen deviceDescription] objectForKey:#"NSScreenNumber"];
NSLog(#"Set %# for space %i on screen %#",[image path],[self spaceNumber],scr);
}
}
}
-(int)spaceNumber
{
CFArrayRef windowsInSpace = CGWindowListCopyWindowInfo(kCGWindowListOptionAll | kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
for (NSMutableDictionary *thisWindow in (NSArray *)windowsInSpace) {
if ([thisWindow objectForKey:(id)kCGWindowWorkspace]){
return [[thisWindow objectForKey:(id)kCGWindowWorkspace] intValue];
}
}
return -1;
}

Resources