A implementação a ser mostrada neste post é um exemplo simples de chamada de vídeo com Ionic 5/Angular 9. Ao final, você poderá conectar dois dispositivos, desde que o aplicativo esteja sendo executado em ambos. Para iniciar/acordar o app no caso de o mesmo não estar sendo executado no momento da chamada, uma possibilidade é implementar uma solução como uma notificação push, que não será demonstrada aqui. Sem mais delongas, vamos direto ao assunto:
Iniciando e configurando o projeto
1 |
ionic start ionic-peer blank |
Com a pasta do projeto criada, adicione o peerjs ao repositório:
1 |
npm install peerjs --save |
Adicione a diretiva esModuleInterop ao arquivo tsconfig.json para podermos importar módulos CommonJS:
1 2 3 4 5 |
"compilerOptions": { .... "esModuleInterop": true, .... } |
Criando o service
1 |
ionic generate service providers/webrtc |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
import Peer from 'peerjs'; export class WebrtcService { peer: Peer; myStream: MediaStream; myEl: HTMLMediaElement; partnerEl: HTMLMediaElement; stun = 'stun.l.google.com:19302'; mediaConnection: Peer.MediaConnection; options: Peer.PeerJSOption; stunServer: RTCIceServer = { urls: 'stun:' + this.stun, }; constructor() { this.options = { // not used, by default it'll use peerjs server key: 'cd1ft79ro8g833di', debug: 3 }; } getMedia() { navigator.getUserMedia({ audio: true, video: true }, (stream) => { this.handleSuccess(stream); }, (error) => { this.handleError(error); }); } async init(userId: string, myEl: HTMLMediaElement, partnerEl: HTMLMediaElement) { this.myEl = myEl; this.partnerEl = partnerEl; try { this.getMedia(); } catch (e) { this.handleError(e); } await this.createPeer(userId); } async createPeer(userId: string) { this.peer = new Peer(userId); this.peer.on('open', () => { this.wait(); }); } call(partnerId: string) { const call = this.peer.call(partnerId, this.myStream); call.on('stream', (stream) => { this.partnerEl.srcObject = stream; }); } wait() { this.peer.on('call', (call) => { call.answer(this.myStream); call.on('stream', (stream) => { this.partnerEl.srcObject = stream; }); }); } handleSuccess(stream: MediaStream) { this.myStream = stream; this.myEl.srcObject = stream; } handleError(error: any) { if (error.name === 'ConstraintNotSatisfiedError') { const v = constraints.video; // this.errorMsg(`The resolution ${v.width.exact}x${v.height.exact} px is not supported by your device.`); this.errorMsg(`The resolution px is not supported by your device.`); } else if (error.name === 'PermissionDeniedError') { this.errorMsg('Permissions have not been granted to use your camera and ' + 'microphone, you need to allow the page access to your devices in ' + 'order for the demo to work.'); } this.errorMsg(`getUserMedia error: ${error.name}`, error); } errorMsg(msg: string, error?: any) { const errorElement = document.querySelector('#errorMsg'); errorElement.innerHTML += `<p>${msg}</p>`; if (typeof error !== 'undefined') { console.error(error); } } } |
O método init criará uma conexão com id recbido e, em seguida, registrará o método wait para ouvir qualquer chamada. Se alguma chamada for recebida dentro do método de espera, ele responderá automaticamente. Você pode alterar a implementação, já que a chamada new Peer() , sem parâmetros no construtor, se conectará ao servidor padrão. Você pode usar outro servidor ou fazer sua própria implementação. Neste méotodo, também é iniciada a transmissão de vídeo/áudio na resposta à chamada e a envia para o objeto html da view (temos que criar um elemento html para exibir o vídeo na página home.html). O método call pedirá ao servidor para conectá-lo ao seu contato de chamada.
Criando a view
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<ion-content> <div id="errorMsg"></div> <video id="partner-video" (click)="swapVideo('my-video')" autoplay playsinline [ngClass]="{'top-video': topVideoFrame === 'partner-video', 'main-video': topVideoFrame != 'partner-video'}"> </video> <video id="my-video" (click)="swapVideo('partner-video')" autoplay playsinline [ngClass]="{'top-video': topVideoFrame === 'my-video', 'main-video': topVideoFrame != 'my-video'}" > </video> <ion-row nowrap> <ion-button (click)="init()">Login As: </ion-button> <ion-item> <ion-input type="text" [(ngModel)]="userId" placeholder="enter your nick name"></ion-input> </ion-item> </ion-row> <ion-row nowrap> <ion-button (click)="call()">Call To: </ion-button> <ion-item> <ion-input type="text" [(ngModel)]="partnerId" placeholder="your partner nick name"></ion-input> </ion-item> </ion-row> </ion-content> |
A tag video exibe tanto seu conteúdo de mídia quanto do seu contato de chamada. Você deve fazer o login com o apelido para ser seu identificador nas chamadas.
Editando o home.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
import { WebrtcService } from '../providers/webrtc.service'; export class HomePage { topVideoFrame = 'partner-video'; userId: string; partnerId: string; myEl: HTMLMediaElement; partnerEl: HTMLMediaElement; constructor( public webRTC: WebrtcService, public elRef: ElementRef ) {} init() { this.myEl = this.elRef.nativeElement.querySelector('#my-video'); this.partnerEl = this.elRef.nativeElement.querySelector('#partner-video'); this.webRTC.init(this.userId, this.myEl, this.partnerEl); } call() { this.webRTC.call(this.partnerId); this.swapVideo('my-video'); } swapVideo(topVideo: string) { this.topVideoFrame = topVideo; } } |
O método de login atribuirá o frame de vídeo ao elemento html e os passará para o webrtcservice , para que o serviço possa enviar o stream para eles. O método call passará o partnerId ao serviço webrtc para conexão com seu contato.
Editando o home.scss
1 2 3 4 5 6 7 8 9 10 11 12 |
.top-video{ position: absolute; top: 100px; right: 100px; max-width: 100px; z-index: 2; } .main-video{ width: 100%; max-height: 70%; } |
1 |
ionic serve --address 192.168.43.105 --ssl |
No dispositivos que vão se conectar, acesse o endereço com o https pré-fixado:
1 |
https://192.168.43.105:8100 |
Se liga aí, porque é hora da revisão
Conforme explicado, o exemplo acima utiliza um servidor peer padrão para facilitar a demonstração e implementação. Em um caso de implementação para uso profissional, o ideal é utilizar/implementar seu próprio servidor. Ao acessar o endereço com https, pela ausência/invalidade de um certificado do endereço, é possível que o navegador exiba algum aviso de risco, deve ser consentido para avançar à aplicação. Quaisquer dúvidas ou sugestões, utilize a área de comentários ou entre em contato!
1 Comentário
Maicon santos
21 de setembro de 2021 at 15:49vc tem este projeto no git ou em zip para eu testar?