質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

Socket.IO

Socket.IOはNode.js上で動くライブラリであり、すべてのブラウザとモバイルデバイスでリアルタイムのアプリを作動させる事を目的としています。

Express

ExpressはNode.jsのWebアプリケーションフレームワークです。 マルチページを構築するための機能セットおよびハイブリッドのWebアプリケーションを提供します。

Q&A

解決済

1回答

1503閲覧

xreaサーバーでWEBRTCを使ってWEBビデオチャットを作るために新しいバージョンのnode.jsをインストール

phpsyoshinsya

総合スコア156

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

Socket.IO

Socket.IOはNode.js上で動くライブラリであり、すべてのブラウザとモバイルデバイスでリアルタイムのアプリを作動させる事を目的としています。

Express

ExpressはNode.jsのWebアプリケーションフレームワークです。 マルチページを構築するための機能セットおよびハイブリッドのWebアプリケーションを提供します。

0グッド

0クリップ

投稿2022/03/27 03:00

編集2022/03/27 03:07

 現在引っかかっていること

xreaの借りているs500に、下のページを参考に配置して、pathを通すところまで来たのだけど、bash_profileが見つからないとなって、書き込めない。
参考ページ
https://neos21.net/blog/2020/10/29-01.html#path-%E3%82%92%E9%80%9A%E3%81%99

どんなふうに振舞えば

今回RTCのP2Pを利用してビデオチャットを作りたいと思って、準備しています。

順を追って確認しながら、
{HOME}/.local.lib/

node-v16.14.2-linux-x64
ホルダごとコピーしました。

そもそもの疑問が

参考にしたページの最後にnode.jsをCGIとして使うって書いてあるけど、やりたいことの実現のために、これは必要なのでしょうか。

テストで動かそうとしているソースがこちら

index.html

<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>WebRTC Video Chat</title> <style> *, ::before, ::after { box-sizing: border-box; font-family: sans-serif; touch-action: manipulation; -webkit-touch-callout: none; /* for iOS */ -webkit-user-select: none; user-select: none; } html { width: 100%; height: 100%; } body { width: 100%; height: 100%; margin: 0; line-height: 1; overflow: hidden; } header { position: absolute; top: 0; left: 0; display: grid; grid-template-columns: 1fr auto 1fr; width: 100%; z-index: 10000; } header > span:last-child { text-align: right; } main { display: grid; grid-template-columns: 1fr 1fr; grid-gap: .5rem; align-items: center; justify-items: center; height: 100%; } video { max-width: 100%; max-height: 100vh; background: #eee; } footer { position: absolute; bottom: 0; left: 0; width: 100%; text-align: center; z-index: 10000; } </style> <script src="http://mrt.s500.xrea.com/socket.io/socket.io.js"></script> <script> document.addEventListener('DOMContentLoaded', () => { const socket = window.io(); let rtcPeerConnection; let remoteStream; // 2人目の相手の接続を受け取らないようにするため、1人目の接続で受け取った Stream を保持しておく socket .on('connect', () => { // 画面初期表示時にサーバと接続する console.log('on connect'); }) .on('message', async (event) => { // 別の人が Start するとココに入ってくる let parsedEvent; try { parsedEvent = JSON.parse(event); } catch(error) { return console.warn('on message : Failed To Parse', error); } if(!rtcPeerConnection) { return console.log('on message : RTCPeerConnection Is Not Yet Created', parsedEvent); } // トップレベルプロパティは sdp か candidate のみ try { if(parsedEvent.sdp) { // FIXME : 2人目の相手が接続してくると Failed to set remote answer sdp: Called in wrong state: kStable が発生する (type: 'answer' 時) // それを回避するための暫定策として、1人目と接続している最中は2人目以降の相手の接続を無視することにする if(remoteStream) { return console.log(' on message : Remote Stream Is Already Added, Ignore'); } // sdp プロパティ配下は type: 'offer' or 'answer' と sdp プロパティ await rtcPeerConnection.setRemoteDescription(new RTCSessionDescription(parsedEvent.sdp)); // FIXME : iOS Safari が後から来た接続を受け取った時 (type: 'offer') に以下のエラーが出る // InvalidStateError: description type incompatible with current signaling state // iOS Safari が後から接続すれば正常に接続できる。回避策が分からない if(parsedEvent.sdp.type === 'offer') { console.log('on message : SDP Offer', parsedEvent); // type: 'answer' 時に createAnswer() すると以下のエラーが出るので type: 'offer' のみにする // Failed to execute 'createAnswer' on 'RTCPeerConnection': PeerConnection cannot create an answer in a state other than have-remote-offer or have-local-pranswer. const answer = await rtcPeerConnection.createAnswer(); await rtcPeerConnection.setLocalDescription(answer); // Socket を経由して Answer SDP を送る (送る内容は Offer SDP と同じ) socket.emit('message', JSON.stringify({ sdp: rtcPeerConnection.localDescription })); } else { console.log('on message : SDP Answer (Do Nothing)', parsedEvent); } } else if(parsedEvent.candidate) { // candidate プロパティ配下は candidate・sdpMid・sdpMLineIndex プロパティ console.log('on message : Candidate', parsedEvent); rtcPeerConnection.addIceCandidate(new RTCIceCandidate(parsedEvent.candidate)); } else { console.log('on message : Other (Do Nothing)', parsedEvent); // 基本ない } } catch(error) { console.warn('on message : Unexpected Error', error, parsedEvent); // 基本ない } }); // ビデオを起動し Local Stream を送信する document.getElementById('start-button').addEventListener('click', async () => { try { const localStream = await getUserMedia(); createRtcPeerConnection(localStream); const sessionDescription = await rtcPeerConnection.createOffer(); await rtcPeerConnection.setLocalDescription(sessionDescription); // Socket を経由して Offer SDP を送る socket.emit('message', JSON.stringify({ sdp: rtcPeerConnection.localDescription })); document.getElementById('start-button').disabled = true; document.getElementById('stop-button' ).disabled = false; document.getElementById('start-button').style.display = 'none'; document.getElementById('stop-button' ).style.display = 'inline'; } catch(error) { console.warn('Failed To Start', error); } }); // ビデオを停止する document.getElementById('stop-button').addEventListener('click', () => { try { if(document.getElementById('local-video').srcObject) { document.getElementById('local-video').srcObject.getTracks().forEach((track) => { track.stop(); }); document.getElementById('local-video').srcObject = null; } if(rtcPeerConnection) { rtcPeerConnection.close(); rtcPeerConnection = null; } if(document.getElementById('remote-video').srcObject) { try { document.getElementById('remote-video').srcObject.getTracks().forEach((track) => { track.stop(); }); document.getElementById('remote-video').srcObject = null; } catch(error) { console.warn('Failed To Stop Remote Video', error); } } remoteStream = null; document.getElementById('start-button').disabled = false; document.getElementById('stop-button' ).disabled = true; document.getElementById('start-button').style.display = 'inline'; document.getElementById('stop-button' ).style.display = 'none'; } catch(error) { console.warn('Failed To Stop Video (Unexpected Error)', error); } }); /** ビデオを開始する・例外ハンドリングは呼び出し元の #start-button のクリックイベントで行う */ async function getUserMedia() { const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: false }); document.getElementById('local-video').srcObject = stream; document.getElementById('local-video').play(); return stream; } /** getUserMedia() で取得した Stream をセットした RTCPeerConnection を作成する・例外ハンドリングは呼び出し元の #start-button のクリックイベントで行う */ function createRtcPeerConnection(localStream) { if(rtcPeerConnection) { return; } // iOS Safari の場合 Member RTCIceServer.urls is required and must be an instance of (DOMString or sequence) エラーが出るので // url ではなく urls を使う : https://github.com/shiguredo/momo/pull/48 rtcPeerConnection = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] }); rtcPeerConnection.onicecandidate = onicecandidate; // Chrome などは RTCPeerConnection.addStream(localStream) が動作するが iOS Safari は動作しないので addTrack() を使う // iOS Safari : https://stackoverflow.com/questions/57106846/uncaught-typeerror-peerconnection-addstream-is-not-a-function/57108963 localStream.getTracks().forEach((track) => { rtcPeerConnection.addTrack(track, localStream); }); // iOS Safari では onaddstream が動作しないので ontrack を使用する (Chrome なども ontrack に対応) rtcPeerConnection.ontrack = ontrack; // onremovestream は相手の接続が切れたり再接続されたりした時に発火するが、onaddstream (ontrack) 後に onremovestream が動作して // おかしくなることが多いので何もしないことにする (removetrack も定義しない) } /** ICE Candidate を送る */ function onicecandidate(event) { if(event.candidate) { console.log('onicecandidate : socket.emit()', event); socket.emit('message', JSON.stringify({ candidate: event.candidate })); } else { console.log('onicecandidate : End', event); } } /** 相手の接続を受け取ったらリモート映像として表示する */ function ontrack(event) { console.log('ontrack', event); if(remoteStream) { return console.log(' ontrack : Remote Stream Is Already Added, Ignore'); // on message で弾いているので実際はチェックしなくても大丈夫 } try { document.getElementById('remote-video').srcObject = remoteStream = event.streams[0]; document.getElementById('remote-video').play(); } catch(error) { // Windows Chrome だと play() can only be initialized by a user gesture. エラーが発生して再生できない場合がある // chrome://flags#enable-webrtc-remote-event-log を有効にすると play() できるようになる console.warn('Failed To Play Remote Video', error); } } document.getElementById('start-button').disabled = false; document.getElementById('stop-button' ).disabled = true; document.getElementById('start-button').style.display = 'inline'; document.getElementById('stop-button' ).style.display = 'none'; }); </script> </head> <body> <header> <span>Local</span> <button type="button" id="start-button">Start</button> <button type="button" id="stop-button" style="display: none;" disabled>Stop</button> <span>Remote</span> </header> <main> <video id="local-video" playsinline muted autoplay></video> <video id="remote-video" playsinline muted autoplay></video> </main> <footer> Author : <a href="https://neos21.net/">Neo</a> (<a href="https://github.com/Neos21/webrtc-video-chat">GitHub</a>) </footer> </body> </html>

これを
public_html/videochat
に置いているのですが、
スタートボタンがストップに表示変更されません。

知りたいこと

取り組んでいる内容は必要な方向性と一致していますか?

xreaでは無理だよという回答もありましたが、どうなのでしょうか。

知らないことばかりですみませんが、何卒ご教授お願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

xreaでは無理だよという回答もありましたが、どうなのでしょうか。

それを聞いて、仮に私が「無理だよ」って返したら納得するんですか?
もう一度「どうなのでしょうか?」と言い始めてゴリ押しするんじゃ何も意味がないので
何故そんな会話の流れになったのか理由・経緯を記述してください

https://www.hiramine.com/programming/videochat_webrtc/index.html
ググって最初の方に出てくるNode.jsでビデオチャット作ってみましょうの解説サイトには、
Socket.ioの文字があるので、CGIを使う場合は一目で無理とわかります。
以下ざっと無理の流れを解説します。

参考にしたページの最後にnode.jsをCGIとして使うって書いてあるけど、やりたいことの実現のために、これは必要なのでしょうか。

https://www.xrea.com/spec/
xreaが何をしているサービスかというと、Apacheを使ったWebサイト公開サービスです。

Apacheというアプリケーションを実行すると、
HTTPリクエストが来た時にURLのパス部を参照し、Apacheの特定ディレクトリからファイルがあるかないかを探して、ファイルがあればその中身をHTTPレスポンスとして返すWebサーバとして実行します。

ApacheありきでApacheから他の言語を実行する無理やりな仕組みがCGI。
Apache内でスクリプトファイルを指定した場合、スクリプトファイルに記述されている内容をそのまま返すのではなく
一度プログラムとして実行してから、生成出来た文字列をHTTPレスポンスとして返すのがCGI

HTTP通信はステートレスな1度キリのでかいファイルの受け渡しを行って終わりなので、ビデオチャットなどのデータ片を継続的に送受信し続けるサービスとしては不向きです。
つまり、Apacheの存在自体がリアルタイム通信に対応していないってことです。


話を戻して下記のサイト
https://www.hiramine.com/programming/videochat_webrtc/index.html

ExpressとSocket.IOを組み合わせてビデオチャットを作りましょうって言ってるわけですが、
Expressはマシンに対してTCP80番ポート等で待受するWebサーバとして起動させるソフトウェアですが、
これの用途がTcP80番を専有するApacheと被っています。

別にExpressに限らずhttpモジュールを使ってもApacheと緩衝しますが。
使いたければクソ邪魔なApacheを落とすしかない。

しかし、xreaはApacheを可動させファイルベースのWebサーバを公開する事を大前提としたレンタルサーバなので、
「Node.jsで新しいWebサーバのプロセスを作ろうとしているお前が客じゃないんだよ帰れ」状態ってことです。

Scoket.io等を諦めてCGIでもやってやれない事はない回避策を研究してイチから作りあげる等すれば作れるかも知れませんが、
一般のWebエンジニアはxreaにしがみつく意味も無いので、初手適したサービスに移住します。
というか、CGIゲームとかで負荷が高すぎてアカウント剥奪して追い出されるみたいな事はよくあったので、
無理やりなWebのビデオチャットを作ったら高負荷で追い出されるかも知れないのでパフォーマンスチューニングも必要ですね。
他人は何もアドバイス出来ない修羅の道になるでしょうが頑張ってください。

投稿2022/03/27 21:38

miyabi-sun

総合スコア21158

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

phpsyoshinsya

2022/03/27 22:11

なるほど、こんなにきれいに順序だててダメな理由を教えてもらえて、感謝です。 アパッチが動いてないサーバーって、なんて検索したら見つかりますか? そのサービスを使うとき、httpでアクセスして、どんな流れでビデオと音声のやり取りを開始できるようになるのか、概念を教えてもらうことはできますか? WEBサーバーがなくて、どうやってhttpリクエストを処理するのか、イメージができないです。 よろしくお願いします。
hosh

2022/04/08 06:48

関係のない人が首を突っ込んで申し訳ないですが、簡単に説明すると > アパッチが動いてないサーバーって、なんて検索したら見つかりますか? このようなサービスは「VPS」や「クラウドサーバー」などと呼ばれるものが一般的かと思います。無料で提供しているところは見たことがありませんね… その上にApacheをインストールすればWebサーバーになりますし、Node.jsをインストールすれば質問者さんがおっしゃっていることも可能かと思います。 本格的に運用しないのであれば、わざわざサーバーを借りなくても自分の持っているPCで何とかなります。ただWindowsのPCだとコマンドが違ったりするので、Linuxの勉強にもなるラズパイもおすすめです。 VPSについての解説はここら辺がわかりやすいです [ネコでもわかる!さくらのVPS講座](https://knowledge.sakura.ad.jp/7938/) > そのサービスを使うとき、httpでアクセスして、どんな流れでビデオと音声のやり取りを開始できるようになるのか、概念を教えてもらうことはできますか? 自分はWebRTCは全くと言っていいほどわからないので答えられませんが、「WebRTC P2P」とかで検索すれば概念はたくさん出てきます。ただシンプルにhttpでアクセスしたいのであれば、先述のとおりApacheをインストールすればできます。(これだけならXREAでも可能ですが…) 全く分からない自分が言うのもなんですが、そもそもP2Pはサーバーレス(処理を行うサーバーが要らない)の方式のはずですので、質問者さんが言うNode.jsがどこらへんで必要になるのかが自分にはわかりません…。単にブラウザでJavaScriptを動かしたいだけであれば、普通Node.jsは必要ないはずです。 > WEBサーバーがなくて、どうやってhttpリクエストを処理するのか、イメージができないです。 まとめると、サーバーがなければhttpリクエストは処理できません。なので“アパッチが動いてないサーバー”にWebサーバーを自分でインストールして使うんです(ややこしくてすみません)こうすることで、普通のレンタルサーバーではできない設定やカスタマイズをすることができます。 簡単に言うとNode.jsもWebサーバーの一種なので、Apacheを入れずにNode.jsをインストールすることでmiyabi-sunさんがおっしゃっていた“TCP80番を専有するApacheと被ってしまう事態”を回避できます。 自分も曖昧な理解ですが、大まかな概念はこんな感じかと 長くなってしまいましたが参考になると幸いです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問