Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
仮想カメラで切り開く拡張現実の世界
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
satoshi0212
December 08, 2020
Programming
680
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
仮想カメラで切り開く拡張現実の世界
satoshi0212
December 08, 2020
More Decks by satoshi0212
See All by satoshi0212
macOSで自分のカメラを作ってみよう - Core Media IO Extensions
satoshi0212
3
1.8k
NDIとARKitを連動させた新しい映像表現
satoshi0212
3
1.3k
100日間AR表現を実装して見つけた面白い実装を全力解説
satoshi0212
5
2.3k
Working on mobile AR implementation, what I've implemented and beyond
satoshi0212
0
580
macOS仮想カメラ「テロップカム」 実装方法とその先
satoshi0212
5
4k
ARで悪の組織の会議を実現する
satoshi0212
0
650
クロマキー合成を使い透過動画をAR空間に表示する
satoshi0212
3
10k
ARKit Maniacs
satoshi0212
1
3.8k
ARで作る価値のある物についての考察
satoshi0212
3
730
Other Decks in Programming
See All in Programming
ハーネス設計入門 〜 基礎知識の整理から実務へのステップアップ 〜
kinopeee
15
13k
スマートフォンでモールス信号を送受信する 〜スマートフォンのLEDとカメラで作る光通信の設計と実装〜
atsuki_seo
0
170
Augmenting AI with the Power of Jakarta EE
ivargrimstad
0
290
AWS DevOps Agentで インシデント対応をAIに任せたい
honmarkhunt
7
3.1k
技術的負債を組織課題として解く-増えすぎたマイクロサービスとの戦い-
reimaru
1
2.3k
The Good Stuff, Not the Slop: Engineering High-Quality Android Apps with Modern AI Tooling
danybony
1
260
WebRTC映像をAirPlayに対応させる挑戦.pdf
monolithic_adam
0
300
JPUG勉強会 OSSデータベースの内部構造を理解しよう(第2回)
oga5
0
270
マイコン向けの軽量Ruby「PicoRuby」で各種デバイスを制御するネイティブアプリの実現手法
bash0c7
0
440
モデルのリファクタリングが難しいと思ったら、そもそも複雑だったのはビジネス仕様だった ? / is-the-business-domain-the-real-complexity
hatsu38
0
390
AgentCore CLI で進化した AWS での AI エージェントの作り方 : 必要な機能を必要な時に
icoxfog417
PRO
3
370
仕様駆動開発による爆速プロダクト開発 / Bakusoku Spec Driven Development
kobakei
0
120
Featured
See All Featured
So, you think you're a good person
axbom
PRO
2
2.2k
DBのスキルで生き残る技術 - AI時代におけるテーブル設計の勘所
soudai
PRO
68
57k
The Cult of Friendly URLs
andyhume
79
7k
Paper Plane
katiecoart
PRO
4
53k
Hiding What from Whom? A Critical Review of the History of Programming languages for Music
tomoyanonymous
3
1.3k
First, design no harm
axbom
PRO
2
1.3k
Beyond borders and beyond the search box: How to win the global "messy middle" with AI-driven SEO
davidcarrasco
3
260
Building AI with AI
inesmontani
PRO
1
1.2k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
21
1.6k
sira's awesome portfolio website redesign presentation
elsirapls
0
420
Building the Perfect Custom Keyboard
takai
2
880
Rails Girls Zürich Keynote
gr2m
96
14k
Transcript
ԾΧϝϥͰΓ։֦͘ுݱ࣮ͷੈք 93,BJHJ ෦ஐ5XJUUFS!TINEFWFMPQ
ԾΧϝϥʹڵຯ͕͋ΔΤϯδχΞʹಧ͚ େܕελδΦͰͳ͘σεΫτοϓεϚʔτϑΥϯ ఆλʔήοτ
όʔνϟϧΧϝϥ࡞ΕΔʂָͦ͠͏ʂ ͱ͍͏ฏΛ։͍ͯ΄͍͠ʂ
࣮ํ๏ ݱ࣮֦ுͱͯ͠ͷར༻
ԾΧϝϥ 4OBQ$BNFSB NNINN
None
None
None
None
None
None
None
࣮ํ๏ ݱ࣮֦ுͱͯ͠ͷར༻
5XJUUFSͰ(8όʔνϟϧΧϝϥ࡞νϟϨϯδͰ࡞աఔπΠʔτ͍ͯ͠·͢
࡞ͬͨࡍͷۤ࿑ΛৼΓฦΓ
֎෦ϥΠϒϥϦͳ͠ɺ4XJGUͷΈͰ࣮Մೳ
ԾΧϝϥ࣮αϯϓϧ IUUQTHJUIVCDPNTBUPTIJ7JSUVBM$BNFSB4BNQMF
ߏཁૉ
ߏཁૉ ɹ$PSF.FEJB*0%"-1MVHJO ɹίϯτϩʔϥΞϓϦ
$PSF.FEJB*0%"- 1MVHJO ίϯτϩʔϥΞϓϦ /41BTUFCPBSE -JCSBSZ$PSF.FEJB*01MVH*OT%"- ԾΧϝϥͷ࣮ମ 6*Λ࣋ͨͳ͍1MVHJOʹ Λ͢ΞϓϦ
ϓϩδΣΫτ࡞ QMJTUใՃ ΤϯτϦʔϙΠϯτ࣮ ΠϯλʔϑΣΠε࣮ Χϝϥө૾ͷऔಘͱग़ྗ $PSF.FEJB*0%"-1MVHJO
ϓϩδΣΫτ࡞ QMJTUใՃ ΤϯτϦʔϙΠϯτ࣮ ΠϯλʔϑΣΠε࣮ Χϝϥө૾ͷऔಘͱग़ྗ $PSF.FEJB*0%"-1MVHJO
None
None
ϓϩδΣΫτ࡞ QMJTUใՃ ΤϯτϦʔϙΠϯτ࣮ ΠϯλʔϑΣΠε࣮ Χϝϥө૾Λग़ྗઃఆ $PSF.FEJB*0%"-1MVHJO
1MVHJOGBDUPSZJOUFSGBDFT 1MVHJOUZQFT ΛՃ
None
ࣗͰࢦఆ͢Δ*%ɻ66*%ͳͲઃఆ
ΤϯτϦʔϙΠϯτGVODUJPO໊
ԾΧϝϥ1MVH*Oݻఆ ઌड़ͷ66*%
ϓϩδΣΫτ࡞ QMJTUใՃ ΤϯτϦʔϙΠϯτ࣮ ΠϯλʔϑΣΠε࣮ Χϝϥө૾Λग़ྗઃఆ $PSF.FEJB*0%"-1MVHJO
import Foundation import CoreMediaIO @_cdecl("VirtualCameraSampleMain") func VirtualCameraSampleMain(allocator: CFAllocator, requestedTypeUUID: CFUUID)
-> CMIOHardwarePlugInRef { return pluginRef } Main.swift
import Foundation import CoreMediaIO @_cdecl("VirtualCameraSampleMain") func VirtualCameraSampleMain(allocator: CFAllocator, requestedTypeUUID: CFUUID)
-> CMIOHardwarePlugInRef { return pluginRef } Main.swift ΤϯτϦʔϙΠϯτ ΠϯλʔϑΣΠεͷࢀরΛฦ٫
ϓϩδΣΫτ࡞ QMJTUใՃ ΤϯτϦʔϙΠϯτ࣮ ΠϯλʔϑΣΠε࣮ Χϝϥө૾Λग़ྗઃఆ $PSF.FEJB*0%"-1MVHJO
private func createPluginInterface() -> CMIOHardwarePlugInInterface { return CMIOHardwarePlugInInterface( _reserved: nil,
QueryInterface: QueryInterface, AddRef: AddRef, Release: Release, Initialize: Initialize, InitializeWithObjectID: InitializeWithObjectID, Teardown: Teardown, ObjectShow: ObjectShow, ObjectHasProperty: ObjectHasProperty, ObjectIsPropertySettable: ObjectIsPropertySettable, ObjectGetPropertyDataSize: ObjectGetPropertyDataSize, ObjectGetPropertyData: ObjectGetPropertyData, ObjectSetPropertyData: ObjectSetPropertyData, DeviceSuspend: DeviceSuspend, DeviceResume: DeviceResume, DeviceStartStream: DeviceStartStream, DeviceStopStream: DeviceStopStream, DeviceProcessAVCCommand: DeviceProcessAVCCommand, DeviceProcessRS422Command: DeviceProcessRS422Command, StreamCopyBufferQueue: StreamCopyBufferQueue, StreamDeckPlay: StreamDeckPlay, StreamDeckStop: StreamDeckStop, StreamDeckJog: StreamDeckJog, StreamDeckCueTo: StreamDeckCueTo) } PluginInterface.swift (ൈਮ)
private func createPluginInterface() -> CMIOHardwarePlugInInterface { return CMIOHardwarePlugInInterface( _reserved: nil,
QueryInterface: QueryInterface, AddRef: AddRef, Release: Release, Initialize: Initialize, InitializeWithObjectID: InitializeWithObjectID, Teardown: Teardown, ObjectShow: ObjectShow, ObjectHasProperty: ObjectHasProperty, ObjectIsPropertySettable: ObjectIsPropertySettable, ObjectGetPropertyDataSize: ObjectGetPropertyDataSize, ObjectGetPropertyData: ObjectGetPropertyData, ObjectSetPropertyData: ObjectSetPropertyData, DeviceSuspend: DeviceSuspend, DeviceResume: DeviceResume, DeviceStartStream: DeviceStartStream, DeviceStopStream: DeviceStopStream, DeviceProcessAVCCommand: DeviceProcessAVCCommand, DeviceProcessRS422Command: DeviceProcessRS422Command, StreamCopyBufferQueue: StreamCopyBufferQueue, StreamDeckPlay: StreamDeckPlay, StreamDeckStop: StreamDeckStop, StreamDeckJog: StreamDeckJog, StreamDeckCueTo: StreamDeckCueTo) } PluginInterface.swift (ൈਮ) ΠϯλʔϑΣΠε͕ظ͢ΔͷΛฦ٫
PluginInterface.swift (ൈਮ) ৄࡉιʔεࢀর
ϓϩδΣΫτ࡞ QMJTUใՃ ΤϯτϦʔϙΠϯτ࣮ ΠϯλʔϑΣΠε࣮ Χϝϥө૾औಘͱग़ྗ $PSF.FEJB*0%"-1MVHJO
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, ɹɹɹɹɹɹɹɹɹɹɹ ɹɹfrom connection:
AVCaptureConnection) { if output == cameraCapture.output { guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return } let cameraImage = CIImage(cvImageBuffer: imageBuffer) let compositedImage = compose(bgImage: cameraImage, overlayImage: self.textImage) var pixelBuffer: CVPixelBuffer? _ = CVPixelBufferCreate( kCFAllocatorDefault, Int(compositedImage.extent.size.width), Int(compositedImage.extent.height), kCVPixelFormatType_32BGRA, self.CVPixelBufferCreateOptions as CFDictionary, &pixelBuffer ) if let pixelBuffer = pixelBuffer { context.render(compositedImage, to: pixelBuffer) delegate?.videoComposer(self, didComposeImageBuffer: pixelBuffer) } } } VideoComposer.swift (ൈਮ)
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, ɹɹɹɹɹɹɹɹɹɹɹ ɹɹfrom connection:
AVCaptureConnection) { if output == cameraCapture.output { guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return } let cameraImage = CIImage(cvImageBuffer: imageBuffer) let compositedImage = compose(bgImage: cameraImage, overlayImage: self.textImage) var pixelBuffer: CVPixelBuffer? _ = CVPixelBufferCreate( kCFAllocatorDefault, Int(compositedImage.extent.size.width), Int(compositedImage.extent.height), kCVPixelFormatType_32BGRA, self.CVPixelBufferCreateOptions as CFDictionary, &pixelBuffer ) if let pixelBuffer = pixelBuffer { context.render(compositedImage, to: pixelBuffer) delegate?.videoComposer(self, didComposeImageBuffer: pixelBuffer) } } } VideoComposer.swift (ൈਮ) ผ్ੜͨ͠ΦʔόʔϨΠςΩετը૾ͱ߹ EFMFHBUFܦ༝ͰQJYFM#V⒎FSΛ͢
private lazy var timer: DispatchSourceTimer = { let interval =
1.0 / Double(frameRate) let timer = DispatchSource.makeTimerSource() timer.schedule(deadline: .now() + interval, repeating: interval) timer.setEventHandler(handler: { [weak self] in self?.enqueueBuffer() }) return timer }() Stream.swift (ൈਮ)
private lazy var timer: DispatchSourceTimer = { let interval =
1.0 / Double(frameRate) let timer = DispatchSource.makeTimerSource() timer.schedule(deadline: .now() + interval, repeating: interval) timer.setEventHandler(handler: { [weak self] in self?.enqueueBuffer() }) return timer }() Stream.swift (ൈਮ) GSBNF3BUFࢦఆ͠ߋ৽ॲཧݺͼग़͠
private func enqueueBuffer() { (தུ) var sampleBufferUnmanaged: Unmanaged<CMSampleBuffer>? = nil
error = CMIOSampleBufferCreateForImageBuffer( kCFAllocatorDefault, pixelBuffer, formatDescription, &timing, sequenceNumber, UInt32(kCMIOSampleBufferNoDiscontinuities), &sampleBufferUnmanaged ) guard error == noErr else { log("CMIOSampleBufferCreateForImageBuffer Error: \(error)") return } CMSimpleQueueEnqueue(queue, element: sampleBufferUnmanaged!.toOpaque()) queueAlteredProc?(objectID, sampleBufferUnmanaged!.toOpaque(), queueAlteredRefCon) sequenceNumber += 1 } Stream.swift (ൈਮ)
private func enqueueBuffer() { (தུ) var sampleBufferUnmanaged: Unmanaged<CMSampleBuffer>? = nil
error = CMIOSampleBufferCreateForImageBuffer( kCFAllocatorDefault, pixelBuffer, formatDescription, &timing, sequenceNumber, UInt32(kCMIOSampleBufferNoDiscontinuities), &sampleBufferUnmanaged ) guard error == noErr else { log("CMIOSampleBufferCreateForImageBuffer Error: \(error)") return } CMSimpleQueueEnqueue(queue, element: sampleBufferUnmanaged!.toOpaque()) queueAlteredProc?(objectID, sampleBufferUnmanaged!.toOpaque(), queueAlteredRefCon) sequenceNumber += 1 } Stream.swift (ൈਮ) QJYFM#V⒎FS͔Βੜͨ͠ $.4BNQMF#V⒎FSΛRVFVFʹՃ
@IBAction func sendButton_action(_ sender: Any) { SettingsPasteboard.shared.settings["text1"] = mainTextField.stringValue SettingsPasteboard.shared.update()
} ViewController.swift (ൈਮ)
None
QMVHJOϑΝΠϧΛஔ
-JCSBSZ$PSF.FEJB*01MVH*OT%"-
දࣔ͞Εͨʂ
None
ߏཁૉ ɹ$PSF.FEJB*0%"-1MVHJO ɹίϯτϩʔϥΞϓϦ
ίϯτϩʔϥΞϓϦ
$PSF.FEJB*0%"- 1MVHJO ίϯτϩʔϥΞϓϦ /41BTUFCPBSE -JCSBSZ$PSF.FEJB*01MVH*OT%"- ԾΧϝϥͷ࣮ମ 6*Λ࣋ͨͳ͍1MVHJOʹ Λ͢ΞϓϦ
1BTUFCPBSEʹΛஔׂ͘
ϓϩδΣΫτ࡞ ίϯτϩʔϧஔ /41BTUFCPBSEͰσʔλڞ༗ ίϯτϩʔϥΞϓϦ
None
None
ϓϩδΣΫτ࡞ ίϯτϩʔϧஔ /41BTUFCPBSEͰσʔλڞ༗ ίϯτϩʔϥΞϓϦ
None
None
ϓϩδΣΫτ࡞ ίϯτϩʔϧஔ /41BTUFCPBSEͰσʔλڞ༗ ίϯτϩʔϥΞϓϦ
extension NSPasteboard.Name { static let main = NSPasteboard.Name(Config.mainAppBundleIdentifier) } extension
NSPasteboard.PasteboardType { static let plain = NSPasteboard.PasteboardType(rawValue: "public.utf8-plain-text") } class SettingsPasteboard { static let shared = SettingsPasteboard() open var settings = [String: Any]() open func current() -> [String: Any] { let pasteboard = NSPasteboard(name: .main) if let element = pasteboard.pasteboardItems?.last, let str = element.string(forType: .plain), let data = str.data(using: .utf8) { do { let json = try JSONSerialization.jsonObject(with: data, options : .allowFragments) as! [String: Any] settings = json } catch { print("can't convert json") } } return settings } open func update() { let jsonStr = stringify(json: settings) let pasteboard = NSPasteboard(name: .main) pasteboard.declareTypes([.string], owner: nil) pasteboard.setString(jsonStr, forType: .string) } ... } SettingsPasteboard.swift (ൈਮ)
None
$PSF.FEJB*0%"- 1MVHJO ίϯτϩʔϥΞϓϦ /41BTUFCPBSE -JCSBSZ$PSF.FEJB*01MVH*OT%"- ԾΧϝϥͷ࣮ମ 6*Λ࣋ͨͳ͍1MVHJOʹ Λ͢ΞϓϦ
ίϯτϩʔϥΞϓϦ͔ΒΛૹ৴
None
None
None
࣮ํ๏ ݱ࣮֦ுͱͯ͠ͷར༻
'BDFUJNFΧϝϥө૾ Իೖྗ ը૾Ճ "1*Ϩεϙϯε ΩʔϘʔυೖྗ ,FZOPUFը໘ FUD ֎෦ ԾΧϝϥ ग़ྗՃػߏ
ݱ࣮֦ுͱͯ͠ͷԾΧϝϥ ֤छೖྗ
NNINNϏοάϋϯυϞʔυ
Ή͜͏ͷ͘ʹϑΟϧλɺϚΠϖʔδɺϝλόʔε
ϑΟϧλҟٞ͋Γɺͬͨ
ϑΟϧλΠϩϞωΞత
UPOBSJԕִϦΞϧλΠϜө૾
ϚΠϯυϚοϓ
None
δΣωϨΠςΟϒදݱ
ελϯϓϦΞΫγϣϯ
Իೝࣝͱ༁
None
None
ݱ࣮֦ுͱͯ͠ͷԾΧϝϥ
ࢀߟ IUUQTEFWFMPQFSBQQMFDPNMJCSBSZBSDIJWFTBNQMFDPEF$PSF.FEJB*0*OUSPEVDUJPO*OUSPIUNM IUUQTHJUIVCDPNKPIOCPJMFTDPSFNFEJBJPEBMNJOJNBMFYBNQMF IUUQTHJUIVCDPNTFBODIBT4JNQMF%"-1MVHJO IUUQTTQFBLFSEFDLDPNLJTIJLBXBLBUTVNJWJSUVBMXFCDBNFSBXP[VPSPV
5XJUUFSͰ࠷৽ใൃ৴த !TINEFWFMPQ ԾΧϝϥ࣮αϯϓϧ IUUQTHJUIVCDPNTBUPTIJ7JSUVBM$BNFSB4BNQMF