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
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
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
1k
既存サブスクリプション商品にアップグレード・ダウングレード商品を追加しよう
aratayokoyama
1
2.6k
バーチャル背景を実現しよう
aratayokoyama
0
830
アクセシブルなチャートを実現しよう
aratayokoyama
0
1.9k
VirtualBitriseUserGroupMeetup_fresh_bob
aratayokoyama
0
150
Other Decks in Programming
See All in Programming
torikago - Ruby::Boxで照らすモジュラモノリスの実行境界
se4weed
1
330
メールのエイリアス機能を履き違えない
isshinfunada
0
220
【SRE NEXT 2026 Lunch Session】一人目専任SREの立ち上げを加速する ― AIと進めたオンボーディングで2分を0.04秒にした話
pkshadeck
PRO
0
3.5k
言語を使う側から、作る側へ。 自作 Lisp で得た新たな気づき。
andpad
0
150
Augmenting AI with the Power of Jakarta EE
ivargrimstad
0
480
AWS CDK を「作」ってみた 〜フルスクラッチで見えた CDK の裏側〜 / aws-cdk-from-scratch
gotok365
3
2.8k
為什麼你並不需要ViewModel / No, you don't need a ViewModel
lovee
1
480
Android CLI
fornewid
0
210
20260722_microCMSで考える、AI時代のコンテンツ運用設計
yosh1
0
350
数百円から始めるRuby電子工作
tarosay
0
130
Apache Hive: そしてCloud Native Lakehouseへ
okumin
1
200
Lean は証明の正しさを確認するためだけのツールって思ってませんか?
inoueasei
1
140
Featured
See All Featured
Thoughts on Productivity
jonyablonski
76
5.3k
Product Roadmaps are Hard
iamctodd
55
12k
Mobile First: as difficult as doing things right
swwweet
225
10k
sira's awesome portfolio website redesign presentation
elsirapls
0
320
The World Runs on Bad Software
bkeepers
PRO
72
12k
B2B Lead Gen: Tactics, Traps & Triumph
marketingsoph
0
190
The SEO Collaboration Effect
kristinabergwall1
1
510
What's in a price? How to price your products and services
michaelherold
247
13k
ラッコキーワード サービス紹介資料
rakko
1
4.2M
What the history of the web can teach us about the future of AI
inesmontani
PRO
1
650
Winning Ecommerce Organic Search in an AI Era - #searchnstuff2025
aleyda
1
2.1k
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.8k
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Λ ࡞͢Δ ྲྀ͠ࠐΉ
" # $ %