Introduction:
Hello, fellow developers. What’s up?, I hope you are doing fine. In this extended tutorial, we will learn how to use agora web sdk with laravel. We will discuss the walkthrough of the agora web sdk installation so you can follow it easily.
What is Agora?
Agora is a real-time technology provider. They work with real-time engagement, offering developers easy, flexible, and powerful APIs for integrating real-time audio, video, interactive streaming, chat, and artificial intelligence capabilities into their apps.
Fresh Laravel Installation for Agora Web SDK:
First thing first, we need to download a fresh copy of the laravel application. Please follow the below command to download the laravel application from GitHub.
composer create-project laravel/laravel agora-laravel-app
Install Laravel UI for Frontend Scafolding:
Now, we also need Laravel UI for our front-end scaffolding. In this case, our frontend preset will be vue. First, go to your project root directory and open a new terminal, and copy & paste the following command to download Laravel UI into our machine.
composer require laravel/ui
after the Laravel UI installation, you need to install the vue front-end scaffolding using the below command.
php artisan ui vue --auth
after the above command, you have to use more two commands to install javascript dependencies. Please copy & paste the following command and press enter from your keyboard.
npm install
npm run dev
Our front-end scaffolding setup is complete. Now take a look at our next task.
Pusher Account Setup:
First, You have to create a free pusher account. Then we have to install two packages into our laravel project. Please copy & paste the following commands and execute them one by one into your terminal.
composer require pusher/pusher-php-server
npm install --save laravel-echo pusher-js
Configuring Our Web.php File:
At this stage, you have to configure your web.php file. Copy & paste the following code into your file and save it.
Route::group(['middleware' => ['auth']], function () {
Route::get('/agora-chat', ['App\Http\Controllers\AgoraVideoController', 'index']);
Route::post('/agora/token', ['App\Http\Controllers\AgoraVideoController','token');
Route::post('/agora/call-user', ['App\Http\Controllers\AgoraVideoController','callUser');
});
Enabling Laravel Broadcasting Feature:
Now, we need to enable the laravel broadcast feature by just simply uncommenting a line from config>app.php.
App\Providers\BroadcastServiceProvider::class, //uncomment this line of code.
Creating a Channel for Our Video Calling Feature:
Next, we need to create a broadcast channel for our video calling system. Just simply copy& paste the below line of code inside of routes>channels.php file.
Broadcast::channel('agora-online-channel', function ($user) {
return ['id' => $user->id, 'name' => $user->name];
});
Creating an Event for Making Video Calls:
Now, we will create an event for our video call. For that, you have to use the below command in your terminal and create an event file.
php artisan make:event MakeAgoraCall
Now, copy & and paste the following code into your app>events>MakeAgoraCall.php file.
<?php
namespace App\Events;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class MakeAgoraCall implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $data;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct($data)
{
$this->data = $data;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PresenceChannel('agora-online-channel');
}
}
Creating Custom Service for AgoraDynamicKey:
First of all, you have to download AccessToken.php & RtcTokenBuilder.php files from this GitHub repository into your location machine.
Now, create an AgoraDynamicKey folder inside the app folder of your application. Now copy those downloaded files which are AccessToken.php & RtcTokenBuilder.php files into the AgoraDynamicKey folder. Next add the below line of code into those downloaded files so that we can access those files into our controller.
namespace App\AgoraDynamicKey;
Creating Controller Video Calling Feature:
Now, We will create our controller for our video calling feature. Please copy & paste the below command into your terminal and hit enter from your keyboard.
php artisan make:controller AgoraVideoController
After that, copy & paste the below code into your created controller.
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Classes\AgoraDynamicKey\RtcTokenBuilder;
use App\Events\MakeAgoraCall;
class AgoraVideoController extends Controller
{
public function index(Request $request)
{
// fetch all users apart from the authenticated user
$users = User::where('id', '<>', Auth::id())->get();
return view('agora-chat', ['users' => $users]);
}
public function token(Request $request)
{
$appID = env('AGORA_APP_ID');
$appCertificate = env('AGORA_APP_CERTIFICATE');
$channelName = $request->channelName;
$user = Auth::user()->name;
$role = RtcTokenBuilder::RoleAttendee;
$expireTimeInSeconds = 3600;
$currentTimestamp = now()->getTimestamp();
$privilegeExpiredTs = $currentTimestamp + $expireTimeInSeconds;
$token = RtcTokenBuilder::buildTokenWithUserAccount($appID, $appCertificate, $channelName, $user, $role, $privilegeExpiredTs);
return $token;
}
public function callUser(Request $request)
{
$data['userToCall'] = $request->user_to_call;
$data['channelName'] = $request->channel_name;
$data['from'] = Auth::id();
broadcast(new MakeAgoraCall($data))->toOthers();
}
}
Configuring Our Front-end for Video Calling:
First of all, copy & paste the below script tag into the head section of your resources>views>layouts>app.blade.php file.
<script src="https://cdn.agora.io/sdk/release/AgoraRTCSDK-3.3.1.js"></script>
Next, you have to uncomment the below codes from your resources>js>bootstrap.js file.
//Uncomment the whole code snippet
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
window.Pusher = Pusher;
window.Echo = new Echo({
broadcaster: 'pusher',
key: import.meta.env.VITE_PUSHER_APP_KEY,
cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER ?? 'mt1',
wsHost: import.meta.env.VITE_PUSHER_HOST ?? `ws-${import.meta.env.VITE_PUSHER_APP_CLUSTER}.pusher.com`,
wsPort: import.meta.env.VITE_PUSHER_PORT ?? 80,
wssPort: import.meta.env.VITE_PUSHER_PORT ?? 443,
forceTLS: (import.meta.env.VITE_PUSHER_SCHEME ?? 'https') === 'https',
enabledTransports: ['ws', 'wss'],
});
Creating Our Our First Vue Component for AgoraChat:
In this stage, we will create an AgoraChat.vue component in the resources/js/components directory. After that copy & paste the following code snippet in that file.
<template>
<main>
<div class="container">
<div class="row">
<div class="col-12 text-center">
<img src="img/agora-logo.png" alt="Agora Logo" class="img-fluid" />
</div>
</div>
</div>
<div class="container my-5">
<div class="row">
<div class="col">
<div class="btn-group" role="group">
<button
type="button"
class="btn btn-primary mr-2"
v-for="user in allusers"
:key="user.id"
@click="placeCall(user.id, user.name)"
>
Call {{ user.name }}
<span class="badge badge-light">{{
getUserOnlineStatus(user.id)
}}</span>
</button>
</div>
</div>
</div>
<!-- Incoming Call -->
<div class="row my-5" v-if="incomingCall">
<div class="col-12">
<p>
Incoming Call From <strong>{{ incomingCaller }}</strong>
</p>
<div class="btn-group" role="group">
<button
type="button"
class="btn btn-danger"
data-dismiss="modal"
@click="declineCall"
>
Decline
</button>
<button
type="button"
class="btn btn-success ml-5"
@click="acceptCall"
>
Accept
</button>
</div>
</div>
</div>
<!-- End of Incoming Call -->
</div>
<section id="video-container" v-if="callPlaced">
<div id="local-video"></div>
<div id="remote-video"></div>
<div class="action-btns">
<button type="button" class="btn btn-info" @click="handleAudioToggle">
{{ mutedAudio ? "Unmute" : "Mute" }}
</button>
<button
type="button"
class="btn btn-primary mx-4"
@click="handleVideoToggle"
>
{{ mutedVideo ? "ShowVideo" : "HideVideo" }}
</button>
<button type="button" class="btn btn-danger" @click="endCall">
EndCall
</button>
</div>
</section>
</main>
</template>
<script>
export default {
name: "AgoraChat",
props: ["authuser", "authuserid", "allusers", "agora_id"],
data() {
return {
callPlaced: false,
client: null,
localStream: null,
mutedAudio: false,
mutedVideo: false,
userOnlineChannel: null,
onlineUsers: [],
incomingCall: false,
incomingCaller: "",
agoraChannel: null,
};
},
mounted() {
this.initUserOnlineChannel();
this.initUserOnlineListeners();
},
methods: {
/**
* Presence Broadcast Channel Listeners and Methods
* Provided by Laravel.
* Websockets with Pusher
*/
initUserOnlineChannel() {
this.userOnlineChannel = window.Echo.join("agora-online-channel");
},
initUserOnlineListeners() {
this.userOnlineChannel.here((users) => {
this.onlineUsers = users;
});
this.userOnlineChannel.joining((user) => {
// check user availability
const joiningUserIndex = this.onlineUsers.findIndex(
(data) => data.id === user.id
);
if (joiningUserIndex < 0) {
this.onlineUsers.push(user);
}
});
this.userOnlineChannel.leaving((user) => {
const leavingUserIndex = this.onlineUsers.findIndex(
(data) => data.id === user.id
);
this.onlineUsers.splice(leavingUserIndex, 1);
});
// listen to the incoming call
this.userOnlineChannel.listen("MakeAgoraCall", ({ data }) => {
if (parseInt(data.userToCall) === parseInt(this.authuserid)) {
const callerIndex = this.onlineUsers.findIndex(
(user) => user.id === data.from
);
this.incomingCaller = this.onlineUsers[callerIndex]["name"];
this.incomingCall = true;
//The channel that was sent over to the user being called is what
// the receiver will use to join the call when accepting the call.
this.agoraChannel = data.channelName;
}
});
},
getUserOnlineStatus(id) {
const onlineUserIndex = this.onlineUsers.findIndex(
(data) => data.id === id
);
if (onlineUserIndex < 0) {
return "Offline";
}
return "Online";
},
async placeCall(id, calleeName) {
try {
// channelName = the caller's and the callee's id. you can use anything. tho.
const channelName = `${this.authuser}_${calleeName}`;
const tokenRes = await this.generateToken(channelName);
// Broadcasts a call event to the callee and also gets back the token
await axios.post("/agora/call-user", {
user_to_call: id,
username: this.authuser,
channel_name: channelName,
});
this.initializeAgora();
this.joinRoom(tokenRes.data, channelName);
} catch (error) {
console.log(error);
}
},
async acceptCall() {
this.initializeAgora();
const tokenRes = await this.generateToken(this.agoraChannel);
this.joinRoom(tokenRes.data, this.agoraChannel);
this.incomingCall = false;
this.callPlaced = true;
},
declineCall() {
// You can send a request to the caller to
// alert them of rejected call
this.incomingCall = false;
},
generateToken(channelName) {
return axios.post("/agora/token", {
channelName,
});
},
/**
* Agora Events and Listeners
*/
initializeAgora() {
this.client = AgoraRTC.createClient({ mode: "rtc", codec: "h264" });
this.client.init(
this.agora_id,
() => {
console.log("AgoraRTC client initialized");
},
(err) => {
console.log("AgoraRTC client init failed", err);
}
);
},
async joinRoom(token, channel) {
this.client.join(
token,
channel,
this.authuser,
(uid) => {
console.log("User " + uid + " join channel successfully");
this.callPlaced = true;
this.createLocalStream();
this.initializedAgoraListeners();
},
(err) => {
console.log("Join channel failed", err);
}
);
},
initializedAgoraListeners() {
// Register event listeners
this.client.on("stream-published", function (evt) {
console.log("Publish local stream successfully");
console.log(evt);
});
//subscribe remote stream
this.client.on("stream-added", ({ stream }) => {
console.log("New stream added: " + stream.getId());
this.client.subscribe(stream, function (err) {
console.log("Subscribe stream failed", err);
});
});
this.client.on("stream-subscribed", (evt) => {
// Attach remote stream to the remote-video div
evt. stream.play("remote-video");
this.client.publish(evt.stream);
});
this.client.on("stream-removed", ({ stream }) => {
console.log(String(stream.getId()));
stream.close();
});
this.client.on("peer-online", (evt) => {
console.log("peer-online", evt.uid);
});
this.client.on("peer-leave", (evt) => {
var uid = evt.uid;
var reason = evt.reason;
console.log("remote user left ", uid, "reason: ", reason);
});
this.client.on("stream-unpublished", (evt) => {
console.log(evt);
});
},
createLocalStream() {
this.localStream = AgoraRTC.createStream({
audio: true,
video: true,
});
// Initialize the local stream
this.localStream.init(
() => {
// Play the local stream
this.localStream.play("local-video");
// Publish the local stream
this.client.publish(this.localStream, (err) => {
console.log("publish local stream", err);
});
},
(err) => {
console.log(err);
}
);
},
endCall() {
this.localStream.close();
this.client.leave(
() => {
console.log("Leave channel successfully");
this.callPlaced = false;
},
(err) => {
console.log("Leave channel failed");
}
);
},
handleAudioToggle() {
if (this.mutedAudio) {
this.localStream.unmuteAudio();
this.mutedAudio = false;
} else {
this.localStream.muteAudio();
this.mutedAudio = true;
}
},
handleVideoToggle() {
if (this.mutedVideo) {
this.localStream.unmuteVideo();
this.mutedVideo = false;
} else {
this.localStream.muteVideo();
this.mutedVideo = true;
}
},
},
};
</script>
<style scoped>
main {
margin-top: 50px;
}
#video-container {
width: 700px;
height: 500px;
max-width: 90vw;
max-height: 50vh;
margin: 0 auto;
border: 1px solid #099dfd;
position: relative;
box-shadow: 1px 1px 11px #9e9e9e;
background-color: #fff;
}
#local-video {
width: 30%;
height: 30%;
position: absolute;
left: 10px;
bottom: 10px;
border: 1px solid #fff;
border-radius: 6px;
z-index: 2;
cursor: pointer;
}
#remote-video {
width: 100%;
height: 100%;
position: absolute;
left: 0;
right: 0;
bottom: 0;
top: 0;
z-index: 1;
margin: 0;
padding: 0;
cursor: pointer;
}
.action-btns {
position: absolute;
bottom: 20px;
left: 50%;
margin-left: -50px;
z-index: 3;
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
#login-form {
margin-top: 100px;
}
</style>
Registering Our AgoraChat Component:
Now, copy & paste the following line into your resources/js/app.js to register our AgoraChat component.
Vue.component("agora-chat",require("./components/AgoraChat.vue").default);
Creating View File for Agora Chat:
Now, we will create an agora-chat.blade.php file inside the resources>views folder. After that copy & paste the below following code into that file.
@extends('layouts.app')
@section('content')
<agora-chat :allusers="{{ $users }}" authuserid="{{ auth()->id() }}" authuser="{{ auth()->user()->name }}"
agora_id="{{ env('AGORA_APP_ID') }}" />
@endsection
Updating Our .env with Agora & Pusher all API Keys:
Now, we will update our .env file with Agora & Pusher API keys. Copy & paste the following code into the .env file.
BROADCAST_DRIVER=pusher
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=
AGORA_APP_ID=
AGORA_APP_CERTIFICATE=
TEST THE APPLICATION:
At last, we will start our local server using our artisan command. php artisan serve & npm run dev If there is no problem, our code will work fine, and the project will be running now at http://127.0.0.1:8000/ or http://locahost:8000/. You can know more from their documentation.
Conclusion:
Finished! That’s all about our tutorial. I’ve tried to teach you the most updated & tested things in this tutorial. Thank you so much for reading the whole tutorial from the beginning. If this tutorial helps you a little bit, then remember to share this post on social media. If you have questions, suggestions, or tips regarding this post, let us know via our Contact Us page.
FAQS REGARDING AGORA WEB SDK WITH LARAVEL:
What is Agora SDK?
Agora is a real-time technology provider. Agora helps you to integrate video calls and other technologies into your website or device more conveniently.
Is Agora free or paid?
Agora is kind of a freemium service provider. It means you can use Agora for your web application for the first 10,000 minutes after that, you must go to their subscription plans.