Sdílet prostřednictvím


Správa videa během hovorů

Zjistěte, jak spravovat videohovory pomocí sady SDK služby Azure Communication Services. Dozvíte se, jak spravovat příjem a odesílání videa během hovoru.

Požadavky

Nainstalujte sadu SDK .

npm install Pomocí příkazu nainstalujte sadu AZURE Communication Services Common and Calling SDK pro JavaScript:

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

Inicializace požadovaných objektů

Pro CallClient většinu operací volání se vyžaduje instance. Když vytvoříte novou CallClient instanci, můžete ji nakonfigurovat s vlastními možnostmi, jako je Logger instance.

CallClient S instancí můžete vytvořit CallAgent instanci voláním createCallAgent. Tato metoda asynchronně vrátí CallAgent objekt instance.

Metoda createCallAgent se používá CommunicationTokenCredential jako argument. Přijímá přístupový token uživatele.

K přístupu deviceManagermůžete použít metodu getDeviceManager v CallClient instanci .

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

Jak nejlépe spravovat připojení sady SDK k infrastruktuře Microsoftu

Tato Call Agent instance vám pomůže spravovat hovory (pro připojení nebo zahájení hovorů). Aby bylo možné pracovat s vaší sadou SDK volání, musí se připojit k infrastruktuře Microsoftu, aby bylo možné dostávat oznámení o příchozích hovorech a koordinovat další podrobnosti volání. Máte Call Agent dva možné stavy:

PřipojenoCall Agent Hodnota connectionStatue znamená, Connected že klientská sada SDK je připojená a dokáže přijímat oznámení z infrastruktury Microsoftu.

OdpojenoCall Agent Hodnota Disconnected connectionStatue stavů existuje problém, který brání sadě SDK, aby se správně připojil. Call Agent by se mělo znovu vytvořit.

  • invalidToken: Pokud vypršela platnost tokenu nebo je neplatná Call Agent instance, odpojí se s touto chybou.
  • connectionIssue: Pokud došlo k problému s klientem, který se připojuje k infrascture Microsoftu, po mnoha opakovaných pokusech Call Agent connectionIssue se zobrazí chyba.

Zkontrolujte, jestli je vaše místní Call Agent infrastruktura připojená k infrastruktuře Microsoftu, a to kontrolou aktuální hodnoty connectionState vlastnosti. Během aktivního volání můžete naslouchat connectionStateChanged události a zjistit, jestli Call Agent se změny ze stavu Připojeno k Odpojeno .

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

Správa zařízení

Pokud chcete začít používat video se sadou SDK pro volání, musíte být schopni spravovat zařízení. Zařízení umožňují řídit, co do hovoru přenáší zvuk a video.

deviceManagerPomocí , můžete vytvořit výčet místních zařízení, která mohou přenášet zvukové a video streamy v hovoru. Můžete také požádat deviceManager o oprávnění pro přístup k mikrofonům a kamerám místního zařízení.

Přístup můžete deviceManager získat voláním callClient.getDeviceManager() metody:

const deviceManager = await callClient.getDeviceManager();

Získání místních zařízení

Pro přístup k místním zařízením můžete použít metody getCameras() výčtu deviceManager a getMicrophones. Tyto metody jsou asynchronní akce.

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

Nastavení výchozích zařízení

Jakmile budete vědět, která zařízení se dají používat, můžete nastavit výchozí zařízení pro mikrofon, reproduktor a kameru. Pokud nejsou nastavené výchozí hodnoty klienta, sada SDK komunikačních služeb používá výchozí nastavení operačního systému.

Mikrofon

Přístup k použitému zařízení

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

Nastavení zařízení, které se má použít

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

Speaker

Přístup k použitému zařízení

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

Nastavení zařízení, které se má použít

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

Kamera

Přístup k použitému zařízení

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

Nastavení zařízení, které se má použít

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

Každý CallAgent si může vybrat svůj vlastní mikrofon a reproduktory na přidruženém DeviceManager. Doporučujeme používat různé CallAgents mikrofony a reproduktory. Neměli by sdílet stejné mikrofony ani reproduktory. Pokud dojde ke sdílení, může se aktivovat diagnostika uživatelů mikrofonu a mikrofon přestane fungovat v závislosti na prohlížeči nebo operačním systému.

Místní stream videa

Abyste mohli posílat video ve volání, musíte vytvořit LocalVideoStreamobjekt.

const localVideoStream = new LocalVideoStream(camera);

Kamera předaná jako parametr je jedním z VideoDeviceInfo objektů vrácených metodou deviceManager.getCameras().

A LocalVideoStream má následující vlastnosti:

  • source: Informace o zařízení.
const source = localVideoStream.source;
  • mediaStreamType: Může být Video, ScreenSharingnebo RawMedia.
const type: MediaStreamType = localVideoStream.mediaStreamType;

Náhled místního fotoaparátu

Můžete použít deviceManager a VideoStreamRenderer začít vykreslovat streamy z místní kamery. Jakmile se vytvoří LocalVideoStream , použijte ho k nastaveníVideoStreamRenderer. VideoStreamRendererPo vytvoření volání metody createView() získat zobrazení, které můžete přidat jako dítě na stránku.

Tento stream se neodesílají ostatním účastníkům; je to místní informační kanál ve verzi Preview.

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

Zastavení místního náhledu

Chcete-li zastavit místní volání náhledu, odstraňte zobrazení odvozené z objektu VideoStreamRenderer. Jakmile videoStreamRenderer odstraníte, odeberte zobrazení ze stromu HTML voláním removeChild() metody z DOM Node obsahující váš náhled.

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

Žádost o oprávnění ke kameře a mikrofonu

Aplikace nemůže používat kameru ani mikrofon bez oprávnění. Správce zařízení můžete použít k zobrazení výzvy uživateli k udělení oprávnění fotoaparátu nebo mikrofonu:

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

Jakmile je příslib vyřešen, vrátí metoda s DeviceAccess objektem, který označuje, zda audio a video oprávnění byla udělena:

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

Notes

  • videoDevicesUpdated událost se aktivuje, když jsou videozařízení připojená/odpojená.
  • audioDevicesUpdated událost se aktivuje, když jsou připojena zvuková zařízení.
  • Při vytváření správce zařízení nejprve nezná žádná zařízení, pokud ještě nejsou udělená oprávnění, takže jeho název zařízení je zpočátku prázdný a neobsahuje podrobné informace o zařízení. Pokud pak zavoláme rozhraní API DeviceManager.askPermission(), zobrazí se uživateli výzva k přístupu k zařízení. Když uživatel vybere možnost Povolit, aby správci zařízení udělil přístup, dozví se o zařízeních v systému, aktualizujte jeho seznamy zařízení a vygenerujte události audioDevicesUpdated a videoDevicesUpdated. Pokud uživatel stránku aktualizuje a vytvoří správce zařízení, může se správce zařízení dozvědět o zařízeních, protože uživatel udělil přístup dříve. Má zpočátku vyplněné seznamy zařízení a nevysílají události audioDevicesUpdated ani videoDevicesUpdated.
  • Výčet a výběr mluvčího se nepodporuje v Android Chrome, iOS Safari ani macOS Safari.

Volání pomocí videokamery

Důležité

Aktuálně se podporuje jenom jeden odchozí místní video stream.

Chcete-li umístit videohovor, musíte vytvořit výčet místních fotoaparátů pomocí getCameras() metody v deviceManager.

Jakmile vyberete kameru, použijte ji k vytvoření LocalVideoStream instance. Předejte ji videoOptions jako položku v rámci localVideoStream pole metodě CallAgent startCall .

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);
  • Můžete se také připojit k volání pomocí videa s rozhraním CallAgent.join() API a přijmout a volat s videem pomocí Call.Accept() rozhraní API.
  • Když se hovor připojí, automaticky začne odesílat stream videa z vybrané kamery druhému účastníkovi.

Spuštění a zastavení odesílání místního videa během hovoru

Spustit video

Pokud chcete zahájit video během hovoru, musíte vytvořit výčet fotoaparátů pomocí getCameras metody na objektu deviceManager . Pak vytvořte novou instanci LocalVideoStream s požadovanou kamerou a pak předejte LocalVideoStream objekt do startVideo metody existujícího objektu volání:

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

Zastavit video

Po úspěšném zahájení odesílání videa LocalVideoStream se do localVideoStreams kolekce v instanci volání přidá instance typuVideo.

Vyhledání streamu videa v objektu Volání

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

Zastavte místní video , pokud chcete zastavit místní video během hovoru, předejte localVideoStream instanci, která se používá pro video do metody stopVideo metody Call:

await call.stopVideo(localVideoStream);

Když budete mít aktivní LocalVideoStream, můžete přepnout na jiné zařízení fotoaparátu tak, že na této LocalVideoStream instanci vyvoláteswitchSource:

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

Pokud zadané videoza zařízení není k dispozici:

  • Pokud je vaše video při volání vypnuté a začnete používat call.startVideo()video , tato metoda vyvolá SourceUnavailableError diagnostiku a cameraStartFailed uživatelem je nastavena na hodnotu true.
  • Volání localVideoStream.switchSource() metody způsobí cameraStartFailed , že se nastaví na true. Náš průvodce diagnostikou hovorů poskytuje další informace o tom, jak diagnostikovat problémy související s voláním.

Pokud chcete ověřit, jestli je místní video zapnuté nebo vypnuté, můžete použít metodu Call isLocalVideoStarted, která vrátí hodnotu true nebo false:

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

Pokud chcete naslouchat změnám místního videa, můžete se přihlásit k odběru a odhlášení odběru události isLocalVideoStartedChanged:

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

Spuštění a zastavení sdílení obrazovky během hovoru

Pokud chcete zahájit sdílení obrazovky během volání, můžete použít asynchronní metodu startScreenSharing() objektu Call :

Spuštění sdílení obrazovky

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

Poznámka: Odesílání sdílení obrazovky je podporováno pouze v prohlížeči v počítači.

Vyhledání sdílení obrazovky v kolekci LocalVideoStream

Po úspěšném zahájení odesílání sdílení LocalVideoStream obrazovky se do kolekce v instanci volání přidá localVideoStreams instance typuScreenSharing.

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

Ukončení sdílení obrazovky

Pokud chcete během hovoru ukončit sdílení obrazovky, můžete použít asynchronní zastavení obrazovky API StoptScreenSharing:

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

Kontrola stavu sdílení obrazovky

Pokud chcete ověřit, jestli je sdílení obrazovky zapnuté nebo vypnuté, můžete použít rozhraní APIscreenSharingOn, které vrací hodnotu true nebo false:

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

Pokud chcete naslouchat změnám sdílené složky obrazovky, můžete se přihlásit k odběru a odhlásit odběr události isScreenSharingOnChanged:

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

Důležité

Tato funkce služeb Azure Communication Services je aktuálně ve verzi Preview.

Rozhraní API a sady SDK verze Preview jsou poskytovány bez smlouvy o úrovni služeb. Doporučujeme je nepoužívat pro produkční úlohy. Některé funkce nemusí být podporované nebo můžou mít omezené možnosti.

Další informace najdete v dodatečných podmínkách použití pro verze Preview Microsoft Azure.

Náhled místní sdílené složky obrazovky je ve veřejné verzi Preview a je k dispozici jako součást verze 1.15.1-beta.1+.

Náhled sdílení místní obrazovky

Můžete použít VideoStreamRenderer k zahájení vykreslování streamů z místní sdílené složky obrazovky, abyste viděli, co posíláte jako stream sdílení obrazovky.

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

Vykreslení videa nebo sdílení obrazovek vzdáleného účastníka

Pokud chcete vykreslit video nebo sdílení obrazovky vzdáleného účastníka, prvním krokem je získání odkazu na RemoteVideoStream, který chcete vykreslit. To lze provést procházením pole nebo video streamu (videoStreams) z RemoteParticipant. Ke kolekci vzdálených účastníků se přistupuje prostřednictvím objektu Call .

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

Pokud chcete vykreslit RemoteVideoStream, musíte se přihlásit k odběru události isAvailableChanged . Pokud se isAvailable vlastnost změní na true, vzdálený účastník odesílá stream videa. Potom vytvořte novou instanci VideoStreamRenderera pak vytvořte novou VideoStreamRendererView instanci pomocí asynchronní createView metody.
Pak se můžete připojit view.target k libovolnému prvku uživatelského rozhraní.

Kdykoli se změní dostupnost vzdáleného streamu, můžete zničit celý VideoStreamRenderer nebo konkrétní VideoStreamRendererView. Pokud se rozhodnete je zachovat, zobrazí se v zobrazení prázdný rámeček videa.

// 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 pro stylování číselníku načítání přes vzdálený datový proud videa.

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

Kvalita vzdáleného videa

Sada Azure Communication Services WebJS SDK poskytuje funkci s názvem Optimální počet videí (OVC) počínaje verzí 1.15.1. Tuto funkci lze použít k informování aplikací za běhu o tom, kolik příchozích videí od různých účastníků se dá v daném okamžiku optimálně vykreslit ve skupinovém hovoru (2+ účastníci). Tato funkce zveřejňuje vlastnost optimalVideoCount , která se dynamicky mění během volání na základě síťových a hardwarových funkcí místního koncového bodu. Hodnota podrobností, optimalVideoCount kolik videí z jiné aplikace účastníka by se mělo vykreslit v daném okamžiku. Aplikace by měly tyto změny zpracovat a odpovídajícím způsobem aktualizovat počet vykreslovaných videí podle doporučení. Mezi každou aktualizací je období zrušení (přibližně 10 s).

Použití funkce optimalVideoCount je funkce volání. Funkci musíte odkazovat OptimalVideoCount prostřednictvím feature metody objektu Call . Potom můžete nastavit naslouchací OptimalVideoCountCallFeature proces pomocí on metody, která se má upozornit, když se změní optimalVideoCount. Pokud se chcete odhlásit od změn, můžete metodu off volat. Aktuální maximální počet příchozích videí , která se dají vykreslit, je 16. Aby počítač správně podporoval 16 příchozích videí, měl by mít mimimum 16 GB RAM a 4jádrový nebo větší procesor, který není starší než 3 roky.

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

Příklad použití: Aplikace by se měla přihlásit k odběru změn optimálního počtu videí ve skupinových voláních. Změnu optimálního počtu videí je možné zpracovat vytvořením nového rendereru (createView metody) nebo odstraněním zobrazení (dispose) a odpovídajícím způsobem aktualizovat rozložení aplikace.

Vlastnosti vzdáleného streamu videa

Vzdálené streamy videa mají následující vlastnosti:

const id: number = remoteVideoStream.id;
  • id: ID vzdáleného streamu videa.
const type: MediaStreamType = remoteVideoStream.mediaStreamType;
  • mediaStreamType: Může být Video nebo ScreenSharing.
const isAvailable: boolean = remoteVideoStream.isAvailable;
  • isAvailable: Definuje, jestli koncový bod vzdáleného účastníka aktivně odesílá stream.
const isReceiving: boolean = remoteVideoStream.isReceiving;
  • isReceiving:
    • Informuje aplikaci, pokud se přijímají nebo neschovávají data vzdáleného datového proudu videa.

    • Příznak se přesune do false následujících scénářů:

      • Vzdálený účastník, který je v mobilním prohlížeči, přenese aplikaci prohlížeče na pozadí.
      • Vzdálený účastník nebo uživatel, který video obdrží, má problém se sítí, který výrazně ovlivňuje kvalitu videa.
      • Vzdálený účastník, který je v macOS/iOS Safari, vybere "Pozastavit" z adresního řádku.
      • Vzdálený účastník má odpojení sítě.
      • Vzdálený účastník na mobilním zařízení ukončí nebo ukončí prohlížeč.
      • Vzdálený účastník na mobilním zařízení nebo na ploše uzamkne své zařízení. Tento scénář platí také v případě, že je vzdálený účastník na stolním počítači a přejde do režimu spánku.
    • Příznak se přesune do true následujících scénářů:

      • Vzdálený účastník, který je v mobilním prohlížeči a má pozadí prohlížeče, ho vrátí do popředí.
      • Vzdálený účastník, který je v macOS/iOS Safari, vybere na panelu Adresa po pozastavení videa možnost Pokračovat.
      • Vzdálený účastník se po dočasném odpojení znovu připojí k síti.
      • Vzdálený účastník na mobilním zařízení odemkne a vrátí se do hovoru v mobilním prohlížeči.
    • Tato funkce zlepšuje uživatelské prostředí pro vykreslování vzdálených datových proudů videa.

    • Pokud se příznak isReceiving změní na false, můžete zobrazit číselník načítání přes vzdálený stream videa. Nemusíte implementovat číselník načítání, ale číselník načítání je nejběžnějším využitím pro lepší uživatelské prostředí.

const size: StreamSize = remoteVideoStream.size;
  • size: Velikost datového proudu s informacemi o šířce a výšce videa.

Metody a vlastnosti VideoStreamRenderer

await videoStreamRenderer.createView();

Vytvořte VideoStreamRendererView instanci, kterou lze připojit v uživatelském rozhraní aplikace k vykreslení vzdáleného video streamu, použít asynchronní createView() metodu, přeloží, když je stream připravený k vykreslení a vrátí objekt s target vlastností, která představuje video prvek, který lze vložit kdekoli ve stromu MODELU DOM.

videoStreamRenderer.dispose();

Dispose of videoStreamRenderer and all associated VideoStreamRendererView instances.

VideoStreamRendererView – metody a vlastnosti

Při vytváření VideoStreamRendererViewmůžete zadat scalingMode vlastnosti a isMirrored vlastnosti. scalingMode může být Stretch, Cropnebo Fit. Pokud isMirrored je zadán, vykreslený datový proud se překlopí svisle.

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

Každá VideoStreamRendererView instance má target vlastnost, která představuje plochu vykreslování. Připojte tuto vlastnost v uživatelském rozhraní aplikace:

htmlElement.appendChild(view.target);

Aktualizaci můžete provést scalingMode vyvoláním updateScalingMode metody:

view.updateScalingMode('Crop');

Odešlete streamy videa ze dvou různých fotoaparátů ve stejném hovoru ze stejného stolního zařízení.

Důležité

Tato funkce služeb Azure Communication Services je aktuálně ve verzi Preview.

Rozhraní API a sady SDK verze Preview jsou poskytovány bez smlouvy o úrovni služeb. Doporučujeme je nepoužívat pro produkční úlohy. Některé funkce nemusí být podporované nebo můžou mít omezené možnosti.

Další informace najdete v dodatečných podmínkách použití pro verze Preview Microsoft Azure.

Odesílání streamů videa ze dvou různých fotoaparátů ve stejném hovoru se podporuje jako součást verze 1.17.1-beta.1 nebo novější v prohlížečích podporovaných pro stolní počítače.

  • Streamy videa ze dvou různých fotoaparátů můžete odesílat z jedné karty nebo aplikace desktopového prohlížeče ve stejném volání s následujícím fragmentem kódu:
// 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();

Omezení:

  • Musí to být provedeno se dvěma různými CallAgent instancemi pomocí různých identit. Fragment kódu ukazuje, že se používají dva agenti volání, z nichž každý má vlastní objekt Call.
  • V příkladu kódu se oba CallAgents připojují ke stejnému volání (stejné ID volání). Můžete se také připojit k různým voláním s jednotlivými agenty a odeslat jedno video na jeden hovor a jiné video na druhém hovoru.
  • Odesílání stejné kamery v obou callagentech se nepodporuje. Musí to být dvě různé kamery.
  • Odesílání dvou různých fotoaparátů pomocí jednoho callagentu se v současné době nepodporuje.
  • V macOS Safari, rozostření pozadí efekty videa (z @azure/communication-effects), lze použít pouze na jednu kameru, a ne na obou současně.

Nainstalujte sadu SDK .

Vyhledejte soubor na úrovni build.gradle projektu a přidejte mavenCentral() ho do seznamu úložišť v části buildscript a allprojects:

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

Potom do souboru na úrovni build.gradle modulu přidejte do oddílu dependencies následující řádky:

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

Inicializace požadovaných objektů

Pokud chcete vytvořit CallAgent instanci, musíte metodu createCallAgent CallClient volat v instanci. Toto volání asynchronně vrátí CallAgent objekt instance.

Metoda createCallAgent přebírá CommunicationUserCredential jako argument, který zapouzdřuje přístupový token.

Pokud chcete získat přístup DeviceManager, musíte nejprve vytvořit callAgent instanci. Pak můžete použít metodu CallClient.getDeviceManager získat 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();

Pokud chcete nastavit zobrazovaný název volajícího, použijte tuto alternativní metodu:

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

Správa zařízení

Pokud chcete začít používat video s voláním, budete muset vědět, jak spravovat zařízení. Zařízení umožňují řídit, co do hovoru přenáší zvuk a video.

DeviceManager umožňuje vytvořit výčet místních zařízení, která se dají použít při volání k přenosu zvukových datových proudů nebo datových proudů videa. Umožňuje také požádat uživatele o oprávnění pro přístup k mikrofonu a fotoaparátu pomocí nativního rozhraní API prohlížeče.

Přístup můžete deviceManager provést voláním callClient.getDeviceManager() metody.

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

Vytvoření výčtu místních zařízení

Pro přístup k místním zařízením můžete v Správce zařízení použít metody výčtu. Výčet je synchronní akce.

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

Náhled místního fotoaparátu

Můžete použít DeviceManager a Renderer začít vykreslovat streamy z místní kamery. Tento datový proud se neodesílají ostatním účastníkům; je to místní informační kanál ve verzi Preview. Jedná se o asynchronní akci.

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

1:1 hovor s videokamerou

Upozorňující

V současné době je podporován pouze jeden odchozí místní video stream, pokud chcete umístit hovor s videem, které musíte vytvořit výčet místních fotoaparátů pomocí deviceManager getCameras rozhraní API. Jakmile vyberete požadovanou kameru, použijte ji k vytvoření LocalVideoStream instance a předání videoOptions jako položky v localVideoStream poli metodě call . Jakmile se hovor připojí, automaticky začne odesílat stream videa z vybrané kamery jiným účastníkům.

Poznámka:

Vzhledem k obavám o ochranu osobních údajů se video nesdílí do hovoru, pokud se nesdílí v místním náhledu. Další podrobnosti najdete v části Náhled místní kamery.

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

Spuštění a zastavení odesílání místního videa

Pokud chcete zahájit video, musíte vytvořit výčet fotoaparátů pomocí rozhraní API na deviceManager objektugetCameraList. Pak vytvořte novou instanci LocalVideoStream předání požadované kamery a předejte ji do startVideo rozhraní API jako 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();

Po úspěšném zahájení odesílání videa LocalVideoStream se do kolekce v instanci volání přidá localVideoStreams instance.

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

Pokud chcete zastavit místní video, předejte instanci dostupnou LocalVideoStream v localVideoStreams kolekci:

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

Při odesílání videa na switchSource LocalVideoStream instanci můžete přepnout na jiné zařízení fotoaparátu:

currentLocalVideoStream.switchSource(source).get();

Vykreslení datových proudů videa vzdáleného účastníka

Pokud chcete zobrazit seznam streamů videa a streamů sdílení obrazovky vzdálených účastníků, prohlédněte si videoStreams kolekce:

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

Pokud chcete vykreslit RemoteVideoStream od vzdáleného účastníka, musíte se přihlásit k odběru OnVideoStreamsUpdated události.

V rámci události změna isAvailable vlastnosti na true znamená, že vzdálený účastník právě odesílá stream. Jakmile k tomu dojde, vytvořte novou instanci Renderera pak vytvořte novou RendererView pomocí asynchronního createView rozhraní API a připojte view.target ji kdekoli v uživatelském rozhraní vaší aplikace.

Kdykoli se změní dostupnost vzdáleného streamu, můžete se rozhodnout zničit celý renderer, konkrétní RendererView nebo zachovat, ale výsledkem bude zobrazení prázdného rámečku videa.

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

Vlastnosti vzdáleného streamu videa

Vzdálený stream videa má několik vlastností.

  • Id - ID vzdáleného streamu videa
int id = remoteVideoStream.getId();
  • MediaStreamType – Může se jednat o video nebo sdílení obrazovky.
MediaStreamType type = remoteVideoStream.getMediaStreamType();
  • isAvailable – Označuje, jestli koncový bod vzdáleného účastníka aktivně odesílá stream.
boolean availability = remoteVideoStream.isAvailable();

Metody a vlastnosti rendereru

Objekt rendereru následující rozhraní API

  • VideoStreamRendererView Vytvořte instanci, kterou můžete později připojit v uživatelském rozhraní aplikace k vykreslení vzdáleného streamu videa.
// Create a view for a video stream
VideoStreamRendererView.createView()
  • Vyřaďte renderer a všechny VideoStreamRendererView přidružené k tomuto rendereru. Chcete-li volat, když jste odebrali všechna přidružená zobrazení z uživatelského rozhraní.
VideoStreamRenderer.dispose()
  • StreamSize - velikost (šířka/výška) vzdáleného video streamu
StreamSize renderStreamSize = VideoStreamRenderer.getSize();
int width = renderStreamSize.getWidth();
int height = renderStreamSize.getHeight();

RendererView – metody a vlastnosti

Při vytváření VideoStreamRendererView můžete zadat ScalingMode vlastnosti a mirrored vlastnosti, které budou platit pro toto zobrazení: Režim škálování může být buď oříznout | FIT

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

Vytvořený RendererView je pak možné připojit k uživatelskému rozhraní aplikace pomocí následujícího fragmentu kódu:

layout.addView(rendererView);

Později můžete režim škálování aktualizovat vyvoláním updateScalingMode rozhraní API na objektu RendererView jedním z ScalingMode.CROP | ScalingMode.FIT jako argument.

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

Nastavení systému

Podle těchto kroků nastavte systém.

Vytvoření projektu Xcode

V Xcode vytvořte nový projekt pro iOS a vyberte šablonu aplikace s jedním zobrazením . Tento článek používá architekturu SwiftUI, takže byste měli nastavit jazyk na Swift a nastavit rozhraní na SwiftUI.

V tomto článku nebudete vytvářet testy. Zrušte zaškrtnutí políčka Zahrnout testy .

Snímek obrazovky znázorňující okno pro vytvoření projektu v Xcode

Instalace balíčku a závislostí pomocí CocoaPods

  1. Vytvořte pro aplikaci soubor Podfile, například v tomto příkladu:

    platform :ios, '13.0'
    use_frameworks!
    target 'AzureCommunicationCallingSample' do
        pod 'AzureCommunicationCalling', '~> 1.0.0'
    end
    
  2. Spusťte pod install.

  3. Otevřete .xcworkspace pomocí Xcode.

Vyžádání přístupu k mikrofonu

Pokud chcete získat přístup k mikrofonu zařízení, musíte aktualizovat seznam vlastností informací aplikace pomocí NSMicrophoneUsageDescription. Nastavte přidruženou hodnotu na řetězec, který je součástí dialogového okna, který systém používá k vyžádání přístupu od uživatele.

Klikněte pravým tlačítkem myši na položku Info.plist stromu projektu a pak vyberte Open As>Source Code. Do oddílu nejvyšší úrovně <dict> přidejte následující řádky a pak soubor uložte.

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

Nastavení architektury aplikace

Otevřete soubor projektu ContentView.swift . import Přidejte deklaraci do horní části souboru pro import AzureCommunicationCalling knihovny. Kromě toho import AVFoundation. Potřebujete ho pro žádosti o zvuková oprávnění v kódu.

import AzureCommunicationCalling
import AVFoundation

Inicializace callagentu

Chcete-li vytvořit CallAgent instanci z CallClient, musíte použít metodu callClient.createCallAgent , která asynchronně vrátí CallAgent objekt po inicializaci.

Pokud chcete vytvořit klienta volání, předejte CommunicationTokenCredential objekt:

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

CommunicationTokenCredential Předejte objekt, který jste vytvořiliCallClient, a nastavte zobrazovaný název:

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

Správa zařízení

Pokud chcete začít používat video s voláním, budete muset vědět, jak spravovat zařízení. Zařízení umožňují řídit, co do hovoru přenáší zvuk a video.

DeviceManager umožňuje vytvořit výčet místních zařízení, která lze použít při volání k přenosu zvukových nebo video streamů. Umožňuje také požádat uživatele o oprávnění pro přístup k mikrofonu nebo kameře. K objektu callClient máte přístupdeviceManager.

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

Vytvoření výčtu místních zařízení

Pro přístup k místním zařízením můžete použít metody výčtu ve Správci zařízení. Výčet je synchronní akce.

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

Získání náhledu místního fotoaparátu

Můžete začít Renderer vykreslovat stream z místní kamery. Tento datový proud se neodesílají ostatním účastníkům; je to místní informační kanál ve verzi Preview. Jedná se o asynchronní akci.

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

Získání vlastností náhledu místního fotoaparátu

Renderer má sadu vlastností a metod, které umožňují řídit vykreslování.

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

1:1 hovor s videem

Pokud chcete získat instanci správce zařízení, přečtěte si část o správě zařízení.

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

Vykreslení datových proudů videa vzdáleného účastníka

Vzdálení účastníci můžou zahájit sdílení videa nebo obrazovky během hovoru.

Zpracování streamů sdílení videa nebo sdílení obrazovky vzdálených účastníků

Pokud chcete zobrazit seznam datových proudů vzdálených účastníků, zkontrolujte videoStreams kolekce.

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

Získání vlastností vzdáleného streamu videa

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

Vykreslení datových proudů vzdálených účastníků

Pokud chcete začít vykreslovat streamy vzdálených účastníků, použijte následující kód.

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)

Získání metod a vlastností vzdáleného rendereru videa

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

Nastavení systému

Podle těchto kroků nastavte systém.

Vytvoření projektu sady Visual Studio

V případě Univerzální platforma Windows aplikace vytvořte v sadě Visual Studio 2022 nový projekt Prázdná aplikace (Universal Windows). Po zadání názvu projektu si můžete vybrat libovolnou sadu Windows SDK později než 10.0.17763.0.

V případě aplikace WinUI 3 vytvořte nový projekt pomocí šablony Prázdná aplikace zabalená (WinUI 3 v desktopové verzi) pro nastavení jednostráňové aplikace WinUI 3. Vyžaduje se sada Windows App SDK verze 1.3 nebo novější.

Instalace balíčku a závislostí pomocí Správce balíčků NuGet

Rozhraní API a knihovny volající sady SDK jsou veřejně dostupné prostřednictvím balíčku NuGet.

Vyhledání, stažení a instalace balíčku NuGet volající sady SDK:

  1. Otevřete Správce balíčků NuGet výběrem nástrojů>NuGet Správce balíčků> Nabídky NuGet pro řešení.
  2. Vyberte Procházet a do vyhledávacího pole zadejte Azure.Communication.Calling.WindowsClient .
  3. Ujistěte se, že je zaškrtnuté políčko Zahrnout předběžné verze .
  4. Vyberte balíček Azure.Communication.Calling.WindowsClient a pak vyberte Azure.Communication.Calling.WindowsClient 1.4.0-beta.1 nebo novější verzi.
  5. Zaškrtněte políčko odpovídající projektu Azure Communication Services v pravém podokně.
  6. Vyberte volbu Instalovat.

Vyžádání přístupu k mikrofonu

Aby aplikace fungovala správně, vyžaduje přístup k fotoaparátu. V aplikacích pro UPW by měla být funkce fotoaparátu deklarována v souboru manifestu aplikace.

Následující kroky ukazují, jak toho dosáhnout.

  1. Na panelu Solution Explorer poklikejte na soubor s příponou .appxmanifest .
  2. Klikněte na Capabilities kartu.
  3. Camera V seznamu možností zaškrtněte políčko.

Vytvoření tlačítek uživatelského rozhraní pro umístění a zavěsit hovor

Tato jednoduchá ukázková aplikace obsahuje dvě tlačítka. Jeden pro umístění hovoru a druhý zavěsit umístěný hovor. Následující postup ukazuje, jak tato tlačítka přidat do aplikace.

  1. Na panelu Solution Explorer poklikejte na soubor s názvem MainPage.xaml UPW nebo MainWindows.xaml WinUI 3.
  2. Na centrálním panelu vyhledejte kód XAML v náhledu uživatelského rozhraní.
  3. Upravte kód XAML následujícím výňatkem:
<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>

Nastavení aplikace pomocí rozhraní API pro volání sady SDK

Rozhraní API volající sady SDK jsou ve dvou různých oborech názvů. Následující kroky informují kompilátor jazyka C# o těchto oborech názvů, což umožňuje intellisense sady Visual Studio pomáhat s vývojem kódu.

  1. Na panelu Solution Explorer klikněte na šipku na levé straně souboru s názvem MainPage.xaml UPW nebo MainWindows.xaml WinUI 3.
  2. Poklikejte na soubor s názvem MainPage.xaml.cs nebo MainWindows.xaml.cs.
  3. Na konec aktuálních using příkazů přidejte následující příkazy.
using Azure.Communication.Calling.WindowsClient;

Nechte MainPage.xaml.cs nebo MainWindows.xaml.cs otevřete. Další kroky do něj přidají další kód.

Povolit interakce aplikací

Tlačítka uživatelského rozhraní, která byla přidána, musí pracovat nad umístěním CommunicationCall. To znamená, že CommunicationCall datový člen by měl být přidán do MainPage třídy nebo MainWindow třídy. Kromě toho, aby bylo možné úspěšně vytvořit CallAgent asynchronní operaci, CallAgent by měl být také přidán datový člen do stejné třídy.

Do třídy nebo MainWindow třídy přidejte následující datové členyMainPage:

CallAgent callAgent;
CommunicationCall call;

Vytváření obslužných rutin tlačítek

Dříve byly do kódu XAML přidány dvě tlačítka uživatelského rozhraní. Následující kód přidá obslužné rutiny, které se mají spustit, když uživatel tlačítko vybere. Následující kód by se měl přidat za datové členy z předchozí části.

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

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

Objektový model

Následující třídy a rozhraní zpracovávají některé z hlavních funkcí klientské knihovny volání služeb Azure Communication Services pro UPW.

Název Popis
CallClient Jedná se CallClient o hlavní vstupní bod do klientské knihovny volání.
CallAgent Slouží CallAgent ke spuštění a připojení k voláním.
CommunicationCall Slouží CommunicationCall ke správě umístěných nebo připojených hovorů.
CommunicationTokenCredential Slouží CommunicationTokenCredential jako přihlašovací údaje tokenu k vytvoření instance CallAgent.
CallAgentOptions Obsahuje CallAgentOptions informace pro identifikaci volajícího.
HangupOptions Informuje HangupOptions , jestli má být hovor ukončen všem účastníkům.

Registrace obslužné rutiny schématu videa

Součást uživatelského rozhraní, jako je MediaElement XAML nebo MediaPlayerElement, potřebujete aplikaci, která registruje konfiguraci pro vykreslování místních a vzdálených video kanálů. Přidejte následující obsah mezi Package značky Package.appxmanifest:

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

Inicializace callagentu

Chcete-li vytvořit CallAgent instanci z CallClient, musíte použít CallClient.CreateCallAgentAsync metodu, která asynchronně vrátí CallAgent objekt po inicializaci.

Chcete-li vytvořit CallAgent, musíte předat CallTokenCredential objekt a CallAgentOptions objekt. Mějte na paměti, že CallTokenCredential pokud je předán poškozený token.

Do pomocné funkce by se měl přidat následující kód, který se má volat při inicializaci aplikace.

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;

<AUTHENTICATION_TOKEN> Změňte platný token přihlašovacích údajů pro váš prostředek. Informace o přístupovém tokenu uživatele najdete v dokumentaci, pokud musí být zdrojový token přihlašovacích údajů.

1:1 hovor s videokamerou

Objekty potřebné k vytvoření CallAgent jsou teď připravené. Je čas asynchronně vytvořit CallAgent a umístit videohovor.

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

Náhled místního fotoaparátu

Volitelně můžeme nastavit náhled místního fotoaparátu. Video lze vykreslit prostřednictvím MediaPlayerElement:

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

Inicializace místního náhledu 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);
    }
}

Vykreslení vzdáleného streamu fotoaparátu

Nastavení obslužné rutiny sudé v reakci na OnCallsUpdated událost:

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

Začněte vykreslovat vzdálený stream videa na 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;
    }
}

Ukončení hovoru

Po umístění HangupAsync volání by metoda objektu CommunicationCall měla být použita k zablokování volání.

Instance HangupOptions by měla být také použita k informování, zda musí být hovor ukončen všem účastníkům.

Následující kód by měl být přidán dovnitř 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;
            }
        }
    }
}

Spuštění kódu

Ujistěte se, že Visual Studio sestaví aplikaci pro x64x86 aplikaci nebo ARM64a pak stiskněte klávesu a F5 spusťte aplikaci. Potom kliknutím na CommunicationCall tlačítko umístíte volání do volané.

Mějte na paměti, že při prvním spuštění aplikace systém vyzve uživatele k udělení přístupu k mikrofonu.

Další kroky