aboutsummaryrefslogtreecommitdiff
path: root/Captura/Domain/CapturaCaptureSession.swift
blob: dbc72b9bccef479d46e69dfa2d22218893408ee4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import AVFoundation
import AppKit

class CapturaCaptureSession: AVCaptureSession, AVCaptureFileOutputRecordingDelegate,
  AVCaptureVideoDataOutputSampleBufferDelegate
{

  let videoOutput = AVCaptureVideoDataOutput()
  let movieFileOutput = AVCaptureMovieFileOutput()
  var receivedFrames = false

  init(_ screen: NSScreen, box: NSRect) {
    super.init()

    let displayId =
      screen.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as! CGDirectDisplayID
    let screenInput = AVCaptureScreenInput(displayID: displayId)
    var croppingBox = NSOffsetRect(box, -screen.frame.origin.x, -screen.frame.origin.y)
    if croppingBox.width.truncatingRemainder(dividingBy: 2) != 0 {
      croppingBox.size.width -= 1
    }
    screenInput?.cropRect = croppingBox.insetBy(dx: 1, dy: 1)

    if self.canAddInput(screenInput!) {
      self.addInput(screenInput!)
    }

    videoOutput.setSampleBufferDelegate(
      self, queue: Dispatch.DispatchQueue(label: "sample buffer delegate", attributes: []))

    if self.canAddOutput(videoOutput) {
      self.addOutput(videoOutput)
    }

    if self.canAddOutput(movieFileOutput) {
      self.addOutput(movieFileOutput)
    }
  }

  func startRecording() {
    receivedFrames = false
    self.startRunning()

    DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
      if !self.receivedFrames {
        NotificationCenter.default.post(name: .failedToStart, object: nil, userInfo: nil)
      }
    }
  }

  func startRecording(to url: URL) {
    self.startRecording()
    movieFileOutput.startRecording(to: url, recordingDelegate: self)
  }

  // MARK: - AVCaptureVideoDataOutputSampleBufferDelegate Implementation

  func captureOutput(
    _ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer,
    from connection: AVCaptureConnection
  ) {
    receivedFrames = true

    guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }
    NotificationCenter.default.post(
      name: .receivedFrame, object: nil, userInfo: ["frame": imageBuffer])
  }

  // MARK: - AVCaptureFileOutputRecordingDelegate Implementation

  func fileOutput(
    _ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL,
    from connections: [AVCaptureConnection], error: Error?
  ) {}
}