DEV Community

Sebastian Larrieu
Sebastian Larrieu

Posted on • Edited on

Video call with WebRTC Angular and ASP.NET Core

Hi everyone, in this post we will build a basic video call web using WebRTC,Angular and ASP.Net Core. Why? because this technologies are like read meat and malbec, their just pair well (I will write a post about this). This is going to be a very basic app with a lot of point for improvement but I will focus on basics.

WebRTC 'Supports video, voice, and generic data to be sent between peers', but as in any p2p system we need a signaling communication channel so users can discover each other, we will user singlaR for that.

The git repos are at the end of the post.

Lets begin with Backend

We only need a signaling server so create a .Net Core web api project and add singalR nuget package



dotnet new webapp -o signalRtc
cd signalRtc
dotnet add package Microsoft.AspNetCore.SignalR.Core


Enter fullscreen mode Exit fullscreen mode

You could remove the controller folder that is created.

Now we need a hub, as docs saids, a hub 'allows a client and server to call methods on each other', I like to think that we invoke methods from frontend and listen to event from backend. So create a hubs folder and a class that extends Hub.



using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;
using signalRtc.models;

namespace signalRtc.hubs
{
    public class SignalRtcHub : Hub
    {
    }
}



Enter fullscreen mode Exit fullscreen mode

There are 3 (or 4) basic actions that we need in order to make a video call app.

  1. Every new user need to notify they arrival.
  2. Existing users need to inform their presence to new user.
  3. Share signaling data between peers for WebRTC.
  4. Notify users when someone disconnect.

As I said, we are going to write task in the hub class that can be executed from the Angular project and send some data to frontend too.

First create a folder models with a UseInfo class to encapsulate user data



namespace signalRtc.models
{
    public class UserInfo
    {
        public string userName { get; set; }
        public string connectionId { get; set; }
    }
}


Enter fullscreen mode Exit fullscreen mode

The connectionId it's an unique identifier that signlarR give to each connected user so we can identify they, we will need it to send messages to that specific user.

Point #1



public async Task NewUser(string username)
{
    var userInfo = new UserInfo() { userName = username, connectionId = Context.ConnectionId };
    await Clients.Others.SendAsync("NewUserArrived", JsonSerializer.Serialize(userInfo));
}


Enter fullscreen mode Exit fullscreen mode

In this short piece of code we use many of the signalR great features. The task is the action that we can invoke from frontend, there we create a User object with the current user id that we get from Context. We don't want to receive our "hello I'm a new user" so we send the message to "others", that means all users connected to the hub but not me, signalR keep the list of user for us. The "NewUserArrived" string is an identifier that we will use later in frontend to receive this messages, you could write any name you like.

Point #2 When we get the "NewUser" message, we need to inform of our existence to the new user with a hello.



public async Task HelloUser(string userName, string user)
{
    var userInfo = new UserInfo() { userName = userName, connectionId = Context.ConnectionId };
    await Clients.Client(user).SendAsync("UserSaidHello", JsonSerializer.Serialize(userInfo));
}


Enter fullscreen mode Exit fullscreen mode

The task receive two parameters, the username of the user that is saying hello and the user that we are saluting. In order to send a message to one user, we use Clients.Client('connectionId')... In this case, we are sending a "UserSaidHello" event to frontend.

Point #3 is simple, we want to send our signal to a user to start a p2p videocall



public async Task SendSignal(string signal, string user)
{
    await Clients.Client(user).SendAsync("SendSignal", Context.ConnectionId, signal);
}


Enter fullscreen mode Exit fullscreen mode

Last but not essential, we want to inform to the group when a user disconnect, to do that we are going to override the onDisconnect task.



public override async Task OnDisconnectedAsync(System.Exception exception)
{
    await Clients.All.SendAsync("UserDisconnect", Context.ConnectionId);
    await base.OnDisconnectedAsync(exception);
}


Enter fullscreen mode Exit fullscreen mode

We are almost done with backend, we need to indicate to NetCore that we are using singlaR so in the Startup.cs add singalR in configureService (also cors for angular port) and an endpoint for the singalR communication



public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(options =>
    {
        options.AddPolicy(MyAllowSpecificOrigins,
            builder => builder.WithOrigins("http://localhost:4200", "https://localhost:4200")
            .AllowAnyMethod()
            .AllowAnyHeader()
            .AllowCredentials());
    });

    services.AddSignalR();
}

readonly string MyAllowSpecificOrigins = "AllowOrigins";


public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseRouting();
    app.UseCors(MyAllowSpecificOrigins);
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapHub<SignalRtcHub>("/signalrtc");
    });

    app.Run(async(context) =>
    {
        await context.Response.WriteAsync("Hello World!");
    });
}



Enter fullscreen mode Exit fullscreen mode

Lets move to Angular
First create a new Angular project and install few dependencies.
simple-peer (and its types, because we love static type checking) for WebRTC.
Bootrstap, because I know you could do everything with plain css and flex or grid, but I like bootstrap.
SignalR, to invoke the method we just wrote and receive the messages.
Remember to choose scss when cli ask.



ng new signalRtc
cd signalRtc
npm install simple-peer
npm install @types/simple-peer
npm install bootstrap
npm install @aspnet/signalr


Enter fullscreen mode Exit fullscreen mode

To add sytles, open styles.scss and add "@import '~bootstrap/dist/css/bootstrap.min.css';"

First lets create a service to manage the singalR messages.



cd src/app
ng g service signalr


Enter fullscreen mode Exit fullscreen mode

We need a private property to maintain the hub connection, we also need subjects and observables to encapsulate the events from signalR library. I like to have private subjects to emit values via methods and public observables to get data in components.



private hubConnection: signalR.HubConnection;

private newPeer = new Subject<UserInfo>();
public newPeer$ = this.newPeer.asObservable();

private helloAnswer = new Subject<UserInfo>();
public helloAnswer$ = this.helloAnswer.asObservable();

private disconnectedPeer = new Subject<UserInfo>();
public disconnectedPeer$ = this.disconnectedPeer.asObservable();

private signal = new Subject<SignalInfo>();
public signal$ = this.signal.asObservable();


Enter fullscreen mode Exit fullscreen mode

As you can see, we have an observable for every task and some interfaces that you could write in a separate file



export interface PeerData {
  id: string;
  data: any;
}

export interface UserInfo {
  userName: string;
  connectionId: string;
}

export interface SignalInfo {
  user: string;
  signal: any;
}


Enter fullscreen mode Exit fullscreen mode

Now we will write some methods, we need one to start a connection, one for response to the new user message and one to send the p2p signal

Start a connection



  public async startConnection(currentUser: string): Promise<void> {

    this.hubConnection = new signalR.HubConnectionBuilder()
      .withUrl('https://localhost:5001/signalrtc')
      .build();

    await this.hubConnection.start();
    console.log('Connection started');

    this.hubConnection.on('NewUserArrived', (data) => {
      this.newPeer.next(JSON.parse(data));
    });

    this.hubConnection.on('UserSaidHello', (data) => {
      this.helloAnswer.next(JSON.parse(data));
    });

    this.hubConnection.on('UserDisconnect', (data) => {
      this.disconnectedPeer.next(JSON.parse(data));
    });

    this.hubConnection.on('SendSignal', (user, signal) => {
      this.signal.next({ user, signal });
    });

    this.hubConnection.invoke('NewUser', currentUser);
  }


Enter fullscreen mode Exit fullscreen mode

As you can see from code, we start the connection and encapsulate the message events in observables, also when we start a connection we send the 'Hello I'm a new user' message. That's the two way communication with backend, via invoke we execute task and receive data with 'on'.

Now to say hello new user and send signal data, we only need to invoke hub methods



public sendSignalToUser(signal: string, user: string) {
  this.hubConnection.invoke('SendSignal', signal, user);
}

public sayHello(userName: string, user: string): void {
  this.hubConnection.invoke('HelloUser', userName, user);
}



Enter fullscreen mode Exit fullscreen mode

Now we are going to write a rtc service, similar to the signalR one, to encapsulate the simplee-peer events. We also need to maintain the list of connected users to start a conversation and the current peer we are talking to.



import { Injectable } from '@angular/core';
import { Observable, Subject, BehaviorSubject } from 'rxjs';
import { Instance } from 'simple-peer';

declare var SimplePeer: any;

@Injectable({
  providedIn: 'root'
})
export class RtcService {

  private users: BehaviorSubject<Array<UserInfo>>;
  public users$: Observable<Array<UserInfo>>;

  private onSignalToSend = new Subject<PeerData>();
  public onSignalToSend$ = this.onSignalToSend.asObservable();

  private onStream = new Subject<PeerData>();
  public onStream$ = this.onStream.asObservable();

  private onConnect = new Subject<PeerData>();
  public onConnect$ = this.onConnect.asObservable();

  private onData = new Subject<PeerData>();
  public onData$ = this.onData.asObservable();

  public currentPeer: Instance;

  constructor() {
    this.users = new BehaviorSubject([]);
    this.users$ = this.users.asObservable();
  }

}


Enter fullscreen mode Exit fullscreen mode

When a user arrives, we add it to the list, and when they disconnect, we remove it (always in an immutable way), let’s add some methods in service for that



 public newUser(user: UserInfo): void {
  this.users.next([...this.users.getValue(), user]);
}

public disconnectedUser(user: UserInfo): void {
  const filteredUsers = this.users.getValue().filter(x => x.connectionId === user.connectionId);
  this.users.next(filteredUsers);
}


Enter fullscreen mode Exit fullscreen mode

When we start a conversation, we need to create a peer instance. The simple peer library need and indication to know if we are initiating the p2p connection. It also provide some events that we will encapsulate (because we love observables). The most important event is 'signal', quoting the library description: 'Fired when the peer wants to send signaling data to the remote peer.

It is the responsibility of the application developer (that's you!) to get this data to the other peer' that's the whole point of signalR



  public createPeer(stream, userId: string, initiator: boolean): Instance {
    const peer = new SimplePeer({ initiator, stream });

    peer.on('signal', data => {
      const stringData = JSON.stringify(data);
      this.onSignalToSend.next({ id: userId, data: stringData });
    });

    peer.on('stream', data => {
      console.log('on stream', data);
      this.onStream.next({ id: userId, data });
    });

    peer.on('connect', () => {
      this.onConnect.next({ id: userId, data: null });
    });

    peer.on('data', data => {
      this.onData.next({ id: userId, data });
    });

    return peer;
  }


Enter fullscreen mode Exit fullscreen mode

Finally, we have a method to signal a peer Instance, this actions is required by simple-peer. We have to check if exist a current peer, if it don't, it means that we are not the initiator and we have to create it because someone is staring a videocall with us



public signalPeer(userId: string, signal: string, stream: any) {
    const signalObject = JSON.parse(signal);
    if (this.currentPeer) {
      this.currentPeer.signal(signalObject);
    } else {
      this.currentPeer = this.createPeer(stream, userId, false);
      this.currentPeer.signal(signalObject);
    }
  }

public sendMessage(message: string) {
  this.currentPeer.send(message);
}


Enter fullscreen mode Exit fullscreen mode

We also create a sendMessage for the chat part, because WebRTC allow to send binary data between peers. We could have used signalR but we only want it as a signaling server.

Now let's change app.component.htm, we will add an input for our username and a place for a chat. Following good practice, we will make a special component to manage the connected users list to keep a place for that.



<div class="container-fluid">
  <h1>SignalRTC</h1>
  <div class="row">
    <div class="col-5">
      <div class="row">
        <div class="col">
          <input [(ngModel)]="currentUser" required placeholder="UserName" type="text">
        </div>
        <div class="col">
          <div class="btn-group" role="group" aria-label="button group">
            <button [disabled]="!currentUser" (click)="saveUsername()" type="button"
              class="btn btn-sm btn-primary">Save</button>
          </div>
        </div>
      </div>
      <div class="row">
        <div class="col">
          <!-- user list component -->
        </div>
      </div>
    </div>
    <div class="col-7">
      <div class="row">
        <div class="col-8">
          <input [(ngModel)]="dataString" required placeholder="Write a message" type="text">
        </div>
        <div class="col-4">
          <button (click)="sendMessage()" type="button" class="btn btn-sm btn-secondary">Send</button>
        </div>
      </div>
    </div>
  </div>
</div>



Enter fullscreen mode Exit fullscreen mode

This is the list component,just a simple list where we can click an user in order to start a videocall, the component will emit an event so we can react in the app component.



<ul class="list-group mt-4">
  <li class="list-group-item" (click)="userClicked(user)" *ngFor="let user of users$ | async">
    {{user.userName}}
  </li>
</ul>


Enter fullscreen mode Exit fullscreen mode


 @Output() userSelected: EventEmitter<UserInfo> = new EventEmitter();

  public users$: Observable<Array<UserInfo>>;


  constructor(private rtcService: RtcService) { }

  ngOnInit() {
    this.users$ = this.rtcService.users$;
  }

  public userClicked(user: UserInfo) {
    this.userSelected.emit(user);
  }



Enter fullscreen mode Exit fullscreen mode

Now you can change the app.component.html and replace the comment for the actual list component.



<app-user-list (userSelected)="onUserSelected($event)"></app-user-list>


Enter fullscreen mode Exit fullscreen mode

In the component, we will use the OnInit method (in Angular we have to keep constructor clean) to suscribe to all the obserable we defined and disptach the actions we need.



ngOnInit() {
    this.subscriptions.add(this.signalR.newPeer$.subscribe((user: UserInfo) => {
      this.rtcService.newUser(user);
      this.signalR.sayHello(this.currentUser, user.connectionId);
    }));

    this.subscriptions.add(this.signalR.helloAnswer$.subscribe((user: UserInfo) => {
      this.rtcService.newUser(user);
    }));

    this.subscriptions.add(this.signalR.disconnectedPeer$.subscribe((user: UserInfo) => {
      this.rtcService.disconnectedUser(user);
    }));

    this.subscriptions.add(this.signalR.signal$.subscribe((signalData: SignalInfo) => {
      this.rtcService.signalPeer(signalData.user, signalData.signal, this.stream);
    }));

    this.subscriptions.add(this.rtcService.onSignalToSend$.subscribe((data: PeerData) => {
      this.signalR.sendSignalToUser(data.data, data.id);
    }));

    this.subscriptions.add(this.rtcService.onData$.subscribe((data: PeerData) => {
      console.log(`Data from user ${data.id}: ${data.data}`);
    }));

    this.subscriptions.add(this.rtcService.onStream$.subscribe((data: PeerData) => {
      this.userVideo = data.id;
      this.videoPlayer.nativeElement.srcObject = data.data;
      this.videoPlayer.nativeElement.load();
      this.videoPlayer.nativeElement.play();
    }));
  }



Enter fullscreen mode Exit fullscreen mode

Brief explanation of each step:

  1. When a user arrives, we add create a new user and send our hell message.
  2. When we receive a hello, we add the user to the list.
  3. When a user disconnect, we remove it from the list.
  4. When when receive a signal we execute the function .signal that it's indicated in the simple-peer documentation.
  5. When simple-peer indicate that the WeRTC signal is ready, we send it to the user.
  6. When we receive a message, we show it.
  7. Finally, when the videostream is ready, we show it in a video tag.

Finally, when we click a user, we create a new peer, this will eventually fire the signal event!
We also have the function to save the username and send a message via the data channel. Also unsubscribe always on the OnDestroy lifecycle hook, in this case we have only one component but following this practice we will prevent memory leaks.



public onUserSelected(userInfo: UserInfo) {
    const peer = this.rtcService.createPeer(this.stream, userInfo.connectionId, true);
    this.rtcService.currentPeer = peer;
  }

  public async saveUsername(): Promise<void> {
    try {
      await this.signalR.startConnection(this.currentUser);
      this.stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
    } catch (error) {
      console.error(`Can't join room, error ${error}`);
    }
  }

  public sendMessage() {
    this.rtcService.sendMessage(this.dataString);
    this.messages = [...this.messages, { own: true, message: this.dataString }];
    this.dataString = null;
  }
  ngOnDestroy() {
    this.subscriptions.unsubscribe();
  }


Enter fullscreen mode Exit fullscreen mode

Okey, that's all folks. As I told you at the beginning, this is a very simple starting point. We have a lot to improve, for example you could use signalR to ask to the other user if they want to accept the call! I wanted to keep it simple.

Frontend:

GitHub logo sebalr / signalrtc-frontend

WebRTC client example



Backend:

GitHub logo sebalr / signalrtc-backend

signalR backend server for webrtc signaling

Top comments (86)

Collapse
 
mammadkoma profile image
Mohammad Komaei • Edited

I downloaded your angular project from github and execute these commands in vscode terminal : npm install , npm start
but It shows error :

signal-rtc@0.0.0 start
ng serve

Unknown error: SyntaxError: Unexpected token 'export'
npm ERR! code 127
npm ERR! path D:\Projects\signalrtc-frontend-master
npm ERR! command failed
npm ERR! command C:\WINDOWS\system32\cmd.exe /d /s /c ng serve

npm ERR! A complete log of this run can be found in:
npm ERR! C:\Users\mammadkoma\AppData\Local\npm-cache_logs\2021-02-18T16_32_06_791Z-debug.log

Collapse
 
sebalr profile image
Sebastian Larrieu

Weird, try deleting node_modules and package.lock and run npm install again

Collapse
 
mammadkoma profile image
Mohammad Komaei

I did it. but it give us error :
D:\Projects\signalrtc-frontend-master>npm start

signal-rtc@0.0.0 start
ng serve

An unhandled exception occurred: Could not find module "@angular-devkit/build-angular" from "D:\Projects\signalrtc-frontend-master".
See "C:\Users\MAMMAD~1\AppData\Local\Temp\ng-ggaBwX\angular-errors.log" for further details.
npm ERR! code 127
npm ERR! path D:\Projects\signalrtc-frontend-master
npm ERR! command failed
npm ERR! command C:\WINDOWS\system32\cmd.exe /d /s /c ng serve

npm ERR! A complete log of this run can be found in:
npm ERR! C:\Users\mammadkoma\AppData\Local\npm-cache_logs\2021-02-18T16_38_33_449Z-debug.log

Thread Thread
 
mammadkoma profile image
Mohammad Komaei

after run npm update , it show this error now :
D:\Projects\signalrtc-frontend-master>npm start

signal-rtc@0.0.0 start
ng serve

Unknown error: SyntaxError: Unexpected token 'export'
npm ERR! code 127
npm ERR! path D:\Projects\signalrtc-frontend-master
npm ERR! command failed
npm ERR! command C:\WINDOWS\system32\cmd.exe /d /s /c ng serve

npm ERR! A complete log of this run can be found in:
npm ERR! C:\Users\mammadkoma\AppData\Local\npm-cache_logs\2021-02-18T16_48_13_596Z-debug.log

Thread Thread
 
sebalr profile image
Sebastian Larrieu

Install that dependency manually, maybe is missing from package.json

Thread Thread
 
mammadkoma profile image
Mohammad Komaei

After update nodejs and npm and angular and change these packages version it started:
"@angular-devkit/build-angular": "~0.1102.1",
"@angular/cli": "~11.2.1",
"@angular/compiler-cli": "~11.2.1",
"@angular/language-service": "~11.0.7",
"@types/jasmine": "~3.6.0",
"@types/jasminewd2": "~2.0.3",
"@types/node": "^12.11.1",
"codelyzer": "^6.0.0",
"jasmine-core": "~3.6.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "~6.1.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "~3.0.2",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.5.0",
"protractor": "~7.0.0",
"ts-node": "~8.3.0",
"tslint": "~6.1.0",
"typescript": "~4.1.2"

Thread Thread
 
mammadkoma profile image
Mohammad Komaei

I Started angular by command prompt and SignalRtc.exe (in signalrtc-backend-master\bin\Debug\netcoreapp3.1)
chrome and edge asked me to access camera and microphone and I clicked allow.
Text sends but voice and video not.
(my camera light on my laptop is on now)

Thread Thread
 
sebalr profile image
Sebastian Larrieu

If text is working it means connection between peers has been stablished. I'm not sure what your problems could be, there is any log on console.?

Thread Thread
 
mammadkoma profile image
Mohammad Komaei

text shows twice.
log in dotnetcore console :
Now listening on: localhost:5000
Now listening on: localhost:5001
Application started. Press Ctrl+C to shut down.
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
Request starting HTTP/2 OPTIONS localhost:5001/signalrtc/negotiate
info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4]
CORS policy execution successful.
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished in 36.3844ms 204
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
Request starting HTTP/2 POST localhost:5001/signalrtc/negotiate text/plain;charset=UTF-8 0
info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4]
CORS policy execution successful.
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
Executing endpoint '/signalrtc/negotiate'
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
Executed endpoint '/signalrtc/negotiate'
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished in 16.5687ms 200 application/json
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
Request starting HTTP/1.1 GET localhost:5001/signalrtc?id=sHQhov...
info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4]
CORS policy execution successful.
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
Executing endpoint '/signalrtc'
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
Request starting HTTP/2 OPTIONS localhost:5001/signalrtc/negotiate
info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4]
CORS policy execution successful.
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished in 2.0513ms 204
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
Request starting HTTP/2 POST localhost:5001/signalrtc/negotiate text/plain;charset=UTF-8 0
info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4]
CORS policy execution successful.
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
Executing endpoint '/signalrtc/negotiate'
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
Executed endpoint '/signalrtc/negotiate'
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished in 2.5617ms 200 application/json
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
Request starting HTTP/1.1 GET localhost:5001/signalrtc?id=LMcT0h...
info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4]
CORS policy execution successful.
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
Executing endpoint '/signalrtc'

Thread Thread
 
sebalr profile image
Sebastian Larrieu

I was asking for browser console.
You need to create nicknames on differents browsers and then in one, and only one, browser select the other user

Thread Thread
 
mammadkoma profile image
Mohammad Komaei

edge :
Uncaught TypeError: Cannot redefine property: BetterJsPop
at Function.defineProperty ()
at inject (:5:4295)
at :6:11
at :7:11
core.js:27701 Angular is running in development mode. Call enableProdMode() to enable production mode.
client:52 [WDS] Live Reloading enabled.
Utils.js:209 [2021-02-18T18:16:07.552Z] Information: WebSocket connected to wss://localhost:5001/signalrtc?id=LMcT0hNHw5mAjC1P3uc1nw.
signalr.service.ts:34 Connection started
app.component.ts:80 Can't join room, error NotReadableError: Could not start video source
(anonymous) @ app.component.ts:80
Utils.js:209 [2021-02-18T18:17:01.203Z] Information: WebSocket connected to wss://localhost:5001/signalrtc?id=A8Wi87etl-9F4qo6nh6EFw.
signalr.service.ts:34 Connection started
app.component.ts:80 Can't join room, error NotReadableError: Could not start video source
(anonymous) @ app.component.ts:80
Utils.js:203 [2021-02-18T18:19:11.011Z] Error: Connection disconnected with error 'Error: WebSocket closed with status code: 1006 ().'.
push.yfcC.ConsoleLogger.log @ Utils.js:203
Utils.js:203 [2021-02-18T18:19:11.012Z] Error: Connection disconnected with error 'Error: WebSocket closed with status code: 1006 ().'.
push.yfcC.ConsoleLogger.log @ Utils.js:203

chrome :
Uncaught TypeError: Cannot redefine property: BetterJsPop
at Function.defineProperty ()
at inject (:5:4295)
at :6:11
at :7:11
core.js:27701 Angular is running in development mode. Call enableProdMode() to enable production mode.
contentscript.js:58 ​…​​
client:52 [WDS] Live Reloading enabled.
Utils.js:209 [2021-02-18T18:16:00.238Z] Information: WebSocket connected to wss://localhost:5001/signalrtc?id=sHQhovh_QYbqS7kkCHzolw.
signalr.service.ts:34 Connection started
Utils.js:203 [2021-02-18T18:19:11.008Z] Error: Connection disconnected with error 'Error: WebSocket closed with status code: 1006 ().'.

Thread Thread
 
sebalr profile image
Sebastian Larrieu

There is an error in the websoket connection as you could see in last line. You will have to Google that, it may be related to security in your browser

Thread Thread
 
mammadkoma profile image
Mohammad Komaei

oops , I must click on a text in list , now I have see my video

Thread Thread
 
mammadkoma profile image
Mohammad Komaei

but after publish back and front on a linux server it not working!

Thread Thread
 
sebalr profile image
Sebastian Larrieu

Is hard to tell without a little more context. In order to work you must have ssl on your servers. Also you may have to enable wss communication, it may be a nat error or any other things

Thread Thread
 
mammadkoma profile image
Mohammad Komaei

After publish on windows server 2012 and iis 8 , error on chrome console :

WebSocket connection to 'ws://5.160.146.56/sockjs-node/182/aa2hoasq/websocket' failed: Error during WebSocket handshake: Unexpected response code: 200
WebSocketBrowserDriver @ sockjs.js:1684
WebSocketTransport @ sockjs.js:2959
SockJS._connect @ sockjs.js:829
SockJS._receiveInfo @ sockjs.js:803
g @ sockjs.js:66
EventEmitter.emit @ sockjs.js:86
(anonymous) @ sockjs.js:567
g @ sockjs.js:66
EventEmitter.emit @ sockjs.js:86
(anonymous) @ sockjs.js:374
g @ sockjs.js:66
EventEmitter.emit @ sockjs.js:86
xhr.onreadystatechange @ sockjs.js:1598
wrapFn @ zone-evergreen.js:1218
invokeTask @ zone-evergreen.js:399
runTask @ zone-evergreen.js:167
invokeTask @ zone-evergreen.js:480
invokeTask @ zone-evergreen.js:1621
globalZoneAwareCallback @ zone-evergreen.js:1647
client:169 Invalid Host/Origin header
error @ client:169
(anonymous) @ socket.js:47
sock.onmessage @ SockJSClient.js:67
EventTarget.dispatchEvent @ sockjs.js:170
(anonymous) @ sockjs.js:888
SockJS._transportMessage @ sockjs.js:886
EventEmitter.emit @ sockjs.js:86
(anonymous) @ sockjs.js:2203
EventEmitter.emit @ sockjs.js:86
(anonymous) @ sockjs.js:2148
EventEmitter.emit @ sockjs.js:86
EventSourceReceiver.es.onmessage @ sockjs.js:2251
client:172 [WDS] Disconnected!

Thread Thread
 
mammadkoma profile image
Mohammad Komaei

Publish on win server 2012 and iis 8 , chrome console :
WebSocket connection to 'ws://5.160.146.56/sockjs-node/182/aa2hoasq/websocket' failed: Error during WebSocket handshake: Unexpected response code: 200

client:169 Invalid Host/Origin header

client:172 [WDS] Disconnected!

Ok , Thanks I will add https and test it.

Collapse
 
vipul83 profile image
vipul83

Hi.. Its a great post...

I m getting "currentpeer" (from rtc.service.ts ) object as undefined when i click on "send" button. so far my camera is activated but no video and chats are coming. any suggestion what i am missing? PFA also

Collapse
 
sebalr profile image
Sebastian Larrieu

You have to click a username to establish s connection before sending data

Collapse
 
vipul83 profile image
vipul83 • Edited

Hello Sebastian,

I clicked on user from userlist shown , currentpeer object is initialized though, but still video is not activated, PFA. I found that "connected" property of currentpeer object is set to "false". please suggest wat i m missing.

Thread Thread
 
vipul83 profile image
vipul83

I am getting attached exception when click on send message. Please note i m the only user who is running this application on my local machine. or does it required more than 1 user to setup communication (video communication)

Thread Thread
 
sebalr profile image
Sebastian Larrieu

You need two users. You can open two browser windows

Thread Thread
 
vipul83 profile image
vipul83 • Edited

yup..it works for me when running via vs debug mode on two separate browsers but when i hosted the application on IIS, getting "simplepeer is not defined" error on console.....do I need to configure on IIS ??
also "SendSignal" hub method call is not going thru when hosted on IIS, if i select user

Thread Thread
 
sebalr profile image
Sebastian Larrieu

Sorry, I didn't understand. Why do you need IIS? I use dot net CLI to run the backend and Angular CLI to run frontend (or build and use a server like Apache or nginx) and open two browser to connect to Angular

Thread Thread
 
vipul83 profile image
vipul83

I need IIS to host. I have to host my web api (signalr svc) and frontend on IIS

Thread Thread
 
sebalr profile image
Sebastian Larrieu • Edited

So if you open two browser and use two different user everything must work

Thread Thread
 
vipul83 profile image
vipul83

No, its not working when hosted on IIS. I am wondering if i need to update anything on IIS related to support simplepeer. Anything you suggest ?

Collapse
 
saravanakumar491993 profile image
saravanakumar491993 • Edited

Dear Sebastian,

I'm able to connect with other user using singalr and hello is working. When I choose an user the following error occurs in Angular. Please help me with this.

ReferenceError: SimplePeer is not defined

Collapse
 
sebalr profile image
Sebastian Larrieu

Maybe you are not importing the library

Collapse
 
saravanakumar491993 profile image
saravanakumar491993 • Edited

I did it in angular

npm install simple-peer
npm install @types/simple-peer

It only worked after i add the below script in index.html :(

<script src="https://cdnjs.cloudflare.com/ajax/libs/simple-peer/6.2.1/simplepeer.min.js"></script>

Why is that so. Please help.

Thread Thread
 
sebalr profile image
Sebastian Larrieu

You have to import simple peer in component ts. Check the GitHub

Thread Thread
 
saravanakumar491993 profile image
Info Comment hidden by post author - thread only accessible via permalink
saravanakumar491993

Love you dear sebastian. You are a genius and a humble person. Never seen a great person like you to respond to these kind of simple error that are made by fellow dev's like me. Huge appreciation for your reply. God bless you.

Collapse
 
timsar2 profile image
timsar2 • Edited

import inside styles[] into angular.json file.

Collapse
 
babakmou profile image
Babak

Hi,

Thanks for this post which helped me a lot in understanding webrtc. Could you please help me to solve below issue?
I also downgraded the version of Typescript from 4 to 3.5.3. , same as yours which didn't work.

Error: src/app/rtc.service.ts:31:5 - error TS2322: Type 'BehaviorSubject' is not assignable to type 'BehaviorSubject'.
Types of property 'observers' are incompatible.
Type 'Observer[]' is not assignable to type 'Observer[]'.
Type 'Observer' is not assignable to type 'Observer'.
Type 'UserInfo[]' is not assignable to type 'never[]'.

31     this.users = new BehaviorSubject([]);
Enter fullscreen mode Exit fullscreen mode
Collapse
 
sebalr profile image
Sebastian Larrieu • Edited

Is hard to tell without looking your code. You may have two differents versions of rxjs installed or you are importing behaviourSubject from the wrong place.
Or maybe something changes on RTC library and is returning something different

Collapse
 
babakmou profile image
Babak

It is exactly your code. I have made no changes,
There were some other errors which could be fixed. For example, I had to add type "any" for "data" as parameter of all peer.on() event listeners.

Thread Thread
 
sebalr profile image
Sebastian Larrieu • Edited

It may be related to some TS or Angular upgrade. Try changing the empty array param in line 31 to a new types empty array.
Instead of [] change to new Array with UserInfo as type between <<>>

Collapse
 
timsar2 profile image
timsar2

i used your repo to implement this with ionic.
everything is ok. two client can swap their sdp but onStream wil fire and has data but video element not showing any thing. also browser has permission for mic and camera

Collapse
 
sebalr profile image
Sebastian Larrieu

I have no experience with ionic. Are you seeing any console error?.

Maybe you need to access camera with some plugin

Collapse
 
timsar2 profile image
timsar2

no errors. with a breakpoint (debugger) onstream has data and will be pass to html video element
but nothing happened ;(

Thread Thread
 
sebalr profile image
Sebastian Larrieu • Edited

Sorry I can't help. As I told you I've never use Ionic. I only used cordova and did not have any problem with video tag.

Collapse
 
steveblue profile image
Stephen Belovarich

Very cool 😎!

A couple years ago I made something similar that encapsulates all the logic in a service. Could be made to handle AV pretty easily.

github.com/steveblue/ngfx/blob/mas...

Collapse
 
sebalr profile image
Sebastian Larrieu

Very nice, I will look in detail to get some ideas :)

Collapse
 
landry_80 profile image
lan-dry

Hi. Please I am getting this error:
Access to XMLHttpRequest at 'localhost:5001/signalrtc/negotiate' from origin 'localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

can you help me please??

Collapse
 
sebalr profile image
Sebastian Larrieu

Hi. As the error show, you have to allow origin localhost:4200 to CORS.

Collapse
 
landry_80 profile image
lan-dry • Edited

But the fact is that I did it exactly as you did. But it stills showing that error. this is the startup file:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using signalRtc.hubs;

namespace signalRtc
{
public class Startup
{
readonly string MyAllowSpecificOrigins = "AllowOrigins";

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(MyAllowSpecificOrigins,
                builder => builder.WithOrigins("http://localhost:4200", "https://localhost:4200")
                .AllowAnyMethod()
                .AllowAnyHeader()
                .AllowCredentials());
        });

        services.AddSignalR();
    }        

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseRouting();
        app.UseCors(MyAllowSpecificOrigins);
        // app.UseMvc();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapHub<SignalRtcHub>("/signalrtc");
        });

        app.Run(async(context) =>
        {
            await context.Response.WriteAsync("Hello World!");
        });
    }
}

}

Collapse
 
sebalr profile image
Sebastian Larrieu • Edited

Hi. As the error shows, you have to allow origin localhost:4200 to CORS. Pleas read the tutorial carefully

Collapse
 
rajeshcherla profile image
Mystic force

Hi Sebastian,

I am working on a project for building the Video Conference site .. and your Article helped me a lot in Implementing this feature ..

I have one question ..

Please can you let me know how to show videos of both the users in the same window .. just like the Zoom conference or Google meet .. where both the users can see the there video and other person video also

Collapse
 
mahmoudabdin profile image
Mahmoud abdin

hi, greate post it helped me a lot, but what if want to share the same video with multiple users, like noly first connection can start the broadcast and when others entered the connection they will be able to show the broadcast .. do you have any idea? to be more clear user x will start the streaming the other users let us say c,v,b and n ... can see the live streaming video of user x.

Collapse
 
sebalr profile image
Sebastian Larrieu • Edited

Hi, you have to identify your broadcaster, lets say user X, then every new user will send a message to the server for a new connection, user X will be listening for that message and start a new connection for that peer.
That for one to to many. If you need all to communicate you will need to do a star network, there is an example in simple peer docs.
User X will have a list of peers to send data and the rest of the user will only have one peer

Collapse
 
avshaheen profile image
Shaheen.Av

Hi..it was a good sample.

I m getting "SimplePeer is not defined" error when i click the user in both browser window. Can any one suggest me what is the problem in it. I implemented exactly like this. This was the error i am getting on it.

Please give me a solution ASP...

Some comments may only be visible to logged-in visitors. Sign in to view all comments. Some comments have been hidden by the post's author - find out more