
Partager:
A Software Developer who is geared towards building high performing and innovative products following best practices and industry standards. He also loves writing about backend development.
Créer une classe en ligne avec Laravel et l'API Video de Vonage
Temps de lecture : 15 minutes
Cet article explique comment mettre en place une salle de classe virtuelle/vidéo en temps réel à l'aide de la fonction Video API de Vonage où l'enseignant peut se trouver n'importe où dans le monde et continuer à communiquer et à donner des cours avec l'étudiant en temps réel.
Qu'est-ce que l'API Video de Vonage ?
L'API Video de Vonage est un service qui permet aux développeurs de créer facilement des applications qui utilisent la technologie WebRTC et Web Sockets pour offrir une communication en temps réel par vidéo, texte, audio et autres pour les applications web, les applications mobiles et même les appareils de bureau. Ils fournissent des SDK puissants pour diverses plateformes à mettre en œuvre et donnent aux utilisateurs l'API à utiliser pour leurs applications.
Comment cela fonctionne-t-il ?
Toutes les applications qui utilisent l'API Video de Vonage sont composées de trois éléments principaux et tout ce que vous faites tourne autour d'eux. Il s'agit des éléments suivants :
Sessions
Serveur
Client
Examinons chacun d'entre eux un peu plus en détail :
Sessions
Une session peut être considérée comme le salon de discussion virtuel où se déroulent les communications. Pour que deux ordinateurs puissent échanger de la vidéo ou de l'audio, ils doivent être connectés à la même session. Les sessions sont hébergées/stockées dans le nuage de l'API de Vonage et elles ont toutes un identifiant unique. Les sessions gèrent les flux d'utilisateurs (flux vidéo et audio) et gardent la trace de tous les événements (par exemple, lorsque les utilisateurs rejoignent ou quittent la session ou envoient un message texte).
Serveur
Les serveurs sont le code backend que vous écrivez pour gérer les sessions dans votre application. Pour que la communication ait lieu, votre serveur doit communiquer avec le nuage de l'API Vonage pour créer une session et utiliser l'ID de session reçu pour générer des jetons que les clients (navigateurs et téléphones mobiles) peuvent ensuite utiliser pour rejoindre la session et échanger des flux. Pour faire fonctionner votre serveur, vous devez utiliser l'un des nombreux SDK côté serveur de l'API Vonage disponibles pour divers langages côté serveur.
Client
Les clients sont les navigateurs ou les appareils mobiles avec lesquels les utilisateurs interagissent directement pour échanger des flux vidéo et audio. Les clients ont toujours besoin d'un jeton (généré par votre serveur) pour rejoindre une session afin de pouvoir interagir avec d'autres clients au cours de la même session.
Lorsqu'un client est connecté, il peut publier (envoyer un flux vidéo/audio) et s'abonner (recevoir un flux audio/vidéo) à d'autres clients sur la même session. Un client peut être éditeur, abonné ou modérateur.
Les éditeurs sont autorisés à envoyer et à recevoir des flux audio/vidéo. Les abonnés ne sont pas autorisés à envoyer, mais ils reçoivent des flux audio/vidéo. Les modérateurs sont autorisés à faire ce que les éditeurs peuvent faire, mais ils peuvent aussi empêcher d'autres clients de s'abonner.
Pour résumer, voici ce qui se passe pour que la communication ait lieu lors de l'utilisation de l'API de Vonage :
Votre serveur crée une session sur le nuage API de Vonage qui possède un identifiant unique. Lorsqu'un client souhaite se joindre à la session, votre serveur utilise l'ID de la session pour générer un jeton pour votre client. Le client rejoint la session et publie des flux dans la session. Lorsqu'un autre client se joint à la session, le serveur génère un nouveau jeton et les deux clients peuvent s'abonner l'un à l'autre dans la session et recevoir leurs flux respectifs.
Configuration de notre classe en ligne avec l'API de Vonage
Dans ce tutoriel, nous allons mettre en place un flux vidéo de base avec l'API Vonage pour permettre aux clients d'envoyer des flux audio et vidéo. L'API Vonage fournit un SDK pour PHP et nous l'utiliserons pour créer notre classe virtuelle.
Mise en place d'un projet Laravel
La première étape de la création de notre classe virtuelle est la mise en place d'un projet Laravel.
Il est important de noter qu'au moment où nous écrivons ces lignes, l'API Vonage ne fonctionne pas avec la dernière version de Laravel. dernière version de Laravel* *(8.x). En effet, le SDK PHP de l'API Vonage dépend de la version 6 de GuzzleHTTP, mais Laravel 8 utilise la version 7 de GuzzleHTTP. Pour ce tutoriel, veuillez donc installer la version 7 de Laravel. Vous pouvez le faire en exécutant la commande install comme suit :
Vous pouvez également suivre le processus en clonant le dépôt ici.
Installation du SDK PHP de l'API de Vonage
L'étape suivante consiste à installer le SDK PHP de l'API Vonage. Pour ce faire, nous installons le paquet avec composer dans notre projet.
Définir nos migrations et nos modèles
Nous devons maintenant définir nos enseignants et nos étudiants afin qu'ils puissent disposer de différentes autorisations dans notre classe virtuelle. Notre application utilisera deux tables - users pour stocker tous les enseignants et étudiants et virtual_classes pour stocker les identifiants de session de nos classes afin que les étudiants puissent voir la classe en cours lorsqu'ils se connectent et la rejoignent. Voici les migrations pour les deux tables :
Ouvrez la migration des utilisateurs à l'adresse database/migrations/xxxxxx_create_users_xxxx.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateUsersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
// This enum field tells us if the current user is a teacher or student
$table->enum('user_type', ['Student', 'Teacher']);
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('users');
}
}Ouvrez la migration virtual_classes à l'adresse database/migrations/xxxxxx_create_virtual_classes_xxxx.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateVirtualClassesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('virtual_classes', function (Blueprint $table) {
$table->id();
$table->string("name");
// User id to know which teacher created the class
$table->integer("user_id"); `
$table->string("session_id");
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('classes');
}
}
Après avoir fait cela, nous devrons créer un dossier appelé Models dans le répertoire de l'application et y déplacer nos modèles. Ensuite, nous mettrons à jour l'espace de noms en App\Models sur nos modèles. Enfin, nous allons mettre à jour le modèle User pour ajouter user_type au tableau $fillable et créer une relation avec les classes virtuelles afin de pouvoir y accéder facilement.
Ouvrez le modèle User à app/Models/Users.php
//REMEMBER TO UPDATE THE NAMESPACE AFTER MOVING THE FILE
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array
*/
// DON'T FORGET TO ADD THE USER_TYPE TO THIS ARRAY
protected $fillable = [
'name', 'email', 'password', 'user_type'
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'password', 'remember_token',
];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
// Relationship tying a virtual class to a user (teacher in our case)
public function myClass() {
return $this->hasOne(VirtualClass::class);
}
} Authentification
Maintenant que nous avons mis en place l'architecture de notre modèle, nous devons permettre aux utilisateurs de s'inscrire et de se connecter. La première chose à faire est de modifier les fournisseurs dans la configuration de notre projet. Puisque nous avons changé l'emplacement du modèle User, nous devons refléter ce changement. Nous allons d'abord dans config/auth.php et éditons le tableau des fournisseurs et exécutons les commandes pour mettre en place l'authentification.
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class, // New location of User model
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
// Rest of auth.php fileAprès cela, nous lançons simplement l'échafaudage d'authentification par défaut de Laravel :
Après avoir exécuté les quatre commandes dans cet ordre, Laravel devrait ajouter les vues nécessaires pour configurer l'authentification. La dernière étape de la mise en place de notre authentification consiste à modifier le RegisterController et le fichier register.blade.php pour savoir si l'utilisateur est un étudiant ou un enseignant au moment de l'inscription.
Tout d'abord, le app/Http/Controllers/Auth/RegisterController dossier :
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
// PLEASE REMEMBER TO CHANGE THE IMPORT STATEMENT HERE
use App\Models\User;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
class RegisterController extends Controller
{
/*
|--------------------------------------------------------------------------
| Register Controller
|--------------------------------------------------------------------------
|
| This controller handles the registration of new users as well as their
| validation and creation. By default, this controller uses a trait to
| provide this functionality without requiring any additional code.
|
*/
use RegistersUsers;
/**
* Where to redirect users after registration.
*
* @var string
*/
protected $redirectTo = RouteServiceProvider::HOME;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
/**
* Get a validator for an incoming registration request.
*
* @param array $data
* @return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
// Add the user_type here for validation.
'user_type' => ['required', 'string', 'in:Student,Teacher'],
'password' => ['required', 'string', 'min:8', 'confirmed'],
]);
}
/**
* Create a new user instance after a valid registration.
*
* @param array $data
* @return \App\User
*/
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'user_type' => $data['user_type'],
'password' => Hash::make($data['password']),
]);
}
}
Ensuite, la vue du registre. Elle se trouve dans resources/views/auth/register.blade.php:
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Register') }}</div>
<div class="card-body">
<form method="POST" action="{{ route('register') }}">
@csrf
<div class="form-group row">
<label for="name" class="col-md-4 col-form-label text-md-right">{{ __('Name') }}</label>
<div class="col-md-6">
<input id="name" type="text" class="form-control @error('name') is-invalid @enderror" name="name" value="{{ old('name') }}" required autocomplete="name" autofocus>
@error('name')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row">
<label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email">
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row">
<label for="user_type" class="col-md-4 col-form-label text-md-right">{{ __('User Type') }}</label>
{{-- Note the select box here???--}}
<div class="col-md-6">
<select id="user_type" type="text" class="form-control @error('user_type') is-invalid @enderror" name="user_type" required>
<option value="Student">Student</option>
<option value="Teacher">Teacher</option>
</select>
@error('user_type')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row">
<label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row">
<label for="password-confirm" class="col-md-4 col-form-label text-md-right">{{ __('Confirm Password') }}</label>
<div class="col-md-6">
<input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Register') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
Ceci fait, nous devrions pouvoir nous inscrire et nous connecter en tant qu'enseignant ou étudiant.
Mise en place de notre serveur
Avant de pouvoir configurer votre serveur, vous avez besoin d'une clé et d'un secret API de projet. Pour cela, vous devez créer un Account API Vonage gratuit. Veuillez le faire en cliquant sur ce lien. Une fois que vous aurez créé un projet, n'hésitez pas à revenir et à continuer :)
Tout d'abord, stockons notre clé API et notre secret API dans notre fichier .env dans notre projet Laravel :
### ADD THESE LINES AT THE BOTTOM OF YOUR .env FILE, OR WHEREVER REALLY ###
VONAGE_API_KEY=your_api_key
VONAGE_API_SECRET=your_api_secret
### REST OF .ENV FILE ###Maintenant, nous allons devoir mettre en place un contrôleur pour créer des sessions et générer des jetons pour les nouveaux utilisateurs. Créons-en un et appelons-le SessionsController :
Remplissons-le avec les méthodes nécessaires en app/Http/Controllers/SessionsController.php:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\VirtualClass;
#Import necessary classes from the Vonage API (AKA OpenTok)
use OpenTok\OpenTok;
use OpenTok\MediaMode;
use OpenTok\Role;
class SessionsController extends Controller
{
/** Creates a new virtual class for teachers
*
* @param Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function createClass(Request $request)
{
// Get the currently signed-in user
$user = $request->user();
// Throw 403 if student tries to create a class
if ($user->user_type === "Student") return back(403);
// Instantiate a new OpenTok object with our api key & secret
$opentok = new OpenTok(env('VONAGE_API_KEY'), env('VONAGE_API_SECRET'));
// Creates a new session (Stored in the Vonage API cloud)
$session = $opentok->createSession(array('mediaMode' => MediaMode::ROUTED));
// Create a new virtual class that would be stored in db
$class = new VirtualClass();
// Generate a name based on the name the teacher entered
$class->name = $user->name . "'s " . $request->input("name") . " class";
// Store the unique ID of the session
$class->session_id = $session->getSessionId();
// Save this class as a relationship to the teacher
$user->myClass()->save($class);
// Send the teacher to the classroom where real-time video goes on
return redirect()->route('classroom', ['id' => $class->id]);
}
public function showClassRoom(Request $request, $id)
{
// Get the currently authenticated user
$user = $request->user();
// Find the virtual class associated by provided id
$virtualClass = VirtualClass::findOrFail($id);
// Gets the session ID
$sessionId = $virtualClass->session_id;
// Instantiates new OpenTok object
$opentok = new OpenTok(env('VONAGE_API_KEY'), env('VONAGE_API_SECRET'));
// Generates token for client as a publisher that lasts for one week
$token = $opentok->generateToken($sessionId, ['role' => Role::PUBLISHER, 'expireTime' => time() + (7 * 24 * 60 * 60)]);
// Open the classroom with all needed info for clients to connect
return view('classroom', compact('token', 'user', 'sessionId'));
}
}
Lors de la création du jeton, nous pouvons définir le rôle que le client actuel peut avoir. Il peut s'agir d'un éditeur, d'un abonné ou d'un modérateur. Les rôles sont expliqués ci-dessus. Dans l'extrait de code ci-dessus, nous donnons à tous les utilisateurs le statut d'éditeur, ce qui signifie que tous les utilisateurs peuvent envoyer et recevoir des flux (flux vidéo et audio).
Nous devons créer un fichier classroom.blade.php dans notre dossier resources/views pour la méthode showClassroom méthode.
Nous devons également créer des routes pour le contrôleur. Nous ajouterons ces routes dans le fichier routes/web.php :
<?php
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return view('welcome');
});
Auth::routes();
Route::get('/home', 'HomeController@index')->name('home');
// Add this to your web.php file
// This line makes all routes in it to use the auth middleware, meaning only signed-in users can access these routes
Route::middleware('auth')->group(function () {
// This route creates classes for teachers
Route::post("/create_class", 'SessionsController@createClass')
->name('create_class');
// This route is used by both teachers and students to join a class
Route::get("/classroom/{id}", 'SessionsController@showClassRoom')
->where('id', '[0-9]+')
->name('classroom');
});
Enfin, dans notre tableau de bord, nous devons donner aux enseignants un point pour créer des classes et donner aux élèves la liste des classes à rejoindre. Pour ce faire, nous devons mettre à jour le HomeController et le fichier home.blade.php. C'est parti, j'espère que vous êtes toujours avec moi 🙂 .
Nous commencerons par le contrôleur app/Http/Controllers/HomeController :
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\ClassModel as VirtualClass;
class HomeController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
}
/**
* Show the application dashboard.
*
* @return \Illuminate\Contracts\Support\Renderable
*/
public function index(Request $request)
{
$user = $request->user();
$classes = [];
// If user is a student, give her a list of virtual classes
if ($user->user_type === "Student") {
$classes = VirtualClass::orderBy('name', 'asc')->get();
}
return view('home', compact('user', 'classes'));
}
}Suivant resources/views/home.blade.php:
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{$user->user_type}} {{ __('Dashboard') }}</div>
<div class="card-body">
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
@if($user->user_type === "Student")
<h3>These are the ongoing classes available on the system</h3>
@foreach($classes as $key=>$class)
<a href="{{route('classroom', ['id' => $class->id])}}">{{$key + 1}}. {{$class->name}}</a>
<br />
@endforeach
@else
<h4>Welcome {{$user->name}}. Fill the form below to create a class</h4>
<form method="POST" action="{{ route('create_class') }}">
@csrf
<div class="form-group row">
<label for="name" class="col-md-12 col-form-label">{{ __('Class Name') }}</label>
<div class="col-md-6">
<input id="name" type="text"
class="form-control @error('name') is-invalid @enderror" name="name"
value="{{ old('name') }}" required autocomplete="name" autofocus>
@error('name')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-6">
<button type="submit" class="btn btn-primary">
{{ __('Create Class') }}
</button>
</div>
</div>
</form>
@endif
</div>
</div>
</div>
</div>
</div>
@endsection
Il s'agit soit d'un formulaire permettant de créer une classe si l'utilisateur est un enseignant, soit d'une liste de classes virtuelles si l'utilisateur est un étudiant.
Mise en place de notre client et diffusion d'un flux vidéo-audio en direct
Afin de configurer le client (qui utiliserait le SDK web), nous devons créer un fichier blade appelé resources/views/classroom.blade.php pour faire correspondre la méthode showClassroom() de notre contrôleur de sessions. Ce fichier devra contenir un lien vers le CDN du SDK. Notre interface devrait ressembler à ceci :
<html>
<head>
<title> OpenTok Getting Started </title>
<style>
body, html {
background-color: gray;
height: 100%;
}
#videos {
position: relative;
width: 100%;
height: 100%;
margin-left: auto;
margin-right: auto;
}
#subscriber {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: 10;
}
#publisher {
position: absolute;
width: 360px;
height: 240px;
bottom: 10px;
left: 10px;
z-index: 100;
border: 3px solid white;
border-radius: 3px;
}
</style>
<script src="https://static.opentok.com/v2/js/opentok.min.js"></script>
</head>
<body>
<div id="videos">
<div id="subscriber"></div>
<div id="publisher"></div>
</div>
<script type="text/javascript">
var session;
var connectionCount = 0;
var apiKey = "{{env('VONAGE_API_KEY')}}";
var sessionId = "{{$sessionId}}";
var token = "{{$token}}";
var publisher;
function connect() {
// Replace apiKey and sessionId with your own values:
session = OT.initSession(apiKey, sessionId);
session.on("streamCreated", function (event) {
console.log("New stream in the session: " + event.stream.streamId);
session.subscribe(event.stream, 'subscriber', {
insertMode: 'append',
width: '100%',
height: '100%'
});
});
session.on({
connectionCreated: function (event) {
connectionCount++;
alert(connectionCount + ' connections.');
},
connectionDestroyed: function (event) {
connectionCount--;
alert(connectionCount + ' connections.');
},
sessionDisconnected: function sessionDisconnectHandler(event) {
// The event is defined by the SessionDisconnectEvent class
alert('Disconnected from the session.');
document.getElementById('disconnectBtn').style.display = 'none';
if (event.reason == 'networkDisconnected') {
alert('Your network connection terminated.')
}
}
});
var publisher = OT.initPublisher('publisher', {
insertMode: 'append',
width: '100%',
height: '100%'
}, error => {
if (error) {
alert(error.message);
}
});
// Replace token with your own value:
session.connect(token, function (error) {
if (error) {
alert('Unable to connect: ', error.message);
} else {
// document.getElementById('disconnectBtn').style.display = 'block';
alert('Connected to the session.');
connectionCount = 1;
if (session.capabilities.publish == 1) {
session.publish(publisher);
} else {
alert("You cannot publish an audio-video stream.");
}
}
});
}
connect();
</script>
</body>
</html>
Pour tester l'application, exécutez la commande suivante dans le répertoire racine du projet.
Ouvrez http://localhost:8000/ dans votre navigateur et cliquez sur le menu de connexion.
Si cela est fait correctement, vous devriez vous connecter avec un compte d'enseignant et un compte d'élève et échanger des flux audio et vidéo.
Log in with a teacher account and a student account and exchange audio and video feed
Lorsque vous êtes connecté à un étudiant, vous devriez voir cet écran.
Connected with a student
Conclusion
Les applications en temps réel sont extraordinaires. Elles rassemblent le monde, et aujourd'hui, alors que le monde s'éloigne de plus en plus, la demande pour ce type d'applications est élevée.
Travailler avec WebRTC et WebSockets pour la communication en temps réel peut être lourd, mais l'API de Vonage fournit des méthodes d'aide faciles pour un large éventail de cas d'utilisation. Cet article a démontré comment mettre en place un flux vidéo à l'aide de Laravel. Vous pouvez cloner le dépôt ici.