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
DataChannel を利用した ホワイトボードアプリ開発事例
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
hidekuni KAJITA
October 09, 2018
Programming
1k
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
DataChannel を利用した ホワイトボードアプリ開発事例
hidekuni KAJITA
October 09, 2018
More Decks by hidekuni KAJITA
See All by hidekuni KAJITA
あれのその後.pdf
hidenba
0
33
NVIDIABroadcast便利
hidenba
0
140
レバーレスアケコンを作った話
hidenba
0
82
「冒険できる社会」を実現する “あしたのチーム”
hidenba
0
170
すぐできるDocker
hidenba
0
1.3k
リズムから生まれるアジャイルな開発
hidenba
0
620
Other Decks in Programming
See All in Programming
キャリア迷子上等 ─ "ない道"は自分で作ればいい
16bitidol
3
2.3k
Contextとはなにか
chiroruxx
1
380
Developing with AI Agents — Codex, Claude Code & Cowork Practical Guide
x5gtrn
PRO
0
1.3k
Observability in Practice:Grafana 與 Edge Device SRE 的那些事
blueswen
0
180
「なぜそう決めたのか」を残し続ける仕組み ― Notion AI カスタムエージェント × Slack連携による設計判断の自動記録 - NIKKEI Tech Talk #47
niftycorp
PRO
0
230
Even G2とAWSで推しのエージェントを召喚しよう!
har1101
1
130
Make SRE Operations Easier with Azure SRE Agent
kkamegawa
0
8.8k
Dataformのリポジトリを立ち上げるときにまずやること / dataform-day0-2026
snhryt
0
190
気づいたらRubyで100作品 ー クリエイティブコーディングが生活の一部になるまで / 100 Ruby Sketches Later: How Creative Coding Became Part of My Life
chobishiba
3
610
LLMによるContent Moderationの本番運用の裏側と品質担保への挑戦
suikabar
3
790
エンジニア向け会社紹介/Findy Company Profile
findyinc
6
350k
コンテキストの使い捨てをやめる — ビジネスルール駆動開発と miko —
ioki
0
240
Featured
See All Featured
Facilitating Awesome Meetings
lara
57
7k
Reflections from 52 weeks, 52 projects
jeffersonlam
356
21k
Believing is Seeing
oripsolob
1
160
コードの90%をAIが書く世界で何が待っているのか / What awaits us in a world where 90% of the code is written by AI
rkaga
62
44k
Testing 201, or: Great Expectations
jmmastey
46
8.2k
JAMstack: Web Apps at Ludicrous Speed - All Things Open 2022
reverentgeek
1
480
How to Get Subject Matter Experts Bought In and Actively Contributing to SEO & PR Initiatives.
livdayseo
0
140
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
4.3k
The Language of Interfaces
destraynor
162
27k
The Illustrated Children's Guide to Kubernetes
chrisshort
51
52k
Effective software design: The role of men in debugging patriarchy in IT @ Voxxed Days AMS
baasie
0
440
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.8k
Transcript
DataChannel を利用した ホワイトボードアプリ開発事例 2018/10/9 WebRTC Meetup Tokyo #19 永和システムマネジメント @kunitoo
@hide_nba
2 おしながき • Linkup の概要 • Linkup の機能 • Linkup
の構成 • DataChannel の活用 • 画面共有 • 各クライアントの表示名 • ココさす • ホワイトボード
自己紹介 • 名前: 伊藤邦彦 • 所属: 永和システムマネジメント • 在住: 富山県
• Twitter: @kunitoo • GitHub: @kunitoo 3
4 永和システムマネジメント 管理部 未来企画室 組込み技術 センター 金融システム 事業部 ITサービス 事業部
医療システム 事業部 アジャイル 事業部 事業本部 永和システム マネジメント 【社員数】216名 【事業内容】・情報システム開発および構築 ・パッケージの開発および販売 ・コンピュータ、周辺機器の販売 ・開発技術コンサルティング、教育 【所在地】本社:福井県福井市問屋町3丁目111番地 支社:東京都千代田区神田須田町2丁目3番地1号 沖縄:沖縄県那覇市泊2丁目1-18T&C泊ビル 【在宅勤務制度】: 多くの社員が利用している 【リモートワーク】: 石川、富山に1名ずつ
5 Linkup の概要
6
Linkup • チームのためのリモートコラボレーションツール • 2017年9月頃から事業部の10%ルールとしてスタート • 顔を見ながら画面共有したい • 2018年4月頃から本格始動 •
ホワイトボードに書きながらビデオチャットしたい • 9月20日にオープンβとしてリリース • https://linkup.world/ 7
8 チーム名を決めて試してみて下さい
Linkup のコンセプト • Web に和じゃを作りたい • 執務室内中央のセミオープンな会議スペース • ディスプレイ、ホワイトボードがある •
オフィスのセミオープンな会議スペースに人が 集って議論をすぐスタートできる • 全員が見えるディスプレイ • ホワイトボードが利用できる • 人が集ったことが分かる • リモート(低帯域/低浸透性) から対面(広帯域/高 浸透性)のコミュニケーション 9
Linkup の機能 10
Linkup の機能 • ビデオチャット • 多人数での双方向チャット • ホワイトボード • 図や絵を描いてのイメージ共有
• 画面共有 • ココがさせる • チャット連携 • idobata/slack へのルーム状況の通知 • オープンスペース(実現予定) • ルームに入らずに、ホワイトボード、話している人が分かる • あいづち/うなずき(実現予定) 11
Linkup の利用シーン • チームの朝会 • チャット連携 • チーム内の相談やミーティング • ホワイトボード
• 画面共有 • ここがさせる • ふりかえり • 付箋 12
朝会 • Webhook を設定して おく • メンバー集る • 集っている様子が チャットで分かる
13 Webhook を設定 Idobta のチャット通知例
チーム内の相談やミーティング • チャットで呼びかける • 話題をホワイトボード に書き出す • 画面共有をする 14
チーム内の相談やミーティング • チャットで呼びかける • 話題をホワイトボード に書き出す • 画面共有をする 15
ふりかえり • 手元の付箋に書き溜 めることができる • ホワイトボードに張り つけてチームに共有す る 16
Linkup の構成 17
Linkup の技術要素 • フロントエンド • React • Canvas(fabric.js) • SkyWay
• CloudFront • バックエンド(API) • Ruby on Rails • GraphQL • ECS(Fargate) • インフラ • Terraform 18
DataChannel の活用 19
DataChannel の活用 • 画面共有 • 各クライアントの表示名 • ココさす • ホワイトボード
20
画面共有での利用 1. skyway-screenshare を利用してstreamオブジェクトを取得 2. 画面共有を始めたことを Data Channel で通知 3.
通知を受けた側から接続を開始する `peer.call` 4. 画面共有側で stream を設定して、接続を完了する 21
クライアント情報の共有 1. stream を受信したとき名前を送り、相手の名前を要求 する(REQUEST_DISPLAY_NAME) 2. 名前の要求が来たら、 a. 要求相手の名前を peerId
を元にカメラ stream と 紐付ける b. 自分の名前を送信する(DISPLAY_NAME) 22
ココさす • 画面共有の領域にCanvasを用 意、クリックした位置を取得 • video の表示しているサイズから 相対位置を割り出し送信する (HTMLVideoElement.videoHeight ,
videoWidth) • 受信側で相手のvideoサイズと自 分のvideoサイズから位置を割り 出しアニメーションを描画する 23
demo 24
交代 25
自己紹介 • 名前: かじたひでくに • 所属: 永和システムマネジメント • Twitter: @hide_nba
• GitHub: @hidenba 26
ホワイトボードで出来ること • 手書き 27
ホワイトボードで出来ること • 付箋 28
ホワイトボードで出来ること • 画像のアップロード/ダウンロード 29
demo 30
SkyWayでのデータ送信 const peer = new Peer({ key: SKYWAY_KEY, debug: LOG_LEVEL
}) const room = peer.joinRoom(roomName, { mode: 'sfu', stream: stream }) room.send({data: 'send data'}) 31
SkyWayでのデータ受信 const peer = new Peer({ key: SKYWAY_KEY, debug: LOG_LEVEL
}) const room = peer.joinRoom(roomName, { mode: 'sfu', stream: stream }) room.on('data', (data) => { console.log(data) }) 32
送受信しているデータの構造 { action: 'UPDATE_WHITEBOARD', data1: dataobject, data2: dataobject, data3: dataobject
} 33
受信時のイベントハンドリング room.on('data', dataObject => { if (dataObject.data.action === 'START_SCREEN_SHARE') {
} else if (dataObject.data.action === 'STOP_SCREEN_SHARE') { } else if (dataObject.data.action === 'REQUEST_DISPLAY_NAME') { } else if (dataObject.data.action === 'UPDATE_WHITEBOARD') { const { id, payload, operation, uuid } = dataObject.data update(id, operation, uuid, payload) ... }) 34
オブジェクトの追加 1. Objectの追加 2. Objectをシリアライズして JSONにする 3. JSONをサーバへ送信 4. Roomに接続しているPeer
と同期 35 send add on(data) 1. データ受信 2. JSONからObjectへ 3. Canvasに描画
追加操作の送信 const objectPayload = JSON.stringify(fabricObject) request(`${LINKUP_API_URL}/graphql`, query) room.send({ action: 'UPDATE_WHITEBOARD',
payload: objectPayload, operation: 'add', uuid: uuid }) 36
ホワイトボード更新イベント受信 room.on('data', dataObject => { if (dataObject.data.action === 'START_SCREEN_SHARE') {
} else if (dataObject.data.action === 'STOP_SCREEN_SHARE') { } else if (dataObject.data.action === 'REQUEST_DISPLAY_NAME') { } else if (dataObject.data.action === 'UPDATE_WHITEBOARD') { const { id, payload, operation, uuid } = dataObject.data update(id, operation, uuid, payload) }) 37
ホワイトボード更新 switch (operation) { case 'add': this.addObjectFromJson(data) break case 'remove':
this.removeActionByUUID(prevOperation.uuid) break ... } 38
オブジェクトの移動 1. Objectの移動 2. Objectの削除情報を各クライア ントと同期 3. 削除情報をサーバへ送信 4. 移動後のObjectをシリアライズし
てJSONにする 5. JSONをサーバへ送信 6. 各Peerと同期 39 send remove on(data) 1. 削除イベント受信 2. UUIDの一致するObjectを削除 3. 移動後の追加データ受信 4. JSONからObjectへ 5. Canvasに描画 send add on(data)
削除操作の送信 const objectPayload = JSON.stringify(fabricObject) request(`${LINKUP_API_URL}/graphql`, query) room.send({ action: 'UPDATE_WHITEBOARD',
operation: 'remove', uuid: uuid }) 40
ホワイトボード更新イベント受信 room.on('data', dataObject => { if (dataObject.data.action === 'START_SCREEN_SHARE') {
} else if (dataObject.data.action === 'STOP_SCREEN_SHARE') { } else if (dataObject.data.action === 'REQUEST_DISPLAY_NAME') { } else if (dataObject.data.action === 'UPDATE_WHITEBOARD') { const { id, payload, operation, uuid } = dataObject.data update(id, operation, uuid, payload) }) 41
ホワイトボード更新 switch (operation) { case 'add': this.addObjectFromJson(data) break case 'remove':
this.removeActionByUUID(uuid) break ... } 42
追加操作の送信 const objectPayload = JSON.stringify(fabricObject) request(`${LINKUP_API_URL}/graphql`, query) room.send({ action: 'UPDATE_WHITEBOARD',
payload: objectPayload, operation: 'add', uuid: uuid }) 43
ホワイトボード更新イベント受信 room.on('data', dataObject => { if (dataObject.data.action === 'START_SCREEN_SHARE') {
} else if (dataObject.data.action === 'STOP_SCREEN_SHARE') { } else if (dataObject.data.action === 'REQUEST_DISPLAY_NAME') { } else if (dataObject.data.action === 'UPDATE_WHITEBOARD') { const { id, payload, operation, uuid } = dataObject.data update(id, operation, uuid, payload) }) 44
ホワイトボード更新 switch (operation) { case 'add': this.addObjectFromJson(data) break case 'remove':
this.removeActionByUUID(prevOperation.uuid) break ... } 45
46 チーム名を決めて試してみて下さい