QTMovie delegate methods not being called - objective-c

Hey, I'm trying to trap the QTMovie progress delegate method calls, and the delegate methods don't seem to be getting called. I'm trying to trap the conversion progress event by implementing
- (BOOL)movie:(QTMovie *)movie shouldContinueOperation:(NSString *)op withPhase:(QTMovieOperationPhase)phase atPercent:(NSNumber *)percent withAttributes:(NSDictionary *)attributes
but the method is not getting called. I've looked at apples sample code here http://developer.apple.com/library/mac/#samplecode/QTKitProgressTester/Introduction/Intro.html#//apple_ref/doc/uid/DTS10003631 and can't seem to see very much difference between my code and their code. The file gets converted fine and shows up on my desktop and I can play it without issues. I just can't get the progress events. Any ideas? Here is my demo app that I'm using to test this with.
#import "testProjAppDelegate.h"
#import <QTKit/QTKit.h>
#implementation testProjAppDelegate
#synthesize window;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
QTMovie* movie = [QTMovie movieWithFile:#"/Users/Morgan/Desktop/sample_iTunes.mov" error:nil];
if (movie)
{
[movie setDelegate:self];
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], QTMovieExport,
[NSNumber numberWithInt:kQTFileType3GPP], QTMovieExportType, nil];
[movie writeToFile:#"/Users/Morgan/Desktop/test.mp4" withAttributes:dict error:nil];
NSLog(#"DONE");
}
}
- (BOOL)movie:(QTMovie *)movie shouldContinueOperation:(NSString *)op withPhase:(QTMovieOperationPhase)phase atPercent:(NSNumber *)percent withAttributes:(NSDictionary *)attributes
{
NSLog(#"PROGRESS");
return YES;
}
#end

It appears that this is not working because my app was compiled for 64 bit. I'm assuming this is a bug in the QTKit framework? I found a few other mentions of QTMovie delegates not working in 64 bit applications. I can compile my app for 32 bit though which isn't a problem. Still, this should work in 64 bit, shouldn't it?

Related

Memory leak from looping SKVideoNode (only on actual device)

I have a memory leak that I cannot diagnose. I have tried multiple approaches to creating a seamlessly looped video - Besides AVPlayerLooper, all of the approaches I've encountered and tried involve creating an observer to watch AVPlayerItemDidPlayToEndTimeNotification and then either seeking to the beginning of the video (in the case of AVPlayer) or inserting the video to be looped into the video queue (in the case of AVQueuePlayer). Both seem to have similar performance, but both also have a consistent memory keep related to the seekToTime method (in the case of AVPlayer) and the insertItem method (in the case of AVQueuePlayer). My end goal is to create a subclass of SKVideoNode that loops by default. Below is my code for the subclass:
#import "SDLoopingVideoNode.h"
#import <AVFoundation/AVFoundation.h>
#interface SDLoopingVideoNode()
#property AVQueuePlayer *avQueuePlayer;
#property AVPlayerLooper *playerLooper;
#end
#implementation SDLoopingVideoNode
-(instancetype)initWithPathToResource:(NSString *)path withFiletype:(NSString *)filetype
{
if(self == [super init])
{
NSString *resourcePath = [[NSBundle mainBundle] pathForResource:path ofType:filetype];
NSURL *videoURL = [NSURL fileURLWithPath:resourcePath];
AVAsset *videoAsset = [AVAsset assetWithURL:videoURL];
AVPlayerItem * videoItem = [AVPlayerItem playerItemWithAsset:videoAsset];
self.avQueuePlayer = [[AVQueuePlayer alloc] initWithItems:#[videoItem]];
NSNotificationCenter *noteCenter = [NSNotificationCenter defaultCenter];
[noteCenter addObserverForName:AVPlayerItemDidPlayToEndTimeNotification
object:nil
queue:nil
usingBlock:^(NSNotification *note) {
AVPlayerItem *video = [[AVPlayerItem alloc] initWithURL:videoURL];
[self.avQueuePlayer insertItem:video afterItem:nil];
NSLog(#"Video changed");
}];
self = (SDLoopingVideoNode*)[[SKVideoNode alloc] initWithAVPlayer: self.avQueuePlayer];
return self;
}
return nil;
}
#end
And here is how the subclass is initialized in didMoveToView:
SDLoopingVideoNode *videoNode = [[SDLoopingVideoNode alloc]initWithPathToResource:#"147406" withFiletype:#"mp4"];
[videoNode setSize:CGSizeMake(self.size.width, self.size.height)];
[videoNode setAnchorPoint:CGPointMake(0.5, 0.5)];
[videoNode setPosition:CGPointMake(0, 0)];
[self addChild:videoNode];
[videoNode play];
Short answer is, you will not be able to get that working with AVPlayer. Believe me, I have tried. Instead, it is possible to do seamless looping by using the H264 hardware to decode and then re-encode each video frame as a keyframe, github link here. I have also built a seamless looping layer that supports a full alpha channel. Performance even for full screen 1x1 video on and iPad or iPad pro is great. Also, no memory leaks with this code.

How to create an event loop in Objective C?

I'm trying to use the CoreBluetooth module to list all the detected Bluetooth devices in a command-line OSX application.
What I have looks like this, so far:
#import CoreBluetooth;
#interface MyCentralManager : NSObject<CBCentralManagerDelegate>
- (void) centralManagerDidUpdateState: (CBCentralManager *) central;
- (void) centralManager:(CBCentralManager *) central
didDiscoverPeripheral:(CBPeripheral *) peripheral
advertisementData:(NSDictionary *) advertisementData
RSSI:(NSNumber *)RSSI;
#end
#implementation MyCentralManager
- (void) centralManagerDidUpdateState: (CBCentralManager *) central
{
NSLog(#"State changed...");
}
- (void) centralManager:(CBCentralManager *) central
didDiscoverPeripheral:(CBPeripheral *) peripheral
advertisementData:(NSDictionary *) advertisementData
RSSI:(NSNumber *)RSSI
{
NSLog(#"Discovered %#", peripheral.name);
}
#end
int main() {
MyCentralManager* myCentralManager = [[MyCentralManager alloc] init];
CBCentralManager* cbCentralManager = [[CBCentralManager alloc] initWithDelegate:myCentralManager queue:nil options:nil];
NSLog(#"Scanning devices now !");
[cbCentralManager scanForPeripheralsWithServices:nil options:nil];
sleep(5); // Wait 5 seconds before stopping the scan.
[cbCentralManager stopScan];
NSLog(#"Scanning devices ended.");
return 0;
}
Now this doesn't work at all as I never get any "State changed..." nor "Discovered ..." log output.
I never actually written any Objective C application before so I'm probably missing the obvious. If I had to guess what I'm doing wrong I would assume that:
I actually have to wait for the CentralManager to be in the appropriate state before starting the scan.
I never actually get inside the state changed delegate method so I assume that my first mistake is: instead of just sleep()'ing, I have to run an event loop of some sort so that the underlying system has a chance to notify me of the state change.
I'm basically stuck at this point: I don't have a GUI, nor do I want one but couldn't figure out a way to run an event loop (assuming that's actually what is missing). How can I do that ?
As I said, this is actually my first attempt with Objective C, so don't be afraid to state the obvious.
Simply run the thread's run loop
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:5]];
More information here.
BTW: You do not have to declare the methods already declared in the protocol.

Using Objective-c hidden api (iphone)

I'm trying to use this guthub According to this link for using objective c private api but the documentation is pretty lousily.
I copied past the exemple code to my xcode but I'm getting compilation error.
NSBundle *b = [NSBundle bundleWithPath:#"/System/Library/PrivateFrameworks/TelephonyUI.framework"];
BOOL success = [b load];
Class SKTelephonyController = NSClassFromString(#"SKTelephonyController");
//this line cussing the error
**id tc = [SKTelephonyController sharedInstance];**
NSLog(#"-- myPhoneNumber: %#", [tc myPhoneNumber]);
NSLog(#"-- imei: %#", [tc imei]);
error:
No known instance method for selector 'myPhoneNumber'
Can someone please have a guide or something to get started.
Oh, I know my app will not pass apple validation, I dont need there validation its an internal app.
thanks.
First of all, the example doesn't say to load SKTelephonyController, it says to load GAIA.framework
Second is that SKTelephonyController and GAIA are not available for iOS7 (they was working on iOS 6)
Here is example, how you need to dummy declare an interface, and make calls.
#interface SKTelephonyController : NSObject
+ (id)sharedInstance;
+ (NSString *)myPhoneNumber;
+ (NSString *)imei;
#end
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSBundle *b = [NSBundle bundleWithPath:#"/System/Library/PrivateFrameworks/GAIA.framework"];
BOOL success = [b load];
if (!success) {
NSLog(#"Can't load bundle");
return;
}
NSLog(#"-- imei: %#", [[SKTelephonyController sharedInstance] imei]);
}

NSFilePresenter methods never get called

I'm trying to write a simple (toy) program that uses the NSFilePresenter and NSFileCoordinator methods to watch a file for changes.
The program consists of a text view that loads a (hardcoded) text file and a button that will save the file with any changes. The idea is that I have two instances running and saving in one instance will cause the other instance to reload the changed file.
Loading and saving the file works fine but the NSFilePresenter methods are never called. It is all based around a class called FileManager which implements the NSFilePresenter protocol. The code is as follows:
Interface:
#interface FileManager : NSObject <NSFilePresenter>
#property (unsafe_unretained) IBOutlet NSTextView *textView;
- (void) saveFile;
- (void) reloadFile;
#end
Implementation:
#implementation FileManager
{
NSOperationQueue* queue;
NSURL* fileURL;
}
- (id) init {
self = [super init];
if (self) {
self->queue = [NSOperationQueue new];
self->fileURL = [NSURL URLWithString:#"/Users/Jonathan/file.txt"];
[NSFileCoordinator addFilePresenter:self];
}
return self;
}
- (NSURL*) presentedItemURL {
NSLog(#"presentedItemURL");
return self->fileURL;
}
- (NSOperationQueue*) presentedItemOperationQueue {
NSLog(#"presentedItemOperationQueue");
return self->queue;
}
- (void) saveFile {
NSFileCoordinator* coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:self];
NSError* error;
[coordinator coordinateWritingItemAtURL:self->fileURL options:NSFileCoordinatorWritingForMerging error:&error byAccessor:^(NSURL* url) {
NSString* content = [self.textView string];
[content writeToFile:[url path] atomically:YES encoding:NSUTF8StringEncoding error:NULL];
}];
}
- (void) reloadFile {
NSFileManager* fileManager = [NSFileManager defaultManager];
NSFileCoordinator* coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:self];
NSError* error;
__block NSData* content;
[coordinator coordinateReadingItemAtURL:self->fileURL options:0 error:&error byAccessor:^(NSURL* url) {
if ([fileManager fileExistsAtPath:[url path]]) {
content = [fileManager contentsAtPath:[url path]];
}
}];
dispatch_async(dispatch_get_main_queue(), ^{
[self.textView setString:[[NSString alloc] initWithData:content encoding:NSUTF8StringEncoding]];
});
}
// After this I implement *every* method in the NSFilePresenter protocol. Each one
// simply logs its method name (so I can see it has been called) and calls reloadFile
// (not the correct implementation for all of them I know, but good enough for now).
#end
Note, reloadFile is called in applicationDidFinishLaunching and saveFile gets called every time the save button is click (via the app delegate).
The only NSFilePresenter method that ever gets called (going by the logs) is presentedItemURL (which gets called four times when the program starts and loads the file and three times whenever save is clicked. Clicking save in a second instance has no noticeable effect on the first instance.
Can anyone tell me what I'm doing wrong here?
I was struggling with this exact issue for quite a while. For me, the only method that would be called was -presentedSubitemDidChangeAtURL: (I was monitoring a directory rather than a file). I opened a technical support issue with Apple, and their response was that this is a bug, and the only thing we can do right now is to do everything through -presentedSubitemDidChangeAtURL: if you're monitoring a directory. Not sure what can be done when monitoring a file.
I would encourage anyone encountering this issue to file a bug (https://bugreport.apple.com) to encourage Apple to get this problem fixed as soon as possible.
(I realize that this is an old question, but... :) )
First of all, I notice you don't have [NSFileCoordinator removeFilePresenter:self]; anywhere (it should be in dealloc).
Secondly, you wrote:
// After this I implement *every* method in the NSFilePresenter protocol. Each one
// simply logs its method name (so I can see it has been called) and calls reloadFile
// (not the correct implementation for all of them I know, but good enough for now).
You're right: it's the incorrect implementation! And you're wrong: it's not good enough, because it's essential for methods like accommodatePresentedItemDeletionWithCompletionHandler: which take a completion block as a parameter, that you actually call this completion block whenever you implement them, e.g.
- (void) savePresentedItemChangesWithCompletionHandler:(void (^)(NSError * _Nullable))completionHandler
{
// implement your save routine here, but only if you need to!
if ( dataHasChanged ) [self save]; // <-- meta code
//
NSError * err = nil; // <-- = no error, in this simple implementation
completionHandler(err); // <-- essential!
}
I don't know whether this is the reason your protocol methods are not being called, but it's certainly a place to start. Well, assuming you haven't already worked out what was wrong in the past three years! :-)

How do I trap OCUnit test pass/failure messages/events

I'm trying to use xcodebuild and OCUnit with my Continuous Integration server (TeamCity).
JetBrains offers test observer implementations for boost::test and CppUnit that format test output in a way that TeamCity can interpret. I need to do something similar for OCUnit if I want to use it.
There appears to be a SenTestObserver class in OCUnit but I'm ignorant of how exactly it should be used, and the OCUnit homepage doesn't seem to provide any documentation on the matter.
You can write your own observer by extending the SenTestObserver class and implementing the notification listeners
(void) testSuiteDidStart:(NSNotification *) aNotification
(void) testSuiteDidStop:(NSNotification *) aNotification
(void) testCaseDidStart:(NSNotification *) aNotification
(void) testCaseDidStop:(NSNotification *) aNotification
(void) testCaseDidFail:(NSNotification *) aNotification
then add an entry to the info.plist SenTestObserverClass with the name of your class.
At least in the version of OCUnit i'm familiar with SenTestObserver is equal parts useful/equal parts broken. I just skip it altogether and register for the notifications myself in my own class. (see SenTestSuiteRun.h and SenTestCaseRun.h for the defines of the notification names).
You can use the test and run properties of the notification to access the SenTestSuite and SenTestSuiteRun instances, and the run instance contains the info needed on the actual results.
I have implemented a simple Teamcity Adapter, you can view the gist here. SenTestObserver isn't exactly broken, it simply doesn't adhere to the best practices:
This is what you need to call in your Observer subclass to have it properly registered:
+(void)initialize
{
[[NSUserDefaults standardUserDefaults] setValue:#"TeamCityAdapter" forKey:#"SenTestObserverClass"];
// we need to force SenTestObserver to register us as a handler
// SenTestObserver is properly guarding against this invocation so nothing bad will hapen
// but this is required (bad design on SenTestObserver's side)...
[super initialize];
}
because SenTestObserver's initialize looks like this:
+ (void) initialize
{
if ([self class] == [SenTestObserver class]) {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *registeredDefaults = [NSDictionary dictionaryWithObjectsAndKeys:
#"SenTestLog" , #"SenTestObserverClass",
nil];
[defaults registerDefaults:registeredDefaults];
[NSClassFromString ([defaults objectForKey:#"SenTestObserverClass"]) class]; // make sure default observer is loaded
}
if ([[[NSUserDefaults standardUserDefaults] objectForKey:#"SenTestObserverClass"] isEqualToString:NSStringFromClass(self)]) {
[self setCurrentObserver:self];
}
}
I hope this will help others out there looking for a teamcity adapter for OCUnit / SenTestingKit.