Gerenciar vídeo durante chamadas
Saiba como gerenciar chamadas de vídeo com os SDKs dos Serviços de Comunicação do Azure. Aprenderemos a gerenciar o recebimento e o envio de vídeo em uma chamada.
Pré-requisitos
- Uma conta do Azure com uma subscrição ativa. Crie uma conta gratuitamente.
- Um recurso de Serviços de Comunicação implantado. Crie um recurso de Serviços de Comunicação.
- Um token de acesso de usuário para habilitar o cliente chamador. Para obter mais informações, consulte Criar e gerenciar tokens de acesso.
- Opcional: conclua o início rápido para adicionar chamadas de voz ao seu aplicativo
Instale o SDK
Use o npm install
comando para instalar o SDK de Chamada e Comum dos Serviços de Comunicação do Azure para JavaScript:
npm install @azure/communication-common --save
npm install @azure/communication-calling --save
Inicializar objetos necessários
Uma CallClient
instância é necessária para a maioria das operações de chamada. Ao criar uma nova CallClient
instância, você pode configurá-la com opções personalizadas, como uma Logger
instância.
Com a CallClient
instância, você pode criar uma CallAgent
instância chamando o createCallAgent
arquivo . Esse método retorna de forma assíncrona um objeto de CallAgent
instância.
O createCallAgent
método usa CommunicationTokenCredential
como argumento. Ele aceita um token de acesso de usuário.
Você pode usar o getDeviceManager
método na CallClient
instância para acessar deviceManager
o .
const { CallClient } = require('@azure/communication-calling');
const { AzureCommunicationTokenCredential} = require('@azure/communication-common');
const { AzureLogger, setLogLevel } = require("@azure/logger");
// Set the logger's log level
setLogLevel('verbose');
// Redirect log output to console, file, buffer, REST API, or whatever location you want
AzureLogger.log = (...args) => {
console.log(...args); // Redirect log output to console
};
const userToken = '<USER_TOKEN>';
callClient = new CallClient(options);
const tokenCredential = new AzureCommunicationTokenCredential(userToken);
const callAgent = await callClient.createCallAgent(tokenCredential, {displayName: 'optional Azure Communication Services user name'});
const deviceManager = await callClient.getDeviceManager()
Como gerenciar melhor a conectividade do SDK com a infraestrutura da Microsoft
A Call Agent
instância ajuda você a gerenciar chamadas (para ingressar ou iniciar chamadas). Para funcionar, seu SDK de chamada precisa se conectar à infraestrutura da Microsoft para receber notificações de chamadas recebidas e coordenar outros detalhes da chamada. O seu Call Agent
tem dois estados possíveis:
Conectado - Um Call Agent
valor connectionStatue significa que o SDK do Connected
cliente está conectado e é capaz de receber notificações da infraestrutura da Microsoft.
Desconectado - Um Call Agent
valor connectionStatue de Disconnected
estados há um problema que está impedindo o SDK de se conectar corretamente. Call Agent
devem ser recriados.
invalidToken
: Se um token expirou ou é inválidoCall Agent
, a instância se desconecta com esse erro.connectionIssue
: Se houver um problema com o cliente se conectando à infrascture da Microsoft, depois de muitas tentativasCall Agent
expõe oconnectionIssue
erro.
Você pode verificar se seu local Call Agent
está conectado à infraestrutura da Microsoft inspecionando o valor atual da connectionState
propriedade. Durante uma chamada ativa, você pode ouvir o connectionStateChanged
evento para determinar se Call Agent
muda do estado Conectado para Desconectado.
const connectionState = callAgentInstance.connectionState;
console.log(connectionState); // it may return either of 'Connected' | 'Disconnected'
const connectionStateCallback = (args) => {
console.log(args); // it will return an object with oldState and newState, each of having a value of either of 'Connected' | 'Disconnected'
// it will also return reason, either of 'invalidToken' | 'connectionIssue'
}
callAgentInstance.on('connectionStateChanged', connectionStateCallback);
Gestão de dispositivos
Para começar a usar o vídeo com o SDK de chamada, você precisa ser capaz de gerenciar dispositivos. Os dispositivos permitem controlar o que transmite áudio e vídeo para a chamada.
Com o deviceManager
, você pode enumerar dispositivos locais que podem transmitir seus fluxos de áudio e vídeo em uma chamada. Você também pode usar o deviceManager
para solicitar permissão para acessar os microfones e câmeras do dispositivo local.
Você pode acessar deviceManager
chamando o callClient.getDeviceManager()
método:
const deviceManager = await callClient.getDeviceManager();
Obter dispositivos locais
Para acessar dispositivos locais, você pode usar os deviceManager
métodos getCameras()
de enumeração e getMicrophones
. Esses métodos são ações assíncronas.
// Get a list of available video devices for use.
const localCameras = await deviceManager.getCameras(); // [VideoDeviceInfo, VideoDeviceInfo...]
// Get a list of available microphone devices for use.
const localMicrophones = await deviceManager.getMicrophones(); // [AudioDeviceInfo, AudioDeviceInfo...]
// Get a list of available speaker devices for use.
const localSpeakers = await deviceManager.getSpeakers(); // [AudioDeviceInfo, AudioDeviceInfo...]
Definir os dispositivos padrão
Depois de saber quais dispositivos estão disponíveis para uso, você pode definir dispositivos padrão para microfone, alto-falante e câmera. Se os padrões do cliente não estiverem definidos, o SDK dos Serviços de Comunicação usará os padrões do sistema operacional.
Microfone
Aceder ao dispositivo utilizado
// Get the microphone device that is being used.
const defaultMicrophone = deviceManager.selectedMicrophone;
Configurando o dispositivo para uso
// Set the microphone device to use.
await deviceManager.selectMicrophone(localMicrophones[0]);
Orador
Aceder ao dispositivo utilizado
// Get the speaker device that is being used.
const defaultSpeaker = deviceManager.selectedSpeaker;
Configurando o dispositivo para uso
// Set the speaker device to use.
await deviceManager.selectSpeaker(localSpeakers[0]);
Câmara
Aceder ao dispositivo utilizado
// Get the camera device that is being used.
const defaultSpeaker = deviceManager.selectedSpeaker;
Configurando o dispositivo para uso
// Set the speaker device to use.
await deviceManager.selectSpeaker(localCameras[0]);
Cada um CallAgent
pode escolher seu próprio microfone e alto-falantes em seu associado DeviceManager
. Recomendamos que diferentes CallAgents
usem microfones e alto-falantes diferentes. Eles não devem compartilhar os mesmos microfones nem alto-falantes. Se o compartilhamento acontecer, o Diagnóstico de Enfrentamento ao Usuário do Microfone pode ser acionado e o microfone para de funcionar dependendo do navegador / sistema operacional.
Fluxo de vídeo local
Para poder enviar vídeo em uma chamada, você precisa criar um LocalVideoStream
objeto.
const localVideoStream = new LocalVideoStream(camera);
A câmera passada como parâmetro é um dos VideoDeviceInfo
objetos retornados pelo deviceManager.getCameras()
método.
A LocalVideoStream
tem as seguintes propriedades:
source
: As informações do dispositivo.
const source = localVideoStream.source;
mediaStreamType
: Pode serVideo
,ScreenSharing
ouRawMedia
.
const type: MediaStreamType = localVideoStream.mediaStreamType;
Pré-visualização da câmara local
Você pode usar deviceManager
e VideoStreamRenderer
começar a renderizar fluxos de sua câmera local.
LocalVideoStream
Uma vez criado, use-o para configurarVideoStreamRenderer
. Depois que o é criado, VideoStreamRenderer
chame seu createView()
método para obter uma exibição que você pode adicionar como uma criança à sua página.
Este fluxo não é enviado para outros participantes; é um feed de visualização local.
// To start viewing local camera preview
const cameras = await deviceManager.getCameras();
const camera = cameras[0];
const localVideoStream = new LocalVideoStream(camera);
const videoStreamRenderer = new VideoStreamRenderer(localVideoStream);
const view = await videoStreamRenderer.createView();
htmlElement.appendChild(view.target);
Parar a pré-visualização local
Para interromper a chamada de visualização local, descarte o modo de exibição derivado do VideoStreamRenderer
.
Depois que o VideoStreamRenderer for descartado, remova a exibição da árvore html chamando o removeChild()
método do nó DOM que contém sua visualização.
// To stop viewing local camera preview
view.dispose();
htmlElement.removeChild(view.target);
Solicitar permissão para câmera e microfone
Um aplicativo não pode usar a câmera ou o microfone sem permissões. Você pode usar o deviceManager para solicitar que um usuário conceda permissões de câmera e/ou microfone:
const result = await deviceManager.askDevicePermission({audio: true, video: true});
Depois que a promessa é resolvida, o método retorna com um DeviceAccess
objeto que indica se audio
e video
as permissões foram concedidas:
console.log(result.audio);
console.log(result.video);
Notas
videoDevicesUpdated
O evento é acionado quando os dispositivos de vídeo são conectados/desconectados.audioDevicesUpdated
O evento é acionado quando os dispositivos de áudio são conectados.- Quando o DeviceManager é criado, a princípio ele não sabe sobre nenhum dispositivo se as permissões ainda não forem concedidas, portanto, inicialmente, o nome do dispositivo está vazio e não contém informações detalhadas do dispositivo. Se, em seguida, chamarmos a API DeviceManager.askPermission(), o usuário será solicitado para acesso ao dispositivo. Quando o usuário seleciona em 'permitir' para conceder o acesso, o gerenciador de dispositivos aprende sobre os dispositivos no sistema, atualiza suas listas de dispositivos e emite os eventos 'audioDevicesUpdated' e 'videoDevicesUpdated'. Se um usuário atualizar a página e criar um gerenciador de dispositivos, o gerenciador de dispositivos poderá aprender sobre dispositivos porque o usuário concedeu acesso anteriormente. Ele tem suas listas de dispositivos preenchidas inicialmente e não emite eventos 'audioDevicesUpdated' nem 'videoDevicesUpdated'.
- A enumeração/seleção de alto-falantes não é compatível com Android Chrome, iOS Safari nem macOS Safari.
Fazer uma chamada com câmara de vídeo
Importante
Atualmente, apenas um fluxo de vídeo local de saída é suportado.
Para fazer uma chamada de vídeo, você precisa enumerar câmeras locais usando o getCameras()
método em deviceManager
.
Depois de selecionar uma câmera, use-a para construir uma LocalVideoStream
instância.
Passe-o dentro videoOptions
como um item dentro da localVideoStream
matriz para o CallAgent
startCall
método.
const deviceManager = await callClient.getDeviceManager();
const cameras = await deviceManager.getCameras();
const camera = cameras[0]
const localVideoStream = new LocalVideoStream(camera);
const placeCallOptions = {videoOptions: {localVideoStreams:[localVideoStream]}};
const userCallee = { communicationUserId: '<ACS_USER_ID>' }
const call = callAgent.startCall([userCallee], placeCallOptions);
- Você também pode participar de uma chamada com vídeo com
CallAgent.join()
API e aceitar e chamar com vídeo comCall.Accept()
API. - Quando a chamada se conecta, ela começa automaticamente a enviar um fluxo de vídeo da câmera selecionada para o outro participante.
Iniciar e parar o envio de vídeo local durante uma chamada
Iniciar vídeo
Para iniciar um vídeo durante uma chamada, você precisa enumerar câmeras usando o getCameras
método no deviceManager
objeto.
Em seguida, crie uma nova instância de LocalVideoStream
com a câmera desejada e, em seguida, passe o LocalVideoStream
objeto para o startVideo
método de um objeto de chamada existente:
const deviceManager = await callClient.getDeviceManager();
const cameras = await deviceManager.getCameras();
const camera = cameras[0]
const localVideoStream = new LocalVideoStream(camera);
await call.startVideo(localVideoStream);
Parar vídeo
Depois de iniciar o envio de vídeo com êxito, uma LocalVideoStream
instância do tipo Video
é adicionada à localVideoStreams
coleção em uma instância de chamada.
Localizar o fluxo de vídeo no objeto Call
const localVideoStream = call.localVideoStreams.find( (stream) => { return stream.mediaStreamType === 'Video'} );
Parar o vídeo local Para parar o vídeo local durante uma chamada, passe a localVideoStream
instância que está sendo usada para vídeo para o método stopVideo do Call
:
await call.stopVideo(localVideoStream);
Você pode alternar para um dispositivo de câmera diferente enquanto tem um LocalVideoStream ativo invocando switchSource
nessa LocalVideoStream
instância:
const cameras = await callClient.getDeviceManager().getCameras();
const camera = cameras[1];
localVideoStream.switchSource(camera);
Se o dispositivo de vídeo especificado não estiver disponível:
- Durante uma chamada, se o vídeo estiver desligado e você começar a usar
call.startVideo()
o , esse método lançará umSourceUnavailableError
diagnóstico voltado para o usuário ecameraStartFailed
será definido como true. - Uma chamada para o
localVideoStream.switchSource()
método faz com quecameraStartFailed
seja definida como true. O nosso guia de Diagnóstico de Chamadas fornece informações adicionais sobre como diagnosticar problemas relacionados com chamadas.
Para verificar se o vídeo local está ativado ou desativado, você pode usar o Call
método isLocalVideoStarted
, que retorna true ou false:
// Check if local video is on or off
call.isLocalVideoStarted;
Para ouvir as alterações ao vídeo local, pode subscrever e cancelar a subscrição do evento isLocalVideoStartedChanged:
// Subscribe to local video event
call.on('isLocalVideoStartedChanged', () => {
// Callback();
});
// Unsubscribe from local video event
call.off('isLocalVideoStartedChanged', () => {
// Callback();
});
Iniciar e parar a partilha de ecrã durante uma chamada
Para iniciar o compartilhamento de tela durante uma chamada, você pode usar o método startScreenSharing()
assíncrono em um Call
objeto:
Iniciar partilha de ecrã
// Start screen sharing
await call.startScreenSharing();
Nota: O envio de compartilhamento de tela só é suportado no navegador da área de trabalho.
Encontre o compartilhamento de tela na coleção de LocalVideoStream
Depois de iniciar com êxito o localVideoStreams
envio de compartilhamento de tela, uma LocalVideoStream
instância do tipo ScreenSharing
, é adicionada à coleção na instância de chamada.
const localVideoStream = call.localVideoStreams.find( (stream) => { return stream.mediaStreamType === 'ScreenSharing'} );
Parar a partilha de ecrã
Para interromper o compartilhamento de tela durante uma chamada, você pode usar a API assíncrona stoptScreenSharing:
// Stop screen sharing
await call.stopScreenSharing();
Verificar o estado da partilha de ecrã
Para verificar se o compartilhamento de tela está ativado ou desativado, você pode usar a API isScreenSharingOn, que retorna true ou false:
// Check if screen sharing is on or off
call.isScreenSharingOn;
Para ouvir as alterações no compartilhamento de tela, você pode se inscrever e cancelar a assinatura do evento isScreenSharingOnChanged:
// Subscribe to screen share event
call.on('isScreenSharingOnChanged', () => {
// Callback();
});
// Unsubscribe from screen share event
call.off('isScreenSharingOnChanged', () => {
// Callback();
});
Importante
Esta funcionalidade dos Serviços de Comunicação do Azure está atualmente em pré-visualização.
As APIs e SDKs de visualização são fornecidos sem um contrato de nível de serviço. Recomendamos que você não os use para cargas de trabalho de produção. Alguns recursos podem não ser suportados ou podem ter recursos restritos.
Para obter mais informações, consulte Termos de Utilização Suplementares para Pré-visualizações do Microsoft Azure.
A pré-visualização da partilha de ecrã local está em pré-visualização pública e disponível como parte da versão 1.15.1-beta.1+.
Pré-visualização da partilha de ecrã local
Você pode usar a VideoStreamRenderer
para começar a renderizar fluxos de seu compartilhamento de tela local para que possa ver o que está enviando como um fluxo de compartilhamento de tela.
// To start viewing local screen share preview
await call.startScreenSharing();
const localScreenSharingStream = call.localVideoStreams.find( (stream) => { return stream.mediaStreamType === 'ScreenSharing' });
const videoStreamRenderer = new VideoStreamRenderer(localScreenSharingStream);
const view = await videoStreamRenderer.createView();
htmlElement.appendChild(view.target);
// To stop viewing local screen share preview.
await call.stopScreenSharing();
view.dispose();
htmlElement.removeChild(view.target);
// Screen sharing can also be stoped by clicking on the native browser's "Stop sharing" button.
// The isScreenSharingOnChanged event will be triggered where you can check the value of call.isScreenSharingOn.
// If the value is false, then that means screen sharing is turned off and so we can go ahead and dispose the screen share preview.
// This event is also triggered for the case when stopping screen sharing via Call.stopScreenSharing() API.
call.on('isScreenSharingOnChanged', () => {
if (!call.isScreenSharingOn) {
view.dispose();
htmlElement.removeChild(view.target);
}
});
Renderizar fluxos de vídeo/compartilhamento de tela de participantes remotos
Para renderizar um vídeo de participante remoto ou compartilhamento de tela, a primeira etapa é obter uma referência no RemoteVideoStream que você deseja renderizar.
Isso pode ser feito passando pela matriz ou fluxo de vídeo (videoStreams
) do RemoteParticipant
. A coleção de participantes remotos é acessada através do Call
objeto.
const remoteVideoStream = call.remoteParticipants[0].videoStreams[0];
const streamType = remoteVideoStream.mediaStreamType;
Para renderizar RemoteVideoStream
, você tem que se inscrever em seu isAvailableChanged
evento. Se a isAvailable
propriedade mudar para true
, um participante remoto está enviando um fluxo de vídeo.
Depois que isso acontecer, crie uma nova instância de VideoStreamRenderer
e, em seguida, crie uma nova VideoStreamRendererView
instância usando o método assíncrono createView
.
Em seguida, você pode anexar view.target
a qualquer elemento da interface do usuário.
Sempre que a disponibilidade de um fluxo remoto mudar, você pode destruir o todo VideoStreamRenderer
ou um específico VideoStreamRendererView
.
Se você decidir mantê-los, a exibição exibirá um quadro de vídeo em branco.
// Reference to the html's div where we would display a grid of all remote video stream from all participants.
let remoteVideosGallery = document.getElementById('remoteVideosGallery');
subscribeToRemoteVideoStream = async (remoteVideoStream) => {
let renderer = new VideoStreamRenderer(remoteVideoStream);
let view;
let remoteVideoContainer = document.createElement('div');
remoteVideoContainer.className = 'remote-video-container';
let loadingSpinner = document.createElement('div');
// See the css example below for styling the loading spinner.
loadingSpinner.className = 'loading-spinner';
remoteVideoStream.on('isReceivingChanged', () => {
try {
if (remoteVideoStream.isAvailable) {
const isReceiving = remoteVideoStream.isReceiving;
const isLoadingSpinnerActive = remoteVideoContainer.contains(loadingSpinner);
if (!isReceiving && !isLoadingSpinnerActive) {
remoteVideoContainer.appendChild(loadingSpinner);
} else if (isReceiving && isLoadingSpinnerActive) {
remoteVideoContainer.removeChild(loadingSpinner);
}
}
} catch (e) {
console.error(e);
}
});
const createView = async () => {
// Create a renderer view for the remote video stream.
view = await renderer.createView();
// Attach the renderer view to the UI.
remoteVideoContainer.appendChild(view.target);
remoteVideosGallery.appendChild(remoteVideoContainer);
}
// Remote participant has switched video on/off
remoteVideoStream.on('isAvailableChanged', async () => {
try {
if (remoteVideoStream.isAvailable) {
await createView();
} else {
view.dispose();
remoteVideosGallery.removeChild(remoteVideoContainer);
}
} catch (e) {
console.error(e);
}
});
// Remote participant has video on initially.
if (remoteVideoStream.isAvailable) {
try {
await createView();
} catch (e) {
console.error(e);
}
}
console.log(`Initial stream size: height: ${remoteVideoStream.size.height}, width: ${remoteVideoStream.size.width}`);
remoteVideoStream.on('sizeChanged', () => {
console.log(`Remote video stream size changed: new height: ${remoteVideoStream.size.height}, new width: ${remoteVideoStream.size.width}`);
});
}
CSS para estilizar o girador de carregamento sobre o fluxo de vídeo remoto.
.remote-video-container {
position: relative;
}
.loading-spinner {
border: 12px solid #f3f3f3;
border-radius: 50%;
border-top: 12px solid #ca5010;
width: 100px;
height: 100px;
-webkit-animation: spin 2s linear infinite; /* Safari */
animation: spin 2s linear infinite;
position: absolute;
margin: auto;
top: 0;
bottom: 0;
left: 0;
right: 0;
transform: translate(-50%, -50%);
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Safari */
@-webkit-keyframes spin {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
Qualidade de vídeo remota
O SDK WebJS dos Serviços de Comunicação do Azure fornece um recurso chamado OVC (Contagem de Vídeo Ideal), a partir da versão 1.15.1.
Esse recurso pode ser usado para informar os aplicativos em tempo de execução sobre quantos vídeos recebidos de diferentes participantes podem ser renderizados de forma ideal em um determinado momento em uma chamada em grupo (2+ participantes).
Esse recurso expõe uma propriedade optimalVideoCount
que é alterada dinamicamente durante a chamada com base nos recursos de rede e hardware de um ponto de extremidade local. O valor dos detalhes de optimalVideoCount
quantos vídeos de diferentes participantes devem renderizar em um determinado momento. Os aplicativos devem lidar com essas alterações e atualizar o número de vídeos renderizados de acordo com a recomendação. Há um período de rejeição (cerca de 10 s) entre cada atualização.
Uso O optimalVideoCount
recurso é um recurso de chamada. Você precisa fazer referência ao recurso OptimalVideoCount
através do feature
método do Call
objeto. Em seguida, você pode definir um ouvinte através do on
método do para ser notificado OptimalVideoCountCallFeature
quando o optimalVideoCount mudar. Para cancelar a inscrição das alterações, você pode chamar o off
método. O número máximo atual de vídeos recebidos que podem ser renderizados é 16. Para suportar corretamente 16 vídeos recebidos, o computador deve ter um mimimum de 16GB de RAM e uma CPU de 4 núcleos ou superior que não tenha mais de 3 anos de idade.
const optimalVideoCountFeature = call.feature(Features.OptimalVideoCount);
optimalVideoCountFeature.on('optimalVideoCountChanged', () => {
const localOptimalVideoCountVariable = optimalVideoCountFeature.optimalVideoCount;
})
Exemplo de uso: O aplicativo deve se inscrever para alterações de contagem de vídeo ideal em chamadas em grupo. Uma alteração na contagem de vídeo ideal pode ser tratada criando um novo renderizador (createView
método) ou descartando visualizações (dispose
) e atualizando o layout do aplicativo de acordo.
Propriedades de fluxo de vídeo remoto
Os fluxos de vídeo remotos têm as seguintes propriedades:
const id: number = remoteVideoStream.id;
id
: O ID de um fluxo de vídeo remoto.
const type: MediaStreamType = remoteVideoStream.mediaStreamType;
mediaStreamType
: Pode serVideo
ouScreenSharing
.
const isAvailable: boolean = remoteVideoStream.isAvailable;
isAvailable
: Define se um ponto de extremidade de participante remoto está enviando ativamente um fluxo.
const isReceiving: boolean = remoteVideoStream.isReceiving;
isReceiving
:Informa o aplicativo se os dados de fluxo de vídeo remoto estão sendo recebidos ou não.
O sinalizador é movido para
false
nos seguintes cenários:- Um participante remoto que está no navegador móvel traz o aplicativo do navegador para o plano de fundo.
- Um participante remoto ou o usuário que recebe o vídeo tem um problema de rede que afeta drasticamente a qualidade do vídeo.
- Um participante remoto que esteja no macOS/iOS Safari seleciona "Pausar" na barra de endereço.
- Um participante remoto tem uma desconexão de rede.
- Um participante remoto no celular mata ou encerra o navegador.
- Um participante remoto no celular ou desktop bloqueia seu dispositivo. Este cenário também se aplica se o participante remoto estiver em um computador desktop e entrar em suspensão.
O sinalizador é movido para
true
nos seguintes cenários:- Um participante remoto que está no navegador móvel e tem seu navegador em segundo plano o traz de volta ao primeiro plano.
- Um participante remoto que está no macOS/iOS Safari seleciona em "Retomar" na barra de endereço depois de ter pausado o vídeo.
- Um participante remoto se reconecta à rede após uma desconexão temporária.
- Um participante remoto no celular desbloqueia seu dispositivo e retorna à chamada em seu navegador móvel.
Esse recurso melhora a experiência do usuário para renderizar fluxos de vídeo remotos.
Você pode exibir um girador de carregamento sobre o fluxo de vídeo remoto quando isReceiving sinalizador muda para false. Você não precisa implementar o spinner de carregamento, mas um spinner de carregamento é o uso mais comum para uma melhor experiência do usuário.
const size: StreamSize = remoteVideoStream.size;
size
: O tamanho do fluxo com informações sobre a largura e altura do vídeo.
VideoStreamRenderer métodos e propriedades
await videoStreamRenderer.createView();
Crie uma VideoStreamRendererView
instância que possa ser anexada na interface do usuário do aplicativo para renderizar o fluxo de vídeo remoto, use o método assíncrono createView()
, ele resolve quando o fluxo está pronto para renderizar e retorna um objeto com target
propriedade que representa video
o elemento que pode ser inserido em qualquer lugar na árvore DOM.
videoStreamRenderer.dispose();
Eliminar videoStreamRenderer
e todas as instâncias associadas VideoStreamRendererView
.
VideoStreamRendererView métodos e propriedades
Ao criar um VideoStreamRendererView
, você pode especificar as scalingMode
propriedades e isMirrored
. scalingMode
pode ser Stretch
, Crop
ou Fit
. Se isMirrored
for especificado, o fluxo renderizado será invertido verticalmente.
const videoStreamRendererView: VideoStreamRendererView = await videoStreamRenderer.createView({ scalingMode, isMirrored });
Cada VideoStreamRendererView
instância tem uma target
propriedade que representa a superfície de renderização. Anexe esta propriedade na interface do usuário do aplicativo:
htmlElement.appendChild(view.target);
Você pode atualizar scalingMode
invocando o updateScalingMode
método:
view.updateScalingMode('Crop');
Envie fluxos de vídeo de duas câmaras diferentes, na mesma chamada a partir do mesmo dispositivo desktop.
Importante
Esta funcionalidade dos Serviços de Comunicação do Azure está atualmente em pré-visualização.
As APIs e SDKs de visualização são fornecidos sem um contrato de nível de serviço. Recomendamos que você não os use para cargas de trabalho de produção. Alguns recursos podem não ser suportados ou podem ter recursos restritos.
Para obter mais informações, consulte Termos de Utilização Suplementares para Pré-visualizações do Microsoft Azure.
Enviar fluxos de vídeo de duas câmeras diferentes na mesma chamada é suportado como parte da versão 1.17.1-beta.1+ em navegadores suportados por desktop.
- Você pode enviar fluxos de vídeo de duas câmeras diferentes a partir de uma única guia/aplicativo do navegador de desktop, na mesma chamada, com o seguinte trecho de código:
// Create your first CallAgent with identity A
const callClient1 = new CallClient();
const callAgent1 = await callClient1.createCallAgent(tokenCredentialA);
const deviceManager1 = await callClient1.getDeviceManager();
// Create your second CallAgent with identity B
const callClient2 = new CallClient();
const callAgent2 = await callClient2.createCallAgent(tokenCredentialB);
const deviceManager2 = await callClient2.getDeviceManager();
// Join the call with your first CallAgent
const camera1 = await deviceManager1.getCameras()[0];
const callObj1 = callAgent1.join({ groupId: ‘123’}, { videoOptions: { localVideoStreams: [new LocalVideoStream(camera1)] } });
// Join the same call with your second CallAgent and make it use a different camera
const camera2 = (await deviceManager2.getCameras()).filter((camera) => { return camera !== camera1 })[0];
const callObj2 = callAgent2.join({ groupId: '123' }, { videoOptions: { localVideoStreams: [new LocalVideoStream(camera2)] } });
//Mute the microphone and speakers of your second CallAgent’s Call, so that there is no echos/noises.
await callObj2.muteIncomingAudio();
await callObj2.mute();
Limitações:
- Isso deve ser feito com duas instâncias diferentes
CallAgent
usando identidades diferentes. O trecho de código mostra dois agentes de chamada sendo usados, cada um com seu próprio objeto Call. - No exemplo de código, ambos os CallAgents estão ingressando na mesma chamada (mesmas IDs de chamada). Você também pode participar de diferentes chamadas com cada agente e enviar um vídeo em uma chamada e um vídeo diferente na outra chamada.
- Não há suporte para o envio da mesma câmera em ambos os CallAgent. Devem ser duas câmaras diferentes.
- Atualmente, não há suporte para o envio de duas câmeras diferentes com um CallAgent.
- No macOS Safari, os efeitos de vídeo de desfoque de fundo (de @azure/communication-effects), só podem ser aplicados a uma câmara, e não a ambas ao mesmo tempo.
Instale o SDK
Localize seu arquivo no nível build.gradle
do projeto e adicione mavenCentral()
à lista de repositórios em buildscript
e allprojects
:
buildscript {
repositories {
...
mavenCentral()
...
}
}
allprojects {
repositories {
...
mavenCentral()
...
}
}
Em seguida, no arquivo de nível build.gradle
de módulo, adicione as seguintes linhas à dependencies
seção:
dependencies {
...
implementation 'com.azure.android:azure-communication-calling:1.0.0'
...
}
Inicializar os objetos necessários
Para criar uma CallAgent
instância, você precisa chamar o createCallAgent
método em uma CallClient
instância. Essa chamada retorna de forma assíncrona um objeto de CallAgent
instância.
O createCallAgent
método toma CommunicationUserCredential
como um argumento, que encapsula um token de acesso.
Para acessar DeviceManager
o , você deve criar uma callAgent
instância primeiro. Então você pode usar o CallClient.getDeviceManager
método para obter DeviceManager
.
String userToken = '<user token>';
CallClient callClient = new CallClient();
CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(userToken);
android.content.Context appContext = this.getApplicationContext(); // From within an activity, for instance
CallAgent callAgent = callClient.createCallAgent(appContext, tokenCredential).get();
DeviceManager deviceManager = callClient.getDeviceManager(appContext).get();
Para definir um nome de exibição para o chamador, use este método alternativo:
String userToken = '<user token>';
CallClient callClient = new CallClient();
CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(userToken);
android.content.Context appContext = this.getApplicationContext(); // From within an activity, for instance
CallAgentOptions callAgentOptions = new CallAgentOptions();
callAgentOptions.setDisplayName("Alice Bob");
DeviceManager deviceManager = callClient.getDeviceManager(appContext).get();
CallAgent callAgent = callClient.createCallAgent(appContext, tokenCredential, callAgentOptions).get();
Gestão de dispositivos
Para começar a usar o vídeo com a chamada, você precisará saber como gerenciar dispositivos. Os dispositivos permitem controlar o que transmite áudio e vídeo para a chamada.
DeviceManager
Permite enumerar dispositivos locais que podem ser usados em uma chamada para transmitir seus fluxos de áudio/vídeo. Ele também permite que você solicite permissão de um usuário para acessar seu microfone e câmera usando a API nativa do navegador.
Você pode acessar deviceManager
pelo método de chamada callClient.getDeviceManager()
.
Context appContext = this.getApplicationContext();
DeviceManager deviceManager = callClient.getDeviceManager(appContext).get();
Enumerar dispositivos locais
Para acessar dispositivos locais, você pode usar métodos de enumeração no Gerenciador de dispositivos. A enumeração é uma ação síncrona.
// Get a list of available video devices for use.
List<VideoDeviceInfo> localCameras = deviceManager.getCameras(); // [VideoDeviceInfo, VideoDeviceInfo...]
Pré-visualização da câmara local
Você pode usar DeviceManager
e Renderer
começar a renderizar fluxos de sua câmera local. Este fluxo não será enviado para outros participantes; é um feed de visualização local. Esta é uma ação assíncrona.
VideoDeviceInfo videoDevice = <get-video-device>; // See the `Enumerate local devices` topic above
Context appContext = this.getApplicationContext();
LocalVideoStream currentVideoStream = new LocalVideoStream(videoDevice, appContext);
LocalVideoStream[] localVideoStreams = new LocalVideoStream[1];
localVideoStreams[0] = currentVideoStream;
VideoOptions videoOptions = new VideoOptions(localVideoStreams);
RenderingOptions renderingOptions = new RenderingOptions(ScalingMode.Fit);
VideoStreamRenderer previewRenderer = new VideoStreamRenderer(currentVideoStream, appContext);
VideoStreamRendererView uiView = previewRenderer.createView(renderingOptions);
// Attach the uiView to a viewable location on the app at this point
layout.addView(uiView);
Faça uma chamada 1:1 com câmera de vídeo
Aviso
Atualmente, apenas um fluxo de vídeo local de saída é suportado: para fazer uma chamada com vídeo, você precisa enumerar câmeras locais usando a deviceManager
getCameras
API.
Depois de selecionar uma câmera desejada, use-a para construir uma LocalVideoStream
instância e passá-la videoOptions
como um item na localVideoStream
matriz para um call
método.
Assim que a chamada se ligar, começará automaticamente a enviar um fluxo de vídeo da câmara selecionada para outro(s) participante(s).
Nota
Devido a questões de privacidade, o vídeo não será partilhado na chamada se não estiver a ser pré-visualizado localmente. Consulte Visualização da câmera local para obter mais detalhes.
VideoDeviceInfo desiredCamera = <get-video-device>; // See the `Enumerate local devices` topic above
Context appContext = this.getApplicationContext();
LocalVideoStream currentVideoStream = new LocalVideoStream(desiredCamera, appContext);
LocalVideoStream[] localVideoStreams = new LocalVideoStream[1];
localVideoStreams[0] = currentVideoStream;
VideoOptions videoOptions = new VideoOptions(localVideoStreams);
// Render a local preview of video so the user knows that their video is being shared
Renderer previewRenderer = new VideoStreamRenderer(currentVideoStream, appContext);
View uiView = previewRenderer.createView(new CreateViewOptions(ScalingMode.FIT));
// Attach the uiView to a viewable location on the app at this point
layout.addView(uiView);
CommunicationUserIdentifier[] participants = new CommunicationUserIdentifier[]{ new CommunicationUserIdentifier("<acs user id>") };
StartCallOptions startCallOptions = new StartCallOptions();
startCallOptions.setVideoOptions(videoOptions);
Call call = callAgent.startCall(context, participants, startCallOptions);
Iniciar e parar de enviar vídeo local
Para iniciar um vídeo, você precisa enumerar câmeras usando a getCameraList
API no deviceManager
objeto. Em seguida, crie uma nova instância de LocalVideoStream
passagem da câmera desejada e passe-a startVideo
na API como um argumento:
VideoDeviceInfo desiredCamera = <get-video-device>; // See the `Enumerate local devices` topic above
Context appContext = this.getApplicationContext();
LocalVideoStream currentLocalVideoStream = new LocalVideoStream(desiredCamera, appContext);
VideoOptions videoOptions = new VideoOptions(currentLocalVideoStream);
Future startVideoFuture = call.startVideo(appContext, currentLocalVideoStream);
startVideoFuture.get();
Depois de iniciar com êxito o envio de vídeo, uma LocalVideoStream
instância será adicionada localVideoStreams
à coleção na instância de chamada.
List<LocalVideoStream> videoStreams = call.getLocalVideoStreams();
LocalVideoStream currentLocalVideoStream = videoStreams.get(0); // Please make sure there are VideoStreams in the list before calling get(0).
Para interromper o vídeo local, passe a LocalVideoStream
instância disponível na localVideoStreams
coleção:
call.stopVideo(appContext, currentLocalVideoStream).get();
Você pode alternar para um dispositivo de câmera diferente enquanto o vídeo está sendo enviado invocando switchSource
em uma LocalVideoStream
instância:
currentLocalVideoStream.switchSource(source).get();
Renderizar fluxos de vídeo de participantes remotos
Para listar os fluxos de vídeo e compartilhamento de tela de participantes remotos, inspecione as videoStreams
coleções:
List<RemoteParticipant> remoteParticipants = call.getRemoteParticipants();
RemoteParticipant remoteParticipant = remoteParticipants.get(0); // Please make sure there are remote participants in the list before calling get(0).
List<RemoteVideoStream> remoteStreams = remoteParticipant.getVideoStreams();
RemoteVideoStream remoteParticipantStream = remoteStreams.get(0); // Please make sure there are video streams in the list before calling get(0).
MediaStreamType streamType = remoteParticipantStream.getType(); // of type MediaStreamType.Video or MediaStreamType.ScreenSharing
Para renderizar um RemoteVideoStream
de um participante remoto, você tem que se inscrever em um OnVideoStreamsUpdated
evento.
Dentro do evento, a mudança de isAvailable
propriedade para true indica que o participante remoto está enviando um fluxo no momento. Quando isso acontecer, crie uma nova instância de um Renderer
e, em seguida, crie uma nova RendererView
usando API assíncrona createView
e anexe view.target
em qualquer lugar na interface do usuário do seu aplicativo.
Sempre que a disponibilidade de um fluxo remoto mudar, você pode optar por destruir todo o renderizador, um específico RendererView
ou mantê-los, mas isso resultará na exibição de quadro de vídeo em branco.
VideoStreamRenderer remoteVideoRenderer = new VideoStreamRenderer(remoteParticipantStream, appContext);
VideoStreamRendererView uiView = remoteVideoRenderer.createView(new RenderingOptions(ScalingMode.FIT));
layout.addView(uiView);
remoteParticipant.addOnVideoStreamsUpdatedListener(e -> onRemoteParticipantVideoStreamsUpdated(p, e));
void onRemoteParticipantVideoStreamsUpdated(RemoteParticipant participant, RemoteVideoStreamsEvent args) {
for(RemoteVideoStream stream : args.getAddedRemoteVideoStreams()) {
if(stream.getIsAvailable()) {
startRenderingVideo();
} else {
renderer.dispose();
}
}
}
Propriedades de fluxo de vídeo remoto
O fluxo de vídeo remoto tem algumas propriedades
Id
- ID de um fluxo de vídeo remoto
int id = remoteVideoStream.getId();
MediaStreamType
- Pode ser 'Video' ou 'ScreenSharing'
MediaStreamType type = remoteVideoStream.getMediaStreamType();
isAvailable
- Indica se o ponto de extremidade do participante remoto está enviando fluxo ativamente
boolean availability = remoteVideoStream.isAvailable();
Métodos e propriedades do renderizador
Objeto renderizador seguindo APIs
- Crie uma
VideoStreamRendererView
instância que possa ser anexada posteriormente na interface do usuário do aplicativo para renderizar o fluxo de vídeo remoto.
// Create a view for a video stream
VideoStreamRendererView.createView()
- Elimine o renderizador e todos os
VideoStreamRendererView
associados a este renderizador. A ser chamado quando tiver removido todas as vistas associadas da IU.
VideoStreamRenderer.dispose()
StreamSize
- tamanho (largura / altura) de um fluxo de vídeo remoto
StreamSize renderStreamSize = VideoStreamRenderer.getSize();
int width = renderStreamSize.getWidth();
int height = renderStreamSize.getHeight();
Métodos e propriedades RendererView
Ao criar um VideoStreamRendererView
, você pode especificar as ScalingMode
propriedades e mirrored
que serão aplicadas a esta exibição: O modo de dimensionamento pode ser um dos 'CROP' | 'CABIMENTO'
VideoStreamRenderer remoteVideoRenderer = new VideoStreamRenderer(remoteVideoStream, appContext);
VideoStreamRendererView rendererView = remoteVideoRenderer.createView(new CreateViewOptions(ScalingMode.Fit));
O RendererView criado pode ser anexado à interface do usuário do aplicativo usando o seguinte trecho:
layout.addView(rendererView);
Mais tarde, você pode atualizar o modo de dimensionamento invocando updateScalingMode
a API no objeto RendererView com um dos ScalingMode.CROP | ScalingMode.FIT como um argumento.
// Update the scale mode for this view.
rendererView.updateScalingMode(ScalingMode.CROP)
Configure o seu sistema
Siga estes passos para configurar o seu sistema.
Criar o projeto Xcode
No Xcode, crie um novo projeto iOS e selecione o modelo Single View App . Este artigo usa a estrutura SwiftUI, portanto, você deve definir Language como Swift e Interface como SwiftUI.
Você não vai criar testes neste artigo. Sinta-se à vontade para desmarcar a caixa de seleção Incluir testes .
Instale o pacote e as dependências usando o CocoaPods
Crie um Podfile para seu aplicativo, como este exemplo:
platform :ios, '13.0' use_frameworks! target 'AzureCommunicationCallingSample' do pod 'AzureCommunicationCalling', '~> 1.0.0' end
Execute o
pod install
.Abra
.xcworkspace
usando o Xcode.
Solicitar acesso ao microfone
Para acessar o microfone do dispositivo, você precisa atualizar a lista de propriedades de informações do seu aplicativo usando NSMicrophoneUsageDescription
. Defina o valor associado para uma cadeia de caracteres incluída na caixa de diálogo que o sistema usa para solicitar acesso do usuário.
Clique com o botão direito do mouse na entrada Info.plist da árvore do projeto e selecione Abrir como>código-fonte. Adicione as seguintes linhas na secção de nível <dict>
superior e, em seguida, guarde o ficheiro.
<key>NSMicrophoneUsageDescription</key>
<string>Need microphone access for VOIP calling.</string>
Configurar a estrutura do aplicativo
Abra o arquivo do ContentView.swift
seu projeto. Adicione uma import
declaração à parte superior do arquivo para importar a AzureCommunicationCalling
biblioteca. Além disso, importe AVFoundation
. Você precisa dele para solicitações de permissão de áudio no código.
import AzureCommunicationCalling
import AVFoundation
Inicializar o CallAgent
Para criar uma CallAgent
instância a partir do CallClient
, você precisa usar um callClient.createCallAgent
método que retorna de forma assíncrona um CallAgent
objeto depois que ele é inicializado.
Para criar um cliente de chamada, passe um CommunicationTokenCredential
objeto:
import AzureCommunication
let tokenString = "token_string"
var userCredential: CommunicationTokenCredential?
do {
let options = CommunicationTokenRefreshOptions(initialToken: token, refreshProactively: true, tokenRefresher: self.fetchTokenSync)
userCredential = try CommunicationTokenCredential(withOptions: options)
} catch {
updates("Couldn't created Credential object", false)
initializationDispatchGroup!.leave()
return
}
// tokenProvider needs to be implemented by Contoso, which fetches a new token
public func fetchTokenSync(then onCompletion: TokenRefreshOnCompletion) {
let newToken = self.tokenProvider!.fetchNewToken()
onCompletion(newToken, nil)
}
Passe o CommunicationTokenCredential
objeto que você criou para CallClient
e defina o nome para exibição:
self.callClient = CallClient()
let callAgentOptions = CallAgentOptions()
options.displayName = " iOS Azure Communication Services User"
self.callClient!.createCallAgent(userCredential: userCredential!,
options: callAgentOptions) { (callAgent, error) in
if error == nil {
print("Create agent succeeded")
self.callAgent = callAgent
} else {
print("Create agent failed")
}
})
Gerir dispositivos
Para começar a usar o vídeo com a chamada, você precisará saber como gerenciar dispositivos. Os dispositivos permitem controlar o que transmite áudio e vídeo para a chamada.
DeviceManager
Permite enumerar dispositivos locais que podem ser usados em uma chamada para transmitir fluxos de áudio ou vídeo. Ele também permite que você solicite permissão de um usuário para acessar um microfone ou câmera. Você pode acessar deviceManager
no callClient
objeto.
self.callClient!.getDeviceManager { (deviceManager, error) in
if (error == nil) {
print("Got device manager instance")
self.deviceManager = deviceManager
} else {
print("Failed to get device manager instance")
}
}
Enumerar dispositivos locais
Para acessar dispositivos locais, você pode usar métodos de enumeração no gerenciador de dispositivos. A enumeração é uma ação síncrona.
// enumerate local cameras
var localCameras = deviceManager.cameras // [VideoDeviceInfo, VideoDeviceInfo...]
Obtenha uma pré-visualização da câmara local
Você pode usar Renderer
para começar a renderizar um fluxo de sua câmera local. Este fluxo não será enviado para outros participantes; é um feed de visualização local. Esta é uma ação assíncrona.
let camera: VideoDeviceInfo = self.deviceManager!.cameras.first!
let localVideoStream = LocalVideoStream(camera: camera)
let localRenderer = try! VideoStreamRenderer(localVideoStream: localVideoStream)
self.view = try! localRenderer.createView()
Obter propriedades de visualização da câmera local
O renderizador tem um conjunto de propriedades e métodos que permitem controlar a renderização.
// Constructor can take in LocalVideoStream or RemoteVideoStream
let localRenderer = VideoStreamRenderer(localVideoStream:localVideoStream)
let remoteRenderer = VideoStreamRenderer(remoteVideoStream:remoteVideoStream)
// [StreamSize] size of the rendering view
localRenderer.size
// [VideoStreamRendererDelegate] an object you provide to receive events from this Renderer instance
localRenderer.delegate
// [Synchronous] create view
try! localRenderer.createView()
// [Synchronous] create view with rendering options
try! localRenderer!.createView(withOptions: CreateViewOptions(scalingMode: ScalingMode.fit))
// [Synchronous] dispose rendering view
localRenderer.dispose()
Faça uma chamada 1:1 com vídeo
Para obter uma instância do gerenciador de dispositivos, consulte a seção sobre gerenciamento de dispositivos.
let firstCamera = self.deviceManager!.cameras.first
self.localVideoStreams = [LocalVideoStream]()
self.localVideoStreams!.append(LocalVideoStream(camera: firstCamera!))
let videoOptions = VideoOptions(localVideoStreams: self.localVideoStreams!)
let startCallOptions = StartCallOptions()
startCallOptions.videoOptions = videoOptions
let callee = CommunicationUserIdentifier('UserId')
self.callAgent?.startCall(participants: [callee], options: startCallOptions) { (call, error) in
if error == nil {
print("Successfully started outgoing video call")
self.call = call
} else {
print("Failed to start outgoing video call")
}
}
Renderizar fluxos de vídeo de participantes remotos
Os participantes remotos podem iniciar o compartilhamento de vídeo ou tela durante uma chamada.
Lidar com compartilhamento de vídeo ou compartilhamento de tela de participantes remotos
Para listar os fluxos de participantes remotos, inspecione as videoStreams
coleções.
var remoteParticipantVideoStream = call.remoteParticipants[0].videoStreams[0]
Obter propriedades de fluxo de vídeo remoto
var type: MediaStreamType = remoteParticipantVideoStream.type // 'MediaStreamTypeVideo'
var isAvailable: Bool = remoteParticipantVideoStream.isAvailable // indicates if remote stream is available
var id: Int = remoteParticipantVideoStream.id // id of remoteParticipantStream
Renderizar fluxos de participantes remotos
Para começar a renderizar fluxos de participantes remotos, use o código a seguir.
let renderer = VideoStreamRenderer(remoteVideoStream: remoteParticipantVideoStream)
let targetRemoteParticipantView = renderer?.createView(withOptions: CreateViewOptions(scalingMode: ScalingMode.crop))
// To update the scaling mode later
targetRemoteParticipantView.update(scalingMode: ScalingMode.fit)
Obter métodos e propriedades do renderizador de vídeo remoto
// [Synchronous] dispose() - dispose renderer and all `RendererView` associated with this renderer. To be called when you have removed all associated views from the UI.
remoteVideoRenderer.dispose()
Configure o seu sistema
Siga estes passos para configurar o seu sistema.
Criar o projeto do Visual Studio
Para um aplicativo da Plataforma Universal do Windows, no Visual Studio 2022, crie um novo projeto Aplicativo em Branco (Universal Windows). Depois de inserir o nome do projeto, sinta-se à vontade para escolher qualquer SDK do Windows posterior a 10.0.17763.0.
Para um aplicativo WinUI 3, crie um novo projeto com o modelo Aplicativo em branco, empacotado (WinUI 3 na área de trabalho) para configurar um aplicativo WinUI 3 de página única. É necessário o SDK de Aplicativos Windows versão 1.3 ou posterior.
Instalar o pacote e as dependências usando o Gerenciador de Pacotes NuGet
As APIs e bibliotecas do SDK de chamada estão disponíveis publicamente por meio de um pacote NuGet.
Para localizar, baixar e instalar o pacote NuGet do SDK de chamada:
- Abra o Gerenciador de Pacotes NuGet selecionando Ferramentas>Gerenciador>de Pacotes NuGet Gerenciar Pacotes NuGet para Solução.
- Selecione Procurar e digite Azure.Communication.Calling.WindowsClient na caixa de pesquisa.
- Verifique se a caixa de seleção Incluir pré-lançamento está marcada.
- Selecione o pacote Azure.Communication.Calling.WindowsClient e, em seguida, selecione Azure.Communication.Calling.WindowsClient 1.4.0-beta.1 ou uma versão mais recente.
- Marque a caixa de seleção que corresponde ao projeto dos Serviços de Comunicação do Azure no painel direito.
- Selecione Instalar.
Solicitar acesso ao microfone
O aplicativo requer acesso à câmera para funcionar corretamente. Em aplicativos UWP, o recurso da câmera deve ser declarado no arquivo de manifesto do aplicativo.
As etapas a seguir exemplificam como conseguir isso.
Solution Explorer
No painel, clique duas vezes no arquivo com.appxmanifest
extensão.- Clique na
Capabilities
guia. - Marque a
Camera
caixa de seleção na lista de recursos.
Criar botões da interface do usuário para fazer e desligar a chamada
Este aplicativo de exemplo simples contém dois botões. Um para fazer a chamada e outro para desligar uma chamada feita. As etapas a seguir exemplificam como adicionar esses botões ao aplicativo.
Solution Explorer
No painel, clique duas vezes no arquivo nomeadoMainPage.xaml
para UWP ouMainWindows.xaml
para WinUI 3.- No painel central, procure o código XAML na visualização da interface do usuário.
- Modifique o código XAML pelo seguinte trecho:
<TextBox x:Name="CalleeTextBox" PlaceholderText="Who would you like to call?" />
<StackPanel>
<Button x:Name="CallButton" Content="Start/Join call" Click="CallButton_Click" />
<Button x:Name="HangupButton" Content="Hang up" Click="HangupButton_Click" />
</StackPanel>
Configurando o aplicativo com APIs do SDK chamando
As APIs do SDK de chamada estão em dois namespaces diferentes. As etapas a seguir informam o compilador C# sobre esses namespaces, permitindo que o Intellisense do Visual Studio ajude no desenvolvimento de código.
Solution Explorer
No painel, clique na seta no lado esquerdo do arquivo nomeadoMainPage.xaml
para UWP ouMainWindows.xaml
para WinUI 3.- Clique duas vezes no arquivo nomeado
MainPage.xaml.cs
ouMainWindows.xaml.cs
. - Adicione os seguintes comandos na parte inferior das instruções atuais
using
.
using Azure.Communication.Calling.WindowsClient;
Manter MainPage.xaml.cs
ou MainWindows.xaml.cs
abrir. As próximas etapas adicionarão mais código a ele.
Permitir interações com aplicativos
Os botões da interface do usuário adicionados anteriormente precisam operar em cima de um arquivo CommunicationCall
. Isso significa que um CommunicationCall
membro de dados deve ser adicionado à MainPage
classe ou MainWindow
.
Além disso, para permitir que a criação CallAgent
da operação assíncrona seja bem-sucedida, um CallAgent
membro de dados também deve ser adicionado à mesma classe.
Adicione os seguintes membros de MainPage
dados à classe ou MainWindow
:
CallAgent callAgent;
CommunicationCall call;
Criar manipuladores de botão
Anteriormente, dois botões da interface do usuário eram adicionados ao código XAML. O código a seguir adiciona os manipuladores a serem executados quando um usuário seleciona o botão. O código a seguir deve ser adicionado após os membros de dados da seção anterior.
private async void CallButton_Click(object sender, RoutedEventArgs e)
{
// Start call
}
private async void HangupButton_Click(object sender, RoutedEventArgs e)
{
// End the current call
}
Modelo de objeto
As classes e interfaces a seguir lidam com alguns dos principais recursos da biblioteca de cliente de Chamada dos Serviços de Comunicação do Azure para UWP.
Nome | Descrição |
---|---|
CallClient |
O CallClient é o principal ponto de entrada para a biblioteca de cliente de chamada. |
CallAgent |
O CallAgent é usado para iniciar e participar de chamadas. |
CommunicationCall |
O CommunicationCall é usado para gerenciar chamadas feitas ou associadas. |
CommunicationTokenCredential |
O CommunicationTokenCredential é usado como a credencial de token para instanciar o CallAgent . |
CallAgentOptions |
O CallAgentOptions contém informações para identificar o chamador. |
HangupOptions |
O HangupOptions informa se uma chamada deve ser encerrada para todos os seus participantes. |
Registrar manipulador de esquema de vídeo
Um componente de interface do usuário, como MediaElement ou MediaPlayerElement do XAML, você precisa que o aplicativo registre uma configuração para renderizar feeds de vídeo locais e remotos.
Adicione o seguinte conteúdo entre as Package
tags do Package.appxmanifest
:
<Extensions>
<Extension Category="windows.activatableClass.inProcessServer">
<InProcessServer>
<Path>RtmMvrUap.dll</Path>
<ActivatableClass ActivatableClassId="VideoN.VideoSchemeHandler" ThreadingModel="both" />
</InProcessServer>
</Extension>
</Extensions>
Inicializar o CallAgent
Para criar uma CallAgent
instância a partir do CallClient
, você deve usar CallClient.CreateCallAgentAsync
um método que retorna de forma assíncrona um CallAgent
objeto depois que ele é inicializado.
Para criar CallAgent
o , você deve passar um CallTokenCredential
objeto e um CallAgentOptions
objeto. Tenha em mente que CallTokenCredential
lança se um token malformado for passado.
O código a seguir deve ser adicionado dentro e a função auxiliar deve ser chamada na inicialização do aplicativo.
var callClient = new CallClient();
this.deviceManager = await callClient.GetDeviceManagerAsync();
var tokenCredential = new CallTokenCredential("<AUTHENTICATION_TOKEN>");
var callAgentOptions = new CallAgentOptions()
{
DisplayName = "<DISPLAY_NAME>"
};
this.callAgent = await callClient.CreateCallAgentAsync(tokenCredential, callAgentOptions);
this.callAgent.CallsUpdated += Agent_OnCallsUpdatedAsync;
this.callAgent.IncomingCallReceived += Agent_OnIncomingCallAsync;
Altere o <AUTHENTICATION_TOKEN>
com um token de credencial válido para o seu recurso. Consulte a documentação do token de acesso do usuário se um token de credencial precisar ser originado.
Faça uma chamada 1:1 com câmera de vídeo
Os objetos necessários para criar um CallAgent
estão agora prontos. É hora de criar CallAgent
e fazer uma chamada de vídeo de forma assíncrona.
private async void CallButton_Click(object sender, RoutedEventArgs e)
{
var callString = CalleeTextBox.Text.Trim();
if (!string.IsNullOrEmpty(callString))
{
if (callString.StartsWith("8:")) // 1:1 Azure Communication Services call
{
this.call = await StartAcsCallAsync(callString);
}
}
if (this.call != null)
{
this.call.RemoteParticipantsUpdated += OnRemoteParticipantsUpdatedAsync;
this.call.StateChanged += OnStateChangedAsync;
}
}
private async Task<CommunicationCall> StartAcsCallAsync(string acsCallee)
{
var options = await GetStartCallOptionsAsynnc();
var call = await this.callAgent.StartCallAsync( new [] { new UserCallIdentifier(acsCallee) }, options);
return call;
}
var micStream = new LocalOutgoingAudioStream(); // Create a default local audio stream
var cameraStream = new LocalOutgoingVideoStreamde(this.viceManager.Cameras.FirstOrDefault() as VideoDeviceDetails); // Create a default video stream
private async Task<StartCallOptions> GetStartCallOptionsAsynnc()
{
return new StartCallOptions() {
OutgoingAudioOptions = new OutgoingAudioOptions() { IsMuted = true, Stream = micStream },
OutgoingVideoOptions = new OutgoingVideoOptions() { Streams = new OutgoingVideoStream[] { cameraStream } }
};
}
Pré-visualização da câmara local
Opcionalmente, podemos configurar a visualização da câmera local. O vídeo pode ser renderizado através de MediaPlayerElement
:
<Grid>
<MediaPlayerElement x:Name="LocalVideo" AutoPlay="True" />
<MediaPlayerElement x:Name="RemoteVideo" AutoPlay="True" />
</Grid>
Para inicializar a pré-visualização MediaPlayerElement
local :
private async void CameraList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (cameraStream != null)
{
await cameraStream?.StopPreviewAsync();
if (this.call != null)
{
await this.call?.StopVideoAsync(cameraStream);
}
}
var selectedCamerea = CameraList.SelectedItem as VideoDeviceDetails;
cameraStream = new LocalOutgoingVideoStream(selectedCamerea);
var localUri = await cameraStream.StartPreviewAsync();
LocalVideo.Source = MediaSource.CreateFromUri(localUri);
if (this.call != null) {
await this.call?.StartVideoAsync(cameraStream);
}
}
Renderizar fluxo de câmera remota
Configure o manipulador uniforme em resposta ao OnCallsUpdated
evento:
private async void OnCallsUpdatedAsync(object sender, CallsUpdatedEventArgs args)
{
var removedParticipants = new List<RemoteParticipant>();
var addedParticipants = new List<RemoteParticipant>();
foreach(var call in args.RemovedCalls)
{
removedParticipants.AddRange(call.RemoteParticipants.ToList<RemoteParticipant>());
}
foreach (var call in args.AddedCalls)
{
addedParticipants.AddRange(call.RemoteParticipants.ToList<RemoteParticipant>());
}
await OnParticipantChangedAsync(removedParticipants, addedParticipants);
}
private async void OnRemoteParticipantsUpdatedAsync(object sender, ParticipantsUpdatedEventArgs args)
{
await OnParticipantChangedAsync(
args.RemovedParticipants.ToList<RemoteParticipant>(),
args.AddedParticipants.ToList<RemoteParticipant>());
}
private async Task OnParticipantChangedAsync(IEnumerable<RemoteParticipant> removedParticipants, IEnumerable<RemoteParticipant> addedParticipants)
{
foreach (var participant in removedParticipants)
{
foreach(var incomingVideoStream in participant.IncomingVideoStreams)
{
var remoteVideoStream = incomingVideoStream as RemoteIncomingVideoStream;
if (remoteVideoStream != null)
{
await remoteVideoStream.StopPreviewAsync();
}
}
participant.VideoStreamStateChanged -= OnVideoStreamStateChanged;
}
foreach (var participant in addedParticipants)
{
participant.VideoStreamStateChanged += OnVideoStreamStateChanged;
}
}
private void OnVideoStreamStateChanged(object sender, VideoStreamStateChangedEventArgs e)
{
CallVideoStream callVideoStream = e.CallVideoStream;
switch (callVideoStream.StreamDirection)
{
case StreamDirection.Outgoing:
OnOutgoingVideoStreamStateChanged(callVideoStream as OutgoingVideoStream);
break;
case StreamDirection.Incoming:
OnIncomingVideoStreamStateChanged(callVideoStream as IncomingVideoStream);
break;
}
}
Comece a renderizar o fluxo de vídeo remoto em MediaPlayerElement
:
private async void OnIncomingVideoStreamStateChanged(IncomingVideoStream incomingVideoStream)
{
switch (incomingVideoStream.State)
{
case VideoStreamState.Available:
{
switch (incomingVideoStream.Kind)
{
case VideoStreamKind.RemoteIncoming:
var remoteVideoStream = incomingVideoStream as RemoteIncomingVideoStream;
var uri = await remoteVideoStream.StartPreviewAsync();
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
RemoteVideo.Source = MediaSource.CreateFromUri(uri);
});
/* Or WinUI 3
this.DispatcherQueue.TryEnqueue(() => {
RemoteVideo.Source = MediaSource.CreateFromUri(uri);
RemoteVideo.MediaPlayer.Play();
});
*/
break;
case VideoStreamKind.RawIncoming:
break;
}
break;
}
case VideoStreamState.Started:
break;
case VideoStreamState.Stopping:
break;
case VideoStreamState.Stopped:
if (incomingVideoStream.Kind == VideoStreamKind.RemoteIncoming)
{
var remoteVideoStream = incomingVideoStream as RemoteIncomingVideoStream;
await remoteVideoStream.StopPreviewAsync();
}
break;
case VideoStreamState.NotAvailable:
break;
}
}
Terminar uma chamada
Uma vez que uma chamada é feita, o HangupAsync
método do CommunicationCall
objeto deve ser usado para desligar a chamada.
Uma instância de também deve ser usada para informar se a chamada deve ser encerrada HangupOptions
para todos os seus participantes.
O seguinte código deve ser adicionado dentro de HangupButton_Click
.
var call = this.callAgent?.Calls?.FirstOrDefault();
if (call != null)
{
var call = this.callAgent?.Calls?.FirstOrDefault();
if (call != null)
{
foreach (var localVideoStream in call.OutgoingVideoStreams)
{
await call.StopVideoAsync(localVideoStream);
}
try
{
if (cameraStream != null)
{
await cameraStream.StopPreviewAsync();
}
await call.HangUpAsync(new HangUpOptions() { ForEveryone = false });
}
catch(Exception ex)
{
var errorCode = unchecked((int)(0x0000FFFFU & ex.HResult));
if (errorCode != 98) // Sample error code, sam_status_failed_to_hangup_for_everyone (98)
{
throw;
}
}
}
}
Executar o código
Certifique-se de que o Visual Studio cria o aplicativo para x64
, x86
ou ARM64
, em seguida, pressione F5
para começar a executar o aplicativo. Depois disso, clique no CommunicationCall
botão para fazer uma chamada para o destinatário definido.
Lembre-se de que, na primeira vez que o aplicativo for executado, o sistema solicitará que o usuário conceda acesso ao microfone.