Created
July 1, 2015 14:11
-
-
Save anonymous/cb1c5795536ca15ef4e3 to your computer and use it in GitHub Desktop.
Stack Overflow Help
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Cocoa/Cocoa.h> | |
#import <ApplicationServices/ApplicationServices.h> | |
#import <DiskArbitration/DiskArbitration.h> | |
@class AVCaptureVideoPreviewLayer; | |
@class AVCaptureSession; | |
@class AVCaptureDeviceInput; | |
@class AVCaptureMovieFileOutput; | |
@class AVCaptureAudioPreviewOutput; | |
@class AVCaptureConnection; | |
@class AVCaptureDevice; | |
@class AVCaptureDeviceFormat; | |
@class AVFrameRateRange; | |
@interface AVRecorderDocument : NSDocument | |
{ | |
@private | |
NSView *previewView; | |
AVCaptureVideoPreviewLayer *previewLayer; | |
NSLevelIndicator *audioLevelMeter; | |
AVCaptureSession *session; | |
AVCaptureDeviceInput *videoDeviceInput; | |
AVCaptureDeviceInput *audioDeviceInput; | |
AVCaptureMovieFileOutput *movieFileOutput; | |
AVCaptureAudioPreviewOutput *audioPreviewOutput; | |
NSArray *videoDevices; | |
NSArray *audioDevices; | |
NSTimer *audioLevelTimer; | |
NSTimer *recordingTimer; | |
NSInteger recordingTime; | |
NSArray *observers; | |
NSTextField *labelRecording; | |
NSTextField *labelSpaceLeft; | |
} | |
@property (assign) IBOutlet NSWindow *mainWindow; | |
@property (assign) IBOutlet NSWindow *videoWindow; | |
#pragma mark Device Selection | |
@property (retain) NSArray *videoDevices; | |
@property (retain) NSArray *audioDevices; | |
@property (assign) AVCaptureDevice *selectedVideoDevice; | |
@property (assign) AVCaptureDevice *selectedAudioDevice; | |
#pragma mark - Device Properties | |
@property (assign) AVCaptureDeviceFormat *videoDeviceFormat; | |
@property (assign) AVCaptureDeviceFormat *audioDeviceFormat; | |
@property (assign) AVFrameRateRange *frameRateRange; | |
- (IBAction)lockVideoDeviceForConfiguration:(id)sender; | |
#pragma mark - Recording | |
@property (retain) AVCaptureSession *session; | |
@property (readonly) NSArray *availableSessionPresets; | |
@property (readonly) BOOL hasRecordingDevice; | |
@property (assign,getter=isRecording) BOOL recording; | |
#pragma mark - Preview | |
@property (assign) IBOutlet NSView *previewView; | |
@property (assign) float previewVolume; | |
@property (assign) IBOutlet NSLevelIndicator *audioLevelMeter; | |
#pragma mark - Transport Controls | |
@property (readonly,getter=isPlaying) BOOL playing; | |
@property (readonly,getter=isRewinding) BOOL rewinding; | |
@property (readonly,getter=isFastForwarding) BOOL fastForwarding; | |
- (IBAction)stop:(id)sender; | |
- (IBAction)btnDevMode:(id)pId; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "AVRecorderDocument.h" | |
#import <AVFoundation/AVFoundation.h> | |
#import <DiskArbitration/DiskArbitration.h> | |
@interface AVRecorderDocument () <AVCaptureFileOutputDelegate, AVCaptureFileOutputRecordingDelegate> | |
// Properties for internal use | |
@property (retain) AVCaptureDeviceInput *videoDeviceInput; | |
@property (retain) AVCaptureDeviceInput *audioDeviceInput; | |
@property (readonly) BOOL selectedVideoDeviceProvidesAudio; | |
@property (retain) AVCaptureAudioPreviewOutput *audioPreviewOutput; | |
@property (retain) AVCaptureMovieFileOutput *movieFileOutput; | |
@property (retain) AVCaptureVideoPreviewLayer *previewLayer; | |
@property (assign) NSTimer *audioLevelTimer; | |
@property (assign) NSTimer *recordingTimer; | |
@property (retain) NSArray *observers; | |
@property (assign) NSInteger recordingTime; | |
@property (assign) IBOutlet NSTextField *labelRecording; | |
@property (assign) IBOutlet NSPopUpButton *selectVideoDevices; | |
@property (assign) NSString *usbDeviceName; | |
@property (assign) IBOutlet NSTextField *labelSpaceLeft; | |
// Methods for internal use | |
- (void)refreshDevices; | |
- (void)setTransportMode:(AVCaptureDeviceTransportControlsPlaybackMode)playbackMode speed:(AVCaptureDeviceTransportControlsSpeed)speed forDevice:(AVCaptureDevice *)device; | |
//-(void)diskAppearedCallback:(DADiskRef *)disk context:(void *)context; | |
@end | |
@implementation AVRecorderDocument | |
@synthesize videoDeviceInput; | |
@synthesize audioDeviceInput; | |
@synthesize videoDevices; | |
@synthesize audioDevices; | |
@synthesize session; | |
@synthesize audioLevelMeter; | |
@synthesize audioPreviewOutput; | |
@synthesize movieFileOutput; | |
@synthesize previewView; | |
@synthesize previewLayer; | |
@synthesize audioLevelTimer; | |
@synthesize recordingTimer; | |
@synthesize recordingTime; | |
@synthesize observers; | |
@synthesize labelRecording; | |
@synthesize selectVideoDevices; | |
@synthesize mainWindow; | |
@synthesize videoWindow; | |
@synthesize usbDeviceName; | |
@synthesize labelSpaceLeft; | |
- (id)init | |
{ | |
self = [super init]; | |
if (self) { | |
// Create a capture session | |
session = [[AVCaptureSession alloc] init]; | |
DASessionRef sessionDA = DASessionCreate(kCFAllocatorDefault); | |
DARegisterDiskAppearedCallback(sessionDA, kDADiskDescriptionMatchVolumeMountable, diskAppearedCallback, 0); | |
DARegisterDiskDisappearedCallback(sessionDA, kDADiskDescriptionMatchVolumeMountable, diskDisappearedCallback, 0); | |
DASessionScheduleWithRunLoop(sessionDA, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); | |
// Capture Notification Observers | |
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; | |
id runtimeErrorObserver = [notificationCenter addObserverForName:AVCaptureSessionRuntimeErrorNotification | |
object:session | |
queue:[NSOperationQueue mainQueue] | |
usingBlock:^(NSNotification *note) { | |
dispatch_async(dispatch_get_main_queue(), ^(void) { | |
[self presentError:[[note userInfo] objectForKey:AVCaptureSessionErrorKey]]; | |
}); | |
}]; | |
id didStartRunningObserver = [notificationCenter addObserverForName:AVCaptureSessionDidStartRunningNotification | |
object:session | |
queue:[NSOperationQueue mainQueue] | |
usingBlock:^(NSNotification *note) { | |
NSLog(@"did start running"); | |
}]; | |
id didStopRunningObserver = [notificationCenter addObserverForName:AVCaptureSessionDidStopRunningNotification | |
object:session | |
queue:[NSOperationQueue mainQueue] | |
usingBlock:^(NSNotification *note) { | |
NSLog(@"did stop running"); | |
}]; | |
id deviceWasConnectedObserver = [notificationCenter addObserverForName:AVCaptureDeviceWasConnectedNotification | |
object:nil | |
queue:[NSOperationQueue mainQueue] | |
usingBlock:^(NSNotification *note) { | |
[self refreshDevices]; | |
}]; | |
id deviceWasDisconnectedObserver = [notificationCenter addObserverForName:AVCaptureDeviceWasDisconnectedNotification | |
object:nil | |
queue:[NSOperationQueue mainQueue] | |
usingBlock:^(NSNotification *note) { | |
[self refreshDevices]; | |
}]; | |
observers = [[NSArray alloc] initWithObjects:runtimeErrorObserver, didStartRunningObserver, didStopRunningObserver, deviceWasConnectedObserver, deviceWasDisconnectedObserver, nil]; | |
// Attach outputs to session | |
movieFileOutput = [[AVCaptureMovieFileOutput alloc] init]; | |
[movieFileOutput setDelegate:self]; | |
[session addOutput:movieFileOutput]; | |
audioPreviewOutput = [[AVCaptureAudioPreviewOutput alloc] init]; | |
[audioPreviewOutput setVolume:0.f]; | |
[session addOutput:audioPreviewOutput]; | |
// Select devices if any exist | |
AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; | |
if (videoDevice) { | |
[self setSelectedVideoDevice:videoDevice]; | |
[self setSelectedAudioDevice:[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio]]; | |
} else { | |
[self setSelectedVideoDevice:[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeMuxed]]; | |
} | |
// Initial refresh of device list | |
[self refreshDevices]; | |
} | |
return self; | |
} | |
- (void)windowWillClose:(NSNotification *)notification | |
{ | |
// Invalidate the level meter timer here to avoid a retain cycle | |
[[self audioLevelTimer] invalidate]; | |
[[self recordingTimer] invalidate]; | |
// Stop the session | |
[[self session] stopRunning]; | |
// Set movie file output delegate to nil to avoid a dangling pointer | |
[[self movieFileOutput] setDelegate:nil]; | |
// Remove Observers | |
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; | |
for (id observer in [self observers]) | |
[notificationCenter removeObserver:observer]; | |
[observers release]; | |
} | |
- (void)dealloc | |
{ | |
[videoDevices release]; | |
[audioDevices release]; | |
[session release]; | |
[audioPreviewOutput release]; | |
[movieFileOutput release]; | |
[previewLayer release]; | |
[videoDeviceInput release]; | |
[audioDeviceInput release]; | |
[super dealloc]; | |
} | |
- (NSString *)windowNibName | |
{ | |
return @"AVRecorderDocument"; | |
} | |
- (void)windowControllerDidLoadNib:(NSWindowController *) aController | |
{ | |
[super windowControllerDidLoadNib:aController]; | |
// Attach preview to session | |
CALayer *previewViewLayer = [[self previewView] layer]; | |
[previewViewLayer setBackgroundColor:CGColorGetConstantColor(kCGColorBlack)]; | |
AVCaptureVideoPreviewLayer *newPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:[self session]]; | |
[newPreviewLayer setFrame:[previewViewLayer bounds]]; | |
[newPreviewLayer setAutoresizingMask:kCALayerWidthSizable | kCALayerHeightSizable]; | |
[previewViewLayer addSublayer:newPreviewLayer]; | |
[self setPreviewLayer:newPreviewLayer]; | |
[newPreviewLayer release]; | |
// Start the session | |
[[self session] startRunning]; | |
// Start updating the audio level meter | |
[self setAudioLevelTimer:[NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:@selector(updateAudioLevels:) userInfo:nil repeats:YES]]; | |
} | |
- (void)awakeFromNib | |
{ | |
[mainWindow center]; | |
//[videoWindow center]; | |
NSRect theFrame = [videoWindow frame]; | |
NSPoint theOrigin = theFrame.origin; | |
theOrigin.x = 1425; | |
[videoWindow setFrameOrigin:theOrigin]; | |
} | |
- (void)didPresentErrorWithRecovery:(BOOL)didRecover contextInfo:(void *)contextInfo | |
{ | |
// Do nothing | |
} | |
#pragma mark - Device selection | |
- (void)refreshDevices | |
{ | |
[self setVideoDevices:[[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo] arrayByAddingObjectsFromArray:[AVCaptureDevice devicesWithMediaType:AVMediaTypeMuxed]]]; | |
[self setAudioDevices:[AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio]]; | |
[[self session] beginConfiguration]; | |
if (![[self videoDevices] containsObject:[self selectedVideoDevice]]) | |
[self setSelectedVideoDevice:nil]; | |
if (![[self audioDevices] containsObject:[self selectedAudioDevice]]) | |
[self setSelectedAudioDevice:nil]; | |
[[self session] commitConfiguration]; | |
} | |
- (AVCaptureDevice *)selectedVideoDevice | |
{ | |
return [videoDeviceInput device]; | |
} | |
- (void)setSelectedVideoDevice:(AVCaptureDevice *)selectedVideoDevice | |
{ | |
[[self session] beginConfiguration]; | |
if ([self videoDeviceInput]) { | |
// Remove the old device input from the session | |
[session removeInput:[self videoDeviceInput]]; | |
[self setVideoDeviceInput:nil]; | |
} | |
if (selectedVideoDevice) { | |
NSError *error = nil; | |
// Create a device input for the device and add it to the session | |
AVCaptureDeviceInput *newVideoDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:selectedVideoDevice error:&error]; | |
if (newVideoDeviceInput == nil) { | |
dispatch_async(dispatch_get_main_queue(), ^(void) { | |
[self presentError:error]; | |
}); | |
} else { | |
if (![selectedVideoDevice supportsAVCaptureSessionPreset:[session sessionPreset]]) | |
[[self session] setSessionPreset:AVCaptureSessionPresetHigh]; | |
[[self session] addInput:newVideoDeviceInput]; | |
[self setVideoDeviceInput:newVideoDeviceInput]; | |
} | |
} | |
// If this video device also provides audio, don't use another audio device | |
if ([self selectedVideoDeviceProvidesAudio]) | |
[self setSelectedAudioDevice:nil]; | |
[[self session] commitConfiguration]; | |
} | |
- (AVCaptureDevice *)selectedAudioDevice | |
{ | |
return [audioDeviceInput device]; | |
} | |
- (void)setSelectedAudioDevice:(AVCaptureDevice *)selectedAudioDevice | |
{ | |
[[self session] beginConfiguration]; | |
if ([self audioDeviceInput]) { | |
// Remove the old device input from the session | |
[session removeInput:[self audioDeviceInput]]; | |
[self setAudioDeviceInput:nil]; | |
} | |
if (selectedAudioDevice && ![self selectedVideoDeviceProvidesAudio]) { | |
NSError *error = nil; | |
// Create a device input for the device and add it to the session | |
AVCaptureDeviceInput *newAudioDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:selectedAudioDevice error:&error]; | |
if (newAudioDeviceInput == nil) { | |
dispatch_async(dispatch_get_main_queue(), ^(void) { | |
[self presentError:error]; | |
}); | |
} else { | |
if (![selectedAudioDevice supportsAVCaptureSessionPreset:[session sessionPreset]]) | |
[[self session] setSessionPreset:AVCaptureSessionPresetHigh]; | |
[[self session] addInput:newAudioDeviceInput]; | |
[self setAudioDeviceInput:newAudioDeviceInput]; | |
} | |
} | |
[[self session] commitConfiguration]; | |
} | |
#pragma mark - Device Properties | |
+ (NSSet *)keyPathsForValuesAffectingSelectedVideoDeviceProvidesAudio | |
{ | |
return [NSSet setWithObjects:@"selectedVideoDevice", nil]; | |
} | |
- (BOOL)selectedVideoDeviceProvidesAudio | |
{ | |
return ([[self selectedVideoDevice] hasMediaType:AVMediaTypeMuxed] || [[self selectedVideoDevice] hasMediaType:AVMediaTypeAudio]); | |
} | |
+ (NSSet *)keyPathsForValuesAffectingVideoDeviceFormat | |
{ | |
return [NSSet setWithObjects:@"selectedVideoDevice.activeFormat", nil]; | |
} | |
- (AVCaptureDeviceFormat *)videoDeviceFormat | |
{ | |
return [[self selectedVideoDevice] activeFormat]; | |
} | |
- (void)setVideoDeviceFormat:(AVCaptureDeviceFormat *)deviceFormat | |
{ | |
NSError *error = nil; | |
AVCaptureDevice *videoDevice = [self selectedVideoDevice]; | |
if ([videoDevice lockForConfiguration:&error]) { | |
[videoDevice setActiveFormat:deviceFormat]; | |
[videoDevice unlockForConfiguration]; | |
} else { | |
dispatch_async(dispatch_get_main_queue(), ^(void) { | |
[self presentError:error]; | |
}); | |
} | |
} | |
+ (NSSet *)keyPathsForValuesAffectingAudioDeviceFormat | |
{ | |
return [NSSet setWithObjects:@"selectedAudioDevice.activeFormat", nil]; | |
} | |
- (AVCaptureDeviceFormat *)audioDeviceFormat | |
{ | |
return [[self selectedAudioDevice] activeFormat]; | |
} | |
- (void)setAudioDeviceFormat:(AVCaptureDeviceFormat *)deviceFormat | |
{ | |
NSError *error = nil; | |
AVCaptureDevice *audioDevice = [self selectedAudioDevice]; | |
if ([audioDevice lockForConfiguration:&error]) { | |
[audioDevice setActiveFormat:deviceFormat]; | |
[audioDevice unlockForConfiguration]; | |
} else { | |
dispatch_async(dispatch_get_main_queue(), ^(void) { | |
[self presentError:error]; | |
}); | |
} | |
} | |
+ (NSSet *)keyPathsForValuesAffectingFrameRateRange | |
{ | |
return [NSSet setWithObjects:@"selectedVideoDevice.activeFormat.videoSupportedFrameRateRanges", @"selectedVideoDevice.activeVideoMinFrameDuration", nil]; | |
} | |
- (AVFrameRateRange *)frameRateRange | |
{ | |
AVFrameRateRange *activeFrameRateRange = nil; | |
for (AVFrameRateRange *frameRateRange in [[[self selectedVideoDevice] activeFormat] videoSupportedFrameRateRanges]) | |
{ | |
if (CMTIME_COMPARE_INLINE([frameRateRange minFrameDuration], ==, [[self selectedVideoDevice] activeVideoMinFrameDuration])) | |
{ | |
activeFrameRateRange = frameRateRange; | |
break; | |
} | |
} | |
return activeFrameRateRange; | |
} | |
- (void)setFrameRateRange:(AVFrameRateRange *)frameRateRange | |
{ | |
NSError *error = nil; | |
if ([[[[self selectedVideoDevice] activeFormat] videoSupportedFrameRateRanges] containsObject:frameRateRange]) | |
{ | |
if ([[self selectedVideoDevice] lockForConfiguration:&error]) { | |
[[self selectedVideoDevice] setActiveVideoMinFrameDuration:[frameRateRange minFrameDuration]]; | |
[[self selectedVideoDevice] unlockForConfiguration]; | |
} else { | |
dispatch_async(dispatch_get_main_queue(), ^(void) { | |
[self presentError:error]; | |
}); | |
} | |
} | |
} | |
- (IBAction)lockVideoDeviceForConfiguration:(id)sender | |
{ | |
if ([(NSButton *)sender state] == NSOnState) | |
{ | |
[[self selectedVideoDevice] lockForConfiguration:nil]; | |
} | |
else | |
{ | |
[[self selectedVideoDevice] unlockForConfiguration]; | |
} | |
} | |
#pragma mark - Recording | |
+ (NSSet *)keyPathsForValuesAffectingHasRecordingDevice | |
{ | |
return [NSSet setWithObjects:@"selectedVideoDevice", @"selectedAudioDevice", nil]; | |
} | |
- (BOOL)hasRecordingDevice | |
{ | |
return ((videoDeviceInput != nil) || (audioDeviceInput != nil)); | |
} | |
+ (NSSet *)keyPathsForValuesAffectingRecording | |
{ | |
return [NSSet setWithObject:@"movieFileOutput.recording"]; | |
} | |
- (BOOL)isRecording | |
{ | |
if([[self movieFileOutput] isRecording] == true){ | |
//[labelRecording setStringValue:@"Recording"]; | |
[self setRecordingTimer:[NSTimer scheduledTimerWithTimeInterval:1.0 | |
target:self | |
selector:@selector(updateRecordingTime:) | |
userInfo:nil repeats:YES]]; | |
}else{ | |
[[self recordingTimer] invalidate]; | |
recordingTimer = nil; | |
[labelRecording setStringValue:@"00:00.00"]; | |
} | |
return [[self movieFileOutput] isRecording]; | |
} | |
- (void)setRecording:(BOOL)record | |
{ | |
if (record) { | |
// Record to a temporary file, which the user will relocate when recording is finished | |
char *tempNameBytes = tempnam([NSTemporaryDirectory() fileSystemRepresentation], "AVRecorder_"); | |
NSString *tempName = [[[NSString alloc] initWithBytesNoCopy:tempNameBytes length:strlen(tempNameBytes) encoding:NSUTF8StringEncoding freeWhenDone:YES] autorelease]; | |
[[self movieFileOutput] startRecordingToOutputFileURL:[NSURL fileURLWithPath:[tempName stringByAppendingPathExtension:@"mov"]] | |
recordingDelegate:self]; | |
} else { | |
[[self movieFileOutput] stopRecording]; | |
} | |
} | |
+ (NSSet *)keyPathsForValuesAffectingAvailableSessionPresets | |
{ | |
return [NSSet setWithObjects:@"selectedVideoDevice", @"selectedAudioDevice", nil]; | |
} | |
- (NSArray *)availableSessionPresets | |
{ | |
NSArray *allSessionPresets = [NSArray arrayWithObjects: | |
AVCaptureSessionPresetLow, | |
AVCaptureSessionPresetMedium, | |
AVCaptureSessionPresetHigh, | |
AVCaptureSessionPreset320x240, | |
AVCaptureSessionPreset352x288, | |
AVCaptureSessionPreset640x480, | |
AVCaptureSessionPreset960x540, | |
AVCaptureSessionPreset1280x720, | |
AVCaptureSessionPresetPhoto, | |
nil]; | |
NSMutableArray *availableSessionPresets = [NSMutableArray arrayWithCapacity:9]; | |
for (NSString *sessionPreset in allSessionPresets) { | |
if ([[self session] canSetSessionPreset:sessionPreset]) | |
[availableSessionPresets addObject:sessionPreset]; | |
} | |
return availableSessionPresets; | |
} | |
#pragma mark - Audio Preview | |
- (float)previewVolume | |
{ | |
return [[self audioPreviewOutput] volume]; | |
} | |
- (void)setPreviewVolume:(float)newPreviewVolume | |
{ | |
[[self audioPreviewOutput] setVolume:newPreviewVolume]; | |
} | |
- (void)updateAudioLevels:(NSTimer *)timer | |
{ | |
NSInteger channelCount = 0; | |
float decibels = 0.f; | |
// Sum all of the average power levels and divide by the number of channels | |
for (AVCaptureConnection *connection in [[self movieFileOutput] connections]) { | |
for (AVCaptureAudioChannel *audioChannel in [connection audioChannels]) { | |
decibels += [audioChannel averagePowerLevel]; | |
channelCount += 1; | |
} | |
} | |
decibels /= channelCount; | |
[[self audioLevelMeter] setFloatValue:(pow(10.f, 0.05f * decibels) * 20.0f)]; | |
} | |
- (void)updateRecordingTime:(NSTimer *)timer | |
{ | |
[self setRecordingTime:[self recordingTime] + 1]; | |
int num = (int)recordingTime; | |
[labelRecording setStringValue:[NSString stringWithFormat:@"%02u:%05.2f",(int)(num/60), fmod(num, 60)]]; | |
} | |
#pragma mark - Transport Controls | |
- (IBAction)stop:(id)sender | |
{ | |
[self setTransportMode:AVCaptureDeviceTransportControlsNotPlayingMode speed:0.f forDevice:[self selectedVideoDevice]]; | |
} | |
+ (NSSet *)keyPathsForValuesAffectingPlaying | |
{ | |
return [NSSet setWithObjects:@"selectedVideoDevice.transportControlsPlaybackMode", @"selectedVideoDevice.transportControlsSpeed",nil]; | |
} | |
- (BOOL)isPlaying | |
{ | |
AVCaptureDevice *device = [self selectedVideoDevice]; | |
return ([device transportControlsSupported] && | |
[device transportControlsPlaybackMode] == AVCaptureDeviceTransportControlsPlayingMode && | |
[device transportControlsSpeed] == 1.f); | |
} | |
- (void)setPlaying:(BOOL)play | |
{ | |
AVCaptureDevice *device = [self selectedVideoDevice]; | |
[self setTransportMode:AVCaptureDeviceTransportControlsPlayingMode speed:play ? 1.f : 0.f forDevice:device]; | |
} | |
+ (NSSet *)keyPathsForValuesAffectingRewinding | |
{ | |
return [NSSet setWithObjects:@"selectedVideoDevice.transportControlsPlaybackMode", @"selectedVideoDevice.transportControlsSpeed",nil]; | |
} | |
- (BOOL)isRewinding | |
{ | |
AVCaptureDevice *device = [self selectedVideoDevice]; | |
return [device transportControlsSupported] && ([device transportControlsSpeed] < -1.f); | |
} | |
- (void)setRewinding:(BOOL)rewind | |
{ | |
AVCaptureDevice *device = [self selectedVideoDevice]; | |
[self setTransportMode:[device transportControlsPlaybackMode] speed:rewind ? -2.f : 0.f forDevice:device]; | |
} | |
+ (NSSet *)keyPathsForValuesAffectingFastForwarding | |
{ | |
return [NSSet setWithObjects:@"selectedVideoDevice.transportControlsPlaybackMode", @"selectedVideoDevice.transportControlsSpeed",nil]; | |
} | |
- (BOOL)isFastForwarding | |
{ | |
AVCaptureDevice *device = [self selectedVideoDevice]; | |
return [device transportControlsSupported] && ([device transportControlsSpeed] > 1.f); | |
} | |
- (void)setFastForwarding:(BOOL)fastforward | |
{ | |
AVCaptureDevice *device = [self selectedVideoDevice]; | |
[self setTransportMode:[device transportControlsPlaybackMode] speed:fastforward ? 2.f : 0.f forDevice:device]; | |
} | |
- (void)setTransportMode:(AVCaptureDeviceTransportControlsPlaybackMode)playbackMode speed:(AVCaptureDeviceTransportControlsSpeed)speed forDevice:(AVCaptureDevice *)device | |
{ | |
NSError *error = nil; | |
if ([device transportControlsSupported]) { | |
if ([device lockForConfiguration:&error]) { | |
[device setTransportControlsPlaybackMode:playbackMode speed:speed]; | |
[device unlockForConfiguration]; | |
} else { | |
dispatch_async(dispatch_get_main_queue(), ^(void) { | |
[self presentError:error]; | |
}); | |
} | |
} | |
} | |
#pragma mark - Delegate methods | |
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didStartRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray *)connections | |
{ | |
NSLog(@"Did start recording to %@", [fileURL description]); | |
} | |
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didPauseRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray *)connections | |
{ | |
NSLog(@"Did pause recording to %@", [fileURL description]); | |
} | |
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didResumeRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray *)connections | |
{ | |
NSLog(@"Did resume recording to %@", [fileURL description]); | |
} | |
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput willFinishRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray *)connections dueToError:(NSError *)error | |
{ | |
dispatch_async(dispatch_get_main_queue(), ^(void) { | |
[self presentError:error]; | |
}); | |
} | |
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)recordError | |
{ | |
if (recordError != nil && [[[recordError userInfo] objectForKey:AVErrorRecordingSuccessfullyFinishedKey] boolValue] == NO) { | |
[[NSFileManager defaultManager] removeItemAtURL:outputFileURL error:nil]; | |
dispatch_async(dispatch_get_main_queue(), ^(void) { | |
[self presentError:recordError]; | |
}); | |
} else { | |
// Move the recorded temporary file to a user-specified location | |
NSSavePanel *savePanel = [NSSavePanel savePanel]; | |
[savePanel setAllowedFileTypes:[NSArray arrayWithObject:AVFileTypeQuickTimeMovie]]; | |
[savePanel setCanSelectHiddenExtension:YES]; | |
[savePanel beginSheetModalForWindow:[self windowForSheet] completionHandler:^(NSInteger result) { | |
NSError *error = nil; | |
if (result == NSOKButton) { | |
[[NSFileManager defaultManager] removeItemAtURL:[savePanel URL] error:nil]; // attempt to remove file at the desired save location before moving the recorded file to that location | |
if ([[NSFileManager defaultManager] moveItemAtURL:outputFileURL toURL:[savePanel URL] error:&error]) { | |
[[NSWorkspace sharedWorkspace] openURL:[savePanel URL]]; | |
} else { | |
[savePanel orderOut:self]; | |
[self presentError:error modalForWindow:[self windowForSheet] delegate:self didPresentSelector:@selector(didPresentErrorWithRecovery:contextInfo:) contextInfo:NULL]; | |
} | |
} else { | |
// remove the temporary recording file if it's not being saved | |
[[NSFileManager defaultManager] removeItemAtURL:outputFileURL error:nil]; | |
} | |
}]; | |
} | |
} | |
- (BOOL)captureOutputShouldProvideSampleAccurateRecordingStart:(AVCaptureOutput *)captureOutput | |
{ | |
// We don't require frame accurate start when we start a recording. If we answer YES, the capture output | |
// applies outputSettings immediately when the session starts previewing, resulting in higher CPU usage | |
// and shorter battery life. | |
return NO; | |
} | |
- (IBAction)btnDevMode:(id)pId; | |
{ | |
//hide show button for hiding dev buttons | |
[selectVideoDevices setHidden:YES]; | |
} // end doSomething | |
#pragma mark - Drive Handling | |
/* | |
-(void)diskAppearedCallback:(DADiskRef *)disk context:(void *)context{ | |
CFDictionaryRef description = DADiskCopyDescription(*disk); | |
NSLog(@"Disk appeared: %@", description); | |
NSString *spaceLeft = CFDictionaryGetValue(description, CFSTR("DADeviceModel")); | |
NSLog(@"Disk Name: %@", CFDictionaryGetValue(description, CFSTR("DADeviceModel"))); | |
[labelSpaceLeft setStringValue:[NSString stringWithFormat: @"%@", spaceLeft]]; | |
//[labelRecording setStringValue:[NSString stringWithFormat:@"%02u:%05.2f",(int)(num/60), fmod(num, 60)]]; | |
CFRelease(description); | |
}*/ | |
static void diskAppearedCallback(DADiskRef disk, void* context) | |
{ | |
CFDictionaryRef description = DADiskCopyDescription(disk); | |
NSLog(@"Disk appeared: %@", description); | |
NSLog(@"Disk Name: %@", CFDictionaryGetValue(description, CFSTR("DADeviceModel"))); | |
[labelSpaceLeft setStringValue:[NSString CFDictionaryGetValue(description, CFSTR("DADeviceModel"))]]; | |
CFRelease(description); | |
} | |
static void diskDisappearedCallback(DADiskRef disk, void* context) | |
{ | |
CFDictionaryRef description = DADiskCopyDescription(disk); | |
NSLog(@"Disk disappeared: %@", description); | |
CFRelease(description); | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment