Skip to content

Commit

Permalink
fixed memory leak, fixed windows filter
Browse files Browse the repository at this point in the history
  • Loading branch information
lihaoyun6 committed Jan 24, 2025
1 parent e70002a commit 68e8cd8
Show file tree
Hide file tree
Showing 11 changed files with 283 additions and 236 deletions.
8 changes: 4 additions & 4 deletions QuickRecorder.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@
CODE_SIGN_ENTITLEMENTS = QuickRecorder/QuickRecorder.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 163;
CURRENT_PROJECT_VERSION = 164;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = "\"QuickRecorder/Preview Content\"";
DEVELOPMENT_TEAM = L4T783637F;
Expand All @@ -505,7 +505,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 12.3;
MARKETING_VERSION = 1.6.3;
MARKETING_VERSION = 1.6.4;
PRODUCT_BUNDLE_IDENTIFIER = com.lihaoyun6.QuickRecorder;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
Expand All @@ -521,7 +521,7 @@
CODE_SIGN_ENTITLEMENTS = QuickRecorder/QuickRecorder.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 163;
CURRENT_PROJECT_VERSION = 164;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = "\"QuickRecorder/Preview Content\"";
DEVELOPMENT_TEAM = L4T783637F;
Expand All @@ -539,7 +539,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 12.3;
MARKETING_VERSION = 1.6.3;
MARKETING_VERSION = 1.6.4;
PRODUCT_BUNDLE_IDENTIFIER = com.lihaoyun6.QuickRecorder;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
Expand Down
13 changes: 10 additions & 3 deletions QuickRecorder/QuickRecorderApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ let camWindow = NSPanel(contentRect: NSRect(x: 200, y: 200, width: 200, height:
let deviceWindow = NSWindow(contentRect: NSRect(x: 200, y: 200, width: 200, height: 200), styleMask: [.fullSizeContentView, .resizable], backing: .buffered, defer: false)
let controlPanel = NSWindow(contentRect: NSRect(x: 0, y: 0, width: 10, height: 10), styleMask: [.fullSizeContentView], backing: .buffered, defer: false)
let countdownPanel = NSWindow(contentRect: NSRect(x: 0, y: 0, width: 120, height: 120), styleMask: [.fullSizeContentView], backing: .buffered, defer: false)
let previewWindow = NSWindow(contentRect: NSRect(x: 0, y: 0, width: 266, height: 156), styleMask: [.fullSizeContentView], backing: .buffered, defer: false)
var updaterController: SPUStandardUpdaterController!

@main
Expand Down Expand Up @@ -302,6 +303,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, SCStreamDelegate, SCStreamOu
controlPanel.titlebarAppearsTransparent = true
controlPanel.isMovableByWindowBackground = true

previewWindow.level = .statusBar
previewWindow.titlebarAppearsTransparent = true
previewWindow.titleVisibility = .hidden
previewWindow.isReleasedWhenClosed = false
previewWindow.backgroundColor = .clear

KeyboardShortcuts.onKeyDown(for: .showPanel) {
_ = self.applicationShouldHandleReopen(NSApp, hasVisibleWindows: true)
if SCContext.stream == nil { NSApp.activate(ignoringOtherApps: true) }
Expand Down Expand Up @@ -427,7 +434,7 @@ func getStatusBarWidth() -> CGFloat {
case nil: width = miniStatusBar ? 36.0 : 36.0
case .idevice: width = miniStatusBar ? 68.0 : 138.0
case .systemaudio: width = miniStatusBar ? 68.0 : 114.0
default: width = miniStatusBar ? 84.0 : 158.0
default: width = miniStatusBar ? 78.0 : 158.0
}
return width
}
Expand Down Expand Up @@ -552,10 +559,10 @@ extension NSImage {
return NSImage(cgImage: imageRef, size: NSSize(width: CGFloat(imageRef.width)/factor, height: CGFloat(imageRef.height)/factor))
}

func saveToFile(_ url: URL) {
func saveToFile(_ url: URL, type: NSBitmapImageRep.FileType = .png) {
if let tiffData = self.tiffRepresentation,
let imageRep = NSBitmapImageRep(data: tiffData) {
let pngData = imageRep.representation(using: .png, properties: [:])
let pngData = imageRep.representation(using: type, properties: [:])
do {
try pngData?.write(to: url)
} catch {
Expand Down
82 changes: 20 additions & 62 deletions QuickRecorder/RecordEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ extension AppDelegate {
SCContext.application = SCContext.availableContent!.applications.filter({ applications.contains($0) })
} else { if SCContext.streamType == .application { SCContext.streamType = nil; return } }

let screen = SCContext.screen ?? SCContext.getSCDisplayWithMouse()!
let qrSelf = SCContext.getSelf()
let qrWindows = SCContext.getSelfWindows()
let dockApp = SCContext.availableContent!.applications.first(where: { $0.bundleIdentifier.description == "com.apple.dock" })
Expand All @@ -66,7 +67,9 @@ extension AppDelegate {
guard let title = $0.title else { return true }
return $0.owningApplication?.bundleIdentifier == "com.apple.dock" && title == "Dock"
})
let desktopFiles = SCContext.availableContent!.windows.filter({ $0.title == "" && $0.owningApplication?.bundleIdentifier == "com.apple.finder" })
let desktopFiles = SCContext.availableContent!.windows.filter({
$0.owningApplication?.bundleIdentifier == "com.apple.finder"
&& $0.title == "" && $0.frame == screen.frame })
let controlCenterWindow = SCContext.availableContent!.applications.filter({ $0.bundleIdentifier == "com.apple.controlcenter" })
let mouseWindow = SCContext.availableContent!.windows.filter({ $0.title == "Mouse Pointer".local && $0.owningApplication?.bundleIdentifier == Bundle.main.bundleIdentifier })
let camLayer = SCContext.availableContent!.windows.filter({ $0.title == "Camera Overlayer".local && $0.owningApplication?.bundleIdentifier == Bundle.main.bundleIdentifier })
Expand All @@ -76,7 +79,7 @@ extension AppDelegate {
appBlackList = (decodedApps as [AppInfo]).map({ $0.bundleID })
}
let excliudedApps = SCContext.availableContent!.applications.filter({ appBlackList.contains($0.bundleIdentifier) })
let screen = SCContext.screen ?? SCContext.getSCDisplayWithMouse()!

if SCContext.streamType == .window || SCContext.streamType == .windows {
if var includ = SCContext.window {
if includ.count > 1 {
Expand Down Expand Up @@ -133,7 +136,6 @@ extension AppDelegate {
SCContext.isResume = false

let audioOnly = SCContext.streamType == .systemaudio
let encoderIsH265 = (encoder.rawValue == Encoder.h265.rawValue) || recordHDR

let conf: SCStreamConfiguration
#if compiler(>=6.0)
Expand Down Expand Up @@ -176,13 +178,7 @@ extension AppDelegate {
conf.showsCursor = showMouse || fastStart
if background.rawValue != BackgroundType.wallpaper.rawValue { conf.backgroundColor = SCContext.getBackgroundColor() }
if !recordHDR {
if encoderIsH265 {
conf.pixelFormat = kCVPixelFormatType_ARGB2101010LEPacked
conf.colorSpaceName = CGColorSpace.displayP3
} else {
conf.pixelFormat = kCVPixelFormatType_32BGRA
conf.colorSpaceName = CGColorSpace.sRGB
}
conf.colorSpaceName = CGColorSpace.sRGB
if withAlpha { conf.pixelFormat = kCVPixelFormatType_32BGRA }
}
}
Expand Down Expand Up @@ -329,14 +325,7 @@ extension AppDelegate {
] as [String : Any]
]

if encoderIsH265 {
if !recordHDR {
videoSettings[AVVideoColorPropertiesKey] = [
AVVideoTransferFunctionKey: AVVideoTransferFunction_ITU_R_709_2,
AVVideoColorPrimariesKey: AVVideoColorPrimaries_P3_D65,
AVVideoYCbCrMatrixKey: AVVideoYCbCrMatrix_ITU_R_709_2 ] as [String : Any]
}
} else {
if !recordHDR {
videoSettings[AVVideoColorPropertiesKey] = [
AVVideoTransferFunctionKey: AVVideoTransferFunction_ITU_R_709_2,
AVVideoColorPrimariesKey: AVVideoColorPrimaries_ITU_R_709_2,
Expand Down Expand Up @@ -398,34 +387,6 @@ extension AppDelegate {
}
}

/*func startMicRecording() {
if micDevice == "default" {
let input = SCContext.audioEngine.inputNode
if enableAEC {
try? input.setVoiceProcessingEnabled(true)
if #available(macOS 14, *) {
let duckingConfig = AVAudioVoiceProcessingOtherAudioDuckingConfiguration(enableAdvancedDucking: false, duckingLevel: .min)
input.voiceProcessingOtherAudioDuckingConfiguration = duckingConfig
//input.voiceProcessingOtherAudioDuckingConfiguration.duckingLevel = .min
}
}
let inputFormat = input.inputFormat(forBus: 0)
let monoFormat = AVAudioFormat(commonFormat: inputFormat.commonFormat,
sampleRate: inputFormat.sampleRate, channels: 1,
interleaved: inputFormat.isInterleaved) ?? inputFormat
input.installTap(onBus: 0, bufferSize: 1024, format: enableAEC ? monoFormat : inputFormat) { buffer, time in
if SCContext.isPaused || SCContext.startTime == nil { return }
if SCContext.micInput.isReadyForMoreMediaData {
SCContext.micInput.append(buffer.asSampleBuffer!)
}
}
try! SCContext.audioEngine.start()
} else {
AudioRecorder.shared.setupAudioCapture()
AudioRecorder.shared.start()
}
}*/

func outputVideoEffectDidStart(for stream: SCStream) {
DispatchQueue.main.async { camWindow.close() }
print("[Presenter Overlay ON]")
Expand All @@ -448,13 +409,8 @@ extension AppDelegate {
func stream(_ stream: SCStream, didOutputSampleBuffer sampleBuffer: CMSampleBuffer, of outputType: SCStreamOutputType) {
if SCContext.saveFrame && sampleBuffer.imageBuffer != nil {
SCContext.saveFrame = false
if #available(macOS 13.0, *) {
let url = URL(filePath: "\(SCContext.getFilePath(capture: true)).png")
sampleBuffer.nsImage?.saveToFile(url)
} else {
let url = "\(SCContext.getFilePath(capture: true)).png".url
sampleBuffer.nsImage?.saveToFile(url)
}
let url = "\(SCContext.getFilePath(capture: true)).png".url
sampleBuffer.nsImage?.saveToFile(url)
}
if SCContext.isPaused { return }
guard sampleBuffer.isValid else { return }
Expand Down Expand Up @@ -623,15 +579,17 @@ extension CMSampleBuffer {
}

var nsImage: NSImage? {
guard let pixelBuffer = CMSampleBufferGetImageBuffer(self) else { return nil }
CVPixelBufferLockBaseAddress(pixelBuffer, .readOnly)
defer { CVPixelBufferUnlockBaseAddress(pixelBuffer, .readOnly) }
let ciImage = CIImage(cvPixelBuffer: pixelBuffer)
let context = CIContext()
if let cgImage = context.createCGImage(ciImage, from: ciImage.extent) {
return NSImage(cgImage: cgImage, size: NSSize.zero)
}
return nil
return autoreleasepool {
guard let pixelBuffer = CMSampleBufferGetImageBuffer(self) else { return nil }
CVPixelBufferLockBaseAddress(pixelBuffer, .readOnly)
defer { CVPixelBufferUnlockBaseAddress(pixelBuffer, .readOnly) }
let ciImage = CIImage(cvPixelBuffer: pixelBuffer)
let ciContext = CIContext()
if let cgImage = ciContext.createCGImage(ciImage, from: ciImage.extent) {
return NSImage(cgImage: cgImage, size: .zero)
}
return nil
}
}
}

Expand Down
19 changes: 8 additions & 11 deletions QuickRecorder/SCContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -457,18 +457,15 @@ class SCContext {

static func showPreview(path: String, image: NSImage? = nil) {
if !ud.bool(forKey: "showPreview") { return }
var frame: NSImage?
if let i = image { frame = i } else { if let f = firstFrame?.nsImage { frame = f }}
if let frame = frame, let screen = getScreenWithMouse() {
let previewWindow = NSWindow(contentRect: NSMakeRect(0, 0, 260, 150), styleMask: [.fullSizeContentView], backing: .buffered, defer: false)
let contentView = NSHostingView(rootView: PreviewView(frame: frame, filePath: path))
var previewImage: NSImage?
let previewURL = fd.temporaryDirectory.appendingPathComponent("qr-preview.jpg")
if image == nil { firstFrame?.nsImage?.saveToFile(previewURL, type: .jpeg) }

if let i = image { previewImage = i } else { previewImage = NSImage(contentsOf: previewURL) }
if let previewImage = previewImage, let screen = getScreenWithMouse() {
let contentView = NSHostingView(rootView: PreviewView(frame: previewImage, filePath: path))
previewWindow.contentView = contentView
previewWindow.level = .statusBar
previewWindow.titlebarAppearsTransparent = true
previewWindow.titleVisibility = .hidden
previewWindow.isReleasedWhenClosed = false
previewWindow.backgroundColor = .clear
previewWindow.setFrameOrigin(NSPoint(x: screen.frame.maxX - 274, y: screen.frame.minY + 14))
previewWindow.setFrameOrigin(NSPoint(x: screen.frame.maxX - 280, y: screen.frame.minY + 20))
previewWindow.orderFront(self)
}
}
Expand Down
Loading

0 comments on commit 68e8cd8

Please sign in to comment.