Skip to content
Browse files

Record video with QTKit.

  • Loading branch information...
1 parent 7b85220 commit 64952de86f406818f75e232e5d1e04093e769939 Jim Puls committed Jun 2, 2011
Showing with 79 additions and 21 deletions.
  1. +6 −1 Simulator.h
  2. +53 −3 Simulator.m
  3. +12 −17 WaxSim.m
  4. +8 −0 WaxSim.xcodeproj/project.pbxproj
View
7 Simulator.h
@@ -17,15 +17,20 @@
NSNumber *_family;
DTiPhoneSimulatorSession* _session;
NSDictionary *_env;
+ NSString *_videoPath;
NSArray *_args;
+ CGWindowID _windowID;
+ QTMovie *_movie;
+ NSTimeInterval _lastInterval;
}
@property (nonatomic, readonly) DTiPhoneSimulatorSession* session;
+ (NSArray *)availableSDKs;
-- (id)initWithAppPath:(NSString *)appPath sdk:(NSString *)sdk family:(NSString *)family env:(NSDictionary *)env args:(NSArray *)args;
+- (id)initWithAppPath:(NSString *)appPath sdk:(NSString *)sdk family:(NSString *)family video:(NSString *)videoPath env:(NSDictionary *)env args:(NSArray *)args;
- (int)launch;
+- (void)addScreenshotToMovie;
- (void)end;
@end
View
56 Simulator.m
@@ -7,6 +7,7 @@
//
#import "Simulator.h"
+#import <QTKit/QTKit.h>
#include <sys/param.h>
#include <objc/runtime.h>
@@ -18,7 +19,8 @@ @implementation Simulator
@synthesize session=_session;
-- (id)initWithAppPath:(NSString *)appPath sdk:(NSString *)sdk family:(NSString *)family env:(NSDictionary *)env args:(NSArray *)args {
+- (id)initWithAppPath:(NSString *)appPath sdk:(NSString *)sdk family:(NSString *)family video:(NSString *)videoPath env:(NSDictionary *)env args:(NSArray *)args;
+{
self = [super init];
NSFileManager *fileManager = [NSFileManager defaultManager];
@@ -56,6 +58,7 @@ - (id)initWithAppPath:(NSString *)appPath sdk:(NSString *)sdk family:(NSString *
_env = [env retain];
_args = [args retain];
+ _videoPath = [videoPath retain];
return self;
}
@@ -87,7 +90,7 @@ - (int)launch {
[config setSimulatedApplicationShouldWaitForDebugger:NO];
[config setSimulatedApplicationLaunchArgs:_args];
[config setSimulatedApplicationLaunchEnvironment:_env];
- [config setLocalizedClientName:@"iCuke"];
+ [config setLocalizedClientName:@"WaxSim"];
// Make the simulator output to the current STDERR
// We mix them together to avoid buffering issues on STDOUT
@@ -99,7 +102,6 @@ - (int)launch {
_session = [[DTiPhoneSimulatorSession alloc] init];
[_session setDelegate:self];
- [_session setSimulatedApplicationPID:[NSNumber numberWithInt:35]];
NSError *error;
if (![_session requestStartWithConfig:config timeout:30 error:&error]) {
@@ -114,16 +116,64 @@ - (void)end {
[_session requestEndWithTimeout:0];
}
+- (void)addScreenshotToMovie;
+{
+ if (!_windowID || !_movie) {
+ return;
+ }
+
+ NSTimeInterval interval = [NSDate timeIntervalSinceReferenceDate];
+ QTTime duration = QTMakeTimeWithTimeInterval(interval - _lastInterval);
+ _lastInterval = interval;
+
+ CGImageRef imageRef = CGWindowListCreateImage(CGRectNull, kCGWindowListOptionIncludingWindow, _windowID, kCGWindowImageDefault);
+ NSImage *image = [[NSImage alloc] initWithCGImage:imageRef size:NSZeroSize];
+
+ if ([image size].width > 5.0f) {
+ NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:@"mp4v", QTAddImageCodecType, [NSNumber numberWithLong:codecLowQuality], QTAddImageCodecQuality, nil];
+ [_movie addImage:image forDuration:duration withAttributes:attributes];
+ }
+ [image release];
+ CGImageRelease(imageRef);
+}
+
// DTiPhoneSimulatorSession Delegate
// ---------------------------------
- (void)session:(DTiPhoneSimulatorSession *)session didStart:(BOOL)started withError:(NSError *)error {
if (!started) {
WaxLog(@"Session failed to start. %@", [error localizedDescription]);
exit(EXIT_FAILURE);
}
+
+ if (!_videoPath) {
+ return;
+ }
+
+ WaxLog(@"Getting window list");
+ NSArray *windowList = (NSArray *)CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
+ for (NSDictionary *info in windowList) {
+ if ([[info objectForKey:(NSString *)kCGWindowOwnerName] isEqualToString:@"iOS Simulator"] && ![[info objectForKey:(NSString *)kCGWindowName] isEqualToString:@""]) {
+ _windowID = [[info objectForKey:(NSString *)kCGWindowNumber] unsignedIntValue];
+ }
+ }
+ [windowList release];
+ if (_windowID) {
+ _movie = [[QTMovie alloc] initToWritableFile:[NSString stringWithCString:tmpnam(nil) encoding:[NSString defaultCStringEncoding]] error:NULL];
+ _lastInterval = [NSDate timeIntervalSinceReferenceDate];;
+ [NSTimer scheduledTimerWithTimeInterval:1.0/30.0 target:self selector:@selector(addScreenshotToMovie) userInfo:nil repeats:YES];
+ }
}
- (void)session:(DTiPhoneSimulatorSession *)session didEndWithError:(NSError *)error {
+ if (_movie) {
+ NSDictionary *attributes = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:QTMovieFlatten];
+ NSError *error = nil;
+ BOOL success = [_movie writeToFile:_videoPath withAttributes:attributes error:&error];
+ if (!success) {
+ WaxLog(@"Failed to write movie: %@", error);
+ }
+ [_movie release];
+ }
if (error) {
WaxLog(@"Session ended with error. %@", [error localizedDescription]);
if ([error code] != 2) exit(EXIT_FAILURE); // if it is a timeout error, that's cool. We are probably rebooting
View
29 WaxSim.m
@@ -6,7 +6,6 @@
static BOOL gReset = false;
void printUsage();
-void simulate(NSString *sdk, NSString *family, NSString *appPath, NSDictionary *environment, NSArray *additionalArgs);
void resetSignal(int sig);
int main(int argc, char *argv[]) {
@@ -16,12 +15,13 @@ int main(int argc, char *argv[]) {
char *sdk = nil;
char *family = nil;
char *appPath = nil;
+ char *videoPath = nil;
NSMutableArray *additionalArgs = [NSMutableArray array];
NSMutableDictionary *environment = [NSMutableDictionary dictionary];
NSString *environment_variable;
NSArray *environment_variable_parts;
- while ((c = getopt(argc, argv, "e:s:f:ah")) != -1) {
+ while ((c = getopt(argc, argv, "e:s:f:v:ah")) != -1) {
switch(c) {
case 'e':
environment_variable = [NSString stringWithCString:optarg encoding:NSUTF8StringEncoding];
@@ -36,14 +36,17 @@ int main(int argc, char *argv[]) {
family = optarg;
break;
case 'a':
- fprintf(stdout, "Available SDK Versions.\n", optopt);
+ fprintf(stdout, "Available SDK Versions.\n");
for (NSString *sdkVersion in [Simulator availableSDKs]) {
fprintf(stderr, " %s\n", [sdkVersion UTF8String]);
}
return 1;
case 'h':
printUsage();
return 1;
+ case 'v':
+ videoPath = optarg;
+ break;
case '?':
if (optopt == 's' || optopt == 'f') {
fprintf(stderr, "Option -%c requires an argument.\n", optopt);
@@ -79,32 +82,24 @@ int main(int argc, char *argv[]) {
NSString *sdkString = sdk ? [NSString stringWithUTF8String:sdk] : nil;
NSString *familyString = family ? [NSString stringWithUTF8String:family] : nil;
NSString *appPathString = [NSString stringWithUTF8String:appPath];
+ NSString *videoPathString = videoPath ? [NSString stringWithUTF8String:videoPath] : nil;
- simulate(sdkString, familyString, appPathString, environment, additionalArgs);
+ Simulator *simulator = [[Simulator alloc] initWithAppPath:appPathString sdk:sdkString family:familyString video:videoPathString env:environment args:additionalArgs];
+ [simulator launch];
+ [[NSRunLoop mainRunLoop] run];
return 0;
}
-void simulate(NSString *sdk, NSString *family, NSString *appPath, NSDictionary *environment, NSArray *additionalArgs) {
- Simulator *simulator = [[Simulator alloc] initWithAppPath:appPath sdk:sdk family:family env:environment args:additionalArgs];
- [simulator launch];
-
- while (!gReset && [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:-1]])
- {
- usleep(100);
- }
-
- [simulator end];
-}
-
void printUsage() {
fprintf(stderr, "usage: waxsim [options] app-path\n");
fprintf(stderr, "example: waxsim -s 2.2 /path/to/app.app\n");
fprintf(stderr, "Available options are:\n");
fprintf(stderr, "\t-s sdk\tVersion number of sdk to use (-s 3.1)\n");
fprintf(stderr, "\t-f family\tDevice to use (-f ipad)\n");
fprintf(stderr, "\t-e VAR=value\tEnvironment variable to set (-e CFFIXED_HOME=/tmp/iphonehome)\n");
- fprintf(stderr, "\t-a \tAvailable SDK's\n");
+ fprintf(stderr, "\t-a \tAvailable SDKs\n");
+ fprintf(stderr, "\t-v path\tOutput video recording at path\n");
fprintf(stderr, "\t-h \tPrints out this wonderful documentation!\n");
}
View
8 WaxSim.xcodeproj/project.pbxproj
@@ -12,6 +12,7 @@
048275C011263A74003DFACB /* iPhoneSimulatorRemoteClient.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 048275BF11263A74003DFACB /* iPhoneSimulatorRemoteClient.framework */; };
8DD76F9A0486AA7600D96B5E /* WaxSim.m in Sources */ = {isa = PBXBuildFile; fileRef = 08FB7796FE84155DC02AAC07 /* WaxSim.m */; settings = {ATTRIBUTES = (); }; };
8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08FB779EFE84155DC02AAC07 /* Foundation.framework */; };
+ A8BC32E4139825EB00BDDB2A /* QTKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A8BC32E3139825EA00BDDB2A /* QTKit.framework */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -36,13 +37,15 @@
08FB779EFE84155DC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
32A70AAB03705E1F00C91783 /* WaxSim_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WaxSim_Prefix.pch; sourceTree = "<group>"; };
8DD76FA10486AA7600D96B5E /* waxsim */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = waxsim; sourceTree = BUILT_PRODUCTS_DIR; };
+ A8BC32E3139825EA00BDDB2A /* QTKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QTKit.framework; path = System/Library/Frameworks/QTKit.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
8DD76F9B0486AA7600D96B5E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ A8BC32E4139825EB00BDDB2A /* QTKit.framework in Frameworks */,
8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */,
048274F11126398C003DFACB /* AppKit.framework in Frameworks */,
048275C011263A74003DFACB /* iPhoneSimulatorRemoteClient.framework in Frameworks */,
@@ -55,6 +58,7 @@
08FB7794FE84155DC02AAC07 /* WaxSim */ = {
isa = PBXGroup;
children = (
+ A8BC32E3139825EA00BDDB2A /* QTKit.framework */,
08FB7795FE84155DC02AAC07 /* Source */,
08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */,
1AB674ADFE9D54B511CA2CBB /* Products */,
@@ -120,7 +124,11 @@
isa = PBXProject;
buildConfigurationList = 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "WaxSim" */;
compatibilityVersion = "Xcode 3.1";
+ developmentRegion = English;
hasScannedForEncodings = 1;
+ knownRegions = (
+ en,
+ );
mainGroup = 08FB7794FE84155DC02AAC07 /* WaxSim */;
projectDirPath = "";
projectRoot = "";

0 comments on commit 64952de

Please sign in to comment.
Something went wrong with that request. Please try again.