Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
TwilioのCameraSource に任意のフレームを使用するすゝめ
Search
BOB
December 22, 2020
Programming
1.7k
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
TwilioのCameraSource に任意のフレームを使用するすゝめ
potatotips 72
BOB
December 22, 2020
More Decks by BOB
See All by BOB
StoreKitTestを使ってアプリ内課金のテストや検証を効率化する方法
aratayokoyama
2
990
既存サブスクリプション商品にアップグレード・ダウングレード商品を追加しよう
aratayokoyama
1
2.6k
バーチャル背景を実現しよう
aratayokoyama
0
810
アクセシブルなチャートを実現しよう
aratayokoyama
0
1.8k
VirtualBitriseUserGroupMeetup_fresh_bob
aratayokoyama
0
140
Other Decks in Programming
See All in Programming
AIだと陥りがちなJakarta EE最新技術への移行時の落とし穴と解決策
tnagao7
0
110
ユニットテストの先へ:テスト技法で要求・仕様を整理するJava開発実践 / Beyond_Unit_Testing_Practical_Java_Development_Techniques_for_Organizing_Requirements_and_Specifications
shimashima35
0
410
気づいたらRubyで100作品 ー クリエイティブコーディングが生活の一部になるまで / 100 Ruby Sketches Later: How Creative Coding Became Part of My Life
chobishiba
3
590
Lessons from Spec-Driven Development
simas
PRO
0
210
AIとASP.NET Coreで雑Webアプリを作った話
mayuki
0
650
Semantic Version 単位で戦略を柔軟に変えて、パッケージアップデートを自動化する
daitasu
1
250
Mujeres en SEO Summit 2026 - Greatest Disaster Hits en Web Performance
guaca
0
180
Vue × Nuxt × Oxc どこまで使える?実運用の現在地
andpad
0
260
エージェンティックRAGにAWSで入門しよう!
har1101
8
1.6k
技術記事、AIに書かせるか、自分で書くか? 〜それでも私が自分の手で書く理由〜 / #QiitaConference
jnchito
2
1.4k
Oxcを導入して開発体験が向上した話
yug1224
4
320
さぁV100、メモリをお食べ・・・
nilpe
0
140
Featured
See All Featured
How to Build an AI Search Optimization Roadmap - Criteria and Steps to Take #SEOIRL
aleyda
1
2.1k
Future Trends and Review - Lecture 12 - Web Technologies (1019888BNR)
signer
PRO
0
3.6k
The Impact of AI in SEO - AI Overviews June 2024 Edition
aleyda
5
1.1k
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
2k
Fashionably flexible responsive web design (full day workshop)
malarkey
408
66k
The SEO Collaboration Effect
kristinabergwall1
1
490
How to build an LLM SEO readiness audit: a practical framework
nmsamuel
1
780
Optimizing for Happiness
mojombo
378
71k
Leadership Guide Workshop - DevTernity 2021
reverentgeek
1
310
YesSQL, Process and Tooling at Scale
rocio
174
15k
Documentation Writing (for coders)
carmenintech
77
5.4k
Transcript
ʲJ04ʳ5XJMJPͷ$BNFSB4PVSDF ʹҙͷϑϨʔϜΛ༻͢Δ͢ʍΊ "3"5":0,0:"#0#!QPUBUPUJQTJ04"OESPJE։ൃ5JQTڞ༗ձ
!#0#(7J "SBUB:PLPZBNB#0# # ळݩ߁ؔ࿈ʹ10Ҏ্අ͢ # ւಓੜ·Ε # ͩͯ͜ະདྷେଔ #
2020αΠόʔΤʔδΣϯτ৽ଔೖࣾ # λοϓϧͷiOSΤϯδχΞ
λοϓϧ w ͚ͷϚονϯάΞϓϦ w ݄ϦϦʔε w ϑϦοΫ6* w ຊਓೝূɼը໘ըࢭ
w 6*͕உঁͰେ͖͘ҧ͏ w 5XJMJPΛ༻ͨ͠ϏσΦνϟοτ
λοϓϧɿϏσΦνϟοτ w ݄ϏσΦνϟοτϦϦʔε w 5XJMJPΛ༻͓ͯ͠Γ·͢ w ݄ϑΟϧλʔػೳϦϦʔε w ʮ͓෦ͷย͚͕େมʯɼ
ʮͩ͠ͳΈΛ͑Δͷʹ͕͔͔࣌ؒΔʯ ͱ͍ͬͨΛड͚ͯ։ൃ͍ͨ͠·ͨ͠ w ϑΟϧλʔػೳ"3,JU༻͓ͯ͠Γ·͢ ϚονϯάΞϓϦʮλοϓϧੜʯɺ͙͢ʹΦϯϥΠϯσʔτ͕Ͱ͖Δ ʮϏσΦνϟοτʯʹ৽ͨʹʮϑΟϧλʔػೳʯΛՃɿhttps://www.cyberagent.co.jp/news/detail/id=25083 5XJMJPհͯ͠ҙͷϑϨʔϜΛૹΔํ๏Λ͝հ
(0"- ΞδΣϯμ CameraSourceʹҙͷϑϨʔϜΛ༻͢ΔϙΠϯτ˓˓ $BNFSB4PVSDFૹΔʹ˓˓͕େ "7$BQUVSFɿϑϨʔϜͷऔΓํ "3,JUɿϑϨʔϜͷऔΓํ
$BNFSB4PVSDFʹૹΔʹ˓˓͕େ
$BNFSB4PVSDFʹૹΔʹ˓˓͕େ // TVIVideoFrame - (nullable instancetype)initWithTimestamp:(CMTime)timestamp buffer:(nonnull CVImageBufferRef)imageBuffer orientation:(TVIVideoOrientation)orientation;
- (nullable instancetype)initWithTimeInterval:(CFTimeInterval)timeInterval buffer:(nonnull CVImageBufferRef)imageBuffer orientation:(TVIVideoOrientation)orientation; TVIVideoFrame Class Referenceɿhttps://twilio.github.io/twilio-video-ios/docs/latest/Classes/TVIVideoFrame.html#//api/name/initWithTimeInterval:buffer:orientation:
$BNFSB4PVSDFʹૹΔʹ˓˓͕େ // TVIVideoFrame - (nullable instancetype)initWithTimestamp:(CMTime)timestamp buffer:(nonnull CVImageBufferRef)imageBuffer orientation:(TVIVideoOrientation)orientation;
- (nullable instancetype)initWithTimeInterval:(CFTimeInterval)timeInterval buffer:(nonnull CVImageBufferRef)imageBuffer orientation:(TVIVideoOrientation)orientation; CVImageBufferRef CVImageBufferRef TVIVideoFrame Class Referenceɿhttps://twilio.github.io/twilio-video-ios/docs/latest/Classes/TVIVideoFrame.html#//api/name/initWithTimeInterval:buffer:orientation:
$BNFSB4PVSDFʹૹΔʹ˓˓͕େ // TVIVideoFrame - (nullable instancetype)initWithTimestamp:(CMTime)timestamp buffer:(nonnull CVImageBufferRef)imageBuffer orientation:(TVIVideoOrientation)orientation;
- (nullable instancetype)initWithTimeInterval:(CFTimeInterval)timeInterval buffer:(nonnull CVImageBufferRef)imageBuffer orientation:(TVIVideoOrientation)orientation; CMTime CFTimeInterval ϑϨʔϜΛऔಘ͢Δ Ճ͢Δ 7JEFP'SBNFΛ ࡞͢Δ ྲྀ͠ࠐΉ " # TVIVideoFrame Class Referenceɿhttps://twilio.github.io/twilio-video-ios/docs/latest/Classes/TVIVideoFrame.html#//api/name/initWithTimeInterval:buffer:orientation: $ %
$BNFSB4PVSDFʹૹΔʹ4JOL͕େ import TwilioVideo final class LocalTrackSourceManager: NSObject { private(set)
var camera: CameraSource! func onVideoFrame(image: CIImage, time: TimeInterval) { guard let pixelBuffer = image.convertCVPixelBuffer() else { return } guard let videoFrame = VideoFrame(timeInterval: time, buffer: pixelBuffer, orientation: VideoOrientation.up) else { return } camera.sink?.onVideoFrame(videoFrame) } func onVideoFrame(image: CIImage, time: CMTime) { guard let pixelBuffer = image.convertCVPixelBuffer() else { return } guard let videoFrame = VideoFrame(timestamp: time, buffer: pixelBuffer, orientation: VideoOrientation.up) else { return } camera.sink?.onVideoFrame(videoFrame) } } # $ %
import TwilioVideo final class LocalTrackSourceManager: NSObject { private(set) var camera:
CameraSource! func onVideoFrame(image: CIImage, time: TimeInterval) { guard let pixelBuffer = image.convertCVPixelBuffer() else { return } guard let videoFrame = VideoFrame(timeInterval: time, buffer: pixelBuffer, orientation: VideoOrientation.up) else { return } camera.sink?.onVideoFrame(videoFrame) } func onVideoFrame(image: CIImage, time: CMTime) { guard let pixelBuffer = image.convertCVPixelBuffer() else { return } guard let videoFrame = VideoFrame(timestamp: time, buffer: pixelBuffer, orientation: VideoOrientation.up) else { return } camera.sink?.onVideoFrame(videoFrame) } } $BNFSB4PVSDFʹૹΔʹ4JOL͕େ # $ % guard let pixelBuffer = image.convertCVPixelBuffer() else { return } guard let pixelBuffer = image.convertCVPixelBuffer() else { return }
import TwilioVideo final class LocalTrackSourceManager: NSObject { private(set) var camera:
CameraSource! func onVideoFrame(image: CIImage, time: TimeInterval) { guard let pixelBuffer = image.convertCVPixelBuffer() else { return } guard let videoFrame = VideoFrame(timeInterval: time, buffer: pixelBuffer, orientation: VideoOrientation.up) else { return } camera.sink?.onVideoFrame(videoFrame) } func onVideoFrame(image: CIImage, time: CMTime) { guard let pixelBuffer = image.convertCVPixelBuffer() else { return } guard let videoFrame = VideoFrame(timestamp: time, buffer: pixelBuffer, orientation: VideoOrientation.up) else { return } camera.sink?.onVideoFrame(videoFrame) } } $BNFSB4PVSDFʹૹΔʹ4JOL͕େ # $ % VideoFrame(timeInterval: time, buffer: pixelBuffer, orientation: VideoOrientation.up) VideoFrame(timestamp: time, buffer: pixelBuffer, orientation: VideoOrientation.up)
import TwilioVideo final class LocalTrackSourceManager: NSObject { private(set) var camera:
CameraSource! func onVideoFrame(image: CIImage, time: TimeInterval) { guard let pixelBuffer = image.convertCVPixelBuffer() else { return } guard let videoFrame = VideoFrame(timeInterval: time, buffer: pixelBuffer, orientation: VideoOrientation.up) else { return } camera.sink?.onVideoFrame(videoFrame) } func onVideoFrame(image: CIImage, time: CMTime) { guard let pixelBuffer = image.convertCVPixelBuffer() else { return } guard let videoFrame = VideoFrame(timestamp: time, buffer: pixelBuffer, orientation: VideoOrientation.up) else { return } camera.sink?.onVideoFrame(videoFrame) } } $BNFSB4PVSDFʹૹΔʹ4JOL͕େ camera.sink?.onVideoFrame(videoFrame) camera.sink?.onVideoFrame(videoFrame)
$BNFSB4PVSDFʹૹΔʹ4JOL͕େ import TwilioVideo final class LocalTrackSourceManager: NSObject { private(set)
var camera: CameraSource! func onVideoFrame(image: CIImage, time: TimeInterval) { guard let pixelBuffer = image.convertCVPixelBuffer() else { return } guard let videoFrame = VideoFrame(timeInterval: time, buffer: pixelBuffer, orientation: VideoOrientation.up) else { return } camera.sink?.onVideoFrame(videoFrame) } func onVideoFrame(image: CIImage, time: CMTime) { guard let pixelBuffer = image.convertCVPixelBuffer() else { return } guard let videoFrame = VideoFrame(timestamp: time, buffer: pixelBuffer, orientation: VideoOrientation.up) else { return } camera.sink?.onVideoFrame(videoFrame) } } camera.sink?.onVideoFrame(videoFrame) camera.sink?.onVideoFrame(videoFrame) extension VideoChatAVCaptureView: VideoSource { weak var sink: VideoSink? var isScreencast: Bool { return false } func requestOutputFormat(_ outputFormat: VideoFormat) { guard let sink = sink else { return } sink.onVideoFormatRequest(outputFormat) } } w 7JEFP4PVSDFʹ४ڌͤͯ͞TJOLΛ࣋ͭ͜ͱՄೳ w ͨͩ͠ɼৗʹ4JOLϑϨʔϜΛૹΔ͜ͱ͕݅ ʢΫϥογϡ͠·͢ʣ w Ұํɼ$BNFSB4PVSDFͷ4JOLʹͦͷ͕݅ͳ͍ 5JQT var camera: CameraSource!
"7$BQUVSFɿϑϨʔϜͷऔΓํ
"7$BQUVSFɿϑϨʔϜͷऔΓํ import AVFoundation import UIKit final class VideoChatAVCaptureView: UIView
{ private var session: AVCaptureSession? private func prepare() { let output: AVCaptureVideoDataOutput = AVCaptureVideoDataOutput() output.setSampleBufferDelegate(self, queue: DispatchQueue(label: "videoChat")) } } extension VideoChatAVCaptureView: AVCaptureVideoDataOutputSampleBufferDelegate { func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { guard let ciImage = sampleBuffer.convertCIImage() else { return } let timestamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer) localTrackSourceManager.onVideoFrame(image: ciImage, time: timestamp) } } " #
"7$BQUVSFɿϑϨʔϜͷऔΓํ import AVFoundation import UIKit final class VideoChatAVCaptureView: UIView
{ private var session: AVCaptureSession? private func prepare() { let output: AVCaptureVideoDataOutput = AVCaptureVideoDataOutput() output.setSampleBufferDelegate(self, queue: DispatchQueue(label: "videoChat")) } } extension VideoChatAVCaptureView: AVCaptureVideoDataOutputSampleBufferDelegate { func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { guard let ciImage = sampleBuffer.convertCIImage() else { return } let timestamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer) localTrackSourceManager.onVideoFrame(image: ciImage, time: timestamp) } } output.setSampleBufferDelegate(self, queue: DispatchQueue(label: "videoChat")) EFMFHBUFΛࣗʹ͋ͯ·͢
"7$BQUVSFɿϑϨʔϜͷऔΓํ import AVFoundation import UIKit final class VideoChatAVCaptureView: UIView
{ private var session: AVCaptureSession? private func prepare() { let output: AVCaptureVideoDataOutput = AVCaptureVideoDataOutput() output.setSampleBufferDelegate(self, queue: DispatchQueue(label: "videoChat")) } } extension VideoChatAVCaptureView: AVCaptureVideoDataOutputSampleBufferDelegate { func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { guard let ciImage = sampleBuffer.convertCIImage() else { return } let timestamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer) localTrackSourceManager.onVideoFrame(image: ciImage, time: timestamp) } } guard let ciImage = sampleBuffer.convertCIImage() else { return } $.4BNQMF#V⒎FSΛ$**NBHFʹม͠·͢
"7$BQUVSFɿϑϨʔϜͷऔΓํ import AVFoundation import UIKit final class VideoChatAVCaptureView: UIView
{ private var session: AVCaptureSession? private func prepare() { let output: AVCaptureVideoDataOutput = AVCaptureVideoDataOutput() output.setSampleBufferDelegate(self, queue: DispatchQueue(label: "videoChat")) } } extension VideoChatAVCaptureView: AVCaptureVideoDataOutputSampleBufferDelegate { func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { guard let ciImage = sampleBuffer.convertCIImage() else { return } let timestamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer) localTrackSourceManager.onVideoFrame(image: ciImage, time: timestamp) } } let timestamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer) $.5JNFΛ࡞͠·͢
"3,JUɿϑϨʔϜͷऔΓํ
"3,JUɿϑϨʔϜͷऔΓํ import ARKit import SceneKit import UIKit final class
VideoChatARKitView: UIView { private lazy var sceneView: ARSCNView = {...}() private func prepare() { sceneView.delegate = self } } extension VideoChatARKitView: ARSCNViewDelegate { func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) { guard let sceneView = renderer as? ARSCNView else { return } guard let ciImage = CIImage(image: sceneView.snapshot()) else { return } localTrackSourceManager.onVideoFrame(image: ciImage, time: time) } } " #
import ARKit import SceneKit import UIKit final class VideoChatARKitView: UIView
{ private lazy var sceneView: ARSCNView = {...}() private func prepare() { sceneView.delegate = self } } extension VideoChatARKitView: ARSCNViewDelegate { func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) { guard let sceneView = renderer as? ARSCNView else { return } guard let ciImage = CIImage(image: sceneView.snapshot()) else { return } localTrackSourceManager.onVideoFrame(image: ciImage, time: time) } } "3,JUɿϑϨʔϜͷऔΓํ sceneView.delegate = self EFMFHBUFΛࣗʹ͋ͯ·͢
"3,JUɿϑϨʔϜͷऔΓํ import ARKit import SceneKit import UIKit final class
VideoChatARKitView: UIView { private lazy var sceneView: ARSCNView = {...}() private func prepare() { sceneView.delegate = self } } extension VideoChatARKitView: ARSCNViewDelegate { func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) { guard let sceneView = renderer as? ARSCNView else { return } guard let ciImage = CIImage(image: sceneView.snapshot()) else { return } localTrackSourceManager.onVideoFrame(image: ciImage, time: time) } } let ciImage = CIImage(image: sceneView.snapshot()) εφοϓγϣοτΛऔಘ͠·͢
"3,JUɿϑϨʔϜͷऔΓํ import ARKit import SceneKit import UIKit final class
VideoChatARKitView: UIView { private lazy var sceneView: ARSCNView = {...}() private func prepare() { sceneView.delegate = self } } extension VideoChatARKitView: ARSCNViewDelegate { func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) { guard let sceneView = renderer as? ARSCNView else { return } guard let ciImage = CIImage(image: sceneView.snapshot()) else { return } localTrackSourceManager.onVideoFrame(image: ciImage, time: time) } } time: TimeInterval ͦͷ··͢͜ͱ͕Ͱ͖·͢
·ͱΊ
·ͱΊ (0"- εςοϓ CameraSourceʹҙͷϑϨʔϜΛ༻͢ΔϙΠϯτSink ϑϨʔϜΛऔಘ͢Δ Ճ͢Δ 7JEFP'SBNFΛ ࡞͢Δ ྲྀ͠ࠐΉ
" # $ %