diff --git a/CocoaSplit/AppDelegate.m b/CocoaSplit/AppDelegate.m index 3d289fab..03fa9c07 100644 --- a/CocoaSplit/AppDelegate.m +++ b/CocoaSplit/AppDelegate.m @@ -32,6 +32,17 @@ } +-(NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender +{ + if (self.captureController) + { + return [self.captureController applicationShouldTerminate:sender]; + } + + return NSTerminateNow; +} + + -(void) applicationWillTerminate: (NSNotification *)notification { diff --git a/CocoaSplit/CaptureController.h b/CocoaSplit/CaptureController.h index 34dc1577..11b844a0 100644 --- a/CocoaSplit/CaptureController.h +++ b/CocoaSplit/CaptureController.h @@ -55,6 +55,8 @@ void VideoCompressorReceiveFrame(void *, void *, OSStatus , VTEncodeInfoFlags , dispatch_queue_t _main_capture_queue; dispatch_queue_t _preview_queue; dispatch_source_t _dispatch_timer; + dispatch_source_t _statistics_timer; + CFAbsoluteTime _frame_interval; mach_timebase_info_data_t _mach_timebase; double _frame_time; @@ -65,7 +67,9 @@ void VideoCompressorReceiveFrame(void *, void *, OSStatus , VTEncodeInfoFlags , NSMutableArray *audioBuffer; NSMutableArray *videoBuffer; dispatch_source_t _log_source; - + int _saved_stderr; + + @@ -208,11 +212,14 @@ void VideoCompressorReceiveFrame(void *, void *, OSStatus , VTEncodeInfoFlags , @property (strong) NSPipe *loggingPipe; @property (strong) NSFileHandle *logReadHandle; +@property (assign) bool useStatusColors; + @property (unsafe_unretained) IBOutlet NSTextView *logTextView; + - (void) outputSampleBuffer:(CMSampleBufferRef)theBuffer; - (void) outputAVPacket:(AVPacket *)avpkt codec_ctx:(AVCodecContext *)codec_ctx; - (void)saveSettings; @@ -223,6 +230,13 @@ void VideoCompressorReceiveFrame(void *, void *, OSStatus , VTEncodeInfoFlags , -(void)setExtraData:(id)saveData forKey:(NSString *)forKey; -(id)getExtraData:(NSString *)forkey; -(CVPixelBufferRef)currentFrame; +-(double)mach_time_seconds; +-(bool)pendingStreamConfirmation:(NSString *)queryString; +-(int)streamsPendingCount; +-(int)streamsActiveCount; +-(NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender; +-(NSColor *)statusColor; + -(void)setupLogging; diff --git a/CocoaSplit/CaptureController.m b/CocoaSplit/CaptureController.m index 7bc3cabe..56388e91 100644 --- a/CocoaSplit/CaptureController.m +++ b/CocoaSplit/CaptureController.m @@ -383,7 +383,11 @@ { +#ifndef DEBUG [self setupLogging]; +#endif + + audioLastReadPosition = 0; audioWritePosition = 0; @@ -392,6 +396,8 @@ videoBuffer = [[NSMutableArray alloc] init]; + self.useStatusColors = YES; + dispatch_source_t sigsrc = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGPIPE, 0, dispatch_get_global_queue(0, 0)); dispatch_source_set_event_handler(sigsrc, ^{ return;}); @@ -470,6 +476,22 @@ dispatch_resume(_dispatch_timer); */ + + _statistics_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); + + dispatch_source_set_timer(_statistics_timer, DISPATCH_TIME_NOW, 1*NSEC_PER_SEC, 0); + dispatch_source_set_event_handler(_statistics_timer, ^{ + + for (OutputDestination *outdest in _captureDestinations) + { + [outdest updateStatistics]; + } + + }); + + dispatch_resume(_statistics_timer); + + self.extraSaveData = [[NSMutableDictionary alloc] init]; @@ -483,6 +505,22 @@ } +-(NSColor *)statusColor +{ + if (self.captureRunning && [self streamsActiveCount] > 0) + { + return [NSColor redColor]; + } + + if ([self streamsPendingCount] > 0) + { + return [NSColor orangeColor]; + } + + return [NSColor blackColor]; +} + + - (NSString *) saveFilePath { @@ -543,6 +581,7 @@ self.loggingPipe = [NSPipe pipe]; self.logReadHandle = [self.loggingPipe fileHandleForReading]; + dup2([[self.loggingPipe fileHandleForWriting] fileDescriptor], fileno(stderr)); _log_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, [self.logReadHandle fileDescriptor], 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)); @@ -559,6 +598,7 @@ if (read_size > 0) { + dispatch_async(dispatch_get_main_queue(), ^{ NSString *logStr = [[NSString alloc] initWithBytesNoCopy:data length:read_size encoding:NSUTF8StringEncoding freeWhenDone:YES]; [self appendToLogView:logStr]; @@ -608,6 +648,7 @@ [saveRoot setValue:[NSNumber numberWithInt:self.maxOutputPending] forKey:@"maxOutputPending"]; [saveRoot setValue:self.resolutionOption forKey:@"resolutionOption"]; [saveRoot setValue:[NSNumber numberWithDouble:self.audio_adjust] forKey:@"audioAdjust"]; + [saveRoot setValue: [NSNumber numberWithBool:self.useStatusColors] forKey:@"useStatusColors"]; @@ -651,6 +692,14 @@ } + for (OutputDestination *outdest in _captureDestinations) + { + outdest.settingsController = self; + } + + + self.useStatusColors = [[saveRoot valueForKeyPath:@"useStatusColors"] boolValue]; + self.x264tune = [saveRoot valueForKey:@"x264tune"]; self.x264preset = [saveRoot valueForKey:@"x264preset"]; self.x264profile = [saveRoot valueForKey:@"x264profile"]; @@ -752,7 +801,7 @@ [[self mutableArrayValueForKey:@"captureDestinations"] addObject:newDest]; - [newDest attachOutput:self]; + newDest.settingsController = self; [self closeCreateSheet:nil]; } @@ -809,13 +858,8 @@ for (OutputDestination *outdest in _captureDestinations) { - if (outdest.active) - { - - id ffmpeg = outdest.ffmpeg_out; - [ffmpeg writeEncodedData:frameData]; - - } + [outdest writeEncodedData:frameData]; + } } @@ -935,6 +979,12 @@ } + for (OutputDestination *outdest in _captureDestinations) + { + [outdest reset]; + } + + self.captureRunning = YES; return YES; @@ -1106,6 +1156,7 @@ newDest.active = YES; newDest.destination = outstr; + newDest.settingsController = self; [[self mutableArrayValueForKey:@"captureDestinations"] addObject:newDest]; } @@ -1154,6 +1205,13 @@ if ([button state] == NSOnState) { + if ([self pendingStreamConfirmation:@"Start streaming?"] == NO) + { + [sender setNextState]; + return; + } + + if ([self startStream] == YES) { @@ -1287,8 +1345,11 @@ -(double)mach_time_seconds { + double retval; + uint64_t mach_now = mach_absolute_time(); - return (double)((mach_now * _mach_timebase.numer / _mach_timebase.denom))/NSEC_PER_SEC; + retval = (double)((mach_now * _mach_timebase.numer / _mach_timebase.denom))/NSEC_PER_SEC; + return retval; } @@ -1472,16 +1533,15 @@ { [self stopStream]; [NSApp presentError:error]; - } else { + /*} else { OutputDestination *output; - NSLog(@"Attaching destinations"); for (output in _captureDestinations) { - [output attachOutput:self]; + output.settingsController = self; } - } + */} } @@ -1494,10 +1554,17 @@ capturedData.videoFrame = newFrame; [self processVideoFrame:capturedData]; - //} else { + } else { + for (OutputDestination *outdest in _captureDestinations) + { + [outdest writeEncodedData:nil]; + } + } + + CVPixelBufferRelease(newFrame); @@ -1569,6 +1636,78 @@ } +-(int)streamsActiveCount +{ + int ret = 0; + for (OutputDestination *outdest in _captureDestinations) + { + if (outdest.active) + { + ret++; + } + } + + return ret; +} + + +-(int)streamsPendingCount +{ + int ret = 0; + + for (OutputDestination *outdest in _captureDestinations) + { + if (outdest.buffer_draining) + { + ret++; + } + } + + return ret; +} + + +-(bool)actionConfirmation:(NSString *)queryString infoString:(NSString *)infoString +{ + + bool retval; + + NSAlert *confirmationAlert = [[NSAlert alloc] init]; + [confirmationAlert addButtonWithTitle:@"Yes"]; + [confirmationAlert addButtonWithTitle:@"No"]; + [confirmationAlert setMessageText:queryString]; + if (infoString) + { + [confirmationAlert setInformativeText:infoString]; + } + + [confirmationAlert setAlertStyle:NSWarningAlertStyle]; + + if ([confirmationAlert runModal] == NSAlertFirstButtonReturn) + { + retval = YES; + } else { + retval = NO; + } + + return retval; +} + + +-(bool)pendingStreamConfirmation:(NSString *)queryString +{ + int pending_count = [self streamsPendingCount]; + bool retval; + + if (pending_count > 0) + { + retval = [self actionConfirmation:queryString infoString:[NSString stringWithFormat:@"There are %d streams pending output", pending_count]]; + } else { + retval = YES; + } + + return retval; +} - (void) setNilValueForKey:(NSString *)key { @@ -1594,5 +1733,30 @@ }]; +} + +-(NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender +{ + + + if (self.captureRunning && [self streamsActiveCount] > 0) + + { + if ([self actionConfirmation:@"Really quit?" infoString:@"There are still active outputs"]) + { + return NSTerminateNow; + } else { + return NSTerminateCancel; + } + } + + if ([self pendingStreamConfirmation:@"Quit now?"]) + { + return NSTerminateNow; + } else { + return NSTerminateCancel; + } + return NSTerminateNow; + } @end diff --git a/CocoaSplit/ControllerProtocol.h b/CocoaSplit/ControllerProtocol.h index a6946b28..15459f29 100644 --- a/CocoaSplit/ControllerProtocol.h +++ b/CocoaSplit/ControllerProtocol.h @@ -10,6 +10,7 @@ #import "libavformat/avformat.h" #import "AbstractCaptureDevice.h" #import "CapturedFrameData.h" +#import @protocol ControllerProtocol @@ -43,6 +44,10 @@ -(void)setExtraData:(id)saveData forKey:(NSString *)forKey; -(id)getExtraData:(NSString *)forkey; -(CVPixelBufferRef)currentFrame; +-(double)mach_time_seconds; +-(NSColor *)statusColor; + + - (void) outputEncodedData:(CapturedFrameData *)frameData; diff --git a/CocoaSplit/DesktopCapture.m b/CocoaSplit/DesktopCapture.m index ce279b7c..f2566f7c 100644 --- a/CocoaSplit/DesktopCapture.m +++ b/CocoaSplit/DesktopCapture.m @@ -60,7 +60,8 @@ self.width = displaySize.size.width; self.height = displaySize.size.height; - + + [self setupDisplayStream]; } diff --git a/CocoaSplit/FFMpegTask.h b/CocoaSplit/FFMpegTask.h index 3f931282..0302393b 100644 --- a/CocoaSplit/FFMpegTask.h +++ b/CocoaSplit/FFMpegTask.h @@ -44,6 +44,9 @@ -(void) writeAudioSampleBuffer:(CMSampleBufferRef)theBuffer presentationTimeStamp:(CMTime)pts; -(void) writeAVPacket:(AVPacket *)pkt codec_ctx:(AVCodecContext *)codec_ctx; -(void) writeEncodedData:(CapturedFrameData *)frameData; +-(void) updateOutputStats; +-(void) updateInputStats; + diff --git a/CocoaSplit/FFMpegTask.m b/CocoaSplit/FFMpegTask.m index f47a716b..319caffd 100644 --- a/CocoaSplit/FFMpegTask.m +++ b/CocoaSplit/FFMpegTask.m @@ -428,19 +428,14 @@ void getAudioExtradata(char *cookie, char **buffer, size_t *size) _input_framecnt = 0; _input_frame_timestamp = time_now; - dispatch_async(dispatch_get_main_queue(), ^{ - - - - self.input_framerate = calculated_input_framerate; + self.input_framerate = calculated_input_framerate; self.buffered_frame_count = _pending_frame_count; self.buffered_frame_size = _pending_frame_size; self.dropped_frame_count = _dropped_frames; - }); - } + -(void) updateOutputStats { @@ -452,12 +447,8 @@ void getAudioExtradata(char *cookie, char **buffer, size_t *size) _output_frame_timestamp = time_now; - dispatch_async(dispatch_get_main_queue(), ^{ - - - self.output_framerate = calculated_output_framerate; - self.output_bitrate = calculated_output_bitrate; - }); + self.output_framerate = calculated_output_framerate; + self.output_bitrate = calculated_output_bitrate; } @@ -492,18 +483,10 @@ void getAudioExtradata(char *cookie, char **buffer, size_t *size) _pending_frame_size += pkt->size; } - - if ((_input_framecnt % self.framerate) == 0) - { - [self updateInputStats]; - } - - if ([self shouldDropFrame]) { _dropped_frames++; _consecutive_dropped_frames++; - NSLog(@"SHOULD DROP FRAME"); return; } else { _consecutive_dropped_frames = 0; @@ -535,32 +518,6 @@ void getAudioExtradata(char *cookie, char **buffer, size_t *size) return; } - /* - if (!_av_video_stream) - { - if (_audio_extradata) - { - if (![self createAVFormatOut:nil codec_ctx:codec_ctx]) - { - - av_free_packet(p); - av_free(p); - NSLog(@"NO AVFORMAT OUT"); - return; - } - [self initStatsValues]; - } else { - @synchronized(self) - { - _pending_frame_count--; - _pending_frame_size -= p->size; - } - NSLog(@"NO AUDIO EXTRA"); - return; - } - } - - */ if (p->pts != AV_NOPTS_VALUE) { p->pts = av_rescale_q(p->pts, codec_ctx->time_base, _av_video_stream->time_base); @@ -588,11 +545,6 @@ void getAudioExtradata(char *cookie, char **buffer, size_t *size) av_free(p); _output_framecnt++; _output_bytes += packet_size; - if ((_output_framecnt % self.framerate) == 0) - { - [self updateOutputStats]; - } - @synchronized(self) { _pending_frame_count--; @@ -625,11 +577,6 @@ void getAudioExtradata(char *cookie, char **buffer, size_t *size) _input_framecnt++; - if ((_input_framecnt % self.framerate) == 0) - { - [self updateInputStats]; - } - if ([self shouldDropFrame]) { @@ -737,11 +684,6 @@ void getAudioExtradata(char *cookie, char **buffer, size_t *size) _output_framecnt++; _output_bytes += pkt.size; - if ((_output_framecnt % self.framerate) == 0) - { - [self updateOutputStats]; - } - //CMSampleBufferInvalidate(theBuffer); CFRelease(theBuffer); diff --git a/CocoaSplit/OutputDestination.h b/CocoaSplit/OutputDestination.h index 7d31726e..86d9cff9 100644 --- a/CocoaSplit/OutputDestination.h +++ b/CocoaSplit/OutputDestination.h @@ -17,6 +17,9 @@ NSString *_destination; NSString *_name; BOOL _active; + double _output_start_time; + NSMutableArray *_delayBuffer; + } @@ -26,19 +29,37 @@ @property (strong) NSString *destination; @property (strong) NSString *output_format; @property (strong) NSString *stream_key; +@property (assign) int stream_delay; @property (strong) FFMpegTask *ffmpeg_out; @property (strong) id settingsController; @property (strong) NSColor *textColor; +@property (assign) NSUInteger delay_buffer_frames; +@property (assign) BOOL buffer_draining; + +//stats, mostly we just interrogate the ffmpeg_out object for these, but bouncing +//through this class allows us to be a bit smarter about the UI status updates + +@property (assign) double input_framerate; +@property (assign) int buffered_frame_count; +@property (assign) int buffered_frame_size; +@property (assign) int dropped_frame_count; +@property (assign) double output_framerate; +@property (assign) double output_bitrate; -@property BOOL active; +@property (assign) BOOL active; -(id)initWithType:(NSString *)type; -(void)stopOutput; --(void) attachOutput:(id) settingsController; +-(void) attachOutput; +-(void) writeEncodedData:(CapturedFrameData *)frameData; +-(void) updateStatistics; +-(void) reset; + + diff --git a/CocoaSplit/OutputDestination.m b/CocoaSplit/OutputDestination.m index a0f62c6b..8e3e7835 100644 --- a/CocoaSplit/OutputDestination.m +++ b/CocoaSplit/OutputDestination.m @@ -22,17 +22,21 @@ [aCoder encodeObject:self.type_name forKey:@"type_name"]; [aCoder encodeObject:self.output_format forKey:@"output_format"]; [aCoder encodeBool:self.active forKey:@"active"]; + [aCoder encodeInteger:self.stream_delay forKey:@"stream_delay"]; + } -(id) initWithCoder:(NSCoder *)aDecoder { - if (self = [super init]) + if (self = [self init]) { self.destination = [aDecoder decodeObjectForKey:@"destination"]; self.name = [aDecoder decodeObjectForKey:@"name"]; self.type_name = [aDecoder decodeObjectForKey:@"type_name"]; self.output_format = [aDecoder decodeObjectForKey:@"output_format"]; self.active = [aDecoder decodeBoolForKey:@"active"]; + self.stream_delay = (int)[aDecoder decodeIntegerForKey:@"stream_delay"]; + } return self; @@ -90,11 +94,9 @@ if (is_active != _active) { _active = is_active; - if (!is_active && self.ffmpeg_out) - { - [self.ffmpeg_out stopProcess]; - } else if (is_active && self.ffmpeg_out) { - [self attachOutput:self.settingsController]; + if (!is_active) { + + [self reset]; } } @@ -120,13 +122,41 @@ } --(void) stopOutput + +-(void) reset { - if (self.ffmpeg_out && self.active) + self.buffer_draining = NO; + [_delayBuffer removeAllObjects]; + + if (self.ffmpeg_out) { [self.ffmpeg_out stopProcess]; self.ffmpeg_out = nil; } + + _output_start_time = 0.0f; + + +} + + +-(void) stopOutput +{ + + if (self.stream_delay > 0 && [_delayBuffer count] > 0 && self.ffmpeg_out) + { + self.buffer_draining = YES; + dispatch_async(dispatch_get_main_queue(), ^{ + self.textColor = [NSColor orangeColor]; + }); + return; + } + + + if (self.active) + { + [self reset]; + } } @@ -135,9 +165,13 @@ if (self = [super init]) { + self.type_name = type; - self.textColor = [NSColor blackColor]; + _output_start_time = 0.0f; + _delayBuffer = [[NSMutableArray alloc] init]; + self.delay_buffer_frames = 0; + } return self; @@ -145,9 +179,15 @@ } --(void) attachOutput:(id) settingsController +-(void) attachOutput { FFMpegTask *newout; + if (!self.active) + { + return; + } + + NSLog(@"ATTACHING OUTPUT"); if (!self.ffmpeg_out) { newout = [[FFMpegTask alloc] init]; @@ -155,43 +195,146 @@ newout = self.ffmpeg_out; } - self.settingsController = settingsController; - newout.framerate = settingsController.captureFPS; + /* + if (self.stream_delay > 0) + { + _output_start_time = [self.settingsController mach_time_seconds] + self.stream_delay; + } + */ + + + newout.framerate = self.settingsController.captureFPS; newout.stream_output = [self.destination stringByStandardizingPath]; newout.stream_format = self.output_format; - newout.settingsController = settingsController; - newout.active = self.active; - newout.samplerate = settingsController.audioSamplerate; - newout.audio_bitrate = settingsController.audioBitrate; + newout.settingsController = self.settingsController; + newout.samplerate = self.settingsController.audioSamplerate; + newout.audio_bitrate = self.settingsController.audioBitrate; self.ffmpeg_out = newout; [self.ffmpeg_out addObserver:self forKeyPath:@"errored" options:NSKeyValueObservingOptionNew context:NULL]; + [self.ffmpeg_out addObserver:self forKeyPath:@"active" options:NSKeyValueObservingOptionNew context:NULL]; + + self.ffmpeg_out.active = self.active; + } - (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - if ([keyPath isEqualToString:@"errored"]) + + NSColor *newColor = nil; + + if ([keyPath isEqualToString:@"active"]) { + BOOL activeVal = [[change objectForKey:NSKeyValueChangeNewKey] boolValue]; + if (activeVal == YES) + { + newColor = [NSColor greenColor]; + } else { + newColor = [NSColor blackColor]; + } + + } else if ([keyPath isEqualToString:@"errored"]) { BOOL errVal = [[change objectForKey:NSKeyValueChangeNewKey] boolValue]; if (errVal == YES) { - //bounce through main thread because it triggers a notification to the UI and sometimes it won't properly update due to threads HURRRRRRR??? - //this can't be right, it's too...stupid - - dispatch_async(dispatch_get_main_queue(), ^{ - self.textColor = [NSColor redColor]; - }); + newColor = [NSColor redColor]; } } + + + if (newColor) + { + dispatch_async(dispatch_get_main_queue(), ^{ + + self.textColor = newColor; + }); + } + } +-(void) writeEncodedData:(CapturedFrameData *)frameData +{ + + CapturedFrameData *sendData = nil; + double current_time = [self.settingsController mach_time_seconds]; + + if (self.active) + { + + + if (self.stream_delay > 0 && _output_start_time == 0.0f && frameData) + { + _output_start_time = current_time + (double)self.stream_delay; + } + + + if (frameData) + { + [_delayBuffer addObject:frameData]; + } + + + + + if ((current_time >= _output_start_time) && ([_delayBuffer count] > 0)) + { + + + if (!self.ffmpeg_out) + { + [self attachOutput]; + } + + sendData = [_delayBuffer objectAtIndex:0]; + [_delayBuffer removeObjectAtIndex:0]; + } + + + if (sendData) + { + [self.ffmpeg_out writeEncodedData:sendData]; + } + + if (self.buffer_draining) + { + if ([_delayBuffer count] <= 0) + { + [self stopOutput]; + } + } + } + +} + + +-(void) updateStatistics +{ + + if (self.ffmpeg_out) + { + [self.ffmpeg_out updateInputStats]; + [self.ffmpeg_out updateOutputStats]; + self.output_framerate = self.ffmpeg_out.output_framerate; + self.output_bitrate = self.ffmpeg_out.output_bitrate; + self.input_framerate = self.ffmpeg_out.input_framerate; + self.dropped_frame_count = self.ffmpeg_out.dropped_frame_count; + self.buffered_frame_count = self.ffmpeg_out.buffered_frame_count; + self.buffered_frame_size = self.ffmpeg_out.buffered_frame_size; + } + + self.delay_buffer_frames = [_delayBuffer count]; + +} + + + -(NSString *)description { @@ -199,6 +342,7 @@ } + -(void)dealloc { if (self.ffmpeg_out) diff --git a/CocoaSplit/PreviewView.h b/CocoaSplit/PreviewView.h index 22d245cb..66a0277d 100644 --- a/CocoaSplit/PreviewView.h +++ b/CocoaSplit/PreviewView.h @@ -59,6 +59,7 @@ static CVReturn displayLinkRender(CVDisplayLinkRef displayLink, const CVTimeStam NSScreen *_fullscreenOn; + } @@ -68,6 +69,7 @@ static CVReturn displayLinkRender(CVDisplayLinkRef displayLink, const CVTimeStam +@property (strong) NSColor *statusColor; @property (unsafe_unretained) IBOutlet id controller; diff --git a/CocoaSplit/PreviewView.m b/CocoaSplit/PreviewView.m index 69039992..02c89ccd 100644 --- a/CocoaSplit/PreviewView.m +++ b/CocoaSplit/PreviewView.m @@ -295,7 +295,7 @@ self = [super initWithFrame:frameRect pixelFormat:pf]; if (self) { - long swapInterval = 1; + long swapInterval = 0; [[self openGLContext] setValues:(GLint *)&swapInterval forParameter:NSOpenGLCPSwapInterval]; @@ -327,11 +327,18 @@ static CVReturn displayLinkRender(CVDisplayLinkRef displayLink, const CVTimeStam CVImageBufferRef displayFrame = NULL; displayFrame = [myself.controller currentFrame]; + myself.statusColor = [myself.controller statusColor]; + + if (myself.statusColor) + { + myself.statusColor = [myself.statusColor colorUsingColorSpaceName:NSDeviceRGBColorSpace]; + } if (displayFrame) { + [myself drawFrame:displayFrame]; CVPixelBufferRelease(displayFrame); @@ -510,7 +517,20 @@ static CVReturn displayLinkRender(CVDisplayLinkRef displayLink, const CVTimeStam NSRect frame = self.frame; - glClearColor(0.0, 0.0, 0.0, 0.0); + GLclampf rval = 0.0; + GLclampf gval = 0.0; + GLclampf bval = 0.0; + GLclampf aval = 0.0; + + if (self.statusColor) + { + rval = [self.statusColor redComponent]; + gval = [self.statusColor greenComponent]; + bval = [self.statusColor blueComponent]; + aval = [self.statusColor alphaComponent]; + } + + glClearColor(rval, gval, bval, aval); glClear(GL_COLOR_BUFFER_BIT); glViewport(0, 0, frame.size.width, frame.size.height); diff --git a/CocoaSplit/advancedPrefPanel.xib b/CocoaSplit/advancedPrefPanel.xib index 207af7d7..461cc31d 100644 --- a/CocoaSplit/advancedPrefPanel.xib +++ b/CocoaSplit/advancedPrefPanel.xib @@ -63,6 +63,23 @@ DQ + + + + + + + + + + diff --git a/CocoaSplit/en.lproj/MainMenu.xib b/CocoaSplit/en.lproj/MainMenu.xib index c65c2377..b88d097f 100644 --- a/CocoaSplit/en.lproj/MainMenu.xib +++ b/CocoaSplit/en.lproj/MainMenu.xib @@ -178,26 +178,26 @@ - + - + - + - - + + - + - - + + @@ -209,7 +209,7 @@ - - - + @@ -449,8 +449,8 @@ - - + + @@ -474,7 +474,7 @@ - + @@ -482,8 +482,8 @@ - - + + @@ -494,8 +494,8 @@ - - + + @@ -511,8 +511,8 @@ - - + + @@ -531,8 +531,8 @@ - - + + @@ -553,8 +553,8 @@ - - + + @@ -563,7 +563,7 @@ - + @@ -571,27 +571,27 @@ - - + + - - + + - + - + - + @@ -600,7 +600,28 @@ - + + + + + + + + + + + + + + + + NSIsNil + + + + + + @@ -616,7 +637,7 @@ - + @@ -632,7 +653,7 @@ - + @@ -658,11 +679,11 @@ @@ -670,8 +691,8 @@ - - - + + @@ -708,8 +729,8 @@ - - - + + @@ -731,8 +752,8 @@ - - + + @@ -740,22 +761,22 @@ - - + + - - + + - - + + @@ -778,8 +799,8 @@ - - + + @@ -787,8 +808,8 @@ - - + + @@ -814,8 +835,8 @@ - - + + @@ -823,8 +844,8 @@ - - - + + @@ -857,7 +878,7 @@ - + @@ -869,7 +890,6 @@ - @@ -878,6 +898,7 @@ + @@ -899,7 +920,6 @@ - @@ -919,8 +939,9 @@ + - + @@ -928,9 +949,9 @@ - + @@ -956,30 +977,29 @@ + - - - + - - + + - - + + - + @@ -1016,7 +1036,7 @@ - + @@ -1034,10 +1054,27 @@ - + + + + + + + + + + + + + + + + + + @@ -1051,7 +1088,7 @@ - + @@ -1068,7 +1105,7 @@ - + @@ -1085,7 +1122,7 @@ - + @@ -1103,7 +1140,7 @@ - + @@ -1116,24 +1153,23 @@ - + - - - - + + + @@ -1142,8 +1178,8 @@ -