昨日はRTMPを使用するところまで行ったが、
今日はより少ない遅延を求めてwebRTCを導入してみる。(macユーザー)
最終的な目標は360度カメラで撮った映像を遅延をなるべく無くして、
スマホ上に配信すること。
#デモを試してみる
以下のサイトが大変役に立った。
感謝感謝。
WebRTCでキャスしよう!片方向リアルタイム映像配信を作ろう
とりあえずこれをコピペして試せた。
確かに遅延は1秒以下だ。
素晴らしい。。。
ここからカスタマイズしていく。
#ソースの検討
どうやって、配信元のウェブブラウザにライブ映像を届けるか考えられるのは、
###CamTwist
CamTwistはwebカメラから入力映像を受け取り、加工を加えて、仮想webカメラとして出力する
したがって、デモのコードをそのまま使える。
###OBS
obsでもプラグインを使うことで同様の機能を使えるようになる。
下記サイトに導入方法や使い方が書かれている。(Windows用だった。。。)
#####Windows
OBSを使ってPixivSketchLIVEで配信する動機
#####Mac
iVirtualCamera
があるのだが、
This project is incompatible with the latest OS, it may causes the system crash at boot.
ということで、大変そうなのでCamTwistを使うことにする。
#カスタマイズ
昨日動いてたコードが動かない・・・・
websocketのconnectとdisconnentのタイミングの問題だったみたい。
##nodejsをhtmlサーバーとして外部から接続できるようにする
シグナリングサーバをhtmlサーバーとしても使用する。
以前つくっったnode.jsのサーバー用コードをシグナリングサーバーファイルと統合する。
var BROADCAST_ID = '_broadcast_';
// -- create the socket server on the port ---
var app = require('http').createServer(handler);
var socketio = require('socket.io').listen(app);
var port = 9001;
app.listen(port);
console.log('signaling server started on port:' + port);
// ---------- read html part ----------
var fs = require('fs');
var path = require('path');
var mime = {
// 読み取りたいMIMEタイプはここに追記
".html": "text/html",
".css": "text/css",
".js": "text/javascript",
};
//ファイルの読み込み
function handler(req, res) {
console.log(req.url)
if (req.url == '/') {
filePath = '/index.html';
} else {
filePath = req.url;
}
var fullPath = __dirname + filePath;
res.writeHead(200, {"Content-Type": mime[path.extname(fullPath)] || "text/plain"});
fs.readFile(fullPath, function(err, data) {
if (err) {
// エラー時の応答
} else {
res.end(data, 'UTF-8');
}
});
}
// ---------- signaling part ----------
// This callback function is called every time a socket
// tries to connect to the server
socketio.on('connection', function(socket) {
// ---- multi room ----
socket.on('enter', function(roomname) {
socket.join(roomname);
console.log('id=' + socket.id + ' enter room=' + roomname);
setRoomname(roomname);
});
function setRoomname(room) {
//// for v0.9
//socket.set('roomname', room);
// for v1.0
socket.roomname = room;
}
function getRoomname() {
var room = null;
//// for v0.9
//socket.get('roomname', function(err, _room) {
// room = _room;
//});
// for v1.0
room = socket.roomname;
return room;
}
function emitMessage(type, message) {
// ----- multi room ----
var roomname = getRoomname();
if (roomname) {
console.log('===== message broadcast to room -->' + roomname);
socket.broadcast.to(roomname).emit(type, message);
}
else {
console.log('===== message broadcast all');
socket.broadcast.emit(type, message);
}
}
// When a user send a SDP message
// broadcast to all users in the room
socket.on('message', function(message) {
message.from = socket.id;
// get send target
var target = message.sendto;
if ( (target) && (target != BROADCAST_ID) ) {
console.log('===== message emit to -->' + target);
socket.to(target).emit('message', message);
return;
}
// broadcast in room
emitMessage('message', message);
});
// When the user hangs up
// broadcast bye signal to all users in the room
socket.on('disconnect', function() {
console.log('-- user disconnect: ' + socket.id);
// --- emit ----
emitMessage('user disconnected', {id: socket.id});
// --- leave room --
var roomname = getRoomname();
if (roomname) {
socket.leave(roomname);
}
});
});
また、ページが読み込まれたときに自動で映像を受信するように変更した。
listen.jsは上記のサイトと全く同じ。
<!DOCTYPE html>
<html>
<head>
<title>broadcast watch</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<script>
window.onload = function(){
sendRequest();
console.log("req");
}
</script>
</head>
<body>
<video id="remote-video" autoplay controls muted></video>
<!---- socket ※自分のシグナリングサーバーに合わせて変更してください ------>
<script src="socket.io/socket.io.js"></script>
<script src="./js/listen.js" type="text/javascript"></script>
</body>
</html>
シグナリングサーバーの書き換えがうまくできていないと、ローカルでは動いても、外部からアクセスした場合、シグナリングができないことがある。
スマホだとうまくいかないことが多い。やはりどこかにボタンを仕込むのが確実かも。
##360度映像に対応する
###映像のアスペクト比を2:1にする
360度映像形式は
CamTwist
Preference/General/Video_Size
でcustom
を選択し、サイズを変更
今回は920*1960に設定した。
設定したのちCamTwistをリスタート
webRTC
-
talk側
videoタグの設定を変更する
<!DOCTYPE html>
<html>
<head>
<title>Broadcast Talk</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body>
<button type="button" onclick="startVideo();">Start video</button>
<button type="button" onclick="stopVideo();">Stop video</button>
<button type="button" onclick="tellReady();">On Air</button>
<br />
<div style="position: relative;">
<video id="local-video" autoplay controls style="width: 100%;"></video>
</div>
<!---- socket ※自分のシグナリングサーバーに合わせて変更してください------>
<script src="http://localhost:9001/socket.io/socket.io.js"></script>
<script src="./js/talk.js" type="text/javascript"></script>
</body>
</html>
- listen側
videoタグの設定を変更
resizeRemoteVideo()のファンクションを削除
<!DOCTYPE html>
<html>
<head>
<title>broadcast watch</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<script>
window.onload = function(){
sendRequest();
}
</script>
</head>
<body>
<video id="remote-video" autoplay controls muted style="width: 100%;"></video>
<!---- socket ※自分のシグナリングサーバーに合わせて変更してください ------>
<script src="socket.io/socket.io.js"></script>
<script src="./js/listen.js" type="text/javascript"></script>
</body>
</html>
これで、2:1の動画が送れるようになった。
###VR化する
A-frameを使ってこれをVRにする。
A-frameを使うのは2回目なのだがなぜか手間取った。
####通常の360度映像の場合
まずはwebRTCではなく普通の映像ファイルをソースとして使う方法。
A-frameの最新版は0.8.0なのだが、それを使うとなぜか映像が真っ白になって表示されなくなるので0.7.0を使った。
スマホ再生の0.7.0の問題は補足1。
(追記2018.08.19)
A-frameの最新バージョンは0.8.2でした。
これを使うと、映像が白くなりません。
また、スマホでもうまく再生できます。
ポイントはCamera位置を親entityで1.6m下げているところ。
A-frameのデフォルトカメラは人の視点にあわせるため高さが1.6mに設定されている。
今回は360度映像を使うので0mに視点をあわせる。
<!DOCTYPE html>
<html>
<head>
<title>broadcast watch</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<script src="https://aframe.io/releases/0.7.0/aframe.min.js"></script>
</head>
<body>
<a-scene>
<a-assets>
<video id="video" autoplay loop="true" src="video/test.mp4"></video>
</a-assets>
<!-- camera -->
<a-entity id="camera" position="0 -1.6 0" rotate="0 0 0">
<a-camera id="acamera"></a-camera>
</a-entity>
<!-- video sphere -->
<a-videosphere src="#video"></a-videosphere>
</a-scene>
</body>
</html>
####webRTC
これをベースとして、webRTCの映像を利用できるように書き換える。
書き換えはとても簡単
<!DOCTYPE html>
<html>
<head>
<title>broadcast watch</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<script src="https://aframe.io/releases/0.7.0/aframe.min.js"></script>
<script>
window.onload = function(){
sendRequest();
}
</script>
</head>
<body>
<a-scene>
<a-assets>
<video id="remote-video" autoplay loop="true"></video>
</a-assets>
<!-- camera -->
<a-entity id="camera" position="0 -1.6 0" rotate="0 0 0">
<a-camera id="acamera"></a-camera>
</a-entity>
<a-videosphere src="#remote-video"></a-videosphere>
</a-scene>
<!---- socket ※自分のシグナリングサーバーに合わせて変更してください ------>
<script src="socket.io/socket.io.js"></script>
<script src="./js/listen.js" type="text/javascript"></script>
</body>
</html>
これでめでたく360度映像をリアルタイム中継できるようになった!
####比較
webRTCとvideoファイルを読み込む方法の比較
いかに出力される映像のキャプチャを比較した。
伝わるかわからないが明らかにwebRTCの方の画質が悪かった。
また再生もカクカクなりやすい。
#補足
##補足1
A-frame0.7.0で実現した360度映像をスマホでみると、すごいグッラグラする。
理由はわからないが解決方法。
A-frame0.8.0をローカルにダウンロードする(追記: 0.8.2でした)
a-frameのソースをローカルのaframe-master.min.js
にすると解決する。
(追記 2018.08.19)
原因わかりました。
Chrome66以降の仕様変更によるものらしいです。
A-frame0.8.2で対応してます。
Camera is super sensitive to motion on Chrome Beta 66 on Android
#まとめ
とりあえずシステムはできた。
あとはどうすればクオリティが上がるか。