mac에 케이블로 연결된 iPhone의 화면을 실시간으로 획득

13321 단어 iOSSwiftmacos
mac에 케이블 접속한 iPhone의 화면을 실시간 취득하는 미니멀 구현을 만들었습니다.

지금까지는 QuickTime Player를 기동해 「신규 무비 수록」으로부터 iPhone을 선택하는 등의 어플 밖에서의 처리가 필요했습니다만, 이것으로 자작 프로그램으로 실현 가능하게 됩니다.



GitHub에 업하고 있습니다.
htps : // 기주 b. 코 m / 사토시 0212 /

이 구현 포함, 가상 카메라/AR/영상 표현 등의 정보 갱신은 Twitter로 투고하고 있습니다.
htps : // 라고 해서 r. 이 m / shm에서 ゔ ぇ p

구현 포인트



프로젝트 설정



Hardware, Camera를 선택해야 합니다.



plist



plist에 Privacy - Camera Usage Description를 추가하십시오.



Device 검색 시 설정


AVCaptureDevice.DiscoverySession 실행하기 전에 다음을 지정하면 선택적으로 외부 장치가 표시됩니다.

        var prop = CMIOObjectPropertyAddress(
            mSelector: CMIOObjectPropertySelector(kCMIOHardwarePropertyAllowScreenCaptureDevices),
            mScope: CMIOObjectPropertyScope(kCMIOObjectPropertyScopeGlobal),
            mElement: CMIOObjectPropertyElement(kCMIOObjectPropertyElementMaster))
        var allow: UInt32 = 1;
        CMIOObjectSetPropertyData(CMIOObjectID(kCMIOObjectSystemObject), &prop, 0, nil, UInt32(MemoryLayout.size(ofValue: allow)), &allow)

그리고 아래의 파라미터로 탐색하면 devices에 iPhone이 포함되어 있습니다.
발견한 장치를 modelID , manufacturer 에서 적절하게 필터링하여 iPhone 장치를 확인할 수 있습니다.

        let devices = AVCaptureDevice.DiscoverySession(deviceTypes: [.externalUnknown], mediaType: nil, position: .unspecified).devices
        if let device = devices.filter({ $0.modelID == "iOS Device" && $0.manufacturer == "Apple Inc." }).first {
            ...
        }

다만, 기동 직후나 탐색 직후는 iPhone을 찾을 수 없는 경우가 있기 때문에 AVCaptureDeviceWasConnectedNotification 의 notification 을 observe 할 필요도 있었습니다.

        let nc = NotificationCenter.default
        nc.addObserver(forName: NSNotification.Name(rawValue: "AVCaptureDeviceWasConnectedNotification"), object: nil, queue: .main) { (notification) in
            print(notification)
            guard let device = notification.object as? AVCaptureDevice else { return }
            ...
        }

여담: 표시용 리사이즈



업한 구현에서는 화면 표시용으로 리사이즈했습니다.

높이를 고정치로서 비율을 계산해 폭을 산출해 imageView의 사이즈 지정.
이미지 쪽이 CGAffineTransform 로 사이즈 변환하고 있습니다.

    private func resizeIfNeeded(w: CGFloat, h: CGFloat) {
        guard targetRect == nil else { return }
        let aspect = h / fixedHeight
        let rect = CGRect(x: 0, y: 0, width: floor(w / aspect), height: fixedHeight)
        imageView.frame = rect
        targetRect = rect
    }

    ...

    func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
        connection.videoOrientation = .portrait

        DispatchQueue.main.async(execute: {
            let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)!
            let ciImage = CIImage(cvPixelBuffer: pixelBuffer)
            let w = CGFloat(CVPixelBufferGetWidth(pixelBuffer))
            let h = CGFloat(CVPixelBufferGetHeight(pixelBuffer))
            self.resizeIfNeeded(w: w, h: h)

            guard let targetRect = self.targetRect else { return }
            let m = CGAffineTransform(scaleX: targetRect.width / w, y: targetRect.height / h)
            let resizedImage = ciImage.transformed(by: m)
            let cgimage = self.context.createCGImage(resizedImage, from: targetRect)!
            let image = NSImage(cgImage: cgimage, size: targetRect.size)
            self.imageView.image = image
        })
    }

좋은 웹페이지 즐겨찾기