22. video chat - hpscript/laravel GitHub Wiki
$ php composer.phar create-project --prefer-dist laravel/laravel videochat
$ cp composer.phar videochat/
$ cd videochat
$ php composer.phar require laravel/ui
$ php artisan ui vue --auth
$ php artisan migrate
$ npm install
$ npm run dev
// https環境の作成
$ php composer.phar require --dev mpyw/php-hyper-builtin-server:^2.0
// seeder
$ php artisan make:seed UsersTableSeeder
public function run()
{
$names = [
'taro' => '太郎',
'jiro' => '二郎',
'saburo' => '三郎',
'shiro' => '四郎',
'goro' => '五郎',
'rokuro' => '六郎'
];
foreach($names as $name_en => $name_jp){
\App\User::create([
'name' => $name_jp,
'email' => $name_en . '@gmail.com',
'password' => bcrypt('password')
]);
}
}
DatabaseSeeder.php
public function run()
{
// $this->call(UsersTableSeeder::class);
$this->call(UsersTableSeeder::class);
}
$ php composer.phar dump-autoload
$ php artisan db:seed
$ npm install simple-peer --save-dev
$ npm install pusher-js --save-dev
bootstrap.jsはjsモジュールの読み込み
window.Peer = require('simple-peer');
window.Pusher = require('pusher-js');
app.jsは、bootstrap.jsの読み込み
vue.jsはbladeで使うので、コメントアウト
// const app = new Vue({
// el: '#app',
// });
$ npm run dev
$ php composer.phar require pusher/pusher-php-server
.env
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=ap3
Route::group(['middleware' => 'auth'], function(){
Route::get('video_chat', 'VideoChatController@index');
Route::get('auth/video_chat', 'VideoChatController@auth');
});
public function index(Request $request)
{
//
$user = $request->user();
$others = \App\User::where('id', '!=', $user->id)->pluck('name', 'id');
return view('video_chat.index')->with([
'user' => collect($request->user()->only(['id', 'name'])),
'others' => $others
]);
}
public function auth(Request $request){
$user = $request->user();
$socket_id = $request->socket_id;
$channel_name = $request->channel_name;
$pusher = new Pusher(
config('broadcasting.connections.pusher.key'),
config('broadcasting.connections.pusher.secret'),
config('broadcasting.connections.pusher.app_id'),
[
'cluster' => config('broadcasting.connections.pusher.options.cluster'),
'encrypted' => true
]
);
return response(
$pusher->presence_auth($channel_name, $socket_id, $user->id)
);
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="csrf-token" content="{{ csrf_token() }}">
<link href="/css/app.css" rel="stylesheet">
<title>Document</title>
<style>
video {
width: 100%;
}
</style>
</head>
<body>
<div id="app" class="container">
<h1 class="text-center">ビデオチャット</h1>
<br>
<div class="row">
<div class="col-12">
<div class="card" style="padding:15px;">
<div v-for="(name,userId) in others">
<a href="#" @click.prevent="startVideoChat(userId)">「@{{ name }}」さんと通話を開始する</a>
</div>
</div>
</div>
</div>
<br>
<div class="row">
<div class="col-5">
<div class="text-center">自分の映像</div>
<video ref="video-here" autoplay></video>
</div>
<div class="col-2 text-center">
⇔<br>
ビデオチャット
</div>
<div class="col-5">
<div class="text-center">相手の映像</div>
<video ref="video-there" autoplay></video>
</div>
</div>
</div>
<script src="/js/app.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
<script>
console.log({!! json_encode($others) !!});
var user = {!! json_encode($user) !!};
var others = {!! json_encode($others) !!};
new Vue({
el: '#app',
data: {
pusher: {
key: "{{ config('broadcasting.connections.pusher.key') }}",
cluster: "{{ config('broadcasting.connections.pusher.options.cluster') }}"
},
user: user,
others: others,
channel: null,
stream: null,
peers: {}
},
methods: {
startVideoChat(userId){
this.getPeer(userId, true);
},
getPeer(userId, initiator){
if(this.peers[userId] == undefined){
let peer = new Peer({
initiator,
stream: this.stream,
trickle: false
});
peer.on('signal', (data)=>{
this.channel.trigger('client-signal-'+userId, {
userId: this.user.id,
data: data
});
})
.on('stream', (stream) => {
const videoThere = this.$refs['video-there'];
videoThere.srcObject = stream;
})
.on('close', () =>{
const peer = this.peers[userId];
if(peer !== undefined){
peer.destroy();
}
delete this.peers[userId];
});
this.peers[userId] = peer;
}
return this.peers[userId];
}
},
mounted(){
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
.then((stream) => {
const videoHere = this.$refs['video-here'];
videoHere.srcObject = stream;
this.stream = stream;
// pusher準備
const pusher = new Pusher(this.pusher.key, {
authEndPoint: '/auth/video_chat',
cluster: this.pusher.cluster,
auth: {
headers: {
'X-CSRF-Token': document.head.querySelector('meta[name="csrf-token"]').content
}
}
});
this.channel = pusher.subscribe('presence-video-chat');
this.channel.bind('client-signal-'+ this.user.id, (signal) => {
const userId = signal.userId;
const peer = this.getPeer(userId, false);
peer.signal(signal.data);
});
});
}
});
</script>
</body>
</html>