Hantera video under samtal

Lär dig hur du hanterar videosamtal med Azure Communication Services SDKS. Vi får lära oss hur du hanterar mottagande och sändning av video i ett samtal.

Förutsättningar

Installera SDK:n

npm install Använd kommandot för att installera Azure Communication Services Common och Calling SDK för JavaScript:

npm install @azure/communication-common --save
npm install @azure/communication-calling --save

Initiera nödvändiga objekt

En CallClient instans krävs för de flesta anropsåtgärder. När du skapar en ny CallClient instans kan du konfigurera den med anpassade alternativ som en Logger instans.

Med instansen CallClient kan du skapa en CallAgent instans genom att anropa createCallAgent. Den här metoden returnerar asynkront ett CallAgent instansobjekt.

Metoden createCallAgent använder CommunicationTokenCredential som argument. Den accepterar en användaråtkomsttoken.

Du kan använda getDeviceManager metoden på instansen CallClient för att få åtkomst deviceManagertill .

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()

Så här hanterar du SDK-anslutning till Microsoft-infrastruktur på bästa sätt

Instansen Call Agent hjälper dig att hantera anrop (för att ansluta eller starta samtal). För att kunna arbeta måste din anropande SDK ansluta till Microsofts infrastruktur för att få meddelanden om inkommande samtal och samordna annan samtalsinformation. Du Call Agent har två möjliga tillstånd:

Anslut ed – Ett Call Agent connectionStatue-värde Connected innebär att klient-SDK:t är ansluten och kan ta emot meddelanden från Microsofts infrastruktur.

Frånkopplad – Ett Call Agent connectionStatue-värde för Disconnected tillstånd det finns ett problem som hindrar SDK:n från att ansluta korrekt. Call Agent ska återskapas.

  • invalidToken: Om en token har upphört att gälla eller om en ogiltig Call Agent instans kopplas från med det här felet.
  • connectionIssue: Om det finns ett problem med att klienten ansluter till Microsoft-infrastrukturen, efter att många återförsök Call Agent har exponerat connectionIssue felet.

Du kan kontrollera om din lokala Call Agent är ansluten till Microsofts infrastruktur genom att granska det aktuella värdet för connectionState egenskapen. Under ett aktivt anrop kan du lyssna på connectionStateChanged händelsen för att avgöra om Call Agent ändringar från Anslut till frånkopplat tillstånd.

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);

Enhetshantering

Om du vill börja använda video med Calling SDK måste du kunna hantera enheter. Med enheter kan du styra vad som överför ljud och video till samtalet.

deviceManagerMed kan du räkna upp lokala enheter som kan överföra dina ljud- och videoströmmar i ett samtal. Du kan också använda deviceManager för att begära behörighet att komma åt den lokala enhetens mikrofoner och kameror.

Du kan komma åt deviceManager genom att callClient.getDeviceManager() anropa metoden:

const deviceManager = await callClient.getDeviceManager();

Hämta lokala enheter

Om du vill komma åt lokala enheter kan du använda uppräkningsmetoderna deviceManagergetCameras() och getMicrophones. Dessa metoder är asynkrona åtgärder.

//  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...]

Ange standardenheterna

När du vet vilka enheter som är tillgängliga att använda kan du ange standardenheter för mikrofon, högtalare och kamera. Om klientens standardvärden inte har angetts använder Communication Services SDK standardinställningarna för operativsystemet.

Mikrofon

Få åtkomst till den enhet som används

// Get the microphone device that is being used.
const defaultMicrophone = deviceManager.selectedMicrophone;

Ange vilken enhet som ska användas

// Set the microphone device to use.
await deviceManager.selectMicrophone(localMicrophones[0]);

Talare

Få åtkomst till den enhet som används

// Get the speaker device that is being used.
const defaultSpeaker = deviceManager.selectedSpeaker;

Ange vilken enhet som ska användas

// Set the speaker device to use.
await deviceManager.selectSpeaker(localSpeakers[0]);

Kamera

Få åtkomst till den enhet som används

// Get the camera device that is being used.
const defaultSpeaker = deviceManager.selectedSpeaker;

Ange vilken enhet som ska användas

// Set the speaker device to use.
await deviceManager.selectSpeaker(localCameras[0]);

Var CallAgent och en kan välja sin egen mikrofon och högtalare på sin associerade DeviceManager. Vi rekommenderar att olika CallAgents använder olika mikrofoner och högtalare. De bör inte dela samma mikrofoner eller högtalare. Om delning sker kan mikrofonanvändarinriktad diagnostik utlösas och mikrofonen slutar fungera beroende på webbläsaren/operativsystemet.

Lokal videoström

För att kunna skicka video i ett anrop måste du skapa ett LocalVideoStreamobjekt.

const localVideoStream = new LocalVideoStream(camera);

Kameran som skickas som parameter är ett av de objekt som VideoDeviceInfo returneras av deviceManager.getCameras()metoden.

A LocalVideoStream har följande egenskaper:

  • source: Enhetsinformationen.
const source = localVideoStream.source;
  • mediaStreamType: Kan vara Video, ScreenSharingeller RawMedia.
const type: MediaStreamType = localVideoStream.mediaStreamType;

Förhandsgranskning av lokal kamera

Du kan använda deviceManager och VideoStreamRenderer börja återge strömmar från din lokala kamera. När en LocalVideoStream har skapats använder du den för att konfigureraVideoStreamRenderer den. När den har skapats VideoStreamRendereranropar du dess createView() metod för att hämta en vy som du kan lägga till som underordnad på sidan.

Den här strömmen skickas inte till andra deltagare. det är en lokal förhandsgranskningsfeed.

// 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);

Stoppa den lokala förhandsversionen

Om du vill stoppa det lokala förhandsgranskningsanropet gör du dig av med vyn som härleds VideoStreamRendererfrån . När VideoStreamRenderer har kasserats tar du bort vyn från html-trädet genom att anropa removeChild() metoden från DOM Node som innehåller din förhandsversion.

// To stop viewing local camera preview
view.dispose();
htmlElement.removeChild(view.target);

Begär behörighet till kamera och mikrofon

Ett program kan inte använda kameran eller mikrofonen utan behörighet. Du kan använda deviceManager för att uppmana en användare att bevilja kamera- och/eller mikrofonbehörigheter:

const result = await deviceManager.askDevicePermission({audio: true, video: true});

När löftet har lösts returnerar metoden med ett DeviceAccess objekt som anger om audio och video behörigheter har beviljats:

console.log(result.audio);
console.log(result.video);

Kommentar

  • videoDevicesUpdated händelsen utlöses när videoenheter ansluter/kopplas från.
  • audioDevicesUpdated händelsen utlöses när ljudenheter är anslutna.
  • När DeviceManager skapas vet den först inte om några enheter om behörigheter inte har beviljats ännu, så till en början är enhetsnamnet tomt och innehåller inte detaljerad enhetsinformation. Om vi sedan anropar API:et DeviceManager.askPermission() uppmanas användaren att ange enhetsåtkomst. När användaren väljer "tillåt" för att ge åtkomst till enhetshanteraren lär sig om enheterna i systemet uppdaterar du enhetslistorna och sänder händelserna "audioDevicesUpdated" och "videoDevicesUpdated". Om en användare uppdaterar sidan och skapar en enhetshanterare kan enhetshanteraren lära sig mer om enheter eftersom användaren har beviljat åtkomst tidigare. Den har sina enhetslistor fyllda från början och avger inte "audioDevicesUpdated" eller "videoDevicesUpdated" händelser.
  • Talaruppräkning/val stöds inte i Android Chrome, iOS Safari eller macOS Safari.

Ringa ett samtal med videokamera

Viktigt!

För närvarande stöds endast en utgående lokal videoström.

Om du vill ringa ett videosamtal måste du räkna upp lokala kameror med hjälp getCameras() av metoden i deviceManager.

När du har valt en kamera använder du den för att skapa en LocalVideoStream instans. Skicka det som videoOptions ett objekt i matrisen localVideoStream till CallAgentstartCall -metoden.

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);
  • Du kan också ansluta ett anrop med video med CallAgent.join() API och acceptera och anropa med video med Call.Accept() API.
  • När samtalet ansluter börjar det automatiskt skicka en videoström från den valda kameran till den andra deltagaren.

Starta och sluta skicka lokal video under ett samtal

Starta video

Om du vill starta en video under ett anrop måste du räkna upp kameror med hjälp av getCameras -metoden på deviceManager objektet. Skapa sedan en ny instans av LocalVideoStream med önskad kamera och skicka LocalVideoStream sedan objektet till startVideo metoden för ett befintligt anropsobjekt:

const deviceManager = await callClient.getDeviceManager();
const cameras = await deviceManager.getCameras();
const camera = cameras[0]
const localVideoStream = new LocalVideoStream(camera);
await call.startVideo(localVideoStream);

Stoppa video

När du har börjat skicka video läggs en LocalVideoStream instans av typen Video till i localVideoStreams samlingen på en samtalsinstans.

Hitta videoströmmen i samtalsobjektet

const localVideoStream = call.localVideoStreams.find( (stream) => { return stream.mediaStreamType === 'Video'} );

Stoppa den lokala videon Om du vill stoppa lokal video under ett anrop skickar du den localVideoStream instans som används för video till stopVideo-metoden för Call:

await call.stopVideo(localVideoStream);

Du kan växla till en annan kameraenhet när du har en aktiv LocalVideoStream genom att LocalVideoStream anropa switchSource den instansen:

const cameras = await callClient.getDeviceManager().getCameras();
const camera = cameras[1];
localVideoStream.switchSource(camera);

Om den angivna videoenheten inte är tillgänglig:

  • Om videon är avstängd och du startar en video med ett call.startVideo()samtal genererar den här metoden en SourceUnavailableError diagnostik som cameraStartFailed är riktad mot användaren och är inställd på sant.
  • Ett anrop till localVideoStream.switchSource() metoden gör cameraStartFailed att värdet är sant. Vår guide för samtalsdiagnostik innehåller ytterligare information om hur du diagnostiserar samtalsrelaterade problem.

Om du vill kontrollera om den lokala videon är på eller av kan du använda Call metoden isLocalVideoStarted, som returnerar sant eller falskt:

// Check if local video is on or off
call.isLocalVideoStarted;

Om du vill lyssna efter ändringar i den lokala videon kan du prenumerera och avbryta prenumerationen på händelsen isLocalVideoStartedChanged:

// Subscribe to local video event
call.on('isLocalVideoStartedChanged', () => {
    // Callback();
});
// Unsubscribe from local video event
call.off('isLocalVideoStartedChanged', () => {
    // Callback();
});

Starta och stoppa skärmdelning under ett samtal

Om du vill starta skärmdelning under ett anrop kan du använda den asynkrona metoden startScreenSharing() för ett Call objekt:

Startskärmsdelning

// Start screen sharing
await call.startScreenSharing();

Hitta skärmdelningen i samlingen med LocalVideoStream

När du har börjat skicka skärmdelning läggs en LocalVideoStream instans av typen ScreenSharing, till i localVideoStreams samlingen på samtalsinstansen.

const localVideoStream = call.localVideoStreams.find( (stream) => { return stream.mediaStreamType === 'ScreenSharing'} );

Stoppa skärmdelning

Om du vill stoppa skärmdelning under ett anrop kan du använda asynkron API-stoptScreenSharing:

// Stop screen sharing
await call.stopScreenSharing();

Kontrollera skärmens delningsstatus

Om du vill kontrollera om skärmdelningen är på eller av kan du använda isScreenSharingOn API, som returnerar sant eller falskt:

// Check if screen sharing is on or off
call.isScreenSharingOn;

Om du vill lyssna efter ändringar i skärmresursen kan du prenumerera på och avbryta prenumerationen på händelsen isScreenSharingOnChanged:

// Subscribe to screen share event
call.on('isScreenSharingOnChanged', () => {
    // Callback();
});
// Unsubscribe from screen share event
call.off('isScreenSharingOnChanged', () => {
    // Callback();
});

Viktigt!

Den här funktionen i Azure Communication Services är för närvarande i förhandsversion.

Förhandsversions-API:er och SDK:er tillhandahålls utan ett serviceavtal. Vi rekommenderar att du inte använder dem för produktionsarbetsbelastningar. Vissa funktioner kanske inte stöds, eller så kan de ha begränsade funktioner.

Mer information finns i Kompletterande användningsvillkor för Förhandsversioner av Microsoft Azure.

Förhandsversionen av lokal skärmresurs är allmänt tillgänglig som en del av version 1.15.1-beta.1+.

Förhandsversion av lokal skärmresurs

Du kan använda en VideoStreamRenderer för att börja återge strömmar från din lokala skärmresurs så att du kan se vad du skickar som en skärmdelningsström.

// 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);
    }
});

Rendera video-/skärmdelningsströmmar för fjärrdeltagare

För att rendera en fjärrdeltagares video eller skärmdelning är det första steget att hämta en referens på remoteVideoStream som du vill återge. Detta kan göras genom att gå igenom matrisen eller videoströmmen (videoStreams) för RemoteParticipant. Samlingen fjärrdeltagare nås via objektet Call .

const remoteVideoStream = call.remoteParticipants[0].videoStreams[0];
const streamType = remoteVideoStream.mediaStreamType;

Om du vill återge RemoteVideoStreammåste du prenumerera på händelsen isAvailableChanged . Om egenskapen isAvailable ändras till trueskickar en fjärrdeltagare en videoström. När det händer skapar du en ny instans av VideoStreamRendereroch skapar sedan en ny VideoStreamRendererView instans med hjälp av den asynkrona createView metoden.
Du kan sedan ansluta view.target till valfritt gränssnittselement.

När tillgängligheten för en fjärrström ändras kan du förstöra hela VideoStreamRenderer eller en specifik VideoStreamRendererView. Om du bestämmer dig för att behålla dem visar vyn en tom videoram.

// 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 för att formatera inläsningssnurran över fjärrvideoströmmen.

.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); }
}

Fjärrvideokvalitet

Azure Communication Services WebJS SDK innehåller en funktion som heter Optimal Video Count (OVC), med början i version 1.15.1. Den här funktionen kan användas för att informera program vid körning om hur många inkommande videor från olika deltagare som kan återges optimalt vid en viss tidpunkt i ett gruppsamtal (2+ deltagare). Den här funktionen exponerar en egenskap optimalVideoCount som ändras dynamiskt under anropet baserat på nätverks- och maskinvarufunktionerna i en lokal slutpunkt. Värdet för optimalVideoCount information om hur många videor från olika deltagarprogram som ska återges vid en viss tidpunkt. Program bör hantera dessa ändringar och uppdatera antalet renderade videor i enlighet med rekommendationen. Det finns en bounce-period (cirka 10 s) mellan varje uppdatering.

Användning Funktionen optimalVideoCount är en samtalsfunktion. Du måste referera till funktionen OptimalVideoCount via feature -metoden för Call objektet. Du kan sedan ange en lyssnare via on metoden för att OptimalVideoCountCallFeature meddelas när optimalVideoCount ändras. Om du vill avbryta prenumerationen på ändringarna kan du anropa off metoden.

const optimalVideoCountFeature = call.feature(Features.OptimalVideoCount);
optimalVideoCountFeature.on('optimalVideoCountChanged', () => {
    const localOptimalVideoCountVariable = optimalVideoCountFeature.optimalVideoCount;
})

Exempel på användning: Programmet bör prenumerera på ändringar av optimalt videoantal i gruppanrop. En ändring av det optimala videoantalet kan hanteras genom att antingen skapa en ny renderare (createView metod) eller ta bort vyer (dispose) och uppdatera programlayouten i enlighet med detta.

Egenskaper för fjärrvideoström

Fjärrvideoströmmar har följande egenskaper:

const id: number = remoteVideoStream.id;
  • id: ID för en fjärrvideoström.
const type: MediaStreamType = remoteVideoStream.mediaStreamType;
  • mediaStreamType: Kan vara Video eller ScreenSharing.
const isAvailable: boolean = remoteVideoStream.isAvailable;
  • isAvailable: Definierar om en fjärrdeltagares slutpunkt aktivt skickar en dataström.
const isReceiving: boolean = remoteVideoStream.isReceiving;
  • isReceiving:
    • Informerar programmet om fjärrdata för videoströmmar tas emot eller inte.

    • Flaggan flyttas till false i följande scenarier:

      • En fjärrdeltagare som är i en mobil webbläsare tar webbläsarappen till bakgrunden.
      • En fjärrdeltagare eller den användare som tar emot videon har nätverksproblem som drastiskt påverkar videokvaliteten.
      • En fjärrdeltagare som är På macOS/iOS Safari väljer "Pausa" från adressfältet.
      • En fjärrdeltagare har en nätverkskoppling.
      • En fjärrdeltagare på mobilen dödar eller avslutar webbläsaren.
      • En fjärrdeltagare på mobilen eller skrivbordet låser enheten. Det här scenariot gäller även om fjärrdeltagaren är på en stationär dator och den försätts i viloläge.
    • Flaggan flyttas till true i följande scenarier:

      • En fjärransluten deltagare som är i mobil webbläsare och har sin webbläsare bakgrund tar den tillbaka till förgrunden.
      • En fjärrdeltagare som är På macOS/iOS Safari väljer "Återuppta" från adressfältet efter att ha pausat videon.
      • En fjärrdeltagare återansluter till nätverket efter en tillfällig frånkoppling.
      • En fjärrdeltagare på mobilen låser upp sin enhet och återgår till samtalet i sin mobila webbläsare.
    • Den här funktionen förbättrar användarupplevelsen för återgivning av fjärrvideoströmmar.

    • Du kan visa en inläsningssnurra över fjärrvideoströmmen när isReceiving-flaggan ändras till false. Du behöver inte implementera inläsningsspinnare, men en inläsningssnurrare är den vanligaste användningen för bättre användarupplevelse.

const size: StreamSize = remoteVideoStream.size;
  • size: Strömstorleken med information om videons bredd och höjd.

VideoStreamRenderer-metoder och egenskaper

await videoStreamRenderer.createView();

Skapa en VideoStreamRendererView instans som kan kopplas i programmets användargränssnitt för att återge fjärrvideoströmmen, använda asynkron createView() metod, den löser när strömmen är redo att återges och returnerar ett objekt med target en egenskap som representerar video element som kan infogas var som helst i DOM-trädet.

videoStreamRenderer.dispose();

Ta bort videoStreamRenderer och alla associerade VideoStreamRendererView instanser.

VideoStreamRendererView-metoder och egenskaper

När du skapar en VideoStreamRendererViewkan du ange scalingMode egenskaperna och isMirrored . scalingMode kan vara Stretch, Cropeller Fit. Om isMirrored anges, vänds den renderade strömmen lodrätt.

const videoStreamRendererView: VideoStreamRendererView = await videoStreamRenderer.createView({ scalingMode, isMirrored });

Varje VideoStreamRendererView instans har en target egenskap som representerar återgivningsytan. Bifoga den här egenskapen i programmets användargränssnitt:

htmlElement.appendChild(view.target);

Du kan uppdatera scalingMode genom att updateScalingMode anropa metoden:

view.updateScalingMode('Crop');

Skicka videoströmmar från två olika kameror i samma anrop från samma skrivbordsenhet.

Viktigt!

Den här funktionen i Azure Communication Services är för närvarande i förhandsversion.

Förhandsversions-API:er och SDK:er tillhandahålls utan ett serviceavtal. Vi rekommenderar att du inte använder dem för produktionsarbetsbelastningar. Vissa funktioner kanske inte stöds, eller så kan de ha begränsade funktioner.

Mer information finns i Kompletterande användningsvillkor för Förhandsversioner av Microsoft Azure.

Skicka videoströmmar från två olika kameror i samma anrop stöds som en del av version 1.17.1-beta.1+ på webbläsare som stöds av skrivbordet.

  • Du kan skicka videoströmmar från två olika kameror från en enda skrivbordswebbläsares flik/app, i samma anrop, med följande kodfragment:
// 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();

Begränsningar:

  • Detta måste göras med två olika CallAgent instanser med olika identiteter. Kodfragmentet visar två samtalsagenter som används, var och en med sitt eget anropsobjekt.
  • I kodexemplet ansluter båda CallAgents samma anrop (samma anrops-ID). Du kan också ansluta olika samtal med varje agent och skicka en video på ett samtal och en annan video i det andra samtalet.
  • Det går inte att skicka samma kamera i båda CallAgent. De måste vara två olika kameror.
  • Det finns för närvarande inte stöd för att skicka två olika kameror med en CallAgent.
  • På macOS Safari kan videoeffekter med bakgrundsoskärpa (från @azure/communication-effects), endast tillämpas på en kamera och inte båda samtidigt.

Installera SDK:n

Leta upp filen build.gradle på projektnivå och lägg till mavenCentral() i listan med lagringsplatser under buildscript och allprojects:

buildscript {
    repositories {
    ...
        mavenCentral()
    ...
    }
}
allprojects {
    repositories {
    ...
        mavenCentral()
    ...
    }
}

I filen build.gradle på modulnivå lägger du sedan till följande rader i dependencies avsnittet:

dependencies {
    ...
    implementation 'com.azure.android:azure-communication-calling:1.0.0'
    ...
}

Initiera nödvändiga objekt

Om du vill skapa en CallAgent instans måste du anropa createCallAgent metoden på en CallClient instans. Det här anropet returnerar asynkront ett CallAgent instansobjekt.

Metoden createCallAgent tar CommunicationUserCredential som ett argument som kapslar in en åtkomsttoken.

Om du vill komma åt DeviceManagermåste du skapa en callAgent instans först. Sedan kan du använda CallClient.getDeviceManager metoden för att hämta 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();

Om du vill ange ett visningsnamn för anroparen använder du den här alternativa metoden:

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();

Enhetshantering

Om du vill börja använda video med Samtal måste du veta hur du hanterar enheter. Med enheter kan du styra vad som överför ljud och video till samtalet.

DeviceManager låter dig räkna upp lokala enheter som kan användas i ett anrop för att överföra dina ljud-/videoströmmar. Du kan också begära behörighet från en användare för att få åtkomst till deras mikrofon och kamera med hjälp av det interna webbläsar-API:et.

Du kan komma åt deviceManager genom att anropa callClient.getDeviceManager() metoden.

Context appContext = this.getApplicationContext();
DeviceManager deviceManager = callClient.getDeviceManager(appContext).get();

Räkna upp lokala enheter

Om du vill komma åt lokala enheter kan du använda uppräkningsmetoder på Enhetshanteraren. Uppräkning är en synkron åtgärd.

//  Get a list of available video devices for use.
List<VideoDeviceInfo> localCameras = deviceManager.getCameras(); // [VideoDeviceInfo, VideoDeviceInfo...]

Förhandsgranskning av lokal kamera

Du kan använda DeviceManager och Renderer börja återge strömmar från din lokala kamera. Den här strömmen skickas inte till andra deltagare. det är en lokal förhandsgranskningsfeed. Det här är en asynkron åtgärd.

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);

Ringa ett 1:1-samtal med videokamera

Varning

För närvarande stöds endast en utgående lokal videoström För att kunna ringa ett samtal med video måste du räkna upp lokala kameror med hjälp av API:et deviceManagergetCameras . När du har valt en önskad kamera använder du den för att konstruera en LocalVideoStream instans och skicka den till videoOptions som ett objekt i matrisen localVideoStream till en call metod. När samtalet ansluter börjar det automatiskt skicka en videoström från den valda kameran till andra deltagare.

Kommentar

På grund av sekretessproblem delas inte video till samtalet om det inte förhandsgranskas lokalt. Mer information finns i Förhandsversion av lokal kamera.

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);

Starta och sluta skicka lokal video

Om du vill starta en video måste du räkna upp kameror med hjälp av API:et getCameraListdeviceManager objektet. Skapa sedan en ny instans av LocalVideoStream att skicka den önskade kameran och skicka den i API:et startVideo som ett argument:

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();

När du har börjat skicka video läggs en LocalVideoStream instans till i localVideoStreams samlingen på samtalsinstansen.

List<LocalVideoStream> videoStreams = call.getLocalVideoStreams();
LocalVideoStream currentLocalVideoStream = videoStreams.get(0); // Please make sure there are VideoStreams in the list before calling get(0).

Om du vill stoppa lokal video skickar du instansen som LocalVideoStream är tillgänglig i localVideoStreams samlingen:

call.stopVideo(appContext, currentLocalVideoStream).get();

Du kan växla till en annan kameraenhet medan video skickas genom att switchSource anropa på en LocalVideoStream instans:

currentLocalVideoStream.switchSource(source).get();

Rendera videoströmmar för fjärrdeltagare

Om du vill visa en lista över videoströmmar och skärmdelningsströmmar för fjärranslutna deltagare kontrollerar du samlingarna videoStreams :

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

Om du vill återge en RemoteVideoStream från en fjärrdeltagare måste du prenumerera på en OnVideoStreamsUpdated händelse.

I händelsen indikerar ändringen av isAvailable egenskapen till true att fjärrdeltagare för närvarande skickar en ström. När det händer skapar du en ny instans av en Rendereroch skapar sedan en ny RendererView med hjälp av asynkront createView API och kopplar view.target var som helst i programmets användargränssnitt.

När tillgängligheten för en fjärrström ändras kan du välja att förstöra hela renderaren, en specifik RendererView eller behålla dem, men detta resulterar i att en tom videoram visas.

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();
        }
    }
}

Egenskaper för fjärrvideoström

Fjärrvideoström har ett par egenskaper

  • Id – ID för en fjärrvideoström
int id = remoteVideoStream.getId();
  • MediaStreamType – Kan vara "Video" eller "ScreenSharing"
MediaStreamType type = remoteVideoStream.getMediaStreamType();
  • isAvailable – Anger om fjärrdeltagarens slutpunkt aktivt skickar ström
boolean availability = remoteVideoStream.isAvailable();

Återgivningsmetoder och egenskaper

Renderarobjekt efter API:er

  • Skapa en VideoStreamRendererView instans som kan kopplas senare i programmets användargränssnitt för att återge fjärrvideoström.
// Create a view for a video stream
VideoStreamRendererView.createView()
  • Ta bort renderaren och alla VideoStreamRendererView som är associerade med den här renderaren. Anropas när du har tagit bort alla associerade vyer från användargränssnittet.
VideoStreamRenderer.dispose()
  • StreamSize – storlek (bredd/höjd) för en fjärransluten videoström
StreamSize renderStreamSize = VideoStreamRenderer.getSize();
int width = renderStreamSize.getWidth();
int height = renderStreamSize.getHeight();

RendererView-metoder och egenskaper

När du skapar en VideoStreamRendererView kan du ange egenskaperna ScalingMode och mirrored som ska gälla för den här vyn: Skalningsläget kan vara något av "CROP" | "FIT"

VideoStreamRenderer remoteVideoRenderer = new VideoStreamRenderer(remoteVideoStream, appContext);
VideoStreamRendererView rendererView = remoteVideoRenderer.createView(new CreateViewOptions(ScalingMode.Fit));

Den skapade RendererView kan sedan kopplas till programmets användargränssnitt med hjälp av följande kodfragment:

layout.addView(rendererView);

Du kan senare uppdatera skalningsläget genom att updateScalingMode anropa API:et i RendererView-objektet med ett av ScalingMode.CROP | ScalingMode.FIT som argument.

// Update the scale mode for this view.
rendererView.updateScalingMode(ScalingMode.CROP)

Konfigurera systemet

Skapa Xcode-projektet

I Xcode skapar du ett nytt iOS-projekt och väljer mallen Enkel vyapp . Den här snabbstarten använder SwiftUI-ramverket, så du bör ange Language till Swift och ange Gränssnitt till SwiftUI.

Du kommer inte att skapa tester under den här snabbstarten. Avmarkera kryssrutan Inkludera tester .

Skärmbild som visar fönstret för att skapa ett projekt i Xcode.

Installera paketet och beroenden med hjälp av CocoaPods

  1. Skapa en Podfile för ditt program, som i det här exemplet:

    platform :ios, '13.0'
    use_frameworks!
    target 'AzureCommunicationCallingSample' do
        pod 'AzureCommunicationCalling', '~> 1.0.0'
    end
    
  2. Kör pod install.

  3. Öppna .xcworkspace med Xcode.

Begär åtkomst till mikrofonen

För att få åtkomst till enhetens mikrofon måste du uppdatera appens egenskapslista för information med hjälp NSMicrophoneUsageDescriptionav . Du anger det associerade värdet till en sträng som ska ingå i dialogrutan som systemet använder för att begära åtkomst från användaren.

Högerklicka på posten Info.plist i projektträdet och välj sedan Öppna som>källkod. Lägg till följande rader i avsnittet på den översta nivån <dict> och spara sedan filen.

<key>NSMicrophoneUsageDescription</key>
<string>Need microphone access for VOIP calling.</string>

Konfigurera appramverket

Öppna projektets ContentView.swift-fil . Lägg till en import deklaration överst i filen för att importera AzureCommunicationCalling biblioteket. Importera dessutom AVFoundation. Du behöver den för begäranden om ljudbehörighet i koden.

import AzureCommunicationCalling
import AVFoundation

Initiera CallAgent

Om du vill skapa en CallAgent instans från CallClientmåste du använda en callClient.createCallAgent metod som asynkront returnerar ett CallAgent objekt när det har initierats.

Skicka ett CommunicationTokenCredential objekt för att skapa en anropsklient:

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)
}

Skicka objektet CommunicationTokenCredential som du skapade till CallClientoch ange visningsnamnet:

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")
        }
})

Hantera enheter

Om du vill börja använda video med Samtal måste du veta hur du hanterar enheter. Med enheter kan du styra vad som överför ljud och video till samtalet.

DeviceManager låter dig räkna upp lokala enheter som kan användas i ett anrop för att överföra ljud- eller videoströmmar. Du kan också begära behörighet från en användare för att få åtkomst till en mikrofon eller kamera. Du kan komma åt deviceManager objektet callClient .

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")
        }
    }

Räkna upp lokala enheter

Om du vill komma åt lokala enheter kan du använda uppräkningsmetoder i enhetshanteraren. Uppräkning är en synkron åtgärd.

// enumerate local cameras
var localCameras = deviceManager.cameras // [VideoDeviceInfo, VideoDeviceInfo...]

Hämta en lokal kameraförhandsvisning

Du kan använda Renderer för att börja återge en ström från din lokala kamera. Den här strömmen skickas inte till andra deltagare. det är en lokal förhandsgranskningsfeed. Det här är en asynkron åtgärd.

let camera: VideoDeviceInfo = self.deviceManager!.cameras.first!
let localVideoStream = LocalVideoStream(camera: camera)
let localRenderer = try! VideoStreamRenderer(localVideoStream: localVideoStream)
self.view = try! localRenderer.createView()

Hämta egenskaper för lokal kameraförhandsgranskning

Renderaren har en uppsättning egenskaper och metoder som gör att du kan styra återgivningen.

// 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()

Ringa ett 1:1-samtal med video

Information om hur du hämtar en instans av enhetshanteraren finns i avsnittet om att hantera enheter.

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")
    }
}

Rendera videoströmmar för fjärrdeltagare

Fjärrdeltagare kan initiera video- eller skärmdelning under ett samtal.

Hantera videodelnings- eller skärmdelningsströmmar för fjärranslutna deltagare

Granska samlingarna om du vill visa en lista över strömmarna för fjärranslutna videoStreams deltagare.

var remoteParticipantVideoStream = call.remoteParticipants[0].videoStreams[0]

Hämta egenskaper för fjärrvideoström

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

Rendera fjärrdeltagareströmmar

Om du vill börja återge fjärrdeltagareströmmar använder du följande kod.

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)

Hämta metoder och egenskaper för fjärrvideoåtergivning

// [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()

Konfigurera systemet

Skapa Visual Studio-projektet

För en UWP-app i Visual Studio 2022 skapar du ett nytt projekt för Tom app (Universell Windows). När du har angett projektnamnet kan du välja valfri Windows SDK senare än 10.0.17763.0.

För en WinUI 3-app skapar du ett nytt projekt med mallen Tom app, Paketerad (WinUI 3 i Desktop) för att konfigurera en WinUI 3-app med en enda sida. SDK för Windows-appar version 1.3 eller senare krävs.

Installera paketet och beroendena med hjälp av NuGet Package Manager

Anropande SDK-API:er och bibliotek är offentligt tillgängliga via ett NuGet-paket.

Följande steg illustrerar hur du hittar, laddar ned och installerar NuGet-paketet Calling SDK:

  1. Öppna NuGet Package Manager genom att välja Verktyg>NuGet Package Manager>Hantera NuGet-paket för lösning.
  2. Välj Bläddra och ange Azure.Communication.Calling.WindowsClient sedan i sökrutan.
  3. Kontrollera att kryssrutan Inkludera förhandsversion är markerad.
  4. Välj paketet Azure.Communication.Calling.WindowsClient och välj Azure.Communication.Calling.WindowsClientsedan 1.4.0-beta.1 eller en nyare version.
  5. Markera kryssrutan som motsvarar Communication Services-projektet på den högra fliken.
  6. Välj knappen Installera.

Begär åtkomst till mikrofonen

Appen kräver åtkomst till kameran för att kunna köras korrekt. I UWP-appar ska kamerafunktionen deklareras i appens manifestfil.

Följande steg illustrerar hur du kan uppnå detta.

  1. Dubbelklicka på filen med .appxmanifest filnamnstillägget Solution Explorer i panelen.
  2. Klicka på fliken Capabilities .
  3. Markera kryssrutan Camera i listan över funktioner.

Skapa användargränssnittsknappar för att placera och lägga på anropet

Den här enkla exempelappen innehåller två knappar. En för att ringa samtalet och en annan för att hänga upp ett uppringt samtal. Följande steg illustrerar hur du lägger till dessa knappar i appen.

  1. Solution Explorer Dubbelklicka på filen med namnet MainPage.xaml UWP i panelen eller MainWindows.xaml för WinUI 3.
  2. I den centrala panelen letar du efter XAML-koden under förhandsversionen av användargränssnittet.
  3. Ändra XAML-koden med följande utdrag:
<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>

Konfigurera appen med anropande SDK-API:er

Api:erna för anropande SDK finns i två olika namnområden. Följande steg informerar C#-kompilatorn om dessa namnområden så att Visual Studio Intellisense kan hjälpa till med kodutveckling.

  1. I panelen Solution Explorer klickar du på pilen till vänster i filen med namnet MainPage.xaml UWP eller MainWindows.xaml för WinUI 3.
  2. Dubbelklicka på filen med namnet MainPage.xaml.cs eller MainWindows.xaml.cs.
  3. Lägg till följande kommandon längst ned i de aktuella using instruktionerna.
using Azure.Communication.Calling.WindowsClient;

Behåll MainPage.xaml.cs eller MainWindows.xaml.cs öppna. Nästa steg lägger till mer kod i den.

Tillåt appinteraktioner

Användargränssnittsknapparna som tidigare lagts till måste fungera ovanpå en placerad CommunicationCall. Det innebär att en CommunicationCall datamedlem ska läggas till i MainPage klassen eller MainWindow . För att den asynkrona åtgärden som skapas CallAgent ska lyckas bör en CallAgent datamedlem dessutom läggas till i samma klass.

Lägg till följande datamedlemmar i MainPage klassen eller MainWindow :

CallAgent callAgent;
CommunicationCall call;

Skapa knapphanterare

Tidigare lades två användargränssnittsknappar till i XAML-koden. Följande kod lägger till de hanterare som ska köras när en användare väljer knappen. Följande kod ska läggas till efter datamedlemmarna från föregående avsnitt.

private async void CallButton_Click(object sender, RoutedEventArgs e)
{
    // Start call
}

private async void HangupButton_Click(object sender, RoutedEventArgs e)
{
    // End the current call
}

Objektmodell

Följande klasser och gränssnitt hanterar några av de viktigaste funktionerna i Azure Communication Services Calling-klientbiblioteket för UWP.

Name beskrivning
CallClient CallClient är huvudinmatningspunkten till klientbiblioteket För samtal.
CallAgent CallAgent Används för att starta och ansluta anrop.
CommunicationCall CommunicationCall Används för att hantera placerade eller anslutna anrop.
CommunicationTokenCredential CommunicationTokenCredential Används som tokenautentiseringsuppgifter för att instansiera CallAgent.
CallAgentOptions CallAgentOptions Innehåller information för att identifiera anroparen.
HangupOptions Informerar HangupOptions om ett samtal ska avslutas till alla dess deltagare.

Registrera videoschemahanterare

En UI-komponent, till exempel XAML:s MediaElement eller MediaPlayerElement, behöver appen registrera en konfiguration för återgivning av lokala och fjärranslutna videoflöden. Lägg till följande innehåll mellan taggarna Package i Package.appxmanifest:

<Extensions>
    <Extension Category="windows.activatableClass.inProcessServer">
        <InProcessServer>
            <Path>RtmMvrUap.dll</Path>
            <ActivatableClass ActivatableClassId="VideoN.VideoSchemeHandler" ThreadingModel="both" />
        </InProcessServer>
    </Extension>
</Extensions>

Initiera CallAgent

Om du vill skapa en CallAgent instans från CallClientmåste du använda CallClient.CreateCallAgentAsync en metod som asynkront returnerar ett CallAgent objekt när det har initierats.

Om du vill skapa CallAgentmåste du skicka ett CallTokenCredential objekt och ett CallAgentOptions objekt. Tänk på att CallTokenCredential genererar om en felaktig token skickas.

Följande kod bör läggas till i och hjälpfunktionen som ska anropas i appinitiering.

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;

Ändra med en giltig token för autentiseringsuppgifter för resursen <AUTHENTICATION_TOKEN> . Se dokumentationen för användaråtkomsttoken om en token för autentiseringsuppgifter måste hämtas.

Ringa ett 1:1-samtal med videokamera

De objekt som behövs för att skapa en CallAgent är nu klara. Det är dags att asynkront skapa CallAgent och placera ett videosamtal.

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 } }
    };
}

Förhandsgranskning av lokal kamera

Vi kan också konfigurera lokal kameraförhandsvisning. Videon kan återges via MediaPlayerElement:

<Grid>
    <MediaPlayerElement x:Name="LocalVideo" AutoPlay="True" />
    <MediaPlayerElement x:Name="RemoteVideo" AutoPlay="True" />
</Grid>

Så här initierar du den lokala förhandsversionen MediaPlayerElement:

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);
    }
}

Rendera fjärrkameraström

Konfigurera en jämn hanterare som OnCallsUpdated svar på händelse:

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;
    }
}

Börja återge fjärrvideoström på 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;
    }
}

Avsluta samtal

När ett anrop har placerats HangupAsync ska -metoden CommunicationCall för objektet användas för att lägga på anropet.

En instans av HangupOptions bör också användas för att informera om samtalet måste avslutas till alla deltagare.

Följande kod bör läggas till i 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;
            }
        }
    }
}

Kör koden

Se till att Visual Studio skapar appen för , x86 eller ARM64och tryck F5 sedan på för x64att börja köra appen. Därefter klickar du på CommunicationCall knappen för att ringa ett anrop till den definierade anropare.

Tänk på att första gången appen körs uppmanar systemet användaren att bevilja åtkomst till mikrofonen.

Nästa steg