WebRTCを活用した食卓コネクトサイトの開発(卒論)

平成 26 年度卒業研究論文
WebRTC を活用した
食卓コネクトサイトの開発
近畿大学工学部
情報システム工学科
システム開発コース
学籍番号 1110960024 大谷武嗣
目次
1. 研究の背景と目的........................................................................................................... 1
2. 食卓コネクトについて ................................................................................................... 1
2.1 共食に伴う利点 ......................................................................................................... 2
3. 画面説明・機能説明 ....................................................................................................... 2
3.1 ライトコンテンツ(①)........................................................................................... 3
3.2 センターコンテンツ(②) ....................................................................................... 3
3.3 レフトコンテンツ(③)........................................................................................... 3
3.3.1 配食サービス ..................................................................................................... 3
3.3.2 栄養士さん ......................................................................................................... 3
3.3.3 食卓につく ......................................................................................................... 4
3.4 ビデオ通話画面 ......................................................................................................... 4
3.4.1 ビデオ通話手順 .................................................................................................. 5
3.4.2 録画手順 ............................................................................................................. 6
4. 技術要素......................................................................................................................... 8
4.1 開発環境 ................................................................................................................... 8
4.2 HTML5......................................................................................................................... 9
4.3 CSS3 .......................................................................................................................... 9
4.4 PHP ............................................................................................................................ 9
4.5 Javascript ...............................................................................................................10
4.5.1
jQuery ...............................................................................................................10
4.6 WebRTC ......................................................................................................................10
4.6.1
従来の通信方法と WebRTC .................................................................................10
4.6.2
WebRTC 対応ブラウザ .........................................................................................12
4.6.3
NAT 問題 ............................................................................................................12
4.6.3.1 NAT とは ............................................................................................................... 12
4.6.3.2. NAT 超え ............................................................................................................. 12
4.6.4
SkyWay について ...............................................................................................14
4.6.5
使用するライブラリについて ...........................................................................14
4.6.5.1
PeerJS.................................................................................................................. 15
4.6.5.2
RecordRTC ............................................................................................................ 17
4.6.5.3
RecordRTCtoPHP ................................................................................................... 17
4.7 部屋番号生成について .............................................................................................17
4.8 通信方法について ....................................................................................................19
4.8.1
PeerID の自動取得 ............................................................................................19
4.8.2
PeerID へ発信 ...................................................................................................20
4.9 番号による部屋割りについて...................................................................................21
4.10 録画方法について ..................................................................................................22
4.11 録画した映像の閲覧ページについて ......................................................................29
5. 終わりに........................................................................................................................31
6. 謝辞 ...............................................................................................................................32
7. 参考文献・引用 .............................................................................................................32
8. ふろく ...........................................................................................................................33
1. 研究の背景と目的
近畿大学工学部の徐研究室と福山大学人間文化学部の橋本研究室は数年前から,障害者支援な
どの社会的な問題に対して,心理学的なアプローチと工学的な技術を組み合わせた取り組みを行
ってきた.今回は,高齢者の孤食という問題について,工学的な技術を利用することで,問題を
解決していくことを考えているが,その背景は以下の通りである.
橋本研究室では,以前から,食事のおいしさに関わる要因として共食に着目し,研究を行って
きた.共食とは他者と共に食事をとることであり,共食によって食事のおいしさが亢進すること
は,一連の研究で示されている.しかしながら,共食の何が要因となり,おいしさに直接的な影
響を与えるのかについては,一致した見解が得られていないのが現状である.そこで,他者の存
在の有無という要因に着目し,映像を介した他者の存在は,おいしさを亢進するかを検討した.
その結果,映像を介した他者の存在のみでは,おいしさが亢進されるとは言い難く,共食の効果
について他の要因が働いている可能性が示された.
一方では,一緒に食べている人とのコミュニケーションがおいしさや満足度に必要であること
を示唆した研究がある.例えば足立(1991)は,65歳以上の高齢者を対象に,食事のおいしさや満
足度に影響をおよぼす要因について調査を行った.その結果,1人で食事を行うよりも,誰かとコ
ミュニケーションをとりながら食事を行う方が,おいしさや満足度をより感じると評価された.
また,食事の満足度が高ければ,食事をとる際の栄養バランスが良くなることも示唆された.
しかしながら,近年の社会的な問題として,高齢者が増加傾向であることがあげられるが,そ
れとともに独居の高齢者の増加も問題視されている.内閣府によると,65歳以上の独居高齢者は,
昭和55年(1980)年には男性約19万人,女性約69万人であったが,平成22年(2010)年には男性約139
万人,女性約341万人とされており,男女ともに増加傾向であることが明らかとなった.
また,独居高齢者の問題として,不健康な食生活があげられる.延原・北園・渡辺・安西(2001)
は,寝たきりや独居の高齢者は,健康状態や食事の栄養バランスが不良であり,独居の高齢者は
手間のかかる食事を敬遠する傾向があると述べている.また独居に限らず,高齢者の食事状況に
おいて家族との食事が減少するほど,食材料摂取における全体的な栄養のバランスに問題が多く
なることが報告されている(池田・浅野・木谷・永田,1991).
このような状況の改善のために,コミュニケーションをとる方法として通信技術に着目した.
通信技術を利用することによって,独居の高齢者のようなコミュニケーションをとることが難し
い方たちであっても,気軽に他者とつながることが可能である.他者とつながることで,足立
(1991)で述べられたように,高齢者の食事を改善することにつながるのではないかと考えた.
以上のことから,プラチナ世代およびその家族の QOL(クオリティオブライフ)の向上のために,
本研究を立案し,
「共食」
「プラチナ世代」
「ネット/遠隔通信」
「リアルタイムコミュニケーション」
「心のアンチエイジング」というキーワードをもとに取り組みを始めた.
2. 食卓コネクトについて
食卓コネクトは WebRTC を活用したリアルタイムコミュニケーションサイトであり,Web カ
メラを使いお互いの食卓の様子をリアルタイムに映像で通話を行うことで、遠隔地の家族や友人
と食事の時間をともに過ごすことができる.さらに,例えば祝いの膳の活躍の場になるとも期待
1
があり,遠隔地に住まう父親の誕生日に配食サービスを依頼し,食卓コネクトでビデオを通して
食事を共に楽しむサービス(祝いの膳)である.
2.1 共食に伴う利点
農林水産省では共食に伴う利点に関するアンケートを実施し以下のような結果を公開している.
(図 1)http://www.maff.go.jp/j/syokuiku/minna_navi/topics/topics1_02.html
このアンケート結果から,共食する利点は単に栄養をとることよりも,コミュニケーションの
場としてより重要視されていることがわかる.食卓コネクトは離れていても食卓をつなぎ,一緒
に食事をし,コミュニケーションを図る状況を提供する.
3. 画面説明・機能説明
図 2 は食卓コネクトのトップページであり,左に各項目に対する解説・ガイド,真ん中にメイ
ンコンテンツ,右に各機能を配置している.
(図 2)
2
3.1 ライトコンテンツ(①)
ライトコンテンツ(①)では,上から「食卓をつなげるとは」「配食サービスとは」「思いをつ
たえるには」
「ビデオレターを送るには」の各項目がボタンの役割となっており,クリックするこ
とで各ボタンに対応した同ページ内の食卓コネクトの機能説明・解説を行っている位置に移動し
てくれるナビゲーションの役割を担っている.
3.2 センターコンテンツ(②)
センターコンテンツ(②)では,画面上部に画像によるタイトル表示を設け,それ以下を食卓
コネクト利用にあたっての画面説明・機能説明およびサービスの説明を行っている.3.1.1 のライ
トコンテンツ(①)各項目をクリックすることで移動する先はセンターコンテンツ(②)の内容
である.
3.3 レフトコンテンツ(③)
「配食サービス」「栄養士さん」「食卓につく」について各ボタン・機能が配置されている.現
在実際利用可能な機能は「食卓につく」のみであり,その他「配食サービス」
「栄養士さん」に関
しては未実装である.
3.3.1 配食サービス
自宅まで弁当を配達してくれる「配食サービス」を行っている企業と連携し,「配食サービス」
ボタンをクリックすると弁当を注文することができるページに移る.配食サービスのスタッフは
配食するだけでなく,食卓コネクトの開通を支援したり食卓コネクト利用に関するサポートをす
る.このようにして配食で胃袋を満たし,食卓コネクトで心を満たす.
現在は構想のみであり実際の機能を利用できる段階には至ってない.
3.3.2 栄養士さん
現段階での機能の実装は完了しておらず,構想段階で止まっている.栄養士には複数種類(
「ペ
ット栄養士」
「病院栄養士」
「健康運動指導士」
「学校栄養士」
「スポーツ栄養士」)があり.活躍で
きるフィールドは広い.多くの人が考える栄養士の仕事といえば,病院での栄養指導や学校・保
育園・施設等での食事作りというイメージである.しかし,それ以外でも栄養士の活躍の場は広
がっており,求められる役割も変化している.さらに,病院や学校などに勤務するのではなく,
フリーランスとして働く栄養士も増えてきており,そういったフリーの人たちは基本的に自分で
仕事を見つけなければならない.
そこで食卓コネクトではそういったフリーの栄養士の存在を世の中に広く知ってもらい活躍で
きる場として提供できればと構想を練っている.
例えば,都合が合わず一人で食事をとらなければならない状況になった時,栄養士との通話で
お話を聞き健康に気をつかう.自分の健康状態に合わせ配食メニューを選んでもらう.ペット栄
養士との通話で自分のペットに関する相談を受けてもらう.これらの事が考えられる.
3
3.3.3 食卓につく
(図 3)
「食卓につく」では図 3 のようにあらかじめランダムに生成された4ケタの数字が入力されてい
る.この 4 ケタの数字は自由に書き換えができ,数字入力後「入室」ボタンを押すことで入力さ
れた数字により割り振られたページにアクセスができる.
3.4 ビデオ通話画面
(図 4)
図 4 は 3.1.3.3 の 4 ケタの数字を入力し,入室ボタンをクリックした後にアクセスしたページ
である.このページでは入室前に入力した 4 ケタの数字で割り振られたビデオ通話を行うページ
であり,同じ数字で入室することで任意の相手とビデオ通話を楽しむことができる.
画像の①の部分にビデオ通話相手の Web カメラの映像を,②に自分の Web カメラの映像を映
4
し出す.また,②の「映像を録画」ボタンをクリックすることで自分の Web カメラの映像を録画
する手順に移行し,
「保存されたビデオレター」ボタンをクリックすることで録画された映像を閲
覧することが出来るページに移る.閲覧ページでは入室時の 4 ケタの数字で管理されており,過
去に録画した映像を閲覧したい場合,録画した時と同じ数字でビデオ通話画面からアクセスする
必要がある.
3.4.1 ビデオ通話手順
あらかじめ Web カメラをパソコンに接続しておき,使用できる状態にあることを前提とする.
入室後は図 5 の様な画面となっており,画面上部に灰色の領域が表示されている.
(図 5)
ここでは図 6 のように現在のページがパソコンに接続している Web カメラのアクセスを許可
するかということを問われているので「許可」をクリックする.
(図 6)
すると図 7 のように右下に自分の Web カメラからの映像が映し出される.
(図 7)
5
ここまでの手順で自分の Web カメラ準備は完了である.現在は通話待機モードとなっており,
通話相手もこの手順を完了することで自動的にビデオ通話が開始される.
図 7_1 は相手もここまでの手順を終え,ビデオを通話を実際に行っている画像である.①が相手
の Web カメラからの映像,②が自分の Web カメラからの映像である.
3.4.2 録画手順
3.1.4.1 の手順を完了し右下に Web カメラからの映像が映し出された状態を前提とする.図 8
のように自分の映像が表示されている下にある「映像を録画」ボタンをクリックする.
(図 8)
図 9 のように画面上部に灰色の領域が新しく表示されるので,選択肢のうち「許可」をクリッ
クすると録画が開始される.
6
(図 9)
録画中は図 10 のように表示されている自分のカメラ映像に赤枠が出現する.
(図 10)
録画を停止するには,撮影中の印である赤枠が出ている状態でカメラ映像下にある[録画を停止]
ボタンをクリックする.
以上の手順を進めた後,録画した映像をビデオレターとして保存するか削除するかを決定する.
録画を停止すると図 11 のような画面になる.各画面・ボタン説明を①②③として以下に記述す
る.
(図 11)
① 画面右上に[録画を停止]ボタンを押すまでに撮影・録画された映像が自動的に表示・再生
する.ここでビデオレターとして保存する前に映像を確認することができる.
② ①で確認した映像をビデオレターとして保存して良いと判断したなら,「映像を登録」ボ
タンをクリックして保存することができる.
7
③ ①で確認した映像をビデオレターとして保存したくないと判断したなら,「映像を削除」
ボタンをクリックして削除することができる.
最後に「保存されたビデオレター」ボタンをクリックすることで,図 12 のように過去に録画
された映像を部屋番号ごとにいつでも閲覧することができる.
(図 12)
また動画下に配置されている「ビデオレターを削除」ボタンをクリックすると,図 13 のよ
うに本当に削除しても良いか問われるウィンドウが表示され,「OK」をクリックすることで削除
される.
(図 13)
4. 技術要素
開発環境,開発言語,使用したライブラリ等の各技術要素に関する項目を記述する.
4.1 開発環境
開発に使用する PC
① :MacBookPro Retina 1.5-inch,Late 2013
・プロセッサ:2 GHz Intel Core i7
・メモリ:16GB 1600 MHz DDR3
・グラフィックス:Intel Iris Pro
・ソフトウェア:(現時点で)OS X 10.9.5
② :Windows 7 Professional
・プロセッサ:2.80GHz Intel(R) Core(TM) i5-2300 CPU
8
・メモリ:6GB(3.49GB 使用可能)
・システム:32bit オペレーションシステム
WebRTC の通信はブラウザ同士の直接通信であり,2 人のビデオ通信なら 2 本の,3
人なら 8 本のメディアストリームが接続される.各参加者が他のすべての参加者に接続
するフルメッシュネットワークになっており,あまりに多くの接続を行うと CPU が負荷
に耐えられない.
よって①②の二つの PC を使用し直接通信の実験を行う.
heteml レンタルサーバー
Web サーバー heteml は PHP5.4 まで利用可能な有料レンタル Web サーバーである.
本研究では,食卓コネクトサイトの Web サーバー上での動作チェックに用いる.
4.2
HTML5[1]
Web ページの記述などに用いるマークアップ言語,HTML の第 5 版.WHATWG の提唱した
仕様を元に Web 関連技術を標準化している W3C で仕様の検討・標準化が進められている.
新たに追加された仕様には,音声を埋め込む audio タグ,動画を埋め込む video タグ,任
意の グラフィックスを描画できる canvas タグなどがあり,こうした要素の再生やアニメーショ
ンなど の制御や,データの保存,ソケット通信,ドラッグ&ドロップなどをスクリプト言語から
利用す るための標準仕様(API)も定義される.
4.3
CSS3[2]
Cascading Style Sheets の略称であり,草案自体は 1994 年に公開されており 1996 年に
CSS1 の規 格が確定され,2 年後には CSS2,そして 2000 年に CSS3 の草案が出され現在に
至っている.
Web ページのレイアウトを定義する規格.これまで,Web ページのレイアウトは HTML を
用 いて記述され,HTML にはレイアウトに関する仕様が大量に取り込まれたが,これは,文書
の論 理構造を記述するという本来の HTML の目的に反するため,文書の視覚的構造を規定する
枠組 みとして CSS が新たに策定された.
CSS を使うと,フォントや文字の大きさ,文字飾り,行間などの見栄えに関する情報を文書
本 体(および文書の論理構造を記述した HTML)から切り離すことができ,ユーザが複数のレ
イア ウトから適当なものを選択することができるようになる.WWW に関する標準化団体
W3C で標準化されている.
4.4 PHP[3]
動的に Web ページを生成する Web サーバの拡張機能の一つ.また、そこで使われるスクリプ
ト言語.レイアウトの「雛形」となる HTML ファイル内に、処理内容を記述したスクリプトを埋
め込み、処理結果に応じて動的に文書を生成し、送出することができる.
9
正式名称の「PHP: Hypertext Preprocessor」にもあるように、動的に生成されるページの作成
に向いている.また、XML のサポートや各種データベースとの連携に優れている点などから近年
普及しつつある.
プログラムの表記法は C 言語、Java、Perl の各言語から転用したものがベースとなっているが、
PHP 独自のものもある.言語仕様やプログラムはオープンソースソフトウェアとして無償で入手
することができる.
4.5 Javascript[4]
Sun Microsystems 社と Netscape Communications 社が開発した,Web ブラウザなどでの
利用に適 したスクリプト言語(簡易プログラミング言語).Sun 社の Java 言語に似た記法を用
いることが 名称の由来だが,直接の互換性はない.
従来は印刷物のような静的な表現しかできなかった Web ページに,動きや対話性を付加する
ことを目的に開発され,主要な Web ブラウザのほとんどに搭載されている. ブラウザ以外のソ
フトウェアにも簡易な制御プログラムの記述用言語として移植されており, Microsoft 社の
Windows や Web サーバソフト「IIS」,Macromedia 社の「Flash」などに,JavaScript ある
いは類似の言語の処理系が内蔵されている.
4.5.1 jQuery
jQuery とは,Web アプリケーションにおいて使用される,オープンソースの JavaScript のラ
イブラリの 1 つである.jQuery は,シンプルなコードで効率よくスクリプトを書けるように考え
て作られたものであり,その基本となるのが DOM オブジェクト関係の機能である.JavaScript
では DOM API を使用してオブジェクトを操作することができるが,DOM 操作することに手間
が掛かる.しかし,jQuery を使うと「$」の後にオブジェクトを指定するだけで DOM オブジェ
クトを操作できるようになり,Web 文章の表現を記述できる CSS を簡単に操作することもでき
る.よって今回 jQuery を使用したプログラミングを一部含んでいる.
4.6 WebRTC[5]
WebRTC とは,Web Real Time Communication の略で,World Wide Web(WWW)で使用され
る各種技術の標準化を推進するために設立された標準化団体,非営利団体である World Wide
Consortium(W3C)が提唱するリアルタイムコミュニケーション用の API の定義である.利用者側
はプラグインのインストールを必要とすることなく Web ブラウザ間のボイスチャット,ビデオチ
ャット,ファイル共有が可能になる.
4.6.1 従来の通信方法と WebRTC
Web における通信の基本として Hyper Text Transfer Protocol(HTTP)がある.これ
は,Web ブラウザと Web サーバーの間で HTML などのコンテンツの送受信に用いられ
る通信プロトコルで,Hyper Text Markup language(HTML)や Extensible Markup
10
Language(XML)によって記述されたハイパーテキストの転送を主な目的としているが,
それ以外にも,バイナリ形式の画像,音声を含め様ざまなデータを扱うことが可能であ
る.また HTTP や WebSocket を用いた通信では「クライアントとサーバー間の接続」
「ク
ライアントのリクエストとサーバーのリスポンスの繰り返し」により通信を実現してお
り,クライアント同士の通信の終始,仲介役であるサーバーの存在が必要不可欠である.
(図 14)
またボイスチャット,ビデオチャット等のデータ通信を使ったコミュニケーションの
際には Flash やブラウザプラグイン等のインストールを行わないと実現できない.
これに対し WebRTC は,
「クライアント端末(ブラウザ)間の相互接続」であることが
一番の違いであり,クライアント同士の接続には Peer(ピア)と呼ばれるオブジェクト
を保持し,Peer と Peer 間でデータ通信を行う(P2P)
.このデータ通信の際にはサーバ
ーを介す必要はなくブラウザ間での直接通信が可能ということが重要であり,これによ
り高速でリアルタイムなコミュニケーションが実現可能になった.
(図 15)
11
4.6.2 WebRTC 対応ブラウザ[6]
現在 2015 年 2 月 6 日までに WebRTC に対応しているブラウザは以下である.
・パソコン
Google Chrome 23
Mozila FireFox 22
Opera 12
・アンドロイド
Google Chrome 28 (29 から標準で有効)
Mozila FireFox 24
Opera Moble 12
・Google Chrome OS
4.6.3 NAT 問題
WebRTC の通信は,同じ会社内通信,一般家庭と一般家庭との通信では大した問題な
く正常に通信できる.しかし,会社と家,自社と別会社なと実際に通信しようとすると
NAT といった壁が立ちはだかり通信できないという問題[6]がある.これを NAT 問題と
いう.
4.6.3.1
NAT とは[6]
2 つの TCP/IP ネットワークの境界にあるルータやゲートウェイが,双方の IP アドレ
スを自動的に変換してデータを転送する技術である.またブロードバンドルーターや
WiFi ルータでは,1 つのグローバル IP アドレスを,複数の PC やデバイスで共有する
ことができる.
4.6.3.2
NAT 超え[7]
WebRTC で NAT 超えを実現するためにはまず以下 3 つを知っておく必要がある.
・ブラウザが知っている情報
→ローカルネットワークの IP アドレス:A
→自分が使う(動的に割り振った)UDP ポート:A/UDP・ブラウザが知らない情報
・ブラウザが知らない情報
→グローバル IP アドレス:A’
→NAT によってマッピングされた,外部に向けた UDP ポート:A’/UDP
・STUN の仕組み
→ブラウザが,インターネット側から見た情報を知るための仕組み.
12
Peer-to-Peer 通信を行うには,シグナリング処理で 2 つのブラウザがお互いに(ロー
カルネットワーク内の情報ではなく)インターネット側から見た情報を通知する必要が
ある.(図 16).
(図 16)
STUN により自分のブラウザが知らない情報である,[IP アドレス A’,A’/UDP]知り,
自分を外側から見た情報が分かったら,それをシグナリングサーバー経由で通信相手に
渡す.
(図 17)
(図 17)
13
お互いの情報が伝わったら相手のブラウザ目掛けて通信を行う.間に NAT が挟まるが,
ポートを直接マッピングしているので NAT を超え Peer-to-Peer の通信を開始できる.
(図 18)
(図 18)
今回 WebRTC を活用したサイト制作にあたって,これらの NAT 超え問題を解決する
ために WebRTC 用のフレームワークライブラリである「PeerJS」を使用する.PeerJS
はシグナリングに関する手順及びシグナリングサーバーの提供,NAT 超えを簡単に実装
出来るライブラリである.
4.6.4 SkyWay について
HTML5 を活用したアプリケーションやクラウドサービスやネットワークサービスの
開発の取り組み第一弾であり,サーバを準備することなく,簡単な JavaScript を書くだ
けで,WebRTC を使ったアプリが開発できる シンプルかつ柔軟性を持ったプラットフ
ォーム[8]のこと.[A Simple and Flexible WebRTC Platform]
4.6.5
使用するライブラリについて
以下は現在主流である WebRTC 用のライブラリ 3 つである.
・PeerJS
・simpleWebRTC
・WebRTC.io
この中で PeerJS を開発に使用するライブラリとする.理由としては,変化が速い WebRTC を
しっかりとキャッチアップし,無料でシグナリングサーバーを提供しているため.加えて NTT コ
ミュニケーションズは PeerJS を対象としたドキュメントを公開しており WebRTC に関する情報
が一番普及しているためである.
14
4.6.5.1 PeerJS
PeerJS は WebRTC を利用したピア・ツー・ピアのデータ,ビデオ・オーディオ通信を簡単に
実現することができ,WebRTC を活用したアプリケーションを開発できるようにするための
JavaScript ライブラリである[9].ここでは NTT コミュニケーションズが提供しているクラウド
サービス「SkyWay」にて利用できるようにカスタマイズされたライブラリを対象として説明す
る.また,カスタマイズされた PeerJS についてのドキュメントが以下の URL にて公開されてい
る.
(http://nttcom.github.io/skyway/docs/)
Peer.js セットアップ
[1]APIKEY を取得する
(図 19)
SkyWay の HP(http://nttcom.github.io/skyway/)にアクセスし「今すぐ API キ
ーを申し込む(無料)
」をクリックして申し込み手続きを行う.
•名前
•連絡先 E-mail アドレス
•利用するドメイン(最低1個)
必要な項目は 3 つ.これらを入力して申し込みを行
う,申し込み後、早くて数時間後,遅くても翌日には
正式な APIKEY がメールにて配布される.それまでに
実装したい,開発したい時はテスト用 APIKEY が配布
されるのでそちらを使用する.
テスト用 APIKEY
“6165842a-5c0d-11e3-b514-75d3313b9d05”
(図 20)
15
[1] プログラミング
ここでのプログラミングでは,
1 対 1 通信における必要最低限のコード説明を記述する.
STEP 1 - ライブラリの読み込み(.html に記述)
<script src=“https://skyway.io/dist/v2/0.3/peer.js"></script>
STEP 2 - Peer オブジェクト作成
var APIKEY = ‘6165842a-5c0d-11e3-b514-75d3313b9d05’;
//テスト用 APIKEY
var peer = new Peer({key: APIKEY});
//ページに接続したブラウザごとにピアを作成
STEP 3 - 相手からの着信イベントを処理するハンドラを設置
peer.on(‘open’,function(){
//自分の PeerID が生成されたら処理
});
peer.on(‘call’,function(call){
//相手から着信があったときに応答する
call.answer(window.localStream);
});
peer.on(‘error’,function(err){
//何かのエラーが生じた場合に処理
console.log(err.message);
});
STEP 4 - 自信のオーディオ・ビデオストリームを取得
naviator.getUserMedia ({audio:true,video:true,function(stream){
//自分のビデオタグに表示
$(“#—”).prop(‘src’,URL.createObjectURL(stream));
//相手に送るために保存
window.localStream = stream;
},function(err){
//エラー処理は必須
console.log(err.message);
});
STEP 5 - 相手の発信&発信後のイベントを処理するハンドラを設置
var call = peer.call(‘PeerID’,window.localStream);
call.on(‘stream’,function(stream){
16
//相手のオーディオ、ビデオストリーム Video タグに表示
$(“#—”).pror(‘src’,URL.createObjectURL(stream));
});
call.on(‘close’,function(){
//相手が切断したときの処理を行う
});
4.6.5.2 RecordRTC
RecordRTC とは,Muaz Khan という WebRTC デベロッパーにより開発された,現在の Web
ブラウザを対象とした Javascript ベースのメディアレコーディングライブラリである.
実装に必要なライブラリ
・RecordRTC.js
4.6.5.3
RecordRTCtoPHP
XHR(XMLHttpRequest)2/FormData を用いて PHP との連携をとり,サーバーとのマルチメ
ディアデータの通信を行い録画された動画をサーバーにアップロードすることがである.
実装に必要なライブラリ
・RecordRTC.js
・save.php (指定した動画をサーバーにアップロードするための PHP ライブラリ)
・delete.php (指定した動画をサーバーから削除するための PHP ライブラリ)
4.7 部屋番号生成について
食卓コネクトサイトページを読み込み表示された時,画面右下の「食卓につく」には図 21 の
ようにランダムに生成された 4 ケタの数字が自動的に入力される.以下に部屋番号のランダム生
成についてコードを交えつつ主な部分の説明を記述する.
(図 21)
17
HTML
<div style="position:relative; bottom:5px;">
<p>お好きな4ケタの番号を入力して「入室」ボタンを押してください</p>
<label id="num">※ 例:0 1 2 3</label>
<input type="text" maxlength="4" id="enter_room_number" maxlength="" placeholder="">
<a href="#" id="start_connect"><input type="button" value="入室" id="enter_button"></a>
</div>
Javascript
$(function(){
genChannel(); //チャンネル名を決める処理
});
//チャンネルを決める処理
function genChannel() {
var channel = Math.floor(Math.random() * (9999 - 1 + 1 ) + 1) + ''; //' 'は文字列化
updateURLs(channel);
//チャンネル名・ULR のアプデ
updateTextbox(channel);
//チャンネル追加
}
//テキストエリアの数値を変更
function updateTextbox(channel) {
//channel が文字列でないときの処理
if (typeof channel !== 'string') {
channel = $('#enter_room_number').val();
}
channel = zeroPadding(channel, 4);
//0000 に channel を 追 加
channel ] //数値文字列
$('#enter_room_number').val(channel);
//#チャンネルを変更
}
//リンク先をの URL を更新する
function updateURLs(channel) {
//channel が文字列でないときの処理
if (typeof channel !== 'string') {
channel = $('#enter_room_number').val();
}
channel = zeroPadding(channel, 4);
18
[ 0000 +
var connectURLaddress = URL + '#' + channel; // ['http://'] + [---] + ['#'] + [channel]
console.log(connectURLaddress);
//var connectURL = document.getElementById('start_connect');
$('#start_connect').attr('href', connectURLaddress);
//href 属性を startCamUrl に
}
$(function(){ }); 内の記述はページが読み込まれた際に処理内容となっており,genChannel();
が 初 め に 処 理 さ れ る 関 数 で あ る . function genChannel(){}; 内 で は HTML コ ー ド の
id="start_connect"で指定された<a>タグのリンク先,id="enter_room_number" で指定された
<input>タグに挿入するための 4 ケタの数字をランダムに生成する役割を持つ.生成された数字
は文字列として変数 channel に保管され, updateTextbox(channel), updateURLs(channel)
のように引数として扱われる.channnel を引数として処理される 2 つの関数の役割は,それぞれ
function updateTextbox(){}; 内 で <input> タ グ 内 に 挿 入 す る た め の 処 理 , function
updateURLs(){};内で<a>タグのリンク先を変更するための処理である.
4.8 通信方法について
4.6.5.1 で記述したように,Javascript をコーディングするだけで WebRTC をつかった通信を
実現することが出来る SkyWay の PeerJS ライブラリを使用している.通信を可能にする最低限
のコードは 4.6.5.1 にすでに記述・説明している.しかしそのコードだけでは,自動的に通信が
実現することはかなわず,手動で通信相手の PeerID を入力し接続する必要がある.よってここ
では,手動で PeerID を入力する手順を省き,PeerID を自動取得・発信する方法を記述する.
4.8.1 PeerID の自動取得
SkyWay で用意されている API の内 RestAPI にて PeerID の一覧取得機能がある.RestAPI
を用いると SkyWay で申し込み取得した APIKEY を参照して,現在同ページに接続し生成され
ている PeerID の一覧を配列で取得することが出来る.以下の getUserList()関数は RestAPI を使
用し PeerID を自動取得,発信する処理関数に移るまでを記述している.
Javascript
//PeerServer への接続確立時に発生
peer.on('open', function(){
myVideo();
})
//----自分のカメラ表示
function myVideo(){
/*--自分の PC に接続している
Web カメラを画面に表示させるための処理をここに記述--*/
getUserList();
19
};
//----RestAPI にてユーザー取得
function getUserList() {
//ユーザリストを取得
$.get('https://skyway.io/active/list/' + APIKEY, function (list) {
//配列 list に接続中の PeerID を格納
for (var i = 0, length = list.length; i < length; i++) {
var id == list[i]
if(id == peer.id){
console.log("ConnectingUserNumber " + i + " はあなたです" );
}else{
connectVideo(id); //id にむけて発信
}
}
});
}
PeerID の取得事態は関数 getUserList();内で可能であるが,その関数に至るまでを自動化す
ることで PeerID の自動取得を実現することにする.
ページ読み込み時に peer.on は peer イベントのリスナを設定するメソッドであり,’open’イベ
ントによりその関数内は PeerServer への接続確立時に発生するようになっている.myVideo();
では自身の Web カメラを画面に表示する処理になっており,その後 PeerID を自動取得するため
の getUserList(); に移行する.
$.get('https://skyway.io/active/list/' + APIKEY, function (list) {}); により配列 list に同ページ
接続中の PeerID 一覧が格納され取得することが出来る.また,APIKEY は SkyWay に申し込み
取得した APIKEY である.
PeerID 一覧には自分の PeerID も含まれているため自分自身の PeerID
に発信をすることを防ぐために工夫をする必要がある.
変数 id に取得した PeerID をひとつ格納し,自身の PeerID と異なった時,その PeerID に向
けて発信処理を行う関数に移る.この処理を for 文で,取得した PeerID の数だけループ処理を行
い,ひとつひとつ順番に発信していく.
4.8.2 PeerID へ発信
先程取得した PeerID 一覧を変数 partnerId に格納し発信する関数に入る.
//----Peer 発信処理
function connectVideo(partnerId){
var call = peer.call(partnerId, window.localStream);
console.log("接続先 ID:"+ call.peer);
partnerVideo(call);
//相手の Video を表示処理
}
20
peer.call は id で 指 定 さ れ た リ モ ー ト の peerID へ 発 信 し , 発 信 先 相 手 の media
connection(window.localStream)を返す.返された media connection は変数 call に格納され発信
先相手のカメラ映像を表示する処理 partnerVideo(call); で利用される.
4.9 番号による部屋割りについて
入室後の URL を確認すると,入室前に入力した 4 ケタの数字が#のあとに記述されるのがわ
かる.(例:http://------/ video.html#5214) この数字と,Javascript のコーディングにより部屋
割を実現している.実現しているコードは以下,4.8.1 の PeerID 一覧取得後にコード
部分を
追加している.
Javascript
var roomNumber = location.hash.replace(/^#/, ''); //(ア)
var userNumber = Math.floor(Math.random() * (9999 - 1 + 1 ) + 1);
//(イ)
var CONNECTER_ID = 'room' + roomNumber + '-' + userNumber; //(ウ)
var peer = new Peer(
CONNECTER_ID, {
key: APIKEY,
debug: 3
});
//----RestAPI にてユーザー取得
function getUserList() {
//ユーザリストを取得
$.get('https://skyway.io/active/list/' + APIKEY, function (list) {
for (var i = 0, length = list.length; i < length; i++) {
var id = list[i];
if(id == peer.id){
console.log("ConnectingUserNumber " + i + " はあなたです" );
} else{
var idCheck = id.substring(4,8); //(エ)
if(idCheck == roomNumber){
connectVideo(id);
//id にむけて発信
}else{
console.log('This roomNumber is #' + roomNumber);
}
}
}
});
}
関数 getUserList()前に記述しているコードは自身の PeerID を任意で決定している部分である.
21
URL 末尾である#後の 4 ケタの数字 roomNumber (ア)と,ランダム生成された数字 userNumber
(イ)から PeerID(ウ)を決定しており例を以下に示す.
・(例:http://------/ video.html#5214)の場合
(ア) roomNumber = 5214
(イ) userNumber = 0000(←ランダム)
(ウ) CONNECTER_ID = room5214-0000
部分のコードは,取得した PeerID と自身の PeerID とを比較し誤って自身に発信すること
を防ぐ工夫をした後のコードとなっており,入室する前の 4 ケタの数字により同一の数字を入力
した者同士のみ接続可能とするコード内容である.自身の PeerID には部屋番号である 4 ケタの
数 字 が 含 ま れ て い る た め ,( エ ) の 部 分 で id.substring(4,8); に よ り そ の 部 屋 番 号 部 分
(room5214-0000)を idCheck(5214)に格納し,格納した後,idCheck と roomNumber を比較し一
致しているならばその PeerID に向けて通信を発信する関数に移る.一致しなければコンソール
を表示して終了である.
4.10 録画方法について
録画には,Muaz Khan という WebRTC デベロッパーにより開発された,現在の Web ブラウ
ザを対象とした Javascript ベースのメディアレコーディングライブラリである,RecordRTC ラ
イブラリを使用し実現を図る.また録画した映像は XHR(XMLHttpRequest)2/FormData を用い
て PHP と連携をし,サーバーとのマルチメディアデータの通信を行い,サーバーにアップロード
することができる.
【用意したもの】

RecordRTC.js

save.php (指定した動画をサーバーにアップロードするための PHP ライブラリ)

delete.php (指定した動画をサーバーから削除するための PHP ライブラリ)
HTML
<div id="container">
<videoid="video_display" autoplay ></video>
</div>
<div id="my_video">
<video id="[peer.id(自身の PeerID)]" muted="muted" autoplay="autoplay"></video>
</div>
<div id="my_button_area">
<button id="start_recording">映像を録画</button>
<button id="stop_recording" style="display:none;">録画を停止</button>
<button id="ok" style="display:none;">映像を登録</button>
<button id="delete_files" style="display:none;">映像を削除</button>
22
</div>
RecordRTC を使用して録画を実現するにあたって用意した処理及び関数は以下である.

startRecording.onclick = function(){}:「映像を録画」ボタンを押したときの処理

stopRecording.onclick = function(){}:
「録画を停止」ボタンを押したときの処理

deleteFiles.onclick = function(){}:
「映像を削除」ボタンを押したときの処理

function startVideoRecord(){}:録画を開始するための関数

function deleteVideoFiles(){}:アップロードされた動画の削除にかかわる関数

function PostBlob() {}:画像や音声、その他のマルチメディアオブジェクトを処理する関数

function xhr() {}:動画のアップロードやデリートに関して PHP と連携してサーバーとの通
信を行う関数
1 □
2 □
3 を手順に従う事で録画の開始から削除を行う事が出来る.
以下の□
1 「映像を録画」ボタンをクリックして録画を開始する
□
2 「録画を停止」ボタンをクリックして録画を停止し、録画された動画をサーバーにアップロードする
□
3 「映像を削除」ボタンをクリックして、サーバーにアップロードされた動画を削除する
□
1 「映像を録画」ボタンをクリックして録画を開始する
□
(図 22)
Javascript
var recorder;
var startRecording = document.getElementById('start_recording'),
var videoConstraints = {
audio: true, //音声
video: true
//ビデオ
};
//-----------------録画開始ボタンをクリックしたとき
startRecording.onclick = function(){
startVideoRecord(true); //録画する関数へ
};
//-----------------録画開始開始
function startVideoRecord(isRecordVideo){
//---★1
navigator.getUserMedia(videoConstraints, function(stream) { /
23
//録画開始
videoDisplay.onloadedmetadata = function(){ //メタデータの読み込みが完了した場合と、
それ以降に読み込み状況が変化した場合に発生するイベントに対するイベントハンドラ
videoDisplay.width = 320; //video 表示の幅
videoDisplay.height =240; //video 表示の高さ
var options = {
//映像を出力・録画する幅と高さを設定
type: isRecordVideo ? 'video' : 'gif',
video:videoDisplay,
canvas: {
width: 320,
height: 240
}
};
recorder = window.RecordRTC(stream,options);
recorder.startRecording();
};
videoDisplay.src = URL.createObjectURL(stream);
//video タグの src を設定
});
},function(){
//エラー時
});
};
録画開始にかかわる処理・関数は
startRecording.onclick = function(){}
function startVideoRecord(){}
である.
「映像を録画」ボタンを押したときのリスナーを用意しておき,video タグに映像を出力,
録画するための関数 startVideoRecord()を呼び出す.★1 では,変数 videoConstraints であらか
じめ設定していた内容を元に navigator.getUserMedia によりカメラを呼び出し,その処理の中で
映像を出力・録画するための幅と高さを設定してある.★2では変数 recorder に出力されている
映像の録画先を設定し,recorder.startRecordingd()で録画を開始する.
2 「録画を停止」ボタンをクリックして録画を停止し、録画された動画をサーバーにアップロードする
□
(図 23)
24
Javascript
var stopRecording = document.getElementById('stop_recording'); //録画停止
var container = document.getElementById('container');
var videoDisplay = document.getElementById('video_display'); //録画映像再生用 video タグ
var upLoadRoomNumber = location.hash.replace(/^#/, '');
var fileName;
//部屋番号
//ファイル名
//-----------------録画停止ボタンをクリックしたとき
stopRecording.onclick = function(){
videoDisplay.src = '';
//一度 video タグのソースを空白にする
fileName = upLoadRoomNumber + (Math.round(Math.random() * 999999999 ) +
999999999);
//録画停止
recorder.stopRecording(function(url) {
PostBlob(recorder.getBlob(), 'video', fileName + '.webm');
});
};
//-----------------PostBlob
function PostBlob(blob, fileType, fileName) {
// FormData
var formData = new FormData(); //FormData オブジェクトを作成する
formData.append(fileType + '-filename', fileName);
formData.append(fileType + '-blob', blob);
// progress-bar を追加
var strong = document.createElement('strong');
var progress = document.createElement('progress');
strong.id = 'percentage';
strong.innerHTML = fileType + ' upload progress: ';
container.appendChild(strong);
container.appendChild(progress);
// POST the Blob using XHR2
xhr('php/save.php', formData, progress, percentage, function(fileURL) {
var mediaElement = document.createElement(fileType);
var source = document.createElement('source');
//source タグ生成
var href = location.href.substr(0, location.href.lastIndexOf('/') + 0);
console.log(fileURL);
source.src = href + fileURL;
//録画された動画の URL
25
//href:[ http://localhost/labo/test_webrtc ]
//fileURL:[ /uploads/-----.webm ]
if (fileType == 'video') source.type = 'video/webm; codecs="vp8, vorbis"';
mediaElement.appendChild(source);
//source タグを追加
mediaElement.controls = true; //video タグをアクティブにする
container.appendChild(mediaElement);
//box に video タグを追加
mediaElement.play(); //再生
progress.parentNode.removeChild(progress);
//プログレスバーを消去
strong.parentNode.removeChild(strong); //[Saved]という文字を消去
});
}
//-----------------サーバーとの通信処理
function xhr(phpUrl, data, progress, percentage, callback) {
var request = new XMLHttpRequest(); //XMLHttpRequest オブジェクトを作成
console.log(request.readyState);
request.onreadystatechange = function() {
//XHR 通信の状態が変化するたびに実行
//データの送信が成功、完了した際の処理
if(request.readyState == 4 && request.status == 200) {
callback(request.responseText); // /uploads/-------.webm
}
};
if (phpUrl.indexOf('php/delete.php') == -1) {
request.upload.onloadstart = function() {
//アップロードを開始時に実行
percentage.innerHTML = 'Upload started...'; //パーセンテージを表示
};
request.upload.onprogress = function(event) {
//アップロードの進行中の処理
progress.max = event.total;
progress.value = event.loaded;
percentage.innerHTML = 'Upload Progress ' + Math.round(event.loaded / event.total *
100) + "%";
};
request.upload.onload = function() {
//アップロード完了時に実行
percentage.innerHTML = 'Saved!';
};
}
request.open('POST', phpUrl);
//アクセス先 URL を設定 (php/save.php)
request.send(data); //「送信したいデータ(data)]を指定し,XHR 通信を開始
26
}
録画停止、動画のアップロードにかかわる処理・関数は
stopRecording.onclick = function(){}
function PostBlob(){}
function xhr(){}
である.
「映像を停止」ボタンを押したときのリスナーを用意しておき,あらかじめ動画のファイ
ル名を決定しておくためのグローバル変数 fileName を宣言しておく.[映像停止]ボタンをクリッ
クした際,内部では動画のファイル名を upLoadRoomNumber + Math.round(Math.random() *
999999999 ) + 999999999 により部屋番号を混入させ決定し録画を停止.
PostBlob()では第一引数から第三引数まで順番に「記録されたマルチメディアオブジェクト,
ファイルタイプ,ファイル名」として PostBlob を呼び出しています.引数(「記録されたマルチ
メディアオブジェクト,ファイルタイプ,ファイル名」
)をもとに PHP でデータを送信するため
の(※1)FormData オブジェクトを作成し,作成した FormData を引数として xhr()を呼び出す.
xhr()では第一引数に文字列'php/save.php',第二引数に先ほど作成した FormData オブジェクト,
第三・四引数にプログレスバーに関するオブジェクト,第五引数にアップロードされた動画をペ
ージ内に表示する処理がある.
第五引数の処理は,サーバーにアップロードされた動画を再生するまでの処理である.サー
バーにアップロードされた動画を再生するための video タグ,source タグを作成し,アップロー
ドされたファイルの URL を作成し source タグの src に代入する.再生するために必要なタグを
用意し追加,最後に動画を再生する.
xhr()は,主に PostBlob()で作成した FormData オブジェクトを元に「save.php」
「delete.php」
にサーバーに動画をアップロード,サーバーの動画を削除の命令を送る関数である.
PHP にむけて「Javascript 側のデータ送信処理」を行うために new XMLHttpRequest() で
XMLHttpRequest(※2)オブジェクト 変数 request を宣言する.XHR(XMLHttpRequest)オブジ
ェクトのプロパティ[.onreadystatechange]を用いた処理内では XHR 通信の状態が変化するたび
に実行され,データの送信が完了,成功した際の処理を記述してあ る.その処理とは,
[request.responseText「引数は(/uploads/-------.webm)」]として上記関数 Postblob()内で呼び出し
た xhr()の第五引数である.引数である[request.responseText]に動画の保存先パスが示されてい
るので callback()が呼び出されてようやくページに新たな video タグと source タグが追加され
動画が再生される.
次に phpUrl から文字列(php/delete.php)が見つからなかった場合の処理,
つまり実行する PHP
ファイルが save.php である場合の処理である.XHR オブジェクトのプロパティに[.upload]とい
う XHR オブジェクトを取得するクラスがあり,送信の進捗状況を調べる手段としてプロパティ
[.onloadstart][.onprogress][.onload] が あ る . [.onloadstart] は 送 信 を 開 始 し た と き に 実 行 ,
[.onprogress]は送信中に繰り返し実行[.onload]は送信が完了したときに実行され(成功失敗に関
27
係無く)
,これらのそれぞれに進捗の処理を割り振っている.
最後に XHR オブジェクトのプロパティ[.open]によりアクセス先 のパスを phpUrl 設定し,
[.send] で FormData オ ブ ジ ェ ク ト を 格 納 し て い る 変 数 data を 引 数 と し て
PHP('save.php'or'delete.php')にデータを送信している.Post メソッドは大容量のデータを送る
事ができるので,フォームの送信や,ファイルのアップロードに最適で,動画は大容量であるた
め Post を指定している.
※1:FormData クラス使用すると,FORM 要素内の,すべてのコントロールの名前と値を取得し,1つのオ
ブジェクトにまとめる事ができる.また FormData オブジェクトを送信する為には※2XMLHttpRequest クラス
を使用する.
※2:XMLHttpRequest は,非同期なデータの通信を実現するための API で「Javascript 側のデータ送受信
処理」
「CGI 側のデータ送受信処理」を行うことが出来る.元々は,
「Internet Explorer 5」で「ActiveX オブジ
ェ クト」 として 実装さ れてい たが, 他ブラ ウザで も互換 性のあ る同等 の機能 が実装 されは じめたため,
XMLHttpRequest として利用できるようになった.
3 「映像を削除」ボタンをクリックして、サーバーにアップロードされた動画を削除する
□
(図 24)
Javascript
// JavaScript Document
//WebrtcCode for RecordRTC
var upLoadRoomNumber = location.hash.replace(/^#/, '');
var deleteFiles = document.getElementById('delete_files'),
//録画停止
//-----------------消去停止ボタンをクリックしたとき
deleteFiles.onclick = function() {
deleteVideoFiles();
};
//-----------------アップロードされた動画の削除処理
function deleteVideoFiles() {
if (!fileName) return;
//ファイル名がない時は処理終了
var formData = new FormData();
//FormData オブジェクトを作成する
formData.append('delete-file', fileName); //delete-file というパラメータに fileName を追加
xhr('php/delete.php', formData, null, null, function(response) {
28
console.log(response);//problem deleting files.
});
fileName = null;
container.innerHTML = '';
}
サーバーにアップロードされた動画の削除にかかわる処理・関数は

deleteFiles.onclick = function(){}

function deleteVideoFiles(){}
で あ る .「 映 像 を 削 除 」 ボ タ ン を 押 し た と き の リ ス ナ ー を 用 意 し て お き , リ ス ナ ー か ら
deleteVideoFiles()を呼び出す.PostBlob()と似ている処理で,PHP により送信するための
FormData オブジェクトを作成し,作成した FormData を引数として xhr()を呼び出す.録画さ
れた動画ファイル名を delete-file というパラメータに fileName を追加しファイル名を指定して
いるため,xhr()が呼び出された後には'delete.php'が呼び出され録画されアップロードされた動画
ファイルを削除することが出来る.最後にファイル名に null を代入し,アップロードされた動画
を表示していた領域を見えなくする.
・
「映像を登録」ボタンに関して
(図 25)
録画した映像がサーバーにアップロードされるタイミングは「映像を停止」ボタンを押した後
に自動的にサーバーにアップロードされるため,
「映像を登録」ボタンはアップロードされた動画
が再生されている領域を非表示にするためのボタンである.
4.11 録画した映像の閲覧ページについて
録画されサーバーにアップロードされた映像は一括して「uploads」ディレクトリに保存されて
いる.PHP によりサーバー上のディレクトリ内のファイル一覧を取得することができ, 取得し
たファイル名と閲覧ページの URL 末尾にある#後の 4 ケタの数字を利用し表示する映像ファイル
を選択している.
recordedvideo.php
<?php
// ディレクトリハンドルの取得
$dir_h = opendir( "./uploads/" ) ;
$i = 0;
29
$fileNameList = array();
// ファイル・ディレクトリの一覧を $file_list に
while (false !== ($file_list[] = readdir($dir_h))) ;
// ディレクトリハンドルを閉じる
closedir( $dir_h ) ;
//ディレクトリ内のファイル名を1つずつを取得
foreach ( $file_list as $file_name ){
//ファイルのみを表示
if(is_file( "./uploads/" . $file_name)){
$p = pathinfo("./uploads/" . $file_name);
if($p["extension"] == "webm" ){
$fileNameList[$i] = $file_name;
$jsonFile_name=json_encode($fileNameList); //■1
$i++;
}
}
}
?>
部屋番号ごとに録画した映像を閲覧するためのコードは Javascript でプログラムしあり,PHP
で取得したファイル名を Javascript に渡す必要がある.PHP ではファイル名を$file_list に一括
して格納してあり,foreach ( $file_list as $file_name )によりファイル名をひとつずつ順番に
$file_name に格納する.格納したファイル名$file_name を$fileNameList[$i]に挿入した後,■1
のように記述し json を利用することで$fileNameList のデータを PHP から Javascript に渡すこ
とができる.
以下 Javascript コードは PHP から取得したファイル名を扱い部屋番号ごとに録画した映像を
表示するためのプログラムである.
Javascript
var roomNumber = location.hash.replace(/^#/, '');
var fileNameList = JSON.parse('<?php echo $jsonFile_name; ?>');
var videoArea = document.getElementById('video_area');
for(var i=0; i<fileNameList.length;i++){
var fileIDCheck = fileNameList[i].substring(0,4);
if(roomNumber == fileIDCheck){
var box = document.createElement('div');
box.className ='video_manager';
box.id = 'boxname_is_' + fileNameList[i];
box.width = window.parent.screen.width;
30
var videoEle = document.createElement('video');
videoEle.src =
'http://buturi.heteml.jp/student/2014/otani/test_webrtc/uploads/'+fileNameList[i];
videoEle.muted = 'true';
videoEle.controls = 'true';
videoArea.appendChild(box);
box.appendChild(videoEle);
}else{
console.log(roomNumber + '!=' + fileIDCheck);
}
}
ファイル名の冒頭には録画した際の部屋番号が含まれており,その冒頭部分と閲覧ページの部
屋番号とを比較し,一致したなら保存した映像を表示するための領域である<div>タグ<video>タ
グを追加するという方法をとっている.
例:roomNumber = 1766 (#1766)
fileNameList = 1766---------.webm
fileIDCheck =1766;
roomNumber と fileIDCheck を比較 ⇒ 一致(roomNumber == fileIDCheck)
映像を図 26 のように表示↓
(図 26)
3 と同様である.
映像の削除については,4.10 の□
5. 終わりに
本研究を通し,比較的まだ新しい技術である WebRTC に触れ,その技術と理解は深めることが
できた.WebRTC という言葉を一切知らない状態から始め,まだまだ新しい技術ということでネ
ット上にも日本語解説はまだ少なく,研究の際に海外の開発者サイトや解説サイトを見ることが
しばしば多かった.それにより欲しい知識・技術を調べるためには様々なサイトを見ることに抵
抗がなくなったと感じる.また本研究中に一番のやりがい,喜びを感じた瞬間としてあげるのが
自分の研究成果が検索エンジン Google の検索上位に表示されているという事だ.
「RecordRTC」
31
と検索すると上位 4 位に,
「RecordRTC 実装」と検索すると上位 1 位 2 位に 2 つの項目が表示さ
れる.RecordRTC に関して日本語で扱っているページが数少ないということもあるが,大変うれ
しい瞬間だった.
最後に本研究は,福山大学の人々との多くの話し合いにより機能やデザインをブラッシュアッ
プしてきたこともあり,皆との議論を交わす重要さを身をもって感じた 1 年であった.今後はこ
の 1 年で得た経験を生かし,情報技術者として精進していく.
6. 謝辞
本研究を行うにあたり,多大なご指摘を賜りました徐 丙鉄教授,研究の貴重な助言を頂きまし
た福山大学認知心理学研究室の橋本 優花里教授,後藤貴行様,研究へのアドバイスをしていただ
いた情報物理研究室の皆様にこの場を 借りてお礼申し上げます.
7. 参考文献・引用
【参考文献】

WebRTC 開発者向けライブラリ「PeerJS」はこうして作られた
http://html5experts.jp/yusuke-naka/3693/

HTML5 で情報最適化/視覚化&WebRTC で変わる未来~QCon Tokyo 2013 レポート
http://www.atmarkit.co.jp/ait/articles/1305/30/news024_2.html

議論多い WebRTC:リアルタイム通信の課題と機会
http://www.infoq.com/jp/news/2013/06/wrangling-webrtc

SkyWay
http://nttcom.github.io/skyway/

WebRTC のオーディオ処理の謎、誰か教えて!
http://www.slideshare.net/mganeko/meetup2-lt-audio

SkyWay Technical Forum
https://groups.google.com/forum/#!forum/skywayjs

WebRTC-ウィキペディアhttp://ja.wikipedia.org/wiki/WebRTC

Hypertext Transfer Protocol - Wikipedia
http://ja.wikipedia.org/wiki/Hypertext_Transfer_Protocol

Web ブラウザで P2P を実現する、WebRTC の API と周辺技術
http://www.slideshare.net/yoshiakisugimoto9/webrtc-slide

PeerJS ドキュメント
http://nttcom.github.io/skyway/docs/

PeerJS
http://peerjs.com/

WebRTC をお手軽に.Peer.js 試してみた
32
http://blog.livedoor.jp/kotesaki/archives/1856455.html

壁を越えろ!WebRTC で NAT/Firewall を越えて通信しよう
http://html5experts.jp/mganeko/5554/

SkyWay Technical Forum ?[Q&A]NAT 越えについて
https://groups.google.com/forum/#!topic/skywayjs/yljXXLSEySg

RecordRTC
https://github.com/muaz-khan/WebRTC-Experiment/tree/master/RecordRTC

RecordRTCtoPHP
https://github.com/muaz-khan/WebRTC-Experiment/tree/master/RecordRTC/RecordRT
C-to-PHP
【引用】
[1]HTML5
⇒IT 用語辞典 e-Words “HTML5”
http://e-words.jp/w/HTML5.html
[2]CSS3
⇒IT 用語辞典 e-Words “CSS”
http://e-words.jp/w/CSS-1.html
[3]PHP
⇒IT 用語辞典 e-Words “PHP”
http://e-words.jp/w/PHP.html
[4]Javascript
⇒IT 用語辞典 e-Words “Javascript”
http://e-words.jp/w/JavaScript.html
[5]WebRTC 対応ブラウザ
⇒Wikipedia “WebRTC”
http://ja.wikipedia.org/wiki/WebRTC
[6]壁を越えろ!WebRTC で NAT/Firewall を越えて通信しよう
⇒http://html5experts.jp/mganeko/5554/
[7]NAT
⇒IT 用語辞典 e-Words “NAT”
http://e-words.jp/w/NAT.html
[8]SkyWay
⇒http://nttcom.github.io/skyway/
[9] PeerJS ドキュメント
⇒http://nttcom.github.io/skyway/docs/
8. ふろく
HTML
・食卓コネクトページ(connect1.html)
<!DOCTYPE html>
<html>
33
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>食卓コネクト</title>
<link rel="stylesheet" type="text/css" href="css/style1.css">
</head>
<body>
<section class="center">
<div id="key_visual">
<img class="radius_image" src="images/Connect_top.png" width="620" height="
食卓コネクト">
</div>
</section>
<section class="center">
<img class="radius_image" src="images/center_img.png" width="620">
<a name="CONNECT">
<div class="text_area border_left_connect_center">
<h2>食卓をつなげようとは</h2>
<p> 食卓コネクトのサービスでは、Web カメラを使いお互いの食卓の様子を
リアルタイムに映像で通話を行うことで、距離が離れた方との食事の時間をともに過ごすことが
出来ます。</p>
<h3>食卓をつなぐ方法</h3>
<h4>1.入室する</h4>
<p>
現在表示されている画面の右下にある「食卓につく」でお好きな4ケタ
の番号を入力して「入室」ボタンを押してください。一緒に食事を楽しみたい方に入力した4ケ
タの数字を教えてあげましょう。<br> 同じ数字で入室ボタンを押すことにより一緒に食事をお
楽しみいただけます。</p>
<img src="images/shokutaku.png" width="614" alt="説明1">
<h4>2.カメラで自分自身を映す</h4>
<p> Web カメラパソコンに接続しておいてください。入室後は下の画像のよ
うな画面が表示されます。</p>
<img src="images/explanation2_1.png" width="614" alt="説明 2_1">
<p>上の灰色の領域にある選択肢のうち[許可]のボタンを押してください。</p>
34
<img src="images/explanation2_2.png" width="614" alt="説明 2_2">
<p>右下にカメラを通した自分自身が表示されます。</p>
<img src="images/explanation2_3.png" width="614" alt="説明 2_3">
<p>食事相手もここまでの手順を完了すると自動的に映像通話が開始されます
</p>
</div>
</a>
<a name="HAISHOKU">
<div class="text_area border_left_haishoku_center">
<h2>配食サービスとは</h2>
<p>
食卓コネクトでは、自宅までお弁当を配達してくれる「配食サービス」
を行っております。現在のページの右上に用事されている「配食サービス」を押すことで配食メ
ニューページに移動します。</p>
<p>
配食サービスでは、スタッフがお弁当を配達するだけでなく、食卓コネ
クトで一緒に食事を楽しみたい方との通信の開通を支援します。Web カメラの設定、相手との映
像通話開通までの支援、ビデオレター撮影協力などスタッフが支援します。</p>
<a
href="http://delivery.gnavi.co.jp/search?a=34102000250000200&m=22&l=1"
onClick="haishokuAlert()"><img src="images/explanation_haishoku.png" width="600" alt="
配食"></a>
</div>
</a>
<a name="MESSAGE">
<div class="text_area border_left_message_center">
<h2>思いを伝えるには</h2>
<p>[現在開発中]</p>
</div>
</a>
<a name="VIDEO_LETTER">
<div class="text_area border_left_video_center">
<h2>ビデオレターを送るには</h2>
<p> 食卓コネクトのサービスでは、Web カメラを通して撮影した映像をビデオレ
ターとして保存することができます。保存したビデオレターは入室した際の4ケタの数字をキー
として保存しており、いつでもビデオレターを観賞、閲覧することができます。</p>
<h3>ビデオレターを撮影する</h3>
35
<p>
ビデオレターを撮影する場合、すでに「食卓をつなげよう」の手順1.2.
を完了しておく必要があります。</p>
<h4>1.撮影する</h4>
<p> 右下に Web カメラからの映像が映し出された状態で、カメラ映像の下にある
[映像を録画]ボタンをを押します。</p>
<img src="images/explanation_video_letter_1_1.png" width="614" alt="ビデオレ
ター説明 1_1">
<p>画面上部に灰色の領域が新しく表示されるので、選択肢のうち[許可]のボタン
を押してください。</p>
<img src="images/explanation2_2.png" width="614" alt=" ビ デ オ レ タ ー 説 明
1_2">
<p>録画中は、表示されているカメラ映像に赤枠が出現します。</p>
<img src="images/explanation_video_letter_1_2.png" width="614" alt="ビデオレ
ター説明 1_1">
<h4>2.撮影を停止する</h4>
<p>撮影中の映像を停止させる手順を説明します。表示されているカメラ映像に撮
影中の印である赤枠が出ている状態でカメラ映像下にある[録画を停止]ボタンを押します。</p>
<img src="images/explanation_video_letter_2_1.png" width="614" alt="ビデオレ
ター説明 2_1">
<h4>3.撮影した映像をビデオレターとして登録</h4>
<p>画面右上に、<br>①[録画を停止]ボタンを押すまでに撮影・録画された映像が
自動的に表示・再生されます。ここで撮影された動画を確認することができ、<br>②これでいい
と思いましたら[映像を登録]ボタンを押して映像をビデオレターとして登録することが出来ま
す。<br>③また今の映像を消去したいと思いましたら[映像を削除]ボタンを押すことで撮影した
映像を削除することが出来ます。</p>
<img src="images/explanation_video_letter_2_2.png" width="614" alt="ビデオレ
ター説明 2_2">
<h4>4.ビデオレターを見る</h4>
<p> [保存されたビデオレター]ボタンを押すことで、その部屋で撮影した過去すべ
てのビデオレターを確認できます。入室した際に入力した4ケタの数字を利用し、再度入室する
ことでなんどでもビデオレターの観賞が可能です。</p>
<img src="images/explanation_video_letter_2_3.png" width="614" alt="ビデオレ
ター説明 2_3">
36
<img src="images/explanation_video_letter_2_4.png" width="614" alt="ビデオレ
ター説明 2_4">
</div>
</a>
</section>
<div id="left_menu">
<div class="menu_inline">
<a href="#CONNECT">
<div class="menu1 border_left_connect" id="guide_connect">
<h2 class="text_center margin_top_23_p">食卓をつなげるとは</h2>
</div>
</a>
<a href="#HAISHOKU">
<div class="menu2 border_left_haishoku" id="guide_haishoku">
<h2 class="text_center margin_top_23_p">配食サービスとは</h2>
</div>
</a>
<a href="#MESSAGE">
<div class="menu2 border_left_message" id="guide_message">
<h2 class="text_center margin_top_23_p">思いを伝えるには</h2>
</div>
</a>
<a href="#VIDEO_LETTER">
<div class="menu4 border_left_video" id="guide_video">
<h2 class="text_center margin_top_23_p">ビデオレターを<br>送るには
</h2>
</div>
</a>
</div>
</div>
<div id="right_menu">
<div class="menu_inline">
<a
href="http://delivery.gnavi.co.jp/search?a=34102000250000200&m=22&l=1">
<div class="menu1 haishoku_back" onClick="haishokuAlert()">
<div class="menu_flame_1_2 haishoku">
37
<h2>配食サービス</h2>
</div>
</div>
</a>
<div class="menu2 eiyoushi_back" onClick="eiyoushiAlert()">
<div class="menu_flame_1_2 blue_flame eiyou">
<h2>栄養士さん</h2>
</div>
</div>
<div class="menu3 room_back">
<div class="menu_flame_3">
<h2>食卓につく</h2>
<div style="position:relative; bottom:5px;">
<p>お好きな4ケタの番号を入力して「入室」ボタンを押してくださ
い</p>
<label id="num">※ 例:0 1 2 3</label>
<input
type="text"
maxlength="4"
id="enter_room_number"
maxlength="" placeholder="">
<a href="#" id="start_connect"><input type="button" value="入室"
id="enter_button"></a>
</div>
</div>
</div>
</div>
</div>
<script src="js/jquary.min.js"></script>
<script src="js/connect.js"></script>
</body>
</html>
・食卓コネクト - ビデオ通話ページ – (video.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>食卓 Connect - ビデオ通話ページ -</title>
<meta name="viewport" content="initial-scale=1, minimum-scale=1, maximum-scale=1,
38
user-scalable=no">
<link rel="stylesheet" type="text/css" href="css/video.css">
<script src="js/jquary.min.js"></script>
<script src="js/RecordRTC.js"></script>
<script src="https://skyway.io/dist/0.3/peer.js"></script>
</head>
<body>
<div class="bezel">
<div id="videos">
</div>
<div id="monitorContainer">
<div id="monitorWrapper">
<video id="monitorVideo" muted="muted" autoplay></video>
<audio id="monitorAudio" autoplay="autoplay"></audio>
</div>
</div>
<div id="logo">食卓 Connect</div>
<div id="countWrapper"><span id="count">0</span> connections</div>
<div id="url"></div>
</div>
<div id="container" style=" display:none; position:fixed; top:10px; right:10px; width:320px;
height:240px; background-color:#666; border-radius:10px;">
<video id="video_display" autoplay style="width:320px; height:240px; border-radius:10px;
display:none;"></video>
</div>
<button id="video_letter">保存されたビデオレター</button>
<div id="my_video"></div>
<div id="my_button_area">
<button id="start_recording">映像を録画</button>
<button id="stop_recording" style="display:none;">録画を停止</button>
<button id="ok" style="display:none;">映像を登録</button>
<button id="delete_files" style="display:none;">映像を削除</button>
</div>
<script src="js/video_connect.js"></script>
<script src="js/record.js"></script>
</body>
</html>
Javascript
39
・connect.js
// JavaScript Document
var URL = 'video.html';
//通信を行う URL
$(function(){
genChannel();
//チャンネル名を決める処理
});
//zeroPadding
function zeroPadding(number, length) {
number = number - 0 + '';
//文字列可
return (Array(length).join('0') + number).slice(-length); //join により0でつなげた文字列
を最初から最後まで
}
//チャンネルを決める処理
function genChannel() {
var channel = Math.floor(Math.random() * (9999 - 1 + 1 ) + 1) + '';
updateURLs(channel);
//チャンネル名・ULR のアプデ
updateTextbox(channel);
//チャンネル追加
//' 'は文字列化
}
function updateURLs(channel) {
//channel が文字列でないときの処理
if (typeof channel !== 'string') {
channel = $('#enter_room_number').val();
}
channel = zeroPadding(channel, 4);
//0000 に channel を追加 [ 0000 + channel ]
var connectURLaddress = URL + '#' + channel; // ['http://'] + ['is.gd'] + ['#'] + [channel]
console.log(connectURLaddress);
$('#start_connect').attr('href', connectURLaddress);
// カ メ ラ ボ タ ン の href 属 性 を
startCamUrl に
}
//テキストエリアの数値を変更
function updateTextbox(channel) {
//channel が文字列でないときの処理
if (typeof channel !== 'string') {
channel = $('#enter_room_number').val();
}
channel = zeroPadding(channel, 4); //0000 に channel を追加 [ 0000 + channel ]
$('#enter_room_number').val(channel);
//#チャンネルを変更
}
40
//----alert
function haishokuAlert(){
alert('ここから先は開発中であり、リンク先はイメージです');
}
function eiyoushiAlert(){
alert('まだ未開設です。');
}
・video_connect.js
// JavaScript Document
// PeerJS object
var APIKEY = '8a35894a-0667-11e4-97a5-57ff82554118';
//サーバー用 APIKEY
var userNumber = Math.floor(Math.random() * (9999 - 1 + 1 ) + 1);
var roomNumber = location.hash.replace(/^#/, '');
var CONNECTER_ID = 'room' + roomNumber + '-' + userNumber;
var peer = new Peer(
CONNECTER_ID, {
key: APIKEY,
debug: 3
});
var roomPass;
var videoCount = 0;
var existingCalls = {};
peer.on('open', function(){
console.log(' Your Peer is : ' + peer.id);
myVideo();
})
// 相手の発信を受信したとき
peer.on('call', function(call){
call.answer(window.localStream);
partnerVideo(call);
//受信した相手の ID、ローカルストリームを表示処理へ
window.existingCalls[call.peer] = call;
// 切断時
call.on('close', function() {
window.existingCalls[call.peer].close();
delete window.existingCalls[call.peer];
$('#' + call.peer).remove(); //対象 peer の video タグを削除
videoCounterMinus();
41
videoCssChanger();
});
});
peer.on('error', function(err){
//alert(err.message);
});
//----相手の Video 表示処理
function partnerVideo(call){
//ローカルストリーム表示
call.on('stream',function(stream){ //相手の Peer が stream を追加したとき
videoCounter();
//video の数をカウント
console.log('This function is partnerVideo');
$('<video autoplay="autoplay"></video>') //video 追加
.attr({
id: call.peer,
//相手の peer を ID にする
class: 'stream-' + videoCount,
src: URL.createObjectURL(stream) //相手の streamURL を src 属性に
})
.appendTo('#videos');
videoCssChanger();
});
}
function videoCssChanger(){
console.log('videoCssChanger Enter');
if(videoCount == 1){
videoHeightP = 100 + '%';;
$('#videos > video').css('height', videoHeightP);
}else if(videoCount > 1 && videoCount < 5){
videoHeightP = 100 / 2 + '%';
$('#videos > video').css('height', videoHeightP);
}
}
//----video の数をカウントする処理
function videoCounter(){
videoCount++;
console.log('Video Count : ' + videoCount);
ConnectCounter();
}
42
function videoCounterMinus(){
videoCount--;
console.log('Video Count : ' + videoCount);
ConnectCounter();
}
function ConnectCounter(){
$('#count').text(videoCount);
}
//----自分のメディア表示
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia;
function myVideo(){
navigator.getUserMedia({audio:true,video:true},function(stream){
$('<video muted="muted" autoplay="autoplay"></video>')
//video 追加
attr({
id: peer.id,
//自分の peer を ID にする
width: '320px',
src: URL.createObjectURL(stream)
//自分の streamURL を src 属性に
})
.appendTo('#my_video');
window.localStream = stream;
getUserList();
},function(err){
alert(err.message);
});
};
//----RestAPI にてユーザー取得
function getUserList() {
//ユーザリストを取得
$.get('https://skyway.io/active/list/' + APIKEY, function (list) {
for (var i = 0, length = list.length; i < length; i++) {
console.log("----UserList:" + list[i]);
}
for (var i = 0, length = list.length; i < length; i++) {
var id = list[i];
console.log("Now connectingUserNumber " + i + ":" + id);
if(id == peer.id){
console.log("ConnectingUserNumber " + i + " はあなたです" );
43
} else{
var idCheck = id.substring(4,8);
console.log('idCheckman: ' + idCheck);
if(idCheck == roomNumber){
connectVideo(id); //id にむけて発信
}else{
console.log('This roomNumber is #' + roomNumber);
}
}
}
});
}
//----video 接続処理
function connectVideo(partnerId){
var call = peer.call(partnerId, window.localStream); //相手の ID に localStream を発信
console.log("接続先 ID:"+ call.peer);
partnerVideo(call);
//相手の Video を表示処理
}
・record.js
// JavaScript Document
//WebrtcCode for RecordRTC
var upLoadRoomNumber = location.hash.replace(/^#/, '');
var recorder;
startRecording = document.getElementById('start_recording'),
//録画開始
stopRecording = document.getElementById('stop_recording'),
//録画停止
deleteFiles = document.getElementById('delete_files'),
//録画停止
videoLetter = document.getElementById('video_letter');
recordFileOk = document.getElementById('ok');
var container = document.getElementById('container');
var videoDisplay = document.getElementById('video_display');
var videoConstraints = {
audio: true, //音声はミュート
video: true
//ビデオ ON
};
//-----------------録画開始ボタンをクリックしたとき
startRecording.onclick = function(){
startVideoRecord(true);
//録画する関数へ
44
//video タグ
$('#start_recording').css({"display":"none"});
$('#stop_recording').css({"display":"inline"});
};
var fileName;
//ファイル名
//-----------------録画停止ボタンをクリックしたとき
stopRecording.onclick = function(){
//録画停止ボタンを押したとき
$('#stop_recording').css({"display":"none"
});
$('#ok').css({"display":"inline"});
$('#delete_files').css({"display":"inline"
});
//[Record][Stop]の ON/OFF
$('#container').slideDown();
$('#my_video > video').css({
"border":"none",
"border-radius":"0px"
});
videoDisplay.src = ''; //一度 video タグのソースを空白にする
fileName = upLoadRoomNumber + (Math.round(Math.random() * 999999999 ) +
999999999);
//録画停止
recorder.stopRecording(function(url) {
videoDisplay.src = url;
//録画した映像ファイルの URL
videoDisplay.play();
//録画した映像ファイルを再生!
PostBlob(recorder.getBlob(), 'video', fileName + '.webm');
//(記録されたマルチメデ
ィアオブジェクト、ファイルタイプ、ファイル名)
});
};
//-----------------消去停止ボタンをクリックしたとき
deleteFiles.onclick = function() {
$('#delete_files').css({"display":"none"});
$('#ok').css({"display":"none"
});
$('#start_recording').css({"display":"inline" });
deleteVideoFiles();
};
videoLetter.onclick = function(){
window.open( "http://buturi.heteml.jp/student/2014/otani/test_webrtc/recordedvideo.
php#" + upLoadRoomNumber );
}
recordFileOk.onclick = function(){
45
$('#delete_files').css({"display":"none"});
$('#ok').css({"display":"none"});
$('#start_recording').css({"display":"inline" });
$('#container').slideUp();
fileName = null;
container.innerHTML = '';
};
//-----------------録画開始開始
function startVideoRecord(isRecordVideo){
navigator.getUserMedia(videoConstraints, function(stream) {
// カ メ
ラ映像を video タグに表示
videoDisplay.onloadedmetadata = function(){
//メタデータの読み込みが完了し
た場合と、それ以降に読み込み状況が変化した場合に発生するイベントに対するイベントハンド
ラ
videoDisplay.width = 320; //video 表示の幅
videoDisplay.height =240; //video 表示の高さ
var options = {
//映像を出力・録画する幅と高さを設定
type: isRecordVideo ? 'video' : 'gif',
video:videoDisplay,
canvas: {
width: 320,
height: 240
}
};
recorder = window.RecordRTC(stream,options);
recorder.startRecording();
};
videoDisplay.src = URL.createObjectURL(stream); //video タグの src を設定
$('#my_video > video').css({"border":"3px red solid",
"border-radius":"5px"});
},function(){
//エラー
});
};
//-----------------アップロードされた動画の削除処理
function deleteVideoFiles() {
$('#container').slideUp();
if (!fileName) return;
//ファイル名がない時は処理終了
46
var formData = new FormData(); //FormData オブジェクトを作成する
formData.append('delete-file', fileName);
xhr('php/delete.php', formData, null, null, function(response) {
console.log(response);
//problem deleting files.
});
fileName = null;
container.innerHTML = '';
}
//-----------------PostBlob
//Blob:画像や音声、その他のマルチメディアオブジェクトが BLOB として格納される。データ
ベース管理システム(DBMS)においてバイナリデータを格納する場合のデータ型。
function PostBlob(blob, fileType, fileName) {
// FormData
var formData = new FormData();
//FormData オブジェクトを作成する
formData.append(fileType + '-filename', fileName); //fileType + '-filename' というパ
ラメータに fileName を追加
formData.append(fileType + '-blob', blob);
//fileType + '-blob'
というパラメータに
blob を追加
// progress-bar を追加
var strong = document.createElement('strong');
var progress = document.createElement('progress');
strong.id = 'percentage';
strong.innerHTML = fileType + ' upload progress: ';
container.appendChild(strong);
container.appendChild(progress);
// POST the Blob using XHR2
xhr('php/save.php', formData, progress, percentage, function(fileURL) {
var mediaElement = document.createElement(fileType);
//fileType = video
video タグ生成
var source = document.createElement('source'); //source タグ生成
var href = location.href.substr(0, location.href.lastIndexOf('/') + 0);
//動画 URL を指定.substr(start,finish)
console.log(fileURL);
source.src = href + fileURL;
//録画された動画の URL
//href:[ http://localhost/labo/test_webrtc ]
//fileURL:[ /uploads/-----.webm ]
if (fileType == 'video') source.type = 'video/webm; codecs="vp8, vorbis"';
47
//ファイルタイプが video であるなら
mediaElement.appendChild(source);
//source タグを追加
mediaElement.controls = true;
//video タグをアクティブにする
container.appendChild(mediaElement);
//box に video タグを追加
mediaElement.play();
//再生
progress.parentNode.removeChild(progress);
strong.parentNode.removeChild(strong);
//プログレスバーを消去
//[Saved]という文字を消去
});
}
//-----------------サーバーとの通信処理
function xhr(phpUrl, data, progress, percentage, callback) {
var request = new XMLHttpRequest();
console.log(request.readyState);
request.onreadystatechange = function() { //XHR 通信の状態が変化するたびに実行
if (request.readyState == 4 && request.status == 200) {
//status:200(リクエストに成功)
callback(request.responseText);
// /uploads/-------.webm
}
};
if (phpUrl.indexOf('php/delete.php') == -1) {
request.upload.onloadstart = function() {
//アップロードを開始時に実行
percentage.innerHTML = 'Upload started...';
//パーセンテージを表示
};
request.upload.onprogress = function(event) {
//アップロードの進行中の処理
progress.max = event.total;
progress.value = event.loaded;
percentage.innerHTML = 'Upload Progress ' + Math.round(event.loaded /
event.total * 100) + "%";
};
request.upload.onload = function() {
//アップロード完了時に実行
percentage.innerHTML = 'Saved!';
};
}
request.open('POST', phpUrl);
request.send(data);
}
PHP
・食卓コネクト-ビデオレター閲覧ページ- (recordedvideo.php)
48
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>食卓コネクト - VideoRoom</title>
<link rel="stylesheet" type="text/css" href="css/videoroom.css">
<script src="js/jquary.min.js"></script>
<script src="js/RecordRTC.js"></script>
</head>
<body>
<header>
<h1 id="room_name"></h1>
</header>
<div id="video_area">
<?php
// ディレクトリハンドルの取得
$dir_h = opendir( "./uploads/" ) ;
$i = 0;
$fileNameList = array();
// ファイル・ディレクトリの一覧を $file_list に
while (false !== ($file_list[] = readdir($dir_h))) ;
// ディレクトリハンドルを閉じる
closedir( $dir_h ) ;
//ディレクトリ内のファイル名を1つずつを取得
foreach ( $file_list as $file_name ){
//ファイルのみを表示
if(is_file( "./uploads/" . $file_name)){
$p = pathinfo("./uploads/" . $file_name);
if($p["extension"] == "webm" ){
$fileNameList[$i] = $file_name;
$jsonFile_name=json_encode($fileNameList);
$i++;
}
}
}
?>
</div>
<script>
49
var roomNumber = location.hash.replace(/^#/, '');
var roomName = document.getElementById('room_name');
roomName.innerHTML = '#' + roomNumber + ' のビデオレター'
var fileNameList = JSON.parse('<?php echo $jsonFile_name; ?>');
var videoArea = document.getElementById('video_area');
for(var i=0; i<fileNameList.length;i++){
var fileIDCheck = fileNameList[i].substring(0,4);
console.log(fileIDCheck);
if(roomNumber == fileIDCheck){
console.log('roomNumber:' + roomNumber);
console.log('fileIDCheck:' + fileIDCheck);
var box = document.createElement('div');
box.className ='video_manager';
box.id = 'boxname_is_' + fileNameList[i];
box.width = window.parent.screen.width / 5;
var videoEle = document.createElement('video');
videoEle.src=
'http://buturi.heteml.jp/student/2014/otani/test_webrtc/uploads/' + fileNameList[i];
videoEle.muted = 'true';
videoEle.controls = 'true';
videoArea.appendChild(box);
box.appendChild(videoEle);
$('<button class="delete_files" onClick="deleteVideoFiles(event)" width="25%">ビデ
オレターを削除</button>')
//video 追加
.attr({title:fileNameList[i],}).appendTo(box);
}else{
console.log(roomNumber + '!=' + fileIDCheck);
}
}
</script>
<script>
var fileName;
//-----------------アップロードされた動画の削除処理
function deleteVideoFiles(fileNameElement) {
var yourAnsweris = confirm('クリックした動画を削除してもよろしいですか?');
if (yourAnsweris == true){
fileName=
fileNameElement.target.title.substr(0,fileNameElement.target.title.lastIndexOf('.'));
50
if(!fileName) return;
//ファイル名がない時は処理終了
var formData = new FormData();
//FormData オブジェクトを生成する
formData.append('delete-file', fileName);
xhr('php/delete.php',formData,null,null,function(response){
console.log('response:' + response);
//deleting files
});
fileName = null;
var
elementIdNameOfVideoBox
=
document.getElementById('boxname_is_'
+
fileNameElement.target.title);
elementIdNameOfVideoBox.innerHTML ='';
} else{
}
}
//-----------------サーバーとの通信処理
function xhr(phpUrl, data, progress, percentage, callback) {
var request = new XMLHttpRequest();
console.log('request.readyState:' + request.readyState);
request.onreadystatechange = function() {
if (request.readyState == 4 && request.status == 200) {
callback(request.responseText);
}
};
request.open('POST', phpUrl);
request.send(data);
//アクセス先 URL を設定 (php/save.php)
//「送信したいデータ(data)]を指
定し、XHR 通信を開始
}
</script>
</body>
</html>
51