Cocoa QTKit and recording movies - objective-c

I'm new with the whole QTKit and I was looking for some feedback on the following code that I am attempting to use to display the camera's image and record movies.
- (void)initializeMovie {
NSLog(#"Hi!");
QTCaptureSession* mainSession = [[QTCaptureSession alloc] init];
QTCaptureDevice* deviceVideo = [QTCaptureDevice defaultInputDeviceWithMediaType:#"QTMediaTypeVideo"];
QTCaptureDevice* deviceAudio = [QTCaptureDevice defaultInputDeviceWithMediaType:#"QTMediaTypeSound"];
NSError* error;
[deviceVideo open:&error];
[deviceAudio open:&error];
QTCaptureDeviceInput* video = [QTCaptureDeviceInput deviceInputWithDevice:deviceVideo];
QTCaptureDeviceInput* audio = [QTCaptureDeviceInput deviceInputWithDevice:deviceAudio];
[mainSession addInput:video error:&error];
[mainSession addInput:audio error:&error];
QTCaptureMovieFileOutput* output = [[QTCaptureMovieFileOutput alloc] init];
[output recordToOutputFileURL:[NSURL URLWithString:#"Users/chasemeadors/Desktop/capture1.mov"]];
[mainSession addOutput:output error:&error];
[movieView setCaptureSession:mainSession];
[mainSession startRunning];
}
Also, I'm not sure about the whole error parameter that the methods keep calling for, I saw the "&error" symbol in an example but I don't know what it means.
I'm also getting an error "cannot initialize a device that is not open" when I explicitly open the devices.
If anyone could help me sort this out, it'd be a great help, thanks.

QTCaptureDevice* deviceVideo = [QTCaptureDevice defaultInputDeviceWithMediaType:#"QTMediaTypeVideo"];
QTCaptureDevice* deviceAudio = [QTCaptureDevice defaultInputDeviceWithMediaType:#"QTMediaTypeSound"];
Pass the actual constants here, not string literals containing their names. There's no guarantee that QTMediaTypeVideo is defined to #"QTMediaTypeVideo"; it could be #"Ollie ollie oxen free", and even if it is what you expect now, it could change at any time.
[output recordToOutputFileURL:[NSURL URLWithString:#"Users/chasemeadors/Desktop/capture1.mov"]];
Don't assume that the current working directory is /. Always use absolute paths. (I know this is test code; in real code, of course, you would have run an NSSavePanel and gotten the path from there.)
Also, I'm not sure about the whole error parameter that the methods keep calling for, I saw the "&error" symbol in an example but I don't know what it means.
The & means you're taking the address of a variable, which in this case is error. You're passing this address (a.k.a. pointer) to the error: argument of one of QTKit's methods. The method will, if it encounters an error, create an NSError object and store it at that address—i.e., in your variable. This is called “return-by-reference” (the “reference” being the pointer you provided).
I'm also getting an error "cannot initialize a device that is not open" when I explicitly open the devices.
Which method returns the error? Are you talking about an NSError, or just a Console message? If the latter, check your NSError variable and see what the problem method left behind.
This, incidentally, is why you should bail out if any of the QTKit methods returns an error: one of the subsequent messages may clobber it with a new error if you don't.

Also, you may want to look at the MyRecorder sample code. It's a fully functional video recorder based on the QTKit Capture API. The code is reasonably simple and should be easy to understand.

Related

Indicate an error condition in an ObjC call when return value is needed for something else

What is the correct way in Objective-C to indicate an error condition when the return value is needed to indicate success or failure?
To be more clear, my return value is already set apart for indicating whether the method accomplishes a certain task, and it is handy for code readability when program-flow is concerned. For example if you implement:
-(bool) didFinishTest;
you can go on to write:
if ([Student didFinishTest])
{
// ... do something
}
But of course some kind of failure could occur during the execution of the method. In the Android version of my app, I simply throw a custom exception when an unexpected failure occurs in the implementation. Apple docs however seem to discourage this use in production code. Yet in Apple's suggested use of NSError they recommend using the return value to indicate the presence of failure and the NSError object convey information about the error. In the example above, this would prevent me from using the return value to indicate whether the student finished the test or not.
I can of course figure a way to address this, but my question to the community here is, Is there a best or recommended way of doing so, and if so what is it?
You can add a reference to NSError pointer, to add description of failure.
The Apple "Programming With Objective-C" guide says:
Some Cocoa and Cocoa Touch API pass back errors by reference. [...]
- (BOOL)writeToURL:(NSURL *)aURL
options:(NSDataWritingOptions)mask
error:(NSError **)errorPtr;
Before you call this method, you’ll need to create a suitable pointer so that you can pass its address:
NSError *anyError;
BOOL success = [receivedData writeToURL:someLocalFileURL
options:0
error:&anyError];
if (!success) {
NSLog(#"Write failed with error: %#", anyError);
// present error to user
}
if an error occurs, the writeToURL:... method will return NO, and update your anyError pointer to point to an error object describing the problem.
When dealing with errors passed by reference, it’s important to test
the return value of the method to see whether an error occurred, as
shown above. Don’t just test to see whether the error pointer was set
to point to an error.
Within the called method set the returned NSError to nil on success, to an error object on failure. In the caller test the error return against nil. HTH

NSKeyedUnarchiver cannot decode object of class NSKnownKeysDictionary1

I'm getting the following exception when trying to unarchive when using Swift:
Terminating app due to uncaught exception 'NSInvalidUnarchiveOperationException', reason: '*** -[NSKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (NSKnownKeysDictionary1) for key (NS.objects); the class may be defined in source code or a library that is not linked'
The context: I'm creating a "Share Links" extension. In my main app (written in Objective C) I write out an array of dictionaries with the information about the links using MMWormhole.
NSFetchRequest* bookmarkFetch = [NSFetchRequest fetchRequestWithEntityName:#"XX"];
bookmarkFetch.propertiesToFetch = #[
#"name", #"text", #"date", #"url"
];
bookmarkFetch.resultType = NSDictionaryResultType;
NSArray* bookmarks = [moc executeFetchRequest:bookmarkFetch error:NULL];
[wormhole passMessageObject:bookmarks identifier:#"XXX"];
The values in the array are NSStrings and an NSDate.
In the bowels of MMWormhole you get:
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:messageObject];
messageObject is just the bookmarks array without any intermediate processing.
In the extension I have:
let wormhole = MMWormhole(applicationGroupIdentifier: "group.XX", optionalDirectory:nil)
let bookmarks = wormhole.messageWithIdentifier("XXX") as? Array<Dictionary<String,AnyObject>>
messageWithIdentifier: ends up calling this ultimately:
id messageObject = [NSKeyedUnarchiver unarchiveObjectWithData:data];
The array is written out to the app group folder correctly -- I can read it using another extension, one written in Objective C.
This exception appears when I run in the Simulator. The code appears to work correctly when run on a 32-bit device (iPhone 5 and iPad 3). I don't have a 64-bit device to test on currently.
I imagine I'm missing an import or a framework, but which one(s)?
This is just a side note:
You can set class names for both the NSKeyedArchiver & NSKeyedUnarchiver.
I had this problem without dealing with CoreData at all. The unarchiver did not find my own class anymore.
Setting the className for my class works as shown below:
For the archiver:
NSKeyedArchiver.setClassName("MyClass", for: MyClass.self)
let data = NSKeyedArchiver.archivedData(withRootObject: root)
And the unarchiver:
NSKeyedUnarchiver.setClass(MyClass.self, forClassName: "MyClass")
let root = NSKeyedUnarchiver.unarchiveObject(with: data)
I asked this on the Apple Developer forums and (for once) got a good answer from Apple Developer Relations. I'll quote the important bits:
NSKnownKeysDictionary1 is a Core Data voodoo that I do not understand.
... Clearly something is going wrong with its serialisation and
deserialisation. Do you have Core Data up and running on both ends of
the wormhole? Regardless, it might make more sense to do a deep copy
of your bookmarks array (and anything else you get back from Core
Data) so that you’re sending standard dictionaries across the ‘wire’
rather than Core Data stuff.
So my solution is either to add the Core Data framework to the extension or to do the deep copy. (I've temporarily done the former. The proper solution is probably the latter.)

How to use ios 6 challenge in game centre

Firstly,
I am fairly new to objective c / xcode dev so there is a good chance i am being a muppet. I have written a few simple apps to try things and my most recent one has been testing the gamecentre classes / functionality.
i have linked ok to leaderboards and achievements - but i can't get challenges working.
I have added the following code.... which is in my .m
GKLeaderboard *query = [[GKLeaderboard alloc] init];
query.category = LoadLeaderboard;
query.playerScope = GKLeaderboardPlayerScopeFriendsOnly;
query.range = NSMakeRange(1,100);
[query loadScoresWithCompletionHandler:^(NSArray *scores, NSError *error)
{NSPredicate *filter = [NSPredicate predicateWithFormat:#"value < %qi", scoreint];
NSArray *lesserScores = [scores filteredArrayUsingPredicate:filter];
[self presentChallengeWithPreselectedScores: lesserScores];
}
];
this code is basically taken from apple, just replacing the variable names....
this however gives an error on
[self presentChallengeWithPreselectedScores: lesserScores];
error Implicit conversion of an Objective-C pointer to 'int64_t *' (aka 'long long *') is disallowed with ARC
LoadLeaderboard is defined as a string
scoreint is defined as integer, thought this may be issue as not int64_t but that does not seem to make a difference.
I am sure for someone who has any kind of a clue this is a straightforward fix. But i am struggling at the moment. So if anyone can be kind and help a fool in need it would be most appreciated
Thanks,
Matt
welcome to Stack Overflow. I don't know your implementation of presentChallengeWithPreselectedScores method so I can't tell (although it looks like the method is taking a 64 bit integer and you're trying to feed it an array).
There are two ways to issue challenges:
1 - This is the easier way - if you've successfully implemented leader boards and score posting to game center, the challenges work out of the box in iOS6, the user can always view the leader board - select a submitted score (or a completed achievement) and select "Challenge Friend".
2 - The second way is to build a friend picker and let the user issue challenges within your game. But considering you're new to objective-c and game center, it's not so easy. But for your reference here is how you do it:
when you submit a GKScore object for the leaderboards - you can retain and use that GKScore object (call it myScoreObject) like this:
[myScoreObject issueChallengeToPlayers:selectedFriends message:yourMessage];
where selectedFriends is an NSArray (the friend picker should generate this) - the message is optional and can be used only if you want to send a message to challenged friends.

Array, memory management

I have some code where I implemented undo functionality, the undo function is as follows:
- (void) undo
{
drawImage.image = pathArray.lastObject;
[pathArray removeLastObject];
}
Then I have one more function to capture the current image from the screen; in this function I am getting EXC_Bad_ACCESS error on this
[pathArray removeLastObjectatIndex:0];
No other calls of functions are made in this part of program. Maybe there could be a problem of moving whole array, but I don't want to use undo manager.
Is there any better way, to get the last object of an array and then remove the first one and move whole array by one?
Notice : path array is MutableArray *patharray;
Thanks all!=)
I just don't have and idea how to solve this. I didn't find any solution in the official docs.
Ultimately, this program would not likely be reachable -- It implies there is nothing to 'undo' in many contexts, and the option should not be given. Your problem often ultimately lies upstream.
If that is not quite the case here, here is one approach:
- (void)undo
{
if (0 == self.pathArray.count) {
self.drawImage.image = nil;
return;
}
self.drawImage.image = self.pathArray.lastObject;
[self.pathArray removeLastObject];
}
As well, [pathArray removeLastObjectatIndex:]; is not a real selector.
Update
See my answer here to enable and find zombies, and locate the reference count offset quickly. Your program should never message a zombie.

Turn id back into a reference object in AppScript objective-c

I'm writing an app that talks to Mail using Objective-C-appscript (objc-appscript). I want to make a copy of the currently selected mail messages and perform some processing on it at a later -- at which time the current selection may have changed.
MLApplication *mail = [[MLApplication alloc] initWithBundleID: #"com.apple.mail"];
MLReference *ref = [mail selection];
id theSelection = [[ref getItem] copy];
// Do something here, which may change the contents of ref,
// but that's okay since I made a copy in theSelection
MLMoveCommand *cmd = [[theSelection move] to: [[mail mailboxes] byName:#"test"]];
// This command should move the selected messages to the mailbox but fails
// because theSelection
MLReference *ref2 = nil; // Need to turn theSelection into an MLReference *
MLMoveCommand *cmd = [[ref2 move] to: [[mail mailboxes] byName:#"test"]];
I need to turn theSelection back into an MLReference *. I'm sure this should be a simple operation, but I am new to appscript and require some guidance. Thanks!
You can always cast theSelection to whatever type you want it to be. You can also query it and find out what type it thinks it is using the class method. You probably don't have to do this though.
For example,
NSString *something = [(MLReference *)theSelection someFuncIMadeUp];
You can read all about the runtime stuff (like the class method) in the apple doc:
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html
You're assuming Apple event IPC uses proxy objects like Distributed Objects, but that's not the case: it's RPC + queries. (Think XPath over XML-RPC as a rough analogy.) It's a common misconception - Apple themselves totally fail at explaining it - but grasping Apple events' query-based nature is essential to controlling scriptable apps effectively.
Anyway, here's where you're going wrong:
id theSelection = [[ref getItem] copy];
This line copies an MLReference object identifying Mail's selection property, but as a reference is basically analogous to a URL that's effectively a no-op.
MLMoveCommand *cmd = [[theSelection move] to: [[mail mailboxes] byName:#"test"]];
This line tells Mail to move the object(s) it finds at the referenced location. That command may or may not work depending on how capable Mail's scripting support is (some apps may be capable of manipulating multiple objects with a single command; others are limited to a single object per command). But even if it does work, it will be operating on whatever's selected at the time the command is sent - which isn't what you're after.
The correct solution in this case is to use a get command to retrieve a list of references (in this case, an NSArray of MLReference instances), which you can later iterate over to move each of the referenced messages in turn. Fortunately, the references that Mail returns identify messages by id, which means that they should continue pointing to the original message objects even if they're moved in the meantime. (By-index and by-name references are much less stable, so you need to be a lot more careful working with apps that use those.)
For example (error checking omitted for clarity):
MLApplication *mail = [MLApplication applicationWithBundleID: #"com.apple.mail"];
NSArray *messageRefs = [[mail selection] getItem];
// do other stuff here
MLReference *message;
for (message in messageRefs) {
MLMoveCommand *cmd = [[mail move: message] to: [[mail mailboxes] byName: #"test"]];
id result = [cmd send];
}
See the appscript manual for more information. Also, ASTranslate is your friend.