diff --git a/CapturedFrameProtocol.h b/CapturedFrameProtocol.h new file mode 100644 index 00000000..cbc38430 --- /dev/null +++ b/CapturedFrameProtocol.h @@ -0,0 +1,16 @@ +// +// CapturedFrameProtocol.h +// CocoaSplit +// +// Created by Zakk on 11/10/12. +// Copyright (c) 2012 Zakk. All rights reserved. +// + +#import + +@protocol CapturedFrameProtocol + +-(void) newCapturedFrame:(IOSurfaceID)ioxpc reply:(void (^)())reply; + + +@end diff --git a/CocoaSplit.xcodeproj/project.pbxproj b/CocoaSplit.xcodeproj/project.pbxproj index 5fa74d77..7ea6aef2 100644 --- a/CocoaSplit.xcodeproj/project.pbxproj +++ b/CocoaSplit.xcodeproj/project.pbxproj @@ -38,6 +38,15 @@ 34792AF316103FF60065A859 /* DesktopCapture.m in Sources */ = {isa = PBXBuildFile; fileRef = 34792AF216103FF60065A859 /* DesktopCapture.m */; }; 34792AF616104AA70065A859 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34792AF516104AA70065A859 /* IOKit.framework */; }; 34792AFB1611A6B90065A859 /* IOSurface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34792AFA1611A6B90065A859 /* IOSurface.framework */; }; + 34AE3C30164E3E020052C95E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34AE3C2F164E3E020052C95E /* Foundation.framework */; }; + 34AE3C36164E3E020052C95E /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 34AE3C34164E3E020052C95E /* InfoPlist.strings */; }; + 34AE3C39164E3E020052C95E /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 34AE3C38164E3E020052C95E /* main.m */; }; + 34AE3C3F164E3F230052C95E /* zakk.lol.QTCaptureHelper.xpc in CopyFiles */ = {isa = PBXBuildFile; fileRef = 34AE3C2E164E3E020052C95E /* zakk.lol.QTCaptureHelper.xpc */; }; + 34AE3C44164E410D0052C95E /* XPCListenerDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 34AE3C43164E410D0052C95E /* XPCListenerDelegate.m */; }; + 34AE3C4B164E68820052C95E /* QTKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 340FE4E015F3581200E4CE4E /* QTKit.framework */; }; + 34AE3C6B164F04B30052C95E /* IOSurface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34AE3C6A164F04B30052C95E /* IOSurface.framework */; }; + 34AE3C6D164F04C90052C95E /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34AE3C6C164F04C90052C95E /* CoreVideo.framework */; }; + 34B74BC91648D23B00818DE2 /* QTCapture.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B74BC81648D23B00818DE2 /* QTCapture.m */; }; 34C6C3BF15FB5B440018A18B /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34C6C3BE15FB5B440018A18B /* OpenGL.framework */; }; /* End PBXBuildFile section */ @@ -49,6 +58,13 @@ remoteGlobalIDString = 340FE49C15F3417E00E4CE4E; remoteInfo = CocoaSplit; }; + 34AE3C40164E3FAD0052C95E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 340FE49415F3417E00E4CE4E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 34AE3C2D164E3E020052C95E; + remoteInfo = QTCaptureHelper; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -62,6 +78,16 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 34AE3C3E164E3EAE0052C95E /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = Contents/XPCServices; + dstSubfolderSpec = 1; + files = ( + 34AE3C3F164E3F230052C95E /* zakk.lol.QTCaptureHelper.xpc in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -112,6 +138,20 @@ 34792AF216103FF60065A859 /* DesktopCapture.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DesktopCapture.m; sourceTree = ""; }; 34792AF516104AA70065A859 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; 34792AFA1611A6B90065A859 /* IOSurface.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOSurface.framework; path = System/Library/Frameworks/IOSurface.framework; sourceTree = SDKROOT; }; + 34AE3C2E164E3E020052C95E /* zakk.lol.QTCaptureHelper.xpc */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = zakk.lol.QTCaptureHelper.xpc; sourceTree = BUILT_PRODUCTS_DIR; }; + 34AE3C2F164E3E020052C95E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 34AE3C33164E3E020052C95E /* QTCaptureHelper-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "QTCaptureHelper-Info.plist"; sourceTree = ""; }; + 34AE3C35164E3E020052C95E /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 34AE3C37164E3E020052C95E /* QTCaptureHelper-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "QTCaptureHelper-Prefix.pch"; sourceTree = ""; }; + 34AE3C38164E3E020052C95E /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 34AE3C42164E410D0052C95E /* XPCListenerDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XPCListenerDelegate.h; sourceTree = ""; }; + 34AE3C43164E410D0052C95E /* XPCListenerDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XPCListenerDelegate.m; sourceTree = ""; }; + 34AE3C46164E560C0052C95E /* QTHelperProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = QTHelperProtocol.h; sourceTree = ""; }; + 34AE3C69164F01800052C95E /* CapturedFrameProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CapturedFrameProtocol.h; sourceTree = ""; }; + 34AE3C6A164F04B30052C95E /* IOSurface.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOSurface.framework; path = ../../../../System/Library/Frameworks/IOSurface.framework; sourceTree = ""; }; + 34AE3C6C164F04C90052C95E /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = ../../../../System/Library/Frameworks/CoreVideo.framework; sourceTree = ""; }; + 34B74BC71648D23B00818DE2 /* QTCapture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QTCapture.h; sourceTree = ""; }; + 34B74BC81648D23B00818DE2 /* QTCapture.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QTCapture.m; sourceTree = ""; }; 34C6C3BE15FB5B440018A18B /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ @@ -145,12 +185,26 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 34AE3C2B164E3E020052C95E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 34AE3C6D164F04C90052C95E /* CoreVideo.framework in Frameworks */, + 34AE3C6B164F04B30052C95E /* IOSurface.framework in Frameworks */, + 34AE3C4B164E68820052C95E /* QTKit.framework in Frameworks */, + 34AE3C30164E3E020052C95E /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 340FE49215F3417E00E4CE4E = { isa = PBXGroup; children = ( + 34AE3C6C164F04C90052C95E /* CoreVideo.framework */, + 34AE3C6A164F04B30052C95E /* IOSurface.framework */, + 34AE3C45164E53D00052C95E /* XPCShared */, 34792AFA1611A6B90065A859 /* IOSurface.framework */, 34792AF516104AA70065A859 /* IOKit.framework */, 34C6C3BE15FB5B440018A18B /* OpenGL.framework */, @@ -160,6 +214,7 @@ 340FE4E015F3581200E4CE4E /* QTKit.framework */, 340FE4A715F3417E00E4CE4E /* CocoaSplit */, 340FE4C415F3417E00E4CE4E /* CocoaSplitTests */, + 34AE3C31164E3E020052C95E /* QTCaptureHelper */, 340FE4A015F3417E00E4CE4E /* Frameworks */, 340FE49E15F3417E00E4CE4E /* Products */, ); @@ -170,6 +225,7 @@ children = ( 340FE49D15F3417E00E4CE4E /* CocoaSplit.app */, 340FE4BE15F3417E00E4CE4E /* CocoaSplitTests.octest */, + 34AE3C2E164E3E020052C95E /* zakk.lol.QTCaptureHelper.xpc */, ); name = Products; sourceTree = ""; @@ -184,6 +240,7 @@ 340FE4D815F346CC00E4CE4E /* AVFoundation.framework */, 340FE4A115F3417E00E4CE4E /* Cocoa.framework */, 340FE4BF15F3417E00E4CE4E /* SenTestingKit.framework */, + 34AE3C2F164E3E020052C95E /* Foundation.framework */, 340FE4A315F3417E00E4CE4E /* Other Frameworks */, ); name = Frameworks; @@ -212,6 +269,8 @@ 342346D415FA301600C8C77E /* SyphonCapture.m */, 342346CD15F9F07E00C8C77E /* AbstractCaptureDevice.h */, 342346CE15F9F07E00C8C77E /* AbstractCaptureDevice.m */, + 34B74BC71648D23B00818DE2 /* QTCapture.h */, + 34B74BC81648D23B00818DE2 /* QTCapture.m */, 342346CC15F9D52000C8C77E /* CaptureSessionProtocol.h */, 342346BC15F6103D00C8C77E /* FFMpegTask.h */, 342346BD15F6103D00C8C77E /* FFMpegTask.m */, @@ -258,6 +317,36 @@ name = "Supporting Files"; sourceTree = ""; }; + 34AE3C31164E3E020052C95E /* QTCaptureHelper */ = { + isa = PBXGroup; + children = ( + 34AE3C38164E3E020052C95E /* main.m */, + 34AE3C42164E410D0052C95E /* XPCListenerDelegate.h */, + 34AE3C43164E410D0052C95E /* XPCListenerDelegate.m */, + 34AE3C32164E3E020052C95E /* Supporting Files */, + ); + path = QTCaptureHelper; + sourceTree = ""; + }; + 34AE3C32164E3E020052C95E /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 34AE3C33164E3E020052C95E /* QTCaptureHelper-Info.plist */, + 34AE3C34164E3E020052C95E /* InfoPlist.strings */, + 34AE3C37164E3E020052C95E /* QTCaptureHelper-Prefix.pch */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 34AE3C45164E53D00052C95E /* XPCShared */ = { + isa = PBXGroup; + children = ( + 34AE3C46164E560C0052C95E /* QTHelperProtocol.h */, + 34AE3C69164F01800052C95E /* CapturedFrameProtocol.h */, + ); + name = XPCShared; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -269,10 +358,12 @@ 340FE49A15F3417E00E4CE4E /* Frameworks */, 340FE49B15F3417E00E4CE4E /* Resources */, 342346DC15FA34B400C8C77E /* CopyFiles */, + 34AE3C3E164E3EAE0052C95E /* CopyFiles */, ); buildRules = ( ); dependencies = ( + 34AE3C41164E3FAD0052C95E /* PBXTargetDependency */, ); name = CocoaSplit; productName = CocoaSplit; @@ -298,6 +389,23 @@ productReference = 340FE4BE15F3417E00E4CE4E /* CocoaSplitTests.octest */; productType = "com.apple.product-type.bundle"; }; + 34AE3C2D164E3E020052C95E /* QTCaptureHelper */ = { + isa = PBXNativeTarget; + buildConfigurationList = 34AE3C3C164E3E020052C95E /* Build configuration list for PBXNativeTarget "QTCaptureHelper" */; + buildPhases = ( + 34AE3C2A164E3E020052C95E /* Sources */, + 34AE3C2B164E3E020052C95E /* Frameworks */, + 34AE3C2C164E3E020052C95E /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = QTCaptureHelper; + productName = QTCaptureHelper; + productReference = 34AE3C2E164E3E020052C95E /* zakk.lol.QTCaptureHelper.xpc */; + productType = "com.apple.product-type.bundle"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -321,6 +429,7 @@ targets = ( 340FE49C15F3417E00E4CE4E /* CocoaSplit */, 340FE4BD15F3417E00E4CE4E /* CocoaSplitTests */, + 34AE3C2D164E3E020052C95E /* QTCaptureHelper */, ); }; /* End PBXProject section */ @@ -346,6 +455,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 34AE3C2C164E3E020052C95E /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 34AE3C36164E3E020052C95E /* InfoPlist.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -378,6 +495,7 @@ 342346D515FA301600C8C77E /* SyphonCapture.m in Sources */, 34210B3E1606001200362FC8 /* OutputDestination.m in Sources */, 34792AF316103FF60065A859 /* DesktopCapture.m in Sources */, + 34B74BC91648D23B00818DE2 /* QTCapture.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -389,6 +507,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 34AE3C2A164E3E020052C95E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 34AE3C39164E3E020052C95E /* main.m in Sources */, + 34AE3C44164E410D0052C95E /* XPCListenerDelegate.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -397,6 +524,11 @@ target = 340FE49C15F3417E00E4CE4E /* CocoaSplit */; targetProxy = 340FE4C215F3417E00E4CE4E /* PBXContainerItemProxy */; }; + 34AE3C41164E3FAD0052C95E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 34AE3C2D164E3E020052C95E /* QTCaptureHelper */; + targetProxy = 34AE3C40164E3FAD0052C95E /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -432,6 +564,14 @@ name = InfoPlist.strings; sourceTree = ""; }; + 34AE3C34164E3E020052C95E /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 34AE3C35164E3E020052C95E /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ @@ -555,6 +695,49 @@ }; name = Release; }; + 34AE3C3A164E3E020052C95E /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = NO; + CLANG_WARN_EMPTY_BODY = YES; + CODE_SIGN_IDENTITY = ""; + COMBINE_HIDPI_IMAGES = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "QTCaptureHelper/QTCaptureHelper-Prefix.pch"; + INFOPLIST_FILE = "QTCaptureHelper/QTCaptureHelper-Info.plist"; + MACH_O_TYPE = mh_execute; + MACOSX_DEPLOYMENT_TARGET = 10.8; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "zakk.lol.$(TARGET_NAME:rfc1034identifier)"; + SDKROOT = ""; + VALID_ARCHS = "i386 x86_64"; + WRAPPER_EXTENSION = xpc; + }; + name = Debug; + }; + 34AE3C3B164E3E020052C95E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = NO; + CLANG_WARN_EMPTY_BODY = YES; + CODE_SIGN_IDENTITY = ""; + COMBINE_HIDPI_IMAGES = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "QTCaptureHelper/QTCaptureHelper-Prefix.pch"; + INFOPLIST_FILE = "QTCaptureHelper/QTCaptureHelper-Info.plist"; + MACH_O_TYPE = mh_execute; + MACOSX_DEPLOYMENT_TARGET = 10.8; + PRODUCT_NAME = "zakk.lol.$(TARGET_NAME:rfc1034identifier)"; + SDKROOT = ""; + VALID_ARCHS = "i386 x86_64"; + WRAPPER_EXTENSION = xpc; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -585,6 +768,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 34AE3C3C164E3E020052C95E /* Build configuration list for PBXNativeTarget "QTCaptureHelper" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 34AE3C3A164E3E020052C95E /* Debug */, + 34AE3C3B164E3E020052C95E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 340FE49415F3417E00E4CE4E /* Project object */; diff --git a/CocoaSplit/AVFCapture.m b/CocoaSplit/AVFCapture.m index 8e4364e7..604d96e3 100644 --- a/CocoaSplit/AVFCapture.m +++ b/CocoaSplit/AVFCapture.m @@ -59,7 +59,7 @@ for(devinstance in devices) { - + NSLog(@"Inputs %@", devinstance.linkedDevices); [retArray addObject:[[AbstractCaptureDevice alloc] initWithName:[devinstance localizedName] device:devinstance uniqueID:devinstance.uniqueID]]; } diff --git a/CocoaSplit/CaptureController.h b/CocoaSplit/CaptureController.h index e2f0a0b1..870bbd93 100644 --- a/CocoaSplit/CaptureController.h +++ b/CocoaSplit/CaptureController.h @@ -10,6 +10,7 @@ #import #import #import "AVFCapture.h" +#import "QTCapture.h" #import #import "FFMpegTask.h" #import "CaptureSessionProtocol.h" diff --git a/CocoaSplit/CaptureController.m b/CocoaSplit/CaptureController.m index 11df802a..94cf2c9d 100644 --- a/CocoaSplit/CaptureController.m +++ b/CocoaSplit/CaptureController.m @@ -30,12 +30,12 @@ self.streamingDestination = nil; - if ([self.selectedDestinationType isEqualToString:@"file"]) - { + //if ([self.selectedDestinationType isEqualToString:@"file"]) + //{ panelName = @"FilePanel"; - } else { - panelName = @"StreamServicePanel"; - } + //} else { + // panelName = @"StreamServicePanel"; + //} [NSBundle loadNibNamed:panelName owner:self]; } @@ -106,6 +106,8 @@ _video_capture_session = [[DesktopCapture alloc ] init]; } else if ([selectedVideoType isEqualToString:@"AVFoundation"]) { _video_capture_session = [[AVFCapture alloc] init]; + } else if ([selectedVideoType isEqualToString:@"QTCapture"]) { + _video_capture_session = [[QTCapture alloc] init]; } else { _video_capture_session = nil; } @@ -150,8 +152,9 @@ @"rtmp" : @"RTMP Stream"}; - self.videoTypes = @[@"Desktop", @"AVFoundation"]; + self.videoTypes = @[@"Desktop", @"AVFoundation", @"QTCapture"]; self.selectedVideoType = [self.videoTypes objectAtIndex:0]; + } return self; diff --git a/CocoaSplit/FFMpegTask.m b/CocoaSplit/FFMpegTask.m index c71a9eb2..46f0bdea 100644 --- a/CocoaSplit/FFMpegTask.m +++ b/CocoaSplit/FFMpegTask.m @@ -150,8 +150,12 @@ void getAudioExtradata(char *cookie, char **buffer, size_t *size) AVOutputFormat *av_out_fmt; + if (_stream_format) { + avformat_alloc_output_context2(&_av_fmt_ctx, NULL, [_stream_format UTF8String], [_stream_output UTF8String]); + } else { + avformat_alloc_output_context2(&_av_fmt_ctx, NULL, NULL, [_stream_output UTF8String]); + } - avformat_alloc_output_context2(&_av_fmt_ctx, NULL, [_stream_format UTF8String], [_stream_output UTF8String]); if (!_av_fmt_ctx) { NSLog(@"No av_fmt_ctx"); @@ -171,7 +175,6 @@ void getAudioExtradata(char *cookie, char **buffer, size_t *size) AVCodecContext *c_ctx = _av_video_stream->codec; - c_ctx->codec_type = AVMEDIA_TYPE_VIDEO; c_ctx->codec_id = AV_CODEC_ID_H264; c_ctx->width = _width; @@ -198,6 +201,7 @@ void getAudioExtradata(char *cookie, char **buffer, size_t *size) a_ctx->channels = 2; a_ctx->extradata = (unsigned char *)_audio_extradata; a_ctx->extradata_size = (int)_audio_extradata_size; + a_ctx->frame_size = (_samplerate * 2 * 2) / _framerate; CMFormatDescriptionRef fmt; diff --git a/CocoaSplit/OutputDestination.m b/CocoaSplit/OutputDestination.m index 39656463..816d73b9 100644 --- a/CocoaSplit/OutputDestination.m +++ b/CocoaSplit/OutputDestination.m @@ -51,6 +51,7 @@ if (_destination) return _destination; + if ([_type_name isEqualToString:@"rtmp"]) { _destination = [NSString stringWithFormat:@"%@/%@", self.server_name, self.stream_key]; @@ -66,6 +67,10 @@ { NSLog(@"Destination set to %@", destination); + if ([destination hasPrefix:@"rtmp://"]) + { + self.output_format = @"FLV"; + } _destination = destination; } @@ -74,13 +79,8 @@ { if (self = [super init]) { - self.output_format = @"FLV"; + //self.output_format = @"FLV"; self.type_name = type; - if ([type isEqualToString:@"twitchtv"]) - { - self.output_format = @"FLV"; - self.name = @"Twitch.TV"; - } } return self; diff --git a/CocoaSplit/QTCapture.h b/CocoaSplit/QTCapture.h new file mode 100644 index 00000000..3567bdd5 --- /dev/null +++ b/CocoaSplit/QTCapture.h @@ -0,0 +1,36 @@ +// +// QTCapture.h +// CocoaSplit +// +// Created by Zakk on 11/6/12. +// Copyright (c) 2012 Zakk. All rights reserved. +// + +#import +#import +#import +#import "CaptureSessionProtocol.h" +#import "CapturedFrameProtocol.h" +#import "QTHelperProtocol.h" + +@interface QTCapture : NSObject +{ + NSXPCConnection *_xpcConnection; + id _xpcProxy; + IOSurfaceRef _currentFrame; + +} + + +@property (strong) id videoInputDevice; +@property (strong) id videoDelegate; +@property (assign) int videoCaptureFPS; + + +-(bool) startCaptureSession:(NSError **)error; +-(bool) stopCaptureSession; + + + +@end + diff --git a/CocoaSplit/QTCapture.m b/CocoaSplit/QTCapture.m new file mode 100644 index 00000000..c3cb7808 --- /dev/null +++ b/CocoaSplit/QTCapture.m @@ -0,0 +1,330 @@ +// +// QTCapture.m +// CocoaSplit +// +// Created by Zakk on 11/6/12. +// Copyright (c) 2012 Zakk. All rights reserved. +// + +#import "QTCapture.h" +#import "AbstractCaptureDevice.h" +#import "QTHelperProtocol.h" +#import "CapturedFrameProtocol.h" + +@implementation QTCapture + + +-(id) init +{ + self = [super init]; + if (self) + { + NSXPCInterface *xpcInterface = [NSXPCInterface interfaceWithProtocol:@protocol(QTHelperProtocol)]; + NSXPCInterface *xpcCallbackInterface = [NSXPCInterface interfaceWithProtocol:@protocol(CapturedFrameProtocol)]; + + _xpcConnection = [[NSXPCConnection alloc] initWithServiceName:@"zakk.lol.QTCaptureHelper"]; + + [_xpcConnection setRemoteObjectInterface:xpcInterface]; + [_xpcConnection setExportedInterface:xpcCallbackInterface]; + [_xpcConnection setExportedObject:self]; + + NSLog(@"SETUP CONNECTION TO LISTENER"); + [_xpcConnection resume]; + _xpcProxy = [_xpcConnection remoteObjectProxy]; + NSLog(@"GOT PROXY OBJECT"); + + + } + return self; + +} + + +-(void) setVideoDimensions:(int)width height:(int)height +{ + return; +} + + +-(bool) providesVideo +{ + return YES; +} + +-(bool) providesAudio +{ + return NO; +} + + + + + +-(bool) setActiveVideoDevice:(AbstractCaptureDevice *)newDev +{ + NSLog(@"SET VIDEO DEVICE TO %@", [newDev uniqueID]); + _videoInputDevice = [newDev uniqueID]; + return YES; + +} + + +-(NSArray *) availableVideoDevices +{ + + dispatch_semaphore_t reply_s = dispatch_semaphore_create(0); + + NSMutableArray *__block retArray; + NSLog(@"PROXY %@", _xpcProxy); + NSLog(@"CONNECTION %@", _xpcConnection); + [_xpcProxy testMethod]; + NSLog(@"CALLED TEST METHOD FROM SPLIT"); + + [_xpcProxy listCaptureDevices:^(NSArray *r_devices) { + NSLog(@"REMOTE DEVICES %@", r_devices); + retArray = [[NSMutableArray alloc] init]; + NSDictionary *devinstance; + for (devinstance in r_devices) + { + [retArray addObject:[[AbstractCaptureDevice alloc] initWithName:[devinstance valueForKey:@"name"] device:[devinstance valueForKey:@"id"] uniqueID:[devinstance valueForKey:@"id"]]]; + } + dispatch_semaphore_signal(reply_s); + }]; + NSLog(@"SEMAPHORE WAIT"); + dispatch_semaphore_wait(reply_s, DISPATCH_TIME_FOREVER); + reply_s = nil; + return (NSArray *)retArray; + +} + +-(void) newCapturedFrame:(IOSurfaceID)ioxpc reply:(void (^)())reply +{ + + IOSurfaceRef frameIOref = IOSurfaceLookup(ioxpc); + if (frameIOref) + { + + @synchronized(self) { + if (_currentFrame) + { + IOSurfaceDecrementUseCount(_currentFrame); + //CFRelease(_currentFrame); + } + + _currentFrame = frameIOref; + IOSurfaceIncrementUseCount(_currentFrame); + //CFRetain(_currentFrame); + } + + + } + + // ALWAYS reply + reply(); +} + + + + +-(bool) stopCaptureSession +{ + [_xpcProxy stopXPCCaptureSession]; + return YES; +} + + +-(bool) startCaptureSession:(NSError **)error +{ + + NSLog(@"CALLING STARTXPC WITH %@", _videoInputDevice); + [_xpcProxy startXPCCaptureSession:_videoInputDevice]; + + return YES; +} + + +-(bool) setupCaptureSession:(NSError *__autoreleasing *)therror +{ + + return YES; + +} +/* +-(bool) setupCaptureSession:(NSError **)therror +{ + + + AVCaptureDeviceInput *video_capture_input; + AVCaptureDeviceInput *audio_capture_input; + + if (_capture_session) + return YES; + + + NSLog(@"Starting setup capture"); + if (_videoDelegate || _audioDelegate) + { + _capture_session = [[AVCaptureSession alloc] init]; + } + + if (_videoDelegate) + { + if (!_videoInputDevice) + { + NSLog(@"No video input device"); + *therror = [NSError errorWithDomain:@"videoCapture" code:100 userInfo:@{NSLocalizedDescriptionKey : @"Must select video capture device first"}]; + return NO; + } + + _capture_session = [[AVCaptureSession alloc] init]; + + + video_capture_input = [AVCaptureDeviceInput deviceInputWithDevice:_videoInputDevice error:therror]; + + if (!video_capture_input) + { + NSLog(@"No video capture input?"); + return NO; + } + + if ([_capture_session canAddInput:video_capture_input]) + { + [_capture_session addInput:video_capture_input]; + + } else { + NSLog(@"Can't add video_capture_input"); + *therror = [NSError errorWithDomain:@"videoCapture" code:120 userInfo:@{NSLocalizedDescriptionKey : @"Could not add video input to capture session"}]; + return NO; + } + + _video_capture_output = [[AVCaptureVideoDataOutput alloc] init]; + + + if ([_capture_session canAddOutput:_video_capture_output]) + { + [_capture_session addOutput:_video_capture_output]; + } else { + NSLog(@"Can't add video capture output"); + *therror = [NSError errorWithDomain:@"videoCapture" code:130 userInfo:@{NSLocalizedDescriptionKey : @"Could not add video output to capture session"}]; + return NO; + } + } + + if (_audioDelegate) + { + + if (_audioInputDevice) + { + audio_capture_input = [AVCaptureDeviceInput deviceInputWithDevice:_audioInputDevice error:therror]; + + if (!audio_capture_input) + { + NSLog(@"No audio capture input?"); + return NO; + } + + if ([_capture_session canAddInput:audio_capture_input]) + { + [_capture_session addInput:audio_capture_input]; + + } else { + NSLog(@"Can't add audio input?"); + *therror = [NSError errorWithDomain:@"audioCapture" code:220 userInfo:@{NSLocalizedDescriptionKey : @"Could not add audio input to capture session"}]; + return NO; + } + + _audio_capture_output = [[AVCaptureAudioDataOutput alloc] init]; + + + + _audio_capture_output.audioSettings = @{AVFormatIDKey: [NSNumber numberWithInt:kAudioFormatMPEG4AAC], + AVSampleRateKey: [NSNumber numberWithFloat: 44100.0], + AVEncoderBitRateKey: [NSNumber numberWithInt:_audioBitrate*1000 ], + AVNumberOfChannelsKey: @2 + + }; + + + if ([_capture_session canAddOutput:_audio_capture_output]) + { + [_capture_session addOutput:_audio_capture_output]; + } else { + NSLog(@"Can't add audio capture output"); + *therror = [NSError errorWithDomain:@"audioCapture" code:230 userInfo:@{NSLocalizedDescriptionKey : @"Could not add audio output to capture session"}]; + return NO; + } + } else { + NSLog(@"No audio device?"); + *therror = [NSError errorWithDomain:@"audioCapture" code:240 userInfo:@{NSLocalizedDescriptionKey : @"Must select audio capture device first"}]; + return NO; + } + } + return YES; + +} + +*/ +void QTPixelBufferRelease(void *releaseRefCon, const void *baseAddress) +{ + + if (baseAddress) + free((void *)baseAddress); + + +} + +- (CVImageBufferRef) getCurrentFrame +{ + + CVImageBufferRef newbuf = NULL; + + @synchronized(self) + { + if (_currentFrame) + { + CVPixelBufferCreateWithIOSurface(NULL, _currentFrame, NULL, &newbuf); + return newbuf; + + + } + + } + + return newbuf; + + + + + +} + +/* +- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection +{ + + if (connection.output == _video_capture_output) + { + CVImageBufferRef videoFrame = CMSampleBufferGetImageBuffer(sampleBuffer); + + + + @synchronized(self) + { + if (_currentFrame) + { + CVPixelBufferRelease(_currentFrame); + } + + CVPixelBufferRetain(videoFrame); + _currentFrame = videoFrame; + } + } else if (connection.output == _audio_capture_output) { + + + [_audioDelegate captureOutputAudio:self didOutputSampleBuffer:sampleBuffer]; + } + +} + */ + +@end diff --git a/CocoaSplit/en.lproj/MainMenu.xib b/CocoaSplit/en.lproj/MainMenu.xib index 96bff36f..7e423980 100644 --- a/CocoaSplit/en.lproj/MainMenu.xib +++ b/CocoaSplit/en.lproj/MainMenu.xib @@ -1321,7 +1321,7 @@ - + 256 @@ -1329,6 +1329,7 @@ 268 {{250, 25}, {82, 32}} + _NS:9 YES @@ -1357,12 +1358,13 @@ 12 {{-1, 53}, {584, 329}} + _NS:9 1 - + 256 @@ -1911,7 +1913,6 @@ {{10, 33}, {564, 283}} - _NS:11 @@ -2177,7 +2178,7 @@ 2 - + 256 @@ -2194,7 +2195,7 @@ {478, 190} - + _NS:13 YES NO @@ -2208,11 +2209,10 @@ _NS:16 - - + + -2147483392 {{224, 0}, {16, 17}} - _NS:19 @@ -2388,11 +2388,10 @@ 4 - {{42, 31}, {480, 208}} - + _NS:9 {750, 750} 133682 @@ -2524,6 +2523,7 @@ {{10, 33}, {564, 283}} + _NS:28 @@ -2532,20 +2532,21 @@ - + 0 YES YES - + {582, 388} + - {{0, 0}, {1920, 1058}} + {{0, 0}, {1680, 1028}} {10000000000000, 10000000000000} YES @@ -7335,93 +7336,7 @@ 1844 - - - - AppDelegate - NSObject - - CaptureController - NSWindow - - - - captureController - CaptureController - - - window - NSWindow - - - - IBProjectSource - ./Classes/AppDelegate.h - - - - CaptureController - NSObject - - id - id - id - id - id - id - - - - addStreamingService: - id - - - closeCreateSheet: - id - - - openCreateSheet: - id - - - removeDestination: - id - - - streamButtonPushed: - id - - - videoRefresh: - id - - - - createSheet - NSWindow - - - createSheet - - createSheet - NSWindow - - - - IBProjectSource - ./Classes/CaptureController.h - - - - NSLayoutConstraint - NSObject - - IBProjectSource - ./Classes/NSLayoutConstraint.h - - - - + 0 IBCocoaFramework YES diff --git a/QTCaptureHelper/QTCaptureHelper-Info.plist b/QTCaptureHelper/QTCaptureHelper-Info.plist new file mode 100644 index 00000000..9dc45c8c --- /dev/null +++ b/QTCaptureHelper/QTCaptureHelper-Info.plist @@ -0,0 +1,35 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_NAME} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + XPC! + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + NSHumanReadableCopyright + Copyright © 2012 Zakk. All rights reserved. + LSBackgroundOnly + + XPCService + + RunLoopType + NSRunLoop + ServiceType + Application + + + diff --git a/QTCaptureHelper/QTCaptureHelper-Prefix.pch b/QTCaptureHelper/QTCaptureHelper-Prefix.pch new file mode 100644 index 00000000..feebaf3d --- /dev/null +++ b/QTCaptureHelper/QTCaptureHelper-Prefix.pch @@ -0,0 +1,4 @@ +// +// Prefix header for all source files of the 'QTCaptureHelper' target in the 'QTCaptureHelper' project +// + diff --git a/QTCaptureHelper/XPCListenerDelegate.h b/QTCaptureHelper/XPCListenerDelegate.h new file mode 100644 index 00000000..13aee41a --- /dev/null +++ b/QTCaptureHelper/XPCListenerDelegate.h @@ -0,0 +1,35 @@ +// +// XPCListenerDelegate.h +// CocoaSplit +// +// Created by Zakk on 11/10/12. +// Copyright (c) 2012 Zakk. All rights reserved. +// + +#import +#import "QTHelperProtocol.h" +#import "CapturedFrameProtocol.h" +#import + +@interface XPCListenerDelegate : NSObject +{ + QTCaptureDevice *captureDevice; + QTCaptureSession *captureSession; + QTCaptureDeviceInput *captureInput; + QTCaptureDecompressedVideoOutput *captureOutput; + id xpcProxy; + dispatch_queue_t frameQueue; + + +} + + +@property (strong) QTCaptureDevice *captureDevice; +@property (strong) QTCaptureSession *captureSession; +@property (strong) QTCaptureDeviceInput *captureInput; +@property (strong) QTCaptureDecompressedVideoOutput *captureOutput; +@property (strong) id xpcProxy; + + +- (BOOL) listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection; +@end diff --git a/QTCaptureHelper/XPCListenerDelegate.m b/QTCaptureHelper/XPCListenerDelegate.m new file mode 100644 index 00000000..3b74d37e --- /dev/null +++ b/QTCaptureHelper/XPCListenerDelegate.m @@ -0,0 +1,198 @@ +// +// XPCListenerDelegate.m +// CocoaSplit +// +// Created by Zakk on 11/10/12. +// Copyright (c) 2012 Zakk. All rights reserved. +// + +#import "XPCListenerDelegate.h" +#import "QTHelperProtocol.h" +#import +#import + +#import +#import + + + + + +@implementation XPCListenerDelegate + + +@synthesize captureDevice; +@synthesize captureSession; +@synthesize captureInput; +@synthesize captureOutput; +@synthesize xpcProxy; + + +- (void) dealloc +{ + + if (self.xpcProxy) + { + [self.xpcProxy release]; + + } + [super dealloc]; + +} + + + +- (BOOL) listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection +{ + + NSXPCInterface *helperInterface = [[NSXPCInterface interfaceWithProtocol:@protocol(QTHelperProtocol)] retain]; + NSXPCInterface *masterInterface = [[NSXPCInterface interfaceWithProtocol:@protocol(CapturedFrameProtocol)] retain]; + + + [newConnection setExportedInterface:helperInterface]; + [newConnection setExportedObject:self]; + + [newConnection setRemoteObjectInterface:masterInterface]; + self.xpcProxy = [newConnection remoteObjectProxy]; + [self.xpcProxy retain]; + + + + [newConnection resume]; + [helperInterface release]; + [masterInterface release]; + return YES; +} + +- (void) listCaptureDevices:(void (^)(NSArray *r_devices))reply +{ + NSArray *devices = [QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeVideo]; + + NSMutableArray *retArray = [[NSMutableArray alloc] init]; + QTCaptureDevice *devinstance; + + for(devinstance in devices) + { + [retArray addObject: @{@"name" : [devinstance localizedDisplayName], @"id":devinstance.uniqueID}]; + + + } + + [devices release]; + reply((NSArray *)retArray); + [retArray release]; +} + + +- (void) stopXPCCaptureSession +{ + if (self.captureSession) + { + [self.captureSession stopRunning]; + [self.captureSession release]; + } + + if (self.captureDevice) + { + [self.captureDevice close]; + [self.captureDevice release]; + } + + if (self.captureInput) + { + [self.captureInput release]; + } + + if (self.captureOutput) + { + [self.captureOutput release]; + } + + if (frameQueue) + { + dispatch_release(frameQueue); + } + + +} + + +- (void) startXPCCaptureSession:(NSString *)captureID +{ + frameQueue = dispatch_queue_create("zakk.lol.frameQueue", NULL); + + self.captureSession = [[[QTCaptureSession alloc] init] retain]; + self.captureDevice = [[QTCaptureDevice deviceWithUniqueID:captureID] retain]; + [self.captureDevice open:nil]; + self.captureInput = [[[QTCaptureDeviceInput alloc] initWithDevice:self.captureDevice] retain]; + + + NSError *error; + + [self.captureSession addInput:self.captureInput error:&error]; + + self.captureOutput = [[[QTCaptureDecompressedVideoOutput alloc] init] retain]; + + + NSDictionary *ioAttrs = [NSDictionary dictionaryWithObject: [NSNumber numberWithBool: YES] + forKey: (NSString *)kIOSurfaceIsGlobal]; + + NSMutableDictionary *pbAttrs = [NSMutableDictionary dictionaryWithObject:ioAttrs + forKey: (NSString*)kCVPixelBufferIOSurfacePropertiesKey]; + + [pbAttrs setObject: + [NSNumber numberWithInt:kCVPixelFormatType_422YpCbCr8] + forKey:(NSString *)kCVPixelBufferPixelFormatTypeKey]; + + [self.captureOutput setPixelBufferAttributes:pbAttrs]; + + [self.captureOutput setDelegate:self]; + [self.captureSession addOutput:self.captureOutput error:nil]; + + + [self.captureSession startRunning]; + +} + + +- (void) sendFrame:(CVImageBufferRef)newFrame +{ + + if (newFrame) + { + dispatch_semaphore_t reply_s = dispatch_semaphore_create(0); + IOSurfaceRef frameIOSurface = CVPixelBufferGetIOSurface(newFrame); + IOSurfaceID frameID = IOSurfaceGetID(frameIOSurface); + [self.xpcProxy newCapturedFrame:frameID reply:^{ + dispatch_semaphore_signal(reply_s); + }]; + dispatch_semaphore_wait(reply_s, DISPATCH_TIME_FOREVER); + CVPixelBufferRelease(newFrame); + dispatch_release(reply_s); + } + +} +- (void)captureOutput:(QTCaptureOutput *)captureOutput didOutputVideoFrame:(CVImageBufferRef)videoFrame withSampleBuffer:(QTSampleBuffer *)sampleBuffer fromConnection:(QTCaptureConnection *)connection +{ + + CVPixelBufferRetain(videoFrame); + dispatch_async(frameQueue, ^{ + [self sendFrame:videoFrame]; + }); + + //IOSurfaceIncrementUseCount(frameIORef); + + //[self.xpcProxy newCapturedFrame:IOSurfaceCreateXPCObject(frameIORef)]; + //mach_port_deallocate(mach_task_self(), framePort); + //CVPixelBufferRelease(videoFrame); + +} + + +- (void) testMethod +{ + NSLog(@"CALLED TEST METHOD"); + return; +} + +@end diff --git a/QTCaptureHelper/en.lproj/InfoPlist.strings b/QTCaptureHelper/en.lproj/InfoPlist.strings new file mode 100644 index 00000000..477b28ff --- /dev/null +++ b/QTCaptureHelper/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/QTCaptureHelper/main.m b/QTCaptureHelper/main.m new file mode 100644 index 00000000..fc9d34e3 --- /dev/null +++ b/QTCaptureHelper/main.m @@ -0,0 +1,28 @@ +// +// main.m +// QTCaptureHelper +// +// Created by Zakk on 11/10/12. +// Copyright (c) 2012 Zakk. All rights reserved. +// + +#include +#import "XPCListenerDelegate.h" +int main(int argc, const char *argv[]) +{ + + XPCListenerDelegate *listenDelegate = [[XPCListenerDelegate new] retain]; + NSLog(@"LISTEN DELEGATE %@", listenDelegate); + + NSXPCListener *listener = [[NSXPCListener serviceListener] retain]; + + //NSString *bundleId = [[NSBundle mainBundle] bundleIdentifier]; + //NSXPCListener *listener = [[NSXPCListener alloc] initWithMachServiceName:bundleId]; + + NSLog(@"LISTENER %@", listener); + listener.delegate = listenDelegate; + + [listener resume]; + NSLog(@"SETUP XPC LISTENER"); + exit(EXIT_FAILURE); +} diff --git a/QTHelperProtocol.h b/QTHelperProtocol.h new file mode 100644 index 00000000..ebe13aad --- /dev/null +++ b/QTHelperProtocol.h @@ -0,0 +1,19 @@ +// +// QTHelperProtocol.h +// CocoaSplit +// +// Created by Zakk on 11/10/12. +// Copyright (c) 2012 Zakk. All rights reserved. +// + +#import + +@protocol QTHelperProtocol +- (void)listCaptureDevices:(void (^)(NSArray *r_devices))reply; +- (void)startXPCCaptureSession:(NSString *)captureID; +- (void)stopXPCCaptureSession; + + +- (void)testMethod; + +@end