]>
Commit | Line | Data |
---|---|---|
c9b9e1d6 | 1 | import AVFoundation |
505c1e62 | 2 | import AppKit |
c9b9e1d6 | 3 | |
505c1e62 RBR |
4 | class CapturaCaptureSession: AVCaptureSession, AVCaptureFileOutputRecordingDelegate, |
5 | AVCaptureVideoDataOutputSampleBufferDelegate | |
6 | { | |
c9b9e1d6 RBR |
7 | |
8 | let videoOutput = AVCaptureVideoDataOutput() | |
9 | let movieFileOutput = AVCaptureMovieFileOutput() | |
10 | var receivedFrames = false | |
505c1e62 | 11 | |
c9b9e1d6 RBR |
12 | init(_ screen: NSScreen, box: NSRect) { |
13 | super.init() | |
505c1e62 RBR |
14 | |
15 | let displayId = | |
16 | screen.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as! CGDirectDisplayID | |
c9b9e1d6 | 17 | let screenInput = AVCaptureScreenInput(displayID: displayId) |
082b61f3 RBR |
18 | var croppingBox = NSOffsetRect(box, -screen.frame.origin.x, -screen.frame.origin.y) |
19 | if croppingBox.width.truncatingRemainder(dividingBy: 2) != 0 { | |
20 | croppingBox.size.width -= 1 | |
21 | } | |
22 | screenInput?.cropRect = croppingBox.insetBy(dx: 1, dy: 1) | |
505c1e62 | 23 | |
c9b9e1d6 RBR |
24 | if self.canAddInput(screenInput!) { |
25 | self.addInput(screenInput!) | |
26 | } | |
505c1e62 RBR |
27 | |
28 | videoOutput.setSampleBufferDelegate( | |
29 | self, queue: Dispatch.DispatchQueue(label: "sample buffer delegate", attributes: [])) | |
30 | ||
c9b9e1d6 RBR |
31 | if self.canAddOutput(videoOutput) { |
32 | self.addOutput(videoOutput) | |
33 | } | |
505c1e62 | 34 | |
c9b9e1d6 RBR |
35 | if self.canAddOutput(movieFileOutput) { |
36 | self.addOutput(movieFileOutput) | |
37 | } | |
38 | } | |
505c1e62 | 39 | |
c9b9e1d6 RBR |
40 | func startRecording() { |
41 | receivedFrames = false | |
42 | self.startRunning() | |
505c1e62 | 43 | |
c9b9e1d6 RBR |
44 | DispatchQueue.main.asyncAfter(deadline: .now() + 1) { |
45 | if !self.receivedFrames { | |
46 | NotificationCenter.default.post(name: .failedToStart, object: nil, userInfo: nil) | |
47 | } | |
48 | } | |
49 | } | |
505c1e62 | 50 | |
c9b9e1d6 RBR |
51 | func startRecording(to url: URL) { |
52 | self.startRecording() | |
53 | movieFileOutput.startRecording(to: url, recordingDelegate: self) | |
54 | } | |
505c1e62 | 55 | |
c9b9e1d6 | 56 | // MARK: - AVCaptureVideoDataOutputSampleBufferDelegate Implementation |
505c1e62 RBR |
57 | |
58 | func captureOutput( | |
59 | _ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, | |
60 | from connection: AVCaptureConnection | |
61 | ) { | |
c9b9e1d6 | 62 | receivedFrames = true |
505c1e62 | 63 | |
c9b9e1d6 | 64 | guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return } |
505c1e62 RBR |
65 | NotificationCenter.default.post( |
66 | name: .receivedFrame, object: nil, userInfo: ["frame": imageBuffer]) | |
c9b9e1d6 | 67 | } |
505c1e62 | 68 | |
c9b9e1d6 | 69 | // MARK: - AVCaptureFileOutputRecordingDelegate Implementation |
505c1e62 RBR |
70 | |
71 | func fileOutput( | |
72 | _ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, | |
73 | from connections: [AVCaptureConnection], error: Error? | |
74 | ) {} | |
c9b9e1d6 | 75 | } |