23. video chat with react - hpscript/laravel GitHub Wiki
# 前準備
$ php composer.phar create-project --prefer-dist laravel/laravel chatapp
$ cp composer.phar chatapp/
$ cd chatapp
// JavaScriptアプリケーションの構築に、Reactを使いたければ、VueのスカフォールドをReactのスカフォールドへ簡単に変更できる
// presetを実行すると、package.json, webpack.mix.jsなどが書き換えられる
$ php artisan preset --help
$ php artisan preset react
$ npm install
$ npm install --save simple-peer pusher-js
$ php composer.phar require pusher/pusher-php-server
$ touch database/database.sqlite
.env
DB_CONNECTION=sqlite
$ php artisan migrate
// make auth
$ php composer.phar require laravel/ui --dev
$ php artisan ui react --auth
$ npm install
$ npm run dev
$ npm run watch
class MediaHandler {
getPermissions(){
return new Promise((res, rej) => {
navigator.MediaDevices.getUserMedia({video: true, audio: true})
.then((stream) =>{
resolve(stream);
})
.catch(err => {
throw new Error('Unable to fetch stream ${err}');
})
});
}
}
import React from 'react';
import ReactDOM from 'react-dom';
function App() {
return (
<div className="app">This is my video chat</div>
);
}
export default App;
if (document.getElementById('app')) {
ReactDOM.render(<App />, document.getElementById('app'));
}
require('./components/App');
<body>
<div id="container">
// 省略
@section('content')
<div id="app"></div>
@endsection
import React from 'react';
import ReactDOM from 'react-dom';
import MediaHandler from '../MediaHandler';
import Pusher from 'pusher-js';
import peer from 'simple-peer';
const APP_KEY = '';
function App(){
constructor(props) {
super(props);
this.state = {
hasMedia: false,
otherUserId: null;
};
this.user = window.user;
this.user.stream = null;
this.peers = {};
this.mediaHandler = new MediaHandler();
this.setupPusher();
this.callTo = this.callTo.bind(this);
this.setupPusher = this.setupPusher.bind(this);
this.startPeer = this.startPeer.bind(this);
}
componentWillMount(){
this.mediaHandler.getPermissions()
.then((stream) => {
this.setState(hasMedia: true);
this.user.stream = stream;
try {
this.myVideo.srcObject = stream;
} catch(e){
this.myVideo.src = URL.createObjectURL(stream);
}
this.myVideo.play();
})
}
setupPusher(){
this.pusher = new Pusher(APP_KEY, {
authEndpoint: '/pusher/auth',
cluster: 'ap2',
auth: {
params: this.user.id,
headers: {
'X-CSRF-Token': window.csrfToken
}
}
});
this.channel = this.pusher.subscribe('presence-video-channel');
this.channel.bind('client-signal-${this.user.id}', (signal)=>{
let peer = this.peers[signal.userId];
// if peer is not already exists, we got an incomming call
if(peer == undefined){
this.setState({otherUserId: signal.userId});
peer = this.startPeer(signal.userId, false);
}
peer.signal(signal.data);
});
}
startPeer(userId, initiator = true){
const peer = new Peer({
initiator,
stream: this.user.stream,
trickle: false;
});
peer.on('signal', (data) => {
this.channel.trigger('client-signal-${userId}', {
type: 'signal',
userId: this.user.id,
data: data
});
});
peer.on('stream', (stream) => {
try {
this.userVideo.srcObject = stream;
} catch(e){
this.userVideo.src = URL.createObjectURL(stream);
}
this.userVideo.play();
});
peer.on('close', () => {
let peer = this.peers[userId];
if(peer == undefined){
peer.destroy();
}
this.peers[userId] = undefined;
});
return peer;
}
callTo(userId){
this.peers[userId] = this.startPeer(userId);
}
return (
<div className="app">
{[1,2,3,4].map((userId)=> {
return this.user.id !== userId ? <button key={userId} onClick={() => this.callTo(UserId)}>Call {userId}</button> : null;
})}
<div className="video-container">
<video className="my-video" ref={(ref) => {this.myVideo = ref;}}></video>
<video className="user-video" ref={(ref) => {this.userVideo = ref;}}></video>
</div>
</div>
);
}
export default App;
if (document.getElementById('app')) {
ReactDOM.render(<App />, document.getElementById('app'));
}
Route
Route::post('/pusher/auth', 'HomeController@authenticate');
Controller
use Pusher\Pusher;
public function authenticate(Request $request){
$socketId = $request->socket_id;
$channelName = $request->channel_name;
$pusher = new Pusher('App key', 'App Secrete', 'App id', [
'cluster' => 'ap2',
'encrypted' => true
]);
$presense_data = ['name' => auth()->user()->name];
$key = $pusher->presence_auth($channelName, $socketId, auth()->id(), $presence_data);
return response($key);
}